From cea99d4105bed749739997dc18ed292b5682eafb Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 12:51:55 +0200 Subject: [PATCH 001/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=82=D0=B8=D0=BF=20=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20Map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 8 ++- src/com/annimon/ownlang/Main.java | 2 +- src/com/annimon/ownlang/lib/ArrayValue.java | 19 ++++++ .../annimon/ownlang/lib/FunctionValue.java | 19 ++++++ src/com/annimon/ownlang/lib/MapValue.java | 60 +++++++++++++++++++ src/com/annimon/ownlang/lib/NumberValue.java | 19 ++++++ src/com/annimon/ownlang/lib/StringValue.java | 19 ++++++ .../ownlang/lib/UserDefinedFunction.java | 5 ++ src/com/annimon/ownlang/parser/Parser.java | 24 +++++++- .../parser/ast/ArrayAccessExpression.java | 15 ++++- .../ownlang/parser/ast/MapExpression.java | 38 ++++++++++++ .../annimon/ownlang/parser/ast/Visitor.java | 1 + .../parser/visitors/AbstractVisitor.java | 8 +++ 13 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/MapValue.java create mode 100644 src/com/annimon/ownlang/parser/ast/MapExpression.java diff --git a/program.own b/program.own index f6fdf43b..25141c6e 100644 --- a/program.own +++ b/program.own @@ -81,4 +81,10 @@ for i = 0, i < 4, i = i + 1 { print functions[i] print "\n" print function(functions[i], 6, 3) -} \ No newline at end of file +} + +// map +map = {"+" : add, "-" : sub. "*" : mul, "/" : div} +//print map["+"] +print "\n" +print function(map["+"], 4, 5) \ No newline at end of file diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 1d71c55a..e964fdb6 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -18,7 +18,7 @@ public final class Main { public static void main(String[] args) throws IOException { - final String file = "examples/game/agar.own"; + final String file = "program.own"; final String input = new String( Files.readAllBytes(Paths.get(file)), "UTF-8"); final List tokens = new Lexer(input).tokenize(); for (int i = 0; i < tokens.size(); i++) { diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 0ffbc3c4..fbee7296 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -41,6 +41,25 @@ public String asString() { return Arrays.toString(elements); } + @Override + public int hashCode() { + int hash = 5; + hash = 79 * hash + Arrays.deepHashCode(this.elements); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) + return false; + final ArrayValue other = (ArrayValue) obj; + return Arrays.deepEquals(this.elements, other.elements); + } + + + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index 6dfb6c30..29c77906 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib; +import java.util.Objects; + /** * * @author aNNiMON @@ -26,6 +28,23 @@ public Function getValue() { return value; } + @Override + public int hashCode() { + int hash = 7; + hash = 71 * hash + Objects.hashCode(this.value); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) + return false; + final FunctionValue other = (FunctionValue) obj; + return Objects.equals(this.value, other.value); + } + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java new file mode 100644 index 00000000..c636a17b --- /dev/null +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -0,0 +1,60 @@ +package com.annimon.ownlang.lib; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * + * @author aNNiMON + */ +public final class MapValue implements Value { + + private final Map map; + + public MapValue(int size) { + this.map = new HashMap<>(size); + } + + public Value get(Value key) { + return map.get(key); + } + + public void set(Value key, Value value) { + map.put(key, value); + } + + @Override + public double asNumber() { + throw new RuntimeException("Cannot cast map to number"); + } + + @Override + public String asString() { + return map.toString(); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 37 * hash + Objects.hashCode(this.map); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) + return false; + final MapValue other = (MapValue) obj; + return Objects.equals(this.map, other.map); + } + + + + @Override + public String toString() { + return asString(); + } +} diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/com/annimon/ownlang/lib/NumberValue.java index 91cf759d..0c522db5 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -29,6 +29,25 @@ public String asString() { return Double.toString(value); } + @Override + public int hashCode() { + int hash = 3; + hash = 71 * hash + (int) (Double.doubleToLongBits(this.value) ^ (Double.doubleToLongBits(this.value) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + 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); + } + + + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index d25f763e..7b75ad0b 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib; +import java.util.Objects; + /** * * @author aNNiMON @@ -26,6 +28,23 @@ public String asString() { return value; } + @Override + public int hashCode() { + int hash = 3; + hash = 97 * hash + Objects.hashCode(this.value); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) + return false; + final StringValue other = (StringValue) obj; + return Objects.equals(this.value, other.value); + } + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/com/annimon/ownlang/lib/UserDefinedFunction.java index 8226a098..d8686eee 100644 --- a/src/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/src/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -36,4 +36,9 @@ public Value execute(Value... args) { return rt.getResult(); } } + + @Override + public String toString() { + return String.format("function %s %s", argNames.toString(), body.toString()); + } } diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index e8feaf94..e0096173 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -3,7 +3,9 @@ import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @@ -170,6 +172,19 @@ private Expression array() { return new ArrayExpression(elements); } + private Expression map() { + consume(TokenType.LBRACE); + final Map elements = new HashMap<>(); + while (!match(TokenType.RBRACE)) { + final Expression key = primary(); + consume(TokenType.COLON); + final Expression value = expression(); + elements.put(key, value); + match(TokenType.COMMA); + } + return new MapExpression(elements); + } + private ArrayAccessExpression element() { final String variable = consume(TokenType.WORD).getText(); final List indices = new ArrayList<>(); @@ -393,15 +408,18 @@ private Expression primary() { if (match(TokenType.HEX_NUMBER)) { return new ValueExpression(Long.parseLong(current.getText(), 16)); } - if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return function(); - } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { return element(); } + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { + return function(); + } if (lookMatch(0, TokenType.LBRACKET)) { return array(); } + if (lookMatch(0, TokenType.LBRACE)) { + return map(); + } if (match(TokenType.WORD)) { return new VariableExpression(current.getText()); } diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java index 10bba7f8..a4511a83 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.MapValue; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import java.util.List; @@ -21,7 +22,11 @@ public ArrayAccessExpression(String variable, List indices) { @Override public Value eval() { - return getArray().get(lastIndex()); + Value container = Variables.get(variable); + if (container instanceof ArrayValue) { + return getArray().get(lastIndex()); + } + return consumeMap(container).get(indices.get(0).eval()); } public ArrayValue getArray() { @@ -49,6 +54,14 @@ private ArrayValue consumeArray(Value value) { } } + private MapValue consumeMap(Value value) { + if (value instanceof MapValue) { + return (MapValue) value; + } else { + throw new RuntimeException("Map expected"); + } + } + @Override public void accept(Visitor visitor) { visitor.visit(this); diff --git a/src/com/annimon/ownlang/parser/ast/MapExpression.java b/src/com/annimon/ownlang/parser/ast/MapExpression.java new file mode 100644 index 00000000..95e77859 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/MapExpression.java @@ -0,0 +1,38 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Value; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class MapExpression implements Expression { + + public final Map elements; + + public MapExpression(Map arguments) { + this.elements = arguments; + } + + @Override + public Value eval() { + final int size = elements.size(); + final MapValue map = new MapValue(size); + for (Expression key : elements.keySet()) { + map.set(key.eval(), elements.get(key).eval()); + } + return map; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return elements.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 82aa15fc..7f63d026 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -21,6 +21,7 @@ public interface Visitor { void visit(FunctionStatement s); void visit(FunctionalExpression s); void visit(IfStatement s); + void visit(MapExpression s); void visit(PrintStatement s); void visit(ReturnStatement s); void visit(TernaryExpression s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 37dd5b43..ca18f195 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -99,6 +99,14 @@ public void visit(IfStatement s) { s.elseStatement.accept(this); } } + + @Override + public void visit(MapExpression s) { + for (Expression key : s.elements.keySet()) { + key.accept(this); + s.elements.get(key).accept(this); + } + } @Override public void visit(PrintStatement s) { From d3f5212f88be2d7e215e8d8857cff51050a71fa8 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 21:15:47 +0200 Subject: [PATCH 002/448] =?UTF-8?q?=D0=A4=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B8=D0=B7=20std=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/modules/functions/std_echo.java | 18 ++++ .../lib/modules/functions/std_newarray.java | 30 +++++++ .../lib/modules/functions/std_rand.java | 27 ++++++ .../lib/modules/functions/std_sleep.java | 20 +++++ .../lib/modules/functions/std_thread.java | 20 +++++ src/com/annimon/ownlang/lib/modules/std.java | 82 +++---------------- 6 files changed, 126 insertions(+), 71 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_echo.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_newarray.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_rand.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_sleep.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_thread.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_echo.java b/src/com/annimon/ownlang/lib/modules/functions/std_echo.java new file mode 100644 index 00000000..4aa15ba4 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_echo.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +public final class std_echo implements Function { + + @Override + public Value execute(Value... args) { + for (Value arg : args) { + System.out.print(arg.asString()); + System.out.print(" "); + } + System.out.println(); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java b/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java new file mode 100644 index 00000000..f2a1fc2e --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java @@ -0,0 +1,30 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +public final class std_newarray implements Function { + + @Override + public Value execute(Value... args) { + return createArray(args, 0); + } + + private ArrayValue createArray(Value[] args, int index) { + final int size = (int) args[index].asNumber(); + final int last = args.length - 1; + ArrayValue array = new ArrayValue(size); + if (index == last) { + for (int i = 0; i < size; i++) { + array.set(i, NumberValue.ZERO); + } + } else if (index < last) { + for (int i = 0; i < size; i++) { + array.set(i, createArray(args, index + 1)); + } + } + return array; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_rand.java b/src/com/annimon/ownlang/lib/modules/functions/std_rand.java new file mode 100644 index 00000000..cc673bd2 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_rand.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +import java.util.Random; + +public final class std_rand implements Function { + + private static final Random RND = new Random(); + + @Override + public Value execute(Value... args) { + if (args.length == 0) return new NumberValue(RND.nextDouble()); + + int from = 0; + int to = 100; + if (args.length == 1) { + to = (int) args[0].asNumber(); + } else if (args.length == 2) { + from = (int) args[0].asNumber(); + to = (int) args[1].asNumber(); + } + return new NumberValue(RND.nextInt(to - from) + from); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java b/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java new file mode 100644 index 00000000..4b25ea32 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +public final class std_sleep implements Function { + + @Override + public Value execute(Value... args) { + if (args.length == 1) { + try { + Thread.sleep((long) args[0].asNumber()); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java new file mode 100644 index 00000000..9eea5765 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +public final class std_thread implements Function { + + @Override + public Value execute(Value... args) { + if (args.length == 1) { + // Создаём новый поток по имени функции + new Thread(() -> { + Functions.get(args[0].asString()).execute(); + }).start(); + } + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index f274d028..3b8c7253 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -1,6 +1,12 @@ package com.annimon.ownlang.lib.modules; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.std_echo; +import com.annimon.ownlang.lib.modules.functions.std_foreach; +import com.annimon.ownlang.lib.modules.functions.std_newarray; +import com.annimon.ownlang.lib.modules.functions.std_rand; +import com.annimon.ownlang.lib.modules.functions.std_sleep; +import com.annimon.ownlang.lib.modules.functions.std_thread; import java.util.Random; /** @@ -11,76 +17,10 @@ public final class std implements Module { @Override public void init() { - Functions.set("echo", args -> { - for (Value arg : args) { - System.out.print(arg.asString()); - System.out.print(" "); - } - System.out.println(); - return NumberValue.ZERO; - }); - Functions.set("newarray", new Function() { - - @Override - public Value execute(Value... args) { - return createArray(args, 0); - } - - private ArrayValue createArray(Value[] args, int index) { - final int size = (int) args[index].asNumber(); - final int last = args.length - 1; - ArrayValue array = new ArrayValue(size); - if (index == last) { - for (int i = 0; i < size; i++) { - array.set(i, NumberValue.ZERO); - } - } else if (index < last) { - for (int i = 0; i < size; i++) { - array.set(i, createArray(args, index + 1)); - } - } - return array; - } - }); - Functions.set("rand", new Rand()); - Functions.set("sleep", args -> { - if (args.length == 1) { - try { - Thread.sleep((long) args[0].asNumber()); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - return NumberValue.ZERO; - }); - Functions.set("thread", args -> { - if (args.length == 1) { - // Создаём новый поток по имени функции - new Thread(() -> { - Functions.get(args[0].asString()).execute(); - }).start(); - } - return NumberValue.ZERO; - }); - } - - private static class Rand implements Function { - - private static final Random RND = new Random(); - - @Override - public Value execute(Value... args) { - if (args.length == 0) return new NumberValue(RND.nextDouble()); - - int from = 0; - int to = 100; - if (args.length == 1) { - to = (int) args[0].asNumber(); - } else if (args.length == 2) { - from = (int) args[0].asNumber(); - to = (int) args[1].asNumber(); - } - return new NumberValue(RND.nextInt(to - from) + from); - } + Functions.set("echo", new std_echo()); + Functions.set("newarray", new std_newarray()); + Functions.set("rand", new std_rand()); + Functions.set("sleep", new std_sleep()); + Functions.set("thread", new std_thread()); } } From cf40faa1261025d94a586e4acd9695b9536104f0 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 21:19:30 +0200 Subject: [PATCH 003/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B2=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20math?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/modules/math.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/com/annimon/ownlang/lib/modules/math.java b/src/com/annimon/ownlang/lib/modules/math.java index 26f2503e..270dfc46 100644 --- a/src/com/annimon/ownlang/lib/modules/math.java +++ b/src/com/annimon/ownlang/lib/modules/math.java @@ -16,15 +16,41 @@ public final class math implements Module { @Override public void init() { Functions.set("abs", functionConvert(Math::abs)); + Functions.set("acos", functionConvert(Math::acos)); + Functions.set("asin", functionConvert(Math::asin)); + Functions.set("atan", functionConvert(Math::atan)); + Functions.set("atan2", biFunctionConvert(Math::atan2)); + Functions.set("cbrt", functionConvert(Math::cbrt)); + Functions.set("ceil", functionConvert(Math::ceil)); + Functions.set("copySign", biFunctionConvert(Math::copySign)); Functions.set("cos", functionConvert(Math::cos)); + Functions.set("cosh", functionConvert(Math::cosh)); + Functions.set("exp", functionConvert(Math::exp)); + Functions.set("expm1", functionConvert(Math::expm1)); + Functions.set("floor", functionConvert(Math::floor)); + Functions.set("getExponent", functionConvert(Math::getExponent)); + Functions.set("hypot", biFunctionConvert(Math::hypot)); + Functions.set("IEEEremainder", biFunctionConvert(Math::IEEEremainder)); + Functions.set("log", functionConvert(Math::log)); + Functions.set("log1p", functionConvert(Math::log1p)); + Functions.set("log10", functionConvert(Math::log10)); + Functions.set("max", biFunctionConvert(Math::max)); + Functions.set("min", biFunctionConvert(Math::min)); + Functions.set("nextAfter", biFunctionConvert(Math::nextAfter)); + Functions.set("nextUp", functionConvert(Math::nextUp)); + Functions.set("pow", biFunctionConvert(Math::pow)); + Functions.set("rint", functionConvert(Math::rint)); + Functions.set("round", functionConvert(Math::round)); + Functions.set("signum", functionConvert(Math::signum)); Functions.set("sin", functionConvert(Math::sin)); + Functions.set("sinh", functionConvert(Math::sinh)); Functions.set("sqrt", functionConvert(Math::sqrt)); + Functions.set("tan", functionConvert(Math::tan)); + Functions.set("tanh", functionConvert(Math::tanh)); Functions.set("toDegrees", functionConvert(Math::toDegrees)); Functions.set("toRadians", functionConvert(Math::toRadians)); - - Functions.set("pow", biFunctionConvert(Math::pow)); - Functions.set("atan2", biFunctionConvert(Math::atan2)); - + Functions.set("ulp", functionConvert(Math::ulp)); + Variables.set("PI", new NumberValue(Math.PI)); Variables.set("E", new NumberValue(Math.E)); } From 4004703cb65f96a5c9dc16f4bf1660c8ee54de98 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 21:32:20 +0200 Subject: [PATCH 004/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D1=81=D0=BA=D0=BE=D0=B9=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/UserDefinedFunction.java | 11 ++++++++++- .../ownlang/parser/ast/FunctionalExpression.java | 16 +--------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/com/annimon/ownlang/lib/UserDefinedFunction.java index d8686eee..fabe27b2 100644 --- a/src/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/src/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -28,12 +28,21 @@ public String getArgsName(int index) { } @Override - public Value execute(Value... args) { + public Value execute(Value... values) { + final int size = values.length; + if (size != getArgsCount()) throw new RuntimeException("Args count mismatch"); + try { + Variables.push(); + for (int i = 0; i < size; i++) { + Variables.set(getArgsName(i), values[i]); + } body.execute(); return NumberValue.ZERO; } catch (ReturnStatement rt) { return rt.getResult(); + } finally { + Variables.pop(); } } diff --git a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 554017a8..f86681e1 100644 --- a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -40,21 +40,7 @@ public Value eval() { for (int i = 0; i < size; i++) { values[i] = arguments.get(i).eval(); } - - final Function function = getFunction(name); - if (function instanceof UserDefinedFunction) { - final UserDefinedFunction userFunction = (UserDefinedFunction) function; - if (size != userFunction.getArgsCount()) throw new RuntimeException("Args count mismatch"); - - Variables.push(); - for (int i = 0; i < size; i++) { - Variables.set(userFunction.getArgsName(i), values[i]); - } - final Value result = userFunction.execute(values); - Variables.pop(); - return result; - } - return function.execute(values); + return getFunction(name).execute(values); } private Function getFunction(String key) { From b6d3c30ee44c00bb2159be27d14e39847036fc86 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 21:32:57 +0200 Subject: [PATCH 005/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20foreach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 4 ++- src/com/annimon/ownlang/lib/ArrayValue.java | 10 ++++-- src/com/annimon/ownlang/lib/MapValue.java | 18 ++++++++--- .../lib/modules/functions/std_foreach.java | 32 +++++++++++++++++++ src/com/annimon/ownlang/lib/modules/std.java | 9 ++---- 5 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_foreach.java diff --git a/program.own b/program.own index 25141c6e..b861f63f 100644 --- a/program.own +++ b/program.own @@ -87,4 +87,6 @@ for i = 0, i < 4, i = i + 1 { map = {"+" : add, "-" : sub. "*" : mul, "/" : div} //print map["+"] print "\n" -print function(map["+"], 4, 5) \ No newline at end of file +print function(map["+"], 4, 5) +print "\n" +foreach(map, def(op, func) = echo (4, op, 5, "=", func(4,5))) diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index fbee7296..9a570dfa 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -1,12 +1,13 @@ package com.annimon.ownlang.lib; import java.util.Arrays; +import java.util.Iterator; /** * * @author aNNiMON */ -public final class ArrayValue implements Value { +public final class ArrayValue implements Value, Iterable { private final Value[] elements; @@ -41,6 +42,11 @@ public String asString() { return Arrays.toString(elements); } + @Override + public Iterator iterator() { + return Arrays.asList(elements).iterator(); + } + @Override public int hashCode() { int hash = 5; @@ -57,8 +63,6 @@ public boolean equals(Object obj) { final ArrayValue other = (ArrayValue) obj; return Arrays.deepEquals(this.elements, other.elements); } - - @Override public String toString() { diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index c636a17b..fb547638 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.lib; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Objects; @@ -8,14 +9,18 @@ * * @author aNNiMON */ -public final class MapValue implements Value { +public class MapValue implements Value, Iterable> { private final Map map; public MapValue(int size) { this.map = new HashMap<>(size); } - + + public MapValue(Map map) { + this.map = map; + } + public Value get(Value key) { return map.get(key); } @@ -23,7 +28,7 @@ public Value get(Value key) { public void set(Value key, Value value) { map.put(key, value); } - + @Override public double asNumber() { throw new RuntimeException("Cannot cast map to number"); @@ -34,6 +39,11 @@ public String asString() { return map.toString(); } + @Override + public Iterator> iterator() { + return map.entrySet().iterator(); + } + @Override public int hashCode() { int hash = 5; @@ -51,8 +61,6 @@ public boolean equals(Object obj) { return Objects.equals(this.map, other.map); } - - @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java new file mode 100644 index 00000000..eac4b96f --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java @@ -0,0 +1,32 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class std_foreach implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 2) return NumberValue.ZERO; + + if (!(args[1] instanceof FunctionValue)) return NumberValue.ZERO; + final Function function = ((FunctionValue) args[1]).getValue(); + final Value container = args[0]; + if (container instanceof ArrayValue) { + final ArrayValue array = (ArrayValue) container; + for (Value element : array) { + function.execute(element); + } + return NumberValue.ZERO; + } + if (container instanceof MapValue) { + final MapValue map = (MapValue) container; + for (Map.Entry element : map) { + function.execute(element.getKey(), element.getValue()); + } + return NumberValue.ZERO; + } + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index 3b8c7253..dfa44090 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -1,13 +1,7 @@ package com.annimon.ownlang.lib.modules; import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.lib.modules.functions.std_echo; -import com.annimon.ownlang.lib.modules.functions.std_foreach; -import com.annimon.ownlang.lib.modules.functions.std_newarray; -import com.annimon.ownlang.lib.modules.functions.std_rand; -import com.annimon.ownlang.lib.modules.functions.std_sleep; -import com.annimon.ownlang.lib.modules.functions.std_thread; -import java.util.Random; +import com.annimon.ownlang.lib.modules.functions.*; /** * @@ -18,6 +12,7 @@ public final class std implements Module { @Override public void init() { Functions.set("echo", new std_echo()); + Functions.set("foreach", new std_foreach()); Functions.set("newarray", new std_newarray()); Functions.set("rand", new std_rand()); Functions.set("sleep", new std_sleep()); From 29a80a7bfeb87751a001249991d751b5fd344268 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 21:34:34 +0200 Subject: [PATCH 006/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B4=D0=BE=D0=BF=D1=83=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D1=8B=D0=B5=20=D0=B8=D0=BC=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/parser/Lexer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index ff27bc02..e7b93535 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -44,7 +44,7 @@ public final class Lexer { OPERATORS.put("!=", TokenType.EXCLEQ); OPERATORS.put("<=", TokenType.LTEQ); OPERATORS.put(">=", TokenType.GTEQ); - + OPERATORS.put("&&", TokenType.AMPAMP); OPERATORS.put("||", TokenType.BARBAR); @@ -73,7 +73,7 @@ public List tokenize() { while (pos < length) { final char current = peek(0); if (Character.isDigit(current)) tokenizeNumber(); - else if (Character.isLetter(current)) tokenizeWord(); + else if (Character.isJavaIdentifierStart(current)) tokenizeWord(); else if (current == '"') tokenizeText(); else if (current == '#') { next(); From fd0b8bd8170a8ecd21dd8567c8090b2b3f6657f5 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 21:58:20 +0200 Subject: [PATCH 007/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8=20=D0=BD=D0=B0=20=D1=84?= =?UTF-8?q?=D1=83=D0=BA=D0=BD=D1=86=D0=B8=D1=8E=20::?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + program.own | 1 + src/com/annimon/ownlang/parser/Lexer.java | 2 ++ src/com/annimon/ownlang/parser/Parser.java | 4 +++ src/com/annimon/ownlang/parser/TokenType.java | 3 +- .../ast/FunctionReferenceExpression.java | 31 +++++++++++++++++++ .../annimon/ownlang/parser/ast/Visitor.java | 1 + .../parser/visitors/AbstractVisitor.java | 12 +++++-- 8 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 .gitignore create mode 100644 src/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..838458f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dist/ \ No newline at end of file diff --git a/program.own b/program.own index b861f63f..10fb83bd 100644 --- a/program.own +++ b/program.own @@ -90,3 +90,4 @@ print "\n" print function(map["+"], 4, 5) print "\n" foreach(map, def(op, func) = echo (4, op, 5, "=", func(4,5))) +foreach(arr, ::echo) diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index e7b93535..fd845ced 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -45,6 +45,8 @@ public final class Lexer { OPERATORS.put("<=", TokenType.LTEQ); OPERATORS.put(">=", TokenType.GTEQ); + OPERATORS.put("::", TokenType.COLONCOLON); + OPERATORS.put("&&", TokenType.AMPAMP); OPERATORS.put("||", TokenType.BARBAR); diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index e0096173..9fb830bb 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -426,6 +426,10 @@ private Expression primary() { if (match(TokenType.TEXT)) { return new ValueExpression(current.getText()); } + if (match(TokenType.COLONCOLON)) { + final String functionName = consume(TokenType.WORD).getText(); + return new FunctionReferenceExpression(functionName); + } if (match(TokenType.DEF)) { consume(TokenType.LPAREN); final List argNames = new ArrayList<>(); diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 7d358166..bc2a3922 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -51,7 +51,8 @@ public enum TokenType { QUESTION, // ? COLON, // : - + COLONCOLON, // :: + LPAREN, // ( RPAREN, // ) LBRACKET, // [ diff --git a/src/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java b/src/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java new file mode 100644 index 00000000..c60ea092 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java @@ -0,0 +1,31 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.*; + +/** + * + * @author aNNiMON + */ +public final class FunctionReferenceExpression implements Expression { + + public final String name; + + public FunctionReferenceExpression(String name) { + this.name = name; + } + + @Override + public FunctionValue eval() { + return new FunctionValue(Functions.get(name)); + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return "::" + name; + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 7f63d026..941609d0 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -18,6 +18,7 @@ public interface Visitor { void visit(DoWhileStatement s); void visit(ForStatement s); void visit(FunctionDefineStatement s); + void visit(FunctionReferenceExpression e); void visit(FunctionStatement s); void visit(FunctionalExpression s); void visit(IfStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index ca18f195..a87c0c4a 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.parser.ast.*; +import java.util.Map; + /** * * @author aNNiMON @@ -79,6 +81,10 @@ public void visit(FunctionDefineStatement s) { s.body.accept(this); } + @Override + public void visit(FunctionReferenceExpression e) { + } + @Override public void visit(FunctionStatement s) { s.function.accept(this); @@ -102,9 +108,9 @@ public void visit(IfStatement s) { @Override public void visit(MapExpression s) { - for (Expression key : s.elements.keySet()) { - key.accept(this); - s.elements.get(key).accept(this); + for (Map.Entry entry : s.elements.entrySet()) { + entry.getKey().accept(this); + entry.getValue().accept(this); } } From 09556f7a9afb1ea512d9a2d6b605eca461554b63 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 22:43:39 +0200 Subject: [PATCH 008/448] =?UTF-8?q?=D0=9E=D0=BF=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=20=D0=B2=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=20=D0=B8=20?= =?UTF-8?q?=D1=81=D0=BB=D0=B8=D1=8F=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B0=D1=81?= =?UTF-8?q?=D1=81=D0=B8=D0=B2=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 7 +++ src/com/annimon/ownlang/lib/ArrayValue.java | 18 +++++++ src/com/annimon/ownlang/parser/Parser.java | 4 ++ .../ownlang/parser/ast/BinaryExpression.java | 52 ++++++++++++++----- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/program.own b/program.own index 10fb83bd..4f0656e9 100644 --- a/program.own +++ b/program.own @@ -91,3 +91,10 @@ print function(map["+"], 4, 5) print "\n" foreach(map, def(op, func) = echo (4, op, 5, "=", func(4,5))) foreach(arr, ::echo) + +arr1 = [1,2,3] +arr1 = arr1 :: 4 +arr2 = [5,6,7] +print arr1 +print "\n" +print arr1 << arr2 \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 9a570dfa..9c96199f 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -9,6 +9,24 @@ */ public final class ArrayValue implements Value, Iterable { + public static ArrayValue add(ArrayValue array, Value value) { + final int last = array.elements.length; + final ArrayValue result = new ArrayValue(last + 1); + System.arraycopy(array.elements, 0, result.elements, 0, last); + result.elements[last] = value; + return result; + } + + public static ArrayValue merge(ArrayValue array1, ArrayValue array2) { + final int length1 = array1.elements.length; + final int length2 = array2.elements.length; + final int length = length1 + length2; + final ArrayValue result = new ArrayValue(length); + System.arraycopy(array1.elements, 0, result.elements, 0, length1); + System.arraycopy(array2.elements, 0, result.elements, length1, length2); + return result; + } + private final Value[] elements; public ArrayValue(int size) { diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 9fb830bb..29e06d09 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -356,6 +356,10 @@ private Expression additive() { result = new BinaryExpression(BinaryExpression.Operator.SUBTRACT, result, multiplicative()); continue; } + if (match(TokenType.COLONCOLON)) { + result = new BinaryExpression(BinaryExpression.Operator.PUSH, result, multiplicative()); + continue; + } break; } diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index 6601197f..2e5f8ea0 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -17,6 +17,7 @@ public static enum Operator { MULTIPLY("*"), DIVIDE("/"), REMAINDER("%"), + PUSH("::"), // Bitwise AND("&"), OR("|"), @@ -50,23 +51,46 @@ public BinaryExpression(Operator operation, Expression expr1, Expression expr2) public Value eval() { final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); - if ( (value1 instanceof StringValue) || (value1 instanceof ArrayValue) ) { - final String string1 = value1.asString(); - switch (operation) { - case MULTIPLY: { - 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()); + + if (value1 instanceof StringValue) { + return eval((StringValue) value1, value2); + } + if (value1 instanceof ArrayValue) { + return eval((ArrayValue) value1, value2); + } + return eval(value1, value2); + } + + private Value eval(StringValue value1, Value value2) { + final String string1 = value1.asString(); + switch (operation) { + case MULTIPLY: { + final int iterations = (int) value2.asNumber(); + final StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < iterations; i++) { + buffer.append(string1); } - case ADD: - default: - return new StringValue(string1 + value2.asString()); + return new StringValue(buffer.toString()); } + case ADD: + default: + return new StringValue(string1 + value2.asString()); } - + } + + private Value eval(ArrayValue value1, Value value2) { + switch (operation) { + case LSHIFT: + if (!(value2 instanceof ArrayValue)) + throw new RuntimeException("Cannot merge non array value to array"); + return ArrayValue.merge(value1, (ArrayValue) value2); + case PUSH: + default: + return ArrayValue.add(value1, value2); + } + } + + private Value eval(Value value1, Value value2) { final double number1 = value1.asNumber(); final double number2 = value2.asNumber(); double result; From fa2295ad1804bc330f269bdd2f08878415b7870d Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Jan 2016 22:58:39 +0200 Subject: [PATCH 009/448] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B4=D0=BB=D1=8F=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 5 ++++- .../ownlang/parser/ast/ArrayAccessExpression.java | 2 +- .../ownlang/parser/ast/ArrayAssignmentStatement.java | 11 ++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/program.own b/program.own index 4f0656e9..e87bb273 100644 --- a/program.own +++ b/program.own @@ -85,6 +85,8 @@ for i = 0, i < 4, i = i + 1 { // map map = {"+" : add, "-" : sub. "*" : mul, "/" : div} +map["%"] = def(x,y) = x % y +map["pow"] = def(x,y) = pow(x, y) //print map["+"] print "\n" print function(map["+"], 4, 5) @@ -97,4 +99,5 @@ arr1 = arr1 :: 4 arr2 = [5,6,7] print arr1 print "\n" -print arr1 << arr2 \ No newline at end of file +print arr1 << arr2 + diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java index a4511a83..4db62d00 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java @@ -54,7 +54,7 @@ private ArrayValue consumeArray(Value value) { } } - private MapValue consumeMap(Value value) { + public MapValue consumeMap(Value value) { if (value instanceof MapValue) { return (MapValue) value; } else { diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java index 14f4439e..5a41a8a1 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java @@ -1,5 +1,9 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; + /** * * @author aNNiMON @@ -16,7 +20,12 @@ public ArrayAssignmentStatement(ArrayAccessExpression array, Expression expressi @Override public void execute() { - array.getArray().set(array.lastIndex(), expression.eval()); + final Value container = Variables.get(array.variable); + if (container instanceof ArrayValue) { + array.getArray().set(array.lastIndex(), expression.eval()); + return; + } + array.consumeMap(container).set(array.indices.get(0).eval(), expression.eval()); } @Override From a17bef18c5768afe13744d9e8abc3ac23d464e5d Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 13:13:04 +0200 Subject: [PATCH 010/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20foreach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 10 ++++ src/com/annimon/ownlang/parser/Parser.java | 37 +++++++++++- .../parser/ast/ForeachArrayStatement.java | 51 ++++++++++++++++ .../parser/ast/ForeachMapStatement.java | 58 +++++++++++++++++++ .../annimon/ownlang/parser/ast/Visitor.java | 2 + .../parser/visitors/AbstractVisitor.java | 12 ++++ 6 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java create mode 100644 src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java diff --git a/program.own b/program.own index e87bb273..cdf16cba 100644 --- a/program.own +++ b/program.own @@ -101,3 +101,13 @@ print arr1 print "\n" print arr1 << arr2 +for op, func : map { + echo (4, op, 5, "=", func(4,5)) +} + +for v : arr1 print "" + v + ", " +print "\n" +for (v : arr1 << arr2) print "" + v + ", " +print "\n" +for v : [1,2,3,4,5,6,7,8,9] print "" + v + ", " + diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 29e06d09..eb01a7b3 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -124,17 +124,50 @@ private Statement doWhileStatement() { } private Statement forStatement() { - match(TokenType.LPAREN); // необязательные скобки + int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0; + if (lookMatch(foreachIndex, TokenType.WORD) && lookMatch(foreachIndex + 1, TokenType.COLON)) { + // for v : arr || for (v : arr) + return foreachArrayStatement(); + } + if (lookMatch(foreachIndex, TokenType.WORD) && lookMatch(foreachIndex + 1, TokenType.COMMA) + && lookMatch(foreachIndex + 2, TokenType.WORD) && lookMatch(foreachIndex + 3, TokenType.COLON)) { + // for key, value : arr || for (key, value : arr) + return foreachMapStatement(); + } + + boolean openParen = match(TokenType.LPAREN); // необязательные скобки final Statement initialization = assignmentStatement(); consume(TokenType.COMMA); final Expression termination = expression(); consume(TokenType.COMMA); final Statement increment = assignmentStatement(); - match(TokenType.RPAREN); // необязательные скобки + if (openParen) consume(TokenType.RPAREN); // скобки final Statement statement = statementOrBlock(); return new ForStatement(initialization, termination, increment, statement); } + private ForeachArrayStatement foreachArrayStatement() { + boolean openParen = match(TokenType.LPAREN); // необязательные скобки + final String variable = consume(TokenType.WORD).getText(); + consume(TokenType.COLON); + final Expression container = expression(); + if (openParen) consume(TokenType.RPAREN); // скобки + final Statement statement = statementOrBlock(); + return new ForeachArrayStatement(variable, container, statement); + } + + private ForeachMapStatement foreachMapStatement() { + boolean openParen = match(TokenType.LPAREN); // необязательные скобки + final String key = consume(TokenType.WORD).getText(); + consume(TokenType.COMMA); + final String value = consume(TokenType.WORD).getText(); + consume(TokenType.COLON); + final Expression container = expression(); + if (openParen) consume(TokenType.RPAREN); // скобки + final Statement statement = statementOrBlock(); + return new ForeachMapStatement(key, value, container, statement); + } + private FunctionDefineStatement functionDefine() { final String name = consume(TokenType.WORD).getText(); consume(TokenType.LPAREN); diff --git a/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java new file mode 100644 index 00000000..c80e009a --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -0,0 +1,51 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; + +/** + * + * @author aNNiMON + */ +public final class ForeachArrayStatement implements Statement { + + public final String variable; + public final Expression container; + public final Statement body; + + public ForeachArrayStatement(String variable, Expression container, Statement body) { + this.variable = variable; + this.container = container; + this.body = body; + } + + @Override + public void execute() { + final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; + final Iterable iterator = (Iterable) container.eval(); + for (Value value : iterator) { + Variables.set(variable, value); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } + } + // Восстанавливаем переменную + if (previousVariableValue != null) { + Variables.set(variable, previousVariableValue); + } + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return String.format("for %s : %s %s", variable, container, body); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java new file mode 100644 index 00000000..ee9bc4ba --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -0,0 +1,58 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class ForeachMapStatement implements Statement { + + public final String key, value; + public final Expression container; + public final Statement body; + + public ForeachMapStatement(String key, String value, Expression container, Statement body) { + this.key = key; + this.value = value; + this.container = container; + this.body = body; + } + + @Override + public void execute() { + final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; + final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; + final Iterable> iterator = (Iterable>) container.eval(); + for (Map.Entry entry : iterator) { + Variables.set(key, entry.getKey()); + Variables.set(value, entry.getValue()); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } + } + // Восстанавливаем переменные + if (previousVariableValue1 != null) { + Variables.set(key, previousVariableValue1); + } + if (previousVariableValue2 != null) { + Variables.set(value, previousVariableValue2); + } + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return String.format("for %s, %s : %s %s", key, value, container, body); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 941609d0..8e80eafe 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -17,6 +17,8 @@ public interface Visitor { void visit(ContinueStatement s); void visit(DoWhileStatement s); void visit(ForStatement s); + void visit(ForeachArrayStatement s); + void visit(ForeachMapStatement s); void visit(FunctionDefineStatement s); void visit(FunctionReferenceExpression e); void visit(FunctionStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index a87c0c4a..fc936f4d 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -75,6 +75,18 @@ public void visit(ForStatement s) { s.increment.accept(this); s.statement.accept(this); } + + @Override + public void visit(ForeachArrayStatement s) { + s.container.accept(this); + s.body.accept(this); + } + + @Override + public void visit(ForeachMapStatement s) { + s.container.accept(this); + s.body.accept(this); + } @Override public void visit(FunctionDefineStatement s) { From 87637951a8cec5cb02e2f30e78ab7c0db315421b Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 13:51:44 +0200 Subject: [PATCH 011/448] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D0=BE=20=D1=82=D0=B8=D0=BF=D0=B0=D0=BC=20?= =?UTF-8?q?=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20instanceof?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/ArrayValue.java | 5 +++++ src/com/annimon/ownlang/lib/FunctionValue.java | 5 +++++ src/com/annimon/ownlang/lib/MapValue.java | 5 +++++ src/com/annimon/ownlang/lib/NumberValue.java | 5 +++++ src/com/annimon/ownlang/lib/StringValue.java | 5 +++++ src/com/annimon/ownlang/lib/Types.java | 12 ++++++++++++ src/com/annimon/ownlang/lib/Value.java | 2 ++ .../parser/ast/ArrayAccessExpression.java | 13 ++++++------- .../parser/ast/ArrayAssignmentStatement.java | 4 ++-- .../ownlang/parser/ast/BinaryExpression.java | 17 ++++++++++------- .../parser/ast/ConditionalExpression.java | 3 ++- .../parser/ast/FunctionalExpression.java | 7 ++++--- 12 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/Types.java diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 9c96199f..427c17d7 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -42,6 +42,11 @@ public ArrayValue(ArrayValue array) { this(array.elements); } + @Override + public int type() { + return Types.ARRAY; + } + public Value get(int index) { return elements[index]; } diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index 29c77906..ac2b49fc 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -14,6 +14,11 @@ public FunctionValue(Function value) { this.value = value; } + @Override + public int type() { + return Types.FUNCTION; + } + @Override public double asNumber() { throw new RuntimeException("Cannot cast function to number"); diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index fb547638..1c77a978 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -20,6 +20,11 @@ public MapValue(int size) { public MapValue(Map map) { this.map = map; } + + @Override + public int type() { + return Types.MAP; + } public Value get(Value key) { return map.get(key); diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/com/annimon/ownlang/lib/NumberValue.java index 0c522db5..5744790f 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -19,6 +19,11 @@ public NumberValue(double value) { this.value = value; } + @Override + public int type() { + return Types.NUMBER; + } + @Override public double asNumber() { return value; diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index 7b75ad0b..59fed483 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -14,6 +14,11 @@ public StringValue(String value) { this.value = value; } + @Override + public int type() { + return Types.STRING; + } + @Override public double asNumber() { try { diff --git a/src/com/annimon/ownlang/lib/Types.java b/src/com/annimon/ownlang/lib/Types.java new file mode 100644 index 00000000..246b1c69 --- /dev/null +++ b/src/com/annimon/ownlang/lib/Types.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.lib; + +public final class Types { + + public static final int + OBJECT = 0, + NUMBER = 1, + STRING = 2, + ARRAY = 3, + MAP = 4, + FUNCTION = 5; +} diff --git a/src/com/annimon/ownlang/lib/Value.java b/src/com/annimon/ownlang/lib/Value.java index 9e8ea097..7f09b5f8 100644 --- a/src/com/annimon/ownlang/lib/Value.java +++ b/src/com/annimon/ownlang/lib/Value.java @@ -9,4 +9,6 @@ public interface Value { double asNumber(); String asString(); + + int type(); } diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java index 4db62d00..45f65c40 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java @@ -2,6 +2,7 @@ import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import java.util.List; @@ -23,7 +24,7 @@ public ArrayAccessExpression(String variable, List indices) { @Override public Value eval() { Value container = Variables.get(variable); - if (container instanceof ArrayValue) { + if (container.type() == Types.ARRAY) { return getArray().get(lastIndex()); } return consumeMap(container).get(indices.get(0).eval()); @@ -47,19 +48,17 @@ private int index(int index) { } private ArrayValue consumeArray(Value value) { - if (value instanceof ArrayValue) { - return (ArrayValue) value; - } else { + if (value.type() != Types.ARRAY) { throw new RuntimeException("Array expected"); } + return (ArrayValue) value; } public MapValue consumeMap(Value value) { - if (value instanceof MapValue) { - return (MapValue) value; - } else { + if (value.type() != Types.MAP) { throw new RuntimeException("Map expected"); } + return (MapValue) value; } @Override diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java index 5a41a8a1..b2222ac2 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; @@ -21,7 +21,7 @@ public ArrayAssignmentStatement(ArrayAccessExpression array, Expression expressi @Override public void execute() { final Value container = Variables.get(array.variable); - if (container instanceof ArrayValue) { + if (container.type() == Types.ARRAY) { array.getArray().set(array.lastIndex(), expression.eval()); return; } diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index 2e5f8ea0..985e6b85 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.lib.ArrayValue; 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,13 +53,15 @@ public Value eval() { final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); - if (value1 instanceof StringValue) { - return eval((StringValue) value1, value2); - } - if (value1 instanceof ArrayValue) { - return eval((ArrayValue) value1, value2); + switch (value1.type()) { + case Types.STRING: + return eval((StringValue) value1, value2); + case Types.ARRAY: + return eval((ArrayValue) value1, value2); + case Types.NUMBER: + default: + return eval(value1, value2); } - return eval(value1, value2); } private Value eval(StringValue value1, Value value2) { @@ -81,7 +84,7 @@ private Value eval(StringValue value1, Value value2) { private Value eval(ArrayValue value1, Value value2) { switch (operation) { case LSHIFT: - if (!(value2 instanceof ArrayValue)) + if (value2.type() != Types.ARRAY) throw new RuntimeException("Cannot merge non array value to array"); return ArrayValue.merge(value1, (ArrayValue) value2); case PUSH: diff --git a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java index c1838061..5e8610b9 100644 --- a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -2,6 +2,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; /** @@ -56,7 +57,7 @@ public Value eval() { final Value value2 = expr2.eval(); double number1, number2; - if (value1 instanceof StringValue) { + if (value1.type() == Types.STRING) { number1 = value1.asString().compareTo(value2.asString()); number2 = 0; } else { diff --git a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java index f86681e1..800a2d2b 100644 --- a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -3,8 +3,7 @@ import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.Functions; -import static com.annimon.ownlang.lib.Functions.isExists; -import com.annimon.ownlang.lib.UserDefinedFunction; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import java.util.ArrayList; @@ -47,7 +46,9 @@ private Function getFunction(String key) { if (Functions.isExists(key)) return Functions.get(key); if (Variables.isExists(key)) { final Value value = Variables.get(key); - if (value instanceof FunctionValue) return ((FunctionValue)value).getValue(); + if (value.type() == Types.FUNCTION) { + return ((FunctionValue)value).getValue(); + } } throw new RuntimeException("Unknown function " + key); } From 556a0be4c260aa3071a76447ae1cabbc69b85f0f Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 14:00:07 +0200 Subject: [PATCH 012/448] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20?= =?UTF-8?q?types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 16 +++++++++++++ .../annimon/ownlang/lib/modules/types.java | 24 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/com/annimon/ownlang/lib/modules/types.java diff --git a/program.own b/program.own index cdf16cba..0cda45e4 100644 --- a/program.own +++ b/program.own @@ -111,3 +111,19 @@ for (v : arr1 << arr2) print "" + v + ", " print "\n" for v : [1,2,3,4,5,6,7,8,9] print "" + v + ", " +use "types" +print "\n" +print typeof(1) +print "\n" +print typeof("1") +print "\n" +print typeof(arr1) +print "\n" +print typeof({}) +print "\n" +print typeof(add) + +print "\n" +print typeof(number("1")) +print "\n" +print typeof(string(1)) \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/types.java b/src/com/annimon/ownlang/lib/modules/types.java new file mode 100644 index 00000000..e82d2a29 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/types.java @@ -0,0 +1,24 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; + +/** + * + * @author aNNiMON + */ +public final class types implements Module { + + @Override + public void init() { + Variables.set("OBJECT", new NumberValue(Types.OBJECT)); + Variables.set("NUMBER", new NumberValue(Types.NUMBER)); + Variables.set("STRING", new NumberValue(Types.STRING)); + Variables.set("ARRAY", new NumberValue(Types.ARRAY)); + Variables.set("MAP", new NumberValue(Types.MAP)); + Variables.set("FUNCTION", new NumberValue(Types.FUNCTION)); + + 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())); + } +} From 1f6a17a024c23706015870308c90ca69b95db674 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 14:11:15 +0200 Subject: [PATCH 013/448] =?UTF-8?q?=D0=9E=D0=BF=D0=B5=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=20println?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 79 ++++++++----------- src/com/annimon/ownlang/Main.java | 2 +- src/com/annimon/ownlang/parser/Lexer.java | 1 + src/com/annimon/ownlang/parser/Parser.java | 3 + src/com/annimon/ownlang/parser/TokenType.java | 1 + .../ownlang/parser/ast/PrintlnStatement.java | 29 +++++++ .../annimon/ownlang/parser/ast/Visitor.java | 1 + .../parser/visitors/AbstractVisitor.java | 5 ++ 8 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 src/com/annimon/ownlang/parser/ast/PrintlnStatement.java diff --git a/program.own b/program.own index 0cda45e4..8d2c5c5f 100644 --- a/program.own +++ b/program.own @@ -3,32 +3,30 @@ use "std" word = 2 + 2 word2 = PI + word str = "a" * 5 + "ba" * 7 + "\n" -print str * "3" * 2 -print "word = " + word + "\n" -print "word2 = " + word2 + "\n" -print "1" > "abc" -print "\n" -if (1 <= 2) print "1 = 1" -else print "1 != 1" -print "\n" +println str * "3" * 2 +println "word = " + word +println "word2 = " + word2 +println "1" > "abc" +if (1 <= 2) println "1 = 1" +else println "1 != 1" if (40 < 50 || 50 > 60) { - print "true1\n" - print "true2\n" + println "true1" + println "true2" i = 0 - print "do while" + println "do while" do { - print "i = " + i + "\n" + println "i = " + i i = i + 1 } while (i < 10) i = 0 - print "while" + println "while" while (i < 10) { - print "i = " + i + sin(i) + "\n" + println "i = " + i + sin(i) i = i + 1 } - print "for" + println "for" for i = 0, i < 10, i = i + 1 { - print "i = " + i + "\n" + println "i = " + i } } else print "false" @@ -53,16 +51,16 @@ print sum(10, 15) print a + "\n" arr = [1, "text", sum(10, 15), [], ["text", [90, [7 + 6, [50]]]]] -print arr + "\n" +println arr + "\n" arr[0] = arr[0] + 1000 + arr[2] print arr + "\n" print arr[4][1] + "\n" arr[4][1] = "text" -print arr[4][1] + "\n" +println arr[4][1] print "\n\n" array = newarray(2, 2, 2, 2) -print array +println array add = def(a,b) = a+b sub = def(a,b) = a-b @@ -70,36 +68,30 @@ mul = def(a,b) = a*b div = def(a,b) = a/b cube = def(x) = x*mul(x, x) print "\n\n" -print mul(8, 5) -print "\n" -print cube(2) +println mul(8, 5) +println cube(2) functions = [add, sub, mul, div] def function(f, a, b) = f(a, b) for i = 0, i < 4, i = i + 1 { - print "\n" - print functions[i] - print "\n" - print function(functions[i], 6, 3) + println functions[i] + println function(functions[i], 6, 3) } // map map = {"+" : add, "-" : sub. "*" : mul, "/" : div} map["%"] = def(x,y) = x % y map["pow"] = def(x,y) = pow(x, y) -//print map["+"] -print "\n" -print function(map["+"], 4, 5) -print "\n" +println map["+"] +println function(map["+"], 4, 5) foreach(map, def(op, func) = echo (4, op, 5, "=", func(4,5))) foreach(arr, ::echo) arr1 = [1,2,3] arr1 = arr1 :: 4 arr2 = [5,6,7] -print arr1 -print "\n" -print arr1 << arr2 +println arr1 +println arr1 << arr2 for op, func : map { echo (4, op, 5, "=", func(4,5)) @@ -112,18 +104,11 @@ print "\n" for v : [1,2,3,4,5,6,7,8,9] print "" + v + ", " use "types" -print "\n" -print typeof(1) -print "\n" -print typeof("1") -print "\n" -print typeof(arr1) -print "\n" -print typeof({}) -print "\n" -print typeof(add) +println typeof(1) +println typeof("1") +println typeof(arr1) +println typeof({}) +println typeof(add) -print "\n" -print typeof(number("1")) -print "\n" -print typeof(string(1)) \ No newline at end of file +println typeof(number("1")) +println typeof(string(1)) \ No newline at end of file diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index e964fdb6..439582ff 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -31,7 +31,7 @@ public static void main(String[] args) throws IOException { final Statement program = new Parser(tokens).parse(); System.out.println(program.toString()); program.accept(new FunctionAdder()); - program.accept(new VariablePrinter()); +// program.accept(new VariablePrinter()); program.accept(new AssignValidator()); program.execute(); } diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index fd845ced..2005dc3e 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -161,6 +161,7 @@ private void tokenizeWord() { final String word = buffer.toString(); switch (word) { case "print": addToken(TokenType.PRINT); break; + case "println": addToken(TokenType.PRINTLN); break; case "if": addToken(TokenType.IF); break; case "else": addToken(TokenType.ELSE); break; case "while": addToken(TokenType.WHILE); break; diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index eb01a7b3..4d6403ad 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -51,6 +51,9 @@ private Statement statement() { if (match(TokenType.PRINT)) { return new PrintStatement(expression()); } + if (match(TokenType.PRINTLN)) { + return new PrintlnStatement(expression()); + } if (match(TokenType.IF)) { return ifElse(); } diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index bc2a3922..adc344a9 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -13,6 +13,7 @@ public enum TokenType { // keyword PRINT, + PRINTLN, IF, ELSE, WHILE, diff --git a/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java b/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java new file mode 100644 index 00000000..ced55fd3 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.parser.ast; + +/** + * + * @author aNNiMON + */ +public final class PrintlnStatement implements Statement { + + public final Expression expression; + + public PrintlnStatement(Expression expression) { + this.expression = expression; + } + + @Override + public void execute() { + System.out.println(expression.eval()); + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return "println " + expression; + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 8e80eafe..a09cf4f1 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -26,6 +26,7 @@ public interface Visitor { void visit(IfStatement s); void visit(MapExpression s); void visit(PrintStatement s); + void visit(PrintlnStatement s); void visit(ReturnStatement s); void visit(TernaryExpression s); void visit(UnaryExpression s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index fc936f4d..8572117f 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -130,6 +130,11 @@ public void visit(MapExpression s) { public void visit(PrintStatement s) { s.expression.accept(this); } + + @Override + public void visit(PrintlnStatement s) { + s.expression.accept(this); + } @Override public void visit(ReturnStatement s) { From 1f1a5ed7f0be78df9f7d94bf53d8914a4da8d63b Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 14:54:06 +0200 Subject: [PATCH 014/448] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=B0=D1=8F=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20thread?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 7 +++++- .../lib/modules/functions/std_thread.java | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/program.own b/program.own index 8d2c5c5f..9aac7997 100644 --- a/program.own +++ b/program.own @@ -111,4 +111,9 @@ println typeof({}) println typeof(add) println typeof(number("1")) -println typeof(string(1)) \ No newline at end of file +println typeof(string(1)) + +thread(::inthread) +def inthread() = echo("this is a thread") +thread(def (str) { println str }, "this is a thread with arguments") + diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java index 9eea5765..1025e75e 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java @@ -1,20 +1,34 @@ package com.annimon.ownlang.lib.modules.functions; import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; public final class std_thread implements Function { @Override public Value execute(Value... args) { - if (args.length == 1) { - // Создаём новый поток по имени функции - new Thread(() -> { - Functions.get(args[0].asString()).execute(); - }).start(); + // Создаём новый поток и передаём параметры, если есть. + // Функция может передаваться как напрямую, так и по имени + if (args.length == 0) throw new RuntimeException("At least one arg expected"); + + Function body; + if (args[0].type() == Types.FUNCTION) { + body = ((FunctionValue) args[0]).getValue(); + } else { + body = Functions.get(args[0].asString()); } + + // Сдвигаем аргументы + final Value[] params = new Value[args.length - 1]; + if (params.length > 0) { + System.arraycopy(args, 1, params, 0, params.length); + } + + new Thread(() -> body.execute(params)).start(); return NumberValue.ZERO; } } \ No newline at end of file From 1fd2dd8e87f166831a15947c25f8443b6269bc80 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 15:06:25 +0200 Subject: [PATCH 015/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2=20=D0=B2=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20foreach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/modules/functions/std_foreach.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java index eac4b96f..dfb7493b 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java @@ -10,17 +10,17 @@ public final class std_foreach implements Function { public Value execute(Value... args) { if (args.length != 2) return NumberValue.ZERO; - if (!(args[1] instanceof FunctionValue)) return NumberValue.ZERO; + if (args[1].type() != Types.FUNCTION) return NumberValue.ZERO; final Function function = ((FunctionValue) args[1]).getValue(); final Value container = args[0]; - if (container instanceof ArrayValue) { + if (container.type() == Types.ARRAY) { final ArrayValue array = (ArrayValue) container; for (Value element : array) { function.execute(element); } return NumberValue.ZERO; } - if (container instanceof MapValue) { + if (container.type() == Types.MAP) { final MapValue map = (MapValue) container; for (Map.Entry element : map) { function.execute(element.getKey(), element.getValue()); From 89d48f77372f0b0abc6c2b20a749fece4193c8d0 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jan 2016 21:21:17 +0200 Subject: [PATCH 016/448] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20?= =?UTF-8?q?functional=20=D1=81=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=BC=D0=B8=20=D0=BE?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=B0=D0=BC=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 8 +++ src/com/annimon/ownlang/lib/ArrayValue.java | 4 ++ src/com/annimon/ownlang/lib/MapValue.java | 4 ++ .../ownlang/lib/modules/functional.java | 19 +++++++ .../modules/functions/functional_filter.java | 52 ++++++++++++++++++ .../modules/functions/functional_foreach.java | 34 ++++++++++++ .../lib/modules/functions/functional_map.java | 55 +++++++++++++++++++ .../modules/functions/functional_reduce.java | 37 +++++++++++++ src/com/annimon/ownlang/lib/modules/std.java | 1 - 9 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functional.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_filter.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_map.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java diff --git a/program.own b/program.own index 9aac7997..859a3c33 100644 --- a/program.own +++ b/program.own @@ -1,5 +1,7 @@ use "math" use "std" +use "functional" + word = 2 + 2 word2 = PI + word str = "a" * 5 + "ba" * 7 + "\n" @@ -117,3 +119,9 @@ thread(::inthread) def inthread() = echo("this is a thread") thread(def (str) { println str }, "this is a thread with arguments") +println "functional" +nums = [1,2,3,4,5,6,7,8,9,10] +nums = filter(nums, def(x) = x % 2 == 0) +squares = map(nums, def(x) = x * x) +foreach(squares, ::echo) +println "Sum: " + reduce(squares, 0, def(x, y) = x + y) \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 427c17d7..e34b8ce6 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -47,6 +47,10 @@ public int type() { return Types.ARRAY; } + public int size() { + return elements.length; + } + public Value get(int index) { return elements[index]; } diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index 1c77a978..c55bd947 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -25,6 +25,10 @@ public MapValue(Map map) { public int type() { return Types.MAP; } + + public int size() { + return map.size(); + } public Value get(Value key) { return map.get(key); diff --git a/src/com/annimon/ownlang/lib/modules/functional.java b/src/com/annimon/ownlang/lib/modules/functional.java new file mode 100644 index 00000000..e5bc229a --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functional.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.*; + +/** + * + * @author aNNiMON + */ +public final class functional implements Module { + + @Override + public void init() { + Functions.set("foreach", new functional_foreach()); + Functions.set("map", new functional_map()); + Functions.set("reduce", new functional_reduce()); + Functions.set("filter", new functional_filter()); + } +} diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java b/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java new file mode 100644 index 00000000..c1f7ed9b --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.List; + +import java.util.Map; + +public final class functional_filter implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2) throw new RuntimeException("At least two args expected"); + + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Value container = args[0]; + final Function consumer = ((FunctionValue) args[1]).getValue(); + if (container.type() == Types.ARRAY) { + return filterArray((ArrayValue) container, consumer); + } + + if (container.type() == Types.MAP) { + return filterMap((MapValue) container, consumer); + } + + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } + + private Value filterArray(ArrayValue array, Function predicate) { + final int size = array.size(); + final List values = new ArrayList(size); + for (Value value : array) { + if (predicate.execute(value) != NumberValue.ZERO) { + values.add(value); + } + } + final int newSize = values.size(); + return new ArrayValue(values.toArray(new Value[newSize])); + } + + private Value filterMap(MapValue map, Function predicate) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { + result.set(element.getKey(), element.getValue()); + } + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java new file mode 100644 index 00000000..bf9bf337 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java @@ -0,0 +1,34 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_foreach implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 2) throw new RuntimeException("Two args expected"); + + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Value container = args[0]; + final Function consumer = ((FunctionValue) args[1]).getValue(); + if (container.type() == Types.ARRAY) { + final ArrayValue array = (ArrayValue) container; + for (Value element : array) { + consumer.execute(element); + } + return NumberValue.ZERO; + } + if (container.type() == Types.MAP) { + final MapValue map = (MapValue) container; + for (Map.Entry element : map) { + consumer.execute(element.getKey(), element.getValue()); + } + return NumberValue.ZERO; + } + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_map.java b/src/com/annimon/ownlang/lib/modules/functions/functional_map.java new file mode 100644 index 00000000..980106c4 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_map.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_map implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2) throw new RuntimeException("At least two args expected"); + + final Value container = args[0]; + if (container.type() == Types.ARRAY) { + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Function mapper = ((FunctionValue) args[1]).getValue(); + return mapArray((ArrayValue) container, mapper); + } + + if (container.type() == Types.MAP) { + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + if (args[2].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in third arg"); + } + final Function keyMapper = ((FunctionValue) args[1]).getValue(); + final Function valueMapper = ((FunctionValue) args[2]).getValue(); + return mapMap((MapValue) container, keyMapper, valueMapper); + } + + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } + + private Value mapArray(ArrayValue array, Function mapper) { + final int size = array.size(); + final ArrayValue result = new ArrayValue(size); + for (int i = 0; i < size; i++) { + result.set(i, mapper.execute(array.get(i))); + } + return result; + } + + private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + final Value newKey = keyMapper.execute(element.getKey()); + final Value newValue = valueMapper.execute(element.getValue()); + result.set(newKey, newValue); + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java b/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java new file mode 100644 index 00000000..7a886ebc --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java @@ -0,0 +1,37 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_reduce implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 3) throw new RuntimeException("Three args expected"); + + if (args[2].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in third arg"); + } + final Value container = args[0]; + final Value identity = args[1]; + final Function accumulator = ((FunctionValue) args[2]).getValue(); + if (container.type() == Types.ARRAY) { + Value result = identity; + final ArrayValue array = (ArrayValue) container; + for (Value element : array) { + result = accumulator.execute(result, element); + } + return result; + } + if (container.type() == Types.MAP) { + Value result = identity; + final MapValue map = (MapValue) container; + for (Map.Entry element : map) { + result = accumulator.execute(result, element.getKey(), element.getValue()); + } + return result; + } + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index dfa44090..39196987 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -12,7 +12,6 @@ public final class std implements Module { @Override public void init() { Functions.set("echo", new std_echo()); - Functions.set("foreach", new std_foreach()); Functions.set("newarray", new std_newarray()); Functions.set("rand", new std_rand()); Functions.set("sleep", new std_sleep()); From 28c7b38025c4d474ea468e834c7ce60d829a9f07 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 12 Jan 2016 21:14:05 +0200 Subject: [PATCH 017/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20http?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/commons-codec-1.6.jar | Bin 0 -> 232771 bytes libs/commons-logging-1.1.3.jar | Bin 0 -> 62050 bytes libs/httpclient-4.3.5.jar | Bin 0 -> 590533 bytes libs/httpcore-4.3.2.jar | Bin 0 -> 282269 bytes program.own | 13 +- .../annimon/ownlang/lib/FunctionValue.java | 2 + src/com/annimon/ownlang/lib/MapValue.java | 6 + src/com/annimon/ownlang/lib/StringValue.java | 4 + .../lib/modules/functions/http_http.java | 173 ++++++++++++++++++ .../lib/modules/functions/http_urlencode.java | 27 +++ src/com/annimon/ownlang/lib/modules/http.java | 17 ++ 11 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 libs/commons-codec-1.6.jar create mode 100644 libs/commons-logging-1.1.3.jar create mode 100644 libs/httpclient-4.3.5.jar create mode 100644 libs/httpcore-4.3.2.jar create mode 100644 src/com/annimon/ownlang/lib/modules/functions/http_http.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java create mode 100644 src/com/annimon/ownlang/lib/modules/http.java diff --git a/libs/commons-codec-1.6.jar b/libs/commons-codec-1.6.jar new file mode 100644 index 0000000000000000000000000000000000000000..ee1bc49acae11cc79eceec51f7be785590e99fd8 GIT binary patch literal 232771 zcmbTe1C;2{mgik{%eHOXw(YuQ+qP}nwr$(?E!%d@ece4h^Ll2zujk9kO0tr@b52hF z>tyfWNg^)=1PlrAkEy;YfbZWn{$m00cP%5LEI=b6D@rH-A7@M60zn9X3S~y-iWR0h3Jwi^^UROSC#u4wRGBB_|XcK8^$W z5W*S3^X%%2zTU5Jw?IKK8J9uctjeoL>@~J9tO^*(2#4ncND9Zq_$_|9snjocP-G>Uh7| zecZMBlKmw7__VlgMnat!oLIR&UmN(ox%7HpSn1lf@??4PK@IJEqi4@5qz(Ll?LFDN ziv@*}uW!EGq8?3diNvlaW`cjLg6oLylrT8f_?sqSuhukSw^h1aVOct(LCyL;b~CtK zd6pe^sGckRT=Wqc@WIb$Cz0jY`TQKgbvhpD-Z<$nIzRx(5H{5k*Y67uwAKz|04gSsmfE|?lLGClp#Qr3r z2BfS8<8w+xYs0cb2C{U<6Y1aSfrOhGejk<65*=2wv)Oj^jheJv>aVu76;_<1#?EqMN10f72v!(=vm533#A$4##6t2 zt2gM4;D*3k@@$y2_IlHq`Kmz6m%9x;<#1!vS?+yQvOgWWaLIOe8Bp%)64-358`|Q@ z{^}Qqd`aEVm?$0$?0FAI=vM!rj9md%`jmJ#NY&LojBXcn1!``T>+d44ovf#W3_Oqs z7mj=NirlaD-2H16F4iUI%|G{zP@AnMysNj@t43Q>hIqL9ejY0fBOmP$Y|jg!Tr1C^ z-Z}z{s`LKXBE%MV28)fy+xdV!Jg2u~4@%Y=J>$iE-#YG#lwy7X1`fs!lAqsmm_%_a zsN@FFlobsaI14gS?u6tHC~bJSsQH8o3?EQTz*q@!kKuN45%Z0jL2K4;(GL^eLs}1& zZmM*EInNQf?%BS{o|le6&riwl{w!-qh%g5=i;oPN5rs5Y78jR5e1DRfLkF@7u$+Bh z9E|;fRYTcqy1al@c{f?BiYv{27V<(BSMU zh*?~|nDRavG*$$Fgt@`Tj?v?>0>Ko=eAq8jaUn*4Z@+*y325Y70F zTD;Y_0HL|EQid#7G(%*gZTbn)P#>f>G=pR@NzVNwg~wg(9zCK%Z&Pwb#qNpssQZbA zI>)8+0~!);HwdPzIKaZ$eK<7+D&@;+5u6iQF~7()*||BFPn2PN+i0cBn)>{J&fPPN z6Tk?oi}-D6^s`Lo%>2u1%rqnmQ!HGtv{u=D)wP)45beZ|B|S7lVo7xozZMVKWR?bB z4C>~Or7p>&G1%T@@B)l=NE0w7Q_+1zqcf8@GT`iZ6UVYfCgrqlh(;_Xw8CcH?l{hJ zNsVa-O$#2eU*xEaV~)wrIkPFbkKQpE)D({yy+|i!?0=dRkIj0BN213IlvvX@SHuu1 zvXwHZXP72Y;q#(WA!Q!T(k+WqTp8R#eKcC*zcNr7kr!h zXH?M@fEJ!)Lm-s6NxYX@%Bbo;z6xyv&Bb))wgUztjx2{eRp6gP==&P3WsyUld5LEt zYW(cFM|`G5D#wv0f8-=4Da$wTD#MpWpxBA7)`K>Ph3}dsm}J-Vh9GnXk$1!tXxQt9 z$cocxK0TEi0iAV79iZ;drZCm!Q1 ziiV!tUXM+E;$a4kYhi^ymAx4>Eym}yw761uEiG%PWxXA4RqqANI+C-lyJikLE4nnA zL@a#$p_Ip=pN4ryES!yzV)yX(3Q;ggpU8MS`Z+niX`DV)-{*i0=Zn`dr;HDwo(*MH za#V&XA!O03O+l;AY@jXXXyJTWgR<4{O}#Rm(0C&Xr9Hl*@at=6UH! zuPalI_9(dL4EH7cxzg3;)$S)@7Q%fD?Dxg+l*Y|0Cs}pBz}Saiy=GgA7wofV3KjcM ze2x{*DxJ9H7z3q0x;Rs3{3OMG&>Iz$IiIcFMg}QyR)aCpTO`R=Z;I<>iw$=eb{CY+ zl6)c!Dpk&yUYnFT7x^@jV@?#I!Jm{lt9u!|&-;_))QXwD3RTY9axDeRDi_MX2Fr!e zO&&^X7bL!NC)|XKv0~KZiZzQI&0zdwi!(=aTO?b}PMVP)^pkl5<)n%ozY3Mk&`+XI zos>Bpz0FlR&fd=}oF?7}a!;O@sh}NjQX|rQIYl+m9_P(4e~orCuv95+lj%KefB$TLrx$=j=6d~|-!79%3F?FzJe>V<M|3h7)c3{F-x#?W^ zGI*T~tZ)J;4!92Bu_(Ew&UH@*HkiI9Y|+i%d!o4wvnv2;Ai42R!(SJ(7T;4Bg0Mq) zg|tPN4~0)tv_@Ptm`()qRoAi8SAr?_%Xv`07u8Qc05Q4y805LWYW$49Uvz1>AK}z4 zpp$n~&i-gR1020G<}X4pMZS`yIAb#{lmW;nr!AMN05Ht3#UCFpeu165Kd+usSsYeU z@v*UOeY?Z;rmP;t`VsLh>#jMxIQWG9`a{*zK(^oo7T4UM`UCbq<1+w&lK&FM0soE9 zY#mJhGlKg+Euj9xLf=l`(9HN>7$W_%p`op{wXKcg|K?EoU&_Y#zdIP(8W|h@3pf0K z0fvg{;RUB^leO?^-cfxHu|rZO!D9IH?aN}RlxaIr?4^p zU$qDSx0{_ltSjLO2LM1$^Iw8-X$c__StSu#CpV{U4QpGhQG{2{Xu&IW}>bf8;q@-7EKmskp! zJ$DzvmR0E46+tSRmKpUWqbggGIyOyMRXQ<`U<>O8#nhPuyrO{)x)g= zO|K#cJW8=x%W$Iw7@owM)-9fuY8L`&+O<`F%IX!N-EjwXtg1hma#2{>8jMv(_;%M5 zl2w;#pNezC!l2Cw*=@($v4`lM9VEH}5WgRI72r>Q-Hdoc?|=(h9DSb6UFgZEL71z2 z78~|cbs+HIe1J|`G_I%d+Mw(pbMaJtiGGhk-EF9+7;$Y>`~MgnkuH*HP7km$^+Y3? zzHimCa2Zpj{SAtUL$1GisTj}|q||eFA~#=q>rt!L#&l|sf5*$Z7-nVuxDkVa9w(#5NvZ=&0?C zM+qoH?#2)XWh?*Ie>$tU~D!S#||Ef46IUogd6+yRV z0ZG%p8_-s>gWLj&C2P&`!>sw(?cbgk*(8rc<$=CZ815T(9gRZ zw1~JL;hC`mdm?Ak%!*F#^LFdjm8jLRX@xZ9BkbU8@~lQX+mYS&5y`7MJQfdf~nz-3rV^1^%t zwIM)yu(E!K@C0;Eos|pPQreddIv@f86j7)DxUbMXqvAZi?ewpMmJP!5|O{dRdtbUgRF-ha{OxUGx%W!Ab8H9l-$+4%@jKDH?+JrCBGEH*F3vU(U+tQJYg8unbM!4vf;u zqaZNKOVYD;*D=)!R1PmkmM7z9Y2ger9whl z=SP!?&zvy)VvuUh7@p=P6Ss?2sSsk<0E(*_LzY^v7rLURxlAvJ@}~FC^Gh?+F*c5` z0Zj!8joz&wTT~8dyhr{_E&e5W9;51tpSM;sW*W}W-i@_1B9n(B;jWydWg(lthjIgw zgvL`UQB<|$r7|!vgaMhHc-^*ro+ZbYID%ifjfN) zKY?Y=2amoXU4Y1@BwOn-192L=HInWkxnB??UD` zMyKCk^VzEDuBeL$OX~Wh%^II&^?)rV@@@Z8(9P&NuX3D|QPol%FhVOyF7L12U&pNn zh>`hH>{l&`x9^X@1|Qs)uM&B<#?x#RFo=Ro-MpL+!Y!tC;V$n=`lYQ2b+12z;->1Q zEkGdHl*t5$6k(@cB+j%g-yOjS0=M;>8ocBtffB?u!!nzQ1i~)zIP&X1bo?W0{hMS2 zLV3&nyo~onlku2c<@@apomXY*EId)O6{Ory1IyV<@*TddU3kjlok^6kW*bBq*@>!- zbuFlV$WR2uN;=f>yHnYPF|6H?>xgOK3keedI<9T_+v93u1ZW4KInc7hP^On-xD0SQ zVeV2kaZ==9kAy$6B^ZwrPy}iT-&1{P7C((ylZ;5MBkiVDEFBWoq6WXeUD^GRv$xt6 z;G8yPtG!(ORtjE0OxgV=O8H|GgVDBwHM-ZzW=J-sS)6G<L-eBw3|-iL{HHVRA(2q$x*Slnq!BFnfC7TPv)XwQtX`+GovN!KM2| zao%Q;3v%=VtuXde*{y+Z>xB?5`hPOB>PwwlXYcR?S}9lMs*(rBStZ>aTT6sUjv)Ah z8dEvoa>pHgw-Iz&3DYHnbqo9YFkguAsoWiCsn1SMTN0Q?_3!Soe{jP0v#fQ-*& zp3qnTy<*1_xbhF@c;2FFxwR<(0d?=~Hj*ZOP%ebuqG91`BL}N7;T=?nH$?FKs35_e z-3IS3olIO}A`ECvXDQu0MeNqe4b)X6IMxh9vHkV}dXnu03y7+UbEodPt43thyGG~% zMIj3^6BNCnH}b_TzZpl@KjodLIr4ws2g|c2ESpAVe!Nv)VEnqb*Xvdy4lxWXP8CbO zQw10dpW|4d%dFm_N~u9?NrFi&TL)T}I6_pFI44g10B$nux^>;tRL(8jcF0|f;4Rm7 z;j&twI4gtCi~=6W!0ywNXMZ+ERu-jb9B51_`98`?+V4a>CA($~e>4}9I%O?{J6cki zi5mq&*UXl0j_RsA%?ys%4=ULhtkX~Nzn{;9=Gr*M*mlgNUQ(KcLt(3jBT*RGsr3dF z%X1N24PH;zf7Rf2hE;Gs+(>ns^5-X5Kp%ci#Nd~tXfQwhlLA@1z%prF@F*aai-era z<{!vHy1K6z)j=#%_-?W#oWOOL6~^4-7A49Pz_H?R8O)DD1{CS3OWwk*R(B9j*0ILZX4zm@ z1Y%F1Ir&kec|Y`22K9H-W8lPpa~#kI&D+bf@Rt_xxvjutgwAL3#NXOllIuugP|-WHH-EI$9o?yRdOE#5 z9bK;)>vNS{nEU%sTG&+|oB~Vvd&;;vqt(C@R$ohzDnHbNQbT0JH?ZtPjGyKigJ+z?TuCU?x*mvG?jPLeV}#1;AK$bHbb z&)Z@l85F8htuxuV0?A;EFY?tYWdnOz^T2+`2=)iWv zXzTR&8t^j?xJHSuw&kPgdjmCV-o zeYDPp@T#$~!>|*;zw~YCY!Wb^8{Fr(bF2qnh{Hni!d}Zo*7!sRThaC#N{j?5!rY`o zb&rL0fX6;q?7z$(abYNOvF}6%G-{*ozEo`TWyQX?W-V~vx%t3O3rdipWp z?PqbzSb4m!6(85S96HH+=qG8Ln$tG9$?yp?E>Aey5Qk;SC!GS8m6JZrT$K6VD48ju zn>8-8YRCSpKWcNhNg(q`>$M4C<05sKSQ&?>!yNg`06w-~DZdJxvMZGrYsPLM|L}hP z=CfXrE7VwG_knP8axCDWy9&vtP`oP|C+rZjdkXk7EkU3tX9=N0CoV||5F-BA|sBn<@7vR+ZYwP zwF^FOhldk6&zck^LgPa)QzQK|`gkOW(PaPeCl{*bwSCGt58n<_8AKvMUIE#?I}1kX zvW+M1n;}}ionco5z)8>e#EkU#53I~u(9LJV3qc$9ou+Ob1KIY75e`|WR5D-T-kVjz z@cabE;lVMx|3lRMnA0_8&lrKgUNEOGD*O`KNl*D=VkXJLie*NISnZhOlvxPH*ht>cRo|$hpxOhs+yW)nPZ-o`@(DCv z$;}_J8Qk%X)tzibB!YrPCZGVIJ(n+wpbvjee9OmBUBZzSDU-MBR}1!YzW+&P{ykae z`7C%B0R#X@`vm~-51jI!18lN#%74@Kf8^@T3iYys^zhq6M|d_?Bz6li(#j&#Vc`*9 z2D(U#Lot_*1)n@8z(XUzIHV z+Q2WH^8b7$n?eK6@^S=PbyGz)v^lX3pm#}R^mA^L2_fvWt<4E{ny~ozWnrCrB(8S|YV5^SmuRK1xOzp5SN) z74zB&&h0R0K-5#;Q^J#hukM1q-Urwh^{CC4=ABVW7Gd9&Y~o+ks76hn-t{8WS=RM;lsH_I+(BPE z&%s|6)vudUj0wxr)WbJiv{OS>JT;2uu97|pZeaK&0*Qq2gct1f>wmTSKLCxHnB1Za zGyuRQCIA5SzoIMu0ha{z9gUe7X$`IP9UYI--)#$Q;{0P~tf-Uuk z0@;G|5_KcO|J>!zUrsfq+zK?1fy&^@T_cv|(T*6;^r3RJ`T zU7)t~{BgJ&Q};xvE`u`LUbI8Bt=yPiZ1i7o2dkl5&)|PPGK#mMJ*VtCF|PT?oIEoy zxz4FDIVmkB&nKuRxVEfiP{2Fv*43oY=O7jL$Y3&Ika1zPPe>P0V*h~G`VP55ropV zG}O4TsJc#_Oy3GRASiNu%{S-YvZ}4!F1V=1%cSl>Ib%eBW5Fnn29w&F`Vm(mV3$Nd zL8xf-;W1-IuF% zNkIzKaEjy$h|@)aw55G~cJtPZ64Wj0%-q)+yLm)$c@NyNv(4YcVu(1Q$;WuR`ovVC zHc-akG9$X;KHg)Xs;wFUw~4VK2=toeWQ)ycCpsxZC=6Y#U+&!2M85@PLt_;k{N6RW zw}`mOsa|d8S;(ZY5;pKB@6J!em4tey;QPi8xQ1H^HCs0Eoon8{fQz%{76eZTbM2fb zX=e2uLUID}XmR6;Ca<06Nld8-^-@5kk@EHz-l~euS6?$#e&@h1zl)yPW7*i%C^IUn z@7E&V*I^md!J0C)#}I-TL<}(qahDH@V`~zU!`HKV2G!b|uD2mVuU^|@@+kO*zcSb_ z*0&q|^rt17vQ-y=!hkFp1a%$IhIS=7wnq9eYUTK zxeol8uA2pg>+=cPkDTr_j4vZXkKpvm*IT-PaXLjnjsJy>_H77F=j|LTk(KpVXAfv$9AYBB^-pLD?($y!W7vmpN!UQqP=D8wk9V*$!sBt1M$6!cUL zt>$Inmop(-qMd3(^oTc$*cTw7gT`E&zMkHqU=YNmanVtjzF~KK*?e@|K1^tLcaOXr zU9jt`#RX?8X3r_&EVvM3Sp~v|FmALSF)Hg;y-a45-6tcXYiNgoKYI1#5=H2EOX%z- zx-K;~X~e^@5Ey*PLUoRu+8_0?fb?D94OR(Ax3h)BT$ho5G;qrnnX7bc`H`J-ytVad{o7CI>?qqopowZD| zy=jn&CQ17ZIRF}o&ZTXP3>TNQ&Lja>3a@v>>k6c6)%krR38xLHB?QzLDUB`9g-jwF zHYiUnJlExMZ0K06M+nBdL_fN&?H`T zF&;}V{AAEeF+Hy7bQe%7Yg7E{E9NK(JWt4=FXSjGxzZU5bYDzH9edQQ0O<}BNF6_j z$mYEjq;azb(Kuf7wDvmWtj#sJ#B7k0nT5zkNaYiWF-54EZh&WAbRiE_Li4Pmv?}K( zp=Wtv3w`dr+;DQ=;UZkgQ0pbrbg|lB)escYOvwB#ckv7}2XEvX| zU-edv2mknWCfkpdp%!Jh8KElGqq znfy3IRHgHmV@|LOtSr9b{*Du;Ys@gb8mT_pfJ90?No!MkLMs+GzYts3YN2AM>_{)-+VZihI|4i_FfFgUAl`4V zL|Efyf{)HXr@1`rc*_t`kVTgu7gEp^k|SkoV?KLlQyTi~-a|f(-X;2HH+&izoP;u# z_SpxXJti$Kip(k%N#&bvf`32xyT0rt5>-!g7yThRFxtV0o%d~we37PNZ{g3dCqU8}x--qemOrQyv@O9vd{LM=Bk}&Jw^g zd#eLC6-43GhnWdLr3S#83AwO`z3dZo13wvnpbl`g$Mp&tHXA568#Fc>Jz|H^Ru438 z1DUkPQr*|l3U^(UOAI~r{03G%;41P)v%G6n56v~`X2e}3Z>$$ckCd4}v{Kcm7YX0f zIO4TZ!R>Q7g0E518G4%dXcK7nTP-(Vqp*#nG`Pe(Egh98<%RnOhM5y*69?U9mSHZr zaqXhOKMQ&I4g2W8P26&@JC(~!@HL90^Hr{ z54=cP)K}P4a3stRq!8DkqFml!;J~IWNv|B3drW-tEiO zqr5K2aAHE3~~3 z_-aFXnI8jkLw-51_k!0grF9y8Ln*^v>m_ z^93MRfoItJPLZS7+qa0hADPjY+9cs7su^b5tm^f9)BmJN>=xw;#7pG^rl-U^oe^?k(OL#$i4 zyHKZKM}bb~27W_dd!KCB5p#@P}~rGkUm9#KFUzs6n!?XKW?1fU!QQ0IAk39H?&KLRS0$n1#~+! zY)Cn@JMR zT!$J(2^=jfXc1x!D(is8Ol%pFA^63x`T&a{JZqSJP&8q7okSFw6)Hv$4Pl~P zbsGN~B`Kn!7sTeUk)(Zv-B?y9`-zQY19>5Jq=RH*Ssr(!q->M=csi;|mc`zlR-{Un zmBREoDod74eZ~Wg1;<`N3={1+$B_Zdi59cnf(VhRHnaVP2-AsnlimKX#!Q#l(NZMq zOt;y|VWfMOx5CsJ8cCKfLoNiqWICV$pmiUm#?K%231LbD!f8W76efGgG`0ttgj~{b zIb3z}@FsD@GYfRe_Tv;SszfcSRISQ5t;!@VO^S9^lJ?_xElrYkRho8HqV^UIhiX-a zYFUTsoVF3}=>+a(sT>ojoMZ7E*?8{hWNv49MH&dz8Dmvc2C5SVs>t+YlBtQegyiCc zWGG@PG)Yz3geoTq)eaJ>Nn)x=5-KWUDk>5xHAz)f2~`hbDm4jJRVmdKan+iXs;cCw zs>G_Q)G95a-|P6l*U5jc6aQYP{=F`UMURu(OTTsh>-dL~1_bcP4G=Ra(JtGC3|QPV zyQ_}t8|a_YlKD$&#*x3tU_KB40NZ~%Es?OXb9Pd4axm7n{`U+eIYC%7PEieN-=AK?&kz6Z-X^tgBf5-V0dU!|Hq>O0VRc@H zf>suX<9*JF*VN9>>pKoViYG0k+kW2Vz(5HUm|XMARDVjKb;tk~#`s~Bej(!4J=e9= z(Q08oxjAY5?$;_^4f^Fv1r2(Hf7H-$*&oC^v7K`TBNBOn8|^#wb5GQ!#2ReIEMUs( zSFgUonp>gen@TfrQ_UbBDN*k4%hgx;EbJeRsQv_m!Hp$Zbjd}{t`(k{=4E%}4BmAX zp*OsRcNv+=ZQN+Yu(Cua30+h51^*KC+cYxD5?&jNB8v(I>jfJ*Si`s4tT}TX&aXt8=e+3loo;qzs!bd4|a$R&PDnM>;SM zu&G4nv*(y=)a;pleH-ZKU@a&pBW)xf33CSaMu! zpTMVZzxI3d=iIs0AaJ& z7v+&hf7m24Gpt?GJE|R>GnLA*(s=f;oCSyb@vW97`kt1pJEmpok?8ZnPce+Y!(WGltMsI%pwS4-3bjr3suNeluGRJWd&I_ zCwh5$6}=<}1#uv!#E}O5dc|H#VbL zdgQMa$W4d5r{|6K0X4vG^Db=XE$*y0kLal#Vx|#YoZjLVUm50f9Z83Pa4(@_8B-$) z_itgZ@cY~GyPLj%j<7jGJ9gzA6CB(Cu`~ss7)ME@s(SZm@hJRycaoQxXN?ln+XIE=>a9ow<%JIQ|Wq<5M zfx6>=*Zw@lzI2~#A9Xp-^1N()?7R+6s)d^|e3fP%&tst7vP;R)&vclh@Qgd>nsN_2 z>)I_YGVkY^Vn2$o9nCk&z9Xj2a-6x-_Fj^*b@y%0GUzxhx_g8dLq$bKNku7)7>}r- z(=}dGQFjl#LXD`5RBqj>e4nnWzjGd7MXjR$Y1(rBdUsB#;W%}s?HzuVBJ1uS@!FKI zb$Cj*lu`5r{oTnCS5EyuCkK#+Oyo?kBdLUMT8357U zVnQPN>iS@cUG7%Ivj&D@z^2CPSe#ipS0MJ02vj%ur>j)J*;oLkf%uVt)kybNuZ|Ki z>RGLs>|oD~Z>eJ-9LGF54cuuXn&RP=2WDEbmkEKp?8PR!-(WtFEXo;D3}k&kLFEiw z>;evS5CNJs3yh$KOfjCA(~yTO1nKF{?N)p+zQ{%z+%P|z?*!ICN|U_2u(4ACFXAF! zPw{40Z7|%)v{Lt}c{s{O=Lf8`K>CK^!!wGbD0K5p6II0` zs7zwL8zD4gQqp0mL}i=aV4I(^BiD3TXS+Je|AMJmwuYrLd9CX3vwwH*Y9d|~Si{4O zGbIf6BvK}=bnp6+JJR7T_{q(~Caq}Gjb$s&_S7tf^vMV3PM7o8zhBQGuYzF9wxu8H znV>li-B_B|Qu&(T{NWi_1rW%#q!k~7Ntay|(L!8NW-+|pBpd0CEXOJ~biu&z7bi~` zGkfXkc6ilalM03 zd5j8ennj0hm<0yzJk8Oaso`!+uuNM$iH@L-)Skh@eT8804!qLLzTW(P2ea9pS>R(# zaOxBK5r(Tzx}W1?ikTZle_@P)%6QmIK+SC36zVPXMQCc`;7pQyU($IYF=W6~ZC*&l zp7X9(cBnd%r$#w@dOF>TQont3ANZj(d**(G5nc8hio=C`34YSSV-<6wXE{AMdKrHA z+IsuLU9?yi3hE9T$V_eKP!sns7r4dhY!$Ad6hF42jy`=?#q#(E{+O5e9-P;UJPZ3) zgrRS=lC6yF_gQ$|8Va}kF1NFNDfB^Qo=@y zSmK0te8x${ zSPeU?d)*8d-Qgv6D*;*)T-wRgChgN7|IiCRORTmh0pER{7KAXT>3> z@i$H$seA*SSWU_xEUYXKDduqkL#`j_E*bD>DATFo$9RlRaPI189S`5)DVa%DIN6sr ztGHlXwhr+2GIau-prql5@{GlP^emn>eVjCN%-B#j$hDBPZOvCx9%N%}%7$6PF;1>c z9Ap%1nW!u@g32QM5OGk}Da_$NB5}wm;y*HWsIJo?a6LhpxWFcn)Bh%+B(MJ!jN~Up zEM~r6X>4jH+Na>&{6UBUN?F^p-zA<%N#hU2Jg(^e#3+NCzu*_R35+R9?t4k%CmX!7 z6L|=uoJG9qPKinI;ZE5jt~5g0l|wpU2f-_-6qGt4f-sFZj0~@2)<|B=svz1a%;6jk z84+Qg1kc-{G*#3&Ox+h~as)MT=?*k~ifgQpzh7dxW5FFdAn#r{sXRr+AmP zhF7Okg!rs=h7>jhrR)L;Ga->$hAkpeos88BEOS$N6ThHqF!54oZ;^-e<)T@Sj$%ca zW8R|Qeyr|1Dx-lDx>%hicee;0jrB3Eav)Y<5)@)vAlqU4)vVdFF|xittq-;1bqQ75 zjHW_O2!NJ%o1M==dA^hfC3!&%%U{gjT^EC}SHGmuy%IqdOmd*l80}l8Uz~ z>|8x<=^3R}i$jJ53&Fy0qF)OGS&|O*xSYw6cblS=l%vVEX}evuu?7-TKfdbF5BthJ z{R0mRu1KYFD+Xi%w3R78o_!WUd^m+Rq9@lT_FtC9G2ZA zcUb@?Oy(_lSyDQL`yO#RqU#y=4aH}iC{mk7=~NHSXKFd@GfzoKNDMj*h=3%)KpZ33 zAvZJ(5f4wM;3!%&q_iK$P6r#AI*f4Ff{yw_bG;NXEY@scudGQDV{W}qTdlTLJv={9#1Ts+0^XqShfV9a;f?5J!%NHI}=y;y{uBfc7&J+^+_<(@g;2j%VE$dEdFD<-=z zy7*k02(fK!0xyHJT*TeLs&}b2*s?VtRz{#_l(yCV-R5qHz%H${_tY5HM~2`pNARoH z4}55kASWMVKXUA4cER8ddpY97yxq;8HOz7Bi*6t7nA9O$6#4&(Jv>2ohB>EfR z*9(T&tm)%;rf%gLkF1iUoDsvJ6k!F8Y960Nrc>A7=cS7RQzP(_{KO zKSt563;J29cgIXuk->`V6|I9Y^4F6Iiz5F?`gHKUj|KS{b-Q z0bzb)ZehTvIE~M?MI@M8{2Ym>M zfUUrRS|F6ti!6^_Z&tpF-2IT<_n*G%a~!F5#%iv_Z=D;T2O*m6%Ot zQ#<9KGFP|`<}Zpl&j4@DVCM19@HC6KCEXhkEV0X8nu9d-Jh3}d+#b{Jbxye(KRy+O zAMs>W>Xn%9NFD%<4truh1db`q$B-Uww1rLRD_g8Pp1G46VsJcG*WF-D3UNYWPi{D~ zTX_cvOsL;Uyf)|RkWlS3Kak5#8u7p>QHob?B!#~v@6V7*666*jlq4E4=7#-Of6p(> z3>X(SeX5vg+8=yV?~2d$NT^<6qL}@eP}G#pEs(3kelQcg`Y<65qOVVOHeQ&w#%sZ= z8PwcHtx1^4Pi(`bq%gRm1s&k^Mg=T3@-L!`G;5w{q0?w{cJJ91qwqE=e z26n?j>01B%oagbRg3auU*O(+)N6B zT?{tx$pranDuZ1P_Aj7&0CwGxe#vwIv@zl~`I0_LcX%ygIRD5a{aaRd;Hs2Cy=X09 z+W5$R4R@?IW!1n*BbYWVT0pfi4ZC73$dz&9UCMTBEt-=+OCuE2yw@MJ-5&!$)H0CM zfnUhuT<(C(uXwRAu{g0nu}X%kOs_H(YG{MgV}@?%Z&ANSfJ2O=jgT2h8AzGBq6kG9 z3?U1H6eh?Glb9&cl`t03c}JW^phu)fs7I_uut&5)av5R~=91LZ8r4{ilu^+vCQ2=n zci^O>VxW&Wvu`(s(RTl zW=%jcuzKA{Yxr;8g@+L)<1Cp)P+L(!OB9+$xNRaHeX27`4Kk>Hnn%EPnMu1Y90*pa z!Fr)tz}In!{n-mp*9juKR}JVvsr}mxz*~bQ+x=^kui%AJ?S+}cdDI}DMi}S`M|!nY z0L~mJmx+sgK4(B+hfeg=rhXrX(ssRBAfks>cJecTg$}gni9UAXGeB@-0O)F(d<0be zMG*r@THv_CjHgMV%F=kM4Bo=SkMsubBn=O6N%G;n@+9{##O9Uk0-iL%uP_L&(R+b= zjz3WUOdw3-T=j?lAK4`<^Zy6CTx?#>U~SQ`mO#-O`rDrchy7)jNl8iP%SKGZN(tF@ z>)_wiwZ9<00eqFu($`7MG6koG)0xO_+-F8Fziu+Q06JfH3;ft%CupgwI_z#$J#Vn9 zIMhwavB$+pbCX14a;wS&&hH+^wpu2wOu{%!f_42snnJh0A|x5yvtzRy-w8L`?GKPs{b3hTp0R` zE)zAmS9xZdmdyWwE;)qW_!c%qR4TV=|A8*Eoy2tw)#v;>P9OdaUD8$*RaEL@N7VjB zm!EQ-Tpa`4Cc6w?SvKm`KDE;!teMcAayp2E{mdMu@6!Wf|AsD|?k!6G7rNy9Pjs2! zeC8tU8YydP$;I}olXP_e`}b-P9QhUe!y!#6BlT{2KRfw8hXC``q~4!6GHm&+jh^7A z#J}hg{Q1uuC4nro6HRh#{xEb!w@4R$^GNU1t+fTITxsgQn$$D+n*<0Z@S8s5WhunL zubno(RFt_LM442k9jdCnk{_v%*LS#oRz`LjjpFG4Bf4Z|{=cD1Td7TXtkFB5XLFDRe!Zh4PC z3r1ja&FS~Z1ll0gN%RioMySO8O z^B90S)Wa`C$i_i%yQ1H}OJ3b2QmExA8_}C!)>n7$!U$=mu+AyxJ>k5!L_XD7#}G2k zcs}gPKa|Zqz7tuJ@kp{8s4M#=!*IwTXEddk#U{R-WR4wnAV1ymZaWKBG4ktpm+^9n zwL!60;kcUd27Tw}rvm-FkvO}e6bhGAL#4a=fZ`(kY@5-h<=H@m4q5ZDd|jRo=@C7R z>|*W;Y8!_0s@vNV`Z-ezNN$Xi^V5IfTpiz*BvBth3e&JKs1#U%o-Nc{>@T@AxBUSf zq5BVVX%+}ChS`xmPBy0qtFptG6R)wCd-6)lqLjBdeVfeatmRh4ABoTB?5~kRPq8Zt z+2#&-)SY)GeO0|rLjB%b6>js8}1ESd`=KZO{BY~^5JO$nv{8M zWaCzT1zS(K6w(`9_%a*I8*msGIOtF?F3ydQkdKYGJv*^7=UsWci$m}#^#VgYIR3s( zr1Klh9i(gQQ_~|kU`P)k>%nJ0L8Rj=6F@X;s3hZk5AF=~d9i_d;YXw1yEIyPhm#aG zlcA^oLEAeA>GmaS!l!K8)+yVzZQHI>wr$(Cb;`DF8>j54U-!(N+kIzx`omZD8EfraPi8*(qzyNZNi>$_lc;m+!U`ga)cPSdv;#Dc1FSd1@raeg=sMR9y+# zd15Otnf5?6A>yD@DT(PN<+DoHIS1vQf%{W)h7srm94N)sgAtVZ{X*opfddf%=)iaM z)BgS7vL{8MIi*y6RH3QVSHApTS~1zkc1Wn3g@P)Nz${C@a&r074KzF}Pvs5*ahabM zx7&ob5|$F#wU6prK4wd^nPB!}<4x7^#O^aFh5J83W=+gz| z6LMUe)Tr!V?R;uj2zPX0QL`LGOGU^rKFtViEJV0|>%GWHWIHnQ&E1{Qd-JX1KTR0w@y*WHlmHbje>uRCr_+zs>tsJ6#Pzrxj`b!PPQUSWW+?m;9CBg1Nsy|Q zg6te{-J@)t%t=ueWG>B!!Q_MpfDu8jH3|$!g$GcC170>2fVxN7?Lr^)s~HZ{0Jn3$ zA9~1>j;~3pKGAB6*PfQo`c;AN?HXdFyrsw&FbsJTO>1f|5NNoHJ!L z9!9aTb-Tdcx}7wVO||qF2K73I6V(jMAAxp)0V`!wfgyG=}>g#gN(*?pN;8nu)_^$ z^>YRrs`M0l+gY`#OOoYD2c_2016RIk$JWJz^fmEl>&JHOXiD=XKib%kH}eNVExS9i zD0d_QYV&OVXbond6b)`5;i|Sk#BK)&5az|Yv)c+eEk?TvZ)4Cl*1`&z_=;VE7pAmJ zf3#z{$UERa82P8_`Fnu4xh0%bWQDUZBda>%fYWT1*zEf~S`I^b6iny6{8;3QeXl`h zoHw4}F@0vmIPh(McA@6drWKqrXK@U%R%O@TCu}h&iV0%8Ux>yrIuea1Y_SxdAseEs zn-M6A0I0Yawd1Nrg=R4o_A6+Fh)d6l@`@qZ4W1V3J?d~sQ%RCC+AFc4BH<5 zx|=|88jWH{F6CFymg?0wh|4S%(kg)T0zz?3m~#mD`AAK8WliWT0$3iatQ8Gl;Gf6L zsSjf?63ik*p=XwaDq1v0f~5TG9kRCrVmp7EFs)h_k^61|43CZHq_N+d1a`fw7;ED3 z778*4Du4V>T}nP}Wt!kDO%N*^QLjaJpFM6~n=ZSI(+y3(YuX6{@^9U=V&hw6F7_7F zX~KP0p&{F_$n9Cy<7ywb@}v8G4*rtzH2bgCjy9*bp-6ri?of>NmAi?fS9G~-`m z8OyLl2C6;DW6OtaBH|>mvb;9THQcCchQ6Z498eX)JG!N-UlI@ZEtTb~{`wL)=Qu9q zA`Rm%QN((63Tlb9d8_+$tilULCz zNpeWnzzp^${?n6{mMZto2Lyng_Trf^DnnW6Vt3qTJ3rQ^^c-B9&60(<#BPi+^uoiJm4$vTewj!`1!5!+tk)>?v}nI!3{H) zO@Vj3Ro&7Res_=7a1sKpK>~4-IFV4m8)7-&2K|-zWW~6>ztL4;UJ&3i_KvYt@YMz+ zeFP?bNY>tFrNYLg?}~Mb0f@zE)1N@8k%YN1#s94;0siAJ7X8g7wr0;aA0Zp`8^8Ux zu-ktL`?8d_6|t0&KW$i6VMbNJncvC9ixfZ**H-Z4G~g|Lo68We%goB0+r?4Uq@DLo z3JyoXbTe;h=}>hqq;y>EMNT4}PYM`6SwDf6u5Ap&OkHbbNRP7}uiBnGkKdf1rn`MO zf$ab)5C-h(z`R;9JSM3n2ngJ zAOj_*>b0FDihWn$Ly;rX+v-PJ$?1|@HmV}!nZwsjicge&Mr)6ctQi#Rtp;}_j5?Pe zyEdDwY8o?k?BCY+{I2xnuw6}V!LQ|t``UOO8&Q)XHB~*|NT1YbZIoR-fRJ=s*^9x$^omt3V3UncjsJ90$^;w zAbO?s*(Q>VhdNnqS{D>d0${zT!;+GKB9YfMiaA$48sL_o8Ue`ez`|--JnUaDl~sy< zg=YC+v1rfT@auP?@nl(t@Le#;Fvl;djXT7mXacHp^q$;k@63hUpL-lrdRjY;yuz!s zEew4RHYQo^+U36_m^T*ep)hc2^pyld&3POs{aVBV=BV(6@7fL^`La73Y@qDAaF+nGtl05bk{B7-B+5^dJpbJ zk9hG0mwsaI=Hd4nc1-b|6d(m*kjb51P^jU;1qCYgE)KTwwJC0_oIaDJnAOt&_&DKt zNb`9>`iSZ3qWFvN-lfzfhtDJV8A0PuTx5TwM>fe^2rp5(7>^hp56>7W7C42+m=qnZ z)ea-zAyXV^@(BfRF4B0(mc5qElY6NTuZ0>Yv)up#OFSf$o8GsDa>^e#p;fE8U zo9|Yd-rdW->y6p-dLg2e;$xO^ z(lJzeTQ_@?9~NdyT2H(-BZ`Fgo)EeHyJJ%uQL2(4)8p!!!*u;N)z;wi`LPb+Tk+h} z^JD8|NO%ImQ8Cpmz=VG|1GKNTXrCGgVS^!dD4)(YVxMG}8tN{(^-r}QG-{waLJ4J` zhwN~9^g1Nrpx!7gu&}6KeO1rMItp3Gh!=nFfQ8(a{v&a zws)#TOZ~BH%igO;c7nH>aovoJ2LE&MdB-9M$avV|+vBkF7>q)jMw3#7jna6u7u^%p zkM(^`$VOrDhxhVx&eb*kEehK;x0txD#aulPP3KjVHxs#vs}2;9hUp)U;n{%9rOGJK z*^^zX)dmgP+Bu2Z1lp2Dz{-P%KEdpgaF8-(XNXFoDt_53;-;7wZq4K?n^kNBa@@nF zQQ9>st7y!HTFkoZ#<&qKDP#N;D-9rgXrryTvPgs_6+0j(B2iJfDl}~4)p9%z&H0o9 zAz|naq`~wCd+b^a^q~YuhN*IP>Sy;>KD0?yZd)2VugWDGi1;=#l<3x`#_#!ezTj<} zPI7kInlv=qYwA` zU-SJ^)q_&KMLup(`58+-$GyAkld@zJRZi`PW|%cG`@7E`YVEC8N)|dzbC^8)Xw&(= zXfJ-SH~XryWJ3ZqvJ+y}*yzi2$T9>n9%&sv2+XuXDi7$9B|Wg&1xC`@{4EA!azFYC z;Im6`gwQV0FUh4k!=c3JSfbI;n=nbhKN0b{r$T=8c?9hcoI-H?EHLcEiQi%0LNfhW zssS$}+P2Y{n>{J??F&?m*D|7jovzajJK zm%WY9CUsCvQ_dL_sf|>qjgiEF-lxUGHwgS z0!o}#SoTs5194ke@o+ja3}F(|=2vD$42}WQBU?Pmn>e$I=yfAYE>3=m-(AAlk9BMu zH()`l^VXRqx?r0%ZfDONOf4b%984ZU));P>Q1w0RvG}K71|n|mQC7;;@|_~l0^?Sa z<5Ug7BdmQHtcA%_?6*iZKU=4i$ze6u%Ca#PK1zgMr3})eDosA7G#INix*`D-qfW!n zipPdg;(;QauRJO_UNN;#YzttK0G_cqy71pJMYR=`;Q6S9 zfCBup%4#h%b9=_>`V<#)#`5&i`vGuX)^o+P_j#vK(raKzg|QN~O<~fNmxA;6WglQ` zg|ur!A;pgqo8u0`rFlkaX?K$oPhQ6ptd7?ocZY<&8j)`D{;_C-Y()kM;k8&bZh1S6 zOu}4NuLmcQ;S8%!6|S35Ft(-WH-va4hv5wlL$L+?>}bmLg@na>nc?~Xzdu&O-w2eDXxL8Ha%#vf|qH)gkQ9zR1 zFMbj7r&V$mmr}(=;?FxZ?fT%pm9dgSVA#Yx_5%e@#Dk9wa%HAB2dm&7s6QY*sC$rD zTN0gv~=rU3ftP48hX~KM8Qgf={7qj*iVm` zGLOm~zLxED1h=Tk;|Q0r9cUw`Zi%%mR=4BK&?Moq zB16BXZlwAO+ZVzz?Y`6?GfjPe5I60A_`%dH5uticEj^oI(j} zKloyHbj$ta;LYQ@Iz{(bLUlOpdRVQvg++CffozF-iH%o89mvl3Fod+)CN zR_`WmKoB+qj{6a~=))t^;P^lHycE;Iin!erF){H>>j!0zNt*GUj!~)-7Dd;vllZKR zm6Id)^YKzHRt$@B*gLFa_ne|Wv1>qM_puBgqmiHaQZcFPf?YgtGa=p93Ly0gzl@43 zMkN+)yCfCLi8_hX5O>Jzq@#r~qN^Y5xtb-vaAwied>J62I5pi&Gq02vWr`ubi^he+pZH@V1)T;jKkNt3>G(%d~4!S{2M2Qn6c~MYYRmx=ZaX%C|}g> zmGBYjM!?K)V3C4+;84&*&eo;;zS86U^dn(XiY{?W_@ZJ;8U~r|)fsGXEz)cEaZ6ss zHb}*cAMj)}bWN5m^R`)2jajEpo~QekUw%s;n-#*~X|WSddegr>uDHKzQrFpzsy^@6 zwpTzWeVjkzaj9JM@DGJK=LD#d6+HPvBV8xUYX@2ngekJ`_7IbmwI1`ar#-&uiaH2; zlDJwZ`zb-KN5XPcKEkeU{A%6gl{HYe@vh%g4oaPcePx8#;BXrh-G_Yn&X#>k9Z#H^ zu-`Qm=t{RRv>JATt(`}`tlX}GgDpy)nu9J%o~rzdGdClAMpmz$&kH?iWxJ<$NI?7j zke-HUQOA*Tb7GT0#g$To%q~*=Zfsd&8X{vHmc%JiAZs%xqqC=FcZLbFFgfya3`koG z!ZJCi?e#02aii%=fpvTVbFCPX!;^|f^?)wtQmxh|a(;|bN&ew=LSva~8mY{T^Fvm1 z$mnzQL~ctX!~-2wN;EeZhY355aZC3749INpCgBXfjUce^&v102(;`&-Lot@6P=;`< zki?W_rbgA%l5iz;85s46C{@ZO%dN|L z7BO+HiB95IxPH%+QO1Qs(N(+J1iZNtCe^;C@msE|lNwKcyQJGXCleYYD$F`JMr27^ zD!R1TP+X`#xhAIuMW6DQb*dF{cXSe)eb_?SsO-McX`kAtq=={WC1kq>h;5RYd1JMC3`DlkI z?)&@Bq*1o#>4L;Tmjw6lsJKL<_^$h&(xPnDswuhw&ynBqW zU@GKQGL*jL`nkkHC>glt?1lOXoGe;>*>|5sF})*m%uWXNLEd;}tj(Uyr|zji9yoCt zg6ShGp3{OgpWA|UcP_I|lI@#kw0RWmV(#labL3~<>C+9NHWQq)nmkK)bD0|ZeeOj% zsDym(>h=0BX?FDXnbi7*_VUxhDNk0aW%1-!MJNAM*}`0Ra<$Jpz<7AQdH6kPyEgD= zRsG4nt1lUvv|36vR0VG4*T_xFo-U*fdsAz-{3;C?VGmUcBc^SV7{V~3G$o?I9LQ!C zmpY%?Wyy_*7;n(0q1m#2$_>-)#g~}z1F|!K6F50=sgKycKvXJFVIn?m$|qxg6@s&? z+(Ao7n&uXLegvT2#Lj`-;MF#7xX1qFax##$pYS%o0c+TSX zKIX_gbEr9QSG?``lWwViV0F{+^Y-QpYknDLY^BkO-%Yi2qD@&=Qe4vp>?`g8`FGm#9N~%v zArbV(LFq>gc@s~}@rTB!*9`f>`wE0`7Ae22k8Q{~~S=RWI7Ee_nBN2IzD} zWHxn>M}aQwY745<(45}0Om2mwgtVsVU`*B15omL3E3xUEmV05z zb{Cbr6+ZCu@48yc8lWa|SMOk(SdGoG7)$#@c+nZu5@mJ0vif*jUxmC1SyHi* zGyZJ<84>xLa6dUJGRL*{b_svmD)@=shu--}wpe8YBpeQA%;rL|e5UewwoIu&vMdhW zV?-DTPjSnB(v7DbQl^?Whu;T*k=pZFbHs#{*bzv}kF%5_UuacTZnoM}c=|YM`0~iC z87nbpb8PCqNgmwCK*qqS*}U#t&+pu1y$LMLbK48w65u(YdfQPL$K90pY@vftKE<03 z@z?ogTxt1}P(JR59C3NagN}qUuhi*mj9l`WD8=t~e)ZyM1q=7cdaW46t2ya0$Pa-7 zuhy^bA3zK}xzO;*0KrDS`WSkF01NB!O$GGW`gI zrU%dQF!wP{_vgb7fiECWm_9D6(ncgf5UWyWnSrpd_+WW>i$UsOy8kb?$7X*QQ>s}0^rnXnfRB3_FvWJA`SERKwvo-F1|54)yEfU0-R^D$w6 zNx!`O)e(K3G0B`k6ST0@#WGQC%g#bVXMAy1ZTgDk9#ZSJ{$9RelR^Dk`{CC%wi)CC z9#&66CKZfdr9C8(M07>3aLN1wVgjbi$8K;ZsUJj|i-VqFVf6!I1S#lRz+Bd==t7OQ zff9ZMD{1^<1YAU^XI**2G3HS_i6#^Mf_sV)I%am?e4$dc&5==Tn}0Bg5%7WrbBlsD zbxPT>h2}XshVPng;u7dMBz3=o`a*4xBjE9uBn*?5fR5fSL=e1K)V7lOv<0iQsvyig z7g;s+*kTAKPha%Jf#H}PcHE!#;1vGloDV!&$AJZt{b=*&#K&9BT+P`I?fK%C*!`_D z2zmIIKdIi^H{E9yU5*#1xuvDduVK2eLEZx-2=Oj0Y1HZ=CG<5?^viMrkFdZ=CX619CuKC2D}N}d`@2~ zk9%HH+=NNs#o*B(q{pSMJuTq7oE$INGTymg19%=ZsH0Muh3Z8w^^bmx3TEO?AC+WRwDGJlMr=uN6v@_jmiYz1 zvd6j=y}IG(JCh}F52VFy6qkjFI(qP}AY^eBCWxl?P6uq51UpFXqI9k~W@jPZ zWVqRIg=~xboMS~EMET3YYpPFdb+lp}l1`E#sR8XqwuM z`SwrNe+(t84hbzQze5Pyjx%u z15C6U_=TlDhQ)Su1A%y$Td?2hL0~s>djo`e1gJ4?F>ZJhT)IzSz9b@m+zB|Gq z>m#*kA%1wEfm^TT(ryw~_Q>cCTdR&mlcb}q%n_xIwCf{0_Ln z`bVoJtO6hY8&(V3{GY8}d=790q@<6EPz4|Oo7pt<1f#*|!Zjwl09=V|b?{UJd-T`z z*8yq6cg|q7eQG^u=-|*0*%VEiSS%52(0j2vqG;CTq)mtNdIN5jvP~@KOvg)1Qj*HL z1*}T6jK5R@+p3Vteip%rSZE?kjXBpGs%FrczcHv96S8(poCMRoje3{v=pH)OisG|X z!hVU<^a`gW438dCsBu5SzW#>-``@dZUPsk1X5Z^9^{vcC`LDu8K{I`azuEkK2>gAi zqvA$o`s9#I zq1P)fr@e06a(Z$hK|W$6Hm`ayRYV2*0|gs(C|{o$gM=_3J9dpLAAwN-r9$FG#_m1ZQ!jb1~c|QfMWXW zF3FiCbEeIt8@=_-l?z@iV*lxfnk(kKqmpkib|FabjDIqb8-VOT?H-~(_+qkn6zTgT zh@ec7Ukc^JHP_6?TBv(Y`xLg|KUy71g%f|~yTo=TKn9Gw7QW%;9PK#JV)G3r1 zW|!>B-p9V@83=Vi??(B%0(lk?6fxym&z65wqc#|NOD}6nO3fF>D&Ck$PR074kL{kT zy}+FBg?j$}k^ZX%`-jhx^TBqR2NV=k5ER!LRK^*UMg+7uapB-SQAq@p878N4pkC-B zbD-17>+4%O`T)^VT8cmflx8{nV8K6r;=R+r8Po=!m#w>zoPCj+5zGuS25P01kULgH z1T^S9-|1t(f8Zce>EqyiLn{~8E7tRSLJAI1z>jv6Na*xHr(W%zp^=`Eo`D~LkyMe; z6VOzAJe09u`K!LEI3p$oJ3syjnFJAx4Ca0BNtSPPLi=x+DC}xzZ0BTdYxBSMrL^p~ z3ku%et&osY61v6WCV%m;%(<3)tN&Cg#hvdIP2m`8oEbtAmpfx8jKFqJ3(rLU9T zTFvUbo4B}o8$QSK)wU{%>nk5gA88*6_RIH^?u)G9*8G*Rc4xiZR-FTXl1caLg2OvE zGq&G^IHsaG=hk1Y$M#@+88$rsy~2;+ca<^{z&wGW4-mq7*RB zI++q?^)kz1Kt0D=H#m^FJ9nkE{M!2G; z+4-)THT@c0Pysio)tCw8VaFV!^4O$CrEOCM@5%r<@(=a&E~mHbqG>C3J#VK^9Ex%L z0E)QG5&scH{kQEDz7a2%uQ3B8ac^;N6srCn;9>Et!B`Rh<&F4X>5LxOVTScwKYj-$ zQ(2}H$>}?yFm{eXOQ#NMfgjov@kM3rNheVP4TwOzX*t3pq6Sxo;#asG^dEVig({0? z`t*>imIDol>i?qQfYTq$yM!_kqEn~N@3HBniNd0%m-xMlVEctGuZFypw@~@dDuq9F zh&{{|_AcKZjTd{cV=Tf5xH8ns!LW$e+y+;@}a~LH@{N+G4cun&5!KjYZVd z#MDgE?t=CiRtbZoWuu^FF37g7{DYJ+(a|-(GAVF=*$uIJ-Hp27W!?6gnl3mx&m%XJ z3F%JxJibj|zD>V;eY{-T+5(vkOY>^j^~R9pzR(8&M-<|56st?R#Xa`j^hZQ^Ki?F> zF?ur*?F%l2WWqJ@a1HWhS3y;#Q5CBrf3yacsCP^lL=Q4jWgFjOM;xnn_Us9x($=;1 z?HQxi)V7Z8DWlfZxn>PwQElDtrK8zWtia=7TwR81okTx8JizLLKfz{Wo~q_TldE|! zq)?}7EJa!}3U7k6=Jhykj9?x?fK>B2YWckx(Ueh^!eFU;)uyJ+-4U%VKodOdY6)b7 zS-J&uOXjXiVS#|W^|yKcES1#Qy=88T*|x*y+-xQ?*;#GOkwMmh!n8Rb11V`yT%5}v z$p<2ZQd08J>5mC}M9Ngwv+cp2H=PcOay_^XxsYh%Je}rFU?PJjUt6bi9+@l+LJ=P) zz1p-;Gk{Y0&0|3vcOGqk1PGE@c4G@@Goz2YN*6yoVa%dP89!|#S*YfWho&~(#w|^* zNXE`LuOf1C9W4pO=5iSE3tZz&hF=s(=Fv_w3Wa5%TeZd+JK{S$avN#X?Vb>oSeiR! zR=r8KdQ#!p?10e@Vu}hZ(W5USYCx%g2Mw!)iB|v(tNH6rJ%~4natHByddEY~W!MvM zXW^&6>r8};5?7?l@7C%)?#`M$I-Z-o;Be8|qCLTTxreW+K}gIL3>dVA;<(xIjGsJx z!hU7{M?5R8aNd zwaSrIG7YGv)KeYyTipFC=!S-oQS_)LR{nJmL!{^tb*O?V{!3rCS~I&ejCkqorDSF+ zODQFWC(%S2v=h+s#RORBX2nmouyiJ)H&4jV4-cBn( z!D?fi(k0_B1A)zz&9oOCpvIPy#lgg5K(6}uHe3c=BYIBsv5z#~o7?oy8)5R0-dTG*mf4 z<>bf{{UOOI!MQ*JJQKE$Tm-v*;?UF^gjDNe93vOx)Sxn>IZ*Bf_Is=21V@;)il>W( zdI!r^yXQ>v=1cFTb#z=a+!b#Xuplr}{@$MQkI34c+`G=b`r`we z@!`!~6y@QH$)%j|gC%O-lqQa5wH(>{y#J*}Z!=%BDe(Fm@H(Xt7OIsc$ojbv7J}6| z@Op;YndSh8h3D_P_wLU9cQ2>UVRPfJhN9c{aUX>9tj}$>_n6ssE>P^U z3av11P#(wzeFT^(u@;=T_9}@feYG1~wE>ZmFImC_$I#?7syAE2cb}@h18k8%i7Pb1 z4^oCOaqd;BhXc4cajCOWlQJ?NWqrhog#-~%;z1&YFiELqaH&MlIB_k5dSSi%i>f|i zov=|A)56#hBc;shDoT&?`%o#7!BY#wcSGgW$ng0l>Ci;vw8-zb(mWL6GhigR*K41R zF8-7n`uSiK!Z-&LH`=o)PO-~&)VN-E3eMHCR#0<5JM2uvN;%XnRCGjUb|AIC^5PyR4^X*EOMc)P_GQ? z6AgitwQ%ii;t@ec^l>JpAO4e^RlFCu4(VTk)i+Cw<6i;U|Cq4FZ{XCeub}AmXVq>dc~^4wDxqlRUVGLy zOfKzh#OyRX9Q}&rqgE5i&(5R%=rzFaT9}w~uIeFZ87I8DlG{d<1G#z&6^sGey^HKz zh=!Z>h%!0-ZI5r@s_FRB>-4Oh(K0UW0Q;6aSJr!PvzMYJfTD$`r zP2lb+6hgS)0>i;99r&w+bL-=aE={&mTt%oy*U_hC^)^U6)>65C6=@}Dh5{@I8+)R> ze(N2tVg@;xIak!RP+_3E5_11oBt~izXeLi@=Z>mD;m zE7tu}O*HjQrP@>U3_dN!aDnRu@JVl=>Or<}Pr`;F8e13?f>Re&}%fef6~l*t+WK+qnHD%$}vXX|v9P+*O_9kMG}W)8M*t z=zLfVVL@dVK4v|Oj>{+6IH5+W9QOzBp6=Yeu9*+aN}HDDVv4p|P$HjP-Fz0s9Q#k^ zUDoycP3+b7b?)0q_)DTBCO%25h8$C97#v=9Y6Xlqc{?%%T&RJ6!BtPk1%cWeFoaB; z*o*|RQo*x-Mc||Yh=+W1s9368d%o5vV<49V%=?Ku7gsl*BdR%Z-qQfrqNyJD9<0unLJG@&NCr=6^26m8il zkVz}75x6U)MZAa14TgPr_||aEq@;SjBM|Pvn~$SbGXiU$C-<`qNVu)lZtOZ3Sowf6p{F-8j}zbx~b}=MegTk-uAW?i|K$I50dx_EzyN8zmDkE zU<JywffP6DQzRhU74J2M7EB$F1kJdl#Kq*vmzZ|j z+ESqMo0TPM)!k+&!sxR zp~5owo`AyO=x)l^fmHtC=Q*s^`6lF_kmukOq!HrIq{0S|{;?P>4RIKEs zeLQ0gqnmpT(hUwDGf+^_wR8rk5V6mAU;!hY%peF9B8|Q~ARt&+kLpS;+bgQxg1lmy zLKNn3gq#;4>`ZSf_I6!jrI%`=D3)HOkV=*bit$j|NQy1N&40C7o4KYP4d~c9pn<5p zN{B2cF0FGzDZiHHteg(YMoUAEc}eFP&?Hs0y`aL}$@wNNf=2q5f6p~dj%AYb(gaa3 zdXBCrevJp<*!Vn=xZavtx(EyE{>$zM`R1ZK-rhn6GN;?X=HWmy9rM+Evs%4flm~|q z_h3kQo=BiP5RPb30Gy8=*&TLtlyi@Z=m&87oUJ`2V~unA;ckmYnH84!GJSIW6~BL3 zvl9#71;Yax^;XIf>x>zN-~3iocKI#_)ivWrq# zZ;-OIJw?naPifs7;Q$)E)T3B@toE`$!X&0%^6Cq4&M#SP--NzKm*8FvGTa=)#(cW) z9aw@fE#d)HicU?!a%3&)E-|83zg?0%sO1Oq9>Uf@5G{T?>qn=4--aB=?e?xYJGM@l z)!hI#&qDZ>x6S+N$?D4RI@_?OB`0BDe)KBWdX}TN3GPHUgr~NxFu&CWm$_!2?>Veu1HG5xt!zK%f&Nmaa9c=O=$%U_wTQRgD9PI>MiEgP7#i22=kuz zC%?5N%Wx91m}ea<4_R<0X-LTy8{loCSz%S$YT^7<#+z$J#phm>MhT)?8;9&)=;F zSEfqbNNz8Mz^P0`qrY#(Vsv(6Zl1lquFx>jM77Aud}3nozMo=h77tzIh)`?Ae{E4P z4qsT%;YQEwoQ>}H_D9%*{n0Y!;_Z2SuO_2@Vwx(^*88Ty)C<2JJ2SJ=jyRz7S?H@q zoXw;LI_JJr-JRX0Zr)o=ml;Qi7^%uLXYZI6F@BM;(yC2XtaOerxenuxSVWC%?kVXPGO5@($cb`@Z> zN%U=zrZgT@ zl`+wt0omboc8-s1JJyI}VMQpVc(#PsCQ~|$#+r+oxFn=4S0AU*{!L*nOZAd4Oq1)F zw1S&^xaUkiW8!wQP9R#;fywQn0LsSmhvsIx7sR+?kC%0D!i*HX8O4MB=oJfx_b0#x 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⪚|U3VY}d-^qs5aFtVx+RK?q( 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

(TLX+)v?_>QQPREAAiWGY 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 Xlz{<4C9qfv_|u{aqy=0+^o00-JA(}_ literal 0 HcmV?d00001 diff --git a/libs/commons-logging-1.1.3.jar b/libs/commons-logging-1.1.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..ab5125407a6a77767330e29a028b97c3ac64ce18 GIT binary patch literal 62050 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 literal 0 HcmV?d00001 diff --git a/libs/httpclient-4.3.5.jar b/libs/httpclient-4.3.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..1db1225f359a37bcdc5f13075b84176a688a8aaf GIT binary patch literal 590533 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
q?HQms=&3 zGk@3ZUCyA>T9EI3_e9=RVpSz5qGoYrQ!R*QwWvpgJ&7fxWBhY~T6y(nCQNtjF8sU1f}wCCyaj8{va-Noo5w(3w6-Y`<&I3d z=VSq%D{l4Ttu=!=OWqkwcK}-+mTAFGU+k+ZwsP-SyzXm_-w)*=9W#-a)?y;oR4Zs*HU*q$z*koXEU)J^ zxy-Ih=Qp{U+wgV;vvWw&S0c|1IEo`q;PcYBJ=AJT>Vce<0?s~dUvKDt?FcT`Q?a0p zp0pgoN{Gpaeo8e;E$)q@rZ6xk6hiymt6ph^x1siHgBdgnr9}}>lC$6=E6!|WPl;gp zsnJ1LiN{0y*9Sym_s(t5^DbOBLuM7+NPuZI`94tbZK_UDm{O8n~GXHOVHIwS6ra_M4VD;8TXVusx^W+Gm;L#FdIgiU7O}^uGvY95gt9H?o z^DsQFbKhXdPFv1H?pj_yaIq+K6PrvPlX%_qmiv>>B2wC8xk=U7Dpl67e&98yK~LaEDfRf&>tw;^LrV_xX}LY3O&#Euir(tKJCnQyxSP zFi&h!_|4XEO7$bR6fP6Ol#-Tk%~BZUO|hLqFvPwW2J%|q^;(T8R{1b?Jub0(VtnFD zQMI2+EbfeBiSn5G21^z@Wb9@%hahYAN2m`r2Rh|M=Js5yQ6Pq{*P}|JM(|5 zp_R0iHL>|t*^AgZ{jVm;WK|pG4YB`_+T$6eBxIC8SyX%^4XuMRN*XO>ECK}b>!9KF zg-$m#xvUj6=OrB1y)S1@d(6!;ewdV8wftw@*iH#6+)I1~xwZB8inrr^^Y7WUpYJ<< zFYTo8TtR+{ z1&#Q7GYcBi+TfXDdp)frbd`CHX?-L_v?{8XKw^sy7l`W1H?L%`%KvtXy^5E zpqvhU$0Lex+!+}9H>E$-?FF@_MQCDKQV`fxskXIeFo6l@rni}G8O27KwKPlFIK?W`lKR&uHV6bVltPgI-AzF2jdCQ<-h;ceC1>?AqW zO8P_58BRx(ErRn)fwD6tMR;K!sv--Ix!X>GVI=lfl>L-{ByBU`hBo~WSRoh$V;)&daknaXNS&KrDt>^XVUAW;!`I-5JPW-qUfT7Ra=g54f}q3YhV4o z3Kmhj#n?8*`lrVOa4$1Nx1!b^VGYF+O~Qu zI)Ya}g$-ngtxnh9-QjIU(Tv*s9pxCU$S7kfPcFzQCb-*tx(H5esZCGk(|BCilFqcu zzS-15`Q9nO<|+FV4p;67rPO_Hn?d`pkd%_QC#}6-AV#w4y=3Gvtc&1zm9M93XbS#n zwTIRn_p#p4lc%rlGL4Qis0a}UTkV%g)O6+Zdp099N(m@&6@cKD<7QfH+vDcx*sgnw zqJioBiMARHK$K9a@9D7EWAGQl3-xx04>{rsIWJ5D_yj#5uIAE5xE2WTh&eDR?rL3a!JPatFu<6uJghg{(Uwh$6)Guno&TR(l>(W zWqQ2rZF z&>W68{*Hdgh?PPFrG|InP>Tl7*ggPb$MhB-L%!jKF3O0edt@IQW5?|FC&q{-&%}Nh z@<$oO6i44aSrno8O(IoJlmWb4U0S4JM(#{Qnt=mXpeEaCDPvY2EOORS-+*G4(THwz zeh_9(pLEqdQK2TkBTOhrRU`kL)(n=s7sV_I86aLSTWAqk`9##%66$`qMWi2+&r z+r1|cO=T+&x80{O@Ua>eSnsPKXp)+a0dwx@Ec_4?2OqU*Yr`Q-3eRgQH34VIncZ`w zU(8zpgk>n_H=tAt)D)`sV`OUM>kZ?T|R!}L1p zd$J1SBA;cm%qGu`#rIS9+YqG*(iWCR)qOv81L6O{>T@JB(!wZ;{t9Dl_F*Eg=P?nz zsR%4%<+3fE3JTUO26oi<+p|0a!cW!ampNL$%2M(xT7t7#l-*#wTKFg?9WrU`u1wd% zvk4X2CG7@F#j{wDOCtU@29l$7{9F%C88si4Ah9@cHVVG^73MOK?m`J~K&g#IX;eXT zqSI%%!$MvoUY^Go$0r{2OrL%!2{Ykn*sWwK%Vn98EQw|+*`?S55u9Ai5jhPlJ2T2d z<_2($qBSUj9Mn1JuXlr?rFDeS`WTxrNYaXhOvrlA*I(*vNB;1pgCsAuRsNy~F zf-nOWtxv1}+@evimy|0C<|DqV!OXg+?69h<#H?kpyFYhyNcgojZZ78wFz?2aclR^h zEO5du{Jca@;sQkdlCSj%c}T7RQxJ#E##u<0L#3~_rIt&xMR7EZ0t(90ScPzU z>(iBtsHKu7Qi>p3FS6+{Fb(0T)qbaf*=Wxwu+5&A5hHLFs|oH1S$zpb$s|d;YF_q= zV$z7O+X~&1DNgolHX9sMA zt*EJd1k%2cN9pDB2kKS65WT5xM&nceUadBS@>01R;zK9wx1r_>lH*#gtHId`8!Mio|=p!CD<~Rls-o20F+s${@d3@%@-P1 z>Hg!HT0@P(2YnIk>4X5`)K29Cqo;7v3D_-h|9q|4S+g&A30q4<)q{2Z2DPnl3)xw| zI}hsTMnNjp2-gGzx(BG#KB)=Im;$CrwtnA{Z0{GTNK+STEQp{MX$Nf^2!%NudF-Z_RiD|Xqxo?U+Fyj21~x1_hW&bs5QV^9&r9#!FQ>w=q7 zjSWyV^xbavvGqxOi*X`3hr6StD!xTG*b?nb6gJPFcsC6`oe^S2vp!fC_-C{{8#kXI z);zm`kbyYIjFB6PV#ggaEBOYq2l2b9jHoSrSue^`>z(gS>)i4{%o;qaCDU9ipIwSS zB0Y~XF8E-E*ttKb(h;JCgQ22wx6j!l%1tUrMRyZbeYBt%mHgWrnaqytVwNCTF$hB% z_$-}RmL|DK#;aL5Curk^Zg3>Bfkr)@U;krUAm^@tfwr=wLN(|wNuXF<-ZE)xTR0IJ zbP1AaV1}m3RkqlnH4q?3FZh7mM@Ligh9y&*SjHJn*h|YY0_HOxq_q-sA0GcXmq@mR{BT*UxAQ@>R7}%?Jbe}XU|`s0)JM}XOCQuEUe9u* zc^*OTik2{LR93~I>(CiYFB)#qk2c7xG`Mnj%PPee!X0aj9Zv)QHziBF$_9cJCRROV z-weti`UZZH6d1g-yGLJuk8OWb9w+%h*LK7j{HG?ZuK)=qAd*x0;310&wCWb6JG9wy z76i|+zt2DOuq}dd7tV5Q@%MmJ+3C&UOkO|yHv?5p@a(pO9x0%`PR5^8POG9(n8xne|uDq?i6Sh+k@EWIWK!d)K+=_bxfH&N<>)f`9z9 zXknBVwsPvLtWQ>sd=ld$-Jy4hmaa{^ZB4msB;vay*MNL7BYTg9b1K|)$&v3$F2W>5 zdpHDX=SbX*3{k&v8);9!-(fDi2#3maXtFP^90bXx&UC2i_b+gg*=jA4B8klRJ4dlo z8$aYJafJcjrW$EC1rrRzd?-Ts)){gB1tKZ%quKdX@@e3+xZGmEh z8u68tLE1hs{&fETaA^zuL}6VX1^*QBi>z1WrVV9u!CRF&Xd*s0Y(L#Q_pg`Qzs@pR zTEkzI*bgZHKYf(V@e54byY@IOH;1~WAxT52)ul+m^UOaNlp$UVn` zPcm!;apoGyr57|lTG{o}L9ntMQE&P!+zlz_zcZq*O0<8n8NU)qK!Tlcpv#aQ97{f_ z)Mg5d=)E{%e&M1V!our`PA5<NAMfRX8+B#>jD!c7B{B~4O zD=EG`sdIqIJnkv@#8>sY(mR*iZ2}eyX{q3etg8qHX4NH!dU}=pXiCQ zj+Vj|UbNHj1<_0L)C2v3{h$5Xf0R)SG?->PPY??^j=|MuneKa^4byXW!$aZ&%j zn&$sXw=)G)B^A?(h>FrlEj4Sa4Z^m(%nJmgk%y<P1y*w#zb75hY9!bFwn?LTT$P$JcBd}2$D2}my zhiy*yhMVBQgx1Z2@E^L;=*+l+?nDeIyv*yaoTG>GX*v~N8igJyA*tDH@5;%pvI20M zfp+GEJKG4LGmn*`GvqrYlEI`~y*vASeA&}(=|ZM>oT=pudp;M0Sgkz_h3{tfHBr^0NIA|X)^1ye=SDBB9bzriXl{;>(J@b}1_?ywl5RI#( zqV=e+(Tevcq)6g#PDnuH?sgsv%zX;642B8g zYG<{*U(thzWN@AQ0}}HHA7r4EHXLfOCm6PgA^m`SOz3sx-~qJd!AtfBU%rv!RI&8M zL1kGqm-ZmVYWY37VF~UUUN{-^4KJJ{z7li!*@J&SR#@5F`LE)7{?~&~aU`MR`7Mj? z{5JdY{I>`H|4+&PzdL=Q-Eo(YzjA4;T&n2TVp|>_W@&j+K{kxWp?1-aG?#=7D3&yvV+wQCL(amn*c*ouN(NL><^~XYe^Q{{k6Q8YR6hi1af*d~ zJy9tr{QeAMU%1e^2N+hR28E_zU8wh0*pdxnr5rZI;)rFv7?Dz&Ezn5}M=Y@Bg&Nf- z$i%nXEadVPE?%VVC|yyWGLoB@X;~8yN}u2;?Jq@;S6DBhi1id$C1gtl=9f3N1{4&M z^`b_eaZZ~QbFLgsBSjs+v6kRiVHLzMa~Ldhn5u0w#`JAhhm5AHc3H}tNsEOg59aaU zDY}m~<>9SSP^xsx*{#PYs+w_BOElxeh|_@Mny=w2CZ{Fb-=?(wjvwnWKJ65$GrEmF zg*0XGuDSJ&`dmU%iw>AS^D?aizYR7u7o;`x5RJkfrz>nQrbaJnLu!m=%3|P7Q1ONi ziM+0Ff#~0~3L`zC#~zj*Wc+ADJKpbQOIfV&9iL7yh zC}A4rTDGB!G)Z-Z3t26>G}OpgFpt1iDU3{E@pJ`GZiPC^^hUHijji z`2fXbIxR0Fsh2}A+K4!$Gc8N6%5n>bVpZ6d*YMY}6N<15olj2i*>wIQ#z+=%x@NBO zyjLXNR*WIlW^nPw8ib<1q$wD8CMntyyMFBa&X?h@%wyz0U3;rPBZVuS2C$w@B*X>@ ztsducn^5g9B4cf?L7jtkOAE>j4?w3sj~xOtml~$WBK5^8mN3;Y2N)1#e#g%YqO$et zaQL`9j~ht|LCaoAKKrN0fF(atU$0A-T0Q@2ODz|8OJoe1CqmjNKc{AWuhEcUHeTo| z=E}+xsG_@^CjA4Mdlxj6UmBz_cmv|eHEke@0RI?dZ`P~-7O^n zU8UMJBk_&Z3tx@q1L--8(u-IPtXARHK9ibwYi>+x&PrMl{lMul&|t9{L94?*fqAJ8 z-`Rsl?m5A$Jc%!d`}(bIHu7McMOlhc&!@8nM~*0yGqp^yBq1Rg{_^unLiJ+eFj#)c z=TJ*eEh(Jeq0ww_hU6=d9qdDOaOcJi^YmB@|uC6@~*}@_f^T z3EWLZ@&y#=b7o6fD@p>KOkr@I>^dHc)rJXSDJF{W?!&8aPDM+PqeWk1YR<~{u?->- zcA+b8fy=d%Lu)!tsd)Vfyz^FD9(^xN9Im&0i&YXXx3M1Ys7}jVY>LhOQv1!iBpB%V z%4_Okl#moyUjDEomSJr-uR!`$Ba-G1ec0S>OeS>i)NMV{IlYgorhJ=^B1?WU>oOjm z6^%7iNZUp^%g(U6w6djq{_O~WE^=R5wEk(-o-eX6d2x5mS1MCzO-(o7WXQH7N{tk$ znkqPrBZu=q%a!kt!e^a!9whU$3KP0Ou9}<&I<}w+=Tdc%9%GGOj?6XSQ?O^EXPYZeHST0*tdU6$vw$3r%erk z8fi_X_O8$2v%He9@8R}Jb1eJ|9HQ|uUUJG`K}GiWaM(1{yT4|<)^%X2CCL`OGev6$cPTHRq+%WvY3j$dM=dD-Yi;tX1PeC@O((l@l}LLS~%Sv^oHB1ehXh5OC6f9icSwm3DHeu;-Z@vwNBR!xx)rwN>1 zx(+CqZuR@H|7Zpv36~3S1vqhP5qR}Aw4MiU!LlItENu%- zGTWR9gHL`qq#DHgVhZQ&5d2<=pzddlL!u>B}7II|kvT1HKz?}w>85f!W#4eaB z;aOmBi#DeDViJ8t%L+e;rzSUobMa1`a`p`UlFRE4HB)SBd!)*mk{4Xvd~?UhSyAaq z7I`Yn(71p`3Oj6AZHPZU*y4ywin!V31)EL*H@te7j|De|yz0WN2QQi=y0-#(l4>}_ z5?2i)g02Rr$bVm8cdi`dCyF$@(d+pGWw|`B&hm8s@@b zQ7JXc(0FFT9UWC$%GU|nGn*qUW2)DhOT-z{UZ=WHPoK4^P3jVIV~hc!gPH(UsUrZ5 zoI%8Q7o%aR_niIyQQefy{8o0XjGon~v)4(~ z*pvy;sl3RL+}4-0_7cxV)i{aP+T?4T>Vz9{Dm6DL%5ssGb!~bNrFX3pX9!2l(}z!_ zUN~8gZ0@FtK0IDjv-QG8Gof$*%l6Vd5V5N;`^hR?r7W(*>puQpY>$> z$!&od%%&W%bt0xV#C3%>>rAtp(67eqTS0xGMpo!TY|vESQ*M%6P z)cQ6EoEwkFWihHk@N}V3PS9R%q+2ns2g>dBF*k!|!&kHFg>JoRvj+C9tbo_0J@od) zN-+$w3w!UTx*Me3T_cCo>u!>%l82y3SIJmxT4iV6FgA~5-sjjal{sU%u=!ts9*c4- z85(U2Qt{c{#aDWEsOL0gZS;vt)j_&vwv=I@9*LxXNWyN5_QR(0 z<7OiLRQTp{D7$Hn|MU8<-`=&$_uJKq1JJ0UICDT0GG0G9HpA3|G}Z={b32U)e@F(B z`9JWvF_z0OgD8^dE90H`J1=M*q$?9_jG=bw#~P+o(_VTJA%MbQJL=#L(jRI?IT;~? z@4#=#xpa358#VTAuQleU=O&ouIoUDA`0*`y>LQJ&!^{LNmmTR*-MVBJv8~2m9M}Wn z`&>)EcBsLRChT1PY5)qv)VmDp>NjV*t+1PLuciP^`HX277c*O8J>BIzrPY3Ar?nYs z;6)DO*=;i9OEfJ`huwul@pzLoimC5T>M>*VG}C&%x~w#{Ryw@Mc66F`v|8z@NeL%& zdPUwtQzaOs=ppS1Gn4B5`g$jt?B&(!ewON-WOIbkRX=W?hqX3tm1$)HrFfQXO?kZ{>?;uHpGV!zSKTpJ!FIZPt+Qi@11GQ!IkASeIMIfn$s!i& z)lpuo3olpT4*cP`Q_a#9>wR$DB32$#-Kvs_m4kA3nWks~Lw)-3)*dJu)4GF>cv~5g z!-~Vwa1j42uO}QqiW2?gsvEF&2fCc)Ssi#d817y$aXLBdC{2I-WQQH)srgx-f--JeJiUBZp41eSuc)uQ3&-Y7tF$lx5Az~zI#g(ronlPma9Lb zR9#EC1Ab9lZ@P6Y%tw7KLB3f6(RiPPEnTMkp5=n!N8p^khtiOVhH~7^*bGXP?hDDp z@sH?txjk39O&?_*x+mMKX1?yH#w(&!5&#@wf$_>rQst6|s%~C0&MKc;h*xDZ7V8Vv z2=Q>_e-CFeBAwq<62HOi7$Ytt;jB+bz0;D&=!CzxuOZ2-%@0)o@Zscs{-ARSI~oxl zB5U{i;EgQ|)t--OJ!Bp4q80cDJYL7G#Sf{PK~bM_ipN{Q`D`$}v5LQ0J%#Yy74g4m zJV9?{2T-QxB;B(=bsZ`!kY2eG=@04L&t$kG49(yHuKs!B*&qa?VYW}-!;sHD+@4S! zsljj#m1~jY9L=)DNkn>*n5Gw$IG4?e9M7=%OT=y{G)mJJG3PDhJ-IpQ-(ig)uGU{g zua>nmZ0?ltjUdFScPo(A@!Pow+gPmDv8i0GU2e-as?)Y*g&$ySSw(c)FB# zmN_A=Iqe;>hY}sd`fRiQ!Fy?!f@NTBr+?cX=033^TD8&+#OU?a^6ic5AmxHk32UAI zoU&yJ+ZpCxlr<`z&5x2Cq)*Eiox>4kig*$#DiZGf29CS1i4Nu*inU$&-R*==^X(?N zS~rxtntS`1mW#+NLtCiO;a+Z^@~n%vp#MiAlbfeQ&f;9gB2wZ}Rtv~9bbua&8pYOr2BhwPttlDWFMi{`dcdta%Sma>n;k-i7z5nm@BBf0y^RU`P%isgQD zQib_P-<@QUXBVo6uTP@fx53h@!9TwNMf8g=9@3xNYwVlzFF;Yid-JcLJ$r??drD)s z6K`VxoE}{$a7v#(uV73*v*6#lUa3rG<|0(M1c!#e^IU9es7BEsK~h$SU$= zEKIQ+&foG`_|KyexsQVVQOBCBY-KWqA$`xy87+R!JZ2~CKi=o6PP8F$vc@<2$A!3I%gUo_Hdl&!!d1gLa3E&;5fM{ zAvnWipNb7!+MC;<_70C!T45n>)`s}-W^nf&K-Z*&*GX0L`ppk$q6(sPc(3^!K%x)9 zz6f9!BEO*ib0UtfV-pme3_bEM=B;s3s0tM%0cBjk8uS_d65XSD(w6q7$Li0o@_?}5 z>+zy7OwV=FaJVDS9Q53m_(I6|>qxGE5hIi6q@etSHmM!{adzIP!DjnJXxl{ia)wZk z>BtPkt_}NwIqs^ha|ZD(h915DuATv(&6BqgW~+_Jf8T~)(p z*YawF=%d|Y+;=-;>fW>=$8P#04! z;yD?0`}hU9%{w(ybn-^*e>@b|@e_e4a59p|JapFm)`8f!|9s}>8JI?Ja+hQK zY&q6bmqW)S)9fNu(1~t=772=SdN-auU+m*ak!)cvFIiIxtGB26;dfuWP?1(VK8}|mc4Vr@D15Sb+Sc5 z?cS6Z>jYC;`g?*yMW>YI9g9otW|^ynCF8nv=Tpzw+UvxNiX+ylWj|T1)Gs63CAnCAFb{_#163xW~=;gPvBS~a) z;zPg2F;d5lB8o@dWv?%7?@e&tUA+;V?w+xn=*@M-ON*Lsqt8K|gE4=Q%VXk0$(~o~ z_W0V6I?j5qA#bhm)|_`kZWU%QNbw;V($iVwIjvQnFDGiN=R4||5%YWpnkJVS4>h@WzqQp`uMV8Btz$dw4d9Gol zzrM%wYiH#$^F}yOw0a_*c4V zMg`C9-3MYv(#^t6(c!chW$fSS95u&Dg89af@Al2~X4b6;zET_k7_VagI#4iU!jSWq z59RFf$<+N8&n1Zq!{o!Qsn?u-wJQgrL8U?T#`no3J^i#6KraT%t!!ZEBl5B7*}ZOA#r@2BcyG$oEAm}_m~TE)i!yewx+9NezqMR5&ZXN^Yvi|4IVWK! zW?i`_dfNRl$l|tWBlB&9b5jfqG>8#p93_P!#QVaWlYtf-Lz`0?VG_r#lnUhZdbFBX11WCTj zVF2xv&K!;GBas6W8f3-pa=7%bTX`NUQ5K~o z<{@VP0vhX314DTNZt)Bu9f>r$((nopHm(QYjd~sgM>4ZGL`P2e^rDTaqATiL4>RS; zH&*_k88@`!#fWBFE0mGQWaOxa=V`l=yD^=`>uMm#NkKYf%I5@z7EeL+u?VYe*)mH? z3l>#o71r`MI(`X%zG>06AxjDt^e)Y1pjwp?y6t>p#281GIC~@CwnO&7)HcIHkdzHG z;a;Tp8!CpdOjby4YNv>tW{cy3#c%j&P|=Q8oMi;r5%Y>GujGAImg<%`Y4&P zm{ZTA1{ezirja|mk)${kW**{*?Q|((%m~9j>w$Y> z`qI&ZU*7^9PH1T$jbi}b4tfVR-SHwLlWz^Z1K-Dz$kbS2>1gS)O?T1=!7n@dR@icT zkG_^|fqPO|h9gNK0+LAEGEB3hQ#=Y8FdA%iz>6@~E{~PqP+kADv1USO%F$ZX17F&M zD9EM&UmK!!sy)Sooiuz@G$jM9a^(dryiJU`{aR6ueT9IF?NyodT>a)#O~vjDr$c^O z8|9=*CAte|tM97BGg$*F)fKbb&Vuag+gkRs;<|1)IOG@tgS~5WpcQB_Y@(si(K@iD z=%NuhM=inCV~}nnR`70`?7Ga8uqB?HB$^v`%U3u_-|XZ&aFZkZAd+UVvZ~7fbbczW zz=lc;r4P-UsRDWabp=(iKg~^%aPlUs)hYyHWVuGLTnkkvBDu)jb&(fxt76r-17=~i zIp#w7Y-uQVe>TGl`%&}`fuYt z(qvZU3TQbe2NvFNF=%=>A<24kp5U_2OA!s0Esp53|1wx`h!s-H=K&%FVZd*J%f)ti zLF^X7Qk7bw8ovX<$~N)+YIo#Q`CA)g{f2{y`d1yz`g-+^wxh^Mqf%CDp%k6whKUG`i( z^S%^?kpu9CiDkRi%nWF>c;k)k(QAFtV|_b{3pG+r)N;V0ZmZqjfIigJR@v#W2A{575G>9c zR{6@*wFBZwTLX*_C2qOGev~s=Y8yq;8zTDWrp-TGA>MwaS!G<+rXBtYQaWd9GZR1@aE6{V~%B(~f!` zkOX$aUQt_wJwAYa4SkUElhYtQY2N>GC2u`R{>nuK2PtWe8KjB$( z?q+U{puvS#c-C37XteXn{JcPIJK3N!1?2mZ&F6QU>EuYS{jgTriH(x zTN1hYdk3s7{Gc(%5Y#@NqkBfC6U&!{?bCepYJ&}XJq?>YUJ*ZT3iG>yk5~})JYG_Z z8%Pxu4cQ1giNj=Gd{t4|`)phT;8VplRNye3pP+e^m(FppD=4E@-pKgLNuOKn)onc* zjbpkK$REVtenH&Fy6)!;3kgmOb)-jGI|;{nQoLjC{d(vH`NS1d{EieEtHHGOX2rTunt|=kT56!>y?3@`A`zWwdQ=K7^85eZ%W_bQeQSIc(XJKk^m$Igf&in7EJN>ZUol zNn=~^3BN$UiiTZo?H5^h)V&O+hjx7Ca`(9U9^E+pd0>v^0m&EP4e`s0-YLOp$&NCG zUQl&1p?-Xoe4>`w7-zuu*oHfgk4ds(7VY>Z)6s>Wbk@6xobN^-VDKXxxnt!qg}7ze zATTo3LvydtfWM;o6sJTFW?x{D1Khvcjn`?LrEp6R;Fx*kP3gPJt_gc3)j$Q{pwlKD>bnkwacI>1ZAGyGWP1 zgDktUHf@Dgdu85&u8P%~`$amkV5X9(ybg;(L~>X(woPh&H5~du5p2xS@@yZwlS86i zxkAFrT*9>T;^yhluP_^j#AYE|iIuI)JSnj5JI(t81(mJ$z4ILED6f~{2-TjGEH$TO z%@$@w=}|mmZed;W%qVc?tO*V|?3gW)<>?zXzMyD4E1fx89`3ElS?W>PhRL)j$#z6mO*@Q>8vtj@kr@OjJ4KLiR5K$wCNi zW|e`s5_hz>M*R4&@6t|jyN3GT=Y-+k6~fEMQ*NS)%YmZeYSJNnhQ`MG6wJ&uX2yw4 z)~&O3IL7lY_6V27j$$;V-Nr6EB6VeSuQco~Xg5 z=5%>^7fG)YSRQEGp%|luAoyu$t|om!1Pyro-%Kztobu57(BQ9%s`X$Z14`XB`ydQ7 zo+6ez8h1d+#YuJQw1A^6Pyl9CY|VvylDk4xtL%5ZUUB>o+P7+rC>aAqYphQa+1dKC zb46f{G+bBKvU()~Bg^$Y4DBViRkHM9szAHRNreW*SkuP+wxzkZH5nzvd?l&ah}hbN z982rWPTx?-&1ODV1((dSkXbNXC<>}6{@W*bzsLJM6q^<2bp3rH;V z7kX~O;w26!BnZw-UjB9nb}R=S2}9zJ*nJw|35Mo6l0?~Wz&K4IqaGTrWrv-~F}lwFZ^VxhF9?ctJWVmpEXQJdj_bLnyyUD1V5QC_3*h`bTn9Owg*2 z>`5YzcZfeBSW%(;$K!gDihK_FQ7U%ogo&h?A%5iQ_y+K%Aqw3kR$jQcYya~STpL4x-KTHMxlhaK38UveW8=GgC zzx{phHLU!5&{56)obP~@fnziJS199KBO~V`%=aQDZ^$d(ol0UPgxXGS^lM~pbQG5S zQ)KKvpSoD6=cCYk+p$0U+%NW*QXYnV;^`2*pnHpN0_2o?N*;cD=OYA{O^D@NL5X%f zv}$lk(Gxl6{Xi2QUfysT@%L2b=X4*p_bZ}d)>Qt@-)5AMG$&cC zwGK|85oIoZHN7jOwf9KE5}gDgSncBHvda%ZTe8HvNY2Mj8%T)#_qWb>`nU_ zi^~v4tH#wdu_v-YGHBH zx7BR8jhxa2_Ue+_R?$aZ0}N5PgT!DoBcJr*uSaaNM*Z3KLklDSt+X1A7H5p71w~o5EQBW*TnRKGCWpkIuuRsbf2&t z6fn9R-N84CK_qa{QjPA~Imynai3_C;U^y=1SbtWp^z2P=dU!6e@&$F&}62tqL5&gkeN3mg8 z6#d~>^6uf*KNiPnj$(s|Z;xSb3E_k?Z_pp$s@};(0h_Io7Lv?xJ5XY%-kG`tk)2|c zhh-@KfIQfCyXxaj6lih=%FcEymjLrvC0C`Yr9*mbHe?mJP6XF;;;OZf>}^DRA}d~U zVd_{l$w9_D8GV29gS9t!o;NdK7d`f>_l;;kF5cWbOs(`Ay|?IBEWT5UYD+h2p%$`q z0=ITAFLGbn9pqO6zSuFW#}hRGrBK99HFtwD>o`=F1mjXoSXYUfoZ?V{wLPbq3}sdX z`uC^-*;h(Y+d1G@X5>CVMWO%cYt$l+{OUstxU><6^dEfA@zmglp;akXnWflXNmlU2 z*GEU41GvQh0e0tM0(WZIYi_2*zEUN<+aYc-=Hpy^DA|H-9E>)O7higI-NhC3pB_9+XQvBi&23+nX>q=8@8pU zQ`!W}GHFpbQ|XiWq3=d#ZH}t^2atj{ZAPRSjaqJKlyk?m|FW)t(Ug_ajMvkjoXgE8 zf0eejhv3Wc!mGG6h+^Xcyr`3$(_w3zP}8cNmTWu&rWUxetwxK>YL-K*7w8bi3^TXoAVx^tn)1K%Fn;rZ=RitpRSu*K?rDtNxcNce}%nkVe`U}5G<6K zq6dlJ2H{1hVAc={IDL_g5pzDm6oqdCb04YjgIHg*F(?2JO{wWM%?yIjHF{v(AWQYZ zXd4mM*Mq*-11Q&GEH4SbpA9kP!mWmZouX2eG+AF-2U~nUlAO#}Ct14RXI{7A7;i78i_!nxS!}QbYlGno-j^oC{|qXy z-h(p7EOb)DBAa1NN1NZ0N&r^C7DM1Mp@%WzR`)}mQ4f_Z^L z)6Mtol7vz~88s8`2q|)EF>$)w5AdF1sBx-fU2L;q}$|R zAkETE8m&w&9ogp+l=~*{$1XjaMW&v;Cpq$*2UBAoGt{D>>dXksJ`PAJjU9>f3Xjm|cd|#otwN30vzD zv(?5AG2sy_m7S>ah|_85`2=-_lM{=hSdTe#N)mv5%j2IAqGkOyrua@-;;7VPUWrXd zQKpb{I^#5*zlA!+JNv3LL(FrAVgF~nR$?OjIE@0MMEs^migvQBP@^LN%6Vw$fr6Q( zyMd(39=+Ao!K~QVT?DlA9*>R`N)#k)7F$N6he|{J_fISDGep}>wKEORd23eE?u~dm zxx`F<)hX^h1>}W1WW}_&_N<$)g+44g#%Vxu2a$uj-OlN{_QD;WuLZu0Ms(9MO!_=n zUOk%FAb#<~1D*}7t_>}ojbHoNpPve650p314MB~MHoNgOM9xjfFgyQ&I=Yn>_qUPG6A)b8DPb!iD+#tm1*ekJOsj#T64cd>Nbn9%$ z7UtKA)c9rlw%tp{4%1?82%8KhN=}-VJ zc4e;J4E2BtA!8JrG?+=j<9!Gxl|@6)CjyODd-|L)j1{1z!6MekNL0=;8g@wT?K38W zXDZkmx&5kBjEg(@)*KBCE>7I!7Do>LDRM|0F6ADjWJ-p#Ne)XIZ+I!d%-adiJN6u7 zYcqjW$Qk0ya%NGURERL1HbsL!FvWs`pR~b%=C|krsPgSOS>WSSo>1a9L4lgp4^~t8 z0f^OupM&fX36f0OcK_X~6xNh7Uu4Xhe9enpgKjQy>LlMz_uead<0U7@5fV2I7xS{{C1^rK1 zBJB0QDwY4bpDo(Z|M1@{0%x;iEE&9sNQr`)rh=*Qq4iMK#WY~^hA2o#V8z^Iai&BW z5iF<}X`O3+RWc(zk)^fhY=PmCZgwl3sv|9}+G(pj);29Pdq3N+<_{D+7i(YskAME5 zzkU96oPEu8ob9HS{r25ABFU_bTspUAklir(DRDx^{G3-kEVA)zjFa(-j4OL!CC>VD z^4FI)*uvZEht(Bx@(Ac+ArACJi6SmIWQeZfuc$So%necnK26KULpo`kGvVBc*F!pE zPng%6BjL?Z5})n?Xh2Za_I{CwXJZ=fKW`q=>yuJf{#?=rlxJtUoGh-x6L|eyD!DAvWAltJu#7KJ#yql< z3H|SkG==+zibc0fM@~I!3(t31q zw0#*-!Zh!9Ol5tFBSuUuhPwxJlJOM%?~=5-hX)Ks1A5t75K$lvRw2pO!R-qz8AeZDQ$O712%^teu;aJ;_Ht{auQa9{t|A zsn@8x8sX*C*)6Xy-pvtDL`l1{G{UC`k-;A$#@mMnQvL6mZax~2X0LbVj3XyRfrA6e zjPQwasL*LiFA*Xfwj$YDfFpiV-!HrhT6k7)>&uzNOh*$FPBeNk1wAHJ4Q=dNp74n+ zGz1P+fXymPOocAyIuey#EI6(pUZqv6=(Hl-6Wdi$i7?6W0R;<6cqplCiTvqvgJu#O zOiC&T7;`0_S`!5ZTo}eCDAPJpkeIotQ#+UWFLd|0d}<_UR&kjr{AMhk_5nE2nN5UV zP1915eR6X+RV$eww*tXxE|8a|Bp(a64Epu;WkWgasMTfG)LCfJV793ISg!(2(?=o5 zV1v`ma4U)#uJwWEaq}iE(|?Y0c5mJ;7{1r_OjNpIp|75Y47TQ6TVBAPM>1TQPq${g4*rZd8iA zNvND>Puo?pzXWv0gZSx)VV{>Xm}%Xwb(ks$7mmt*wDomV2M$Q|R-B8qXOM~rLDPL* zW=g}gP{T7?WZ$E*M40KUwU}^1_{_tE1(RaZd2Qt2fd%_a;5gwb0)t1U-`!p3cOzEG z$cKxDx@&7dBN2zHxsr6Zb3OkEmY(C#I#*+3c!{MI0r@d9lF|`Dy_U#@#ZaTk>7Wv`frhz&#$8J7GqtfO zUOr|z3L`4X!wpU)Wsdau6hu$--Mj~-#l=Kcsyz#>&_5jOS3_b7t+@PKq5t%pCf-7Q zn74N$dK3i)dF)RcsmJhWsLiTSroSOG^MbLBaf2RfX_%OhN#N5l${shZI-$VH2Ja7H}qn0M9i0-ZaWyoEbU5Ug#o79rFMdwR-& z<+S}?6jJEDNPIZ>n*E>Vlv%uR=a3ymnN{bF*u=PmKl!mtTnO80!zf{^-N)H}E-JhZ zd3*4G15mo~dc@QPHKdGhbmj>a?jV@uJF%!;-s^k)k zEA`}2$!()oNVV|i3p+~_y_8tgS9T_*_44B+8Su6Q)z&~$81qfG+No7apJ)ND{5a2N zOoi-$Z4OfHT*@Z1h>>D0+zlCYvT&UV{wD`}*k3-@Y?hRKk&w6q{+s(2Z(4{y1N&!h zUct3xxImk+W^YFt;|6$9Pep5WO)|muB^bf>g<0^M(rsAH!JF{jfLyk7 z_s*gGvtswOW&M0i_z(CUlldOkJfT;WsxX6JZ)W}@`%c|D#Xd+==bAk7`ByWWlZI&X zSlbJ}oldcM`{xNTy@{~HdFLs9;`*7+gD%fD z?+Lyf`%{&n>W<2#xP9kPlX^ux-AO?Fp3cMiqW(=mmWR76zUB1f8X#7C@a5cckk$o{ z_}l1C;7umD@38J*CC8(aYi0nTosgh`{ST#`#@icsqVZ{RDD(Y1Uq-mK2hlWrd+;0g zi#{1R^R_+^bBlGrvU#IA<@=`}=YCZXG+R`8_m@CW6yeoF*xCpDckXYbogd5kEETw2 z5kjNR4P1}`hzP(p4$lwOBW`4|!n?*n0}i6TU42*&!1#M}-?iayx%E=@NQi%g_;-u( zHG*9AuViJ0VEvLcxD$11fOJZjM4(va12&DGKD-zHaNa)e!r$36Ln;hyYcp1aeDrlW ze;&O{x{*!Eb|qYJ2o-F=q^ef9MwDC!Kvuj2idWMN_leRQ_g?NzaK#!*9RVF@K(VGn zogsG`VuM*MbM`1OeFbvB+&syQrB6s;ehV;pPym<&&*w^Nf=ej_fUtyRFw5v4Gv*Xy zl?tZR0a#+F+j7R5-GgeEp~P1XW@nVF<`~mVMInxewydU)d$x8(JxJl$l+luSG+y=? zDoyfm%*y?M;LZAzjMi+MCjt`mPEf}+x7rKDqfDY}R=I*4W30~P(pml}D@GZ_7~!np znh`Z?sh9-fn-(}iC$hYW^a*Sd1~`@@dLwl--G95|`VvCE9j;04|r8GD^>U&5Ul(g(1*(sc<{>7tZ+;EwA0- zJF${o(W9>kD(4|?PouZt?e*lGH|8Pld`#ju@fyJ+3|^AMvPAYf+-~C#K)d?i85w3$ z$-!&Lvx=CTkT*JO?4e!N1^^K6q9HENARS{+W4PPku*G69j@TQ1z7gUxl5{kh6&zv` zUf{G{5tLYY$kDUt$mv<2IODoNBFqvYT{V+F;_PxSe&%HgSelxw zo*qWPww;p9LnQ`O6niUtf)>1Dv-yS{&$ZUx=G%?_tUD8`wz2VZx7SwS%;@@KwV=$U;l5l4Kd2b@5!V zk(ir!iUg@uK3n^3=~UjLJ9jm8>D~nxBjbpe+kHj(D-hRp-I6uZGdBG3Pnt<~yh`YI zV|3@p%(6aNm379!BClabv2S$9^!>mx>6^a62=np#If*fNGoc|Gm@K9{3&v4+`*}y( zQd9cd0rsoRG({G*|S?tb|-WFuC09{ zwDJ89{)Ef})|j1+`8mt)UqL(cgWehVNww*jHosAH#=56b7mk^OhMHJ z4hfFvhOXL+8l`EY3VWy^zu><`yO z4zwlcW}ohg9}kDNB9ViGpH_;r4awLw$y%Dr(psS-HLUH9s^CQL;AiMllr(3O&Kx zi1+C*0BqgEw#@Huik)a9BiokVX1n#>(}&XbPm0zq;CF6|I49AE2Q87F0&Q)m8qRgZxi_u>6|5~hQhe-+-|Db} zZA&Z}pfK=)RerGS>S?#OtNMF~?Sjvt;RyXMdF@}K=g4SPoeSUwKM}?tZqJFV-`iCf zirlQw3>;g(Oe~}NeT*?lAF@rp(uo)(@4nz{BUnLaRd(SDrAY|# ziI=EoNSrM4M7_%oIwC-o*DK2VHdSYxEF9jh4YEvAaPDu^Rh030~< zdy^P}Y~Cotq%lW~u>uI37V9)S@2Q+5?^3E}@#lDIEka4URkzB;k~ujUmXn2Ap}sA2 z60jPw|B#y%bP}=bdH@r4@_-hC2X-D52jLRsOYzvndZ?TgrCeCojYdzBc{LRsa>(4Z zU^@bLwt_yv0h6G-F&7s!=y88^Uf^aFmp~aKYwr)Ri}jiN4#F~tdeyt`DRz?k`*U(z z(u+YuFv7S3_L4XxM5~HcM%Yw0!e_ftULz~Rqvd1E^=4io2)0QPdzg8|ECc5i!^Ahilr7nah4qKRpkit&U4i|JXWzwy0-Ge@-Pn+kn_QYUad7zQliuR5MJy z`JfLz{>mpt3PX>I2fr_Jjjs6Hy0x|9uh}@lk`2gLWt#I7p z3nTHS+M7CeY;dpT%a}KF<4aK76+n0d`|i~X<*YyO`;_+GC#dg$R*lA>M*N}0 z8f34W;(o>&$3cxnG4+vtb55Qpn6TLG2|Xwim|C5f%3dNodvNbEKM`W$CzR-&5jx-6 zf16OL=`S+d6k*8=yfa2x?O$9jnaJ9RxY9(K6w+>g$XmP`ip!n>nV$uoEMdSSALm~| z0c~Uvxj+dMb_UQO>nUY?tgOr7bn0-0m|(bO8=j+S>{EnIkbF$Lvd)muwum_6u?k4l z_&Y0KpZYUh8pVoUz^t2WxA<7%{e4AnI@p1!-ivnvG5Djgv=fwqdvGj0G%;=$`Y*B? z{W1y9d2|LG((cUR*GRhcws}SR=5SZv1DLn!u2%F|+S>%O<5D*w4!bpW%>ock+8{vO z$$~P78u`c0;1##1>md(S%5AZ2Zxq6bUH_?HbNCjMJ%{*BvD+7tm=|WwVBGxs7nR;* z+=Aa1nB7^~IjMhU@4m%3u79e^8`lePV8$27*`rLl0|(|^Rk{-g;dq@@Ha;lX4rs+o zJ(=28!-aMWRV--`OWZn7EW>j3H87ib&yKX5*17OZ$wJEHrxlv=G~Nn3!nPZzT?+TLWM?B)dkeRDgdJ?`hR zI>-?Q`9`Z*Trp=xx(Vv3cjM0bQ|$rkK1kT)oOTq+r6e zo2CS7a%n|X0>jmu1eTkn7MVL~$q`D0717E@m$cV&b&m9g^$8^+UyZNe(ZZ?y317hN zAMOoFOAlmHCDmgeU7Ontc4CwZP9kc_TWyYA(ie;E=vekGr$9!A$E+Wyf-AZ3bkUG4 z4nZB=HN7$fZDoF5Si1m2DTX_7wPIwsI(1=g*?GDpnn7KHlC>pdbo1W;+M!d9ULt2i zc3ly;Jjuhs?yNjv|3BiN`ycdiUwI2A&W6TwDd{xQacp|l?SfG_KUeOh0S&NTu zxwARQLE(@+Up;OF(o0Th6}IFuS)AI{MNKt{BsD(#avaN8YcOt{b4hgRY=TG2Jx|%< zk(bSFUMTGLme{cym)s-X5|&mPthcvA1XT15ipzEB$Rv-%>C0i6h#mkor}TuAM=V2* zWvnqgE|rX3F;9D`PgiAVh(>PCI);10IhRw^=>`;*#!Ak?+=?mZ zqE~+RqqmAa_f9x`>|K{kTIvJFKCi^(FXc-ZX$=ivFdd+PDy!a31mXj4?F#$+mP_%F z;4H(MtPA0mlv-ta*P0Kuo$^1$TWIYC+Xa8zb)lOnmpAOgphM$nDzZpU2cXCM>JX2L z6r))WJ0Az}#!u2eLLh^N-}q28a{cNu^HbptFOp z;}^X7WvE~kteF+9!3|tym=|TiOTbUa&ICA>d8AP-(3wBzaVl{osO^AY7jbjUAQJ6Z z;b*&Q`ZSr3S4`#0>D1u5R;Gj2)Z^akoiscoo=6-x*fr}orp>Ucf1Ixu70jcd1ya6Q z+^*c?RJC#3*6tVYBy)~At$!@kNX9s(RQD}PH9V-L;ti+h?iB3-C|spAPBQKorHPMK zWWw^x3}ttT+mdO?zk>?!&(9(1cq_kD9+$gnM`BZB3!=@7>`gcC<&<f zm!?eHTWJw(xp=H`-ss9n{EQvmmddI5j6Ho-w%chS2oa~202e+6r{Th#P2ac7Al=E` zKBJ*+h46Cb%c=OOx{kYoH7hc~DLFFX#N4K_uYP`pXg>&7?Wie&q=~cIc$%y3uyG!( zXe;6f$gciGs>G7rJ7=8E9_ouxzXO&T3hEtD!4lrMb!gTuId8TKCG9}6(3}>2(!t$4 ziYgM$@zu=N5`o6qc*a7Fbg{LrWHwPIT}SAIRSkFZ5=(MjyWhOn5`^apOJcfRbe5{h z`vi8`>@D6B#v5QOyM1UrxyqisPFlQWnRTZ$1v#zf5I5s|7*vxgF_{VE_YSxnA z)BB!78fuLwtB-{-5d}eEzmPXAGjmLOQkd)x6Wz!brgX7kW&kcMI#L|3i<||+p}xl^ zn7rh}rX6!cS;V|-G-fB0n_E>XzlV~xlXldCo1GGZj_j^?p^wxzOman=nay3t)uian zbj4Y7P8Ll~7d_mXi?=NW=LmNc>S9rNt?RDHw#`~a)LLbfmg{-)4e&piu=PmNJKmq| z;MqT(ZlV7L6ZUWHpv3ATfrkJTnMpL3h@}bowdFpUsVR%;Gev z)fhHvQTi`JW$#ggEel~UA*WNonmb^uX)KdekD>tDjManKhHtE!pX>26d7kGoSP~U)XCK@45mM6F%038YGjaL2UxSJj z*PVf5KO}Kek#JzFLUV`9k)ebxK-daKK@VX{k(u;nw1sR@L!}Qd)HtglzpDZ|0)iVF z^p|lmw^=~OZw*`0kn9yY>ym8NXcrw*JyF|<^_6;wyb5+$A}BF>si1TRQ9&6Zq*fGT zde{dqmUa3JLGkvHL9_RmAVER1{qci8BV;0@MuLtJ?gTgKr8e|2+J-w@wg|0Bt8yT8 zU`Wc=g*!%ndo6A6KxO4rNY6IE{@r!SV>P1F=IYO!b6g_bBtvW;=-q4bU#z@X)fyn)k=zEpZ{ z)h445)5#zb&sECcwA3@eIAy-QUhYsTf+N&uYF1<`-I)))I&sJhj^Ddf%(E;w<#a9e zE-vv!pIUa+!xwHYS81y%Gfy7(8_m!mbNH zB|{!2d+HOBc0Fl^a&7oZ9GJ6T*PphbW)Mc+49go&6n=q8hI5pL)yxIkEMw|_`*Si) z)--{LHgI}xlCot-N5sxd%t^r?msUzw+Bx?uAM?d9XXKV?;s}8+ z%$U~E2Aq_!B7o1)>i^pTKKI(3=P#St1&Q?zdWS^*-gN#?&9<^TdeFoX?S25ohgi%$=j;*yn;jg@Kg+}2q0iz$ zA!*OIN!j#7f4;8u{;NFwBP9M^)S#E~Q=UA2#Kix8mB_z@#A^EHPUbeI|Ef~|qUkeL zb(DUoVEDj+utJWjK`9r^l{5z3)Ea1Mr7ObDtQo+dkV7m*PaYvO>9{OU4lO~Oo@Mq+ zcg|rtJ2^QOx$XXxrUu3&1=F(#zOh>#G6F~n)eBHh-Y4&^`;O`N4A>tV$2bo_|4861 z`xMx%A_g51rXZW>prJJP5Nd{!9dATVh?__RdS6QjB!eeQNCJi)w2**ifE;BC)Se;+ zVnI56B<5`j>|PzlG0Fs$TPDG8c9*1Gv$L|VtSVHm`8l&;ayQ&&?@5OUOLyxCLFsL$ z3y{I~C_@HpDGpuPd7Vx5qqVy0_O#YL$&$)f_kQ)+qD;SOQ*E0RsUijud>}U6WtnVG znAko0B%9-gRpaP$4Uh5@%;9^ETK#Yo&!GmHpYXKAm9G{ap!_ZlO0$VkhoGed^LrWlp1e8w@ zUtEzV1Y>d}{gV|Ax8^6fGL9cq9sqzWLd~*cF^gaW0}%>+R`9lk1&kms$p22wOr4(-|t6I z*-ZDO&IIP`QPs(^r)6bDei9{aSY)R&bumzA%G7(Xs66Zidyo%h>=ESsyHHY~zKOKd_{-)GMU9rB`ZP)$NPQ_DAQg8Pg} z!##`hDXVs5Ky6Fiex<2*2yyohrW0(PjX*pFg^}H&{1ne_98mYBFx}&VaUKe>g$fYz zk+K~O)!Ph&Jhn@tj6Y<>1`@)sUvz5mn3Nl&#)M}o)=;Mj1*m+(s4{oh{QzBz$zeVv&CBd5|q=z%+YQm6{SPcV!Jp-^U_$5Dq zz)Q%}wA7+~T;cu}RWnY>gP7nfc>uVqgu3yDu@cZ&`H2!j&J}k9mT}Fo$>Eq2R6-QE zhi$OQvrbpN%+7a*jtv~~m2t8frf|ZEd?fmOo~0E&=?-GLVLlDK>i>tfWK zU*7q3a6@C|CFX9k4_u*;wEhCIt;XWgxUr(b9c@@J2J%VekTIbgj6CP?Fj~}&ev*+l zlMF%Tbb%=OK@ld8MhGrtAnzN^pqzmkTW5o?Ds#3}eX!@LQh7qryr<0J9T&5wW652k zt6W2B9DeZm>+#k#?OP$SsxhGomu~E2UVLP1D#42$=mYqF7Q5%)s+K)~007B989N;R zi(c&i<(l}<4lGmY|0w5l)Tq&Kt8y0k8KNSS(L?B92S`OiQi7IG=^KPxOj+xd`lIg# zK@9i&`Fik-x$76uZvov_m`uAIyByQ&yM4Z2Aaf#3ot5qBL}8!LTl~QhaYfxw^i%cM zqMc^FMsLbYH)F!JM`HJgt{toDxew>C`n?H^hxpcN)!tj<-o(dwEazeKY%TIXxq_O@ z)pn{MG0Si~OE7F68Wiw`R{2}y3}TQNx9b%Pymy^ix?_*cSAMM#~X%OJU+~8M2V#RmOR6{ zI1L1=W1rHl07JFTw|g|V86F!SB*c-zjqsKwNDFZd(HtSpnWFW6%;15XZ&=)XLxl#Ym)4HpYZ zpo69S{*&U_Wc$5!?`^94JsnO5yb`>;w-PAtQ?$?SpDMZGSI@ciOAJ%6)lGT$eXnGM zcB^_H*k0Kf9WUbkK9|x91Fy74Z2tuVx4duv#SJ^Vhm2&r_~35d;VGctTN;%&d|GAq z{>AUZOJO+q;N_4V>!u*#5mn-Ck)3c4$-zV|OYw)2+y3lI zUR(>S9?3c$bJr$}*iT;$+F5pOeTn!aA_FcmvnW?mAdHD3E&1sUaPblRF*_GZ6x3TOWZ;I47@cH&oact+2;2l|u9g z!!?Kl9Vc-2ObhcdKVY5bp)23G^DoI%fo{-FP$glwTt}Ha8b*mg@(LM&{i@g;et`~F zS{O#vemiGZv3@=oE@U9y0nNfrhp!uIv>3>hWGyvBF*kw3m>6^-dgw`L9tR<(B`K=S z{jyO1(}0DV7(;@Cl+b4xGgcGr;N$89P*ZdD&_ZfLbG5n3;2pPtRrfg^f6HT#65;Fl zT+?}Y;d4^*<SasVQw#kgrU*c9cP26F}6g^h&wikFl0%ah`USh*9I2g zCZSxguPiqG4RcrZJ|-|fG>z942+@hFg&bwPdzmPQto73&8gc|PxOr;&h)PrNVt<0vmGSbUv3a|7%haE znDN=4Ejq&$z8u<%_LDu_y3997K4tpTZ+{JTOcl4r-3EKiXF9T_D!b`#q_Agd+ye{h zfgXy2(gKfLb?b#OY|R`w@z8O)8E-5*#=>j2j)omNJND|bsBIqCno{U55m#vlk1q5O zJ;GDt^2F+8i2D{XV$W}q-1UP(LJ>yL)~(ZdXB#8%g`i9sz%Y)r1_)Bxrf zG8z?ubaB-6W4+i_2VMI8_4Gy@c58cVt&M{HFalIU+>waXkP*;qzZu1Kci!wcc{b77 zi^&Bj2j_5@m%#K@ETbEkBt4aTfF)^)DAg5E_g@9Af-9f`%=^XPeU;J-31_$gZ`Pr;84XTLBbb4ViTUaooHOkSaFT|g9}8C5Ix zTCz*ex(g9?B%HDle`Xn1*?wH3Z|c3uZDG*TBE6F@xpVp9DLa?J&%qU>>#Xa~+YyfK zuoMjlCNc%aARL8DQV8O@dzC`kj(WLsj`~c#AGN=gK_QknmBChscqm7Cs8t{Aeu#C| z|M`&>;^u)T)Dlw*ZomL`C(!tfAbWK8i1L{z+t9pd(-9=|Oj@1ef!i3ci#o8Lc2wgI z=e#;egEeJ1pIQ4+#``oz0<{%#836ktRmSkESoGwBck-I4fL$&PdSb7>poO2Fr&!xe z)!ux%eP-$go_u~?rGhwN=x&CyU!iTIZeWPRDl$cFK3lJWa-TBMeaAK!D<8o#s~Fwg z2lLU*w>a5+BN0MTvo45%!yJoX{|9u4P?eEjxRMcxTy=q4bpK3dpFh4Oh5f9&FIl#O zi_F+4xW$~I?^$)ZLonnznsNK;{xkVDmk=>&ptX?O`;iw3mq-~bONlPJ=oZJol?py0 zkO$+Xr@W^!r$e@QPL0^D5BNf3_{Np??FsY+KX8hNxy`3AP8Dow*r?P+3<77w4Hoh5iT-oe_?73A@@O8vRQ&d+DiXuHJ|oi6oy z5l3yWYh~bQB0|F|QHcWpDIUp@!A(l;S5&xia(?^2;0PsQC zV-H_44~Ozsuk-erdd{@;`Fi^T?h)E>Q5Zx;N7Zg@7HOwhz0^cqE^q0T*HF?-HHA^A z#3AFM1?#((+F~>5QDKH?#j~80IeSOkJ|$BGS>Nll3Y4^%CtUoMD#u9on zX@VV0*C4^mmc#`kO9e;V@1$kk$`H}UxQe(LqieEOOV<}Ao{)GiHvFJ6c5@KTP!+EH zx)cv%OQe~>}HXD+Q`Kvj0wc)%Gl9EhHN?2va7;dot*Y4?lPmG?KD1_ zla&}$&y@a+2SEyFs=pfC-|_qeZBAb`qqnxdIRUQ|cZ6tl2egM*w7~y8I`p5@<+DFw zNAWY-9Q=I$H$Ch>W{dwji9^xY-r3mE>0jq6Fh))am;qr>&+5{mQWF)FE^9qpf&1wo z8%k23bj=e>D7{W*=|cFS-2DdlC3j#|rD-Od1LWv7{n_N`{Cyv}hvEszip&hUfi&o< zQ|>*guf3rk6b~+QlX-qfpm%r(d{C`m{7wDNlOMT{TZuvFcG< zw`AjNte$M4a@t|waIf)guUYsx!T97WPC2hs1TXPQYdVRxj}y+{{sbtK3+fW;Pm#GTr44m>n0HjwSTMaV z9@LmcXOiQcKFt)9xn?fpOh3c$tT{yfhGzJ*~w=*hM8~RhjbzsB%S_{KRv( z9xlP=7l?X9Lm>MIq&y6Qt3dUrTWp5@;C3rNH2Sl;ZSGje%Y#RCX&92i(6I-maO1W# z!c4^|Ud3agI7wdkqlEa(0(7$J5%jX|UZ0n`ccXCLS0p8)eF4BC2BEB}9jK4gvd!*S z3%e-oN3y$b1deCu-VjSPz}4n~8qtyGl>RrCtyiiojxg{Ja9vhe45{oH9jEI19oum1);{Bu8(s_WB`OuZH{?LB8O@jexzwq+U^d!#9Nf5;y1Z4HMhiJ!{<2Fni#eR=Y?Fj#>n;ekHM zH}oD8#;^nUC-Br?s-?nI?O{s=7#XYzvxRo1*2WXmlLI0V29%Z-WVg&%&AOPdGE7lr zT&LV43Hha~kZ@R|K5MPY;d9Y}B;jf75`>Llrpml8r>KDcn>7xjS}wHV%E44&`vW>W zp;A8>Eo=3>>2S5<-tINk35BkqH0&3uD5xYXP5R!00)kt7^;cc9j-$8QCW2f2W`XEQ zC#XSlmaVJvZwY)J)CzipOz0{)yoIGCWA&!wPIhKrLr*D}le5R{$p*Mc0FVie?d07A zQ?b~CsaKOsngk4LCRsm`*E~`pl9{<`8b-CkIdoGm)Gj3Ti)=aSVm^jkC1{~js`87f zQL>1~0e+#)c$WiCXIV;ClwM{b{t4)YGAwrx2UrwHBNJWDy0VR)XqXLpyLhc=f6bE- z4YlE7%kX5Pw3yk4MzK4@uY2b0|S6gX(M|%p+f9ddB5C|l~P#g z4GqxTfnB9H7SkO)QigeoOy+mdMr>pNd}l?OA9EJ<6tDS`II>^Sm&zoo7WVs;R~Yab zd6Y+grPXBw{6QYGm?LQW*}Y^vhWcmO`ggE_S&E3(kGZhd1sk(gBce_xA-(#=H=y{Ge<%0FrUqq>2vpI{eo_%ia0N{0{Gkdh>!A$yFunjlbk@*7z#eTl`YD zZXUp=dt?n=$L3JO98Chv!^kJQ+LlC6WHZp4OpXl%`d%`rdDv7we_tY+=;%S%<-7;F z-c;Lrw3>D&&wH=0Lz&Di@n*f*amIxm;hlvZ`cT}l(Y!|>vlNqr_2XF#oh@Y%)59t$ z`uR0ID^zaHcjKpE$)1e1tZyqZ>JPsM$o1#z=g9}K+U&th-92bU3udALtHKz@@q@_c zKkzVmC8ijKod(=K!2YWx`-l9{>vI6cLHxOtGynix|F&-bE0EXdzY=*)y+))00!h2@Z-hO%I(tLTYJcTg-6boWe4DAODRb?4g4gj7{U8MYIU@9}EZX@nJ@*mL z%$IaTHid(i@_lfs*ZE-kaBcHen*P-~2{xu#R`c0Gn9eb2~ z_dwJC;m9F6&~K;*NfCoHvVf(#c$M%pGjn>7JTS zhpPPzb2_PLs2^S8Pi^qAIG;`oUF>WjZWfhyW$<}H^>dA$5y(NVK+mtj)Z;_^X;&Y5 zBOz`b)v4tB;SI0Abo7>2NjAN~gDdz!A#aa3b6rcqRdg8GfWBW3J7E175JAYdZgv{f zWy6ui7-#xKX(7$2_vj4nNWdP9M#MF&>`je0OF-1Lzr=|RsP;r1ekplxotb^}PoT^O z3{T~j0;Mg?m(H}MObuAxVn_%P3h&njjyAGmMPo^&;Fh%iCi01zG~~vUB}w*Z#jro2rt$6+WEhGvq5Xr85HW@aT|=mEp=$NN z{pIda%UTF(K$NiZC01h&RIw+)+dYjcYKx3%arTmAYeU{`cgiPQt0tQR>p4#o0dHn_ z@JnZ?rnAKrX3;`h+U=xAIEPLV^GYz*bSxX_e^ciKRZ|gD(($nPi7u343(jMp+Qdf!=hgnd37gd7n)c0A|jb2E7BmEcDY}Cm6!1#Csa^UGLn0;D1xNm z!9AU0D|}2~DA2E|2|7APwYO&j+R`j|a3-4sFA9Rz;Kb(5cSbB^a$^$9@Wl@_RUuKw z3hha-yI#mmh&&gq!Hj$yl+YLP(*-;~BK$;@a#Y1exf`RH31@*q}^_HsO_s1Y|wWAfC=T zM;&}(bmdX%8={-cS`*#s{EAA`KH^-l9zxx77iT6sX61XwMhl5-y;L7yQL2y82ugoP zFkn*{>ijD>f8_=DRzz_09=+U5!YkO0iQK5liYqrPMBxBsTrZBz7C)69X;;{)gc{>b zHNU4qo^c_Yal9QX4bp&E$c{3Pi=MHo?}~v;s!$?L3#WeRFI0M2Z?WMz5YVu4n)wt) z6L2BE$vG=zy`aWQf;>eDra8)#x*Yi`8KACKvr9ygLt({&1Lo42gOYM_Xj_AwyL^q| zpxr>CFQq}QwMX4NBb1V5*U9$qDN-4G`M{rkOd-+~rKbo9=Rl?2RK{-0&)fqY9ag1s z>EYmR3_O{}-9X&Y4y)>%SuAMpuJjJv z2@lEqPDc0ah^(YprUslrg8icOURrQ41ObnM4GiY764B{dpCXFa;&e_17F|g_{Wc?N zj*8jvhd5F2Lia#8^M=%#`(Gyzj8mMt)bJ9#=RsL0xH(-5>Wv$d;{*NUEV=~gDRGBK z^3sGx=`Ft$Z4c5nHVS;ho6I`mv^F-%?0+@;Z>&NB7RHS}05oML{=#y#D;_Fuv2;Tr zz@VAk^yB6_S7J8yPgh|o5@WvFO=sbp%pA#dvFZ{Aep=(~B$%VwbgO5{`sj zF_#&M%LTCRHrt>oakk#Un)OZ5Wr`ugH~q5c63MS$fy&HxWqw2RUZ1pN)@HR`d|~xi zo@};!z}@zhK_f>X8oGrT1R`N*&hg7alg=en>|uhATa7U3+PE%Lr!oQ_=viHsJ(>r&_8;Z{Ni-IGT>= z8)TI1`hKH(s?C3WpNO`0y#4ySZ%DG%hsvpjuB>R!(kGB{?_HUDK+bg?;D_G?azlge?q-%UV+6% z4{g~cZT{>l+`ULeE2jP-bdQd6!GM-BCgQ=j8OafhUFs33wm!lX;FKlVg=HdJEvp}~<$?%|H6bC8FCPK;-O`-&QL@E6- z$Ms_(To#*L5|dm=`Z#F*vMgnJ3jrKf&$h4(n|+KD(x>B-L#Cj}{7r$0m<~!c>$hr> zS-0+0KJGsNvhoj!2Hvp&@NyXNu8FbmnorcM_+VNGaSJcfd~43lrWaR%B`ZmzKpvmp z0nC#V+Asxhf$wpdQ*J}A_38$y^lgDu26X1F+A&?fnWuhfzdhjp!}P^#FAyd@p>`ku zu>~uF3v|*ie^4)fFbkgxafO;R+FmhJDmW#FB+}uF%$$09IG5~5{e@w43HY?noRu|F z=av|0lovke8bSRB>VV7P@Y+A1nBW_8gfwN z+8@wY#~xNxw3SjF!0jzyd)YhnBg0lB4S?}VSTmp*sKa6bcdafpZ%;%^J<7Np9;iRR8{*Vubf{zOJBcHLgmnxZ|A&l{)9C0VTtT9 zx#&;Cv$N^#Mb|!&<%@q1h81Fhg;&nc-(xNA%~Wtn^lJ>r)f2m0O?&dccV)!CPS1WB zXf7RV-5t*yqvxBwa-UUG+}b=SUYSybZkUW&*>cN#k(GAbqP&7r%q+zKGRSr532s#5 z-z?wZ5tt!;2ARy+)}I(Yxtsd6+4JJ&pyL-Jk?pJ1O5{e<<;kaq1yoktrfN8*VPzGJ z1t}4~&Z`Rsvj;oS*ZE6{?<;2LO;u3etu{UM`s*oaFwgQzz(qmY=@RhM0gMBd0M88kN)uk+wZLu$YR{xDtm8-V} zM8oPG7Mqdu4pw!=&6m3yHQ}f(BWKsVCoKz=S$F-9Y!?9%&H|C6tL_2%^}U*O9uEzWHa<_b4l(k2#pIOzYgY%C1BeCSYSqT=~(a~ zWj6R}8!$6qwCwxfBX6?cL(a3iEN+l*xk8U;ZL982P~UbS(=qj1{e$&ly2-i746>fI zO_ori7lqlGwtTE+!nOxgO*ZW@0L?Mj*@0-Azdy?Es}sDQMEeV+M?iHEnHvGmc=(CC zuC$mNWM}X4ysKNOg8+IN|6kfdLnrJYpCnpUqGpT@mrr->n`+AC?BeB<=!E&HdA#{^ zJ8!a+6cArvwNampnc|&+3lE31ts3mq0d)I=uOR4$-;0WFvZ8D&UD_s{8e6&>`Mlt4 zvSJO@c5`+myFS1R4+3qon&+{NMYhZveAmRSGa|~}pv4Ee51#skolUlDyFz=YH!{d` z#h6{@fTfm$wew)x4f8-(Iq5C&Yi_8OyR>KfxRMTZ*1L-hnYOv_ZZuv_R6JA7vyNWv zcM`}Ke;}_qi{x;7vpM`Ot+FqQnOaQhutLcs?bdZ+mmHvYHT-|IoW@(pbx7E1WVQ6) z4P@~z%HWX}CJ2c#d;J#OSBJq%VGx&jO{xz1x*TiTEYpkbSc&vuW8bh@zoQ}786Z&i z!Kotz8b2QidmIUat?@$9sEtLt*I5`~I6Gtybo;w>PkJUsT(tW4ao&l}wrJQb-1-q9 z3T=LA{g&D2k*8_#d1k$azC3oNTR3t;+AMDgNR>}&yP@LiukoWH9`9FqTBSmGCM4Bx z45W#QWy0JxXPMlE!=frV$r6>2u)TG%4;fsG{|S8F7hVI-d?0e@iw(O8m!bhU_B}ECWX z!wQsK8Tn`RF&~8$+fmv{%0t%pdV=PFknaS$uYO2V>(Z?6#pQJ{fhq;@dgM3rsQXBF zJeKw#ya-VY)-J1Are+!4_D}fExFnYV-1M=kuAWF_Dfc|NN`51wm4rdSAX zf<=@iy06w%XXm8?k3}B!2LlI02R(fbxWYV~QBsB4yt;+%ADT%7eoX{leX8d=sQkiI zjth!>wQCxiCSSAx6}CU)k!voI-O#c8Dkt=+VmJo+Vlu^Iwc00Ro5W+$S+Wqr-g54n zL5FjAq|)N#J%Zjb88<-Dx%b4~Lz*ovo^=<+O7~ zO1@p(t+5P~vMB2(^^rC|uNplb63kcI! z2hlCYDD@j66RFeJhLC|}rP0T-)fnxhN~D7Vk6Zj1?4k6J)!J02ps8RgfTDY$ZT>ZP zZ%u|EO9Z1sU`^{^cD!=$K?#xRN+1|}4ZC4JaY}}r~jKVopYtCcz zO;sWy?r|Pq+v~TirPB!{Z60^*jvIG19|fHY2$ew`VuF=dI9zhhK?}P?t-zyohV9fLBBkPa9v=&@ zJ|w>rkU6zFp$8~aymiIje-D|}sr8z{p$xdKOn7W-myj;P^=Cet4kFx+mCZSqc*#bE zGd?(R(G{(!sx|AyNHjSX-fMGCUXq1fOkxL{rIux`E3ef@ZCjj~Ns=5EU(p^npEDMW zDLf>;BU5L8ITT86QG-R!*rW3-{w>tdh~;ywN8=sB(uv@KzW)xBL>c z>%nKLRHFsM_7+axKk**4B8Y_vbaHL4;B>_x{h)%5veC5#recBo{i%whyA>Kn1%2k+ zzP^RRk-dCA%$4)*&n9?upO=B|w5fa=&^C9v$>n??g%j@~05^i*&i8IJsPlpjA#G zaTQWpT-7co0j0TLK}ixybxIOSJ%~+=RpUc^i-cm>0R*W;I8OLh{qsko{Mm7Bn zmOquPzD?mb|AneaSP}at5;(@2$SAav7nDMR0RRNU z0ss*I+cKhN?qK{Myt%5Dqml~Bmn>4+yqyQZjABlpBEozE!h*x>(i}9XUt{9W8hJDp z3AuRxQ3+|;zkcfS0=>{|fOy!0vzR8>dKs~31NL4|=r2dE<@6Sh90J@zfXf3-El<8c(5I{_S+N$7842N8hU~P;cL`7jH3Z%d9XQTYo&#y;8zp+p9PE+RqUz?pz4d(7(=1R?DpS^y-*IL0k!!oxnWfyP@w4~KPb^B{?z2gBr2tW?#z(xKfZX|Kz3GCG%)9k~W ziO2L0wxG9JOAub%X-SL2xAa)!PLfwqPH)Sd7zmirQ39Aroi%VGT7eZK5eI~C|- zF*l1?2xUUbD5ISteRh%CSMZQBg7HX}-*>nA0qau3EmDip&t#{~O!4t;3K4dHa2I+8 z1_CVp#nK_mc$nx?C6xe}`J{n%Fr-vv_08f?g9NCuW=I{#^Ealp+`+}aK7vw~Fl|E1 z$f7e7B7I4I9-xlPU;Nxz48mD7P-!<=e#g5CGZ57vA@P++s(eA*WK)!GcuEo8PSnoQ zy&M^)y!nQ2wP0WyIXl2!BQl+S(RRI|;ZVFoWKgub`0;p~QxYu|J4#-nIP-6gpnAV0 zQ*;#UP(4T7vqxKzmr=BbVbdBLXE&wHNacI>mbcb4>2h7Fh%@7Z)?y_=$?h$PkXEUL z`32e$bRgJo3;ohtNc2DFE~#@Jvi^3>*t1B_B(}nxj5kKEk_)}M-Xq2k(W=tbE=&52 z7L?u3t5s}BIaxxs>&h_vwa)6P*BBG+|kzELLkNj&>g2)gw9ytK`xS)-qV)bI6zu?i9WQ3n2QgRQHq)fd$=PA;$>1fzt zVxP$rgm&**K$w9@2A;=;94@hry65mRE7-A6;B^?fEi+%ZHqK32Dl}muN zVX)X}9c-|t2_`;@oExr2v{-3Xbu-0xPrvFt+Vp^b%9fe8E=u*UOLYL3tc^cv-#M1( zG!DA_Kn-X`rjKhy=4VAd8l;xyYP8liwvAtB@Hf8y(m8sivdbHM(6T-~{20^dW@U&> zcp%luYc}pk_YR4R#bozvmKDNXJ+1K$|D_T&R>wtt-zrtSi`DgRmIPkKwQ}u9hYrr$ zu9Yf^xlCH?_Q#jzJV1+!W#bT~%Uszm*XysO!JWb2rkLKm%BQQHed-(FxwlR8Y3-Oj z9@S)~XJTOL(dwhQvi+~Z@-y3-VYO9!I4_slDj(Jt_qrf?k=Eb>r{5%;wMldnCKn{L zn*|06nk=fU$@09$w(1QU=(x5{^Q$ zje9#y1WYxabRzhNwpSvPC44}?2vwoe^AexoEIP-!Sw&3}+f&PHoI*kHazy*&TCpW3 z(ixDIY8&cOkk$+ODH-;gu!{0VJCAsm;5;|a(@f;K)%kBThLAt68REO3@i){Hq6sH78F*P7)w_kj63H|=GnLT!p%!;VEmmaq z+-Wv(RA8z89BXyh^9=)0#eR$C85jL9k(*;hZ6rP!Z~vFn@4)HoE@~e>Pf0Tsy0JfC zzru6mkUp8YKBRHy7dalzsNede!|`cG8rbJhVy6&KKa_|X9n+`_uBa7Lmv7U#4fI^C z?XY0Ox9g#;F2miY#vP`{PjU+JBMUQZH7`CG2fCT$&O0(AZ8)xXDIchg%X-<&?)`RX zT&iL+%P%f0-7yk5A|&5#(TL?i@@e4>=M5s#2UPnNDJ{QsXX;6J6`|1_rmU%D(+%b$fd7(TM`%*2~>&@}2cVfYOaYat=% zVP+J`s*0NHiUpK4GRrOmn~~|T4KJhP;BBCyWbY6z4oZZ)Bun`Wv69xO zD`Q<`*0JVjyoc;OmW_%n1@TvG<$eLlP|P#R9vv6Km}+N_-4w7G7e7t}T&3Kt^1-y# zo5=qD6KN7*K3Zd|Io)x8G8UA zmp~l~dRd_l*`gv@rdTLQd%jYY*~BCnq~Q?h?utpVIeY9NX(DW$s75fHuU+q)B(=sw zP}9yz2@_|k{o53_A>}gv-khkM5lb}aaQ{fdBh`LWKLYg0Np|T($9qcr7^jVlR09xq zEW~iS9_*cxCut1WW$LmLmDjA4-l{5!cqU~Q9maGZI-I|3?#la+F0upy3+1egW?MU5*EHcE5ZqT8Y>36nH~{euh_i^5kQCK_Mt1p!X~Nqosnz zgz~&4FjU2PSvWZKPo$7){Wu2DQ-cVQsqJe|tHZOnM+6qfe9@s9$FC71q9Bsu@VVTV zF(omX412kh#d($bng;#%{+3%C-*-SW!^CWt2dgEi(M|b_O4fR{lguj?3J}+ubt7ax z4-l#F!wDT^Kk9KB?$xnkC5W5o;#xNY#67SxgV1vb?R^EDCVQRgDtID4pR4HKT-msH z$~X9~^6fZoDSsWB<3-O}wgbhykRiXu+BxkTLo};pqo|lC+aG8B>Xbt0SmL`($eTj= zTkq6E9Y)lyl%3kVQd9iGseq7d0^XcItiw?eK7e*#aF{m)PbOd&zICo{IGA_1S$|7v zte%{r;@BwJFjk+4M$0|bIQ%k8GE(Db?VT0`qg3kaZl(OzaqxKgfPX{FW9UU0w87Vo zx@1IhFFI3%bXgca0*B!9<|$zEA#P$oI#;Z91B`tk%yUIRijtV{aA{T5UZ+5oEXo56yjtn zfij~myN`qAovhn6T+mT%Ho!ed7>S16S05%C&ZsK}D}xFDG0hJCMrpqyiFG~>>BXL7IxHoeC|EUZaX<5Z;a8( z_#1Z^AXc&zrpk9LNRpCou1Bumj;ZuR5V<>U%r>Ci1z!1u^*Q}EU^_yFWKx}9%l(}O z>5dawk=(qyD6iJ6_VXoh2Ut##G=~()H`rjpfmq<+=}vjO;ERUvL!g6CO52>as11{h zL3#l}=N8QC@4Rwehdf99e^+z;^I&M5F`M%JXnEg%U<`qOJ9{Yom}UPj#@;cw(r{f9 z?v9;~ZQFLzv2An3HafO#+qP}nwvA3Ed(S!FIcI-0d!}mDT7TcFb=Unq&qd4sf3q|3 z|EItFkBf=YxHS?3GLHdndzD*lQewYy9)P!On|Kkx!uWafh*N+B#Qw;jB$IB<2RqqDcWfXVmGp(rvWcA2ME zYV4MZY}fRTMQm~1n6&8o2Z?D(BQv zxw5(8PiukJ7@`$2m$vXx7w$y@GZm&lQ1ZkL@>5^~dyd^M7a@~y?Sd1h(4oJ&I2_J0 z6ILjv55)-DtyzsadyCCmXtM^-Y9WcNF6Ave#=QyN=!M824ug|uxzIzdhdZL*?HTk$ zGzM3#+1Y6kuusub`-n!OPk5_OHrGeCrQ@ve*g$tIbXHY)jvAB0UHQ4`FJ9gByN5_R zcijOV=Z#n1Rm38hU)Q_7IS{V+!_6aS9l_1f2FX5pDR15+za4Fvr}^@7@%-FLRBXYA zjbFzUoN$IKmViXLxRRAY8h)M8*bp{x9_aE3JQP#lNSs=KDy)`){Z7pBnRj6*c`w`S^cI4jYxUZ4d>JdHNMp zY^m1QN-FWJP*70U6449?9t84ciz9Q%;Ubb1SHak>PUlOSivj^4WIk{o7_QU&ahNAb`dD|nuV*TyfhoysPzhiry5L}fOjXFwBJ;eGFi1%;Cn=9|QWkmSC%06cM(!-Gf|z2rNg}wOit(R> zMy5X|UZQVQmBlp@RzVfM(Jc~{u@TEB?Ql+))_Pt8$}&d}vOs*1KUXJBzKZpzBt+7#irS80D+*pzjq!8h)re}JH$U$s8Q1W^VawJJpM+3DgW(|{u2fMZ>^DyD%uY7D!+KxnuvxG&|$>KOgEDJ z_J5cwYM4nYh9C+0^~QimoPd)z*@83~8Kp+2@;=Z##D4zNNjY^aipiaDeA~cVJXKd; zA!Uu>eIQWvc)q$?NH!+uHJ0xW)E|(GIm|=X!W2PB zX3-MI_bp^kI&3E#Uu3TWyohX6k<|c~=XDSqZHGO(*xz&p;qA3hjA>#@!CFAYmTAh1 zp=nhcq&U?#SYwbht2kr^^~Q%?Dn@H8V z10i3fJWJ=B+qYkMEF87*EO~XyunjYS2zyQ`))R-=)U=y%nha8gt9O}eMr9W-QNA{v zAE0XL!6>2ZE6m@U=~b+-oKxOUN^2>G9Bnd|i=kQ$w=Gmus&9q0suYqc+e(@|rk$?V z!6(Z5Y+93JUyTlu;#3CQC2Kcx9=~i6#j)rmlPJheLe>5e37k+FCu_+dm-_2Mh>XY^ zu44!Nb{cy$sH>$8`rH1q29neWJ-5N|% zPKz~{VP3)Zx9+{?6pqD+?Fe_tYMQWqb|P;_82j1R&!kgf_)cM; z!)jTLyvYuNv%yjod)~&K`Jp7R~hknaBWg|!SQ^;xxYCppicnNPZyn6n$z(%)8? zkCmZif4&TwjO9LOud)g9#5wI{ULPJBYcowfG`??2oR1X|ANIzcvHP{pIflQMTiGBs z%v|KSk#q>{S(=+?h(m@v#5(;VHD8rPBU@919K=O1$7#6{dr13uT4Qm_1PHv$RL@}X zk`zT@f#g?1bHaJeSi9q1mXy3tm1tW?{Nj1>GQV{3L4oV? zLPb_#^4hA8Dr#{KuJK~lbW3egrtbE*aWN%z2ew<$)s9=9zr25n(`{$4sAQpXh~hTw z!1XujwB|sLdZaMQ6GeGqZXV^GIJ9~aq9$!=iuMyVsC9BtH{Nj3Y)Xf|=twXYPi=0- zBPN1bizEz}8+l?lnY#gMfiJLjw<~(OGet({m$>>_h|vo5G1Qwa&@uf8eC6#Y0vj`i z%hN>0iMgI{#cxcOt-sUU^Db1ijwW-~}|H8mft^8yj<%a4eRA|@J>TP%h& z0@g(#wMU-k>j*Ra%0(0B8;v1+o9$OhpN{kpkM6&E!t|RHgV@it`1D!%PgQe;pktuy z>1vqX4V7u_#dQd`6*xLff@xcZp_Qo)i$>@@=oJWgs#nyBZU_pguAZDe6H>jAWz=0O z{)X8&2@IO4aQsV#uZ%s~p-Y5M@fK9>qaZc?4B;WPX3&lyioy+3IZD}a{XSNRES_rw zAdcY%NC+yLf#gn-ScQ!~V=mhe3u!e=Lcui@r+njLgFoz>rFjHu2e-`;GN6ReG;1t)&GMLH71V_3|@Qoz{@x`x z3ev^M;8zW93(0|tdlz%8KRQ64Bweu+>#K}J3I(N zCmRV?sA8{z%D)D!@JrB1fX{H>EQdyajm&(UyG!CPdzo;%T7f+TZ^DvjMi9H)4C!k6jt9CD}IpKQ-8A#(- zU@52usd53MihYBln3?UWknPL+Basi5$Hm7N$^xRo;Z~@-Hr~so)w&FyX-K`l8>9n)V5oDuqTi%#6YMRKK zI%$Fqc91<7Ud;}|OYX7F5y{VE2glZFMrBDJWhao+eTJPRcnAXIZ+*ZqrkNC` z_|>oOj!B$Xk1c6Za_JaqhO|M|El$bg6t#X)cf{WiFCPz>)*^4|m}mK|tqwWx!FVi5 z#$`Kl))eNc-Ah&4v>5r<)REZ1`LIHl*h0K}#*(YR4ek-+D%V;x4dH5DNd9kNS zH#W-rH?m(gXZrNzvhwfCw=%W5YjO)7GOJiFH!jQHRn2=2jk@O5hwT?!6x42wBMuni zdYbt-3Y3Ztt@$~*2jQc-pJ!^~RIxxW-y{UnKws~ALGQPi?*e<8 z=_4DWHpw&A80w>KLr6WHu&uwV?8w;6qNRA1-)N=?rd2#UpE4%DB?=r4*sufbTvNT z*5ps%(A86nw77?BuAv>eYVkD8GlEGsY3fZs4ieVLRMcAa4U%tmY%N9TV-Hbax;Y0A zNxq0PA(=x9zj)FM>@rhWt#UUA0u}6YS$1QS z#K;K=nF&Kw5?wc1%8jU&EjZvP@p`-e3<%%+aXrfaRWE~_$BqD7XE}W_@m(M0e5u>r z{r$88B*4H}q;oCtgW8suEy)C?dk1SV3Kt z`_7Yzeb5$0P-F?_N;Ts#Tk%;iHD~UWojX`K(J^NK)l@7r@TeHNlXTKJ-w$33X*1~IJi@UgT9x}AF-z6wq z9H3Hg$00%BOOMkr3_;8AB&w10J4rP!P>adnj!=f?EX!Yi4AZVf%81|Ber zXbx%B{t!DktP@jNN8tJ_m+TxqCSl~eTJPS!SSP>!us`L0kEYN!ll%MMj^;lQlW#EP zpYJ(KfU)&I<5&K-4d-8Otd)uXOy?Q?Qj|bjE2Q~->!-B6M_F@U$iA(!&`Uq%VG{ zj5Qe@vF<9MRXyl13~zyYYsXnz&4~gl$zf)OA?tDMS$nv=GPdc06IqwEvZip~BYk6^ z1%Z4nZlA+Gb=RtkX`zsf_JV$3+&fWWTnSsYBLwd0cg^Ip)*OtIXL>sqx9!4n5n~mj z(GUMt8BtK<8aaWB!g@%tRuP*FAdSU*4>nmCe|tL08;r~o%hZHVcDY$i%FL3zAmt7^T@)n$CD z{s*_>{#0J`z`Iz$0lPhF`;|BXh`dnP89<)=y$MB&1JStvTZ8+!nq85k-s_I4@i&cA z9FLHXZQN*TXs)|m1v6bvd}y(zS-8qH-jBsKBvNTgBzh*0~$+GFr!4}RJ76U!vD-a~bx z_zk)lGM_~XqL?y!N_X<<8g(bCIP{P?FC7+VZ78-r-{P-d7ZtN1^iKY|RrojSXhSsW ziTy8L#6G6Dc$l~u(C5$pk}~|`lxNO!LKFe_;|B=A|IHlwe}EJJ{EJJxp5H^_@UlBw;s!rpt$-+`BU;Jx#O3hxth+SPU|c%ch^oo-(I#- zV?D0tY&hL%5E}Q3DITWXXgJzpifPA-J;wHzFkjcfpsJ&%4j!GytuoH^*}es@C(hSd zKiiFs`Dl0hc0%(^_!s&9@|P0Hw=vG9ti{Idc3a1DJ^b4Bh7Z?s+5Z~yBRxL!x8wru z1UrTAVwM7*T3rt_R%UFN7!2TiOv4hp{aFY@CzYlcI)^8s(3<@(lH9o%vz+>PG|0O zTh%md@CFdp-8{%pt#7Yuln0u0I9EO+=Vid= zae)eIlj6jl_Udm^)}012xWT#Ftd{vWG2-u_ZK3Syte#C;>fmOhOQnTSYX#OTA&%x& z#=i_#_weZQ@`;o!ugNpD@ zD(;5V2;-K&F3PfUhH^6Nz~^=~Iko{slT48IlR9hd92kWIV^L~-t64ZFb_(rQobvJ1 z7`6Kzmr7FG_jt>b8wku@%*91|gU0Cm_U7sKYFJ$=ru2uuFCvgoK<@0exWp6CkWmIs zaOJ0m zJ!L*7l+!KL<0z2|sZ~-;i3ob?k2EW!$3o%36 z9R@a35hZa6h!2=UNcu0ipi+mG>EhQ8V2S|-LJNApM5g$KD=X_o^v;x#%}2xvWQrrP zmXyMNHH5RcK+&a8vT$&K`L*%zOS9W{LNLMN6}CpKambNcGYeMZWhIpZ+6Y~3pypG;gx1byzOqZdGs**13>;#c-+Si7TB|KIjDwivs1#?P< zBRCFxIACx&aiWNDiiTlumPGN-p#}<-8q;QJWBQJnX#0gu@oL{s?73lm=&$gv`<-A^ z?*LV5M}0O1o?w(-C0uImm)gMWo$vTHUu)>@P&SQ~;O$?`-BCztgsgHrZ%dK{^upsIqq4Sow~Q#w_^Hjr zOI;s!8w;d7lU@SXct)On-*co$bM+e8*g`p_g}D`>uXE6`2YUmbw%|I5_Q;?;I zgWV_MO+0W7rn_U8?L}PVCFD#_oEDGAL7G+u4x@upBtu0jX)tPTpl$_i-;ZERmKjxv z9;q|xqCyubpj7P2erO+i+yOYxDcebEicjm#84qMM49l>XGuBPe@Vix zq>A}y*A6n%rCf}zSnB2VZYkO0m(Xa{{877iuu3-9Y#5LGqoqG;piWq_>e;$MtzPDa zFy>-EYY;Um5PW4SLm8eBFKsdYJ6Nj}qj4fGhx&O$IjJHh5%sP2v#4rk%??gl+DKNU z{Du}fdLMF&7&*ibiY2LHDGe$LMC7ORDmgLAHEIPm0mp_c1ST214escYlqhSEet(3a zloqy6#~dURhpY(Vye%)5)Zj_9F-_OSM-jbOhUS1_MngFXfT`k>&KzzTbH{87m58@5;OU`ESV^X>(j(E7^iV44n@G9yn1$z6KxPAcdG;)rJ89ApAsI{Z=7;%o#zta0P(V?e zgtRlHnwMLGQkW_%S5{V^_`A|Y1E}gB%>CJ69}+jt_-qc^Ws)-O)t!@Qs>gXTt5flF zX%fW`!?5Tbl|O6c#ZZF_lb;uAZSyiXj(P2O$9UR!PK+CTVDIfwMNCsas_9;bG@|Qe zSh?QDq0cSK^-df9HdBPh!d9JCw)LM`UZ0p@-{>b8q;)AlyiP@q~$j8be($@=YNXl?Z&K9W` z%y&n;5{>|gOcC%ZPrdIjVmAlK7^;vnN5nDN!uD!wD!YSatqU~@-2@3X_LF1z(K*6> zS|e%*k;9Mvq7=NB;|So$jU!MxHM}axvjC9TCR}LBhqCGYyy`dDh)(Qq(u!o;(|B5C z+xywDGuZ;#Z5*K_r8~gsi#zNhJ$XmkTslj1(QYP zsHxcLsp|kfJ}o4*`55W|E-xA0*uQC!=}}fdVfUvRYQM`*@h=WTF2^+82G;Un5=vIA z%?z`keVf*|b16vzv>SEA^$Sy+(~+MJ+w9YrY({VQ`ipcnGe!kfW*q6zEz>Tl(6C@| zL{OEszEdYbRx^$}dc*Zz&8z7+Ez+ydE33j8E~-~#14;l%+{1 zhnAHC7n;t>`Mp*R%W)JbP36{d6+A?2aMEC%Df!%%lFI%a{Bz8`c}u?)xIYnd{?N^2 zc7b+7!gQ3eEZG6XJ3Qc>Mf=8$YHx+pC3c1uf<$M4)g|Tl4&}UC9*|gFtJXDD;^{y2 zLAv>3)q%5}8*r^K1W$hG;R>`p41O!*3BuPm{{qSDpB=YfPRib_F!qyrj*0;13uNr} z-8H1%3TAX&ot;*~9lBhvyEI5L5MPH9G=#)KTzgcHSL0or`I`^|Tb4l^ zdfe|*dxvCZZ3Vu%Dw>1-`AnfT>p1FmyRGsFilRAR0FJiu5`VyCSo9GN4g!wf6ZJ9#GW|J*)jVwg&`F094)< z=@igbIN6R3qGSi!1iEouM?lR)T)8n0aI;L+vV8YASQOnZ3XX9(cI-R* z78!hsoxdgXIn7B-FuU{Ex?@7{LFN%p@=OJ#Hm<^Mn^~ z0R?);2(2*QCkcz?hRoe(BXt#PuyX-#Cl&5nFY0zS%~;QuC#&6HhxeJ%>5r90LR7q_ zQ$bq45<&GP_ zbNN%2wpW|9umc-dr$`AhtrpU1pkei*?n(ZF*ID5mDv0?`O24g#gyv@4WFkqWB8ioG z1huUMC%pj4st!f7z+Yx76t{H}-5oY^B~FwC9Vk|J#z*72tF#4u z&hmoOINj@1x5O`b9N5<@g0=W&^*Hp_mJ#X1fhCy|_LDj{Qn|#qz8F?FA0vE?Pd`nP z&g#@()}SQDza;Kw(tBqEg>NBa2&Q=LgQ&-9pajzYx@oKe_e3@q1BgQ7<>Qx4#?vDrZWD0(2AVBjjNlY zvE?_gV`Kl%{AD>yeQRrD`~Q?9WGcwZA_@HZZ0dYka~Gt^12+)n0ECZ)f_crL$7>h} z<-#uC^vyrD0ivm!Cm+LZh(2(5%R?E4Zh$}J2Uh3*kUk9_Sa-den0BpROhn(?nEqhB zY59|0b3dX_AiI@U)GS2}u-8nl3*{ocVo1Qb*8cV0kHc#Z_sZZRh`^O8mYKo1IUlPh zhQo|uPv?aZAn*}Jpj~;%gzmIh+`p>wD{OOAUiP>4Ry;CAW5V|aYEC8eFf;9NxqXon zNfx-i-l&k^G8==z0WWA2B^qSgK|xpxRNNhc{=J7TRNT`BrwRtkGP9jKgy-NsoT1wh z*eH>?M`iRjMVoy~`aiwq<_gK4+^B{@d&d3F*D+z}S)aizkjeVAvw68KK5&CCpsR+o zWNrD{l{gKbvEtWhUh^9k3+*uXiilDukNz>^Mh(ms`&P~C`XjEFZ%xDW_b$qIsY#Tw zl0#ul-e#Bb1AWSU7zWp|r0}kvC>IwVyG#n%56PcE{boLYYBOi3XD3KlLQRm~V#n@V zWT?`7BT*_4Rc=7i11KiqY*}OZL}M=)jn@N~5r``D%D!R|L;O^e-v+-}Nq5|G0*!1zxffswo;q9dZ-Y1$)X)g8+k3`o=6%=7y zq?-3+)HXSR@rj&BP{&QZOw_2JxE<5VR1no+oTj=@oCB(+ei$RHrhXFVPjWRbl}#G8 zdMjBTwl9-ZImz#(iX5vIr(p9a7$mC1ZQUfY0)mK`a@~+}9uO=|NJVTh2>mILLlui{ z1`Qk7Cpy_=YhX){AlqnXgpY}5b}j*9iTKhFNefe)AL{xXlL}LaB;Nbh=Oj9Rn%i`; zBUd3^+;q8XAxdpNG>;=pC0;E;b_o^u>Z~!N@S9wgsF{-~J-pn1*4LGsCxaRE+8ard zjcZz(G-#f(wCYrUd*guHMwc8Hlb%Q>&T36U%{r4r?eCE)^wBfS@cgVmJ(D+MTG-Rz z#FHy`EZix1Gr$dDRL;7^>e-eYgMnyWnJJM>s8-*6Fq0SauuTFbvKlrXB|}nyc%0<3 z$}l%Rw@EEzf75FiBEdgvHy1*b2xyh6gi#h&*y+8@z=Yh`i$@SzlB91~IV2+(2RRBX*K6PXm<+seqiqTIMmj4DAhieAI_d5!amPJe+VQK> z5s&0$?#tnCd4wdHLYfTcM|S~4;idUZaWfw#{9xX9$L_0h7F4>>62@t4B6oXV?eDPd z&WLlKr_tpD?5NCXUTw67=x%kbW7{7x^B9Ix%_xQYe_N2OHOxQOg5t(7>y-+#IP_32 z#FF?O07r0ShZ6}%k#o8EApt7_t|ej^3<;RcyWD_PvHxarxHGizIdLse#b z$nfCl=FGE8tC{r>R1cC!V`$6$34 zX3;LoCTHr<#$WvS=XP5oXWbMSvkrX(==9I z=ycI5wu7I!PK%eU^PpnDe?yIW?C1aZBBMv6qW+2~0-`Vo0TpC`<*k_FxyvCSIAyva zo^mCwV4dFcJWQW_KZy*aiv*QA)%N|Z%lTV23gj(5wD*q=yHQ~No_ht6^~Dmx-(NE5 z24ylElnG_!*}beiBN&mQ6W%m7lPjmSh(SVjWt6rVSeP|YIe$dKU^pn{2{-1~J$h84 zM58e5wG4t_&WLCsf$`!AfU{$exOTW@aN1@$%(DzW~&43DF2wL7ih2 zi{dei;~l2~e*qt`f>nF=vo7lB?CIi^#-#J^HZ~}werrKQa5cl1zrDTCOmU3J3SI8W zf`r!8<#n3XQytMreU^z^wnHxpt`RcAGyB}@Dboz*CMpbnvgJkep(WAqRKSjE1X!Qy z94jvLf~LgU7Q+!E70X=Lo(+S--FD~rEu;r*$;HWfyjfC-)T3i7KG+@CnaG!scWCWF z?naYqB7}u<3s!NXK^9vnv7)d6+f1dl5OOIhNA zuR(Xj)zd#=-dSFY#yj7cUyDY<+ttLaRf*u}$_xAWT@gL)DAWr%PW#9NCU;^jD3SVD)xL5}($k(225SNB2bHO6?~L8*NEE zx$G3zKt>yIZH4}IMceMNK}DAMC?Qd28{RVZy>4=D>O1?(>^&6MfptCPA@)8OAbUE5o1eC=tF5$E_<-@~xc7~k@j5Ws2wAQtD+O37^ZWBN!Bi*H zP=_bzl}>E}Z=4=J&Mk0K3p zPasQK_-FZZJg-RPo`TI-_*f&a62)X7FFb7WvctT5WncXz{Us^rq2^l`^h{YKVT z4GX+!yKl?CTL#fFpdXJ?kA@O-P0nDsL0L&AMrISh_BTn=ik=Bg`XaS`I)Y}hl z4xt+$+CHQ~7P9_L zd!B@*g2(Kd%Qm?)kIS`L(vd!|$`vJlyj#P-dO@jA=B`y|iM8k_)8;okQD|trF$DgS zAl@7D8mbtACE!f*3dv<3j?`nn1Bvnl54yrlzHl$?x>*iEa|lxpX?>9Ln)hzR`Re2@ zBOmHDq4soif=o1t_jtzAcaOx<*J<*||AD@EB`QZ)SZ3P@>4)&8J_@0>01{sSFBKjR@gZ8V4YDMb@z>c7RCSXAe z;s9VdN#hybX=`_nV#zfO9No3uM>~vSqj(PiXpF$`X?9-hE2eJ_y?iivEGu8R>ch+R zPoF)&?N!N)X+CI7={s&_fZopiRV{zNJ;=MU^LopJE5et9^A$reaPGLtc0*6XON8?B zrv<9w({t)<`uO{2=q!211zpXuv7^W9uFi?=-K9jAqU9~xM0i78gsnZyDc{`~rh5R` z=n%qr{i#J1{Q_0Zq&Rxed0GaXc1K@R*bP@(vTWKL{ZXAZ^PtWM+2gNEt)eR6eqGJ! zC$7)(P?;e2yCV=&Dp%bfYQcKt!}fDZaYLVxy_4}GLfW#wKt+}hYMq?XPTWqjG1hXb zY&TxzjnDbnp~wT^(F-{;Fbm_z4dcukB=fT*Ob7|;uIUpZzs(U6IYP#@GUD7^e;@7h z)cuMcCDe-hA!0<4mAHP>v9Nz$2UIP=q!W+25o2N}Uf$^AapK=S&GW&-TTbs6n2P&M z-d=q1xDW3ryITLOf zqRUCkY-dxsTQw}zob2eTs;e^3#Z`5Z!t!^UZtx4)5MbF1Ev-ZSsUD3|_mj#PX=x1A zGO6+;)+#B5hq+y)52v@}aJo067p%Uowzx=B|B?2-$5y?w{Ds}ijRwWAdNYEfC5W#e ztgcmn$R~T}n;5IY$WFg33;jJER!rC&mFM!^FQtua$p*SE)XPb+pd%SWn(hksS%&?g z%<)oAJyXT-BYX7cMMGDdKvpga_aXmJ;8BJc?0A_&ZE9`TeWQSAWc+EUM&$5l?M*I_YAM=sN_#xzTB-Wh~&@ngVGT?r_=qES&yO6Y4f-T?Bz zE2P8rjr@PUJ`=2WhRme~vujlgd+ggRhO;#rlb;sEwU7_oWn^(YmuoR7zhe=-PBK6* zvbbwXlY(LUV%Uqu<$O&{j8a-|C@h|I)s$1M4|E0V4JKtl#;MU?5dJ69PzlR7tNkrg zs{K~bvHV+^lA@uRv6Zocu_@rYB+BhyWOG*kfUdlokTs?V<&Z%=2hrA`GK4EgSx7~I z1h7c31z%b^X_p7jwH=yCc6CqD2v~2yU-BdBht)yCkgxlvwk{^_C#EK*rggV{evt1K z`Vwq>zqR-U$H4*I%r_9xu*3}ciLK0cU{J1k$*agm3Mr=Ot|UCAI!q6Tcj+*r&EC@A z5~Tt%-Udu%1k0N;$u!Hs=GOL6;LZ6M?g^_y@OR@N0eS8Be={(oEaw&P zo!FrWOEV_$H&rt_Sq9gKntl|g@JAzONJ*yV?H{qz;8k%6tQ`NUZduZzaH+v=xBP49r!iIQXQDD+Ky3dSAqSJ0*_t=Cl(E`?ITE1s-5e2P=zk)UFv=1yD~^ zq)6dn1J#~@^w2V8a0e%oG0QSC%Q&fqmukkSBziE7Ga`P62G`RC#P^U_0>^%DrOH$y z;D}w!QGss`;^Jb!d5{%x9sI;Y*Nmt&^Ab;;YoO<+!gkCSrvhwL-4j6sUmG1x9yKfz z&*hw24>Br6mO^mC5SI0h$#2fv?E=*)B5g9Da4|lFQx&v1Vp5`Lij6Qt7p|#soH&>L zzdSo^|241wP!xqAp&+GxJ8K}Y{$CyK|K$SvA59~fs^4*;->qh{b?LC_;$lFMzwjXg zgN)*90{zzf7LWxYkAT!6X3UN2xQKz%H!r6we>67fteJNzS(OXYH7$l!HVD$lNoeLM zujg5HKHl|@LC8g%HZL;&wT!Sb-aH6<@E&mWBgX;7nR)? zj8-W4!Cj747j1IzN3&MV*QgCVcq=zz58K%7B)*?3^B)^f`Ox>n`+k%cR^0gU{kwCQ zgR2m&teEk5``0S1sIlhZgDZ+wPgz>o@xvA^nUTW+t*qE_idJr{o>P3w>EeKiBBf8m>fTc{~XCF!xRK0_@^)r0uY(dBh{_?tsbOi$ z|D^!CG6F#Q^QA$VlF{QgQ}Z#GhnvMZNjsMqIop9M zGB7#rgZUzTL&It|b9+!cKv9HzrIW9$umF7$zG_(kKvSk+a|COGSj1Zddm1Ez=R|@i zuIL1E?(tl*Z_ZvtD$=*gq#(43?wBw8o(hM;dK!bd{Awv2J0goQ4kcSR*WD%num);F zU{J>BG8bYZ8a>gO+>nTj$B6GWyNneDJ{ka?6>5#;=Ac~NcU57UcTZSqM7g;Tp~X#J z%VS>Y=E`C~KUbf#B@(_i)7~$`b(ss3m;rTij46t?KtkMftZ?JklkZ|8o(@Pc<|-fk zB%NVzbo^0$sA#pYkTUU2>9e*LuOhNzsIVRccCLmh=i1?11*RDm^9=(|=q^fw3HZy4n4!;lT8wHUy1c*dElD@6VJJ3pl4i!4Py`znS{bf4x7!DC`4$0wpOPFS-Zo97mR)FUkr}^< z5mI>RngXAV+D`vjpUwE=oJzg48zEksq$0Z?c4KE?Z{<&$xvBPWrcN3SHQ}P+Bx}&YUk`Vv z9CFqLcTc;*nP$=|ys)`u4!WEp+S`6(4SLQL7>VqFEYseg>i}W?CXP zvG0^*#QJ+OzigJf0uX9q$ZW_u9QTeGx9_CWIB2tWaSH)I%0%1ZC++IXf!R`z9Epq9rOgH1}I-|<pPiD<@{q%)T57*l$p%&vqqkKP>hX#2fCnjm28`}u6jFxR6p??prN_g?pGS1x2B#T zVqG|x7C<#Um(tnMTy0SO7nr+99db2D^-ydAH;2gV_q^+>I6T7muwz9;Y z+k8JCV+}-5Q`143kWzgN3D4=C(faUL&6 zK*6T@*(NU){pUDL=0arj`}mZyL@o7Ad&CJ$txX#uXj@U3o@@SU=lF1HlngettM;3y z!9FwYQ>+^N%3rbA%GZ=7lD9d@xXo_R=`N`@pC_>X@?&pt=TpFm7{_}$3P1sLM?y~61y z2FIud2)+CqOOQcdKs#giU5k1xrY{E>wudn|-2usWjkEI}Qbca7IyVn}64;YTQR(Bk zCI~K_hLn#qmM_VaVm519#^>TKhP=TJ&3Z6h~xh15UE%meHao3>m46n z-`)-9@O)I~y*hH9X>;4!Sw@2FsQc?0pJ5{(+$%!B1QZlH$ze~GcY+dVc zVY|}IVO=@ird$`_(Tl~h-q=mNQ!Vys5%)E;zs9-TxQM%Ka>~9VWpnZ2Y3MWl=yLGR z^ZLo3#D~hj>U)4ZWM7w-hKoOf|bvBEJYt&RmkhRn&d%`FPUgW!V z-E`eI6MmOV1yT8{%^vdv_@yTPw*~ge-F!>L^4cC#ila~%CcHvxI_cpH1aI2B1A7_Q z+1803t5jJZNpQ)A{Fztl^16zB7-v^m@s@)t(&QlZ?2f*b?PImWV>f6sr`EHA2&#kW zPuf}2`bU>6p>Y8v$1LG-Nb1Y;k-6rZAb+z0N2Jqq@$QfZ$&~}TVW3&RK?904;q*e8 z5mNg_fYUiO67PCv#evB$9G{Gg~LtY zhjwDHoW>7*4g?kC-z}Xx$Oq%8egkDdiZMMn*zE!HW}Zo>u+mIdZoim>GNs98#%Ay7 z{jdesNnvwUEKpQ)rL-b7YQgRYG-UGuwoE0xX?1~(?=V*f>%t$qtok=HX9U|Uqq&#( z(n(eIL6g6d?}~eRoL?L~GEM~fwZiRdaTD{>VGaFuH>#5aeTG4-IVdbMg(anXN^{oaVk8lIlshfY%XPJVo~ zn3t!#YIqcxj`V`t^)ESN52tTz$paA;wg!b|fNWw4bcyjGQ+x)CUdGa{X4{i|saxDS zZp~lVM4RsD_;pXqSZ;(%>X0z8Cd_p2(%7Go+b~fR7}v_TAbup1-m1vxlF=77*I{-| zaI1qjEY2;mS5Pd0);0i*W6=5z)g=MK=TD{_lb~Bq5C6Xid&lU^-gWD5sqp@CVL80^LE&nzN8^L@rTAZkvWTYqA7jpQM<5) zv=rWj`WVpOC55av(*97eveX=rmI6|WczlXeCc<-x7N*6;qgK9`g(gjlNjcK13k%K3 zQn#c=>gfkz*(WLTsr6K8j&SSPEx|(2HP~F0Q|4oxR%v5=nkg-oBT-RBZ60E&j5;3e zeC9^uE)8RX@IGW!n-kN~Cza`_rilOxC5CO>McPkS&2d|or&(9y4g)M(1RqPA%s9fQ zRSO+`R$3oBUr$B1;UW;4gd!Hflqa<;GakLU%c`cvku5SW*ag9lAfbTj>9Dnc%~C<( z?)K;T`DvDJxMT~~Tph_GZ8n;(r}Lh5nC)KeKlb;|BA|d2zKKcfZ&s7!whd(1S$Vm) z_|D%cU%s`N$oyLgm zofc|*p#-u*+*Cn2j-i#(<3V(=t5VKDH?+=@JvUA%y>ZVG?NByzwJpYlc0i)E+NW$% zx~#dan$XbI4FpqX%^paWk^?@0eu1iXp&=}qEBa!hd?X{kq8s>fenyLj1mD{Dq#3cNzX^rm$ zFq2i%o6Q{`7~tPBnK>_8`P`Q`(ZzLdk?YNUv|I*uG@(`i(?|qGhzcKhx4NXKG_P|k z{AjH6D4uLQuKBiebG42z%FIQtjE2yRRprOH%#1#qCB97hkwMF^B^pJ?n6Fma-W>fU z6#Wu(X`x%E?qM9u?V4|M$WM{ z5!u{S6|q2LnP?&9m4}mSioC7-%Q#}BNnLd0NSB?RjqFNm7v(0O4;H_hRAwbTS-PAu z?98pyg8Fkt%GO1#^aYKaYNT9hT3={llp|(gC^kx%4h2r zKK@qynO4x5K}}1o>jwAt>BaETD8`-VrEd>rKA&g@dMbRhm17LyBl{j-@JB%|pQGXu z>`yB}I)H5~_+lw~y%Aeh6FNgVzOe-%Xp0^oCq?G$sLCZz-Z;d>8lZXB!t>Nd^OV;B z3kYGDL2a(+Dt~cV=GkxgfX!9yK3)-G$kWsrkq*9M8#W@6i zuR6oIKw$TWi@q?Q!l4Qk-E=uWP{HcwhWyhTNXpp z`Zmc$c?*0n8;o=Pfl70OTi^=$wCBNUFh7ub?ehJg-&76y3_ciH#xbfBo8y@I^O4u0 zUV}g#QV#O!!LtmD0rL(@^1>RdEE8uJi%~ySqc|pZ)BF@xiVG7(p7RDl0IN)t@8z>T z-yOCGItyM@sB2dXpu4;m7nu?n3of_Sy9Ib)F zv?-ME#iak;O>-Ii=0EHXgb-4DeYH$KpDTAw);;_Yl^@neU->u9Oml{L-f1!@mGK(~ zcxA*mW(;4mL#aDNmG%4JEXgNuo?>-JZr7_byl0<|!F7f?Vp|`~$r98-Qea~8)ZjA1 zecp&nQU);DsJn%8l-s3XsvX23%Xl`-+gy{1*)@&RC5+vt!aJn`i6Y|aJ@&Ch@;-(< zk^pec?{r8$zu$_=R2?omDc(5W)HXJ?bqdF9Q=mseK|dpAB8}2m7ghuZ8D63 zjh=7K;o0}tmh(T4Y(1QuO>BfsObuMDorP_k6iuA$tX==RDoFm0PdL_n)f5@h+$pYW*Yt^a9`nh z+G;!6a=M&e%GFE#p=-eQ6KLp%Q`%D}A6tJvKr_CSSMc;bc5i-vR4I@=gTy1EbbK3qtDG4ptr(P&L_3|1%JdK@f@ ziHUL1di6vr`Dq5Tk_ir!53OPH@JHd1Cbhx!zOF#XL(^?ICz>|Q!hs3~&B6wy&*@>5 z^Sj@qYJuPb)YPLUyMx6U4`zt|l@=+FtVZIqI(boM{3Q!qc{7gKZI>h!?Wf%OM59I6 zj{<&GA>JXOWM0H!#*dmI z#vmE!#x4Geu;xMNk85D_iF-69_Iu{rgGA`Yx@n@Qo*}9Skh@VzpU_k}XvH`483?{Os7UrAfajOI5h2PtnhP zuucL@?itgqM!I;Ptmurc|CRVh{#quf`CTVAeW(60{l`o3@1)OvgAY}yy*c5ip?y|Y zjcDqQ2s$KCGldJS38$QwA8MSlS2-vm%i}JqZVvm@rO-c;*Q;*okTHS17Sqippl5`j zLMI9NDVm!ZQrL1Iwz!61nd-nnbqT7yt(V4p(IC`0FaCouXEeccHLe-l0gi;7w7Jv6TMqn}KB}v9;F< zZYO~rgE|MxDaQ?=?%3|5XSW-X7Y zMzW8}at;OMT9h*dZ-gha&y@Ig?z*8-vBFOc%Oz5-MVnOyI7ha1$EpMs1?Is_8o5@P zLfG}R?>;3ByL@4vOiou{&B}AkKZo2un=VUGP}ov-62{sh&b00$Wq|tu+N-D)S0UVo zLb85%+HKE0CS{Xb1W;tYGT7RCIjJ0ZZ5TtlWMil&4dv{qIn3)7f&+yBYpLQ>SYCQo zj(Inae|LgR$y)yT%Ny0nXG3S&dXr9Tq=Nbf*cwY`R$A?;tHhSABdMKb*uojtskmiL z?mWZcY2&tSnZ%MEYL)CQF4l=FLJP)M6U15NB^-HyCU6wp5FWo-_w8Qyu-q9qsans#hU?c&B-&8?8!6J^yxE{ucWL(Xz}KulH8tI>b&p< zH6pjJS=2f}su-pB-msw`;se+2m@};5piu!jvGJEbvJp}H!J?u`gu1Q_6a}Hk#3B;4`%&?mg1qypv9r zO`5mHWCGSR@|444X>*>zwu>?{31;S|{U+<78O1yPG}O$-9^trqn|hte-}@I2dzR>yo5f8d*iJti^(vcns@3b>FaOYw7D7+%IacSb zDb^$}osGGh2KPWkDr%r8R=4uIrpfxFz~nZL@r(`Nk?#*xU;?Yntj6Qrl7#;q_UnqIjSh}EwL^s`4{K{f+7ZG5T8l;_=4CQm44|w@(RU zdgW)HsI?|e^wW;&b{y|RC;C(lU?j6okcrKLI=G!;hX%D3Zs7ATAL-`sytU^@L^>3k z)dIel#ma#M82Jz?hxc^}qmWghl(9Lxs8xy67-$1i+_`dLR^TH(yHg9~2kc&aLs%n4 z2fe<|BXK*z(XyXVTYe+LsqD zA+H2&C+G#vK&(#b?YNp9%q|UH5e9UIUACcNd<3)JVT(ibJW;PYIR_W)mi>i;Q6k4% zUtGdaIVDt|kWU5gls_b-dcVH}qdebtRWuDoFkaQdq-?wVbaUGeAkvxHw$Nu=H!~xl z?1;LWenIi%M-*2@KF^-^@`zmkgKZLfI^^X0gm=2dpL`Zn2Ma%M+SoaGb`#!=-xIjYHul$D3 z%JWIj5-?YI!vIWl%R%&(viOl-D|^J3_jI4pc6HYNX9v~#ojf~yD3e8Ese!J9Vh2wi zr9%FxIzTNjc#EIGy&uV0*Bp-h+wpw@kfQ=r_Mal)pExG(6rhUWghkMFd7?QikA@KB zQ0|zzET5@vUkN0R#Z4CG@lV_xygyxi(OutN?!esel8z-%0b{ zLh9wYp%-Ysu_d^K+TnArfxU#gcGC8|g4y|a_Jh4jocVA(4g}{%?d9Dt+Zh02-2qy4 z?+=9l`5twi4oIwWX@)XI;c%k$8sgsV8qMdV%#0k~)zrVo;qvYe49xQ*|D?f(+o9zr z>iU(~HUhj7J_7Pg%C8biacu#Td$Zs0oMrv&{o^Z!LHsK6_9ga5cMt>4v*eGn>m_b) zxF?+Fc7*M?$ejD#Ok&)0FUdbWWVv>4*Lb`47Fbz{Gj74NEd1VbobPf3-B$w$-p_fo zAE~q-qoD*4?q71o*GeD)D338%&u#vCn9p`#lxaFAjfofHYi(JHN~IE}A*hv?(yo-2 zu`6{36xb7mis{buo#o|J3ZSZjs)&gX0E9VHENc|~ugZ*h2Z2PhJkG9%Edm5I}0 z7vAK_VInQZ#XN~AfIH9j*QZCf?!uCWo?jD>vcbQ-*ew!mfAMF|eit0q?VZ`vR}G0^ zp(T#SB!~B0fN*VoFJS{2F0X-?_ppu3nD+JjkE086J|$JRXikm#B$Y5#{>^ylI2+w; zSHl~H2Smg2nPrUHLc<+!D`$KAU%*RhiwS|gN-*oJ3{+bv`l+f~-p)X*7KoN$VDH%HjkyHyHZiB ze0*GpA$;wuP%-#{DKWL^;mruUQAt zCgD!cBV?5HUM5V_DUWyFGbco;(o=~)XN?xgW9CEBM+2gF_TXmZ>~GBwue(bT-i z(^$XuV({dckLxlCi}ud{s@G#mf_$V4vH#W1jcZ+IRkGKjNxki5HzPe81oeW>Gu&EH zBeG&m@#sCk8vk`7ePWDIP=a^Wg20(6jr((au;y9-{BwD*<^~6a!_T90D_Tdj8M71i z{mHDmOdYEtw3=G%<$4x~D^Yq4`ePb)j-IJ{ zlWwcKYU}TuOp4_yV(lse9y2&@*)`cSL~pTvYLIm=q36$sy>;K(>yeN6IUDp5G!$h0WwXxI;Q0lOTd;ku4o7;k#B-J54Y_RfRXEzjC$x$cEPO7H zKHsq$NawU$%;|JwKJfZfK_iHT;h zhT71voIQ88c3X^pDynGs^f2T)l&DKLN^etwIcPr{{Vo+wC;o%t-N7{$HhZMHzH2F$ zB>C`ox_VJ4slK#6ri~{07|B$K>4>$K5aU)9o`=em=Q6J7wsk_7}%!7~ti!=Gu{KpXZ* zFm{M%I`=XZU0)OG67WvABXmF`87!VbocDVl$Xb)Rco%;JhO3z_rPfvy?vw#|#{f2|H% z@=x0Ca;$Jn2H^x;xOQwd-)QMBs-vwCD-4MPur!EY6kXXtZem^q`{jX06|a^f-rlPN z-ZXg}>GA5xZHHQ7bOF@nvHy2SFXT7?sCP(_qaMzH;nCqj^zo>7k6RD=9O5wh_mR4* z=8RX^IYO?WM_e@9kPeKtJUUclwWFo|YiHo1_DOkb%8MLTAMSx4B5T+yr39U$SC@ z_s6ptr3oaj*kq&~+R`9Z6wHP`c;dzE9P%=W<=BeUFQZ)`7rOWH7`Vp_D4}RG#(byi z9_x)xYfT)$rPw~xboLh&UmoZNvK$fj1!F6Kbj8m9j3&M03KoGDtJ8k{4sg(i&D$9& zXbrO0VvZ;(skMf0ZGOLjBBy2U3ojn2RnAbT-1(F2mcNBp1znM7p76P^{zaG}R_BSE z+U&)-FA$6#>}vPSstIw>9Y=wD`$_=nK{T8>T=y-1u(G_5%D*V}ik4rTT2m1AaNPW< zEAJn_p~mCT_8pNye40|DsLLq zy&5bYUf#O2zMW*yA--1Vn*Itdv945W$7Ic>`SfXh!OoZW;eHc>%WkAJ@bxfR4(kdZ9eT9N6Mf%K5y6 zUN0_0M*i~L=4zps)Afb#%v;BVa3`QWEjjA3`<@|HwoMV!h z;Pj9lu|^=G7W~Db(i}lq^YP?Reg{^BE2jOC8>#FPwkRQfjNO=-{nbI3swqlScUX-{ zi##&mRNqTjhob6Kp6A}3$h8#5xsj`Kouph%({IW5En_ihsiWl?(U=rWH?b5C`(SWp4uXyYtGf*(LJE+a{XL z?NgH6wJ_PqV!qK?3NstMSg>RpL5iu_dB*nE>p~Qe2YJpD)gmZ(RTAE~aE1|}qNRIu z-G6$9crOSdY5E21e-5{o3p#h+6(h?c_M|Hp!&^$R$-&&?+4fni=+_z9NET=fkKlZ! zKD<*m1ShoyubK>roCgJbku$m)CuT;0@W%+`#QJxKEv1DcSQ~`zwFpe&4&dmb#v>- z58DnHmLE9j=gX{~+0h|1qKOmEY6T7>n`od=9;|8a)&O6+i1%wMmq~o`GCnc|`hDJO zE9!TS^AB{mXFT*d$cbx+fPIa|z1Y@}jBQbrr*iC0agRoGoYF0ks2DeSRt7h={oGX+ z=k8ocTn?tUPECmX%*Hy)mDp72d!ni>C=?IbQ)AIS;3G0CgjNhWR+O*B|nw(&{e&f zzeb7BDJGLp1uu}FP+Mx2PN$9Ci3YpD1cG-^Hp^p;^yyzVudfBtG`Ck5Lronk-K{G< znHRR%l^R#4I?w;+5&uUCn^7(&lJgCh)cMYK`p?RMe=PvMr~meLwoWGU298euy#r`c zd-=|F!u{$bzX&m=L8MftM2r!_76%FN3mQ<25d~tSUYzj!ecgti9+xjsUj7@f*d3kq_cx^v@zP`l@096ju=z`uvVSI2N z4>e$>`Kxdq^f~GB#twL22l!!hUGG2=i0i(RdN3RcuDQHkZg%jGVjh5E5!B0r!g|;* z!U!k>aq-u9%z4~KwD`B52Z^#>z|I4}F4wsH=5OWdze^?bwcI?o4a zz898JehsknDl?3rd+qVz;vQ7V$s|;KsS9i@LNr(+6T*9*cqM-)j6k+D_?5*`#&fYb~q(xD< z6sC#(Lzvjj-^Mc=vsOfBNX2{cTY+yOK57ZaxWllJAko%E-7&_BIpv0a!K74J zDzml5E?17VKQlX*iwn$sM|&MGZH3mPg@uwVpRG9wi@sy>&#kN{&Gpdf&C?dr^fii@ ztaSBK_}|HYY)~bj>Duv)aK`E?1r%Xqb}THFr9(6#p7oIi9&p2n!C0$n9DJ5E+5Y8w zoX{7rINGVSMOcrE*qhDedFyJdA%sl9d2_V47ql-G{6!e7k(E+tuu2@|hX;im5jn?= zC*m2)jieR&x5lRC4)zbL6&tp)9dGWE(^`egsMqCw%cK4LWCcj!f*YGlB(kcmq%sq~ zvuLmsf@&YSU2TtEU+)9r6P_q}tI(BYG9HO6)QQJ+9Jk_>637xuG7;h!lCm~x(x6R@ zLyJ$0!NjH*8K$v_%Y`X=Q4vlw)eR#o22@tEUMQ1ONx~}ykuDR#4@<>I4!MA8BHcmk zpxvbg*WJa0afDzS$m1cvBiH46JBYUi-iTF=;K=OhN+8v){lgg7M65^Pk&r6M+_So~ z?dZ6Mh?*7zQRuB)Mmk#qYt%V?BXVMYQ`=LSB@{X3ema(Ol6{*>vu2 zJwl4-n-M5A(>_KQ_+>rPpUpIv5*#{Ec#+Bd$p7UJc25@m$t42Jj zv+7FMOv}{;9(&MJPhE`uBzbuy0Fp9b@yD}dRhTX0rl6WttVO*N9u3Yuq+HviIpThE zGTfd8!c*BGRKHEBaA<|b6X^qAoL>f%Gh0_D>97)E=jO1?u~Q(a^yx=+^u~|C%PHuP zDH&#%Aecmky6euCkqyI0`bZ{|5!-{cUH{1-mXtj@Qrh6(UC*+Rf2i+IW>XRDTVgM* zPpPYChmN7bY>;^KOXwSnT7F zM%@hy_^Q&n)H_aHw)Y^7%ivXP>y2{`sEj+~Ws|IH8FD+#AR}nGbofYLL>f}`Bm+Is z*(-hmSyDND@%U)eqKh z`DDk259x0q3?kpbN{ePON^^kOk}qB5gWjeK>p`)@W1n?n0)2g)#WBYC`#V*a>a7kG zLfnghoydR0!Udp!!9EXrzGccQDDG|tlTRA{#A%zlxgKE?G&5!833UapxvSj0^eOs9 zvY&R+AI%&DuC*wrwbV(hp^-d;E=qxzBMea$&zM4q^=rK;+9lMspC>}2!ht4yJj?G_uYeh>)0OmZi(Zw&Y>tqZtP2Hx*w}*A;7Ie<82r5sSUnh-NHwmj z0HCjarpApts3%TzCSt3@4=7rE%mL(I`lwQo#Hk8)s1qY~`S3@lD-Ru(!SAXV&8=k& ztR1VIs_oZWtG9e?C^I%j|jtyMhIHH9S$OQL8eSeR9`CR}2RZLxy&9a#OK< z`0v!NTicD{R)kI0bwGIEuQ#bV?w>Y#Ww}58H%zvV-0auIcd&{f%#R=6T$CSnj%Ex7 z_6Fa!BnI>Ei~t4;8+&U8qi+Qq!#_Nf|CK}4q^hB|u8R0sjbR*x9FX`^0;E;T5LTE@ zM@cEg4?rj@-8U>T35X>kv8dN*_hRmQB zeBBgq$Y@a=m6f|#i_FvMbJi8N3!IHB8qeRxZl?A$l)~!zfgHBTO<>-cSUtBSZ1aZ0x z?1c2BDld|xKU;&zIyo4fk@dI;aptieGNiO77u9SIv$RJ`XB+iJRJSIf1gVM1Lw|xT zj`vd#kl7|B)crCN6MJMZG>$2cnMQEo$x25OW^TMuZF?G604|~ba0XoN`yC3<1se(x zqATTbR5Ek(XkHrGk!-pm;jEiv45((bqGO5qO2QuGpy``hX{NMQrlv?YDojG~-TYYR zd3K(!jD@lFGeYfwC@5;=>nz!t@Ol1G3z7OUTyXH@^9JHT_Pim2@f zRpR@bImS>e7;}!1ksvm5=*2oN#3n8&2vf9N82*x)9H8>XAX`gu z2zE#lX_g+w*@)h|g~H*v!7Q zLf+pN&;P6!`d6dyf1}F&9Z~jQ+oojYf9Ug=d`j&)rA?Gut?EDwkVUl-qzQ}SkdlOD zE2x0I{raejxXtwTY?~cDi~PQ4eeXvxte$Ke$;QV1fob0suA`2N&)1I+2)`2dgdxUD zgndDDgqzXf4M=O=>3JqQqdw92KFg5P=OBan{QmZRcAc8O?2tb)W|=WIOYX z-?3V@ZGp;>eJZMKK1C2+mAvYkl-qS)T849$LXIUOGt4T;F}H#pztF1ljM`Lut-ysi3CMjt$i{P-_I3K35z z#(|9?Wc|@@{zrEu7&0w(bct;xh2tAKq}YmCJIo0&+jWz)auLNyzpK$^bEu5#kR-_t z@QY5|jq3GPr~vU1DTV1>Xh~sfaZ_yN&gdfClwHQCr!zTtlF?xfCs>+PJ@SyEFWpz3 zZhKww%`K)RPY#n=GnN}W`@-NW>mZlYOc|NAXzyGDn{H%|wLH?(>GeR8%Q@3*jYnrn zC-fOhy3*)E zItLL=?RAvCDwE`zv?JWIJszVX>`WA^sNDGM#`$cel5@nCt3!M?u@iVU+ux&6{i)t9 z+5YxgR|xiKtznG7E+%e$w4UDA#o%$wM;8z!Y2bE7^h@XZ2F4gDfT01hQqrd(6j(fhm+>m}ke4+^PBv307 zP4>d(qov|pTfB%*h!q2+Ou8aAv=S$w2%}faw;_gB2x<@95y3B;v?2|fU2Ej=WAq>0 z2thLJ!7zI(@b{qhr`{yM>M7hfLcYaW_tG%`QbF~l3qtqi3$okE?DtX`Y7t&&F?o)G z-G%1vU_e zq2n`=Mo7Ss%2Y-tSr%_Cwt+i_T6V2gQnOi;rQ)pBl3lpJqVoNJ}~-G>Fp zWyh7%?lR>L5xa1VqTwPrljSP5;sk4gUW=HjFEAXfjh~t?W!ua5?w8hR&vb$XyRm$9 z#ej-DXth&TLr~%15_L_v#58J8tm+?R+goJOj(!(4eMxb51#6d?aP~sghE+qWET6RH zD)~)sD#uc}zF%w-b-&zIbG?z=+w^vpBHuDrA%d2noG{a zxx>_1ZLdw6oZdruoMN1p0U9r5cGR@4sEj$am0*SV zj?`su70t38Rz~sj(75SOJz_g)(kGOVRI#|DR2zAUyc)Wrx!up33?B=|Ec47Sg=*Zr z*ENlT!D^iB;LK%A>o~T2(wnLreU~0kB5ttiis~%JmfdNM$hCS#^v&BH;4Irq zy}hA<+k6g7dsRT|`E!jEW%*3EHG56&TeA0r{pojy{Ygn+XYq{kS-EEeceRrevd+BG zr-SCyKL$e~N3vXUR+g3ic)8AAR}wvvUO#5Lz!>X=9g_zb(7UdV0E7EAv~8@u!D{Q! zS2M9&L>&bn2t5e54TW3o=_9Z-oUEMT6Xin*X#{ z?>M(2=hFDw)mNc$m?Vo)|K5}6QSqmssTv5$p*{1EZ&0fRZd|Edp=(U;%)?1Cw%wR^ zx@f`_Yfi*Mr>yDX9nkEmAF6SLtG>80iRw3tP?1DIr61dp3{z?|p0_8T#0i1ub#tI- z1BuB&mfuCl0Nx_?xZoJ>B-B`*8=H>M!f6g0+S7?}Y!JpAUn+ypA;6~2)H9K*ho*=W z#u8uXq9+?B!{Vq+;sGqbP!Or?#p<}sU+w=A#TBMm%OiwFdpmf6n5CT|JQB5V?tCfh z_s(Z*Fx$THIx;zsZb zmS@~&yA@rqH0gxJey|~kKIo_Hu^sopI-sWQFP1+uN&*#|qD_(-OnWj)m3w1~tdZ&k zJA$lJ!O>QO+P{U(Ym_+uX*sMbVClU9c|uAU)3P~Nmbkjt;DMD4_3P0!F@3seV<^B3EZ9# zHZ7gX>Jq7mSskTYGpyNy30!@IFXWiL|E`((XVe{m{gx~Lt$2t1u44Z)W%qv!a7yMb z&c=3bwj%CECic!2cDDaj#Ae6yNP!5V2G1OXTNxU9=LfHaX>vw{i3rD))*=UC$_T8& zuEuYsxANe8NHnVk1Xt-FG9X`78c#-U4H0q@#pCMI5+GQ#BVT}OlF#qk2NV2 zo=sG!jOuok!(8D4G?_SC2RHK}k=C!Y`MB$OyYRpk0g96O3`gy(A2FfnSmI!tn&)qc zq28F=kw5&BMAzLLRc9SfdxioYR3`0b+2`US1L{&;|K3Poer2;xk+0JQ|BRFG1%|d*K&HlE z0&6S|1{Vsmp=#82?%yrc^Dzohb8ZyjA+clLCpdi0-b9p2{Q0(L z?D&{rl4MdHR&XvWmje0jD=#05}HzW#^KIo<{O76F0T5=sqhI#~YYY-O{ zSx9NY)EzT}dNa>7-h}!NJHT$bUW%$IADNczsy#W$G~LuPAIK$)$XSuY<%t#W%WzY< zO9{3wO634nhimiT#nK~XJ@K?skYg^ZsBAdN@@!AL1GfFHeIU*fF)x)SguyImrc7l3 zuce%x0khn~^VEjgF?HPpc@GE^pS7GzbRzAr6(B>j|E`+M0itR>(Zo7&UCW$uax3Ci z8hmDrT%Adln z<=Qh(-M{F)gauzYBOD?-HQRP#s*8wk!P*Z{2lQl!8^c58EKor}XvV)mmSt39T?q$W zgp4AFT(o7L24pa|Wsc7?Ytyc6IDi6=>2%5^dQLtQPJZ&$LELID6_(&^9 z%Isxw7d#Zg>PVGQHv+19ftYHj)qBmL(H*qD!A>nEv-^7B!n?RSE$~xZwGS^OO5iU{_4KJv$o_53|G#Jn6o9z zA{#i`{<0g)XcD+n+!1HJ=cM)sT_!MK9LsLj zlHIBVT0ROBxBQvUQ{h0VmKjz^W+h}AM3zAu+v|hlAc&~dRHnJ_T2sF`vpG9Ew$Ci1 z*0t14stZr4)O7AR0H3wUNf5X5`_oROqLSaDk_(ICSaoq>nZJDMK+H(BrJG`c#eGcs z)1yhc%2F%8g^3jwV)+ICx;VL|=+#@~4K092qIRYaBilRyb@i_YG0F)Tvo_sL>gkkN z9?fl`CFg2_#VT(Bcc;ks93N91Da14)nY1H4~!te9O5Q%}5|D1Km?-E*6+G&NwVJY3ij?O=8jm3G6 z|J`qewl_EzPWp<3lvDP0g#>ON=8V{ZdIRs?A=nA%z8VJL_9@&7$gm}N;9eB=;dMr$ z9PAF*-{8+lk{Si&J3|^IbmSVkBp7EA^9%0b6eNq55n#>dG&cAz4mt#h(fS>Xa3()AThp@oc3=@{or>q{hvS0(spKM-+6>` zF3$EY&PvXXCI&YD8UQ8fIH8!Jj(laNl5bY~S(sA=W0x9k>C=Gc$%tgm8?xHennW~t z*2%Dsai7?lHUOD+K7l-wwcZ9`L9uXu0~#qqzgEwU=Nk>XM!C#poN}CeXFR48zo+Q? zaDH;N6^7t$>>hD`xThuOx{(5=l8r|EXlc1B_Xz4o=rzCo?F#N8N&(+f(Pk`8PJMoBQ~d^Bc)B! zU56fco7|_34;e=5uPCC7j)qJ1bXVO0hxaFApEM$-v4?-MxhJw~)SKHjdAM0OM}`eD zbA*S=urBxpnhtpEMLScLRDSo6I1O2_({*sUmLIb$-;xTp)2aaeI)Pz?E*_o^-({@c z(2kf^I*#gT_;Wkg8?*Yz-@4%^p$v=CFTAjQww>j;B!)lu3CrlR`F4J*>ZrM!dVX(`&@{nDunIIsvT(cj%n z99eaFcgGejFZcdQk%t<4PRsJqH9=Z%aBf$&V@CR-f#kE54~u-_Y%K} zpSeB^jf?xfxp7hjIX}#R)hKIjt#XkJPVZKiDwkE7B8+wuz1-e`IQFh#@1abijE>gaYOd zAT12UH3=;qK`rPBKcc%SUFImd9Ghk$QB>>bJA0#(M>#LGjg#Eyk$+Hl=#2Q&$iILm zdiyl2{6$k;Jr=XRj6u-WzsE%aQwPuI1f>V>H9TBBh_l-)MB{gb>|WnN6mSyBcMRAX zc{_JBLFEzL*r0Tw_lpt4%M7Nt$D4h_J2J>U?4}q;;J=35FAjj8*wX&N8_{@gC%&*R zKGr~jb)SBZU+*1#cq(j}g+w(-6d&rA8OZEj2DU@@KULa476ejnA@KR{+-(Gm|38M> z|7U2$ziKr#4{ux*yf4}MZ?1%_lyu^(4UDaoB*$tal_ItE@A;4d|F~gNx5c;g@O*IC-`}Px^CRg{>lyirKtI^I~yuf2u z%hku$)$?~FpIMT#{f6m>f<_jg$7bj*hPgc5>(a^};Ih87XKSV}m?%45c*&%CqrTtHXR_*}UT^M9nhzwW}x#$ihFR`Ie{dH2{2Tww^7;NNL*j%KFcV#b%C{SQ%bZ#;-oCrO59_m-v z$R!#2ON4=IFq~mR{lg$;LPoj>ZiCedgG#`uSE(Q=+$y|<5v5DTI^CfIiX1rr5RD3B zbQwvF8I$PB0QEJc9(p||3~!TV=MW^O<~14J#GJXdoyD8>^x`CIb!i-R#TB-56A(tm zm@ezm2{BCa6&IvQ^NmW|rA(>;>vJ+LeDhPL1%|C;i0SYiB=jP*`kdemp zlsO)@+Bv)Sr)cwQrKUAw#M6_Zl6qT}nmvqZfU3lHvQ`@gCrh@svZ);5mb_9JXaHX7bIf_Y(l154N$vXPROg?!Fje<5Nr z6}T0Oxv-J4=(x3!fw5te5cZU$F7z}8f5>|Ur7aFWe%=eTwMx9WyGgaY22i9$X+i~K zGqf`P&~h?NB%$f`6b6QBbImmYSQodbP#tqv6t@B|x(;bK0^9fY>E&wtiKUh!WfUb7 z6;(Q4E&*+q^Lv3TK=kxq$T~=Iu+rEgs;x?T<`EQ%XV`PNXT>av=lMqW8-G_(#S*^t z){${AQu_qc3DrC80BaUV7}Uvy6v_1)1B~y=pzacr*|%2~k2s)Eb#mg*The#C2bxDn zDZYJOc8R2r5_ti&=8T`!+bMyrj>9#O{r8fW8Rv~*Imi#J6kE?;6N;S5TE)&8qoWK9 zomNhj;jXY6wH$lZY)zy++>?J~x9bu)Yya4o?YvI+uObyo%XmxZ#LL--5$UVujVXib zpSbbNC==+4J0c*rkBNf)1`M81APklLCyeP{itZfC_6|-1A9+Q4xq*{A5M){NSAy=r z(X*5@xQmvdv(%xd8nai5?qdEC5op~JeB_a368AQt|th07IL)c0QMbg=*Yp#h%bSst=k(Vf5v#OLkm>hSk^w zNk&owvA2fwA3_7N+{!-1dnd2KAyit>J%csMA0j_I1WxFl5!_doBpGRtrwgk1y8!PV znC> zeF+bPV($qS;mrA{vIdKt-INp2fL!kMI~GjC%;P{ZWq z&=nbG`A(v^e_Dm_#%@$aa>h7u(Ah~M01p-G1=MqjcvjSt?3$;>7cX4pPh@=pNhRIE zyJkQ3!RiNGw8nM0xKB?b3`@(rUhc(bxww@jofsL%W+*ML-T%>!~rDmIqEtM3ZNFBh>(xdd}T8V}IspLa4NoU{jnUTF$o4TkuI>Jf=uz4b%k@K%T zAWVw%Aq?*Be!T97F*LjN;s=Y=C5EKlU`ydZ^HVhSQ zVodLKY01agS6HI>#Fdhx^FIPY1_{BKn0NwqWpa}CsO)3#Mk2?K5GH#u<-;ZA11IcQ zTn%wRvjUm@w3-z12~t74DdO8yp zjRQ1J)&A%RU2}9MCSzG}mv>6wmxaM~qF{3o(i-EqQo1!4)lm>l+H3+T_mTCF@iEx} z~>2t;mEu}?ThMzu(?f#}3iB@_i!LWzqZ%uC9 z`3>13iAejLG?Up1V#`+oYy;UoOZ{s#z*$Z9MrlqHbyvWHwy%SGmG4i=fs@uCCccE4 zh)t?oBy(v=lq7tysqi3OGFv_DGk!pzLh2M%2RRc;A5 z?0y}8rRy?x5c_G-9V`az#pqiExXMEOrRP=KI%^!n&#d&Ug|csrw)QRf8dzmp%x8;dEQa+-FUwiwLA!h)qQ?ZNFNMs@4 zX{Uf5ubHFdx@UB@kEKG@rF*N5hlpLDF!|&QnO_c;cW)^$9)s!v>9eQprp&t#=UF;_B~!U8^tQ9C$dPXgJG$BmAF za(J~#b*FMD@melb9aJ-ZB=JhY7oIu?3iLP&kL$+DsHKg6w>jK^-`0*`n~=40N$7=q z((-z7l_h&p6X7&fxm5~vz+-Y+Gac3q_RojnSU2MV#Lu7680>$Gq52Ow?_a~$f7*=y zF?ttosuEc~PZ$WSu9zS%D2S9%yTw?%W8>0B{ITS9x1uIL zjwIprG>~`P&9vPdb3cxm58}QjCr09gV_V^2m zyH*xfsTJ`Vr$8hRuASpEPzZ!f{kJ3nwBL;=@PhkL;Q`&RtVi4?xu^ZR!d;NYBnwKk3%dLml^ualsENQ_GLef{P1>J=<%QaRYSq z%8c3t`KD7q1N5axsnQD$ZIYp)D-2706D5v4@eeG$4P4FGQS-h;fFY2l1JL%mkBku!3<-?Na8sW~B@FrY zX)4ICqo`=HD}DRnxpD}(@UIX%^l$s(8YD;azENL-4i7&H?GILw?|^{6m_CG9t0dbK znk*k>a9>i6@h|bHcfwn-=x0`^Hxs3ON-5!N0PMt-F>oz`eki7mw3=`LJvht0{}dvc zrC%M5o+WTIO& z5cX#BGufI5xq;MhG+|hH@vMCrC>k5TSxd|)OO+?f19yO1Qo%~rtjv)$-x@?u- zgC5u*S7=#z5t$Sfx(IJSfzVwZs~z#)M#&54arIWcrh6Ttx{dGVV2YD&wUAS|#LyQs!@PX?7n9;kLi(F{YMh8I4v9PUoCPR(p}9`oxFB5xgV6Xo zhCqW4!DrwlIn_cMzU?m5i#b!m69hMGo==^q8n$C{qOAw1|B0&KB|>Y+h~X|zxB`Kc zB_GCbB7J}>D99aBHyVVTDIyTZ$pV!Nw}i_~w+QI+n-tm56GuLu!eb_oIdSJcjtUR zIO%f*z-(0(_>~HXWY4Z=TNebN>ZSEiCq%a1J$~*Ao_V$SNI;KduPfqh85)lPl}IMj z6yuV7OqUhqF|)wrURdQW36^U?ElmlpPQuvVk&IZ2 zi>vUx&?kS3W;!2lZ1xyL$ay(&e2&v+S)?sq+W13PDc~2)K+R9ID_oW_jK2gg*!=4{ z>lgt)UVW{i3RT4v6iRCL4EKvtJr42cdWMPRp%uMtsvtsE-Vg4wm=JMdWIVcst!Ok` zbw3(x??5RGp*pnH-y;kp>%~5?BgfopyZh|5O2#BQtO`@X<(r?`G%n)SAwtNPpP`xJ zGuNAI^WczRQzsZmU*~32o<%+DBLdO}tHYG)p@RBevDjki?1szD3%q14XHPMTE7#vK zy2)G4(E1b8A-ZO?W@kI}B^Z%i>9wbq>CX$L69{Q#wY+=AT5wR@Cyf^2m_D&-Ib@QYBBsRk`!XXS?(59qk&z! zCsry#}S5%D(az9Hc8^M-gG2cF}v_CeWWK|F~NQmUDbn z)4FfI%{i5tyBE=O-D`VGwc;bw|A62#0k=5CAV_7Zag?N>9D&TN%w%dJH6Sx5q0Ts7 zmZgrPr)FE`l3ct5Uv!$bEFQ8gQWSBZq$qmo_*h|3NPd=)hf^-ixVc#<@5scRTt>G1 z$C#W{+)SfMjJQ*mk?90)pyG}Mc}nU?d1G0M&{D}VreuMLMUX*4UHWMBwbfwFhr!1y zGpIbGXmVA+Mau~kArUGsN?l=$63eHNMNzH_yy(w@l^W*IMKV*z-KMMCg~_NDT=!ZouR`)w z5kiT#0YN+Q{F2<&DA>9uuiMOPgMm(5+KzPM4f~O+D1kn@HgUay2H74IoCr=tpz3pgy-%8D?gG( zG<$@yN;Vq1sY+8Ag?lZM<7C?VU~+2_?|a;nq&m89pKe?KjuDhWZV;@|$4~N6{0Q(s z%o5x|%2}GQqOG12si_k)MIHN8*l#{>rm z14kSXKkOHkgAsVqP6|CW9Z=nt{6}GY@#~2pjaZna5>>Yw_xu-Bf~X zPnrq@h;4-HQfceOm+FVyF)ibjEQ0}jlRcMZEJ7}rJjJLN#|M7b)Jq!B7NV+@D=+=g z@7dYTEbg30=tsAga*fbe>{O2(EKngkd)@-TNhP$%JgCa=? z;aF+yl`|t*^V{(H>t2U?n{*iC(ch+06^8v-!^rQO5_r1b1o+hMCXJ`zabA|(d)gZf_n8V)U*A*)KXBrdW zs^kyAcY#&ZHvm?T@VelR%w`Zx9c#q8(IGQ3X2Tv7&%d9Z#_FGmaxB7<|DXwm$sW-L9bCmGwmLs4Jb`cH{y1JRQP;hJLk4Yv=ckYI6G789h^w6G9fz|v(oB8s&0&jen!5^z?s7H z#B!Z=1wV=Nba~@!nYMj3msMY&)_yu>KvD1i7Rm4G>E^uCs(^ z1-Vu7%l?)v>Jzbrdly(tZ5OnHX9X=|(|qm)<2duo5Sf|n&MCs?4Xtv^ff<5yLeeQb z^axjYf+{+plq=2gNwPR*@01bR9>ioDnb7qLa}9cdQ8>3AT3`#H(vJ^4_vs9{f#m&d zmrjsf#3H1u9h1N-wtjI65H&a1Dz**a<5$3x&#(lEFKd8v&vzThaZ?NS`VI{aU#bbY zi-xU9;?chzg1>%Jh{+ot%{9Jfw8Lfd*9>`o3)&s7nlQw^XLkl&WE8{n6`)B`;GO?N zu93H25NCRzH}#*-!ap)hSOY)%$1*Vg+YPVyUooWr-@StDe~~AbkiS$Cy5>y~6-edt z{Q~W@D0mV0#my*7{8z!F!#;ZJ1uyx|MAp?X3$^PyB&^IN$<6bMXip)<+)?Oe0r7!Y z;?V!}G%|iNzJ)e^UTX`dP_HHGbG5%*bD#K}WZh(Wx_sVz`#iJzZQS{XAek^{4Fl2t zkZ&!px@-3Fp}{}+1VN#YO_h+8keJQr3fL%*+3Ax8^~}TevH;xFvZyUrBPF0%u~dk!8zU zOZQD7{_xGLH=o5MLC-L9gJf1zGxo%>PMGw$YJqe(oJ2i{th0PR3N;vs@t@#NG=B_0 z#1uQkaHg`9i{u#;q^(v6csuOQ4kO@-;!pWgJLET|M|RB!0sdNnvKa#=2pe-$b|Ooi9u z=NCO_>qP$K9aeL~fUp*`L>NoXJxaIqixo=s2~diTuw%J$`Ic50&3hG^G$S(bcPrYB zD_9QJc{^_7T0C|7SK}E?Q=aPByLvEW>ehN2eyw`-RlcPdDw12dhXiD*G}dF?%k1ed z@G4NX7oUVDy@zm`zCpOy8p%2X?F7$73lx~(-|F^Nt)P|!sO=C6At#INgQHpTRrDp~>?>tgBx(Ku8?3ORdV=faB%$ioAVAh4>Dm3Kv zsop2YmOVh__+2M^S-6&&u~2c4>Lvo_Biw+w$WT$OJ;yxeT21WTYLXmTOxf%E^VcKj zlB#EMU-~o(50`qANhoFz$L{H~9lGVjp85U7Rr14O2#f*WEIEjMZHPE=?({=-*6P=F zQ(m6o<(zJ$CWE^r9-whB=6vSKOG}DoGY8H{C(^w0P?Z@$8+PstN)6_$Idwy>Idju? zdr|Go+n2_+-9tx?|W- zvu5G>fJ)UPhrwLlC9o-p*)=s7EW;?XWFMJgos@o7cNa=RG9o**i{b-QjMshCE_8EP z9%#uT7t?Zg#v}mdxIM>P3svNMn=1#3I`YHGA`L=ZaHkxzJ#rm?+M-85G9KAX+6I@OG=$iSOU|;l93e_*Z{ro_ zC!3XuXNauB2^pL5$G4;vk`YdoPr6`_T$f%kYm!n|CaK$RBGE#%7a}ym(Cjn!4c{L= ziqM^Mc8bcWoi0OMK=Kn}a|O}d6O3rkc7Pk!6P9E0CmT2+AHWK|qdP8EmmnuxN7$g4 z7hZe#TF42UmF9p%pgffrF|!U?UW5j)D^a!=JjQTyiXOi}YJiQwi~UCc#u^|}Rg@ZI z>Y2Y85uV(2=fV?bm&|CNmEOWX-X(Cf3ri1!fWQ#aHqg0FP@&zkLBL3;(2tt^GU-l= znHLBG`+eQ_62>6)aWI8?`b>L(M$M=4q*e=XFCNQkpT^klU?p}spJZuCVWBjxt#^G0BqppaNTzT=|Dh=8Fz>~^TO{e&luh@toe|z&@)VasC_V(F!(F-VS9?H|FoSWMo5h znh9|WL9ycCs6+R#8Qz3FGx1vb@mQjRGp{pHu?ZXO?aI&=kB*jA^n6Lz>%vy}H_(6H zjQ{di4Dv^_f&AQzaX&8D|5?ua537};v7_UULs{JB$8aU!Y+~|%(>bbCb`%j+k-k{g z;^0*H;gGetllp0_gN@(di7XNi-U8g@NPm zpiHz$WQ`c1q1wrFbt4`-ltIw$Y1f_VOW_lznBSR&ZAGmX4KZK_p^H#YM;tkTsRm63 z%>`A;H3r3Ci@@A~#G}^ZO=I1)tAX$y2ee`gHsKv3ze-8=<=}JX#$2#-Gg}UtVw0k> zUcHvelchB$H#!KL{$XP@JcK1LWx7n4AD~!u2cbTy{GFSnsNQ993=L9s@l@X?#bvCi z;uV0g2vuzIp7^aw1L)qQ z{u9CD@ttL3qUCfo7V}`VOy3ZqP1GQv!0&deF+R!;B?q?eO>qS6ogs0R2sCB{ikw8= zJb35_*u%{@z-(B{odo52wa>;Uj4#+PQ(LHK=_%Tl@Hg8kCgrfi&^t$XWO5LZ%H5Zz zW~Rd&qUS41Ix>R%uSnl@v^qmg;~o@dRCd05RME0(NDgRto0TU-cQwdHEKHUz;;rZ}|qYL!0hdf+SHK^m5l)Q3Gj0?Ykyklrr z#WVXcx{#vP zA{b|z^THM7NTuD=u4n}?9tdo}-Erx*tD`5$_3t>uIU4{?MvMgZWxzH+1e)(p4nW6YlW`fF8F+J=^0kYM zMkK~ail7AHv&WkvWnIln6kJ48{|FiW7^n?>dwpT|R&Q>3{mQBMqFre4;I2HP^j3hB zzM>+8fl=Y!!d-YSkJ^EdwGbbjkdo1afJeek*+Tb#_qoDT40eru=J8zt3kX~Rvt_S= zd3uCi$|J9du0E3T%qkVt1&$;JYa7{v@jSJJ%&5=Y;RSSp;xPH)3&hDkp5-Cl%TH-< znW+gc2LxxFgPAbnZ7?M)ayBR~M}Fn>40lQVz{g5~Gd#sd%iVl( zI4QAabmZidl?CG32b5}azd-^>tyAyQht-l!ya5##`(qBjByH(0E%32rF({1kJWpZc z(%#$8bwN2G-GjOABDqG=rY;f<%dK-s9sj;c#tM#&m%{GajeJZf@M+eX6sV{pa1X0k~1C^K-A5-~j-z{*Pk(f9R_JIV<{i zFx0x~-_T}(Xz zAP6FMsUc91;0lF@<^jz$BC;YPBEWFmB0=4L0RmE_?!1`Pr(fE@Q23_JkTTjHe@@@L zYd64l`J8M4poFdCW!Te&Y1)&84YXQAW!QNFxQ>SE{Iw(U<8kLL-&2F*$4Y{A9iOmw zZ{QA(M@y**y#qdEE!uG))w7uQH zv$*4Idk6~I0j-1coxL5wLphMe$bm8_du#xg1-Ug9e@q4V(g^!;9{u8?{`8gN;X>?f z2z~|4Nkp0fy$$ekD~9_XjJ&^3=+PbNltMjzPrJT_ z2q`(zUonL(LSa%9Aw1RE6=7S-W-(OO=J(3v3MN%rKFy-?Cu}2=3r{7^3Jnct&WJbc zF^Db(-!#)x;NV>uii5B$ud1qON{YlhzJOohO5SM9h>ELL(qZbH{-9C%PdA0+`fM?| zl5r`VB`KL(wdJ$WBu+Tc9A$!os=Cl*qx3#~&v_J;Ik!mvsSHIP=AuIQ6RCFW=X7`P zrMw-)VY(;qI2LZcN^2XlbqrUE*rbHQ@u65hlhwjg5BtwKRV{Y8q(cNWHGbdW!bofw z7=$pLNM)a^Rv9~WXCTcuIvd{5K6((PF7k>4ts>tz=wGw#g46+FV6RHrvUOZKT_a%= zv1Ll+j)F<_NL1)o9tgi}&t`;sFREfDXii?ulxC8UJ^JD}XX4NvF#2`oTF*wW7TLQg zk4`xI@_nXtP-U(hk1H(!{6gt-nE z%wWw1ec@nEYN}X{eU=d~HmQ%0%?>3#8I?#xJ05u)W&{oKIO5;?xCcFVi_u{^ALVMr zG$WlZ?%5lRkA|FluOrC@3%C$cggmS6p!)bC@9(a>wpGm0!79 zs3Z$BPtO|~V}(2)?N#A@qDO81*~dbe9F}HTP`{u8H79yOOlYo}$qfZ6$Q*YcUDk?y z#lk9@DBpaBeAjA>Y15E?I>*#2XsxJSeEeFCWtB%275(Vq)@5vaZm@K2#^{m;&C#si z5whak*{6sgfa*@5JHkl3Tn}bHfqC!ApRVm zET>t_?KfTxlfnF2SMsML1~YWHRAUn&C)rEDMK6TYvSby=94ZJ>nl{1V}^!LjnrT z-w8LCRcdBg5(xs2PhggTLoR3{lo#jsON!q^uq^zw*_PW0+Y6QSfB zH2t8!D6uM;EuIp;6hoU9r3q}|9VEJps`RYU^6#se66Qwv{w@tMp)&GLBy4N!fQH3~QPUHxdKqM1oRzaKEr_q)>$y3wf%M;e_O3$nV?qzYw$cVMnv!64tCj~6yFydbbvwVuPpRJ(VyDY?P zQXCukXV+R$8Vo6Yk`!5A#~*R4I==vWiJP1mf$o=8Mxc)sn%=7dpA$!JZSL{@od=4_ z4Ond1&M&7TUn2BnrI}u{5HW%rze)ItO&~aDKxDC`>N|0?AI9rr$4fu zKP95Vh;OXIN=61S@ShY3pCCt@fQ5cx2v^21HQatke zMYE=IL42E;zCp8CCAHG1nImORs+avZ!C)_sD9nPno^UI~U1T#G(S??n_GMy0HfDWojHFavkh)ptk@K9czOK_;y8WGr2Ni=CzXMRT!B2_^}SP4Fr^99+xs&(RZAH+v`s z@QrgGPng^(*nJE^4{EoZd7u2NXI`)bkOe)76INf9H`!&dk6JiK<_h`kLpwANhNhGw z>B43@l8)rw%~8xXSg60tHVDC~9rDUO&CC7Z&-0|m!_bZGL2KHcu5RE6sv`>|k5evb z%*rDTa(*)Vbi9x2IR)z2Oy-!(o@aSA839GMF z5C>`aUd#_TKrA1MJa$#wylzV=wPY|y)Iyv$)KnFt{7l%sv>8h+XlyVygi>0#BTORqkMroFa^e>z zDbxG=U?#W&SzKa-nie=gBz2sA2`FRo95u&i$0rAL%*cAk%L*k2{M=W+yL($!5XLUT z_Mg-uFlYTR?J>Hg)-%Psa7zvV5FaVcXE4&{VRA|yoLmiK@2wIqs#Y4h%x9{dxHZK~ z%0-;Y*AJA|V`u$vdjY9r-uHgg1B1B>c$A5+t>;rZCs0#zh)C?E3f7w@*PcC$1B#!w(oMfu1I@4S&fsb z*O;Rb3gf@8``tDp(OY3l_W6w$3`9jaBrufOku02lZI|3yDBUHE98*##!ExXiznsj?}qXMwb>AI&&OZV_b~+ zJ-bVB@0v^agq*!n>3hZu-McI8f}}K{$_J;e2I)Na>gG0A@@OxgZ`G5i)-N7bBnJ($ zj@tO3ak@l;6vmr7`hmAq8ka^{=@BqoVSuEi>uSo9d|4MFE3Ts zbah6me9hgwUZ6Fk%LYxiWE2jpm}bvXE*2zC5ZFz?j`Gz~dWzVghF84GY+87Bsc zOf?o_Tv<@S-Hk3GAFh2?o^;qCkBlUNSSTSekc>FtAl!&NKI@eizl|5W_I=H|J1zLI z>5_Z;k3GhYc#J@M{=y2g`zRTB{$^r9uz6qb?vRCtI^u$mu=F5@6jOlPWjN77tt}z6d$8qSL*9&-6(8^AmWwNHJww{Q*srwz z{IYlkkrMv?aklaPaQis^$1lr2<*znAW-fA&#cY3EY6IU*x7*ng#G-*{ki_UXjgjjXvq60`t+Ed$;=0qvY^N_ zneBNq$u+Z?zIMB^)ARjnm#Lf44){R(bRZ~fjAnB%$zQXTdV}-#&?5`hP?Ucx9p!$t z)lE1H>ALsavGBXvAP_bzG*>?<0Tob3{6aiyE$jgS3WxrLMqFk-8&!mz6W8*ckf^xe zVg(2W0%DG?lqND|k68(7;GYDS&c{aWDvlgZWk`x9JVi<*tMAT`RmPDPW~_}<`Rb&3 z_ZjB}SMwhaLXX*B<;=YfQXEa$DyK1r$g{Iv*G+<&15~UPa2gA@TH{CGflV+*I>}+f zZ9w#!EIX}tld7zSPjpJv`rwq*7Kw(Gn^7LFD3C}Q<$<;pJ!!Wgg1{Ev+`DgT%)RZq zNms!7dBlfwD;1f>tdLPJ5eii@yUZe$dTr?8r4#oK1h}H|f=MBS@$SGJ3+g0`C1DIw zqm`ol@kleY)zMZIwxUP03D)!XWrb1Iya)4q{E<3}0I&JsSVlsb7 zvFCQuZl$NMi$SsF;fQSHEVk)v!HqG!LeR3(T!I=hA;Op_)Er2#=7^@0!sJ%q5L_?lQ_Bdo6~`!N}#dQt;x-wX&BY|=+K z8}2@EZ!IhY7|&$7+a@I$S5xQx zSthTEd#(jpcdLo&@YZH2Y$OBHmXdeOpx-GmV8~b*3mF-K9{-)TpLV1-o3v1w`cgA| zK2Rw#=z!u~v`J#Y3N|KwnC`X2l0RxRP65{(6@y^5N6MvqZN`O)x}4|{t7sEIGZ~g>LajH6Sg#W2Y9DzhSUe}vz|mo zJAZ``s67l5g+LKVB%2tmC#DZ4mk~FcM8ZTMfk+@PY)DpUALt`rojtbrry0r{c}k8) zHSZupfNiNZ41xuGcKsdm6N;VBmX2`dQq z7T|cSIv7WPc4rj+AIjCTyrw^}YaFpkMh*JtB089{J41Bau~zKL$#mh`mI-J+;aiSU z`I-!C4N+Z*v~{*N=-h$Mmp43o@*{nj20TLWH3zfpL9qBT>4g4Vh&pr#b&7iv~1 zpG5EHK;vMxF7*$xaLpDP8x-&675POH&+PdWf7(O=pLrc<4(&+}R`IXfU z6h}7$vv2Jl(IGxSum8n6{I6ldxTN|%&W|(m{Kstf-?CBkot?~RWt60(g>8OTyN$(v zNU20h?smou(m#_2eN*Fqi$d8-Gq#w*NMFO~-=i)I9OmnVNaG11`&K@!(BQ$%NOoeq zi{RD=h0^nYh@Muo>Ngq}&WT{)G981&v)kH`l9N$ zm*55}HCRdjZpw~7+3LZ){^X&u?vI+Ej&y1?|4H#i8>wQmVRS;J=x;7kOo57Tf)boW z)lq+g@@|tI0*Y@AR+OQA%Q;G_VKA#&t5kGA&nMPQx`3jRthG!r{JVlKQb4Vph!&H7TKN0DySeNiQp1U4G{cg9JsAUErb)PE5BV^w2#x)(lRYBEpP1CRl{>8IbG82Z7|zy;17S3% z1JN~OQ%oqJP{>Ipstz*lWP@OR1%IeqFnW?`7FZGC#b`dj5MB6gd1TCaJFNnm?DkHB zQ?(%ttQV)v5z+E=rMeI&u2@tjuPAfuTs}G?SW^;cYIYRthDr5q+X>>{#dv#*Rex+x3nz|44Wp&5)8|4m6Ai$aqGblq>7uNP z(cQ921R(JM5`%n|M49+$*2@g;0}EV&@TwO*ii_8JDsF_V8yB<^B7F?$1d3JEvj_h0M$ub(S01oUb6OT2qys$8y$+`zvWoRz-xXD3`j za^z)jwIg~7nm|6=p3`hVpXA@fqr!H%dTnOY5>H4Snc6{3NDzIAUEt}bcHqTfSmul~ z#=b~iY4Q&v?q^)-I%8MIdG;wi#U7nm^UA_`?V>749=RVstn0P0STot;FMY$n8OvDX zvv55hxkDzsAFHFDp04(YsQ;pOK~k)`A(jlk*l_^B0QW}1&e@VE#`=RhMw$2k34-&m zobku%rHyw>qr74ne89v!bf6tppzXi>{IHD zuy*?2yaHX`N`LgW+!B66(JF3|=Nm(ExB98hg7CK~2pIr#&aR`#hrmbu@@JM&Vw3`A zN>nP7Js?m&0gA+N7Vg}CRAta88c9m@g)dytgyi@|tIq)J^+ESKA$MDmmktUO*aHJ1 ztIq+9^?3yMx&dbgTmy1t9Hp=$x{PPjFBA~ujuth~e^j@D)4l@%fx=t#X441}03_6(fF=VwDrqZeEwAzPLaRRv74hY*T_9)t9+jpr-jr=?_7rJ@#k_c z&$DZfVHUX$uqS`oBpDrdrs3#JmjO0IWtMC@qkzI+$`WN?ySo7k|zASdJpFy$@CGlmBrJe>@D()53$!yaC^R3Vr-X-^(ZwRM<))SnMnpxWE>JGVL&PJM1Wb1^;rEvst8c5T_N%-$x^Tm!^MY9MWxKMegXu? zu-`64PKS23Qy5XbRfe`bcY}mIXDd!2!kN2mj&Z%E!IU<~RT9**Gd$!zWc3SqaXP&1 z?#-#fEWF0>sXjn`Epj*lg6^5Wv-}X*-RqrWGE7RL za8ajW!Kn5}^#8K2C(C`Xv3SEcsGg~O8gp1`tG4=38Mtxs3QNTBsX2fh-{C#~7d`QS z_El_Tq_yw>@k4oJ=hoFftjeMSu!rn+7bcYY1x8l_OS*X*bbIM^+|5eJ=$kwJdJ8m`Rx93}*$T#nH3Q^#3lIPy>jSl5 z;1m;cW(ONjW;PK(b2dg_f59&|NDLb+DF`H)xu3_|w%9!85D+JhO_tcTSe8{3rDdp~ zuW6#)?lxjKWhn=g!Addu=-(loc zCkb5y8RPbPsT*y!nuFqWQah^hzB3Z#Ef(juTeEs(ovI{0s1OJUn-M@H;M&mlzgMd> zGixkP<>6oiBU{)-4y)822Imf}W)LB?YRFwFtyKbNVU}bKzDViNN}Sn% z>8xptufLE5NQcTSz2nDmiW&6f5MX=U2)x*eP;N;v`T7W*0HNoczkHNDM^w!LQ!W$b z?^ek;%fBctu|c+mGe-kw$d}r}Ct`)1S=TX@`nq6*bJwTYuyq$K&$`!_|A(`8imok; zwl#NbCws@XZQHhOJ3F>*+qP|I$Ht0nt8&h*R`;A+t!}ldhxt5T{x!$H`slrXn#2CS zOhY%ASNjkS&cf%*SFo+2Ax1j&HBYe&tJN;UvQa~5Qf!0lgy`f{~&qRir|TfW3-tFlf5)zc?zsSh}>!~`Q) z2W&I-B8L>_NT}cD0b>~lm`Zyr^YzkRIZl;z(m}Fq!#bq0WN9ssKS|jgQ^}`WyiI0P z@l%p7vvLkeeSfl#83&-JpwvDY5d=G$w@Dz8=M0+|X59*(+hgyt=b~PI;Z5a=dEJ?1 z`yrA3kR$lk9=nBbH}k`8(x7MNfC~cMQSH$qhw)a110roHm+jtw?dlqzxr2%$Dodja z%kHS_H!nFLz5mi}_n+X_RY?rdpG!6#;;&y+|4%Z;e@?vr1h@Xzc~`9V>4vR>;X~Tl z*tnJ*;+jtZqbyWx(kRgZYNeH+nHiJ66U|W2!z0ip$lI$gvvc&O(h{kjP3*5`#rm5-%hUw?#fgrF3V8n)S;aU`?8b zl$Y7(#!VcBk)Rfz7$(L|Jtiw!PgQ(jq?K^fW4KvDbu|=C=uH_)yt{cM?uj2#a(tHo zkt?mknk9Bno#3r7H$kiA6q;T zjpBdY=7hSfc{|Z^^GOa5*UD65 zMjdp6c-$qJrr5ZcUQ3CPkZwpdOvWbY>8$yYZKQ7&>egK4r7Ay~NP12$VPd|J7ah!*UIR>n7DHRri!J4w}y+4mDCHB zY!kYxtrF2}r*Npya4JCLL}W537nk2={*MHW`1YbyKbeV9JIZLt)%K3(ZpML*OywI; z<&Rxo(vTFh;h)#y+?KM5z6-Kqg)DatR=iT1clYn>O$7CVx4xmdG04s~iZhUHY!le! z3-ns)!G3L>M(kYPQHRZzDr2{j(JeqoZ;;PAS?7Dl^CR%jMxIGvMGC*PYJf z+#Qv9dISy8ljqR=$H_J2aOdl^JR&=AcA*Ux_`eteuY{y0d7Sqf3jWKd{a?{iWDrP7 zvLlwVjPnu&+v63VtP{Ya7QR_ecvX$r;nrsM2yf=?dfD)|bo~pZ?UHt(Ruq?|DI+ZD z-!xGs#A@C}N!=3Y?By4k`P$PxA+z+obE2^mzrsv3eNoEEP)~or#P#CI_c@A+kGwVW zGMc2B6Qe76=%%lbjns@8iaIH$m7 zR-OnOV#TW(iq@fJE8U6(>u~1lQ8SEyMeJf8jK5kvm7ZXy?}3KtlJNiuh1dtme2AN_%`)a3$AEvSywaG$&=7641Usj+*sFt5!fu< z5!fu$2c8zz+)H@0$5-?BK!};x^aQNj6R-zFn@NoCx1`z*DQ$HIY`lMr@Uk|S>La6X z$gd<`A12o0q>MOHZJ*GJo0ou?NwUR+B_oZ`Pm<00fGwr9A;zbwfw@uC{?Mg9LbuvF zEL{=!+WIYBGflf)gRb!nUl`o&5fpa_Ie?n`@7@t9cQpYv>=G=2R&;?@^+4GxS^WUS z>%N7m!^EHy1Jx^vmIm34_Ztprq$!m!t`k&gDcs7&E!B z65t~(*u0loCwog2h2c&u;z?~lZ9u5Xyw{T+AcZv{wP_wQK7N0#%Aa zrF>nn=}BmAS+ZKcc=eiPBew4Wpr*>DP~&(4!By+s1eh9V@XvO;60cg9Bf`t_ICCKCmNm z%^Z6X5MJ#02W3`&zO+*amTgFS`VS&-NZdv7YpH46QwK6DR__#`G) z=&|IrA`1DnjFzbu#T{0r9RushI8$q@mW?rM<~xfNokMh9(f2xIFpB-^H7`orgQzQA zrfn_iBfx2oHmPk=ME7k=%(9D&YD56r)|$;x#6hdFen+AVC)RZnbg?#VCSArjX(d-? z(NGB1grQvqWr>@7^KosVcQmR^S*v^Z|n<9l!LV_7sB4Y#By=?*d*(#%VK zl|km#b}Uat(tmvu&NVAjwWeMA52X~I3Jsd=*2?ktB!k%^z{MOrwj<)BFmNn7mDxcH z(eSRCi~X|k)))H;>|vFLL|gSbI}1v?nkTYp4JYmh5tyCHU@@%ZL80>)%3z-JDe?I- zKNiUb+u4-6t)mKNHb7gY6hr82sS#LDfe`^Rub0Z`Ps(_72%P|L>V88!*^?Js*)#TFADl?`VeqGH&I}BcvYr+eOsP}p z1g@U-t*+&X-&T_Zn3gVCBN#YzhK(_e(yF^zG578gqrY^QZ@o$R`lvWr4I29f&NE~& zgB0HD)m>q9Nj%P85-PE5$Q{`_I>$ZEy1<-{=S5fs(6|Bg$y>i0Hr~#=+FO}>R#Y>V74P2H9yF<-Y@Mj8zcfjjcfTbLE=+lcq9D#a@g4!?ZK zq8=1NHzkLiNo~JB$XWE1HqMq)o{sM53|A?ApA1`=QVV_K`;XBtQubJhTq9TT$s)gq z8^xCNu7vMsDV;o+%nqjW5%SJ7HQh<{i4(Ig zyP1zSby_B7E0(Kt1OsfLRB&2JEKhZs{Ia`4uCD1!JARKZF_0PC=2RsKoTask?a=sBdyYFTvLsOUMA1eSLi zfCB5b&)wz$N%L{d9HjaU>9<>cwlk14B8fBNJR%A>rvq0&TR@<#Dqb^0_>~;sp^{`s z=zstR;osh>1Soh45*$1=nWiU_fGNl#R1R8@CALU(wYYeI5;$_is_+?^DG1axgF6qD zT^q+944d4QSfe-y{pH4Kh?6wgRhbnWJNOFD%_xypy}$vUoMb)N6bJoCa;EDplMn1>PnKK2GIdnaLrv zXu^`Gb;^(=Xd}2R@Xr;uZE`C6ei>n*i^SX|wtf4S&@p9L4$3lvjTP`@i?CS=jr4hd zw!9+%3kx2f8q|j*&A_!X$hPvNNaH&3awUyn;W}kY1+4+4&_lURK~cx>7A3_Kl@hi* z4*)5-W%LY@(gK}(70_~L;D+A?Fz||Ue)#x3Y=LXyZ=57lW7Y|3!TF*{9ks7j8Ec^L5j#D7ydX=6aps*;#YC`ns8I-2qA)^^a1Z9#bDusizLU zjfk>RkJ+VGB~%}-6qJE+p$S?MY{MPsfCYmwunzkSfyKZe*h)egE|JdBnm^u~O^a(V zY$v&L=Lyn=+<=X*PtHxu{pvDeJ@d4e6}){ILKDs;55p$d=Izr0@zQ>rGBD2zpoe6B;Yl(zc>^{qK z+&cYNrxsCr{3qONv-t2=r%q>rLsw)$r(2t)x#GGr!{ua|xqDBo*^8kyyPiszQmSQq zh8@+X&zx+y5(w(`z!n;-I_)b)!%ZTeORjQZ3tbd}TC4U@x)Y!lu9aelniZnPYNuHs zDx~f-oZ}$K<1dmsN36o!vTj8laDK?>Pj>w0=%`Ghb4vCZmpz0ATK&TU zA%xyV(UVcE0}KshGl0dbvIGo7>@}3}by~Zn&V^AMrxc*ML(xtTeAj# z0@?s~mXPz(tmW^=SkGQ=v+5SJM)Lh0O`*zjjB;xUIx9W(D6RH9H5#~7YuX4PuEG+f zI1UTbhNDI@pinM9rI)4KCGT$b-Zf+u2N)6p!<2o}&V2z(@FjHTK6Q>0)vZCCOwMxq zHBBX!i^(fOZ3};U+dsj9Clr1lg^0ecK(ZiRv4?Mt4u1#EHK9B-2h3P}iGkbxyDCKc6`(!MM~Xz#SrKx%dhhDu-hi z%ZfEf)9d5l^>^S6r{E1HR$~}7s~b`hcY?$4q0#4>pQxA?8z4P8NtNiOsqhQ?W-PIg zi*ru-%BCE6$o~^B_M-RS-C1`Zp*>kYaJ8i$O_Tb6D{Bf{n3*^^|2JKe6G~lsY3X>% z({9_yy^nqz5f_vxoHPMQP&GYsxk0Db^Zoo~ATx$~J*%6PkV<9lS)j-|*}zs6ZoNSS_7lcO z?y94F|6-lQ!05IVvvbFKs7w{@+CA``7W35#gUk>tmd&9}Hmf$0KO6N#f4Cf|W?SlI zY4s0ib8LWXc~!AT_2ideOP_Hw#|jTFf6&A+2{0tZ;mlfJ;hT;V~P_t~?(Z zBOxa%Zd4@(V4VY)*Evz4q8|O8Ax}SgnR0ncrkX5q2My7 z%%7>;%j=ul+nX)2gm_?}*#eK*I6rWgRu_8MaChhtD=b0TF}%oDkpUpUk>%Ru&Ur2T z>tOfN<*hmWrk0fRV7$P(O&){n#R}0Xh{1W+bZ<~-F{maQEX2MGq{-p$XnT4^oRfPo zBh#*q{n!}G9+l4OxP#&4ceWpS(9WJi$0O4)>an9mSXKm>jf zq2@SIEJZkRF9|S&;VCOtq)VGe*QMF%hcJ!pQUpe!gQk8^HBX~2BQs;VOqg&_X_+4i zzP?$99GzZ_cgA5iX?6Og7bbdSxu$T9{vZRuI!YRLQ*f4Kdf6c~MBT{$>)GsNFre40@S(B&9K zSd9Es9EH6rO#z4o62B`+yNOM~!3+Ce{LdW7g;B;Mu3(#**yeM($yT-S&YA}@RyD+B zByv@s9b0y*h=++h$x*2(18?=o$GeCcMqwf7&{mq$-;ri=NfVtzi~>PG;>p3XhC|uK z#1DGDq~n`zhXh;@YRYNSm|X8+Oyr@x z&G+#F%pzTsuCfmFDj@<1CUc!!MG8K@+VC%D;*4ydr(_Y~?G+zJ-(F4|NTq1_P~fev zaW|VIp`_uV1R`^z-^>?%^_$s8OOPeWwm6v7TR?}wB1U>GAix!`*VFfxnk*d`7m@yM zfksq*@wu7+b0jrjC4eiN>-G}A>QHJ^qyB#|fI;hYW>#{@J9*`Behv z8{_Pm$HIz&9IfM5R?^?c>kTbZtR~i)JGtAXxGi#Vw3oD2ZV=wa@FI7WZuL%9E$!jH zbiwEP$=zK5pmTo=kx)(7Exz6 z`9e(@7cZFFN5aVoKBoA~;hPZ-FpYHgAoJK?tcBn}EN8SV_SajWv>X8^uUEYD zMA@0YWqZ;33r0C^v%P%l_Tq{2?dMCZmz@yUmb7N1Vq2BKcWRHa$vq=erM^GSr<9WM z2KVol&OHJz_{b&t5+?_z-1e1wc}h=WRg$VXTWl?}1PI7CRPcF#8rjs2xSTKjZ8xPE zPRc4JE0MFE-73KLF|-Yv!E?S^Gh_i(Mi}Xnn@3C1+f#^%<%N@HzCevpgr!mctl{rR z;$@MsV623b9Xm1$R@rNTBstcVYh|`nBmQ!5xvOlOOr@{i*kLq?Jprq@PZMKe0oq1$ zA^{9pAbF$d9aNqoY%5%d;Pp6uB=9_J!*)u>*t`WN1?8&5pK-8nT7lCBFUOrE zhdfr8euo!r_h!;_W3Fkg_99m&mXyPla1ss97Y8`Y-$JRfN*qcC0$CZubixJB&j_$- z+G#4?bZnSZWrugB%R0C!uiPwXW$A}q=L-Q1_A`Wr`K%|)hBFi^ zs!6xZ5EUZ!P3X&La`=|j-$quI0_p97Nfe56wFGS>)B4pO@g(p52)T*u>`O?rbi)-G zqFW`EOp!ky`wOdUDPd)q!*qmLZVEkgdb#B^+eb4)XQ7CyCiU+z6hy9yvqkpB1F|yP z>w@V~l?iKya0?zYT~!X-V>l4m=S5(r{6tGty!dZ3chApOh^;uTwrcJJ49jU=i?d_M zwfmTd3FU4S8{kG-^TELFlCMqP6dQtDVpyn%N&hAm_<*gkqm+DzHDr8y*$=b7R2Kt< zpP&No4SHlLeGVNRgk=9c*7MV+Eh;Lsyc`Bv(vw05CnXkfi4vj}cBp(riOohVPBQG5 z)Xk)DrAlkzsmryNAuh)}u4y`Hd5U>!2@wr6oOUi)zYIBt92TKe#NHevr8R%%I9qxl zba^+ZN&g$B^U8%?QR~bR#;WGatnbNQhG1F_J;77CGn94ddf86a#*^^5{=I_{ zB(y~zt(UjN8L**_Yd1`vr}h`yIyP+Zc|Hx{10rZEuMgLrAF@ZhlJJ}zw%_{$0y4uJ z>Wa)z`T>Y@R`@(Y_)=OKv+x*k?o;@hADqTBBLxRHC^T$7b@akTL$tByZp_pvN{_YF_N>>2#>bYNOjR#mYBpsLNk`n|-($FK{&6IN*rU zTf)O0TQq^tRw^O)(4`z|PE(r$4ay!HROIj6JNhcFbfY5@V_d!pV_hQb1z}-KeP*(8 z>HIc|G57Kvd6qb1IkMHS@nlbCnK@ei+}XCCq@J%ti&x6TGS<_6s5Asq{q^}Yx&8%- zsc7NY=EP$eKV1b5U=L*LOmqK2;awzwF(Vw=N*aV7!mIv9GxL!YuirhAn)(2mB1hib z>*_M>;Ex&ua&bSWpYYIFURG{N7J^=2ZRDpHJJc+a=mhgs)zS;zlUab{r`W43*7MNRVr8&AO&!r2RnI9|=AxoR2I6|@bdiFGkm9XEW;x5y? z{t7nXZX(<(M;gV<)U7IMU*hJ^n?u@-R&*pSLEXcrz8Q7SJP+V^N6psnD?1A*;?fpRZ`AEyv992GPYwy9!}+kB22NE1T@^B_jqI zCPvTAJ?DYHk7>Eob9iUNl`27Br)$=L!jJk2jz>^bBE+h zGl6v6duP-gf?)R(W!|HABY&Or!O>OPt{;U9ZG2-41} z#)z9UBo3qFE71MHpXym4dw@0PlrEhxp${{fq14?Fv$ktS^Tzjlu`@#SLskUz=y>6$ zb%i{zml>A?Wev`~gA42OC&{_NX$I%coG@tBGCzo|FA~C(7aUd>bNr?^VAX(I(1V9I z*aCN_XZ-`a3bRj<{=wsN!qIrd<|&Yq=vOM3%TuRvc|)z5j-93_nr=jS2~5q!4uV0Q zaKdkRGcBkyEtn-QMtLyisO2Gz5~x3HYtNTPdlAkt#AI&dWBYq-7yX4}3Dz(vPrG6Q z<#XaXAi5!b+e{%kQ1b!vBB~2izGU4b%l~H|+XoS^3s{E@O_PIX;MwAhm!>sliWv~m z5&fdqzMwLU=-6Y85Y;b*iz)zp3C){hlfs5aZw$#lKD}_Rra}pjP@CLkGUH?jlI8qC zdAM@H3p_fT(r*s94 z7vjhQBH&C2%c?91iD$sY12zv1rX^kg4s01=Eap>bkn59YX^>USO$>3L#Hb{DXdygs zsg{zPBq0pI?`_Oy46ttFkQc50?m2g%SWHHDg+%KW&S<%kKuLBRY*vzX=T0|7_wHy5-O+2ZgDz>OD+v<#&kI%|m16Ep1&y|TaK5LgYVW$jeA#EQVSKH-pP183XP zY#3@oX4`x@0Asykn0g7mY0!zmXWofM*M!p_X@gL>!y2*GQcADy2`96mh2E4sz|-Q! zKnxT@0!A0l93>8q9T!1QSv}IW7#5g-_blbmrOoN5CawlZp2u-dcBa#%;qk+Zm)#R9 z_T7DK2w@vzx)p6lwM#hOCp(b*Na}*Mi$A|%x-;!kx$56I@P5&F{oAF(8x?xcc?6_^^x{7q4o#Xgzqa&B+p+Hg z{>`66IiGrZI1`vXCs{oHEI?K{6QWkeP0^>MocR^!Y9TaT=*3P$^%kb!&8SG1NJ7VzDwAQ#IXR?x z1Ow-#StjfVAeJeu_G|!0eyqjtK4P8iHyIyw%$XvtTXIN`2;`(zEeg_~Ft8Eil`##(Uu-G_%iF%@c2!{@5pBw)^4ZkS z?fB+7^0NDd7-+o-inKHh1a8v8xHAz^8u46Ygt4Xq%P({S5(kOSXk6Yap1GPT6%X0+ zn#COr^Pc+ct=8#{yAhb_J;67}REttpNfV64*XY`(swr-F%G={G9v;Um$R@RR(E}bd8gde1f$W^$#M8s7@6cH9;q@{7?T{I;J zj}{5+shSF|QnXGhh1rT2Zd&K$*$#R+Adki!nc;fy-*DE3X6(>v3q~LDaI%*{%Yb6e zrwr)7fDusCY=W;e)H{^1LA7yDpNyJ3mOEw$6UnOr>@pOidB*QGrg36fL;A$z@jt!A z11>aw&KLiZ?*1ebt2mNF4r^jH?n^+muix1Dx`@XI7$}6{Vter^w*5?AOhxTF7)|Ka zr9Iy0hBOMbv(Ns~p?TBrw=Q@Ho+e~Pa&;OXC(O_ew>LFTUTH6{6?3M#xmwJiW$Vax zHin2UdB8_!IFUqi%s5#%vwt!laduiPW&>f~XYnu}amir9WOZ`l1ykW6ZF4T;v;@Ls zQ}g|v&m_oW2ig7pFCG5>VP54#(ljIgAxw<_Y>oaO>7V~;Ui}Y*i@;A@><2JmVPxQJ z_kXb^k`@04TOzZ_!4MV>Op*c&n#DqJJ;VMK269|-le8jJwe3_Ymo)~h)3G~#AN4ED zj9(Bk-{%+I2a@43KcPc>gmEs1ncHze`s>~00)`(aZe^SQFro4$HL_O1T}r@);%0mu z^bDzlGO1beaf~-N z^}?~xWW8l+ttA6C`Tm`P@CpJ6Vc~T_5LL^x^t5RTC-N%Gw$OkBzMiJh#!YWNefd(F zDLKVLif(SXVLkvm!I*p(*>pTb+*>3CeeM&|^-04!c8crOgbzP;!!vMGmhzt1tVEF@ zJ$C|F!h|xC7^d7S5(Cu%ZDpKRAIzkolKH08n9ZO-VtIfUy8%PHgz?UT0saUww@9}S zE#lYR`!FlnR7PDO)Fc4Y0Q^1)ZLApSYN1xqSfxU7kL$m$7X)V=ok@QVUdm5I@qd5t z{_A?-zfpsd6Ai3T1Q0`ZWahR1@@!P7SXnKp+a!kJ2IM7>)6n$u&lwi)j!wHsv>dn& zZjqk#D#`;xfBo`JV)t|#niq}IGc|MF@i28gzB;b8aq3`?<|2D2>T@OaG-KuQe1`WAfl{E>W@6t@?GYbs$zv?v7Gl4E>oVgv-HmqU<=55sCT3Sevs@k9atOL{Uovs>DZ{3-%|b>?W0 zLS;GS7OT|43S7GK6}e&(;h;{`5ze_lnQ)hkGHVn6x1=o7G|3Oppp;6Z!nkL!K(8Lg z#3K3*Jtl33o7(djxKmUaF>DijNSYh#p!62o+d_BBC5*uk?io=M*n&+UD?o%dgU!-mCwE?3Woqua(KGaaNT3RX*ZRq3=> zXZd<&25pMlAY1Rxuyiru-EU>QBGuf;+ZG}nR?qu&xbY75t`s)|c}q^|3YUJ>!YA+1 zUmY66g}$fleR6a6S;x5F#ARm=LPHjh0Dn^kme&4!j95is-F+-L+Tf>C2{t0bx2Xl_z4Es_iuZVMorZ} z_Qcac{CUGo$OhG7kuK+ZY3Y*br_yP%$fBG`whSoEz$V$6qIxaNrX!DO{B^5f9>1@c z=QEm#wCToA4J}BmFnG?Tsi^Q-;|`)|IJgr;+*mhISQUB3>y|Sw;cInqqUT*D&1lgS zRR9MQ8Cs7`{Np2kfu0?SY>d?)*#ogEmKPTJ=fOr=(Ji{0*_O(+E;Y>(T)k*arKM_C zr~`eES+mz{iHPJ~3OM7O#o~tDQU1C{rz|6PkkB)kGtoa-tGP=NCPFP_Z&GZf#mzyv z%xAl9*eo4Gur0@SG%Sa$v{?UZvDhmTH5Y@bb}nd%uyMvX&Cv~*Ho$c74;P_L5UjFR zZf&U->=6_Nmb4V#IUQYC&WxAVY)0KT?=!8N0#NiV0#GcU=P z=`B*5HMWBJ+^({6fD+QMP7Cj&xTX#J)neTO+!#ZZEN!*uNu3>y@B6m#H(W$g&(0VY zdZNR5y@Z~tm52f&`bwb&Mz%Uh;$O~uC*a()CUIfaNZD*jb7Gv8R;Am>xUavQ zIqw>nSb&OMK4DB2&+4?Ei8z+?G(cmVk$}D^tuEoIjR+yBGWws_ef!FEo9mXbK!s|l zS{0;GRj_ZBTh?s_^3EVc5=vHmA_6Bvmuz*&(1_=~PfFSmQWf_6^3^FJ><&Wh3VWhr z!f;fy#D5;TJ66Q|7o2KNp6?yW74ku1XvG7SVT>web;=5~Bjtu&X?YQeL2^haDhyB^ zl%)6cBGM|u%n2kP)Hj1|3D)|u{@R3;h8>iqVd)iVB&L-BV0r39$^y zBS<=bq`4Rl1ZS)CU;daUi;vEJJ9vQ$3H|KBj0NZ}siN@3#?K8?!HhJh4i%J?Lz_nZ zyt1y4Q~6=i=M3X2caZ2UgrHUN2V^p6_jOWe$%JJQlt{|L;%N6G{Af!9l@VaDmx3xm z^-?{QqA9Y!!-pw3k(6!)v_;Qiv)wD$qY1Q?Q3avJXe@@rQ1g(UQ{tQj%_w+63IqAk zN+V6Y+`)%ElkNhZ#{AQ9p*eWuB_8V;=gNm$z#B~c^pkhN8QVi^0Ky4zIa4kv?cSrn zPYo6rTr64?!QCo8Q>0+>GI=U;Y4e5tgf$YU>9E~N2AR#ct>*;iz9oI*gCip3r6`(; zPt+dxWpv}05XR3+DEc%r3I2rqLq)&vmEgavOg{z=nE+(G;s_kX- zaoPpR(_1eUT%h|fa;=T_V8#$hS{Lhv`(pb%ADc9OTjV>Z*q!&9c|(nbbpZr;2FpEL z4~aTIsijV~xtTmK5K#}#5 zfNJgFW<6=oNI`0KadFhd$d zBI}EUHjXvf%0`;&(n^b@(b+?_oG&@HsT&;!@Bdy-&V`DG|6UC&v*-<25{VeYyMYDk zPFmS3UO{OtWL_>MWYM4|weuY9lqq54^#oYiE5{yQdz9goSibB?W&&em|x0}g3Vx`qH@7ScpIQ1|67VUDHdg2?iM}YL)J2w ztkBm$%c@4p3P2NSY&Ir(WQ(v+z*$LXaEKji;j>Cyzh!J!f2g5ToQ-@mLZun1q5ZjD z9X7IG>o^kFr5G9?Na;JpO`5I^!+ZBf{~K+1yUY+Nl;O3Z)fFhvn{NYk?SJqu9oE+6F_T*A#@zCSEMCP_3U8TtF(ytTWWd_=L}+6 zN+ZY^u)M(V`gu{p-qg$EAwZ@=wtB;O8DL0>Tgoak_TXxIpxbFuq-cGagl{5HPdRM5 zd_8uCQB$NSl-!{>zh*059%^o0V`X7ssk7BoUR|C~(a=-=j{`!X3BcQYrng-v z9AOK*E;k8TD9qA986&E#+EMqaVc066U&n>WHYX*C6s&satH5Ju=R`#h)f}f%;UR9D z1js6^QyWy*y8Nousa?vMl%td5GHx-0f*G$<9*}3*ukW_)D-3`dCSNwV#IlzwtDzmC z`=eJD+1`5wckE{m%d8*)kLby3%%Qdzpr~h=lTitnlU}@@z_is^2^QPl$?F>4sBx0A z-!VSY|}Od-%lM2dR#C1`4rjtNz*Y zH$dTjeW$WytwT){*K^;YbyT62Ml*B^LeG7khzrm8o4V6&Xc10)Sur4mb*y6gJUpe1 zc?hYoh;B!15vCB+IddPD|634)Bix?=yQD9(40I~0wbZtg9B^>~CTXaqnYH4#;0oqDTyi5}!{-MnXirp@j>qLbcsbc{6P zZEr>}KWK-J`>65#-t=?K`Lo>inx(pj$(F6;PJ_G}{i`-q(3zop*KOl3_J{=uWblbQ zlpW3GAhP7vX;R0#1BVFPYu)VcHfzCCTx3GEobDSqKotC!OUVw0Ut`QF#Gw(6`?G#U zcShPVqXhCS25AjP!nZ}C+J-;`MU^&R3c{|nqWNk`JCTiMKPv~uu9GM_YgjT43NT0+ zQ5|s}XhLld9vm{U9#OTqWdh2ME&5Oee6Q87%4=-l`?zy2BtwhP!S;EVW1M__@p%Xb z4w3+z6gGu^;NVQ=eNU;0V#0R(B zlU!o};9X%n?jUT?5Vpv=n=+erzxN#?(*nE+gFXS&o1~cbo#ul;{{p#&Xt7b%3gs?D zdfEMeZc*VQQ|)Wr!2$s)n{>LPNVmUnQLlIN6BM}#l}yPXo1XfQ-JTG(OESHioV4Xl-#YIveJawxNB)5LR#pIJrV*QyI9^ zIbPPS6}i$vci=jEcmU4Q^p(0^I) zF@edjrwVVreiLi) zJ|D9m?#j%L+|SGB7;JcNbA@};r!ZSM9d*G)AmlNM@b@>KV<%lzPm<6i2CPn`Sc9E{ zXu+}DsWHAdAC@BNpblRg>_r@bj%r14P8>tgG{lf+v37d0mo5Xe94BjP(3^v%nv^Xl zKs~<=i?tL{&SDjXPna-iA@qlD)N|R1DIzOztI0~WfE|Dr>4)5*pDV?%`SIe`H9H*k*Kjmo&1t`7wx)&LD@bT{c(ZYyob_L@I+YUWm zRg&jG8K8rd718&L=eYbQ7oE2*jwH`}h(X*4opS5UAp^aGvWY`a%}!8!4y8rXJ|pB% z<+&^WSc3=F;tnVq(L}e)3^6oS-;5j^_FsLH&7RBuvhv8GYww{Ea#{a4Shak2?8TE` z>i8%&KXQN)u@ouhH;E^V74Z@;i0DVUD|VTO5vtsmw+%iV=^hCjaop$izN#3D=EgNj z+%q&BOXw)}C-bo0qiDakr2`qfYz1j;>0pr57?RIjQ?I^n^`}iw%GnF_)eCAHfv=sR z#O)3oX6qKbQRrM$?vzfS_mYC>=1VvJxprHE@l?bJ6q}3HOxAJI?r?DC%chC2!wrzh z_xk_ZLhwK}?-7@ynCdTLNlqF8Th}P;MxDN28fL1~V=fUklD*z0`hccgIMa+8K+^_$ zIK$i~f**GA9%>duFKM4=YQsfdsW_S#EkpX_A;2*L>&8qJM_c^EPCR~8b)sg@4pO8- zmJGg{zxE0-ZPcvX;Sl=E*i*P)$w1m35{~sEo}2lCF2$*kIMpeEnLE!ZWS0Byg~K-5 zdEGghd3xCL62Z8KYC7AX=Zh*zqT9Q=71ao(o6xqfYBsV=UPCclGP#FkQB1T+)MR{F z^2lzC*Xa1+2RlC47g;}Hj1o@Wu0F@himZ1|q^D%fC}TFYtZY~rOh-z@#3v&R&P1s6 zKvp%aP zN3^JIIW*HsDK$hkLoyx@$2Jpy?H_XG7ntcIMFb8qlc+~+A{>VbDN6b+&WY@rKPQ;q zl;6~puRj{3ubDz&gHar*(6OpBH7|0?p+=WL#K#+YZ8hRM%k$iG|NXq}6q@sL?XU3b zUL+mIXHz>s$sT5K1>M%9Eqd=9ww1{%a4!?q6XQz%<@aDV?2hRR{Gbl(j>!w|Aa6(~ zM$fE0!eBS75bPlAJBH{5=<}MxuV*G~V|PFALTjF}8<3i>xav@J+?IVLZcFfsl~!0W ze(-OOk^XL6BlLaU*v3bq5VE#6qyK}kcM7uX+wyQLvnp-d-f7#mZQEw0ZQHhO+qP}9 z>gNBRzHz$GL-&o?FFSUumlbobvF7}J;~ULB{kMwMJY)3P^#|r$hC?%#YLOWj*~YKz zFYh2P?&+bQ0=8kQ`Vr z5$r-+skD@Lr>392JQ*#_3H{FSHOEJifm&6tAZ?&dgM~=^MPxyRD)Sfu0*#fd=v$e7 z4QC1ha17?HqezRcSTl?FSNUZ)XGgdp|2iyxXpSPsu&2j>ps)~75)2kbfE5+OsLq_7 zzgzc8VL?i$7c(RaWvVbH6l}1HxF+ZmG$jy`RiUZrXx;2zR?D8Zaj#Y{o6Dt2(Wuf; z&_tFwHj55vQ-L4>vUdv8jNKS^An(#>{gCq|Yw=Aq81g`ce!-lYcvVAR(E{ z?^>%qNxUPLs7UE#?nr~OaeeQ~?^>`-qqylrG3C4~2!|m5ac3q$nukF(`Y&P1Y|@*D zUxB~@x!ZZ84w7TL$aB^yEzI!nluFX2a%Kgy#-c$FJ+6k>>Xta$&1i$~dkadT%qH); zNc3o0`D8H|B0n=0ED96e7kvR0?BV=IXhnROo?5o-e3i1gG@q;J`ZOFI4c??fv8KV8 z^}K45BtOSK==|JeZcHWFbm{Z_Rj(9AJ;^{oi7Dne&* zc*1)vLHVn2Yx^mh`<8H+?GJaS3xa1~Dhd8eZM8^iYfPB{8nW2{>PW{_HKwM*u+3K4 zAqo?6PTUYhL^w529RXEeIZ!%r*8h|$y?2u+F7c)Yyo0>x45_gADFLs<$rweb@_GXs zRzU<~%Zt1{h6%GM4HH^nf5yh#oI>e1rl!%MNabXAI;&2j24r*MIQeDD?Qj2G9;*G2 zqmV0~eCp8WQS03^v`PIQTNSGHFjLl7PD;)R8<`0{R@9r!KD?0Cx)7v8!|uz(#u!5^+820L=J450mHMvb2G6H%IxFv`5U8cycRiyo}933aGsvXqUs+V}7>31&`-w z`Ei11@|K49NetDB*;7-!&HjQ|GPlNBMN*075l0o5Amz;B_>+cvvWm^@ctg`Yb`_T@ zC9B1eyT-SzGe-rqiJ>q_Uwv8Nc1;W&Xpjb7PGXxo`wJeI{@2fdxV}L>lUh183@d07 zXqR9G(4f4?yv#iRzCO9LJJ`N6`?L_bBXeAVCev$p3oe=kcnfZt3HWm^nhW@IZki2v zEnL+x_+mG$X9A{8>2T;!#A18oGN%FJa(O}vIT{Ojl5;uQb9&`gIz_2I`h1|Nee!p9 zR+NY#rp7Rl#)z|1M8;Z$$w3QxsIGj7iy$zpAT)xx$kNn^!qgC;y#e8Tkd)B3RSJzd zO{!j#ed!|;kP{+;-|AO!>iC62?s~DPrvCC8nNXzW_AIGILvMOsP2tYUdprlcta)kYvC{Ss(0|>`*?Y4T^$Mb5M;7mK=K@LlO(&u zHKVkQ25D}to&SwW)^4?z9okBGKuzv0nWEia;=YFVzLGH{JTNN>EqueT!ge=tYB6Bn`fyC$DpCpS3~ z)i<%F7Y`PzX9}-Mh%}&+tyg*X6@fIxDJTe%Ap5E@sYjSRE{M3CrPFsCfr6rzom9+RnhLcK> zR14&_1V>Jq%n(-e5!S5tLCYLCD028yc3dv;E0C#Qk!h00H<*w7cLq3+6bZ>p&~@rB zO097Fw7MBei`)!sNZDIv2RAv!2DS1fjaEXB?Rn(Su=t^LB^MQ>8dE~A@$)&!2;L)U z;-rbvYY>_w2(em_k*M>@@nWq3U4^$}5gJ^kbz8#Yx*gA^vwW>|eC+sDvDGXSh=x zFie-Qz}}lME4{2>*f6)Zd&lZ2Iy+2Dd9K`vN}CX=YDTS7#QlZI9i}saeP`kWN4ae?Ao6Dh)d)<`_X=7aP9=k{Oe~Id!Lsl8PtgE}XWsTE;9=I`{Th zy4an8vM9NG5b6}R*dcAYnJw&+l-L;nzT=TW#*0+!{Glo~dyxLh14MzUEGf`-wy$hBSrQ#J`+af=j3{#u*eT2xa zIcB%8$RuTLkt_1(@ty&uz_6@nH=A?XdWoTV-gxh=l&PHxd%!q}xxBSfYbRp7_v=(c zcCE3$1CNY%wCckQTt!QEVXI7fjzfbm6wW-H$6uhJniYJK34NCZeUCAB1qXEx1A z^TAkU%1jf{vMF66@X*p;S!*lNQQ?6CeFEdVmp(X8?baE=HUy2>cy%+~B z2H&KcGX9EN1$hv|357DhgRoF#3IaX)9C*)(j>$`96u9__IBT?&x|SQ(TAXoK)-cuA zid_LSjE#)%BOu@!hKW!nSZU$o#*-J<2exH0`DN9#EnNpu4RpOMttcVbY;smmH>K`v_gu==e*XCnKivtz%Kks3Mf~^DWB6Y;lK;(5_a7_o z&Ju|pd8Eg@L&ue~rEKBsc#)%}N!bP5U!uZFCOjyAV5}E!K~s&5&R23NJytvPz_^hOM(+>E|ocvsP%6U)&1#5&4CxwfQ=22jO3*WMo z345VN!Kwx}V5US$yuIrKq@o{o;u5gzV^cv@-vh0teZ$!X}10tu6;Mjjf57KgfXTngX)kl zABb86@L00)=saucv(Yu%JHw&X9_qtpKOJMR6kFgAeEl_N8ZfwD_RzxlsQHHGYG3T9 zkD?q~rV*}1J;-B(0vWvH5FJv3pgc;Rl^!D?4;SV}Y2+3QpRl8BtbeI=yr8trARpC= zx!}6j$B-Y;(`WAz3actI&(Zu0GcBXir!k%kDb!~|wX_QS#LLb&WT6WvftZj~#)#O1 zoKqAaswjWN334Tx^ulBe7oVOk(~vmOkWd?LDonmm8x!Mn0<)sWjU4vM$0&qvP5D_LL zcKzuh`ZtkAUWCU3(31_+OhQ>FNnN?G>5M9BqF95k?>$!NyNJFhT(t|vChq$If{okF z+S_`1HD(7V^nuTkjhc+q3!0{*N36GR&9Zcr##?vieCIW^$q z6?3ZkVm4hB57DOUsA0qRA4TC@up3}tMjM;?Qg2f0M?>QIV_gonN+i1QG1AxT+9lID zsrlU%zZXn}!IUBmB!=*-2XEzu!dIaQhUln14A(u3OH`o-2CGl(4(L`GHnpN0DNP&A zci6_?xX~;s0L!;xsp2RDsi-DGL)KFClb^D#=psH#wF^%i+B2q%g|rwrPeXe&CC*If z_8Yb797X9Wiqp*NS4_}7M@9X(4v!Tuv%Huh5>9@dv1YznWp)}wk5LBDi$eF=id;e$ zu`Zy+D6;VLJOobRw{=o&A}k<5R@qiS5N5<0gU;cRZ(-=7n6cSE=6mD#lcxhec~A&# zio})QeE-mfc1j7)z<-d^oxPNV>i#+l)sG%T6?418Cg1v5MDK?cI2Knkiy1dM_G2wC zb$09%Nqy2tHm-G4i}09%URBIfnSoiiU}O^BF_5jlL;NxJ(IyVU?45Y79$$lyfgRcFE79UtNAKL-w?Vs7I#t{sj{*W!GUJ~7#hIrAAx zp(-r`EavcSC#E=T+%WNW9!Ljo{tcS}y5Wb+HsNv-O-YaSQIoQ$%Dg?rVGN>l=F76v z)DyL5WQg<^_2#3kirflEbGi0mlwY9t7?4`@lL12XDGXVKapMvwZ;f#{tk{y56Cpsh z>khWIRqA&OOpGw`yiaN6?8G)My;dC%xG;?cRwjy7FHa^&rID2? zy#wkzdLpYE(mT!b2w7rV6?iA97Cu{q9#VnHLdKwm#yYP#@r)3vxB^NWqg@yM0)I}? z2;?}%gJ+D1rqt1|<}6d?x9AwEW$ia?>BFwWM!}INj0BUMXdmb~W&y#~8c!77#XF); zHBr3hGq;~F=dX?EJON*j94QJR_Sq0)U_t=TwajRY$?|_0usM8a3cDzRbwl@3UX%QB zoVS<_jL05jrYDu|I}qhST=6J8_CAePQTS^YOw=&zc4zITowNJ5E(g6I*RF;;KpL}!UCPH&pWd`6S#)sTr4h?xt7;1HxyTe*Ez7y zF)cgYZ`Z6iu3OBkVhIhoFVrvGX2ZciKf%F`AO_SW6x8{td1;lJ@hsxup++PT83vFg zAc-1s%EAsK*2&}XOQx%)@1>4$QIt=GuMKJC#J0EVC>j|;QjczsG)0XRu@uPZwaCvl z6sl-cH)K>jcptf{s_z1;t6D$c&xEHF1&31k48VE3fzUSH-sE8-Ebjmc{bb&J<*8M5u$=M2oO9^354@Cbjx_Pi4i7T5qboq? z4h|<-M%qBa&GfLrS{dE9d)*CPa?sRrs zKHteIE^|R>OM7rEx>1x@my~`t2Jq3OA9-R)g2qW*ze?aI_t>NDW`;Z2uB!P@uL^DZ zY4q2p%=IZV2CxJeM{YR`+n|25Y7nS)eAZZ#Y}!LqjE_8kO9!e<>n}CkuV9#cHm2v; z{4(Mc1d?K)Q&bBwxAG5?`4h7Vl7W##HV#b98HHr0cZ*k0i@#q``AQo^Ph6x-fn5-T zb$HIE8ui-Q7`H{#4G9y)#)6vBHbQ+PxDTR;L zYQH=%W{SW|`V8hZxiS~+c#a-nohzhce3?3jKed8S7DDs2;{(vllCMhak>MmiPo~Ko zU~OC<^RUv!M}zH+qCLeUDOB$Sjsq1MY7zi1-_HVZQf>Kgnq~$yqwi^}SQvOJOl4^c zc}1>&Ld8_=Vn1ZwlDYmf^SI75zsnpprw7C~oF_YUz-bi^x|P9Lv5#NYl3 z1|z}5@`v|UINjL34jqVXXDEpNQ3=fCZzqp-5LLp0siy#f9kwcE*!DGRi;(f;~s7aY%+(d ztFRK6&C!Qybw}tLEi`c#z8dVb8x%6RPZH~+8Dbsh7DpmL^DKb^y0i1Zy6b^wbPE)> zd0FPzf*dqnzQDTfus{Do9&L71JZZQDLm%9AeUr;4u0AKuSG->*D;GaR?h?p(VjK}J zN8G!;s3U7c?~t>j4InI#-H`>zgY$$DhJ)xNx@}0cc#-O6f&Bk zF5asz&A+Xoj6c`Q$gImwo|u^8tRN;<>YFV)saGvAwP%i|sZbQDEcMqXYcyY3NYp4z zP&C=lmb7j~$`k6U$HTnqk&-yU9MP0hT9j8}7AKNlP$DJk<~V=I8lYX_AhGQ;{Rm zf~O8}$8qyJgDZ3ZL{pLi)>s@l2tph8Pf%z~5t;WJL2bqh^*9HWmUXd=QHVpmJts_n z1RGX{YWdhlwZ)>$b&M(%lI8SPgx9!mkG9KtOVS|-wn9|ya*S%g(OHV33#|qA zQDEk4we(;pxt<0eajXo;GnN}0^ViaC%vRuh@oEB+Dor66`9$E!j+j@&<^s@_* zsba)o$zq%uEY!Xv=r2g^Fd={&GL!y?#zK7A#Zmq#oZkc%Q}u4gs}49Vg&RhVSnJJi zcgHB5VPN)MI=G>&?kzpM{t8H)fftm|A2pP20kph>8IZUmNRY>N8h!R&>V2B6#k-=f zb%A$(O-;-#SO~e&dc>Sau&sp4H$2#x4cQ=cbALXmr*e#5jptbx`&0-)4}bBT`m2}8 z{hgC(k~(r0Y{D#y)L0-LK4XS|T1pq?$pO|o)hQ^%RfPfxcd&aLjU)0x4fM9csy4V@ zvQTCWUrgGdutI3F<`b;@%j3g9S@;JabIox zb4F)f+C#@_*-zSm5BU0WNumCtfJqRJC1zz2B#qCy$Mm-}JBW{gSJM?V`_jH_4Y)Nh zJ!HwxsV(ynb@APKDLI?X={~b)bd0)?^0;aEP1d>$NQ7V~ec2iW@97D^iD;>7a9ja! zyf4>gG*8QD7}Pte4dKGZ!e_Gzr}Bddvt2~o{_VWM$*?5s!GE@{(sEOm zEw@Xyuv}HgV%e-l3?IUC0V|bL9Loyi>Qa4BO;FF3{aJ_f{T6=+U>*KMFz$O$BR~cI z^Jl-Vu7h{QKsT|ZLdT%S#ytS}8M}QIifyzSV#OY8+>zTir0j#_?kQI@5C$V zG!+@2a9>kU)pA+qQ5f}ZmuN%i`Ba<#rOChBVJ26%zplgA<@YS``#X1l3EdToR9K_? zz6W9#UPxD1(Xl3p{0@MpCGL%8`}op2aFsLh3u|@2CO_kAG35()#vBe8N#<=Oa4lWqD>Hn_V_2iI3UlMuX+7OJ|6ogdi z$9_Q)J#tT9gL5bzmX44h5PuDf%CCUH3j;S8?7G4)L(ty}jTU$#lL@F7+sRQ7`NJ1;gy)St%mr&m1BScdKd~q6db(h6>W<(O?9=Uyoqa^e zGWi;2;=}fW+pm=g7$kMVB=xmgP;k2)w6xE|MOAl^3m9-{+HxdqoYDR{$ zvFh6(f*43?jf9Xn$L-s$Qx6aMXk*U~1=7G;?0c|UqhYl{I5vnP*)9wcus+oa+3eov zvpPQjaqnNXv-L}DmPQn`w}ypvsiWQ4MYx7*GTqTgcWNTtAQU#aGPgK2Rihl9g^m=A zCCAwTP^2LgIyA88S6%I>)@MW>-KGB{~% zui=cvc>Le)8)T}$_VVEA$_fP$nOIGSrki!xOKKC%{4yB>7Z5h*v$#LOWl!q0ReO9K zMe!UvJFdc$U795FTj@-tH&D2t?7T`@ z5@m+>1=U>mi7{{@hZpK@#U||7(!z`LG^sj{yK%a#{Szx#pd7R@BSz*kayu)^T_GJB zi9z#<961c{%eg3JSH-N|xVW_@ROeCgfZJgs{mS?A%-UJgF$1PWT8QKQ_baAJ|I{!mWXaNa%yZ#N zm0XB|XmeSsR`ls3*qYF`|bnK9tv9$lk<_W21pzyo8-6yiIhT~lKRuy<^b63*2* zL6-=Q#s+R-fot$m${!j70;n$B6n- z%KqsniybUR}K z!0;nJR5D|W8|+2|6)1MM1(l{^l(vZ#VMmODD+-wtzucn4aJ-^NONtJDNz4AB6>}@V zGN|m3Lj+*~;_ePpOST`dci49|7tqVig&94XT6BQCi9@UUa;Nll*|Y=g1s}1#;V)N0 zN@R8--X^^$5)U>qAErU%p&u1$atxiO(LVEnCL0sE9Fkk%>q+hDB)QLU7zOLUEzIbZ zSGH4CmuCj(^0StFwqvCDsp-iEGIr+WJ4YRA1j0Z#+h&eu!!Oe02oQm;cSMhdfth&ApSzW#iicR(wTf-AT(v=*pp3fCymO-h z#>?b}H}H*Bo%xd-8?^g~0xDf6bQSGp;bjHwfreuh@skz3K{tqpp}l9DZIhKUMPDZ- zgCuk93>RZ++n~?P%Fd1$picWMVJ*$Tkd$**f4U*pP|e_h&LBetJ!KhFCPW=uznOHf z*^Z;#owyF{q{#4g;OzDkU8oC8FmF|kD^p~YDbt&&jdz@t6?X?s7CUssxZ{bG z?=MiV{DPv`z)fsMbM4OQX{?W6?-uyBBezfHMN>n7c%dQu=QdN0QKe$hoTYu>g&Jm{ zdr1DM1b-%imI7NIe0d`rB7>Iov_EKDFHW?O9?!&brFsI$`6VCs&+$O(!anq}iD;&E z)=K8$iw2^L%LTLzzbBVnL{P{Erq#T5>{1>ui%~yJM||-}9}48LQ5UV**k2bR>5+oW zo%b@dF-0Lr2WPvl;D=|hB*O`Dr^)DK97S!U&FN19a>6%mnaVXp-Mq=+1Vk3mBS^uq z4<^F;mhCL5VlOZg`0(3UTf2E=r}A*#$r&>X=5W&U6mZ_r8E~`oa9tujPqVbA!f;>V z8F&jZT#t~fv~#Xpj{xqIJX^;b43BX^SqH~Vn;?It5Dg9vu16n_9MbI*gqsr4UvZ*d zd_PXeP80l>{|JY{75t49#UA0`SFz6!-IR%O8w7jX{5Z3nL9^6Gt$2+U^-2xn;)RA` zfp+bcQSlti+9$%bPkAj3k(n>>2X5#yWf%S)^!rm7;%2@%agu&km$P!a35<%cE zkL^)v%J(owQS!vqjKqmLsWXJ{u7SigR6@dg2#ty8;C{pvxO2;kgH%dGjY^WM1DKgw z1u8m8Zyq4L%;WXVy@9%SAM$z(dT)C9XU`9kaqs*1ZD?mAT`3yQfv=E_nPFbj{|dF; z3l!zT=Q%=v3lGuba##5A9cOX`B*l!@yH}C&Or7!`qU_>&}Q8UJQsyMa+2!ooVPk(RGwh3i;EU zQsU2ji)CS3eQ^f!J!m*9c*N8}Fb`Kc)6ZxS?N?PgwdJjwx<|*07FHB~HdV%yP3cwT zk*Vh@Upo7Rb@K__jZ~S#R9a-7iKB207F<3~IjLdcB|DD1;w6K4kHX?I zhbz_Bpr^~028|abCv} z$2blqSdYV?f6P(;p82%C<~#yisQ6|((KnZa=5#oAS!e}@q-kI;Q_||eIIlIUPjQzc z9*Z!K1h#r3VT59<&W>Qx`m$gdI zf-2n{sLso%T;nbMhYXRLTyc82BEIXyE9L-Cw)(SY9~0 zg)lNeG8?=0y&u-MK)pyTulTzc*0&o!@ap?mfAClZ?7+T;!gM3t%42yU?Pgfta{lO9 z0Nnw8)n<9!Qkus2^$vB`^~ht{`HS~3;xxtaI?R^aM~tj)pXHHd98Ei4V27f;S5@Ku zR}v3^bvwE_^_9Lg8rzAzO}pulpAXlN-8+7*T)NekgyFa;S^} zMXeBis4wt*om99Q!`E+(YfstMhkpIQM14l<_{-wcc-p<1p-PHZSdz?qZE^v(Hn5K) zmfe~li%wRPd`;Qt1~=g@C>}WElKaP%-S~D%t}4x%CwxQtC3m1YcZdS9S266;{sbAD zcTUlKno(AxdDH^o#ljNxoKhI`)?1#2m%tTjn6$j+(K)op@6)?@4&?APSIT zXL|-U}$} zz223fOJ5{(`{`ufC@^TVSRvjh(Lif==SZSnC^AU547&_|K}e#w+4@(sd+9wO{9YTR zfhL;+^b}7fis0(Pc8=PXh$!@78W8!JsPcat5ttuNZ}v`q_s{(`;PfbRejMi#Eofzl z>4s7;xT<)@sInkSA|EwOfGY1yDo<0M$9PJy+J-FA1P4t$@+57TMHFI`_&_=) zS{uBk^M$_Dtry=KEdF?I`gnzYdE*6IWgCBPk7svwlKx6do~|60?5II9!vX#@7VgNbFgD~)eEiP6c*(th%e|0g`|Dik z6_Cu=@+Htoq0PY!<;Zk;&UyymmVUw(z-<4_&70#Dw`@ouXBMX@0WR_nPm;(0qX{Cx zN>P!}6@n9|T=hxLp1JGg=+eV^wTYeNtdLxmo*UBk88O$`(N(O!M%HE!m-c!MPSB>p zjjct{=spuSuRhwnR`A?ZNRF3O7kNlLQp&5$l`t+Mo(Lh*-hG$6MTYEmNAM2n1|)T_ z6E>|AFUjn#oC+uO-u&d`tb2}ck(x8^`{8fn*gG>tY;EIEi_6TOcV;ZJR3W(GZ9Gi@ z;J|?9l3sF+$8`%Y&oO$h^D^ws=lx+lGuwk1mz{_@yCV@Kf#se=&7^A%28YwAuqING zbfr|`G|y2K!9uXvha z802b{B>G4zA_H#_x_XF>&KJOO?HQ>-TGmu(9pCJ<{M|HNVs;^7dxycTRsdS)4z@@! zpN;R6EMpG;Y^8W)V_T@vT;6 zeZNk~qT&hlHjE$C-VRZy(uo4%buyz2lP^Z2`Vvrqw0jO@_eAj;5HecB1$~WMXWz7V2+%tyU)cZW z!w9EZW1>~ZX0-l!we4>DNRxA}+NGnGn{&l^$~w8RXA0MALQ{qH6hPG5d|9rq7%ZGI zY}3mzFioYkt4%JgJ(1pS!{}`OydAlAa`%42=blXefiD0}p^IO_5QYCp%=Aj(Hd$hx zb`QNoKC^$F6L9uBj)ih&f#nD!^-qf4yKXVi5oM$78vsYH8?@nf+;*8)mW_P3ep)w@ z@xE(O;&Hy3F{7$++6R*P?Sxs3`7N_k5|bmER1Z?x1pVeRi`)yN`K{_c?q6-p&mUF3 zk!!oQb;3CtXD7v?-CK{N&zx^TDBh$~rww7uQ>JE^l!-fw<)ovY>%82tFao|uMR}(O z7|BJ9SnVlxPo(kb&*u>T?%_Msm*eNmAqgcI+ z;KP2*8vMz%pFpHQX1na1Ol#|qo{`yEC=$JdF3617GNE?NkkWRTo7@$g6-rt72`lm8 zIatO!OL`Q8tnf$3{s^WWcG*)5#VMhDmR`AtQwhvlzRQGyIETUWF*$8)uACcZ)O;y(d`}O#tW* z7RK2A==2{^z_T+QmrSd()`{h#<^xBH9&<+@SQjQ|BqSR&Ti8-2BFIlWJQ!~vFpV0P zJ=anHLHpCuyKY~k*cIkI*Kzt1Y)7DZvQHZJ!+1eV{L96|Ske$DzSYtR0&Q4#?H^f4 zJ~eeqmX}W!T)U}!uHiH%i41ZMAPVD`FqHxH@g4;1OG8SPD)MC>2uWp<WB-0Qi8XMs1D^%w#C+dBU5T|Bf1~Ik<`hj< z!OK!qUW;#tLM!zRi&Qz4AnIdO&R@-6?RUa*4rb*?_F`g>+4y0d7xFpFR#okt1AcyH z;lsIh)!^}LyEed+ZM%KWX+f!CiL0)}i)xEToqK51=m=1sgPsq%)wGWp^b4lsskq^A zV#VQf0^#hfcY{6k^OVsMBnWGh3oxg#S&$M%b&o?Pzuxia78&ODSLo*WW%_oa9s)Pc z2G%Q$XKrDX0Gs-wvW=LvJ#bjqky;lC6;xh`J>xQ-nJ4bNtu-gorqtfW%uPsPb+T>-ZK~Yt1NLAtvLuR75-{LmzJ*l~Iqw2&2tyu3_ zgG^p%AEAg+#!L^o1;7+c^q3<~lC`iTSWk_#9r2+yugN_U;TQKv= zCbPeJ&mUFXrLY5QfhRgD_DPelesZO8>+mSGZHoj|3#z;g1Zf5bTDyKn5T($Hewn!&h6q^dMxGhBZh^mhijPp8x}P|8oA zD@OeehkPJncp4KL>;g&VNjf>M;M1{(J-qG#y&snw+2YGnXpbF&nV^G+ix z6o+?=qVb;spr1O$UiW<^_qtS&&ib8L zjQp8ff;#I#O1qftp)Li17<_ry&5!dxoz)0*sgMDvlvBU^0fPVvcI8`M)bQId%mA{z z5mqW&e!;qKs8y%TRw$ChXeMJc;aly{8~pBwyQuvRv>gJkkh`e3li&XhS^bC9EB}q! z@eiZl{f(?L|F0+clEw}O#tycQ|DyH&gO%3)7o|V8l0LPrQlYHbW6>n-u&03XFG@cE zlnBI=bq#hZ@lqx=OXy2hhnyS;o>V6_qV(Vi|)%Rv0570aop}W!3yia9VaSg9@=CguFhMKzXS~fzCgh+G6fMHqpfWp z_3w=o1DK}g%4I$<(znjRE8FjJ(rOyV^|FWAjS}FJ2YwQA4FXps56`voaS3haA%P_grh>+~lvy|Irq z2X*F*cD48--0S|)x5BX;&R{y&15}d~rPMiqu1EZxtv&#~GPPq2>j~O09|qk1M~nmp z?8BG)?Q^L5#y$S`80mj5zJJ9?-$n<=1rcO!OU^heY&ue}Mha_4x1s-E%mm5b{mfJY?)PCGr4Nex;Ep| zAGXB2(zjq=yHqAWTDK?~rmDYe@;RtpJDTF77vrs0Q>$Vq1ep$br!RL2uJg25cjVKb z;(-5-!DADsbGN>}blg$7*fWuEx$@0xyn4flcyaHbtgKG-p16L(iGVX&@c?)c-ua_u zJ^i8(11l`*xp<5B<2DpSRoYIAjwLAU^Bm;TBp)7|0m$r0bWD-t`2eJhmc>`R?Mvl0 zAuNzW$(WdiGW@^~5A}Cm3Rj-7qX=sX-6$=G#9{m3f@J-t8)X&gwLX5cLkE9HpsnvY z>)>m!Aat9bYxFegCzjBP%^P@f)qxsD ziMPqOF+sMz%j)GbG%sZ=3a^DF@Pde{wTTrI<_d0#b|oWr+puEQl%=!N1)S^J>zzv4YwcX!-OI|8BLF_!5u&Sl zY*-kKH8z&J_NWdgHL1l(g;wZlZy2{9J{R~@vb|_KiuRY09 zT0*`TK~zSjoFZjlMY^*z3CRQZb@igIUsDT3ZAi7c%%rl0A~!UMe`siC7mDD6)U|#+ zE|6%Eew!VPN1KGc%?|POS}&m&-N)m>r3Bw*2TZ}>jdTRDW27FTy#i$Z?gmNgGQ2`&!EVgLu-&f&rF_}vAB@B#3N+^15-W^m1KJ+ z>u=F_PZtTi_BSHvPV_P<4v9EYG}FeIrO}n~*_B^dDQh@&%m`~1d(&=4=O=Zetn_gZ z?~*D9gwE2Tn!tZR{v8?rQ)uWBU^u6~3rrKx{~sj$Uy<>@{^y)ewktfKprC@FJT9O% zE}*y~pni{wos0Q{@r$wJ{z`&7`%WUDP%t@_gY`mR-@_7|zINmDCp(=ir4Xd02t+_> zR>BV#{o^M;I}Kbwb(j{S`if5wj5*v1SGSNebu|(*&y&*<#z%W^(Dn~5(o?e2Qp&*; zCPs7PbrY*|)M8UJY8yE9{|t<9JVOvL}(l)W0~<+<-0!u5Ze zi2u#H3A-5@+c}xr{+H?^DSpakf$yJ=5wF(3M4vc??hPo5g+|K2T?HkCBtnohT(R_z z<;qLpXO8-nAl1{Kj~Hw($b)`hyJ2huCafxf-;VXk$>~2Qtc;d_-yaUhd{0(wa)U*e zxtfo|LP~I((N!GMC=lR`lPi>Rp0*ikU!WyN%BG`cP005Jmfv@(S7>{5zf4MdR5a3v zg2$(?WAi*RzvaXe-ShX2V$;Sf@F(=f%b!g6mH2E~6Glry0MlKc^dPNBA$W0I490x` z#*h{=>3+%2zXM^g5U%&ku|$G`I1G(0pELUcw`!-cFVe+MZe|;vVUvys?LLga!OxlX zzZn9^C3{xKuq#kQ3CR>JtNrulWFx3zZxXkR9H%qEnxpsjMg*{uMzg!++VcM&%HF{} z(|*|+3_2ZmY`bIIwr!hF^u(QxZQHipvD2|_+n&7N{^mOSoNH!(GylSU*RN{Ts;aeC zsuoI-)CAl4)0cH+s@>@&VRlzo0XVYO3l)}(XP=AljLv2&8kWfs1;=2E_EbY`Wp~OT za0m*);y$r*uq}Zw11vDhWTPE~lf#QE#x>Ryt-?)$_(zB)1Y@YI6vPz<^MaO<$POY> z<(18kgBmT8Zj!9kxQ51gRM2c%L%NF@dif^z7A-LD)t!r2`HmRA2^+zGCsU389!f@fWyi^OZqyEJUA20X>zeu|lov|S{m)U6 zo|X_!?xT?_(C&c!bA9>!_VLgjWXvt$i}8&niG&=aF&XBO`7$D&i6~?lwWMK^3F=aS zzpPXs-dcghOtR9Oa%h=cFLFrJdX2Jqr40r;&z8PQv2@s8#w&D2!1TG5fFIkt|28x5 zgey%}lI$G3D&U~9scz_taofd%no*0FvT*`R?Js2fJn-`3m1mF0P31r!vQ(9*UdWHp&41_yU zNY9_A6YaOmpvm@4#E3?sp2Q{j@Yi9~skMZzlQv0)ZWbyn-vd8rvup{o_RUOX8viSk zn?mcvOLZHTXUtMG7JiLF`tlUv!cU?)N!v69e`+|3w9pNMbVk{e5?E_kgYm6gL!;?y zwGh*8%Pe3i=RIX&-8krh$w8R?2Qebb+ zP%S?q`LMS(m`OD9H6kbzj@wOXRCJoNx?K;?JN*6Yzta6bam&1z?CtU$1mqGP1cd(o zPu%`1=NGD5JK>0;^U_AL)bYqg7(=2W6W5Ur*O3eWaJ#>|zK5agEa_2W?tmE5u-{-> zibaUIU%}ASbaz=PE5Lf?x~cRezjv#}2ArwUZkDXPE20)_hgA%R5e4dA93OjLwl-h# zdV9Wo5`4bvg9tx4;XV>$3|aDE4IyWpbkp%v1~CJEj4@`YPMRkyFq3CRZntvma1^6w zvl|Z8!c#Nuao56N^}#Zta@BtI#~!%eZivCZ_zI&!$oS}Xrb!6^|H=#BE)6G zW1ce(`W=$@93XQG@SCKRn^o^ZADMx?Q#Gi`djYVo)7U`#KKmf0Scd2B-ri#J8s$+< zkKL1|jD)$=JCBP85rsF58%Q>S$W0|9;ofr#4q8jY;58ImCeTO8>WXHf0G?GZ4OhZG zze#tS)Ej5FwTBhbojQTvSTCtxMMc}Lfwfuy$mq-SDy5?C(fBkgZBQ?y5-|4x7ctnJ zZ% zS;tg|2m)2zazyg8!(^eU)kRJTu%U^zG`;gX9EPxDU=iynSxzcjyvJCSmdUQ?VWdgG z5Kp2Jx)ToqULA!+>{iqgLmCXoiAFw<=#DlNpc`ABHa}LD=%&g~AI*6Zao?1Y69HEI zH-Y9=={nWyOBA+TH%?k^V7mtws7pV!YTV78p0`|sReuE{k=HGRb||Y;E(Q`Big;zG3#fia6ZM&0(?~?Uw9iImcZZX0gKyd$}tQ_Wq~k9Gb_?a~n3a*KFW)}x9Vw-h`v{tDN0JK+e_c6^ZC<2%|D%yPC^Zf?6*aqG=TUU=w1v%7XY2&8O^oMnuv%035ZKni zy(xQHe_Lv6joE^cRAgBQ7>Nr5- zH=4ZUUG0h+?jxh-%R$e%SKqYD_{(30MjxXrZ;_>M-0UwzoUaJuJ0u^s*Nn>_l+%19 z8_6PXxYfI`Rz8LCt-~tkWTU$#mI-loe|?6q;%6nU(DG!DzUdQvJ~HKTYz&|lrEbx6 z$STP&Ln7Y-?s%HL`px8$JJFOEMfkZg#Izet+w#>V$gx_Fon3 zA8dZ}7E7M=g}g|3+Lr+KD4=X9g!Y509DOq z`-j!l{2&!Z%wR~-`5L`-0hG|xig3n>>5U9xc?n*<)C+x>hdv|`q>u7_DO(?@0f%5l zkcCF28gvJvC)S{X>!wvi0(2`)+k3PNb~v`6VGE8YFx-ZXLAfkCk0Ole6iK~YcEXgX z&Y4l^gVYOb2RvS40>q!>#g%04%%~r=XIZ1Qx)Lm0{RVCN-Gdg6& zcqMJVi9ql8K)KZUZLUS7WI^9@MQ=LFYrxl|*@ z_Nt>Q7aYpxF*&+>^vsSD$;eQ( za~Po!nkJ?^-5K^`4CKZc>7?u^m?#W$+>q-FY4dfGQyNs>wu7Dd#`yA4$ug#t~@tZ2S{RJXNhsqEYQD3Hwo2Hl(xXOokThasXPS7VfS({ z;5>R1hGBPRfMH;|ZH=n6;VF9Xu8bw>!n*zUPEh8*Tw_FHY|#Ce3My@pUfC8Tjk^~U z2K^2(7)P5Wej>pjbI1Qr&4MMHSV6om@|XLIgZn=;7+?2Q|Fz0ZDj_Qe*uO*Sd9U!VkL+M*nOnvA?)~=kkbCE`(1=LZ>^69VlRx1`Wgw(ON2vqT=v{dhr$BONuPa)H11kIb1Y#90-h6f`-;WQTb3NMB8V zxs8y?OV#T!;Tq*4KkCTU9X7772gP)iMVD31rMW7Or^neXZjNCmb-3K+3fwEWQzbdm z?3P8bPf1VHTfp{Bjm7Yx|JtD(4Kq$xb1$+~lkPRc99eOlkV6y>9*0f?8g1g(_vU|j zG#Ke?L=n}JBXd=4w?&~2wLa?8kqJXGj%vaW)r8I6#+rOA|B!k3T&!d}aWR}G!F)OFn*=8O zuu52gG(M8!BamVgEIv4fE5+z0sIO2vTcNngm64h`Z@Q{V^0K@kCf9GumzWaI3GO1Z zRoH8gP~PiaORP$3_Y1-j2eAh`Q(kF~-!GmcS`r{agMagF3Rbw--J2n~nh;lDM?i`Q z(wYj=I?5@8@IFGUe#;0m!G|Qso7M00moM6h9p}9(=n7_95)!GR?a(3=Uu{8F-T?yA zY%&>58HzF@BYr*1Q`z=?xTZH{Q=feh{25G;IH2?BoEmN+KwW-V`{Q1t*gA|Tbq6v> z9e_ke1==C%>>A4Q#QD<;E+bn!MPDdEUjQS!|L3HEpS*F z{4J~E()+q5VNyw0!f^0A5>deSCPslYKSAJ6CK$|K21^!98wF>9sL@NKjYFIV9PTNh zMd04!lk4dC8@b;b$<9-)wt6&hqnbR#&ZDpndwA&7JoTYIz)QkvUq2@8%ls;)eP~Na zd{2mv6PkUeuM!2x{STK;OP3q$`Xtze;BK={cLIjTwgxO3{o~tE@{ zR$f*bToBH?)J6T20xIlNfF6&>v#tS6@MptLVev7hYq6bq&~e4nD$hSsP)4rd4@QKf z^zN5|Uf#dFz*>4Fqxx{g1H%3A#53K{aWQ;2(&W&k*+OGY zuErOqua?wgTGo!T-l*GOBNLsVa2-Qz`rkxDW>?7Ccg)+H!ow~zZY7(G0&a)I za;VN>8t2!LU-;0aAZ?}8Or->uRZ9}8luJL_r8(BI-#-8gQ5EV4@qfgbz;gY`OQe?s z)vu~@ip>0+j=7ime8!F?&FVNoeEy9r{HLYfq7OzQvSr~ z4Vj(kTA}6rQAWjAxE;{tu^Lw(SL8h3c9u}!OU}VUCpaIje|0%Yc749Qz;`R#xK8!z znqP1_@ppUp!Uvg>NR8Bo{*0VEao{mBxWq&c*1{S~%)F{Vt}Kf3>3Dl~kIsFUqOt^H zV!sEY1RW?4zy=jcP+fP8MvJ(2M>B<0h;fnAP;jiD8q5~WFm!}Erv`Lv0jPTCR)`ba zV*@+tEZs_ER8iX+cHBr&4A1P6j+9nde!Wtn2NseyV|lzE1!Uha>{3BL+ES|4ov%PU z8p6qYy*q&g^*~tu68$lw^vgNZLyh_%kx1!M2twFgU)_}Ep&-5D6TzF`6*q{RfXdXA zZtz3}Te3p8uwh?Lu-)Ckj@|16H|n;duFDHUeagjbJ{7<3^izwn@0?$F;eCRjHkACG z38ZvNOyC1~9o+#7o=cg;1cUM>f`?=iTc)SWZA7&W??yl(hC(Ax8A#sCq{fpFR07If z;Jor+U>z1%A8?p(+l#VbR+hFUx9G5rQLH;x!N|HuNC4M_FFVZvi=#9%+{gA6A~Yq2 z7Y#u;U4`jYXX`b$vmbkg5am9Bl(P4v^~e1?u@%%JqO4q=zpy8+8mX~y|woqy2Y4~Cfd75pu~ z|JBNJ8Pd?-;ga`pKMUn=`}6zZA*U}@8AO5udo&OFjmmnwgW`ZUH2(0FLJ%2Rw8{}2 zz4K#dw%a=I#$tX;{w?2Qo6se>r2eDlyTD;zK{e_?laSD)8%G)_!v%$Tz;n4SHA1ca@wUuxf7y=OE)w^tQe`$ zj9AB`Ikp6yCYVEei=|QJT!Wvzsc+*b>F>vX{19FK{ALRLR{xDzxdeG!&M+#6%TGTi zW`lN|MEF;~F^w{vG0G&ReQuELC?%>SJ-5<(2+kLaQYPa5gPk&oVt%23i9$7PNf5Ug z5L_oDMTqB~Mr_PM*IdOv}eNzaYp>=!oJ87j8p%8B8jnK2fW%a#r5v5ObDV&}~Rh1Po8@;Icd z1qPG08?EnlIllzKHz102l&D-Y;9s0d=7lS$n6}+{m4!Sv_w|mmD9PJ+n5!d|Ib#D7 zqg&z0=e@ZZQ@4-pwNL%v2A9wB#`d1gr{!mPP+?D98KS8J%v}~l-R=pr$!O5M!$ek4 zt_|JFR2luG4_vCe0nmWS_E63R{PM38*QhC>ar$;pDxVPJba_8!K$fTb_m@G@0Ennc z#%IePVs(ykIagVK%%a=UqFk6VP| z)d-|~pwx%%S~}GQ_^*E}R{wO*EkGWW+b{mt_e;g$KackR@z?xU6#TpCoHp=Fx#3F% zil;4NRwVKqdsfUcT_m=GDv&Lq<(tW2QQpr%*E6n8QyM(!_z3g4>0!W%01S)5#gxEm z!Tt;?wnljH6ogcI#lU`H#Q>~4&(%1r{TuieH*;8lhU179&v>fS!?L;lpU%l~{SWWm zM-cqMYT~89uNu;?OvHoN!|cWucId$Ljn|J1weP23Kn=~NDH6P$5=ln8ZzLsShN?d{Ze+e$b5NVpi2r;uHvOSFb2bhM%`u!Q%+;1yN(9qx@TLQF2<4&~;m_0$tI7^PDj&~@)a4tyDnb2zvoi%-zlpgTF}xWIH=C;h$_rl=fq zhHPJ42?b`b?VX4CF3y2x@q5zA*jTS(O?`Oz1$I$HP`nH7 z`&q#d1)8euO77m%oXNM9hw-*3!$i{v>MSsbN1K8){4s&Gop74aI>Y^nI1Ob)WVKSx zNL(H2FsoS?*(}5enQ7^#=g6DGkhr~>i4~D%j~~oC&ut~G>**1h))=<4yJ>Z_>J_ah z?d+~ZS~lfM4xgWF;=fOxM$y(@W1C{@k8q}_nR($=0l!TZ=QGoehJDJorFd`C#w1xv z#B5h;7yo)VaOE0EYqlt#!q}?kag%mR)9$VCk)qn{=i&=MYrMdSikY3aDWe9%Y4QO# z^>7hJj70$T9ejeBQzTl=bJb!;Q zOGnTmYX?#0+R5y0d7CB%ARoQ?q^NYOhr1^{NLon_Cb;WmPyR|7p1*Gu zyGjDjcs$t-yU=Yy+G6d>%5WJm>Tc^N4EB#LJT(5rE7*?mZ7QFUzh1?W@u60b7fUmg zL7^d+@N$2o>dc+R8v=h~UXHBCsvZ=RDCYC}$GePv%a!U!>p-WTmg2)n zkLIaf>-?PQTXhS_eJI#!{42?2evzjG<@3a)m$x25ZE)}{V1lmno6gl@+ z*3^@B1hFY3AnW|%9jIytP-mv6AK)3!p;HA>TbabFyBkRj(&&`a&!^?$%hjkpoS6DI z^ol2D7Y%$q`n-gs4+Z;T5MROuoXq(L_C2qi#V#bS)l%;stPG_J4nYq66=9c0CuPo6V1FnMUhquOBVSAcWpY%2M2T55hFNYg5nBda+OJZz!=;iTy5ZLke!778Fyr>piyO+Bg(olbJ6{FID$;a^CRL*VjfY|0NC`s{SZ}pffMP4qwU>2taDQWcKQ$o4(Pvz zV4omcKLeelP!AIgJN61IM?Zt&=N>TmWcxD?>Jd<6mZ)5}0A4}RraN{-LG|xXH1X|{ z(7R4+=lL736ZQ5xI=5%=E1CN=5|yuvBg-Hz{5vDcjG}^|qain(zW#g?N80?QzG*Uy z1CRRF5T@OI^VC_jlG%k228m=2ORMV8w;90_X!2XPsIJnoe1u3^TNTRJj56%onfsKK zhNREhcithiH==i)u!)2)cb^`aL>S!Nd^2FL7!0$h$(E|H`xTEnm05|slOYG&&t`%M z(un)Y1jK0`2nZ}!TdWITXI~8|7{SeyyD;IecsAP*8+2FOMSFzDLs*W>dR1x3&qlPC>qX-#19N!79uLoB*5TSAuHEzX?HCZm zNuiz4Xd@VcMhY{z=1iDhO+zD*aVvj;NFdzZs z(}ym7?4bU*qm43083;Foia5N|q&cY)F{^y5(w(4IPCz*rsz{YCwG5C$E%#7Tj0jRz zb9A>AFTO9s+TSVg?HVs{C@O8Wy*H4bC|L%h##@hHULO6Tv_nnhTCta%p0{ie;_!7Ylq8SS zhCjo9$fg>)fMXgjy72h2=t&JjNvw5xfhgL1M-=ac2giglNC{+sC7}_~z#NG2^<>A% z#nCOBvtgPi2ybR-^>njI!ezqm3`YT#uAJSk7QJcOd^y-2nWO16d%wGoDm6U7A;fiM zJlhEgN5T-tdI(q^m!QmpMYuu)BM*^9tQ%SkxPr!0zw#m_-$D|l-_*`|`-$KFEPb?9 z{(;!vZ|q0!$hB_x~aAeBUc{BKSpFb9^a`{%3*b|Jt0Av~{o{l@($6->t2h zr;WBc)@P>YCD7}fV?=kwME1PFa@IDqevkz(w?ek5-x9wv-NssL;Y)>aHFBM+X@5w9 z^m|AoV}DbXAHUlfLRy{63W&>8gDPcW%%GbDp+JZtCPM&=!0tP)QG?gv%XDzu%2M0r z^9Asa)8ETi?>|oGmpWWP&~EbawwmwU_o?T2y`gO1?)kkR1EVSJlRrwGs`ZpM0_^VP zcuP7YO)nG=^n|XRl3^bWo5O{b_hU*g9$IQJE|oUkN6(ZGK+#sK$J-M*bNH{epXI49 zc|A^caE5T*s=;DLMqro5c2?zQ^8f${u@|%y?hN=+r z6XpP2&(RpbjxA(z7*iw$$YJVdYtK~NxJF`&47iZxF5t~kPP$N8Zmvb5iZ8Icgu8jO zqz3AKn{XtD18OKz{{DUHOLZW{za6@Lj^JlZmwf39^jhO*L|bd*W$p8f_{ux-o1+pi zSYkuvN{1grfrWJ0@WqaBE)GfC%EXD3S4UB(bptRLlDBMh+&o!fg9qO-m}}omi0lus zGIh)NVz&Z|&fk>c#sl(uG{Wkck9B-3_^PKyZD`abZW>->*ziL49ZlmzRFDP|to+9- z+4`leK+V)}@YU*5R*uMVZd&|w;O>$#CnD^DZ84GkV#s0DXp86@PCwramLlbxTWitJ z?JAVbI+^fBZMtdg*1}eCw<;_})-V-H)2B`a<{~kFwAJE~*hNq=7mdU1*pWI6NA`_M z6Vo}oe$GOHO6T5C^p#?L?%m~Tkp7ajc=VzpNH@LdUAA%Ki~Bnc`^4qHIJwN#B{E!8 zQm@7`zW!Pl>SZI(keP<$P(u|)9fEYw?37{OLi^W-D_dee9x_pcE1Z#WbP;pqd97kw zn9T5}$1*c&-$P3mi)(^d(F%U5Ek2$XK)t)LHu?8o{H@!}-&d@b;ZxqSgT%v&q!`;* ztf}D+FB#zu&lT;Hx3cTu8I`%|=N>5_-eBK(e_-%}hG&Vu;{8s}>gD1JqkAE>JLCZZ zAZ_|r&oDqATUy#nzy&+;9lMkrDg$2qqwi0_*D+S|HW0;6=x~Lrc|G#I#enIKofCta zEKd&rpZe=qhTWOh)wIYd-%A#3c^#kD%GX{nJ5^~Ket(kO4{B4mo*gE>J~A(KI}hyg zW2d7W>;Dbq(MN|00(chqz9Z)br9(xEBndeTU@7J(_Y_|{`!It%FXci(Uq zGALFq8^@>Ios9DBye*_InbY9Mbh=j}%Q|*gs0~vm#NKj;qU3muLQss0Xk(uc_vKQ?RPdYgQD%qIvLJV-~3ylfP z@9WL4fH^d}wf>87Jdd1o!)SA!zjET|>2KxVN#hnSUa`Ex zP#i6?r5k&t;I%B8DJ#n)ERo_4>9(Z1bNOasHe|~g2Ry2Bh2O^ePW%evBruC!It1Fr zs-N$AO_p1-zoLSc`KWeLebCtba5?2Aaz`&$>7jCUiF#iemOnKcO0ti^-_zmCGq4;wTefC>N|TVTDV|$A%iK@auI{3*0V)$*-b<1F~Y?s~WPX z1Wvm_45DF0Z;>yooLUrb8pQaF&Aj6g+_5`k_(UfkA-H9J{FHgAKg#NGmc?0AplFmFH}uCMYi0&TeO@O zSB^26EWdF{?Qd<({c5V(E1^oC@q+J=CT!LV_LYJQ&xwu-#9kSMf-tv4$?KFLpJkb7#orj(*+e4gDJ#Zqb;V6pWXs)l==L_=jLQ)PHa&{1FvTX+bffbvq`Y~;O`%AwVz^1x14zqsawGRUXv;(! zaX1YwH|Vw^8?K2l;?zR;w9uM;EuCL-28>F)x;mWW`u6tY%$~CarQS;Y7Nivu?mT zN$O@MY81O?BV_rU8$dxz(Q;Z*BZ)bM7@8V+UP(Riq-1QG;3_*H3bD?%Wnn zi=9AA}_>CL}f!Rxby!UzfliH;fsgQA>89d2U8* z83fHe>O=@(@jFrC?>Ki|zf40dui1sgPwZ{7GEchsga{nTdUx-t7A38K^f;PW?hW+j zIJM!hGS$-xjTxQf3{Fdk*Sx?)l87^1fFJusi6rK1GZwCcFs0$CWC@>4AC#!rXc$f2 zUVVH{pp7vU3SZ`QKo6}L4K6E0Vbws0*n|di^?q1DAe}k3b;LVTOfW{gN9H_iSJ5$ti?TXzHCsUE>C5m;oyT|3i3Aez)WL{>{_3%KNE<&75l1a zuW1ztZX$tIERwG2fcAvLO8<)sk#g?!xFPSXk+(J#tJ%Yxr&<-ihf}vrP-a%;Ud6_F z1FiCG5KDT8qytakFUd6P@*DH`c>8SC9}ZHUS_#krBb~;sPOP)hT^E@5SaSoV&{}H? zSqyFRyHJ6-Em#NL(PPnCL-~2}NU|aLmW9aJdh=J>y#mZh7SZXASQXS`E*5O(3EaI& zcZha84R5)L+#Q$)(TW6;izbaj!Ur88F+h#KRlJ%{CHc_Q&K*IUmQS5gi`PL>@iiiC z$yQJPQL|U#y;oU^KO}O9ZGwZ?UEZMWEuZS6ny;gRfG%$km&ad*d-K=qZ@NSLSMqqD zy?@+)4GT#&1PhUKlT_@2zlje86Mw{xe_&=`C*+(p~qb_M8XaOV$weTTkg1xo3=ep7P_!4+E zzeypf7OX44&Y@!uZ2O@TqQV_&#S*zxryYd$w{7&tyUqf~-PXdw6XOT+tU3mY6~5E5 zq~*=3aD^d`XFs@hEmW~GJ>q2+O*^~eW}52}pkde>t}oZR?Wf|pRW1Jj&#|Gptb2CN zhQokUlNPBs2@GfU=$tT9ny;Wy;G^7yQ_1W&kGO0n&omEk^QQa712;!VcG+-)n;;on z<>yp!UhhVXnXUW2Y~90AWMBEb1(Qy_`pnBRYr^ew;)XT**lO#!X{nZQc=2v|je>`! zT(}h=PcgDQ+fY$e)m0igH3XU*xe?ts$|EEFszj^XUkYZJwK4{^-3L8MB)_6JmRf2m zYfUnu0J~^!%2PU?r9vJ*`mK>BT3V-=*5r@IwF9Y{$h>OspUzNcLci=4skQJ>Wg&7h6DIsYfEP(4+L%Ocj;NKH_$W4V0-Qt$NKhDNe~i zg$W79SU{*dSgJXR*2P;Bh#j=D{RC^)*{2bKo$BAuP(+Ce)8 zfAaOBQp*zprs6bo85F)R`r3DWQAnM3MF)g6ZujxP{kOAdFrU9cI75aUQ9~ndtm9eX z$_`XHT6Nz`(bPDoWz4a5nv4Co6qh0om7Pu%LRw*@n+tJic09L_*}&lUS$Wr*bE!0r z>CgOzB56}p0PJgIS{NO4PwRL;S0FYkF;&nKqK%Md*B$IP1AVL-z~3o0doYKnZGLLM z{k;r_vGN5U;K0L7`OQ2WX?yf1lHaW&UJJ@r0YO}~1gCtG*7RN>k8L6~}y0#hTj z_Qz)Tm0u-tk7lmTRri-Fq+l#g?FwTw##=S(Da^yX}! zB9mp#%>a^AjzRm4U}Vy{$d=_$WZ+x5qtEWM`sknihmPr0hje}JkWd&vL_H~ItRnKx z6iK4+`wD2r`Ju(lec9mY<5OolLlgLJo5D%2Ucx-;bNG)ONx{fw@WSWtHU>d#-ISkY zkngyyb8Bt0-uJbxn^T2d|yFThcpf>DW>H;8-6tr6%Gpop>cu z?#cob%q5$&>+=`q5UbWa4`-!J?JQ0YicG7szb#80WSS*;(njbal%>Tc{xqXgs4$E=aRx9z(zGC`^pQ zvGc*#RUXDtN{uD7=RPJn);jl!-rhXQMDqbsm_4PU4M9=kuZ_%h5k3P!xJjYWPVHhB zYWw@EOQ+woAk7w9_F2n|YT}+%m!|!b(Nf3LXvZDPdE&hI=}0!IO{{9lP=*%W{Clso zFzjuH7TlOMw6cDa1yoR#I)(7D`30b15^+6XrKn>jxoDel%6?UbFo*a4%zYcrz}zaEy3KX^^WiIBhK z*gI2bnM7xL{#TJkw+!=GiPq2rbkb5q2Pj+&k&$}HNs|~qqh|=8pII#@i_sNoe|-z~ zSL1b!_b=yiqru4Ax<-umRoCD6jbV=d#yie}QqgBMs?Uu&$;mxnd8dzz0|B~SPtKMe zO+^KAp7%&3Ue9P5U4}yUj|)wjjy{b`qn{hfr!vh*Gp7-Nh1ev_LnT^(_;0IFl(kVF zcIk=jYiZPv?-?Z(gd2aceSdF!{twpCNwvW0*VkTy3p5A_@BbSuZ){`v)!NW2INH0p z|68B)b+M`J{I$boYVQ6o0PK!>I+Qe# zlyQK_JpGuvvAs)|)8&K0)55Q5{%M3rDpn=xYw#z<5@ydN6e9|keHwi9m?5dG{(PD)q~UqC6M1T!dDY*A z_Y78TbtIihj@pCHI_notRfpDSm_TM2pq?u7Y+m3)!L*?Qdn`-Ya~TaoTLrkS+P9$a z*~xEA^EJ8O1{Yj%*y0{w(RdT-y;iXF5qOXJ<6{q8fXv2PT<9E|DrU?|t5jhs{;4OZ z656q*jY}9>>{Rq{O)j(RO8gX5Y^v>4^3|WD`cmwSE5P#HOX}$S{h!Lf-^(c5(0iwC zSQ7CfsOPuQNaK!er(-$CN?0yaG}Vamf=VmaO0Q)^Q%4jYGCKgwbjR#Kgtk}FQ{a(E zE_CNqGbU=fB6ra^khOuC-r?AS>OliBZejf6-+}f<`9KLwodTr}wl)l+ODh9h$GXw=nNaH8A@G}3) znv*T2DvlJRYqBm!gJZ1ZpE(S4X+9b))kx8MY8D984}irTCm zw)57Uze9~Gzs`V2=ravWpml2FQ|fSdqUcehQ)*HQF2jTgNUgZ#vKnxYRbb-|EnEot z3P1x>opu%hwZXZ%TJVM$k4M@3rtWKl6a}266nu>w-R3ET2x6MVu&+@GAN^OCyuON_ z2GjUi5_lfL;tu#2HX3K%$2NhN9XNC3On3PT=2FdOk1@-<0UC87%%$=OUq}0Z`4y+H zHKinaafm0dz5qTK)R*BQfnvJzq#f~5n|}0!DSER929Fke%8&KA@?d?!H=K1aeUc)g za@ienT|{e9qfvvjJ2>|VL<-(7X`$SRI)AL_$)kgwh>h)BAbX2r>n2|c1!VJLnDPQx z#tY8#q36%H-^%SxPLI$B!=@W?bPDoC?Lgmu-&gsEzHqnTpr!nJ)r7bpAe{e)mn~vy zY;Wt}XzJu-X=nZqn<4zaUi`ma^?>HPC+a+!55=-cT{{XGD46xL%5M|)5#l5*u);Wy z@9;rEAn=V%@e?U+DZnGviAJ@$Jo;KSrL0jMnMlj(8d$$qQb_KEcp@@9@n0zpW z8rz=~LQI1FGu|b5d}#2+do}!n2X-m^_KKN&$OpxH-B6B)-rw z=aK`j@HMA3F#VCtCu0xdronHAXAQA~l4@LLm#VN&a>PpJ z`Oe^4kbttZTZC~HkyfD%6FJTpE(G!{Co9GHD!V~uL%@5RUB?%#K-9ri zCR=7ha$V*ALoThTY7{!MKT$V`WM|jaI8ok(d||1HrA?~gRpzaf`#l3>ff8;b6#v*G?~ za%D17np8>AP?6U^4ukG{SZi|gm=bjyEc+$v>wlsX{gF>Ayr}-n7DHtP1ikfbT}d1W zSkR&v3s*D)-^GX_fG7SzR>ibT(RN3+)G98!@Q&fdLMYkDS)Gbq^z4D&#Lhco4&tdV zTyoO7&6h7)$d3}OcCA%yd!}QxIJQq1!)aDK#}9^z6<%W*_0nze`b9X^WMe|y6D*z; zm2D;T;bAQCmA%L_b_*@Z3bu-8BzmD6mBr5J#j-($D`krzEPE+@1HzWHh1|=KbIwe~ z{)#m}Y08A3!)XVtR?2#x^xILX&pH^cd%!oLGjfRL-Ex?P9c!42?nN}4L39Xh zK(e3RS&V}oO$)pxX1r zvgpx!wIMVa40s}BwTs3L%g*BIZ8?6c>iZ+I6g<$EaQ)j4;o2U$`Bzjp?nJhp_|X#9 zszn!I%<#9ss|I9g=;8uU$1lHT)_DTX`jf9^@e0|Ny>0H z)y4+H+nj~cQt&iZJaQ7ZX3x{j9lTa)uc~_Y;ETHBc4FSnxI?m%me0bOzBvQSQZFYH z`tkWv;4l+HDBT)=_1mQJLIaUGo9R;;meKjHEzOYC+V`70rOD7Cpf^j7sNq5FJXn&hNTdDM^p}=2ZwHnqs5`9 zF$xr7i51v9?jGZu%#gGJ-FMuM@5w`bD9pL_F+I%l!g6usTZGX>n31{yKUcg!w=KpX zeku>cCm_JZDv2Y$GQhr4k{wHgw!QHw9pq+q0bGu9fC2Pwj-GK4kvTA;n}Qa&jmUq^ z_EovL&7#zWG^?+c9>r5sLt<`3kNVk^paXuK(A|}Xiy31mV#HS&Jcy2`{$Nhq{o7WN z>(YYr=s(Yj|bp*!Vu}L;cCE7SKK-wZ=IN96{UWRZ1 z13Du2`Trv9oPsO;*EZd;ZQHh!j%_d1A!ulcOl?T6Ic9FH8HSFE}_J!dp1pzF;!=W}r^h+3;V1 z9}5qAL4e&8K<0;MHi>;OHvDO2>&R(fn>zQ&oFut?N<#y&zDWhnPU__4=j>(}O;L`} zSr3_Vt|Y7)DSG!|vc+j9K>&z$(nl)aU>jP*3pLCXtfQt=yKo7KAB_|i7qzx;6XZQm z-E+wNdZ_$jHDK_p+2(W^^WEB?$9aP5IO)2--Vz#_OooU8UJ_=p&)FSsD z#(VT&P}x*wN=-f`X+-8RluCj&l0=}f0QHg$Bbx5}PB{gM2a%3r2$nOq zTkb(NSyKn~FB{(o@8Lb`v9whwJ{3goI&ScHqW^vH^iTaImPN)T`z1pph5gU49bi1sQecW9c z{s3!@4yB#ilZW6^pV(u-&~L>6>fyJjcSd<(cw8&s!#Q#xsvu@$e_aF~WC%RGRfTu}yuinr2%R5M#(95R%5}*r3yJ zUvCDxz6{rq%~URH9I?z`*scqI;38Jj7qGj>R{JHblMXR4({was|Bgwpj%l{-dX8c5 zBH9Z7rPk$aWGy>I!9;Bx^T5_h7LH3-|7Bz^K1d$7gb!r3Bg2jLVg(>Qb1pO3<9YXa zNat`$;2xN1d6is7aI7^`oSi9G>Z_3HeiV-C(V@wMdMQ7FzmVqDjmMt;#L1JhwC*eG zFsrj2>yMT|x6>IGYnRPG3TI8TXU!a6w%e`@k3UUYz<_JMc-HgIC6C0H78wue)Q66$ ze{_dxptI}ra?xedpgx}j-9~l6oyzNFe)}O?lv}!Ml1M&91@AZK`I?8^1TnsW__|&N z2a#i~1@wc+v3kZgWQK?IUSMbVPC99h?pwA?=UWwEA}Wf52hXLjd&JAv0Rcb>+$XH6 z3X#`SA!fJDE9{+73ZUT;{M`=+hoP)*#oNccIqSL@W}6FrSwd(NQcsAe^XKfZ{U%RP%W%-O`mSt3*v>I3w3C9_v(07hV+II+RE7fnQ!&4(8C) z+Ugbjz;>5N$eO(;l2Rz-u8i5qEX>|gY zX;}WjohLKW-V+J2eevXm1$)e>gJErL+?8mBs zs{=()J+kQPDoO%!Q4*~l!vHJE<5f z2pE#qrL^RaWKW4S3zR|&#n#&f&diI#&$|>tWWv|q!$G{zL0+eZUmr-N4U1Aa#moL# zc%1=Tx4D0!s#z9w<~n(iXw3%s{5K^P)@QdY{MW^qDC&QXQ2zua6*DI{GZ$AifQyYN zz}6OEZ2K=Nz@+B-4}3R_zY6XR338Asb2g1L;xenYxXpp$>p>#8>6|PuYlV32iF04HHvK3w&EO;+?tqrRtp#f%Py=IWd0weNZ{Rx064_-m}wI^*E(BF^O!2-uV8p zqx7G;dZ+yH_GI`8yVIq`Iup^ro6RxSRU}hC2N-rar&|1zH1z{`@-zmf4#!7+mW@K+Nub{YMae&cY2%Kn~`ug)4u_egkAZ z_j<@Q%)2jC_fIC+M+)t3TO*`y(&BDfP{a8fH-haMC+%N^r4|+CsMgexFMWD_w7QR- zv=gC#HlvQ{+kO%0;5+|16>j0RvR)n>xO*GsAUg9o$7%CDQcm_@x_79^SD!jrz47pZpMo-s%Kg2bsOMPs0nd({{n>_Qp2 z8r;3vHm~7W0MTpn)$rD!ui6?;sm$sV?Vq4S41EmC5j)J`(WcD3*+RBsZ2*T7?rS1-FAHAss{#BKvW;#f)NvN^=AE!(rV zEtyNx)AzBn-t54X+qvAH8lk?gZ1~mA(9O}dPs8jc#DYdbq+*<$^9!FwMQXlRhwCNb z0fa0@s#*3o6|mZe+4!QNP<~h&RRAR0ZJsC*Tzg7cozNAnPprIaBYN*B4HuzYedh<^ zQkidvw=DK@>5)cNdO^@a9aqGNF1c>}eR~H7xQ+Le;nTld&#U(ND7z!D+|f{LS{3TaEX)TJr&CkT_g7x;%dg{A&Qy zV=vG|H9X^HyP{1K6@X({Ao<&H2A_`GMt&!?PZ2@=odyM8MPiD+|F?$YcjAoBZf&%h zJtW>w%e}9GM21k>Guu||{em3*Fpsf0QD<;P6+qTxmt(_gV5}j`#iB4lLt4S0JgB^I zH#*2`jHb*9=w_B%wDO^Rmo@8nl3as(1^)oQmgl1EMC<9wZhz?441! zx}x!>w9!d%emdAU|Fy9yzpPuJMbIg~=2w~Qd)Z)vA*0@0??CeWieQ=xN)*qu9ruQ~ z#V~Ek3&ps@=pz3Ov$kq4$6avSSx4DaKFO=+p95$nj}IL$TKd?zZKIAaow;pGu)mzO z?-gnfo{CmX$Zjz$?LiKu*C$I6KnUJ-)c4p>OP}?IY($eJ&=(n6_dx#ljws4`dGgDkFmGp4d6!-<)rxwn3dbLDI%Lq;et(;;GLhZx9xnxXSVDR5n97)hyV{Gc3QpoZ_9;Nq?9gQ~`t-+@6TXWry_HKsHYtOoKjpp%U8#Qn)+M2>!ps(>+Szg+y!ij_Tr%PLr)0HrlugR7R&NPG)PJ ze|6;hpyoyiyOItv>gg@&ZJwo9@Hr7DVf(`ph3(8g)8q+fLp(pjto`>C`%e=r{#`YE zQHHep+SrOrJzgSWe?i_EL6)n-$)$x%sOWFs)JY_RYS!%4CpFN;oy&^22$bl(x9vFF zW%@4D=Th|X>WBYdv|iHgC;;p-@CJ6v;!YTz>l%PRPVJmOxE`}oIY|A+;*%qGZH1mQ zgbbvGT3)GaSlwx#-fs{omy`E^--7Bx zX|i#JNLG+(*}YXuO|&n$8D0ErEI~p$_wXj?Qi$2Y%YFGVF-f(*8^z&TK_Z_n>rT}| z=9pJ?<)9*OO#K~A?KCK9z zZs0v`%n z((0$D5eCsM-L+DM5*|^I$I7vY1&O*AX>ocD1LeDnl&4bP`-I@wapsn1ryvnyHRhP4 zRpG#;?7nPzeHCY+NP@`6gFDITAVJv-*n_EGtU47w@zA}bY*Lq8US7a{zi*%g-QFo8 zeQ-1d;Xqm!LV&Ws=k43vHs^!r(t6XV(x2_1g_K|7nJ*;(z|t=3cV zg{VXke9HIQEDAILx8XH1D4?AZ_d6Qd7o1MTGJLB2c(wfL_o6f?nJI+G;*DPCD>4`? zxaXlZ-In&Q8Yv{Q0TnIq33lTneen(L#{&&zS4r87WBI*a6G(X)NqL&!Vv^vZA1>qZ zoZuojL&AMZEHGh{s{YD}@ByU$9$C9xQRA7Z-i3)|ova>8Y)O!0RXx5+(LF3q9vb{3GA66*d*1t!ng@Nw4DSCBFVx)Z z?agde%*@?f%uN3cgOQ}NX^p0g{Hf3`Z5{uqW!DsCry4vb=2XYviGot790}gnxcN3e z>FQ$obYAb%{0!Y4!37(>6O6Ru@{GTC?j9BlK9u2NKIMBi$>nkctZ~=|;qULlrMJ}| zz;CXwRIeke%~Yx^yi&?6Rmyqv0o_aL?N=^EBWrRh%u#s}5Nu7#=~4}NNsa$)LSD-O zZ8Du4=L$59i3&SU@r94@)XcpVvogzu#Xm>b8iTWXQ}KDM3rl)z|DkBl2fC!py~ipG zR&?ryo2l$eI_>MPjT6Kun@peiu{X7bfYB_p<-TgFptdPSZ#V-}Z*fJ(RRnN+jyjD=I zNg0HPR(WHItxA-Pjk%@SU3@hMpK*yk{`J*ZV(l_fg9m2)gIG$Qv)3LBVZ$yrq`mj^ z@3fMU%aT;}pr!@A@OmGjL&4Cxc0c4;>CeBUPha^XY}BrA4f9#g^a|n4#dn$SsYk?> z&{z0W6;Wr;g$3z?Z5D)z$T+QQi2{E)4Y|z?j4O@4p&k_HgLVnlJL8I8Dv*XYVsKxP ziw~Hb;^l+O!>A!usnW4&Dk`)T_`OtaL=kFLRAP7Gx9#!f(CD|7HhM|fkwH&Q^kc08T0?k>!#Y{p7 zTgc=yxp?OxVdm*Ko1>dnlg-aIf#j8RgNwzwl98p|RMiBtk*V&?#VzJ8Dr(Xb=gCS5 z48+!tkLfq2L3EoEoqTh~?g=wJTfgxhAz`4bBYHiHekQw0#o|pW==Vorl2u>HJrFKb zxeHV0ekbX`~e=C|8zQ=D3AAeR1dvI$(9C8FfLN>AuDomk`7oj4Fp*T4r?}dJ7d3T z@5lW;=zJ)h%2&nHGK$K+3>2<7_%jDrlQ_Tlo3-F)kozt_lz)Qs7f$cCXb@Gm73y$c058rqLTy0k+g& zeq46BTE->w+Hb2fKY6=#0MF%k`F-dMPIO}Qioe5@jSnf=QkQ(!iLZyhq_EH2YbRKp zUz?Z?AY`IMMeH##O=)1c$H-v6f(>z6d|Fv#y(duHTz%>%NKxejZZ|o1*S{yZI+>1n zmZte8C&{dMTeOeI$wjHAH@J&M#M$7runa7`kx#g%A+XJL(q#-#VIG@G+shBx6=+Tu z!$i8V$VA>Q(*rPd`TKx4lym1Zdpa#A_FT(HI{gS7gic5#Jox!s%5(OUbKa|kScd>b zZkuQ!J=WRP^@&7`#TMLrJcEqm(k8tX(e_F2{<#8v)|*`23+*KUIYRSn)J?0E&DLoy z?KlVyp>_L~iFGv5=z#lIBw^^nx4Noxt!j`zo5NFM&s82G4nc>n!0yAN-HP!Bo%pW7 zT)ubq91`@RivOwBiDaWzIm8PyPRaMn-d#}vhX9omLs+Qj6|z$EhQu`tP!q_duGh~s zG!6o>t#V`M8>&pB#t7S8vFGC{JM?q|h9QLB0NKf~!iVuLH57A$1|g(=!}hM%$E5a- z+M{w~^DfcPkw%OH&jg#HB2lS&pzl~?zQUnjtX{C!@|=b3|JAs9%Jvbdzr*~{xW>Zx zF|@rhO~pit@dGHN_Kp^1QkZYb8l=5AKxV&NAE%U7vO`X98+~4$$1Gw7AXn{TFEFp{ zw$2=o+|amTde;pOE`-IS;anT~x2MD{68*1&Ts+hFHWk5?H z{{ht2)2rA?%LAr06}R*zz32*iH%wJ9mMBo!nO47fiUII}`LR5WrK?x2P|}-Z6O~RV zca2w|Cs5N)n!fTDiH1ErLrsJJSem1Pa z)p@#F$(+Iy zjs1?Kj4jCnqcEVmN zyqr5zs6!Xt<<(ej|DO&XeUdu4oc!Q4_=G_Y*c4bK%j$`U13eyA!K8${?GeyHDF$NK zpjLh>y-tMa(YhoiXy&yz>Ul6OvyphU2wVU@`(5iOIy|C5iQY6jQp(XZ7&SpC*@93r zGPAIzv1@9Pw@@Wc*rXUM(bQ~Zygf9_0-lR#*hRA&HE5>{0Vmy3y%_~OG|d#rnG5u5 z7D=Tf+00?ysIOWf5xI?nLFG|w31iqjmDB`6to=yVRTFJFXaq4l5u&D%O|QjumDoHp zhnT7F22s*rrATfZZgz7W|4r1@6YkU(+6c;h?pFVucB5@#wmHKvsF_-Z07p1a7I#oC zuX0fN$OD{UR0Um*4z?HrLO|U35zrXl!ax9iQ{!E18Tvk%)QWP@M<|S(n$pmT!GKB)6l=Z*f92mLz+X-0OhVVJCs*wk*zW&=t3^%dE zU4DHQx`>L>nsDl`(p2n3AD*q0r2Wgm2mq>kpffOkkOORF68b} zZ^83sJ#N9Zpt`Ql_qp;&zIUV%FQTCx%+eQtSw#gj4*x`hFiZx5MFahK2xEA?`2@Ov z&*&P7p=XwU;$0I5BN^z5N;?VK<|!q@iC@o$c+fUS6%m99OC z#7mc<0a>V_KI zIwNE*wHGL-ePYaLqc|k>PXImPcQZdD?51B@tl`WsXN(BFhWg(A-{8(ZaPq)hBS;6; z>1#A3kZFXQ{?5T@<34Yb=(Hw3ZVqR)WM+uN$~qu!A93>nZg5>I2Y4{QssYOmD`g}zEVd$JGB9G#TU24$PBxjF_ zg^rYeWKJu3-th(e{crR7e~P>!(8C18uZ<$)*G5tFKQssb+9m#9PBx_g_NKOG|H2M7 zsmrS3Ng{tj!f5zLL-Gw?juIkPDV*icxI>n)wp2o(ZJ`tYf^qo zA!I<g}!3-0z6!_^%n+0#n@4G2nWI$JXl1r-55oPmV5gYvZ0eZ z_<-eVqj=Hg8s-+}cC#gwx;wo6Vw**|TPseA{Je!0BcB1;w7xyNcsET^2A6Ji5*NMd z3TqFoyVrWW`t<4#9ouori!{iTVw05CE)$rVUS_~6P&{Mrt@*lW1)w!8v5|7pK;yey z=coKzN`E>odRHr8E!;%5jtOpln}xh{CWOSLTM=^d1e;avgX^125mTG3s^ALbfTQS0 z5@icLfD8OWz*j&y@#z^Y+d+1IYP}VGdQrIZ0vkj(~K`V6KmQT`9nKsc<`^J~P zan!9+HYJy4bW1y7+O5Y0MAbMu1$mo_wql7EAiRyzn9tG;bnLxLR4gXb7C;b~V3$;= ztgV$R5N^pM;PH0%FXN8!UgqWRc1P0^&XBSGk$aZrnI^`Z4kP^D&#*l*Bu+xIhLzWr}Y&3sDn}H`|-O zw8*@K%uFagp$E=F)~CzOfY>{_G=kEq#dT%Jc~$jy&iuWE{^d)43(U0Xv|5b&l5l^u zb4<&~^%_lGTL8V3jAWE;b*D<0u+TkSVfp)lm9qGF0Ns6JjL65H(Vx8;F7XOQOH=m8 zX@01;C`v!ih;C`z0527T-gWkQJ{eCaD_oqdA)h?36!v{~g{k7#&+q56=OBCVP$w2z z?fg^zuQ)pISc4`#?tU@E65AA(TE!0+y?^C^k|&0fl#V35nS>67zdnb)6i{ml~sBC`Qy`UXgCt!Tx3 z&dkTy^iHS26p9bIP0HJWqG1>XgMZj%I!W5tKA9HCNbCB<9|)JHH%fqQBvL}L4(b+P zt$bo0bHy=hk7Ok&a2o4pKOJJm(k2Y#Fl^clwY7TE0cdt!$kommj8ZK4TugdZlKZP(Xrjq(cRmcOTI- zF5z0NzjFpFNDG~GJEVv^`9^J}8ag%*SsZ41&_n$<2T8W9R?2l&&=n4;x`D}w&dP3w zo*j34p1D_7{fLcK$YY=ZXWM)&N62CA&|sihin_smQFyTieo3*pv}sj)0%F#6h^jIh zIc%f);+!gYPHD%P*|@t#W4qb8HRpiRNRw$=u?}<^>UUcaE;oyC0JoQUO`dwlNo12a zjT_@hak7IN7e~hk7s8q}y%vRirL!(pdk>Bqt#=l;2)cKZc`1eHvYe#f$eFkF0TQeIVZvs_+>>m5`%Kofdi@}>PXavrt&7)L`_A| z{AV56km0h$HT;U5m3D3O69q)C4?D+(O79ME940M_^w=7&rll0_)Aqb`U!hRIEL=NE<|@#k(r)k{I0R(gsI*z1`Hmn!A?;m z2t4fkv{`3A5^8Itd?NOO;yGf-XAFk)FeWuVg1y}OJ)&vE(4p|`choUf+8Hx=5{mhZ zCNdNB8XQQ4HX@v-Yn@^4cvixnG8w#Y>@0fRof$=w1XW_2tk`4AE&_XQEj+=cm zcp+&1M5T1jFCr`nIcX-?A0s0bTe5dV@&lOI z7IC`DmavMy__;@8x9~X^)`4t1jF@zEg6~g-LKsg}F3XbTUw!f$pc_fKCXCv1RBs`+ z*nTE7?%z(4*)Crd3NUc6WFISiD@#*Zh;}XNQQpJrW10;e$sVAL#|$kU`pAdUH6OR~ zO|qsA=*XuCcHXn=C96?`H}~hD+ieQL8pw&}KhbzcRi1guH~Ezr+DQt3OB5CwrP3yT zC+hCE9swB(Dm&w0V)YB^XR+jwM%H0_=xCR(I)~~<_&-?x^ zp5iid2Y>D3=Udl)hUI{6A~+xa!^IDn_Bi{%)czE$?h#d*P1IJ_{j-=!YM=1(ZAa1X zI-A*3kvak)&_BSwlOJ>&t@jE1dS93PBNHsMV_wW6hVHa!ZbAe<~E) zFBAs2@o_=<$&l)JNM7vQ;SlieqJ4+ftMrYp0qVoo0QLV#hy7R3QgLwm;tBujs^Nbw zT9QjL7O%rCsOOS~m;UT-gahnFq1gbqH zYFy$B%(9;ZYNrs){9#QrOc#vPv7}>oYEV{O6P=Z%p0mu*BG(5bJw+I;A}po?&Wt&` zy?S=UbirL&LSDbOPhHJlu=l*u%?<g!AVFAqY2q1)xuK$bpk%(_AHBoi65FJ)y0Yvc zhsi{0q&)#=L)Z#y$F#FpiGo=iHAj72od9kcYFtK?Z9KN#zU`f3wqCQ!19dJZy5nXY zwQb5SjTMid|BXlXPcdEo*<_#h#UsQ0LVEK3hhkdP!QS;t03~j3;$ZrJ5lAE@+9@vl zV^{|V#IV7D;1LEFM~|jcA|meQCL{eFk3aZrPjoNB%%QE~)NZ;Ncs$3Y_b)3ly9_jH z===E1RrjNoziF%M|K$~X4Gl+0&(G|{gG2e?JobH745?T?(FiYuo?(^CkPR!fMP~;8PSlK-=;Qhd zz2X(~LGnkr=}ASajC~_+2kXjF2VvJ~&2yj+AHU-RuU}fkAN!GOb}$b-`Dv6o^w35z z_8yh~n)dQyE;+Ge8iKivop8XV@v4$(ehy#{KaEAseB8|$q&>nLA35M4F0u!rI~H&P z1+U=5D+}{Jg4KQ8d5}jJ4Q`nf0IzE>$n4Yi-DTVXLZrIAeZr(9+3^x?`}m-XG$R1L zuyu^Nb5$j7J(kb@L?tCT@s_MQLUM>!&GqNLle)|2(2PHs)lB-D5Q!~qLAHk=-@045 zx?9B6XninUYk4RMTnlr5ZS1MFGbQh+!&nUij)bhph&h#zJT~X`O#a_d6J7%g(SFL) zMBtd|F3geQjAQkHG{c+WM)=15!pgwsBLwdR^GL-a`wK@r^2z&y51)^co9PmVU{!Ex zyy*{cHWysVeOdg-c?45oAs*&UQQSAnrV#sA5Q))jt6zRp4RFhr>{gJNaX>gdsx$k& z5P$eVY%nfnDP%hg$AnG}cj_U=cn{4;Gj}|FU|mI!AAt#6*g8pNNF6q3X8(~1cPvvK zXM0S;(kbueZ*7geq?I=TyVKZ}S=V2LV~?CwC-H+?_&%ZKg*i7`kOKngR4Q+R|L#${qn}n95d+Cm3=Ujfp9m z-hML3e)3|9|NGYS;~lNn?pbaCS%xN&Bb9qGN|--BBshFHi9s`3PhFiqU4l!GW@j{s zNt45ihoOj2wJv^m|2ZzC>bWkQN{zp4j|%s-@Fr*MP4)J+F~rVOLPn5oaY_nIC?VyT z{T;tYYS-&Dddsjk!ufD~9LMUO0)c5i3EjVJPZPJ^Q+>z=*O|J^Q-25#RwdddS#G@+ z{XE@qL#3}2cc`8^(XU4bd*4igUk5Gw$9G|iNe+yAg-lijJP6(A;FmNfdn&qqCcbf4 z*4;3(m7@O2t919Ul0R0v-o++#E1Sljt!K{)vvrsvC^8-q7|Q(T3$}&T=o`I(fr9`GZR!Wt@2eQH%!i8+Y#dDgr&=**gDI+_u50j-qr3qGi9KG34lV=xqvD}eDI1ULyXMm&X zr20BYh%(YFUi7kob?`F&nu@Hs#SCigX7i#d;@(V`qA(49lHIAs2OBHJV8S$*-uZ7 zQcpu5l!`Sr5c`~RbKJ|NC_QrY-10I;lw)2exoBPz@{7t<4ct=1@_0CV1`}Mk0V4P3 zkd8bVivU}$?Ff#qEiit}-oza+57IaifuPPm5IZPlznp;&p;k*X7%p@q>T}%cV1$-JA@% z2veY0O7!WGZdo5yXcg+>(b29*TWrN2Wa(+PVOq4H?9@#7&b^OWcqT#hPMv?&9q=~Mj@rTG8DIja zC5y1TF4|yO6n66|;Fs2V%?~8?brh2%C~+0jXDi0m)%k(mz80pB*Uz3=`BjtPabB`3 z6g`zjwl1%K_zXXw2kpC)5dw;SGnyVCAC8-bX8QGIB`~$RJVSUWpl`J(`D1(BKI#8t zjRY6!4Q=C~evtznpYX?x8;X&p^aL?NR7i~_(Q1~x_ssfXzB#}A$a_O&hro)9_CL9m zR8(mDz2%Zx@FkNS39sL;~xZux?(j6pntByRP?8NsG_zHvN0uJl2f;in=yK``8m@dW6X-?am^^-Wn3% zw!uXdUohu3=-MOs_~t&C*_-9YuXcru|7(b`=5y@ibh z7SD(sA0iQ!ExkPwzk}_sIGPC)5L;G!=}bO$|LM)Pka?@`_jB}=V!CZ7Zo}-X(FggZ zLqffn%h;A}MmHTQCP@>enVRZNF1w#Ks2*R^YEI^N=9VjJzmLG5`x%y^ped&+c~_Mh(Z zdhJ0Hf;D2?EZX%wciFY#-|zQ&4ucaKY5;vrgPY#<0{Qsb`Js2_?uaAcKGHlFLF#uJ z&M{-Yv6nt4_4;V{)o`H0*&pw6!TMJK>N%dX0=I3b>mX~WJ=F&SmUcKGe#|gW;=U^n zoxyI+FzY*Jg>b3AOZVk)ObiNQ{7}BuL}&niP7ds`N@eJ6!FbmfZ9+aKKyYD@xU%*{ zy0UrxMr6(Z9vb+=T8nx{XzrTjvuJOdR&cf!JBRi;#`CWxzu-QUFv;9C{$U$+4y>M< zlsIm3{2pbJ9;_8?8wdN1cVPv)gi@s2EH`cq0771=R7~WGPA&WHu#>De)MO*jc{K*K8@1$! zN@h@#{(LDlia^T@^2}g4kT<#=sT()WB7!F;gH|U2^M)5uo_+!nZi@7nWtj7eQSggw zM$g6pwNd)Aj!jVSBo6fUxIq^M#QF!3HX-|U-$$6N*04FPvu+gbLpuEnWa|+jBbDfz zzF!z)+H^WNPOxo)2mzAOsItqdi9*H>Oyu?_T2Z1_8`aYqoz*kMb$Q!!vACS~$r6VnlmS2BsMW zD1S-ka-lY=TQi9^v5z(e9Z=L3in*Ph$O&=E6CO!Zj~fwNL9wJ3 zYx!CozZeuuUPI-xproW-%FZGPH*H|W;L?DZ&K3J^B}b1nibsIveUFS7XqGEE90@{! zS}rBETCx@q_G^u};H>6BX4Tp2I)iT*=wKW%49^%4bZ~51hX3t!Ya(1ph3q!RJ#eV8 zRXeO2a=VIO=helD`(>vK^D(6DQ7mfhE$`s4AR`AOGe~;IwmXA@jNE7@G04sh7s(h{ z!Z1ZMYg36R24=h3u0)NW5Qd4dWO%iCQibTfDn)~PRzX3!i_ubs{Dx4rqX5Q8Wz5XP)|m05*(@J z=Cjh$hrFwco`jqtSRyTqVMJlC3sE1TgPxl)uGm7AW=|f)8z&T@96m41drkD$5D3EIpQ0Ab-g%!>? z6Sjoz7f)*uA}`wb>Lg{5k%|HzS|#)L-K+~K33QzTM5s<0LDyaNg6v>9BKn4Sy8|O& z7PaLU7S{1%!eDt!&&(^1X4=CTtYNhwjKwvUq|Xs?bfQ?LqNh~AL*@(d88<9Kc;GB9 z9J17v2P*H_a56#^7U~F69KpELqeX#5E5cgOJb3T~DZ9?qtOX}DuIGao_|a`0&;*)$ zwEeqemHNEZ!yK8htV{TEvGJ0|T7#oeiY1sjqYp;fy$;Z!>Sk2UN+C)vGA*v`IjQrI zpaASRI@9NYOvHTO1FhPwrr?{9*>XDggpS{i3nU||g`9KDdtTA#Zh%k z%QOq+^C_kBdG1-fmPsSSc=+*@ZG6&vCC$`Rb~qAd`OdVBIwc7wrqx{<#fjPv3t1Wz zu73(Sg5qs4^h#7u)Up)zp@5&Kh=JV8Mp;X9IIvaoqq+9M@4OI}T}E}ndASN*gNrY} zXEBfU%6Kh*dv|!JAOSB9K?f^UOGn8M)Af4li;D8PMN62o%9X@Q*hbOk>V;!o`Ng>` z_Fno?M42u2hposojaY<$YgEfR(R{Kc@`C_&1%;P6`rL7*Vum(zgenB*di>wPMCxwRC(pp|ik)sKuU*jW6!Xe)~5tl+Y$=6PHhWw|TUki)Y|=3g4s zOW;mKvbyE4Xd2~9YG#ID?UqkbQ5=e*^+UR3Q4pm#d@>2}wPp7Ma?zW7)kjv)0XEqB zW{=gnm~bf+0$Rz5jm2^$vR0v?F9Be^s-K%E_Nnl#DIeF(!1IL8N`y@op-3aHC(-1F;dC6U{WM zb(!8`!){fH^)?$>?GDAK=${hu%l@n`%xx(zt89PQ$}Dog!Ku)Jx~Rvu;f_n;ps&$g z+}N#EJox$%1r2|hYIFK3hBzqb#W5t57+^9NQp_Sfg7=q+9Y+FG#RmbOEee&h+lX4L znb>i?vfinwt)4qk(w;Ss^>4?0?N$J#-CJh-VJGt~yyUSRm6pE6Ynu|Cua0dPR9hY& z?Z>zEe`FI1SPMfG4I}dd4 zB@wXj)*!FCB4Qk;LRlr--P>kQ(z#e8j}OMh#lFY+%nsHq)I#e)Oi$&SFivnL1Do^g z*OukN&Y~Vc#TSX0i;Lc5`B>19hoxq+Il=;!;q=NCNefxRS`(8|E#Xm#drCE1&v{@iGYa`!t` z;9>|)QpT^fmD$;L_sg}mRfRffxuf+HHR2~jR^5cCtVt>CYWC2Mq4yQ)l#zSgkPx}R zJi8UCOO@1{$hZx#S{Sd7c5J#vg=Uk`<0bx6(LTe7REgUlIcOdmt8KW*`*2@V53JiU zMr3^)7Lob`CkM`j6zV`6EGc7mRH9e}r&yF>JG@S$Q^F$70sA(|>R{uM z22LigQR)=ePAJ^n{%FyfKDSVx!3F-ijC4QFtPD3SgM+AA!&NhN zM8?UD(Rcy(3ua$F1(%(Qx>|iMNI9<*O}hcwNU!MB`-qa z>$pzzw;}C;tV?G;oqf3|w|IzA*2xKi4gq-ZWGYzjwx;F#4MR4Z6b&*I)s1-ZTx|+@ ziM$!L^JDlvG&bHCzm(6aS#hx#Ci|vXDbrw}uwDZJD09(xz#VCyNUQr0F5IY)6f((s zqS?g3JBc|Nq`FAIsO{{|wQQ|R*SoB@F=NM+VG?enV=&~ub=%=^-&w!kw|+G4BTr_ndfgs-urJ-HnxjvPtrFceLvHUHsmBkk0bX0sGl-?sJ2hA8 z_rGXqa{^V`@Wg#$mqXvfSNSs;LLvO9D@*qPN%)u#!evt5FX+9&tEb+<8>jqR*ZJU3 zXK--mx3p7xwL*rQo=kj+D4CsEkfF)Z=W4~^s;;D2{l*t$H2|+TE_^#VEySp$=^<(E;#)4 zNOzYOMR#=(IpZt=IUt+^-g1MGa+1g{vk~(sM>Qyej+7mPrfia{KUp@SxL<2gga)-e zQ8KO}IrsH#`{XBz`dKOMhZ%kb(Nn=nwK6G+^as4OCIJL^T9OECJ6<_YbJub9C1~^m z3UPIXlaO|)KPRt%fo~K?j5yTBkD!r&Pmw?x%*R}C6DO!k(`b*;S>hp_m-kMx{1EajOXMeo|li# zb8K@JLv7y)(NZ2Occ&^(bquEXf^<;DJ_CFoIvG^04Vs<(;g;~_c>je($9($3m@i8s ztcRo*pITca+}FBJ38|DTl<9sD{G1rztd?rxceM4LAY6(7jI5!_z{xP5nYoxGj%sin zuS;2ubV=FVs)VlPsl4r7<#5eO!rni=%&zezBan)Zt+uuEs+PZOJiAnU9K$6W`c#9f z&ikxH^mvSn;(L6s%xCMFj zBl3^`s>WR`K>T_3_^llVZX;n6?Rvk1nGEEI1*kx!L9XYAfvuGBg}5V=_Zy109COuu z={s-l5&l*%X6SUBzShV!f6N~M#uR#*kaR5@3`e`<59ry=nIJ;FK7s1J=Wm=xfG3MX zoIvHn1v8D;V$-<}8gp6I17eJmEmM zHh)0M4OnLw)eT5?KL0rxfubr+c3zri^3gtzPDqX_p1-ioHE32;%87NS+|ynQ`_Jup zG|yoFAkEV+uEX@c+bhRb{`Rh&H{8_>lxL3M5R_LC(jDbaK_{wjre=hqDd=YKO=k$) z_OQo}Ib-lMqfN;j@4EJ_!25+NzJ(uJnTaM!2x0Fp(iiMMCe(@|KC{zpy78J}q0hP? z8grqQcLNiPk~Q^yJ-{%W!W6B)l6kd)Idu?($Bq0s*`|2>ea^Ne;JUlgmh>;9$>4e| z;v&bpG|w+DWL9rPswo?!chotZ2fBNCBK}YGH}6RQ=V8GL>8}RWueD1h%KyiQ{9ijd zc`JJ>JAm!KpQ>kRX=|cOqJP3-dly?_%$61?v!I$eETkecOMFKsB?bZAi(k1%XRDHb zTG<4%#H_#aF?N23@Z!j(#+_n08VY?c=sY;kcw$DJg5+mjRCl|ox~g`I_#cd&Q*@?L zl%^}TZQHhOTNT@ODzRHo0H$7M9yE~WPUi&=n^9FvsKavEA zy>bM9W+tjcxEjk(b0@%|GRk(60~Byj=Oykj8Knx1WhE)xLng_DqQxfa203+_DeKHkQHr_b%l^R`=Fwc}V>>k$^b z@Zz~3pd4lc42R>pY!a+{^v}^IJyP0FT!yLVcyw85Ko6y9P;@FOK`DV+^2RLxTx;T! zva2)w4^3>L_&Z#xAfC|%BhgSlz{?^*wzD{a@C;#!@X)L5K*B)DSUhlryWaT}Qd;ci z)3$(cVf+(o#oQDZKDeGl_$)<;@I8r7nN%EG0_{9knq=`Ww>fJ>qq2hFPO=X{G?_#; z+%D98im1{hC*G_4h6RKIKX0sM31pVkjTrK6SyY$hm?DM*=7X59n#kGY(OC7(-n_DeZEFAaML4809VgNbeZ_M&@I8{M>6Wwi0k-Q-2-Ag(GtB~X zTofZixhrk6?Y!wuotSuQ<=z#+A(dK-!6M-|{-&lQZ9b5iH}XMp!@jVX_kuXib+jkO zG#Z{b*)fs_lhezL05Aw5AQ=o%%mZ4{Oa(0NqO`L}3i_vLX$P5$!9i(b=#30U)&ffUy;=hWRILii>!W z#yvy7+Py%4@;zBVakp~oB6BMyGGV73cb#0gi11&XAq5nb%WT#1+r-1qFq^aNOTdi$nMFwxoheTy3IPa?$3*3oQ z0f2wY9JfUWTM@l3po{`JJfaJuRompu1DoQ?5>bjI1icd`dJv0i#>QrFS~4`7eQAO@ z_Smr{o#`OiUUi?a0y%$J0AVjpS6*(}t0SxXy|V$836G3T}f!)W24wTaUB(n@7%;HzBD{ ze8Ok~#fY+I&e|I8aW0M~#>8DE5C=mZIb66_Lp~-Ry?;Cmy@LD=&Mh}RAYR>GlJaFp zY3a7&05wORyclij1z`+Ka>j4?OKC8fX%9lRDDgU~#pUBy!gxsjkcLJJSK*W`zkSxz z3I7?Im?Sn|N>H~BrtE#BlJRc) zaB`UZ+wss#DIIV{h%6@Glp^LG(&agX&r$e>QxW~QQ5l+ACn(x?*bUyLjixKQw zRHu%}1s`Xm+mk;FZ%#zFSdDuz5}l%>h+kbPOi1_USVH?ZVuVM6*F)e&gy@YKbl<%} z5#ra60ORWyAU8khAzbcf3@IVTKl`TuF|aU_3GOvIa-vr|3QwG$5m-Z-kNm*Fls~%B z=LM?-_ZlAL2>Xf|LRLnB6D*LO4?m;BBUl}Jq{~}p9?P6Zz9!GQTSBpe9k096*8Z8B z{{?57w`XwG&8UBXCD6mPEi|gU(IT=>9pPG@4f|3VE;E~-nLRscp{Rw8nL*IdJWye# zO2Fpcyx5*CzFV^zYg12|AQ}e-Kd;CjGTh?38oQh$heYQyz@JMRJs=e`Qw5@<_4Wd~ zWF21(DW+$QT~eNfP;+5Ni?38AI}YE0gjdcuG54IyBYUvcX;-aVt~XDv8m&k;J3rlB zAfbrtYbv9Dbxe=+lbegR_#9r2&WxT>wYKA`ySHoP-;Y2_ zlZ=dBHV_i2mXry8-#E+$d5%0)0lqj{2EF}OWdS2{%j-0 z{ugwOE_yB?iI4;OY8Je0=nGjliCT=&zo8L{tkpCW*itR^&V@QPz8kiqYAVBa8bY|r zPyTc^Yo7R9E|KW{bs>b2sa0w27Nvs4U|Y-}Yj0&O#HDSkf0mbII}yFIriSsC4WA{_ zLyorK)I_Q2^>BIM##g9trF2ZTN1Yc<2z5Nd0Xayzr|+jLg0sarl|LEKu1=E#-%mIzJ15-Y`NVaG(K<0Ib)tP$R$wXaE`DUvMh);0OuSF!f&Q zLuNSZJiX8aOu$h8#^tumIIY|KH!h5q%y7X2RybPxJtxa-9w}tSpI&-EgICy&9L#G@ z=)jRiw)r&6ev+Enx@)%yZ*p+Ek)-k*cogBPTRy!#c_u?YX%cb57m=U0!tj-sVzJ=} zxF#73lq>8k1q+v9S|sUmrMiQ*3E?TlxyFD==&wTkxuU{7^|a7IKhzZMx>1X!tJa#r;LzQ|q!|m2yjD3oL{o7FQYvX_()KYnud%Qx5DxPMh&i@(2iaEe zn3;tsr>?>(h?^Itp$-=dHYM&UY+0x3Gs#H=B}o~Za#@gN@|B%~miW_R4qAd`)AP$j z1P(^#fzi`uZ_t_yZ7{xQP$7%rwt9$7q|oHCdUPG^VZ*1c;pZimx|(t$vMIP7W` z?&|DN9G>|iEIDn^!x#`jpz5@=dE&`ob3v?EUI;?q;S)|hCIF?F|$1lD==%d(mX!rw)bRCJ%AB4ZhUNse{ukuI~0H7Hcf*2+YNhzD<7 zI7hmphU8(FfTdLvh}=%p-2fa-Dh*}m$7%)5QW1vPFnsH{wKIu8inQ<3w#yW+k=Zg}#N+`%^PxMOC6)wf=jpy9Laa9sWRAfl^Ayd6!1C3wU(A z*>+9Y75!BfDQs@WVu!WXnvE3X9X90m$xsP*;P`6lS7UX9rjV+7#!P8jD6h$9W19ve zY+8)gxhtbM6z@NAHI+iq*cRKmKu_ysX;$wGIzb7Ofqzs>Vt z6W%-lrYBQf%VA*~mf|u$p5;oQaI<+Mp<|H})n8;X#^njaQl%)Xh1)P&tF3l;jx7O}UgQt1>%>>ULGJ~C(*A)3^>r+Ph?8r7HM(nYXSH=Ahss>E?S`uIS|GR zvDkyjPb|P1zYXbbthOkKDq-uWl8ofn-5Iy;m=6o_G2kFPJV=@8l*JS2)IQ8IW^&vx zu*lHvN!_%wDs)KBOsS76;csLE&*4r~IDb6Q5dUWI5EIKeZ@)J$*i}0UM_8cl^B#x~ z?#w8PJxs%$u>40;_6{O7y940Yn@1^tqXR!845}qOq#+IDjRg|&ZvBdB#G|>E!c|Jj zw=C3w=}%Jihy8p)Qy9cCbN&&6EOQ5AW(`f4Ou&$fYc?U+hggyHDZ;xK_h#ms=(m%= zlOCGjw=0rwAp9foo_k7PTKCE+ksrSlb$+bm_>5S;*DGxJ_a5`Zk|QR66!)8v*w@ZU z2Mw9Hj4pCvMCFermA{fJ>^Izl=_@c$F|JggJ28bWdaVi+KV2~Gws{$w-AZA3xl4JV z4MG=8r_=N;XkfPCB_<3~7h~OTi&HIRhBf_ImfU2s!q&6lCSwzneKljgH(_QQitGrm zoFQ|+Abg$t)Li8Vha%|?d7+bf6FRd!hIoF3!Rw(MzxQdgv&VD2Vx2XSA5NQE6H>_& zi*rTa-((AyK-h@7p<(e1dd^^PS;a@rfEFV(z_dEG%r zn!G`&SdX1mM+L&oMs4954u|DrKl#GmEv>zdO2gN2V*#b8wkzQK>ge2UqsN<9bs#Sy z^qVnhVkx;OyhavMg7NJUV7E7^hk0J1i*7D}{8(b)tF+3owaf@x=&TJB5@UB72jYCZ zDnBkYu&yv!SGx8?PW|{DXMy&lbUx{Yyi#j;C?~A^@jWk?7ohp>1b$}$!w?vKs6j1+ z_j()@1GZUJE)=vYrAz1{L#Z_x$-Y>(Cjhk@f^;vM3i>SPd|!Es3Lx4u7wTYf!|h~K zO8^xQ)@H}C|H~6q{j`O7Kg<)Y^zS|Feux}Q@P{Bf`YV@OZSCNbAuziahYs%0muM?6 zu<1$iJoa0UoImuGBqC75Zt_Bif%EuxLp(y)XT`+xVXS~v;17`fSRI(-9_im;>EjHq zg=I%#VJ$P~k0W89Cxb%&PghzJFhbd#;NP6A|39yE{_ieF)ZN<7?EheeZTc{N>T7F4 zXZu;b$0tc~b1=j;$it|@BKXJ%AmrKN^2FF&_>^L-$!5c|;6r^HRqJiKHL5RVovQ?> zZq*t!i@JJk?zU|XZFO~;wzh4WH3vMN`?Ys{GU3mUFEf2Fd!F;(yS@iqFZ8YtSq+5V z@IOwYwK#fZphk1ty^pTkUBSq#t8Ixr^XEQcjv~Cx;i6be7iam zV&1#Ff?}Y$ydq=raBr85*=KO$K6lv$>-KhiGGnAVBb^_}7zs~+EQ|rOV@LY@Lt`oW zAE7Z7onF~70{Yh{zzY43*ci*sS01bdmj^h;fGU71@9aoU%!9X|$b=D~0q#M9z-Tyw z!%re2%*&Oezxv4WzN4410Rwm?;e`FAIKt&teXwdw(_aSI`M8gdADHm$dV)gut~@HZ z?-TuKi4Polc>{!qe$Z_YT{eOW0fCPCe)^GtOrwE~kxJUPLU0c&q95`Tfq=7*uLFHQ zl~5yKVDvrRN4Ip)ASp>GJt`xB5fws|4mGS;8m$|VD#ir2f*E#wy{c4eF+}0!*v10j! zv^*+1vvkDzAz~^!vv?#+-a81<(p5ks!g16{apm7K{A`S<+*M_63|?V&6(GtI6T#Ob zy&B1romn$NVPcrXt&Cq9^~S5uh-n4{isRNt zYvZ-UlH*XR7;!hzDblWw!rOfTib~_%9iH=1z^@v)+2xl-L*U&Vnv+yuzi{H> zN?0GICAd8~Wy!W$2Y@)VTbZ;W@^y7dqSf)jJGs_T#;*ZT9NMjc+ysOtrw`fOA`uhb zAd0I1nR=yV08RY{MpQ~zg1UnGWusIr6MBF_YfKO$XCxvcFPwxjTE+Q2KAC-~t{XC%ZFXh7%|LXneHT*JoM9sH`BWJw=KA3&M?7|DIDwzT3D-QI{D z-7r*n9Vc4c*KhCFx-fSq2{ts@sy6dSQGR~5bZO1$=9?>`PS)5U@t=B%;{)u-`ujH+ zPr6|fC}atZmBSreXsGS0O1i@YNHGDN-Ox%jQWkMAy<+Gzgo;cx)Wsva`5I}_<2j*7i|XfM+4l&m#zbX* zj12$kmoB8heU<Gmrno6pIN3Kk%6&Si((A8jQGFK;vvd2t!DmK#he9*wc@WabNXyN9P^vL$ zmaT+wbq!;Kt2o#8-Q)qDt~*$@hHY0oVW)<-Z4nxZ+dtTi6-a3BQn4!d>!+>$V=*k}#Kl(7rG?F8rb@z#00TA|AxOb#a&pPSQ)Lb%kzu zPMHe_UdRy&cX7hl1;fYouQWd~eCARBZ@v>E2jMWCM>=%%fmX*$%4W zlI(DVW16~DUfqxYtCynY=+T52z7!`r8#=&<2G0yRRZn-;ro-SNuCaCg;K8o0l4Bn4 z*l9kYv)N#;HS8!i_kQf&CQIk4lcXlrcxn3Q99~bryuQ7W6{& z^Gz*ljUI>NYiy|D|HX_!lezju@`z(#;W^*7t+?#6cp?-FIzUszO0n%DO?XDV?;?9D z6pE4*#KA3m&*!(Hk0S9kOve_=Whsfqx#9s!^=!YLZZ+(^-6fj%5tcv&s7XuvYio1+JNIi-acmy>5!vJnM~c>dytT zhj;xbchuA}cc1?a#h`Tar?kdHJgu*x9SJVeTDaqxGWOiZ7p1GW-=7(!|M|H!XaOG2 zzCTGVv=b>`g8A~U?U5``+(|rzW!j5)`;=W;E;siZI|6<#-^B#X%qJe*vb(*h(7ZNkyqNq%ZQ#uhf?_%Ff_%=h7 zlWb(0lB9cbPL@Mc^+Ay@2yG_FQ~Pbx@6Nf^!1hR}U;k%AFc+~3V18n%=%Nn36FMuH z8+QGs=fZ7%0}>eNsaC?YiT15x)#pwQ%O~vknJ2D^7GZJ88{mN(%LcZUbz91B`|P89 zixQ>Mzmrj4syo4ScBH-(Z!m&gPvC2$4I4 zuuV{R&qeQ+QDbsDFRif)m$7R0CgNad#UjVVQ=E+yw$qp%lg@p{GqXFUveXvp#MkcY z^sU^5v>P78rQxyGnBmnmp?@7%QQ z9jMokgXA)4jwFwGZTOm|-kErSp~TV{Gu#SqN-DxC3FeX>$;jp5yTDp2T0Vho}mEiH;NJ9>&iqlDXqcmfvXg~X*(0v++(aQcP=qfz@ zC@C09*e^y#Ngy?t6x$$quKadBu9`jf_0F`|viK;n%2GwtTsXHzVn7d-`a-U9T~r-p ze!>p2#c4_<`9!KUX$zV-ibAeMQw*w^|PO{ z-!`&l^ab9zt#cfCBifd|X%G1TYr`7oQ}4Jq8mE6y8kN)4FM-zK;-5q5aP}7}dvoZC zK(U9?oN_a`OddiBs0gMs!Dbawv-HMIWPAvsT^rToMRChHiZmvW-MNAM67`Po5afky ze7P8`xQ~OBB!X@7F?-C1a>|$aS{j}m%*sZ+}t&boq<>~M17>g9)FCCiRdg( zQ)j{hP{tbmGjvS8BFrE5RkRTJhUwC}>K80fYXxS)HM~p0x{|=DPx*TR9{XLa3|1{|i$8QW3;AgVzi_#m?$PL~A6I#H_pan}p zx2xpz6{~nv)-S#2XnKVn3|omV!Cx%8NCX}6iY0Hk4z7aP(w}d^T(g7qBG>U6Ho zOL~PacZxj85c%rg_e%shTfW)c_#j+9)OaYK&?RpUZQR?JI@3oYXQ&~J>-B6d&vg=c zJ*cv_wv}1&Z`LehQn-cJ8{$`77QCzwl-<#fh&0&@i-}X_0qz`Dpg@0ps%cr2CH#Zy zvwF=~3pz9M%Qg3l-HGL^LbVY#FayY=!0#A|HQE6GVEaHS`jB0|y}tc7e7g)`21U{m z%C&q($-%NtG&c=Z&onY)bS9L!D7uSsHcue1o5av~u^D}K=}M_cE&5M{iP2+3Xwrp< z?MEN1F9&Iq<%1k66SwlAbOSzi0@=hGXSXiFCaQrLf+`0CwvMbk9*vc3MByvr?l5J` z1d+0^`PRz9wdz3*fvVOoXH+oqOKJo@wBVcJyJpEV1w3PnpD z6z(kJ8X60xOz}q9EO&dGeBJ4C^g0>5uva;{nj4&JNpqQw1w%7&jgwQ;9AWt8yvODQ zJksOI`su5zqB$|-5ohwLi19x=+*eRsjEEjiQ}bgylN9dL=W`y%CrNn5PJbF4V{@6; zW;}65hajF<=pCQSO>AY078%x1Uzy(yd0~<;EI(8Y9Ic zzif`@X&lZ~Lc7?Rp_%Uk=|L8Phe!p7WiXwy_qt-K4Xp<+;|k)n-X* zKyyv~wX$5$T85{ixk--he$uPC*nrVFIPjXBH{PAPebEQcO1gdV)QH*OkC1Xs?nYjC zfK1eB|3hH;XvlsSi8a>uLMhLIJ?>>*sb)^AS$qzfndfX^#V@PJ1l;Efw9^x-47Lw**_CD?gMMl7qEJ7pH9P3H)CIs)Z1dyQQm2PzuLfR1av@z6|Rm{r%H3q|f%$5w5Ba?VN9PIM9F@1=$cZ z2#5F;E5HWkE6HeWAZ$bZllMb$&2ccNIsk58^NH`?S7QJ!Q2B`npi~{OQ2PxZsPgoF z|Eu~NmMFP3LH*tF&x`Z==KA~0-RBR-bWq;sc>=@@; zz*gvg-l1(mA^xl_h4&yPG=tK#0(r;!<$wEQ9C!Zbqw_BUZ52-J?!h<*DCT`mcn@aA z)}T0mQX+z;>`(LOpR!We*T}xi9T2t2gX7#m3hYC@os$mP-4NG5KyxmCv}byNg%Cu` zT@OYx{B(u3t0&uphVZ~9bbzwZ9z-NzZWw_&bd4UeajqiRWQN;R1+8)YaU>Q$bm^2u zpdAHqO$}=6^y4Tcc_{q|mhWF7-zF}ot^1Foqx7NpMkukNRb6Gcr{6_j|AaG z20xNB*|-aecE8?=q97Y@g<3zP$x{_-*wbE;nBa4w0kCV z+aP`0Aag56{&FI6OlUbE_1tlOtXC@OxO1CybVD+(M?9Giglb2o+7y#-6!w!vF4z*y zjilPLuaClvY=8#ZM&wP`lQvBA@^2y9sd8pggLW(u3~m~kF&BIuQ|b*)!_*FYxLhqc za^=%tMmB-7%p13~7)>2nPXrNcf?zh%fO`au9B;x{G$L#oLvgNwYKr}I#UI>Ev1n9$ zPs_Fd;maJv+^$zoqz{?%s-{PdKohSU@!Bjod$1d2Fh(N0N-t*0G1+62^1%PH z1nT_RAIl(w8tdE#Il@Of!h2OfGcB>Mr%dT`5fE;-cG%UOBeQisWXzlqU?U6<0k2gU z^Pe`1+8Ly9sHnRAA+`%rGG@Rmvztfh<)V6T#*ei?HpFYB#09~`vC&xT!!htb#YO67cRRKDS8yl8ctX876Ymy66-L`xwrUkggIJj$R8=Lv+$RN8h%I^ zdz2V%<%m^o7gmKzNu~9=A$J0DYFPZvnU{> zaa2!uALiMfpV3inss?86End58;U77_ zh|RPycbT}4;jW!H--WM3KoD+B1_x+gId@56x;>9Hjb3yQiMxyy>_u62klK~5GaHs< zcc#+7&@d(gRaO5${foPm3P4Yc)o>CTM{S%qS84DX&K0IY73C`_*J!8fkD;y6ru%Xv zv|~xCwV3ds3qezPw-J2!kP>z z>+P00kn2yytu+KxIAYzAmU=YwH-@gpt$`vb)D6JTg8PE>s$;Qw*ITIVU)! zd4|7=P+Px!{Y&s1dIVpRi_J-i-JLn4rmuNV4ZdRr->t(Wi72cjmqQ0hqB^*r&gluy zsY18Atce@~-B!$C&py-7nIQNhM{gU}^q=9}!TKVi&NeFW`$qYcB}ur(#i!{l@|406^jrPjO#yAE20-`k{^G`-H7O+}cW>L7NQ*WYRf^@Qo1| zi@s%j5~#TTmBiASyWtaE;q{~cXw5TmrMUH{IDEa;Bl=x99n@jZt`TZT&35LcuVJ2a zxjmJX6P3JzsNqdnS(jv#MYV0(y-h%0M8}OsfY|4O*5^teWSR!MmMK!~GyD!121{qW zOr_A?khaz`7O!N( z&r!Ypkg5FQ=av26*w;mG3#DsxPpMwh{3qi2El^%{wjRPmA&nVvlZ4ckHN$ftuQUbq zVD~MhZCH3`5#CaQ3FRPfOpzdU%#Xxl^SI}&^Dr#mZ$_r6d!mzqL`S; z`$r>RaXXTgT+_w*wPbshyYE#m#_7t!H>O23fW+0`;(6QyhxVUFDN=^)}wsHIfd_1msGt zYf~-yPrvQ7Qurs_d-*p`QqH<~|M>?LTyn1Py^CyfKH9HHy_2qZyvwdA-V8PzPc&{J zep9}fc(?Ru?gIr?_iqg;0uK$zJUf*VJv;eM3~m*E8+t(BgJwd)6~PU}Vf})`HD)lB zXW(j0G@X*!2X#T8zDLa!W}KN&uEU4oik*0%U7`iRaHBDe%}5h$3`;+)O8{g@3>871 znORUMfu8rUE>64}Ms!acRDX|?V0gWioA3UB=fRWzy(jAxIq`Q>iMKpVdo`d=e;KmE zFuweapt?b36{%yv++t5vgGI>@_0L)%2{TysU7hT#SXtEZtY zI~%t(AX68t|8eKmrOv8A7Uv7mKzh)S=yq#KE6IMU^4N{aRR8OZp~7*2rqDK zc9aYEd_}5<9cbEn>^!Z)u*|R>Gf!XSyI}(NPBc%5dxu?uQ30=n=$GjS5R5yxL&IJpLZZZ!cTnv}BuEwl zBGm`L559&DG*qN*k8Xi%=D5_oeo{*$7mku{d)+F+jQrZRnb}lpCM_q) zk%uNu2ld0NW|Q#XDu0V{ZYTc}p|-ErZMb;0-aoNG_Hy>V$x3SGNRz_E<_t2UYneH9_|A=TlGwe@u!Eyg12 z{a-yTaVAS`46QD_z<8|zBTWGZs$}YZe{IohHCn{qkKCk`Mc>%HwP?-c+FJDJTg(F~ zIZ>fDQugYTafzWnw6s?xIArMt1x+5=EY>&-rfdJM79Yr`R*i8O;uPK{2lM~EIcb2( zvJ@*LRtb%wR&Es@++`{eYgR8wXP?S3;u2O$?9I#@tT$AA2~W|UWd3~a(MBb(+ss>_ zRm{Nf^3~k5RB$$~xtVX{MX0Of1gkV(>_ZjYWwlzkk%iE7bb)k&!y!|O2N$(M5xVxf z@OaqSD%{n#&4Ci!Y;bnED+jtllaz6SqC<@M>U!fs6(8jJJRcDA_9uM&s4p7~Op_6f zLo)~T=Taw@X-;Su{+=YSX`<+T#JelG5-#3ctqh)KscL#1*5BdlKmDY8^?AnZc${KDHW^RwV zZci>>%w}|B!!X*oNAObnQW|u{zRU_^%#|xz^?okNB9qR;Wa+|4(NN` zHnDQf;YXxo`wEi3bT9VLEYE@v z*F3ecJB|+#w@;M7<$KF5_ai3Uujm09ZEle)xSIow53v!U2c~h;(TCqwaorNwS(T6+ z*Q52c0O~wstxFv?-QfpE{u+Rgm+nyRZ^15BOUm5DH5djYQaN^r=;3#(s2mrEKFM~y ztjOL6tLThyI>9o@q1phlsl#Mj-*H(ReN;S^0P>IiGP0wQE7lC|6%n2)hUxCqOPpR8(>f9T=-CB;l- zBAgJn@D=gC#mw3W%Miqs(xt&T3MleAMe&DEjp}Kwd@+YtL=Tmql@z5Q-H+Ip*(6HF3+{+$yw{{G)@L$ z#S*cui_C7OAMw^i!zAlu^Eo3a#qm*bcoKE&P!UR8&0SLpr0Aw4+tQvMe`3Dm8`_$+ zbk7#CNtp*Yt3Tb465*}9vTQSFHhJ)96>1B3EZ}>D`i&u^%EPb7*0W?udzaShc9b`G z7FKYJX7t|RuQBP@<_6l=ENq*bb^o!GNtD~F*3}lGCI5raly)-dBwlke$qq>&OXU$4 z`{%JbHHEJ{+VeD?17sN#C%4lzulVX-tBFwog!jnfKhJ#Ijh z-aF;3xnawytswIw4ZRclDa;`ipCTK1HI5rUKWxpNb!Ez)W|`drrtC6s`n+%suf<_3 z(G?f`Au#X`-8 zvty2{an+d+)2*?&9T2db?v8rmHjgKLAYO8s#?~$#lIS;U@=zmZQvKfPz4VCA&u$Y#HQj)?Df6Eb3%853~gBOmeIjkxtQroW)1 z8l7BckV!v;bg(fW%01r@k5c1>J2~+ukJOQPg8_%8nFRGb!7H38d|ucwkJ`W?N}J*n z+w+LI*oKXV3(Z-;W16xvN%^4$|3UPd7Xqf(gsQkRr^zg4NGnznlfrzAOr9{l%yn?s zh6z-?FywpU7{Mff+AxC331(o}Fu~HP3ay4tMJYe*V<1sRP6?~TBNn%VSB39%bm8cl zJiySB>fsS(Mx{49(`dqKi$#kER#WC#@r8VXF&mxYPmvIalmxHL~?LwZw0}V!!^X)@NWmJA!|~{r7O_$S3%A-@hiKR`mb50qZ{{ z_p0V*)-L9zZvQQDQ>O*zg|C6}<=DKHwU^g^QJ(4mXHL>8BRl*%c^RC}Iyw30_P7GB z9fnggPV&&yJS*Q$IX^f`TQ7QzvNvr)yJ}3Z=(2LzE9e*$Ef5a<;0~)FL*k2%FUPJW zN4bCDv%k;vZ1?eXq3dkd_jD?Q&>MYlN)(|4ti)v#bDW}+J=8!9XP65K*i{0dv>?iK9Dnck@$-#V0>YAB(!crad0h?fjfRXSgK}aw8TXV^g*m+&++L`>aXeQt+Oe<<4sl zbI|m81dwYLzuUpni3llwF_Yb$HosxVF#hTi3#S9S%J%RvO%Cn-lRnVKqG|eTAAw#Dp}tdqG?LS9rbMr9O!s2;1E)h6Rq95w>UT3&^{B z-O+bS<4MWnuuNWosH9L`OrEfOlgkSiwBiPhkwx*MOI3&Ko&B9_is~Wh`3I%Ak|D05 z16XwTP|nJnqEal~xiQZ}V_HW8_fzKWSWsm=zV=2kTzzlmN}DyqV-BU{U^bgRf8A}{ z18zuLTfIt+wY7H?>r&Q{hD-<<} zacg4(jj6OZn924Pw!iw&7B_!*1H(^jgzKR-h|;~_+%h8A(b^00fU;93T6VlY z<=&rvrmPzaUVqdS>UsEEpx(dqb)tDVApYp;S)GP`Pc z*BEvzRyivz#?1Y8y}(Q~rc$ijVX?7{-Zhv*2?Lv65;^(#lf zh$$CO*|Ja`&><=OU$QpuKL){AW#=a3b^5UJy;FzFbe+&F{W)itqZiz-AR(LA6!6Af zINzc{obQ;)onvgw8%LIG*kxG5#d}MfjgsWA?bK6~R@X~x5UzT?^gay>v$hH$qaiH# zo!^D}>}nV-AAkABJI&PZv9f3b*Y3G+ng(oNp}wjBMz0;=SSJBWA-7p}vRR6>mKXEn zziY$4rFS1L;!iVjYO9WlFEsKnalc|R*gq0{>4>3zRdA>P?At0=#f8;ja@h^a8tUdE zbQX1cUBlX{ez1{U_JS{qD^r73^!}|$V;KYP2yC~3h3c=6eξ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

OvEZ>qFp2yq9`KY&Yi!5B zqCEcu?J*`#75e-*e>9&Pymmb%8WtY>GA$bv51ZsR`=Fw%|}0zAu~>p$rDBdx|9Vfm-KmOnBf{E{I4 zf+M`^&yiLu&+6wQb&KJ|v*dZZEmdr4*1MW9z@?Z>a3{#Q=;ZB_W=~jO zIiYDg`m(jSe*9!}emU>$(>V;lAFis-lcb_;>a*3}@hr(K zWxth$C}eq~+wuE1{2mHZ>g(&5=vM;qp9Og#fRlxhl8KYOovo9}zXV_;sc6fi2%>%# z(dkmsxGB=3-vroD(Gt4T1b{~@s1T}1dg~<1@))CEiXn zu{LUBOU`pSac#SGIXz8$eBNI|{sQj^|0!01<3KvVI3Go7N|G8#4A7%RCx$XoyxXWwb^R!7S`pOLoCaxYpUZ_U7pIVGUNG3vmwB3$}v5lcAgG? zuO0Aa)n?VZc{%)7~D6p1z>aWTuhtCTz%k!e$_ z>dazOVey?L-KdDc&@g8MRQu8+k0qovQfmD%U`M6zENC@4JD8bQP%9Wrtchs$xq(L>$v&1$Yv`fpcVM$Bu9fKE4tlw zg)Z30!dqj~y#OAMCzqsX}HQBp<(m8f-* z*5Z4x7c8)1Y#iVmm2eEj^iY2w z9y#eNI_ws-t@>=9h&eU$>hy#$$uF39Up4Q!q6j179DB0#Kq!0eB6524HAhZ}F9DWI zBGu!xNXDPw^+9<+LN)$kid0T+?qCIZh`|D;CBxma5Z0gSBd zOillv(cM4IEsA{dAcBZK{l&zTd4%lb><}`}g|)pJ5{9%;> za3W@BoMv-vY#bb$Jt_X!eC*J{VKzlmjP!_QjZYd|(z_CFq@GQ7_QCk3a$;kd{E1N!sl(?B;C@pd26xC|q*k>_9Vhlv=lWKPq(1N>yJBQv-W zKycg_)99ATh2*;@*$S*7W=5A96}0&7Bn5=!5q(S zUMLlDA*$9X_&REdZU6kkOp$a#dgiyYOxyWQXYSR^KrXZyE*e_`lyL$K4fzquK}9V2Jug@9?dalx z41$$gVXWbsh9I+nD*v5HX~gEusgTYc-y?E84PFIG$+VncSfJUqS(_@9J8 zfwfx-K(WA&UN`Xj?v>l@pzbmAfehCV*}iR)2B;CgogBlL6wFV$T>&BYYK?b&=xgbh zpTvlcfxET=UogSbr;gVfgD@xk? zR^}GA`=dj8F70Q~Wwc90;u`5>6pMG3x^Sl%3kEVIBUX){p-1X2l@U9ZQNa&CbDF3S z;hZ5F(-kNy$~}LnBf`wjh%GVZb$;O()#r7z#X0%1EMlsjy+C~a2*wCt&IoZ=uN4gi zc$P1r?JROD8wOoPU>aJ6`zmKucbU*7oO?h25JI9t@z6MJ8! z)H+)Kl+ZwU?-vw~l&!Slc|5DThf*2hiJ0MDS8fCMNSB`X5GE?l)aoI4#G-X$FT#G+LY^F%7Ka+-Avy~)mYyei^cGT zVrT0oDLBR7QVIfAiKRjUj#_RW0sSqGeQ*$yqee`bmS(KDk*8W#=_=7p%A9n%4VQVb zRaY`;KMRtu_8TSLXvYcTBm!o~3K)>9^L5XI~hLjPQO1b`W#t4=P2WA|Og z^+?38PtY2tR2wVS7meENY7A6Y{(wq~s8<;m?L!R86DhQXvLPr|GeZYudt95JS;k#~vrCWA^7CZ}i)(F)}>JhzcrV@ur#RUG-~O>7wi$J&j) zQsvT`(3`z?q-IWz7M7cew2{TMi3ii3uzGmKG?pq;PYC5TvX7$ zXu#MZe6CayK+WaoBZlM@;T&5cwc>-M&A``=dIyykfu5fYuTFF zh%Ba;2)ipPsi!Kbrx-l-8%@Vau1rC~xkyN_OwF8ZQu%~-PeU)0jK~rc$&a=~ES}IJ znqH<0rxruIW57OE$ZXzHXcSrvHQ3TTN~1+9R|`fZ zfZ`Z>qRH9GMjK-Bt;jKGG1I&qi)E|1P^$^eBrmMk7R%Peh28w@#AoEX4!>gr zIcN9870samSJI*xOj zNWEpQrGLEn1nzkST7^CO&?kzXo~?+MI;g#J5KNS)|3AM^~wGQf3wIk4e@Y+S%hRs1g&G{8gz)D=tD393sHd z&?~dj+g_Pb%Xv$XUenggC=S6%(=AYoDq3aIHQm6b5lzs}b`ct6dtXi>?4Et8OXssC ze_0_4`;`PT+t%fqHGP<;IP4IEZGi%feP?x)!;&W{VO6{ma@kkCXwZ$(wBIa@O{tVV1zjeq`<0Mv@_gfM+w>eqRx0S|)L*g@S^ z|EBW-#UHNY9eM3kgQ-pQsoNufBV7grYqs&uqH2@tlH#4#x8i-ena9=>_0H1zif3ks zAu&U2eeQQ0>G^TPnKs+|f^OY|I&^(VF{l$5gEJCD_@wZq!3KGG-qyOeQXx_#!D z!WKQ+p4(zAPNezNAMe$Z$z#A3>qRKqjmsQ^ov=1k_V7E@Oj}%XXCBrmOC~`&?c3d)7=a03n9Afra zv?5l>ZIh@$;g7rh@HH!Vsg$_QNIGykLQ-BhuOl#K(24Qe_fa7u-k-O>e}R5_F2u#k z5&2GU7UK)?!ya6G(rh+>!LdzukiRQEr)n_89*@KuZhHW(1x|lhaU;~|;{n&Uk7p&q ztv2S??hw!R$i6kWXvdYh5AR?oO9j}@nTeplc2&f^A-rxM`ewfp+G6GYY@{(DLmwO% zSHzSu&S|-QSUmn3Js*T}tvskyhBvh8P!XMP$|7HNtEH1HxNrs3In}`t&q~#CtD)j6TdJ?7mhn>! z=VVl>1fDA`mVo*Gnq-c|az`9=51qe+!nr`A9nRKtU zI|cSS%rRo~=p=t<#XD#{I?#z=YQ8n_ENwztxwBa1m#M@YXeD0M-Z$srB<3j^!%9=aFN?dRJ z$@3Qbyk3wWT?cesFC_7-Xkfj*5BA?Uq<&HDWS2r}T1l2yHZi8x)3FQ{V4v!#pugbYh?A`H&?tUCip%^ zRN+6;7QaFFV62^YBUn@n4FJD>1W{Tl&yj~K!4LO*=R_s$4K&e|e>#5m1m!fOBLq_m zk}Wpk3He>@&XP|~`!t!RpudTm$d8p#*48M;2OsDy)i)I!U6@j~yfJ*T8o5D0&U;eYmTPjl})`PTTgbE5c__$v4xo<`XjU~3F; zH2#N0_giXyLFV_zFkLz)TFDwh=w5DoB$Aq5FzYa{@b0bG93 zF*T=8P5i|C1noSP<=H4Mx%fKIf(6QouW4~XLRj_O>v^tuu9xpSt|vO)o@2VdZGgq> z@Y-6uaDounhTNiG1>Hzu-fKkdDOUu?!UOn%q7a#hHlkvCD=GPf*~7g?f@{OP!Jcai zBY$9rBi()I^n=2Y0X6f<+W53#q?3a$Rx0`X)8>>vBa3w`QcIby3$gmx@@*VWxdV2m z4zAvQvbHXMiCRV)mk+5&*)|yg+NZGK&{a6eL@cnydxL)l@P!G@nuDawH1hUbar#Q{ zfs*O1@tX%n_-?Rd2ua^pnMzus;x8MNJBBmx;?5*irLi`%(=5{=1b@S{Ebhsb|HM(f z4WxOiqb|5N@?R*jF9{FLKghh)iuXsuqUQu{$A0x{@{lI8p1tA^6sV%kI(PqE^>r9P z?^p6JORQ};1@o}#Ux7`LYanxM!+(c)N6k@@3qgI-UZ4*{#j@OZ;0Bw+ZYfGhG&3RNWTy;xNB+oT^MCoV zTh}v*l;^3@Kio4YH^-J;5^M8v^NetK7S@IM+a-B;+FkMtS@g%2?+3P?r!+*bOWyLAKtzEL^$PO=(k+(vg|BAhXRvu)WM|Y$wVHv8yl$UXF zo7idjh4Kl2(ZGj4YjxjiQRMRio8=iiC_JRmE;OuU#J;NCj2qTK1(r0pdGYNYxaWKr z-C8soIlY&5klZiF7jr=D*AC07yNmk}q?|tS$}iI|mK7y^UdJBvx8KaCi)DAENZf}T z3;dZp=o`m+v9gq=P#v_-oRm-5G-b6|AM(s~TQn|CN+s z>&5w<4(own=Rr~G@#R-`9AQ&O&0CK(Ci^d|Pz2!*k>u}>j=BN^u>C|+jJ(6EDXDZc zKRMu;j{CP$t7-oRrX8kmKC?9RFhITd;gMzBTKfLvqf`v2OJU@K;_h=mfu1Ynr`zMU zjXg$osEtIsdql#aUJ5Y=r%u}?+4(df845#%tV@9cIZF)`fc|C6|cH4b^LiXTUQ^XBzI>_wY zqjvT1@omWOe!rvQ9q7Y^k)+*=^DiVFx-Imtj+Zz+uJf;kt6;>Gy;`u@WiqC&TCQ== zSWfS=q+V>YY(kB$E6bzjQ&cYOsqbNIqr@vs#ypb4P22>8R=2qH+LsaI)y~>n!q`Pz zuRWNQ?Jc!*_VbkHRy(PI`D@(Ca_Rt9%njUxAQmZPm9Fv=- zJcE4{>?6Z2+bga*Dd|HrD!$~#V=}k#s&cfdx@@m(iAH8nA$kW@UHtJ4iyl8C1~Bns z7K3}q+$v+q1+M_HwY{5% zano3(n))d`RcdNqS(s!ddyT0f9vO;>x6zI^3K}na4PXyCTZtk`EhPe?i$!seynhnq zLPGMUZD^aAox)xpqo64G*-iyG8YsoL>T^`?ggNaC3oQSWEiX(Zc1EFT@_@u@&E(T& z!0BXf8#K3&(aB!{*m@0;NHBOTEgp^Q!D{*>IQeW!<16X~Mc)-67=ulTG0zf)7ysUc zCLs4KreBONV-$a}EaA`g35uY7yRt236@^+0KkT##gptdpoY|Hhz!g&cip_aeCz@d6p?S3fTpUlrNv>eYAVtRAdV8`Y%Bx3t~;VtsrBao>@tIj)7G4}X3 z+pN_2RuC8?MNG|<^m6hn&yZran_J?vz-^KR#XE5PSM$~$1|b%vh!~&tu!xp}V`hi6 zC8Ykv*mt(J>iq=9WjkZ6LbI+E8vK4CZ(9OrpiiXIIhiT4@{m5}I~qzg)8`+V543o% z>bSe@iO$evR%BN6Qp56wc($>cXb)~S*6Bh*@c|Zs+X->rd!lLRiCyIl!YYgrM-;3+ zz6n0*a(GL-R~Jy^^mjddfA)_7(;YX1V`EKk#3Ym$0LW@jr~V0Ba`&M>|&wW0QYoPBp68DC4N2`r1m=5osca{t_!N z)Ru-?F)y?rg8~8X1*RZt{Z>0PZ6%dX*qku!U(w%t*~IE@Vv&lN;{2mx_HnhXIK3$= zjGPB(@E!5tdd@uNeeT$pwDbFT`0)$jhB4H4g8GFN3cp$X9aD3;*XcyTF0 za+0;7%mlh$j(#C!yhDj_l2zDEZ5n5zN-H4OU9SE#(&8|oW1=xY0^Pbj^FXydHZz~7 zJk2D|*01a51C+?|pp++2=e^iPxY-P|ZO=$#yWQXlB}_t$V?e7h5oHN|8ch;;G8LO) zYd@L(nJssj{saxUfTiwPDsvIhX3RfL)3&tZ+Rq&G-~))ew-;T3qkxUL{p(BP5oJqN zDFF(MlZ%BVs*D>LoZ`pyl-^i^kv*u7!b%CdJaE6@jsAetsh5rLuN*mZwEESXuWx!{ zXWwG@BO-w~O~f(Juv*Gjnr;26EeKgRei6%JcWfs?@V6B#yV;xKt4UM+Bzbkox9+gN z2sPEDce~!wE?Z|BS;u{k7NWgWRNvPp){<=xsN!y!IABC=4-%>pSpzleEiDWsQ+5eH z+r`?b>9Bz8<)jQ@X!{b?CyBLO{fsuY=S6}W;?vCK1Qg9M$vf|wzJznsTLXywN%wq0 z)DXAavnfci6|9hU1E;*;>(s6_<$L`6>Nh_JF<#==YL$ajyo33`bwuIQ1ANdxAgS+u zuXWx`->T>CD-2TZQQI0y42q{3rhaf#qqw|S2rW5<2;~<^HxMuWTw5E~O3E^y()1Vu z3gzmTqesAU?5C*-oYogIx~hd?x9T+VfGm*OeD_Spj`>!)L-lC$Z9V;+AY5G2V)nVt z6OR&^&4`Eld*>7XEa-f}){U*`v=o&JxSl48{`wTg8NZJctl9hqlZ#we{QHRR*+v{5f9Ih>3Q_>+%eQgm?_J1Oz-;MEZ}D}>6v^3enC#pc5>g+H1V=nk7aNQ#_oa< z0jcyS+(43cwIjz=5l9wq68(6O1jI-SVJZ8!i8y~x416eWUH|>yJM!_DAU@&)_mRa46{7&SDBHVWL_gNT-oC}Kc1(ZRC<`+?Uv>iW%41$+7F+2%>|G{ zUH;Bh0(q3mbt?xtRAP~Mj*VQ8Agr%=l~^8d%Bt?4O@8}#4S&zA;P&cEc5M1;X#OuF z^!~q0xX_r*Q;tZ%n!7%Wlv^$!iHgr9}i|Sq0m1irUT~Qxoh-Gy%Fuucv(S*lYJj zef>q@p3#_4%L&oNk3F~VfjthO`oJ|0>ULu22b5j>65HlNl3bJPesTcwlM3?1A$VWM z<_3mcFiL36aYLs>r*ymZ(l(+5%8^_pvgMP;yA6D`39kZx*V|6So1`Lhnlw?HI!xzv zL%!Sg^kI}^j)_)BB&{Yr3RUW|A<>J_W&K(@>BQ_>musdA2KtA1Bpn)X4sTJWLc7I3 zgWOiL1unz>?_V2InHU|9#noeopmquu$1rb@E54#S?`)=za!;k^h8f9BdI%hg<>3n4 z#nq9)`tspL;QCPcGYDIvyDBt>yIn-&1;a4RHm;aqX3IU^LlgqI2ClSboxf!CQtU73 zR6}EB#sq;aQ(8tniw~h)lrh-B7{(|D#&CUka~oqV^^gx88otRrNA^C}oRBhxw1m2{ zC}GSXVHrfzlj&oYAOc*JDtu^lrr}nVmh{`>zyIa~u__Su5&V*^e}A2!|0G?@0Nnq* z8UCkqt-LO$EQqQ*FITNr66}ZuE^6-Y9zag6GZ)mLnvTlE2PgU13wyZ|&4t06h3xZ; z;eE4+q3=$pY|I?VTgs0S(pM8S&n|OGRovN&xTz97 z2M)BS?;h@nu$5+pRZk)zc)IX8T^#HZO_Eu(fw7WzADfns&~Z~q0W_X@+6d!@nu=rN zRU*Ke5NL?aE6vS{sdiM1Cf^w=qm<9o{RwvuQF(T26rjn#NlfvZ=&{cE06Elg-{SQr z!vj$RKvBWuH7(#Z4(FGQV%?6xxhg>vJWfUGKov|DIoHg|kWK$C*eW+iaGB0;ozg4?JLIQF z+`sAPl6b%}TmN97uGMt{U9N4xQ$-PxN5dp8PbDZH0T@e>RZKN>6g)>XfgmD@r1gQ| zDIyF4Xypq!fLmxO{{d|YfF&A+5|hu^o;jrie1Zz`&`<>5@MRyeTr)PtAG$4(B*Y!c zLv@P=5p*71{F%Pq&1VTH4!Rj2@U>d0WfMJkXhiI5O2u40C zh|F?e7XK9bp^{1214fsD=r^K~%~Cq5)2L2YWc#%$b=7W{0Auyv`P{uoL-R zjE68tl~N+Y$6N%T6Nb~65~=Bm&6Y;pz=b5&qlt-D?_*{&-}n2&>2HoJ5k|<%DmpRy zC>sj)%7fk@9<&2Uy_I0-4%+b2(5Uv}@SNWS^qG?e1{Zv5a@4h8l$ zkw7mPY8ZYKh4ICxFENakiB=W2IrkpDnj?7Y;ZL%y2Ay(s2C&A*N7H*8be@5`iSDdx zcjYb(3*UPBLWUUBSp}V{EQYFbV8m@eN zgzg5{dSxy4QMAicfZZoB*NGcz%RFSyF%M~%1a#SL#9c>u?V3+Mf05f{5@rv$jG*x4 zZx4NZ6CLdUIMp(&3i5A&(iW|d1RIG;v}+$yz_QiH+;LvG5|&EP`b4} z`sM;7-6T|ue%q0LA6tk5F&7>KEI9->pkQe;5{r&I&ooq-;08;7ZnBTycNZN)bqvb- zOY1?7%$~{!G~pt^(PUZ|X_j%S7J~J5jmYX22XCOGMvcud!u=s!-(*bmW1R_jmyDNQ zKdKxvu6E@je&!#ZpD<dvLRKZO~WW`>+x6^>X# z834Jq(Ux?q0(bkQ&wSou$c<;e`w@7j&&skaUOUHXY(0uInHWrDb~!Hdjq=HH@LI;Uez&PG5QOow6iT3{RPn!=lzFRa0G z;)E-&+z5)lAb2LJq`R3WkT5X5-JlV#*N>hM8Io}RQp(~98oNRDARfXzA~_u)+7l5W ziz6gXj{Xzw%$>gA+gDqhDlx2Qz#9y`zuIxvf3B}2!B3)X;*oSK2f0p0YTf}Kyfq@Z zL8@~u$k$0Hl1G7bxu z^yBW5IwLj}{_A#wBpDSuh)gs?6-iA`5bTgHcaY*9yzum0?jzJKC#3fb;@-$`H28a$ zkh9k_^$o_qntbSuYbF2jQ8W9ZMEobj+5a!qOWjSh=?bsTD6 zEB^;}s6l)xqxo+a2xE+wFlO&Fx7ckzG*K9=Xk8CgF znmupr#+ol>AKT|l;fQ;Gb8GvQuz2OXftoFsifLiQ;3~Y%xGP@S_Ss}|lgywM02#n} zPqoN=(uk)GHkZ4_wtxjp7g|#4jZQIrmu5%3w9UStP^KLZU4agwE9VdHKzGTenxxK6 zVC>($0t>Jky2O%eAO`6*Ax@jJ(H5DNY4y(DmEyP^rfJTl#$2H)2V^5xzg5nlM?@QP z&0=_7VSJ{d?6pCCUC>LXCgYOO1)B!jP`Ff10*+tfhu4_DFmMK~8&+EFm}ix#0cTtL z_>A@CTw7f>bnlKfG^BBAuOy00jQ#=s!Wzu>AQW&DJBj|FoFn%8^=RoH!#dRObe_hI zFL7ArJ7%7o_lI|yJU(UE$#5G(cyMlT6$YyP>EBU`vFLqW;R=KVE+9F&x4*&8dvR<- z`<$+Ktv`YHt6b-ATD(Rva0L;t!h1N*LX)Ju`19(Wamti47bGU(tNq#L3*K@&NfHlR zZY}R{kL}n>N&r~fMGC3#y5)P$-wP`qBKcEzE(#BEzkLod%3ePzDsaQj5M(L6Nn$Ec zq#of8BJ46&#>|unCkoq+~unC8xGB9jV2V{SA{k7chnP(Qws(QG`Qn*GiQfHRPbPsE7SLt%*9JbWVZAsT;MK|PG65cg*$}XALSHP9+7o7{CC>wcB zI#h$)|AMEL48`Kt6!zf$&dgf3Pd{obl^54RHZeqMnqf?p1dtlN!3JmR?BNGe%$%n4 zof=A_YMm>XQv)!^jo{7ZI?yb&YOPEi0vA?(bk8XM2G*CtU{5JV#jXC@Vej9cAV~pW*MAg4 zS?bFN23&F0@2)1YzGl16*=e`?et$o3dZ?>#dyzU$dwSdYPqoEw*|$n}bRp|CI1-*G z_W!_Wq3M+EfckT7{+Jsq`qhj@p(_txu&t~of9{X*0GWFn;ve47j3y$e56qd{YDz{I zW|%Eu_))P~9I4wzp)_Gh*Wl|M1g+{(>~Mr!&s;e zI)pWrC}fuWL>2KRxFV8Pcw)}A;+ch2S3#IEEiZVU=Zvlz^u{B?Kbq_yw7b%%~>;sqEvEoKvG|y4}t2#P4o;RW78+gMsgf_ zAZc*6^n%gjTlyWpQGkpq9~LmGxWURe#rL2fD;tWxbxC>jvUv=?DsL|3w;G^WNtO6;SXDs_WtZB8_zu^)w6ry!ZM!y&nv6Le2L@8vEOKag4z9 zJpG+`d-bKhRUXMsjA4(gU2kyQJs64i@ZpOzw(tD}ow}|4MH+)-)E~40KjNZYgDG>z zJ3bw*_xJSgGOd>t`wJ-9JJTEFWfJ|M+e@2h%yThICdgI7x7dnVI4{oKv+@$LYWeBe z7fm0p7q)*$L8*+VdSrI@ZW77-wOTHZAlo(mF|FV4#4cs8ygKdouVsmlBcnq0b=j=` zn!>XDhsORN_4t2~4($FpdQDQ6wfo|U`D7pz!vSZ|L8uh8w4~BwwSsX3j< z9e0{s)SlF$mmS%mK2acB6v99Seh~zxZnh9u3i*p*Pfm4s+?%?6+%G>9e0#d7h$Lo@ z^iUG}o-n>WIVoI~-w=kFK4LwVU7DYBLS=_O>(!d(e@19SQkOS!) ztE48ZyM+|MAG>fU4URG$UqyMF?gcaKbaF_`ighQJ5W`Hw=F~}3Y=+YW>Z|j_H?SPj zU9k}GXEq|!d&#nfJ5}R$SJjF^@y#s{c@DIAPT-=z4>Ju3mNZza<(Rn5k+dlf2Kzq- zun`~D;WXEbc5=XY0X!h!eH&;>`MgN_IH+yGUdUY!-cIoWO0j?wSJ2mS9-HgRnfo9C zd!R8Fa5-E%r^ZRA*=PpS5jt+~^ibfuRG z7q{|8D=x;kMy&^ioBgt@v*ZIG_Oko4V*cll_8{n4*0AvSJCs$^r^8S;{0GFpo;rs> z9!F7xZ{Iq<*c$&Md(z|!Y-?l=u&`zL=l4L^+Tv^W{}0~ne>Z@YnjYRdE{%V)M@ij| zrex>kBu|}XIhZHsa#6KXi81Gri%4xo=i>8%M-SN3Tup6L3uUPYiJ?lMsMXLbe@W`~=j2_7A!MJR*aseKFF5(SF{c?P7x=yne_Yvd9Y6CNdnIo(PyanE zMfs*SU+fHPAQE2e9J%+yR>OTQ7=>GIFM^(Tzb{3{U9CTb&0Dme3J*ezmABXk=-pjr zROH=#)OSzstvlSkgTvkNQUs5;9pBnTH|SRIQiZymu=mdG`-|Z01y*VjG%jKTbn2iZN~Cvi`PH-1z1!F|^-huicG4{s(%s z=MA>r@(mA$Z?zHJCt;S~!YxHd?!N3tGmhWTU#Q8ymM;Q$e`C5OZq>=j%tg5A$wDH= zSRKz=BF73#ZeqpgkH|R~gxgT@EwGstM*C)Aqsn;A2NBrPM=fV*sXCMDRg74GP7g4H}kg_+P$MzJ|!;@iLkMPVsOpgjb8gkh2oKbU19b zvy(zqY;acYWzlk%2k0Fi=WH>npabtH`Wf>|v2)foh5QI86UFivg?US|H;`O^g_dpD zph}I2a0*b~IgGGpaIMO&$Dpzx58XAHB6j%nUFR_=M72Za$6_T=fB&GQx;{15nl$Z8 zlv58F`IYeDb)j2@oY9Np!`ks0Lz`%^3HP$J3{y_D+97xM@UfsYxpcfz5lWLMJkpAZ z4CBmlzLZ1{vrCpq{~~i^KX^YRq9KY_L*^JLqM&sYwG|n1a40_u!n(u>Ee8iud`Vp) zPA2eq;|Do{3JVgaH^hn!L3?)XPV#n9D^goq*aF1-G-rxShDPg4?-rI4Y7I3;zbhvOL3y}%i>x!HuHMD^w$P-D?hfIom>Yunu z^D3vwh;0a~H3aDx^<|83h(#=OlHhqqS^BPN*=dZwI2|trK5x$*hge!ScRY1?ChK%y zpJlSzCrOAV^^iup(!ska5;CdAOeUj7-1o|XiWWj6lEM@cOU`VGf{sQs*{9U2B&L-4 zhK^dWQ6iQ$g;hJS%pxILp_8fAbZFUJWv?JVMvV98RHfR*0E9l`M?~z`j(*oi5F5sU zMj;;_hO&HUPtB!08F5We3i1ZB;Mm_Htp0>d_WI?pD#6YPE;4X&0g9dV%?|9Nz2J%%7 zK*(w^Yld86;W?U5;ZHMoU+i|?)j5k~X36_qbX|aSvhljq4E>fnV>XApTsWZ)Y_mua zRCns7?ugp&dy@3x;c&!ru1J$qfw1jwXY*_qY(K@0s$!!gO@G8hEn@FmqY`2c^ihsU z8rd9raZko7gADT`K8ukqm7Q5iL7$jU<_8Kg=$UAJCzb)$t zxo3zVhxC1xbA;em90gaa97(Yo8SyF@fAfl%&5VuVo!j_B^SW!da6S*5CqicmqjGCY z=cfLA^~kokTkWN)-x6Bcg~W*EV6+iR=)U=bZ{Sst7wg+Ef3K6kxtCb6XY2<6cg3tY zY+K9~$H{lkncp!|mLI7-_5;6T=7q8OtY=6-hAAi|INe}$oMK>bQ34@Fv&5k>K6G(D zQc=+Ocy4;hDVm=`;@S9$u&O2RMA(=U1ak_8Q%eqrdVlBVtzCc|gYg@n36RM|iEXhD zJ>llqX;15+XWI2Dzgi}DYOZL{$D^dZ?sg}I0C zZI=g^U!Zn-Vf*aRB+tzHp?~g^FsOdJo_Lbr1!mW+J+CO*u}j_R~Hf*@+>n9{;sgiezelScuMY`#lzH_(hpl^M|_}QQ2TbS zBvH)eNp^UEp^ZlJ*OMzV|3u4k&Qx|YBI>zsW6mhW6~HQlb-au3aRc%DjA97+rFoQs zIn_h&_FVq~&exYzcfp;LCdIS%Vk6HwF^;ax*HaP_Q7f#)Ui&QfS1cPt2E3|o&=zUl zVI|BBz3U+qqfA-IAH{Mv>7WfcJVaIwiDiW^U6s#L;B^6G!TCZXR#=OoMNWv4F?vpsB)7jv!Gt@X?n_DXOhms z2j!cU`XEY`(&(T`^c<$I5PSGcfMO9t7?^$t+Ll=~2`fw5^5H`>RBcFDvq?@pOyNry zf|k}(8Fk}jX5z}Dv0zcm=(xH6#HNyaF+=9)9jlqS$LEIhT>!C?Y4$O!S`*da`2*$E z+ML&@1_T4C!mhv%jmPipaw{q;V@B?>+vae^_c@@uQ}a4*o>l4^ZKYdu3=HJbK*Kk} z1HLlUSEC?Z`uCVwENwsa+60lXzuKHQ_3EbW9L59*)3GwzoIh7H+(ycW$ZOy0N}I8V zW=1-FMe&MdPM4}kgn8ynRvrv#`b;VYng?aTq-h>JZ2{* zFD{F}cA)LDL4Ij}j*nmbg8S^F7$#mfO!ADu%0ZJh0DKENSNLAdYYy@uOH|qQ?(owG z(m}zedYI_3^E<~~bL8un<-4T=ix85R@_0-894P4GB5aQN4JJi|X0@-IBElw0uvUtRc*nP}bP5AAodbh04>$E|imAj|COjtGN-PkJ4L zW`F4o)TX#>4P%RG7GrJ=@-;IKzc$ftbfGD*^#-ZPDmav+?lE%aG6$^-4rwLmGDfDwEKSa| zQ9BbXF!o(HdRbo5j^sMTOXq>ldp=UD>>I()_fl?7o1@z%AMKC?HE2#KTrwJl!jAC1R>8N;AT+A(bS^Qbp4T)Qwl^8k%tg!+br=xAs3 zM#m4Iy3G&!-!G><;#&iOIGh_u!-11q{5TL2;yhx^f}en*JRryxkTf%#ZjA&iiN3M0 z9ndOADl12rRY9Cp12-xY^-FdfX+8Du%f1@%JZ0&TMmczU;Q1+{3>tmld=L2~5|~av)b%GL*of7%*Mniyi2&?OF4$ zwc8BibFU3Nz`XeLa4n9Tz`#q*1xka3uah+$?FF^0MLg&EdN{n@BO{SNCf>QH{W}8g zPP$CW}U3!Gq!O4BEwZ>dcwi)n%#Ve?)IfMuNvE!<7=n>r!dksH!uFO0u2 zm*nN&IAj?bxecu?nZ1qc;)t1VQGHvV`8MCu2I}hmOEOXBA-~RY{ zi78Xfvkj>^cMI7uKlp3~Q!=J1T*C@|z{{KJ|;l4yKPzoGxBIGP-|>0dl2`s3es#KoCJ7 z!4~<-pZL}R9&ba9#UbrLjW=J~>?IdJbm_Ly!iH8ufkclNxiRht71(5HBfG8QT(`t~ z)Ug;-S2gx+*GEyjb<6PQIbgM}8l;anSm^?St1mU4rHgQxM?~QB;VK$du>7sBx;NRD zHTkr0n&P1roq@mjJyvl(x97sqwlwi{k}nI^vJVwvtIDDcKFbA6XO%^=<+kd;xpFKd zabdycY(K!3fWlR-h<}52K-77kb|mg$LGFt#xOqdNCa*l)TLD*f^)c zh2x^|EU}~N0MAvW9vKwZps0>Goo+qAnsf)gp*9kr^OZK?dRL_*WLH~m@96k%)1xYA zlF`m?V;QLqyZ+h`p(SW!byp(ANi5#NrnT2YzUA@8n1hdJXP2LEfxE`IJMj!7@FQ(c z`awd~GyJbYm{nQ1uCBZi=M~>poFX>U;vY(O%w~W2zM~G_fmaZaT1B?_@9D7Ag9)+KV7r$@9dUvanXqI}BqIG`_SpR3 zIs{L~F0UAUmmdh~8Hn9-H#xqDt~&6fdQW-Bqg)E0L`bG%(1TAj9QH(5s>z`Z*fJ{LqxfRjd)p%Qe#LYQQHy<&J8#^E?S~v61E1^7r6uhkV~$mztOhR@7Dk zCdba#<8T}(XqJu~e3$nqg~5{eVsQVZVtB-zM1X3VT%Qd$zHy1b)<6r(zhU;~)AW(D z!RQyPYMj60cbgt^Db-*|D5!iu0~~5*oGqg0xt}duP`>ZFIL3Jp;k`q*{`lnKf1(je z5!0rTInxfjCHEd7{zrXK?0WD|!b(u@o#za5#WHD>mchh9L7x89kLJve-@5&}awG3JPs z0-PE)m>wNG$(|i-0o6Wf2@SdvD-3tlSZpCq8pmeLx+Vk9WRcP!$!*tjA(yL!UNMX0 zAVFk-%l}l3_;s!96U_fwz6Z!pj=OPA5cn5@v6yoFimZ7uaB8GP^RH0)o-OCKs@ZxMe?`1 zeBTvbhkrjb zg$Jm_?>#wwp_52#s(pRuYH{^K?z+YBHelgZ2dvn8qui!Tv(Lo{)L~(eo?XNC1P_bJ zNk*MM>hU#Ma(3Y|QVGJVJ1yb>!v0lU0wjbyLZq3!#It6M^J@f@O{l6NCdP~bT!id+ z{krsXKv%`AW{YwR^a_JOHhuY-%4oD+V{gQ>O&#p=#IQthcocBCt$z*=W0-M8orQlU z1t2{ZGHTCd6W^c`@6QGKK=>u-yp8zs<5)c`p@cUfXp^v`C$67aO(E8GA?yNnZGJTF z{Jzu+l6{aJ^ypv}F@uCEPin0)!)m_q(tRG`i0l)7GkJD+|+-Tg2 zlgHScrNsIz-WslWb_@lS&orR)kGjfi-P1oVM3-PC8|Nc+DHWNAdok@K4rz0%bwBBU)ppc^#e)6-|w1Vm$8&<7{CJ zGJo7n?5|uC>0o>_fF3bD_GAxae?r*Db6nW|V7SU!;IOtgy?95Sx^HC}CDT62=hBWM3jU?T0NK zxbb^wKDb~GA1E!3F1l=CkOfjLD5>eh9ZHK+on_WJfND&f(=^T*THkPADj`;{z}p%t zKNi8tG|{ingIK-VYOPnoYkEj$l9?h#!;Mm=>c}X8r>)ck%!$PwF`;EjHZ0eU0Uo}_ z&I`kdwygkE8e_@;7$HGCDJI}1nU(c-vh#uoSh>M@Pn8?crRN$nXl?LzJ7(afHSE0l zLuIUNKmkFIzz%f8xop(UlB6TaIJfX~n`el@5i@Uc4U+$D7#u^iNGA`VS(X{9dyA8&7sq+wu-`0d zo>)m%;H+@4o3CqfQ@w2N0^R7t!0`^xZ3?Kn4IVx#WWHWunUb<^d`mf2PS^;xDfD0= zR<7|{j^*U(c~Gq|q+SSRn*8oM6M-fT6mzLE2wN=Z7isWrNK{mV&9m}zzOwz6R3&|kWa|v8u;aI^O8i^2*G`f?)=ZMe=mwAl z#GA>XZz~-dGM8q4@FtfuX%O{Ny8JliYcwXxh_Fo$W=W^+wZ%PUMQt#MSSSJ02yAHK zG`%Ma%uDg`me*;a%yum)u1=Mn-=M{X7#>@5j6N*YMuN1iKxmi(RgCL zBj?X1h?1tHHRpm+&u&4+vJq z42?O?HEi!T!4JNrp3gupbFhnzn~8k%_;(~h7ayEK$t}gV#I$v{FGN7wEDA!#or-qwMM?-hf z6)%1y)ia?ZLU1)Pt0oBz%Hp@O?m96(XMCV2XN?@YherPBa`xdX!sVYk-+#rZPldz~ zDa9-RWS??DsWV^n3l`CTbE7sEN#&&x=a6nW#UYAn@XDmoi(Xkm(Vxf6q~Y{?ZGi$i zl3@Sxmjn7ode2(lgq^iGeTzf1|F_ZnzigO)tr{?fznlwPUpG4Z|M#M;?*4V%Yp-fy>F#P_F6m`v;rw5h zzW=?K%~joW#9&1gRC1-2^(pwl#K;JO;si6H6A^$UEx?xe5hAg1yuFS5y~yC&_OkjF z?j3=dBrw5i58@fQ2ZHsYhOAhEf-#~-FHS_wnL-mnkck)v+8uchyI zi9)5dLgsQLFa95d2rY1V-^h9;JZ?=DrRJw+)TMwcVmJm%bF~o}W8Vz(0`&@bDhleE zk~_+5m9I_WxpLF|RYhNLE{Na#w|TJw4T^rL;@ZErqbu$Fj`SSo?9>9T2fp}tT7LCW zG^>>`*}fz!gp)P}&20(2z)|)tJ|%KlD@;g?l30PM*aB+_Cs%pu*;#NoLX;WX@2onN zVza_Un)z5s-xaN{|G53*^uH$=Z~SF=Dz<(6-trVB6%*%j;HCiK`n&oV0K`k?(NMpU zX|>|3pk;sosN8pb2uScJqpI>&d z2OAPa@vJY0M+06`;C3zx|7NKs6egx^Ic&r} zSkLYh#kM)O&E5g=8~?8V$Ic%NVxb4vU*fcd`FJ?dph|eA{t}_8{#A27gMN$Wu$al= zPk2`SioGxV(q49(6yhpf8?xT;xewIt0Fr5&$}c~?0Y!AjH0S6bn>Ou{YSSJ?eTVxm z4U@EG6H8&((|n8efCp0+MpeE`4-yjg*!zixGUoMTM5+ElLpwt# zuwGuOml(xFWZ(&FY0rdJfGlj=bdB7scJD7l;NNnRXK_{mhA`qtlOO#;0#qOXRPzAj z+f7IaH5S9L8ahzZnE`lvpd&zi;0<^QiXoo#nvc)>8c39elr6d#xP+5$T!jt$nJL~? z2P)BjQi3?)dZ`Xaro zO2?NtERqo- zITCUg(32vx^+@oW+XtaK@nIuW!09HFJ zk;!hy)j9j+ls%zZ#`73-^4i!5()*bDv^r!AbSB}Dg$xP>GKGkdwgKtsMt(%+Bi>ta z%XDC5D}aLGlAn8Vw#2u^6&Y5F9g{8VCy_(ch`H4|V8ykD)VViB zGR%7}O4>$zxP}gV%CKu2`4-`*DIfVKN8=3b^z4z)Hs4gX*r91oxfnet0ruFV4Bh0z zqFwW+bG#0fqe<03kQZ0eGYf}W(jskcB+vrg(!$wRmfc)z7W4N~H`kQRVAj-quxr?# zh_&eUQX=Zdkz15(@u4`g7dWYF8qfN)QVZwqrF}9Lm&l9I#Bz)6tP)Bd(-Vtys*@2$ zg_)%OkMBs6N_K$6!l}_aKSk|3Su+V8yVB~6o&{7%dpS$@-)P7rDc&Mh?$>3kfpC4*iXq!?Iom!%jDPaJ_1`zFh@{PRP3GSlVguqt^VtbVJDC1)hh6^uk*I;Yc00IL=-;ZZH){z_DE zq?T5w(=qUFHy)=st~Uat>_OafnRdPC$u! z4P6lgxMV_%c9bW)IL)RAIv$st`+_k5x45BEl`5r1MFcsBQ=d~DOMFY+6@tYBDUGH| z70HlVVOk}!y&+aF98QC{2+j2GBSJ&zhrQqk!07(ghu=|~PQsPEG4TArUKzW~6fi=5 zK;Sa0$MQ3{Q#BfJwmwyo3C9_g_FNamKo^a&MF}GvJ*zAsFmOh-fx(ohc*fIEP}BxB_W1=V)ExZ#(5oK)4U9nv?s$7GGr2i5R=Ir0;Ty z&;D0yj}tf3&o-l)9IBIlAKgyiPtHuaF+CzKX|NhG9Ezia&_IS}EhOPV?YfOsmZS({ zlLYlE4vDb{D5zwE6-;nnhLV$a8)pNO!q!OIcD%;4bt*Scqr_l)ab%?nDX5B3$1W1a zX{g>AtT{!k-8z<2+k;7h_W7onyRDcoGD(>Qwqxjp~<5?FJk_&#j($ zF8s$ncHv-xSAiIs$1O`g9B}{bW)v{9^}n^%!+P(sVw9yUpD7;lM^4Hpg5qS*>9{JRV$ ztB|U}PpYMP9pVHtcnrcMnk2nliviF1B|8P+yU(5`_zH59oVr!-L27RT;48u8q$Y%Z zJyh{pTbB1z4L|S}=?;?_HM)Of4Fp?PU4;gqrV3Q7ni;m?46mBuU&|Fjfs94_nG|d{ z;&?#@8B?x5wwxpZ8C?kL?dAIiw3{mP9KR~QL$f%D6U#v?`mzSMPyLZj=({i^z`PP) zzaortW@Iq$Q**_my7z)d@SyXn8bicx%aH`4AT3)S8i%MbBwM28H^fp2GxSNkq34QEIQD zTzwFdOO%jw`T%+2rH&?GROaHLLaNKLFGxEX&Gn!tiNK?Wet@!=Xr9afLcfD0?awD{ zxvHFXQc3X2ucUcUe1Bl12H%lzK^_@O+&N0Ii>KbWMAj~)dtz83=a01W7cWtlioe}d zpqYlS4f7~O^#2HFzyC>)2ov=R=}t)_?Y{|-sk27a5W|lP_ck?Ti(m^6){X|ruo6&a zJO}mo-@w^;jeAWFP0y=PmLr}W2JbGV?SE|3B z1|X2S=Sp)l#60LtFn(f2%G8JUjIP!ZVPi0{)H7vqNFU?L$~0ur2C22@qW|@Makkug z3D*pNs~I$WoRDt=t38nN3(=!NX-x{*EQf4e6xb4NZcC0EHfTbBnuBXg*L5bY9!2Iu zdQ#&j%6GjX$&EJbfJ9kL^$KY*<=&3zcH<^00sB)VeTyp?J*rbleJUv+Tf0ZQ^2f#p zX?E_rS6Z`g+|I!L1CyaMn?1_O2>yzMbkcnlk$XGi`&`YlHrT_C~EB31Xfk*W2xuB4LJiF>m0Dm|1Vb2(HnPkmQO#YmFvMbKkrwUSOPtNu>| zSYHD4dQ@n1&qi6F&vlfbu~bt%cWG|<2~5E8=@bfL$v|#{MwK^nx2{ghvTf=BpG>i> zUR;Sz82v$@0xF;1w#`J8R#eF2Gsg09=I83TIa1{alsz!I`M1RiPXX%Q1)|`K^tqV^ zt2(@gi@||LK?YMWI2d@jjUTK=uLdp?As9o_ssO4X=|A{G+zkm$1BI*yW0V9$MohTD z)FN^^6c0HlhBgkZ?AkP`UyVaq&dZ z;fx(|W*F*VX1BGMjs2Eyshj-x^q+m?%krj|EYU>zXv;)9Xp8Q)A1&3?;6q`*z9l2n zu9&BU$Pb1SvmqMrOIg9kk5Rn<+Pvleu2EB&nbzViR~5u;QvODXj56tP#6{G4yYpqV z^Z<3JTvprDOTT30AS9_1FXlu5Ie9ZiX1yqroVK6F-}WT*vKoc zg?g^p>WC9fb$fe?j90vZhd$#%cLWzy@_s8X7IZnte_|A9TVgN}f(hsajBe03n3ocv zi+ZQ-`7c)$s5KTm0aReiqAHuw!S0`?hGwD&t7U zYn9#Fe6hubxVPsd=w&SqNrBYWh2!Z8&|9Q+UZ*oQUsG3wW@iV5rg zmlNT?VTAu3yYVG*czOS~;+3lPW`wDM`ELfCi2(wiPLM=pQWtESV-bB4m4ikCeCX6- z@M;VhiG6cQWbG|CEv>t`>1h(E8>Et_Ec_@Qpq@9=&i;pK3s3Xsnyb|@@Lq7&cc%Sh z=i{kY_#bo~Q~+hdP^Cs2UTwXXV4xWfu!Y@Umn}2{yA%yo1w?z)_`DFYu^w2;szp` zSm%9c|Agmf?ZDPCau4ma#>@RNeQ2fjumnZG1GSija_}cey%oNWx zJcIr_qQM1U>Je!-s~_K4&*Tl_b62KFr&*r`pS7v!yLW1h!G z7da)&TN46vTc%030g)bh+-7NtF1o9!>ZNlflKH8QQ2aIDx~vqkmsCqFW6@bh>!gO! z(|9|MH?XawvzU=1^Oo)?05J#La?H~Y2#Y;cMF4C~>Onzu6{1I7AczoY7R+iTk2+k# zQIwbBQ?etdi>Q2}J}+JlQ{onFT0=tgCg%o`{U`v5V76kax>#Ghc%*6ZMP!=0mK+6v ztY$APVR^!)s^s#IN@S-&2UUxkH3@O?p56vey?g?_QR?G#Da&^Fp*5_(ht$X$;^^Pw zoaQhviOu`c^LhB=_Z`P9Zy}`d;Vvzk%&8m;aTyPP!Nhg@d=EvAjcL-QLHRmURd#AO z;c?eb3`Z5*@5zZ(>q5X>wJwju<%&2VN9FUEIn3T9n`)GW%d@!S_2N1qd-kbC(m4nD zG37)&Ex){D>jNfFqIDAL;~}QoUUX5sM$99ALImDJe z))I~i-5O-J^~{B$%TJ3kvgP)^{V;k{owN0yxKS;4@1!hvHrwAC>u5TUH8H{ny=hv0{VT!Qm{`0RCRWjm@Ds> zHTj~`N%903rw!-P0f$}T9d*t{Ym6oPF9H);JAV9FXO@u7hUR=*YP+(yjLAOVOU`pb z9d?G-gr^u(awi_!g+3*r-Sf;Y75bg&S9&IiuP2`Xyx+^C0lSpjF4Un{{HZ42MKk|W z5xW+r^{galU0;dofWer)SUnY#c0F3DxC0Yv>!jczp=`#uotgZj4vlpQ5W7&)5jUAK z!ujCOu1eul(2BM|Qv}esx1gD3T#VpsRVu0~`HPZbRD8|a5d-n1F1AA)wGnEluQabk zrmD~Con7ee61AFsCjqYIkpb7tVt`JA9{B4<{N1-(cgONHZ!mB~mOoUSHR$)Q#0Wai z+6~%%_$T)bBfq9Drf;4}^D}6VwS3D!Gi%f?-4i2n&CEDs7?6Y1hIg9%RuJ4|#-YhB zHp#HhifK(bq>dB24PLsVHC@|*?}=_B`Rxk%o39OR7p}%Pu9?CH1+hP*lOK`*_4^3A zEA09GGQl7MGf(KXLChc*d^^8CijC!T>3wfw4?n?n{?MQF+uiMPg7FAn4hxYLILE^; za57UNHOh>!;uYT-ODN##j#F1<|6F<*$fw;}W_y6(K$aF^UwxTdQXaGuO|=7o=5+Hd z2~-d`+1f{~m&nHcs;p&#b3QuDHM5l{&`|PWkNMEa*eUx? zpONEVZF$d`L!@$NJ|o1+(-A;e_(rk$ZjePm;M?S4Z|WBeHEC~4tPKK0^JVXncgdp4 zTV*3;pz`vBVMygyEE98;O-p31G^L zUZq3lXO=i%YcvqRl3V>qmts9rFJ43D^n;UvBN?r!i0yi*9YUk@d~q#}2{qVUs?3=e zRK8^J>v0h)In?58r(f+XmUc%OA7Tf!XcT#+)72pz`Pl%};K9SF%dF3V4p7J6^fM!6 z1mEob*l=A<0sdg5a9vAhH4{eneSy#1EyKd-C61b+e~3^|ih*yCng`zJFb~%-%^8BDft*sTbg_|<%W(6 zJC`$95JDhdVgq@$o`n-wuWbmDb`9 zUI4`y)O#wtbtu%fJmZnl2dwx$oH`jUM6QDV;_vibf9LFqwED?z=aU?ko$H0L^Bd%#v=!9ad4t@w80rbC<=C2dzg}_9 z4r9dM8Fbm6jDLFu2*-DBhobBufBMZu^4}H^Y=xq*-%IPaeb?i(KZqB^Z@C|f@2G=t zI$SVg@*~Kbb7uDRUkFtOE$aJ74O`_pqkZZQ`_o;m-tbcEN6p?JKw%k~C+(I$Cq?xh zt#?=L(<`+>2aLvR89e*?#jaks182YMKfy3{kC(J+ueEaeg$wT-oj4uBM#U< zM`^nc*MDsc3z)wlr>+`3X}>eW`u7_|T@|a$`91}=DMNMs$W1K}n^*Z98fJ92^m!Em zb3?CrApCsKsyQQU@a2n<9}H+5+O3=DJsS1I|5>{qkaI)x*=PQ5`6i&tUfA{-qbN!T z;Rn>6-pyb?iX@iK?=j8Ls6A`M6>_g*$icrBJqfj?{S1|GT%1r4iEOqNPcY|`$B-A) ziL$;(50{`!*o>U`x!s&kZ)SJddOx^)89%0d&Pr7ELn7OGI$!qowD)$>i?5}UAz z?;n*(uU4pJ{f8kn{t=1U$L*Gk2puE@RjlM_D^ey;8xd+Q|H_&5ounqSi;&=CCsZjD zb!p7leBSK*7?kh)ig9z>4`0!j7&L6)wF1=2>tia4wU5RRhZ=Dz7`kZM77(ZIplrW; z=c;PhBeb+3ROkbZU6*o>QIhYxp2-&~(<^Xw(5c1+BP1W|KYt1HK>>VF@GqlkgToiW z%ue=1T@s>S*b~dD#xf{j6od_kt-YsL&f7O`fiKk-UK&^_UV7_0dW5$$g zwe(0=nu}oeTGGc5%vWK+OpKeCx+KD8qZn@1K=EW1>wvXfgn~#( zs3leNv(N^lvL5d@fqQ;8hhJ{`_;1OqQ?h@Ahu5V(D!|`Z1b23i&EI| zGAPB>I*IKZhVd$6Ge(#lfZ)1gVdZit-VmuIRi-q0;=zqHKDO?f51sqDL6fg>5K%*b za?7^(FM{yKKd>edXyKDIz7DWqV{Q~AsL-W^PMSZMN>ooOBO8K|-)uD-e4;z^LUa1b zqBAr+zE(4<0UKKV0G%lXzp|kE>2Eqi==|VH?c*@ zIOpF3B?tzG=QjdfA^?Y#LP-PxkFnbfkB#am0fWP6L)b9N_%bSFD`hqNS}O{ct3-Yt zWr`jPysV`WevbHf_A_(v$q9iTCA&Nc770@p*Us#FQ{@zhkTTMiYFeR)*hFNf0lgGV ztF8E<-!FAv93tHSR61W zYppETvwWaBTBwkj5%44xH5YF;>T;<)X)Z_3*`H7m4L+}o^N}en8YHn1FPSLS9I5W= zlOJ~Hl~r5KqJcOUN(y~$nt)psMcRQ&7gjM)6b&*Bp_4i{O~DOhsSj_nccnKXLZm)< zO1WBNB+6SJx&UM_A>$OetI&*;7s}ua7fN82bl}oYxB)_Nr6&LRR zob(!iDyBFLH8+5O8Xm2Bvt^20G1x6aK?#@{N+z;$qdtzBB-B6%;tESVMmuUKUCZRN ziKriA7uLcD8b=YR8PM9YLF{nzED&g%&p~jVk+`dq7c+`{C%X=YZ7r`1& zULNLP%_U}cV^y14s24^S;WpOA~2Z3qU^|^K2FYVTuq0LhpUQ$ z>fljfkQifZHGR<$ik45+mKdP&)L|xDzi=nWI6n)N)UJwxHh3O}H>fff;W{srO>h=92pP)kfQ^ozE9|hg zl!Z~v;VM#PFJNN3FvWs!{t-mBp+ViD&A3u-TFo?r9$v9;|A>5Hn%1uuuZpMZ28hcj zjB<4c5Y^7TOg%;rt?=-MCv2D-8zsPf7j#fIt06m>X>A04_d{D?A0C2byN_Z0q$n9V9R8QvcVP|7dfK5%YJOE zWp}NLR0~D*Un2r-ynM-<8>K<tk1!gcu8`k3zXSVA38wDCVKpnc>F^qOhtCqVSk(cIs368w5xj=whk+{2bS`WKl~^Kq2-v|#@A2yp*W35 z3=u3~%f*^C=*cgQUFv-1)|L6$F?veZqd+w=_?IF>ZXXYR!7jp|ihpj9)-gDp(<9mZ35n85-c z3>mLRo&}O>X~By-7u>iqR4uOis5}eeJF})9bUCaw+=rFk@#$xYGe^$jn9Gt`SXbq0 zQS}!x%+f7@93ERo9Wm9tbTaD>1D`&Us=rePCa%DZjyW3*DkV zyLsrP&kJ0p4hV;G=>$_wsHinOV`l_wj3TeHe<2&cC`|Xzcd@rdBP3H(9Kj0I+h?JA zX|#i<$66TZ6!Ttxo1V9CXDJr#L;6D-J6)le*aj(^bSiTDqj?)n+J=GE<2Z)n@XZ2x zLIE?4>h{ZsS4tkLLet(I{>(xg!S@lnG5qVXBe=c`$EVnrrgTzYF~u^j$!=iV03|!p zGl$F!O~#t?3`337D>}}ikCTxFXEF%fpC)c^`G+KuyudCSljj?ufR z`vYN0nG?w5*kqSt$E+i#*{q;Sg-KDVg*B}(BM^y~?=!91K=C5Qxh}<(GmNF7=vgc5 zOlhsG6x+n|cf|!M7mxZIUF*7IdT3`8Sy7l>)*cYA z>pA(D!Q_35)evx4q-Rt=LYhM9&fPM(kF}}E9ezG2^vSz*o~P3dQ?tD#wX4fc;2K}Q ztDp%vzM?R4X$(|3Mos-Lyj15_D;=yKVNvI3XF+jId01MkQ*z&I5J7d%RlRATQdLSV zB_M4@W52PQ4;vUf70LGQ_(;NGUsgViM*pUWqk(amy!gC)xC7_7fomxJ_rcU9eQm%0Pv|`n7yvFpZ2f?;cU8#MS6HZt z>f2ZUJ;l^>g=oxBcVF{G<9y}Wg64|gWyXd7h=21k$!WRzzOx*{rE-(RiThBkUdw&u zV6Jmx9?bFgA2p3?HxVN`EcIs5-tDd&#f56H zAZZTzX^S&j_47J&>BC;^vC$d8oeVTP6Kx@$3}Ux^Yf+sHPpVaie#iR+Cy}r6OQF>4 zO`tUV7S3>sPSY?nF`GoG8PLK|zprurf##8P7v%Zf8n1o@#Xjl0_))55vvJDNEt4F-7a5|ZyFC6ii0|eAb(s(?hufd zVNof%;pqn2Ask}1G1-7oTHz3EBahPTxwrJG80x-^S36e?4>&jX1AM!g2AAPSeci=( zQGSE&&Kv&Xgw0f!T)7O&F0fr!WE%&8?8D))%OcD*wiY)`I=2qVnZ8 zWZ8M!hBYEtE_!h==bC%UuBS$}L$yz6p32+un6Mvu^ySz-ZEa?8 zQP~q zskhG}IaLhkJe|1{64#?2d+wQ#tk=XGXYt0t`IyRUZEiELmFZ;*cO8<6JOD*P&p0%e zj(6B#t}7NXB&zZ;P`YQ+gj0S?dfz#glVrIzHLLlo(f+ln=D|wmLA^Ik^V;5txhVau z$wQkO)`r&wY_=dPO9F{;oEPWBbn@Q>lDqKBh_o?J#kD?|Ve^tdu%KbP{H@QXt0FZv_y z?xUwm=hXMo)}x7Ad6&s=S_wt^DBg8o@Z}b$`bb1=&?9|PxYBVx==#!E?>-sG+KQWuB4wZ0uU&w65F!uUJ?2a zOi_U-$=LyrpYYW;oK1r$bAiZS1g;=Bf^WV6NP)eNLEakyrm*xI3NJiG zFR0)fd>}&Y!O;zsA%YVK*$ZY9B)%}!`3%_=&fNF*O!W@;50-c@{L7kyDOA}0GiG<7 zjO@D*M)w{9D{c>FGKkm||6PsYSFI%MC*w?*(=hb2(iY@yG}p7J0FrQ2I@<8tDQaF3 zSdhj=1vb9|R7qYW{2n68;AmKoW z;CwB1kRX2Fd32L!;uekQ_!)7Or)Pj{G@xD^P5bvDNV|l&s~B;pD8+|@5JHN?Vrh{XM%H((T&rXrvxnAZeOeVYt-vcm|bz^ zcXiCk`MVp6h0}oY6nWik+gmkD(fU7$l{w0)@8ABFsIpUH8TI`u|4Sza-}8&O!Y!HM zL!yMgUBWwXre7fK>&Ap{yUQNj=3V(hEJ;^4AugS)%CyE_DTcXxMp2<|SygF6J5 z;80LlaCZp~!9BpOf4c9?bWhLi`*6Oh*V_BkK5N@rsdM0-2sTjk2T;fqHAM9j;Nm4Z zE`baiGvME>i^*aM=kVJI)w=ES$9ZQ zl`YMI=fgSYXEDGzH^a$iyUB|>F>Gvso7!6t|3;=_p37%a*#tYQF)#YX9812ScJPh` z#H_&bSTi}>lDO=`vI$WoYGB%`im*YTt=!)4U4|EPblKht&07x&piTK&fG{NEX#GVl z#*>&i3hWnsq$yFl8AM(<16eqkPMq~1e|-Oa2Q>G-UI)T-XqPL|Il1=@Zvo8o-g5&9 z3k8llUuQbQsAX-`cUOpjFl~&=da^7BKtMM#%}#<&BF`@8MmY6WB!f%bq^Waxybn&t zY<~#kw6+N)GI_^YdK_}HXD24D5nzBfyigd?dPgO+HVq6TjIBPsP(s&i1y0w@M!;fB zl)7vbU0fi}N}nM_u760qEW3(R3kz8sWuODIV#lR=B1YUxC`9WjDbHav2*k7ja5lFd zorB-JT%@#cn>n-CaC-c~zY}8jh#VW1t}Yn;Yr}i|&OCWw`h9YjY^ak&n643{OBCj3i>i+Od@!6;AYoa zS@{c=BnOaF$UZG=qxNBmUOw?R(gN8==-&YQhBO1K_Za>}>-!zOnS^vZVJ&k4wh7*% z#sRp{z-qkQSHeksD?dLo>D%&tTgEa3eJZ#~b5YY$uES%Ry9bNv2GEN?wa zi|Z!Na!R^5>@tLFMoTf_oR|bOI?YKj8cK!BFY<)E@RFEsiNK+lHjVsi<7jinKlAnT zWZo%CQkniN;d4t3w5le5C!#zHyj3vng&5_NI^{AS#1|QQv_mi6T zyRgBIsPJo&MGmR4!7E`!4Y{%5;{$Nz8s&GuRpFQ8C`{6DVb+IL)pE(^Bt$w?>&xNg z;o7Nw5pnTi9j>4KNNlq$(>St77;fzqhjvb<8c9p0B9N2-%BWc6Cy?7Ts#3ZzPD4&y z!P;B8683-mlXygSh>7JOh*U%kL@M(6-!BiTTe^AKnp_o+V+oVjRwgU5hbpPk(Z0H z!q5m~_Glx?dCL!ns`-eAVWrMcfie7ffGYmH5j-ybnq33TdtS@|-$i4Ugrb~A{w&-i zvAyERmQxxHbK9(VvpRlH$gi9$`0V(ENcEpvFUq0zAnLy=;pq zvbAX5YyHF?s99Jt8IE^cnkef$&VJwu%wlI;$8A1PsiIblc+^QexWwv6_c0=z5aQ$` zM?G}~QuVNz@ZGowb5Qq~Qqj(^?&L%YnfYYq;NXbAhi71{+;WD?=hc$lR_7oFDES9} z-Cpz{qB-dFHIArGVh|^spt{UTc@GyTV35Hk=Rd_;t-Z_C81t*Y%&8|j#tc~9=kLvX z_;!*mHmFlSA2@tvp?zb^FVvQsHVy?9-O1>#I)@-uEtp>#rPemk^?(t2bkN*OxGmy1 zUdtb9_CwrcU@P5J{jBhFMjo~eohs3|KyOA$nH}19pVa<(OA5~CGn`7*-NGVkWNdMR z07GzrOfbgxFYfj{ zSh(su2HX;-HqJFk3ebTn0~w^TuTiY3*ek3hg%7_^w1dm{?l2ZBD>V6A-#&)^jxE_cw?PW-=ySw{a&uEj92F5bjCS>nrJm#8>`hsOWD6cEF zHbaoA!JSV-F_L?cKP~MMr9qe;cM?A{zQHY-gSml9b@eT&$MmI=%C%w63*e0)4_^|-HB8cqxb$AKvMRJC(?9lOV9k6JQ`=CP$=t%?1EWz7qC-!@Pn|#V;asbe-P?DCYe|Hf+Gu{ zYvGDK&x#w$wz2aqDt9Bh2*6}ufh60^ONf-0Ri;keh`w&aBxnD6#qUKb)=BHNhm>g6 z5ox&c^UH!-GlVlb%x1^&gRVz0gUc)9{WiH3*IHbA*wy47kq1Y-X+cMlj6JS~i`H=A zLz56{cy|(+W(_z@$j5)<3;Aml-ATHyjSTcYKp}qm#Pjdphrf$K|4kv}zYk$*>i;GD z`u>Yo^}MnX<2M-oaRfiR=&qWLy%QNEEUs7@C*4dY{gfF4Z)&3jK4?2)X2PFjmuDv1 zt314X(&Jd3Uq^;Z>qb!BS#)%|fLS3bdxok7z zPeUwCED)<@{twMIhvsS%<=LvQNX=H6^n;Z?Mn5|wIBfkh9}7QErJi^Uo97;62nS^)U z?^^cRS!8AVGA%AZHEjlcxg~+AO7i&+MDppL+`Z<+v`k0eijTA(8cQ`g^`X5@SLG!M zfkHe@&r53GVr<;I+?t9pE9jA4>+{CY6V8Ya7~#9lH3KRp;9qhP+)?b(?P!_PSief- z>q6&lZ7^EL_zy4ql!X$&?RD7VL7HGIKi3<|y+0i^NI|60mM=ckwWu1@+HGL!t<*2b z+F!3Xz~tJz$}v?-PM^Or_)s5s@ZlM^>;&o)Y<6NKK4z0ds3q;>vVy6KQQKcPpWZF6~B5B?fl2 zom`NhM>scza5+9cZfkb46zq12BbV7~O;H}~c$trsc7%{F_aTs=Y?b!XBVF_6S?RP{ zcF4^Wz24%`b3uT$S3|bcB+x_r|dGbTG=DhZiM0WANE+o>c1lTIJHJFILm$ zF6z=V=?SU{g0rxxIo$^8*EdUAP&{O{<}|E^m((GH4U1XpbLCnqJDKfUp6@4F!k!aR zveGUdZqt#K9c4+fEI+#6gOKDr$AIecauF@<;RoRT4?j9QvAOlTwujKjmv_Q3f@kYv zBWY!Dkyjn(Ib?Qh-2J`_Rt};8*!E|pDF+nPYfuiaaq^0ccQrZMhH#5dh?ThcLLws^ zL+bx$3_~~`oA+tJq#-c?_YVY+IZ+y~R(P=T9o$YLSf?o{Jme1o<#%EO@k?_Lns1O? zLbkG=pXk=WpYs8qluQYf)T=l&4jFwDIW~QlJZCGKl1?7xKS-tUg!(TF$<<6UqCWK% zHzsd!29|>3z2T|(EvuK_|53kdMet5|fETTpO*&&5w9N8ah?;!Tr-|Snl(-mt3+?^3 z7M}a|)deh`9K~3{<%i*Y#EOX!yf8L&h2`PrDeiE6CfF0$-6ttnJ(|G_9Y`Z|G_hi= zT`nk*HnMaxG@l`W`?2;6vl$P5>BmwN3)hPRw&Sc}E`trj8-#?MveZ$xRz?$1v?1adnJ6j_(ZC|@*`=jiP z#^q~M8B^$|e_8;l4<|50g8uX=8HCFJSE2u=Wfa--1b6iCq!nHfrcMi|`iY)@+~)%{jAYLqc~W|Tver_?B&jL&E_ zd*amrqe*fHXW6FS5fSNhc2n8zN{`FNk0-x%-`|gtM8F99_w5jO#kiQwbbvC5mbe-D z%4RDu@|p&T!&h`8mK}^kSZL1*z$3SZh-1Tqfqt&lUUGSkNVrl-54ET{HqDq5+*3A+ zFsh+i#AVOPldNS_d4aaTsq9=0T_hOjymOE7$*dE+jGXIkh4Gx}$+Cq|xe9BA38i~A z5Rdo-oaPTM4fwVayYvZ9X5&DHjuUBSy3t8O`| zl0_1TbhqI7&a7K6c}~3UB{Q+8Qo0N!+1Vfq0s~y z`abuok)2v#hSi|;@XuUiyEMhClpUciHdSyr=*QHMM3{W7M)MAx`p(Cf-#iE(m%!WU zpqJnJs~2D1VlZfSv4JB#xG%67fJC`enw2f0gv7sfjHs)en5AB=A7xG&M!??$ZQx;h z?00*|i7MR%hQjB&KND)m?}XnGp#*_&%-&$U#XnP36v)-YgR9tKc?{byF=X9_ z!v!@8@*y$g(NzIx2w@DrB1E>{u_eUTx*WsKQW1{eJq!qhK)F(hl!g`t@F86q6}!UiywIJPv^u`^u0)sX^{y3 zalM=~{AmpxD;mDYLcOqA$Hc}gliA}t)0x;cH_(4sWW*_`%{x>r;IvfisiglimY#3% zE>nFIZ3QB#GNbssOYc6G9w;H2y~0Eq)2@{7{=DiV?FKh%nJ#^m*MxSys}n^F9GY>3 zOyG^>&Kx)TeU5x}#r**Gp1WeHUE1SKi*^0DCjN@kiT!*Esi;B89~5Q8+PEshuM|sz zIy6T9WJj5Nw_6ja@-rAi9PPdFQ2dE_%1tZn3F(CaV4){*V22@O;hl479e^QKh@LZ3 znxvw)znDP#9(g>tz}31&8jHD#>v7G9KGh;-0R7F3~LR*th+wZyFYDT$~Iaw|C8n5jViz-=7%xWPXY682{srshb;eSt5uvMi(^v{r|xl^M93W z|7*FlN5|KM&=TvT=Oz9{f_WxsvP1(@$M81t?3nzF!c z_sB=@Jcw);53Vo$surUUU8OJcDldFn#-~69o%&cc4o0fZlEHmiB~P;-h{j6~mb^j%aSKLn&$4%)qEB$ALFnMbTM zwHn5Jn3Mtvj>_iPu7t{7dc4(A;F4YLV8Gr9H-w%~ z4>kwD`=x;sjeRftAxt*eS8lYwN&bf=AN7?{{nc{>9j&BdlMr6yInYhB)B@4Mv z@T5W(7>C2|%J>Cm30xp4TamUyJIACAP>ZRYh548ZFK?;=Z#kmZ0ek=Le3svSz;*=j z!H!8Gt3aGWo)Fs8c1*$s$yiFqn#29eaNT!vNQrP6vAFvaxHHBgIK>*t21rN>%|?$aA!kjoDvO*`#Sr&3fQPaEVD9O*_{~}*m(z!hY4Y0 zXZ9Rc@|T+s&}c#W80-B*tLe)RE$eSz@yq+2#^pr_lAl{!<#p~<>(sJSI_E8r;T9xm zn?v(iO|30)l#gf!2a|R><4GEXnqY&_c&vt^QJV=pLJ8wG#pGGTa~aFqs#P}K|IZod+8aIhe@@BC>WW{xsl?Ur@DPp}5JL=d^Ro{TB*e_uxVnn<<2weNJtRu3$V)5F7ydvPxM?8+H{Q>&AE({ul2iWcd79DhAu4$<1z>#8J6oWR2?@fZCOCJ_>N1q6`Qm?%6ocBox!C~mADRQV4@Q}Qi*$7F2 zlnE{>(W>6v+$IvKDQL2L)!HKhPZdwkIL+IW@Nma|Dk$SmJns}9&-L+-SH0mV7JqPF zsR)y=$v8%CP*cRq(djd);>RshM`dlsav4_YV;m<+z!C!wX)g59HAjDuw=1w~%4txk zb1k~$5GfbY5IO6TGml2sHPuv!swdT)BiMBtqu}PYSLOVWL?9650`RFM*r9EHZwlA2 zAEQW-F6ZZ$DE7<7w~19ymqpvE0N#g>(p2XUX{J=HE;!i6AZJvil*@7&mg6}v*^x_9 zXIRN`%9jsaVO#I21~B9&iGE|6C(L1(7X;HyGu`r=&^U5somOwh&7n`xF1NBvsnjgz zw)<);403lwGtWAw^6x{COp+yrmG`q&jUS`c$U+@iqRc~dLp|I+EJ_0W;bdj(7eE&gDuKtn302OnzF?y5@6@P>WO{i1ZM1M@MBj}iKfQ!DC05w!;Ydels4C8axE63`y|kd$O6gG&?a4qWe2U zPjnKW$sIfBvDr+G?`JXq`&obD4U_TeiR%SqU$?ErdePA0MnuBk=Ed6 z)0Ps~<1CE3O9|E)UH_!h6#Z*bGA~Va1SnmR#|H(8TE)n0vJ``)AL)p{@4#Y3>?GQ4 ze)6kb-A~wVI;|}Z>R=;Ak-Y8x0`BLbogP@Q>@8dC43T|wYZ)Bd+3oc>+$hJ5#u8#S zid%tnQj%C`;<7tu<>1M;-A<}Gg8=n ze@WwY`f6C-uZN6zG4A9)2#Hi5I) z8sMWarOq_vgJn>GPs%#^WaJCptX0rgn-RrkM1G)22Kk}*)S+6StR%}-w?0wma62YnA{hVS}4-OK+quKb#X4a{y8gGk{ zT+u8?f;XMQb^_keQ3owTdlhDoZc0=kdL(O_3s-qd8S8{`)u`M7Dl!JJnMg&anC~6F zY>#YmlevKeupyLHp;5YHse|3JYFwrv6k6V6gJ)Wp@lBIn-KKfVi9{=pZc@8i?aDA= zk@0=^-LA}!8-tA%9dX6 z`HzwOq8`kvbcm2XU3ib0d2;fW$7TpA``m+#Kneprgp=q4!0uPVr63E*qkWIX1;mcO z3^aPf12=G1kbO$^q4xtJOXBON*LEZ(QGsa|KsNDEF>h><)b_h+zd`d2YBP5!w|3 zXrHf(rMTRe$NBm3xK?D5 zYO4}ZKPChxUglKBnuNym5DH^ET^qMNMknJ|91l065NCm1a)-8{9y#tKW#)iM!Vg!% z9)KPa2d8u(web~mXyqtQH^(f?4b-KQW&T&4E?)FJQ+%R~+Bc>=jT z(hz-Fxl|3d(ihVnszJf*iIcf?@hpwR?!V2;SY-bB&AOnT zaul$KZ@>ze$v3|xbi*WgYi!x;UM4sCRl1NpVRD7yX-a-AmbtY2zH8f6YpEkdo1Rex zyBIS`wRlNa#>9fhd9{fEaofC4sxUwJBUACIV*|j?ja%?%8;h;B%d`|(%rb&aQVO_x z<{mEfEDtk3>#u+7Y|R`iaFlW);?)gAEkK1RQ zI~BMCTXfoomz+??&YS@^@W67MqiwDK*|LRZ9rBPlfBGg-vi=%7zTQ{ucvPiajWXL$ zq2_Uu_p(xl@L^Jn)dn-*0E)}=et7uf&@R)9ZCzW7HCz&EyZ)FY>QpS}rg{fXtPE{;)J zPe3N|#BCpEM$s2H+lC}u!qqQSVPmzL$2Z0Orr(A{i+@*wddw}dn|XJ$O@^BCjurOL zlz0kJUVi)Wqackxp14a?M5Uf z2B`h;b_{xS{ZGm^G(~9VLE!(DZU1H7IIhauWRPjQKytpH|Al$Yot^D%Em_3>;{#OA z^>DK_|BJL&%huV!^sg`fOyip}CI=yk8u<~cOF0r~%xpwS&l}4_5h@X3bGMV~FVH4? zrr@1*g2vLD=zUGf88jr9!)X?*Wqm`rlaQM$3F*yY`2z^vNnXQh~JcoL+-*d{% z@z!OYGaqefF<`aNw_chqV&P4@4f@b|1`)!Y{Ad^#f5xttp^o!n|Gc{)0T;UlP^Pdv zrmvU&iK@{e3}^Mvv#Al&o#&y~Iycw1H&>_g*L~WR40wC6GtJVm?oks%3uH#b;C{a> z*m+|QpbeGBh`1>hf6nO_^n2<78$@&)b!Wb!3p08jbsTO4jX8%w@Z3ks5lS zKSBPZx0Me4&8AMgx$l2!scYyfV~AqDmoG54#MM=b zO19Wl^?b(ZrWldOPZ%i<$0rVO`=PKle4&)DMDWH!JrZ09XLB|1be_asu!=2fLvdhw z+=YwyDK1t2)GF8HmXlci7L*PIFZ%fU{Yi;V2XR`CW3KQ zZjx2)aLw!;K)jG&t!1QQY#MzMxje(|5NVK)*B+iRer=Q4hps&n(&`{GB-o<=s zq+f#q?KO=c82D}1^fzpWD8w^Tq>ZjqYLCS*0w7MQnRdr7@`~GiJQy#vh07oP zB#~<04O@UI5~h=k%lk>_ca_>W(5+mGqr`yfpqcrOLXo{Sv8wi5 z0U$Oj#~0qei!mWY1StD(0qXgVVJ04@DDP1#3k-4K6F7Tw^T^KbYAfiGEf#Y&y*$d z!Z>wryM-E4)Txpe`+H9ZvJX|C$pK@ZoBXz`0k_xm&xFX5kIg;?TM%;gypYGY)p#qH zt!_MkusnzPjajtPKKi|?ANO|?ev9HWShgpGf4sH!qYS#!Aiz)tqFkc;cQH!Y&DQ!q zJ`~)HIF-}yR^k|B;;=O{^zV+Sirfw#sa zLT90w>Sz~5N0c(>25=5`%2b!p*R($RAcR$0vldQQMP-3Ck*_hJdXIxl9$#2m*X9hK znBE6~YTrThjZD|i>36o&ya!{+o`0J;Q}XuH9bOfVJxp1rZPN>3;cqJ37jjTBhLnfGB1*c3$_|Z5RMd!2`r{!ukQwSo zq$FE>8>k2mSksq1p?FyPFCV)MyEK*XGjetnTen?5LL8*?1BBHq$THTdzkms%a|mVQJG%~Z5`%;oi0r|H3ms~O92a#JZqR35Sh zar&~{u-xwPLH-r^_R=Bgl_UjU$t?JO)$r}-F;L)-ess%e(& z!7CVWKQGQ_{sUVi=$xKkxE*;E@#o?bljjr#XJVIobzh^c z-Cl9uz$mhR;nHQttO%ovsMmu9d_L0z92^l0an04wKl4-$=KjhR$8WcZdN-Lghl|r0 zGBhYZCNO2n8jkw10Et7*HDLAGE_AwZ0kQ=5Sv{TYmw7VgwRpC1(VK_HWWz=`3(D-! zmn|~HY?-TL+bcOr-Z%!dou{OV{scW%|{MNM{joeeeg-t`GT@lJQ2P9o{)jYlh4gHkPROF53jHZ#W!y8}#dBR@(e z@*c!+s8g5e$)ctt&a|nf<%x!}x!G_C(CR3Sb-Q*`&TixYGuuIvo40yWZ3F!(;+;P{ z@xeU5beyeTAqwW&@U_jlp1=4_yQ91&25yV=_q#@;QLz%v84b;{{G)}Tp&a=ZL6#K( zS@>T`GW`#BxH{-56r$&#Nj@?~hZd2i8 z9l5G;y(swfNjRO?FkHxoHThRqfXMU9`x7MVh-8dt44LWi!Nu0{_!hh=n_grK7vbt} zz_~_AOx}}9uH;A(Qr^a&-4#@$ye!-1CDrqAR-GvGCU$SlhaNj+_cr{w*NXyUzGf@* zG#xEpYJ=~IMw_Aq1{d5J8@?pzi%`baQ3tXn^bh*;c5YM0+txuCa9`_&hpbq%5WtJV z)K!yaBw0$Br*6&*Kd%~0x#MIn!vy`)3>pnS*cyfA6HIGB_J*L`C5j+E@ui-qUS*Z?#HVSElryubqTWrKN{5_j5e62R^kqc+CmeVigmXDV=zaQcfBV1Y{>%?J^#VZ< z_6rEY{;Rpa|JCMyIYG?Q^zSKQhKBuLKvoE%jLDJ-K9;nXKDKP8K01byic(OF)HYd1 z4Qac4y^+Yki+i0eMkEdZ*!d3+_*l1>IqG1om--(~^&I8B9>4#&e}nm=S!)SuAG?+| zWQ|O~{c22?8W8-dt|MDCn!J%*u#k%w_Z$nK!~NJfkM1RJzy09bKfU%KyeeI!YgElf z>7^mgs-RU$gDha}iAH$)L%yIY)~!Q-KZNtBo});=>1Tr%(uV43Gp0~t{*}gk1O~e! z!tV@O1CNOY7YIknDyBQ;<-KToCh(-2!`arA$K_kieh-*$IF49G=%`O?&1}NlDP$qvJ!>|DCg)e{5c|Q zI9mRr6H2)3nu~CRjWh{^t67y%63E^_b#%Za}%#NDcrHRDdIbBHojr6 zgc&9Yv)GjIq5hygN&lpgsg*0vBdlwrLtH`1k+5!aQ9WN1u}N=DfOE8?!#nyI^STL? zrPKd zt)-KP_KTEK6e+l?U`0+dJMo0- zK#T-nwDnmi%uZ4DdknN^>0E?m@OJQ&0p<4FyC=d92QpE&>($bLKx9NSX>6D2#~=YB zd1S^uL(tF7Je(>5`wXlToJdJy?6SiM;eB7KoE57qfote(>wM}hj!Eyqg5?W>>yXR~ z-y@u@_WmrQSU^ZH{$^Yf=Jk*f%P~s)DiucFH;)ohZY$>>inyCF4|tgh2b}!eH(&Jf zozDGLfmzBHYDGed6@9fWCM&CNk81uXo6Y&7Vz;%lbvG~K2PBAfI$HE-#?5(Rjv~_% zMS*m|R(m!%)+?Fxp3mVl6uXHn1s!7!r)#Q_2Yqo-U$x0+5%oV9a$?f@YpDH?Y8O22#*UT42{WLwCk&*x}R>RD-2<&_WBDsU8LCu zqm>`DJNoJP4PD;?&*-Ps_kW&S_JqTi9|k?D2q2Hs{O_~~?d$v_JAH52hPX6FAHee{$z zWV=^ukz1n}P(~K|n$xW5rr*o!>7WP$c8#L+0G&jt}=%A1`h z7dEU>jetG&} zN#j78>IAP2i=4sBRmEHRQ6YHS>oNXsY4T)A&S&^#nq+{rIf&he#bpR!2F_-nZ6+II zN##WCg2`Yv#6;E0*;p;uu865vo84h2lvfYCz8SBjjp&1+cau{2$N3^HQ^l_q z9QjhG`!_o{cbv=Gt{BQ z$pN@bs|l7T1}C0Q{q~wU9H;yyz^gy*6tP>5PaBt#p9Y9!uS&IGama>nqzQmp^^>8o zA5}f;wy6C6VAB5>rOlX^tFvXh#65uL(YW4JL6UaYM{~iD8A9?o_hW)iu&WwIv|FnV zz9$4z@pW5^k>o~{0W!PNFT~h=DdP0;t}@a{wF*y32-$#6PiZWWia(T9+i*F}U=DLG zdh$GMFRa1Pt`)q>R*yHt7Qwqw)Z9LoR<|SS6;XXCp^GT`FmYd>wxk$P9s#q`;DUX` zC@ z3Zn>_X!=})<2Ak>!nxfRXLCfA;GuI69DU+V4Zknaq!b&lw|l54P+Tu3wW zHHUU;emz|bl7tK?GL1Jd4*fq3zAH{HD;({&D^!WwvB)m+qFp|{=T{XT*ze(j zo6!uFHcrbgIuK6o9krcw4B}ehO%`nOj3w!3;Hx;MrCKr0%)k7&>&j?MViBS>t9$2C|I(Ak(fjTu50dnn-{9_b+rI2JQ`YQ^84$J@k z9{lay|BvLs&DqQLKV{P+;)UP`KZ7D5AgeMXUwdX`nBVd;;kL%Atxj&O zB3cds5~PsFl4Pb|8kBLpI6mL~Q@f$1=_0uS6tEybr97^G_iJS>K!C^A!}os#Ee&~R z0!_^KdZB&S4K~-!Of#|LusQzeD)Dtp0XD@vVTH|7fUS=V?B)v2E42RzzOi;0LMEfm zAUGI7sT@?1Lvk`hrADLp9uFCy)0_rVz!6~fTj)0b@p)?UTkyvN1;11epz0>qYxqXG_?e{*(E4Z&us)ZQhxZM%1?wn39k64)Wt& zzkU?##kNJ4yLizOZL13mTQ!%2syTMYvk~y9Hn~iU&@VY`NX3A0Zw>GdU5mzM3N)!= z^(L@$+Yne2`2BR)5FHyS_X^hT;)ufcaT?-Q+69pCEV$u%0SV*i0+hO zY9(?>Rw-ghPhH);NuGF)rCQrrzyjaR z?(!vJ_8FcGc~gRYa!lGp1crLwLkipl|GOB=XOk}&ys|v=9`|{xflZ01OZhKYWRWL3 z=j3@LAJ{~Lh>^7^?C+l$ZLJbWT&OK6t-7Mad!!D@D#H zI2-WP9f`EV8zc6-5MktofHTVMU*7oE5a(d3Ud;VAId8J|bO|vgFNO-Kn8olUX0bkS zG?|ry*}^eAF$=7l*JD-NZ2XGKI5GKP>=mJEN)L#SY5+EZ^DmiGX_(}J)K_^WOZR~x z;}^pnkbfZlG2j85si*Hie| zQt;^L=rSqn4R{lB2IvBAM38+W{V;bWs4TW$Vg`DHmLH#1w*mqKz&ikq{YCvL{dueg zxmnf4+~H}%Z*o2Ky0l-%D51}qYHrh64@X;BR^avbQio#T9DSu)@ZgtZp7 zP}7t=MLUmdujv*>= z3)S2jOVgImB^z)0{fK8@_v6nksPN&)X2zyY^ml9sqJ9pz)Ksa$HLkP^% zJ;{x34BZW2PPG(J!!zx2o-?H#A;%wcvAE!kkAJSUc#;B;DCUf&)r(vPC;VEu(IFBu zulbP8i)F?3!zEtRI57nxp;EF>0A{?qDM?%EX}gm7@XHS4a(_0{r;Kt?bJc60+C@<- zQ}`Bc7%qKqopWd__*@AuYf&{-C~KDEUdh>;N2p)H6C3M=WCbGo%ckmYHzyKQ3N-I2 zgrUA49m4*6j`gl;q-MWX2%{eVL-}biPy?-@Z=?dsKsv^};L8QMbwM-DL&g%M#~~HZ z>e5OVjn)WqCU3Q}q|OKv%E1XjKQlvq+nSwn1FN#n02h(6PHr`PXq=PzBF_;OW-T#_ zwi!vwnlcUT&!LCT@QTQ^H;*kGC**8TAwk1(k z+O{ig+qP}nwr$(CZQHh0nN?{!Yw|Uw-|N-gYv$vwb-xcHcI?U0QdGtXIN54T6v$+M_ZENHUhQtmzWZ?afdm)1;pgYeQ1e9px5_T8s#PS?w+-QVx8 zSbl@g3Il>^i1gI>LFK6T1|=A&L{kS2j5(J5cW(RxC|zTvx)vb=o9v(R!bckQFj7NZ zVO>ENX%-`Zw#=XENpms8;oqDQKy38I)<6=Us6O4*gQ)f4BD|sr3y2KVhm--}zn8)U zt`~MFP&E|zv1A@M7YtgZOln9K$E%shtE(gsi!LTsn3oZ6nuH``!*m!**z0T6D*$RV zXza*kB}@ZPv6``*RxhFu=1y_6m>el$EY6UkNXnzBIrQ68HL#{KiL`X>H_T8}7;aFO z;}b+fTZH_8h5#RZEdgPQ&R{RFoiqc@Bt!X;<}IJTXik(tuPmIaVio=S2`n=UB-%^L zO2)lE8pecW?{-CXzs27 z15$kRYP!k>CS+nwCci`aIn>7;tJ}x2ng$_hqvsXsV&`*Wu?O&e2>TCvVNz*7jGoS>#puo5|4#Q9NTnf|K7 zxYOWCwDgEWV*m+-4vs)ahN=RIfF9-UqraL6_atrpHK}s{BJ;LIm#sd1&NUMU-fL?gZ4V zC&H>x)gB4}Cl{?}&{`={L$Xa5#PC05lKqUN~6&F1R1IFnG~_A)*m z^G~(%cd~Y4kH`wf;UBG5;~4wv3|;fPzkQaLJPGF#n^SeF0C7unlTwi^;N}!rg3vX} zWRJsoATZg3=^p$ZpDBW#F$Eec!G8ypTx~CZ+;59e`tdXElQ-#u1KrC$TG_#iUzeWZ z8{A(rX-uy^5fy0qh!~adQrwb!5@sOs=Cwzqd@>ZJLwY6o`9ljuAuR(x9&}00pY=Z@ zcRiuwx|(}l1INTQ`;po&l}<1Gf^XIiH?xG%#kiHKW@Qv|g~-15J5kEcG3Q$jHl#`J zvqYY@56~n^gu;3J&=9SAfV0X(e@3bX`smH04%)Od%heuqi7;mBD2&R6cuk(KkC@p7 zBFT%L-mG_`hL0s+WaoRdkZVS zUJ?CaKc(#W9w)q)Y@4fV+eU5DEzVZ0jY3K{KBu<)2?k(yK*me6xR-El*ME^skjNAW z!0SjW?%i;yP_lnE9e?2HaRKvjY6PirP4jHRRhdemkHB5t0uR5+)T;`qhcW(ZXkGa* zyFxG$gdIWB1J?$E?lzk-ukEbiLbmTpYwzBV*419I3m#P&9(F z-^{-?N7$@kMBuOcbX*>%ySx)w?`~cmVfT>1P+h3c>6f_7JrqVMpwU&%SEvQa5jj$z zaXN7@+U&(IIlskMCL{q6H07YipvQ z&4*DRF)Fl=K?aC2mI{t5vZqbiPnKkiiM*E4LY8Vvmwi`%*M{S^KM_Q6s%^wRK|hqr zEtaz5GGcEg&V1@W3>wWF2%>5}bH^=p{+Qi4A{)%#&KS;{cO4Y!yp?!4av@wj7g^vI zXU2&6!IFxaV6>7$3mEH)8@j*@?FD)v^IUG;kb5_3scND}_MQ8f;DR&a+ZUFBXa=WN z=n0?@23aC&j9?ZR!4UYRHp>RX1Tjv@dfFXM9f8rpXms+YUHOCgXJ(+0UHpQ;>|Z;B z_dlSp^xt2Z|T`?({rHGvcNvc}nlRN7qUHn1-i$ z&Im^BV=#$V_Hn}dSBQ(veWe}nwHC!+oe=i_xE?k(Uu{$y8{@xe5an!bt(45IjcuKs z{tf14|3!tk3ri4&6+?i_gb0ro28@up#c7z>H02bUp0lS3-V-N+P_H< z12g_2LDcZ%Qjpwjh_*Bg;6#x~T)QLHZPBilO4F(3j)=Ji%+K7Zj!v>k$qpAFJ5*y@ zEbsj$aI^9yK%`A-tb2V*!daD7>k@A|F0HsMQ>%YMs*Xm9(fe)L`yP2^T8kMH?tGRa zNoFt>Y(MbH%R*xHd-(xr6cY4#7BC3pDDFgSPZKooJ^pT>ekj@d58dH9NO#E?-6(;n zUMle;-9V`V+yL2#g8WV)t(^Ws{mXPU4FfL{cfTV!&e5rMrn0T__+h;p=B|Aw|Ns`6~P-hD~_~@Fdm6Y=sw24~1=(|_kNCR)d zERMF0s*qWT8rUrf5c9HoA)#M=VxzRQ1|zin_e_l?HFXn*Y6e`A7t8$Vz-j3H19A4` zjPb&!JARYsyTC2>!pbMx3ittQog#(l)9nW{u0x9bU$zr2C&w&Q zKX_R3%VJvFqeOdimxt>MGj8cQYCIGxvr_PY8C3?X%BYTKCuc~j(q{@!~dPSS!FrH zr|4HaBD^Kw_F~P|FARUmb>GMqmL7^YGEigCi6kvu>g7r-3NI>FE{j)5f5L8MjE)~& zg;J-~tcYn=xn?>dHIBS%TJk=~L9#_9>_k6fB|`8{~}r3)dH*UL-bb z^k|=(J9Osr6P5?qC>wP`8&nwc`diTbk3LvqD$*?opjzFy<*mp)_>Hy{q7S!3;4K1F z13XU4z-r2m)HEC16GO&4D^Mv!M}R}|mc1`jmMgH@yMowv;hLfpA)%Sw+!t!ssPPav zdk&lSdMyr9HeG!i%v(|cKindlCMg{hQ5H@IkujQIP4-U13nM~-6 z^;x1j(@OK8s*>P~8O$)m2oE@XBxU}rK7NBkCFX>~zN%P0m@f4o^ldFhsM z5fe6JCB&N)XkbS5zr&nH!a}F@T%*zJX$&&+d*i*5!`&&76eOO>T|x~B`HreO@r+Ab z5yoS!i>!)jNC9um3$sgKaYk_siZ!AeIf9krSPShr0*ASZrppg?>JxCb@rE2~NsqL0 zVR-R8L1l8G!?2!+`sEJVOud$ zI~w0pgaPC}ddp9z!N^4RsB5Qms=(lArgZ`=HN0YE!y=ryxhOg2T7}|#qq_dL;V1s~ zT$LDK#C82wf&Jg3n6s6WxtXoCv7FVH|3J~!(9+oHFS@8~)iYOQQ;grz4eRSuGJsU# zc7SUj8f)J!C|Xch{X+OAR+xkuf&v<@1+` z(i}B-)!}sC`W@d7*mDMZ^FMdi$4S<+mwx3m-tk;Hdgi`yI^OB}dcgIS((lIL%%R-t z3y8?`Q<5N(k<;0+_=^V*kzg`%jDQJ(r!56aJlM+AuOk?i-Y$_={%cvFW% zrJ=^wPPUO9_23`MW5kZTY4O*x9YECkD01aU?UC2}NcxAA8gnJ~H3}TH1Lvq-TX%gw zfO-ycZN_wOH=H#|gqgyy_7QmH*1CMV=PeoMBRufI44Ql4Zj z&1#^x_*ugJ0~wFy9l!gO^U|p|s5vpD7m#%SQfl{iAvw!w67&YM)y9ZeGnalFi1z>i zNzZJP^l!hQ6{%f&h;seXGR;xvRpg^M(jT(vIl#@Qm>srDN|I`_k~2R~9;&RL*D({o zoWTvF3VO#L#7e@;L+YG7890A*mT<4<>K=^W(aTY^hnyg6pPCE1J~X6q3%D_Tdn>du zW9HT#C`p#EToPVJ)QEyYY+;f>n`+os`WQru5E*90$&A`&#$6o$5li#+fUL;+O|)EL zP>va?TY`IMPz`4DuS^w|E3BHCp*Ym10k-PMYeJS|x1KR^#Xhl0HX3t<9QS&mnO`nV zV4L%P*tH;111+%#5)>#aVsRfD$V5bHG(9>F<@$r=%kP$}mU;X&&68W$EZqCTTMOKv zO2B5q*3zn|XOxTIn`mE)fRsv>822E~y66kloXZO;+oxV-Cfw4c-Xc`g4kpqRog+u$ z$YG^@g$B2Xo#KDT$=gR0SoRR{bE-58YsF;dh*yGHZQeZ0#pM`dXe8vfV+rPWdGiQZ zc`7R0bFgR9rdIZZ$@WHQuT?w1U+M#~x{>3n&uPbmHwg$27DWlZxdouLY9)LaPN8s%1lS8diQ+8L<9c3`Tq&ljRoJEWS9^lV7h3 zt0G-#)CR3WmiU<66tTPibYLHyE)z+Wys%Vwz_Eky+1+{iIx%C)zdEIK1E!@vyy{}U zD2YZr$|%9`S!RIhh{*HM3u|Ga$?$PH#I8Y+Ppc3)YP~+j5@xH7IIEdLKVH8lqcR!N z&BdirI6&8$NF|>Bz^c$p1>wm!7~LWyb=hM=qM~k+>`r5owiaaGn9ah%d`^(6xAB0m zNFjxLEOUldd?e|I0EbDVvgBK(L(YdqO@nUVI>|B39c3#^@V)1cAa&Y311*ltLTtLq8aU zEV@C9|~bs|?6|dJcP?WZGoe*0~d%>sy+-z}# zrCx<-0deMe7d4%NrGHXEF zPPXd+*Ia{txpJwr$#p>5buR{I ztbFyNle%N?eng=F71tjLjK9C$X8i=Qr+(=n(Jfp|y#~E%lLbE0txwhkBv3m^6VHRj zU>TI>kI==z+B0D6Wp~-*db~3{gr_-(bW1XKhc3NgGtTh~BprW1ZswNuo|U_6OzU@C zdgrn^0PzY#yJ78$>ODkx57hcSRLL(;V}Rp4@r>;tH3{94hxhi}?udjF^oZ0Egpb{K z(%u`!_zVb|CR9Q$mG1{@Kqq!at>MB(mZq819*YZc3T|u&kVINT?RQ!FVgLGwA3{#) z=!yXsmysu1s}C%yK3r=4LG}dsajWL@7C)q>Ccv!W-vlP?jMSLygKZiQGAvX^TvzC- zYN077)Y$gHaFkY-xP)`Jw&T&PlauYAZ95;)njTHbujFB|;Ed-9#991m4mZDW<8fsL z)#iT$=t8%j?uxH%Yu3UakqsTg6-)~@RVuil+Li;cUkmtWr|2Kj7Q726T;DG=+xvxP z8U7oZ{YQoHpNqg`rGIE!QmD+k{RlUM00mK}1XGM{rEvX@;f?Op^gw$KgDXa#8KiE#rx+&weEZsey(ft@-Zm4Pu<^peK`AacfURL`u2d(#lip` zrp+2auQorZW9DLs8Sn(s-hUSRW3{FCS3{#hC)Jf@>;61;0oBE73v>XL#Gt|&vSW?* zG~Y5se+WLgek{568coJZ?u0(a9}G8EeT|T89^dpA>u!I1{rR}|f)HXf`G7LGUU9~3 zy>tAKQl}~5e1W2&a#}M)*L5l;x^03!A#1BJ*02c=U0M-?92+pxa&6(&DV?LWT&-v! z12V_%LZUo*gYo0*y+v(uYYNGTjtX|1eb8S!ksYTL6`2V#N0qYAj5e+9RvTv%jz*0| zjisCePwtFTt6+L%L;5k6sw=QBF{!O6`(Jjm;1q|#efa=7<%$`gTtsQ;q9Rzgf

~ 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 z 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

X5iT4~Zv-|Ea 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 literal 0 HcmV?d00001 diff --git a/program.own b/program.own index 859a3c33..4cc1cfd9 100644 --- a/program.own +++ b/program.own @@ -124,4 +124,15 @@ nums = [1,2,3,4,5,6,7,8,9,10] nums = filter(nums, def(x) = x % 2 == 0) squares = map(nums, def(x) = x * x) foreach(squares, ::echo) -println "Sum: " + reduce(squares, 0, def(x, y) = x + y) \ No newline at end of file +println "Sum: " + reduce(squares, 0, def(x, y) = x + y) + +use "http" + +http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { + println "Added: " + v + http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Patched Name"}, ::http_get_demo) +}) +def http_get_demo(v) { + println "Updated: " + v + http("http://jsonplaceholder.typicode.com/users", ::echo) +} diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index ac2b49fc..b2544f50 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -7,6 +7,8 @@ * @author aNNiMON */ public final class FunctionValue implements Value { + + public static final FunctionValue EMPTY = new FunctionValue(args -> NumberValue.ZERO); private final Function value; diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index c55bd947..3eff1c7b 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -11,6 +11,8 @@ */ public class MapValue implements Value, Iterable> { + public static final MapValue EMPTY = new MapValue(1); + private final Map map; public MapValue(int size) { @@ -29,6 +31,10 @@ public int type() { public int size() { return map.size(); } + + public boolean containsKey(Value key) { + return map.containsKey(key); + } public Value get(Value key) { return map.get(key); diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index 59fed483..f38206fa 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -14,6 +14,10 @@ public StringValue(String value) { this.value = value; } + public int length() { + return value.length(); + } + @Override public int type() { return Types.STRING; diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_http.java b/src/com/annimon/ownlang/lib/modules/functions/http_http.java new file mode 100644 index 00000000..50df52e5 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/http_http.java @@ -0,0 +1,173 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.*; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicNameValuePair; + +public final class http_http implements Function { + + private static final Value + HEADER_KEY = new StringValue("header"), + CHARSET_KEY = new StringValue("charset"); + + @Override + public Value execute(Value... args) { + String url, method; + switch (args.length) { + case 1: // http(url) + url = args[0].asString(); + return process(url, "GET"); + + case 2: // http(url, method) || http(url, callback) + url = args[0].asString(); + if (args[1].type() == Types.FUNCTION) { + return process(url, "GET", (FunctionValue) args[1]); + } + return process(url, args[1].asString()); + + case 3: // http(url, method, params) || http(url, method, callback) + url = args[0].asString(); + method = args[1].asString(); + if (args[2].type() == Types.FUNCTION) { + return process(url, method, (FunctionValue) args[2]); + } + return process(url, method, args[2], FunctionValue.EMPTY); + + case 4: // http(url, method, params, callback) + if (args[3].type() != Types.FUNCTION) { + throw new RuntimeException("Fourth arg must be a function callback"); + } + url = args[0].asString(); + method = args[1].asString(); + return process(url, method, args[2], (FunctionValue) args[3]); + + case 5: // http(url, method, params, headerParams, callback) + if (args[3].type() != Types.MAP) { + throw new RuntimeException("Third arg must be a map"); + } + if (args[4].type() != Types.FUNCTION) { + throw new RuntimeException("Fifth arg must be a function callback"); + } + url = args[0].asString(); + method = args[1].asString(); + return process(url, method, args[2], (MapValue) args[3], (FunctionValue) args[4]); + + default: + throw new RuntimeException("Wrong number of arguments"); + } + } + + private Value process(String url, String method) { + return process(url, method, FunctionValue.EMPTY); + } + + private Value process(String url, String method, FunctionValue function) { + return process(url, method, MapValue.EMPTY, function); + } + + private Value process(String url, String method, Value params, FunctionValue function) { + return process(url, method, params, MapValue.EMPTY, function); + } + + private Value process(String url, String method, Value requestParams, MapValue options, FunctionValue function) { + final Function callback = function.getValue(); + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + HttpRequestBase httpMethod; + switch (method.toUpperCase()) { + case "POST": + httpMethod = new HttpPost(url); + break; + case "PUT": + httpMethod = new HttpPut(url); + break; + case "DELETE": + httpMethod = new HttpDelete(url); + break; + case "PATCH": + httpMethod = new HttpPatch(url); + break; + case "HEAD": + httpMethod = new HttpHead(url); + break; + case "OPTIONS": + httpMethod = new HttpOptions(url); + break; + case "TRACE": + httpMethod = new HttpTrace(url); + break; + case "GET": + default: + httpMethod = new HttpGet(url); + break; + } + + if (options.containsKey(HEADER_KEY)) { + applyHeaderParams((MapValue) options.get(HEADER_KEY), httpMethod); + } + + if (httpMethod instanceof HttpEntityEnclosingRequestBase) { + final HttpEntityEnclosingRequestBase heerb = (HttpEntityEnclosingRequestBase) httpMethod; + if (requestParams.type() == Types.MAP) { + applyMapRequestParams(heerb, (MapValue) requestParams, options); + } else { + applyStringRequestParams(heerb, requestParams, options); + } + } + + final HttpResponse httpResponse = httpClient.execute(httpMethod); + final String response = new BasicResponseHandler().handleResponse(httpResponse); + callback.execute(new StringValue(response)); + return NumberValue.fromBoolean(true); + } catch (IOException ex) { + return NumberValue.fromBoolean(false); + } + } + + private void applyHeaderParams(MapValue headerParams, HttpRequestBase httpMethod) { + for (Map.Entry p : headerParams) { + httpMethod.addHeader(p.getKey().asString(), p.getValue().asString()); + } + } + + private void applyMapRequestParams(HttpEntityEnclosingRequestBase h, MapValue params, MapValue options) + throws UnsupportedEncodingException { + final List entityParams = new ArrayList<>(params.size()); + for (Map.Entry param : params) { + final String name = param.getKey().asString(); + final String value = param.getValue().asString(); + entityParams.add(new BasicNameValuePair(name, value)); + } + HttpEntity entity; + if (options.containsKey(CHARSET_KEY)) { + entity = new UrlEncodedFormEntity(entityParams, options.get(CHARSET_KEY).asString()); + } else { + entity = new UrlEncodedFormEntity(entityParams); + } + h.setEntity(entity); + } + + private void applyStringRequestParams(final HttpEntityEnclosingRequestBase heerb, Value requestParams, MapValue options) throws UnsupportedEncodingException, UnsupportedCharsetException { + HttpEntity entity; + if (options.containsKey(CHARSET_KEY)) { + entity = new StringEntity(requestParams.asString(), options.get(CHARSET_KEY).asString()); + } else { + entity = new StringEntity(requestParams.asString()); + } + heerb.setEntity(entity); + } + +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java b/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java new file mode 100644 index 00000000..02ff8ff0 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import java.io.IOException; +import java.net.URLEncoder; + +public final class http_urlencode implements Function { + + @Override + public Value execute(Value... args) { + if (args.length == 0) throw new RuntimeException("At least one arg expected"); + + String charset = "UTF-8"; + if (args.length >= 2) { + charset = args[1].asString(); + } + + try { + final String result = URLEncoder.encode(args[0].asString(), charset); + return new StringValue(result); + } catch (IOException ex) { + return args[0]; + } + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/http.java b/src/com/annimon/ownlang/lib/modules/http.java new file mode 100644 index 00000000..1138cbd2 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/http.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.*; + +/** + * + * @author aNNiMON + */ +public final class http implements Module { + + @Override + public void init() { + Functions.set("urlencode", new http_urlencode()); + Functions.set("http", new http_http()); + } +} From ca2462dff8033241fe74278da7e4a526e496a51a Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 12 Jan 2016 22:50:32 +0200 Subject: [PATCH 018/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20length?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/modules/functions/std_length.java | 37 +++++++++++++++++++ src/com/annimon/ownlang/lib/modules/std.java | 1 + 2 files changed, 38 insertions(+) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_length.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_length.java b/src/com/annimon/ownlang/lib/modules/functions/std_length.java new file mode 100644 index 00000000..b6aa93d4 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_length.java @@ -0,0 +1,37 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +public final class std_length implements Function { + + @Override + public Value execute(Value... args) { + if (args.length == 0) throw new RuntimeException("At least one arg expected"); + + final Value val = args[0]; + int length; + switch (val.type()) { + case Types.ARRAY: + length = ((ArrayValue) val).size(); + break; + case Types.MAP: + length = ((MapValue) val).size(); + break; + case Types.STRING: + length = ((StringValue) val).length(); + break; + case Types.FUNCTION: + final Function func = ((FunctionValue) val).getValue(); + if (func instanceof UserDefinedFunction) { + length = ((UserDefinedFunction) func).getArgsCount(); + } else { + length = 0; + } + break; + default: + length = 0; + + } + return new NumberValue(length); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index 39196987..d5e7746a 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -13,6 +13,7 @@ public final class std implements Module { public void init() { Functions.set("echo", new std_echo()); Functions.set("newarray", new std_newarray()); + Functions.set("length", new std_length()); Functions.set("rand", new std_rand()); Functions.set("sleep", new std_sleep()); Functions.set("thread", new std_thread()); From f8854759ff1e33b112357b98d14968e44861f5db Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 12 Jan 2016 23:14:56 +0200 Subject: [PATCH 019/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/network/github_timeline.own | 68 ++++++++++++++++++ libs/json-20151123.jar | Bin 0 -> 48224 bytes nbproject/build-impl.xml | 7 +- nbproject/genfiles.properties | 4 +- nbproject/project.properties | 16 ++++- program.own | 18 ++++- .../lib/modules/functions/json_decode.java | 61 ++++++++++++++++ .../lib/modules/functions/json_encode.java | 53 ++++++++++++++ src/com/annimon/ownlang/lib/modules/json.java | 17 +++++ 9 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 examples/network/github_timeline.own create mode 100644 libs/json-20151123.jar create mode 100644 src/com/annimon/ownlang/lib/modules/functions/json_decode.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/json_encode.java create mode 100644 src/com/annimon/ownlang/lib/modules/json.java diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own new file mode 100644 index 00000000..2363f3b8 --- /dev/null +++ b/examples/network/github_timeline.own @@ -0,0 +1,68 @@ +use "std" +use "http" +use "json" +use "functional" + +header = "* Prints current GitHub timeline *" +println "*" * length(header) +println header +println "*" * length(header) + +// Executes in main thread +//http("https://api.github.com/events", def(r) { +// foreach(jsondecode(r), ::show_github_events) +//}) + +// Executes in new thread +thread(::http, "https://api.github.com/events", def(r) { + foreach(jsondecode(r), ::show_github_events) +}) + +def show_github_events(event) { + println event["created_at"] + actor = event["actor"] + println "User: https://github.com/" + actor["login"] + println github_event_type(event) + println "-" * 50 +} + +def github_event_type(event) { + type = event["type"] + repo = event["repo"] + repo = "https://github.com/" + repo["name"] + payload = event["payload"] + + if (type == "CommitCommentEvent") { + comment = payload["comment"] + return "commented commit in " + repo + "\n" + comment["body"] + } + if (type == "CreateEvent") { + return "created " + payload["ref_type"] + " on " + repo + } + if (type == "DeleteEvent") { + return "deleted " + payload["ref_type"] + " on " + repo + } + if (type == "ForkEvent") { + return "forked repository " + repo + } + if (type == "IssueCommentEvent") { + comment = payload["comment"] + issue = payload["issue"] + return "commented issue " + issue["title"] + " on " + repo + "\n" + comment["body"] + } + if (type == "IssuesEvent") { + issue = payload["issue"] + return payload["action"] + " issue '" + issue["title"] + "' on " + repo + } + if (type == "PullRequestEvent") { + pr = payload["pull_request"] + return payload["action"] + " pull request #" + payload["number"] + " '" + pr["title"] + "' on " + repo + } + if (type == "PushEvent") { + return "pushed " + length(payload["commits"]) + " commits to " + repo + } + if (type == "WatchEvent") { + return "start watching repository " + repo + } + return type + " on " + repo +} diff --git a/libs/json-20151123.jar b/libs/json-20151123.jar new file mode 100644 index 0000000000000000000000000000000000000000..472b253051df5ee5c3aac9c34a575aaf40fc70c3 GIT binary patch literal 48224 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 literal 0 HcmV?d00001 diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index b7e25008..6d4a3457 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -222,6 +222,7 @@ is divided into following sections: + @@ -698,7 +699,7 @@ is divided into following sections: - + @@ -773,7 +774,7 @@ is divided into following sections: - + @@ -800,7 +801,7 @@ is divided into following sections: - + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index 1037409c..1cc3671e 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -4,5 +4,5 @@ build.xml.stylesheet.CRC32=8064a381@1.78.0.48 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=48c1b1a1 -nbproject/build-impl.xml.script.CRC32=e3eeb3f1 -nbproject/build-impl.xml.stylesheet.CRC32=2d327b5d@1.78.0.48 +nbproject/build-impl.xml.script.CRC32=b8cd7788 +nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.0.48 diff --git a/nbproject/project.properties b/nbproject/project.properties index 9de6066d..f4950c58 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -1,9 +1,10 @@ annotation.processing.enabled=true annotation.processing.enabled.in.editor=false -annotation.processing.processor.options= annotation.processing.processors.list= annotation.processing.run.all.processors=true annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=OwnLang +application.vendor=aNNiMON build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: @@ -26,10 +27,21 @@ dist.archive.excludes= dist.dir=dist dist.jar=${dist.dir}/OwnLang.jar dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= excludes= +file.reference.commons-codec-1.6.jar=libs/commons-codec-1.6.jar +file.reference.commons-logging-1.1.3.jar=libs/commons-logging-1.1.3.jar +file.reference.httpclient-4.3.5.jar=libs/httpclient-4.3.5.jar +file.reference.httpcore-4.3.2.jar=libs/httpcore-4.3.2.jar +file.reference.json-20151123.jar=libs/json-20151123.jar includes=** jar.compress=false -javac.classpath= +javac.classpath=\ + ${file.reference.commons-codec-1.6.jar}:\ + ${file.reference.commons-logging-1.1.3.jar}:\ + ${file.reference.httpclient-4.3.5.jar}:\ + ${file.reference.httpcore-4.3.2.jar}:\ + ${file.reference.json-20151123.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false diff --git a/program.own b/program.own index 4cc1cfd9..7ac6f023 100644 --- a/program.own +++ b/program.own @@ -128,11 +128,25 @@ println "Sum: " + reduce(squares, 0, def(x, y) = x + y) use "http" -http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { +/*http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { println "Added: " + v http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Patched Name"}, ::http_get_demo) }) def http_get_demo(v) { println "Updated: " + v http("http://jsonplaceholder.typicode.com/users", ::echo) -} +}*/ + +use "json" +println "json" +println jsonencode({ + "name": "JSON Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } +}) \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java new file mode 100644 index 00000000..97fb9d5c --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java @@ -0,0 +1,61 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; +import java.util.Iterator; +import org.json.*; + +public final class json_decode implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new RuntimeException("One argument expected"); + try { + final String jsonRaw = args[0].asString(); + final Object root = new JSONTokener(jsonRaw).nextValue(); + return process(root); + } catch (JSONException ex) { + throw new RuntimeException("Error while parsing json", ex); + } + } + + private Value process(Object obj) { + if (obj instanceof JSONObject) { + return process((JSONObject) obj); + } + if (obj instanceof JSONArray) { + return process((JSONArray) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return new NumberValue(((Number) obj).doubleValue()); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + private MapValue process(JSONObject json) { + final MapValue result = new MapValue(json.length()); + final Iterator it = json.keys(); + while(it.hasNext()) { + final String key = it.next(); + final Value value = process(json.get(key)); + result.set(new StringValue(key), value); + } + return result; + } + + private ArrayValue process(JSONArray json) { + final int length = json.length(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = process(json.get(i)); + result.set(i, value); + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java new file mode 100644 index 00000000..8263ac1d --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java @@ -0,0 +1,53 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; +import java.util.Map; +import org.json.*; + +public final class json_encode implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new RuntimeException("One argument expected"); + try { + final Object root = process(args[0]); + final String jsonRaw = JSONObject.valueToString(root); + return new StringValue(jsonRaw); + } catch (JSONException ex) { + throw new RuntimeException("Error while creating json", ex); + } + } + + private Object process(Value val) { + switch (val.type()) { + case Types.ARRAY: + return process((ArrayValue) val); + case Types.MAP: + return process((MapValue) val); + case Types.NUMBER: + return val.asNumber(); + case Types.STRING: + return val.asString(); + default: + return JSONObject.NULL; + } + } + + private Object process(MapValue map) { + final JSONObject result = new JSONObject(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = process(entry.getValue()); + result.put(key, value); + } + return result; + } + + private Object process(ArrayValue array) { + final JSONArray result = new JSONArray(); + for (Value value : array) { + result.put(process(value)); + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/json.java b/src/com/annimon/ownlang/lib/modules/json.java new file mode 100644 index 00000000..6af5997f --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/json.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.*; + +/** + * + * @author aNNiMON + */ +public final class json implements Module { + + @Override + public void init() { + Functions.set("jsonencode", new json_encode()); + Functions.set("jsondecode", new json_decode()); + } +} From d0c8c75734ed571eb82e4f0560ca11ddbdc5c51b Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 12 Jan 2016 23:52:26 +0200 Subject: [PATCH 020/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=B4?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=83=D0=BF=20=D0=BA=20=D1=8D=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D0=BC=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/network/github_timeline.own | 13 +++------ src/com/annimon/ownlang/parser/Parser.java | 5 ++++ .../parser/ast/ArrayAccessExpression.java | 29 ++++++++++++------- .../parser/ast/ArrayAssignmentStatement.java | 5 ++-- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own index 2363f3b8..b04c2c24 100644 --- a/examples/network/github_timeline.own +++ b/examples/network/github_timeline.own @@ -28,13 +28,11 @@ def show_github_events(event) { def github_event_type(event) { type = event["type"] - repo = event["repo"] - repo = "https://github.com/" + repo["name"] + repo = "https://github.com/" + event["repo"]["name"] payload = event["payload"] if (type == "CommitCommentEvent") { - comment = payload["comment"] - return "commented commit in " + repo + "\n" + comment["body"] + return "commented commit in " + repo + "\n" + payload["comment"]["body"] } if (type == "CreateEvent") { return "created " + payload["ref_type"] + " on " + repo @@ -46,13 +44,10 @@ def github_event_type(event) { return "forked repository " + repo } if (type == "IssueCommentEvent") { - comment = payload["comment"] - issue = payload["issue"] - return "commented issue " + issue["title"] + " on " + repo + "\n" + comment["body"] + return "commented issue " + payload["issue"]["title"] + " on " + repo + "\n" + payload["comment"]["body"] } if (type == "IssuesEvent") { - issue = payload["issue"] - return payload["action"] + " issue '" + issue["title"] + "' on " + repo + return payload["action"] + " issue '" + payload["issue"]["title"] + "' on " + repo } if (type == "PullRequestEvent") { pr = payload["pull_request"] diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 4d6403ad..9608ebfa 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -172,6 +172,7 @@ private ForeachMapStatement foreachMapStatement() { } private FunctionDefineStatement functionDefine() { + // def name(arg1, arg2) { ... } || def name(args) = expr final String name = consume(TokenType.WORD).getText(); consume(TokenType.LPAREN); final List argNames = new ArrayList<>(); @@ -188,6 +189,7 @@ private FunctionDefineStatement functionDefine() { } private FunctionalExpression function() { + // function(arg1, arg2, ...) final String name = consume(TokenType.WORD).getText(); consume(TokenType.LPAREN); final FunctionalExpression function = new FunctionalExpression(name); @@ -199,6 +201,7 @@ private FunctionalExpression function() { } private Expression array() { + // [value1, value2, ...] consume(TokenType.LBRACKET); final List elements = new ArrayList<>(); while (!match(TokenType.RBRACKET)) { @@ -209,6 +212,7 @@ private Expression array() { } private Expression map() { + // {key1 : value1, key2 : value2, ...} consume(TokenType.LBRACE); final Map elements = new HashMap<>(); while (!match(TokenType.RBRACE)) { @@ -222,6 +226,7 @@ private Expression map() { } private ArrayAccessExpression element() { + // array[e1][e2]...[eN] final String variable = consume(TokenType.WORD).getText(); final List indices = new ArrayList<>(); do { diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java index 45f65c40..e5dfff81 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java @@ -1,10 +1,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import java.util.List; /** @@ -25,26 +21,37 @@ public ArrayAccessExpression(String variable, List indices) { public Value eval() { Value container = Variables.get(variable); if (container.type() == Types.ARRAY) { - return getArray().get(lastIndex()); + final int lastIndex = (int) lastIndex().asNumber(); + return getArray().get(lastIndex); } - return consumeMap(container).get(indices.get(0).eval()); + return getMap().get(lastIndex()); } public ArrayValue getArray() { ArrayValue array = consumeArray(Variables.get(variable)); final int last = indices.size() - 1; for (int i = 0; i < last; i++) { - array = consumeArray( array.get(index(i)) ); + final int index = (int) index(i).asNumber(); + array = consumeArray( array.get(index) ); } return array; } - public int lastIndex() { + public MapValue getMap() { + MapValue map = consumeMap(Variables.get(variable)); + final int last = indices.size() - 1; + for (int i = 0; i < last; i++) { + map = consumeMap( map.get(index(i)) ); + } + return map; + } + + public Value lastIndex() { return index(indices.size() - 1); } - private int index(int index) { - return (int) indices.get(index).eval().asNumber(); + private Value index(int index) { + return indices.get(index).eval(); } private ArrayValue consumeArray(Value value) { diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java index b2222ac2..d39b7f6c 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java @@ -22,10 +22,11 @@ public ArrayAssignmentStatement(ArrayAccessExpression array, Expression expressi public void execute() { final Value container = Variables.get(array.variable); if (container.type() == Types.ARRAY) { - array.getArray().set(array.lastIndex(), expression.eval()); + final int lastIndex = (int) array.lastIndex().asNumber(); + array.getArray().set(lastIndex, expression.eval()); return; } - array.consumeMap(container).set(array.indices.get(0).eval(), expression.eval()); + array.getMap().set(array.lastIndex(), expression.eval()); } @Override From 794d6f2d76a614a21a9e0c341aa138b2c433a468 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 13 Jan 2016 00:09:44 +0200 Subject: [PATCH 021/448] =?UTF-8?q?=D0=A1=D0=B8=D0=BD=D1=82=D0=B0=D0=BA?= =?UTF-8?q?=D1=81=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20=D1=81=D0=B0?= =?UTF-8?q?=D1=85=D0=B0=D1=80=20-=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=20=D0=BA=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=20map=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D1=82=D0=BE?= =?UTF-8?q?=D1=87=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/network/github_timeline.own | 28 +++++++++---------- src/com/annimon/ownlang/parser/Lexer.java | 3 +- src/com/annimon/ownlang/parser/Parser.java | 16 +++++++++++ src/com/annimon/ownlang/parser/TokenType.java | 1 + 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own index b04c2c24..41dfc0a8 100644 --- a/examples/network/github_timeline.own +++ b/examples/network/github_timeline.own @@ -19,42 +19,42 @@ thread(::http, "https://api.github.com/events", def(r) { }) def show_github_events(event) { - println event["created_at"] - actor = event["actor"] - println "User: https://github.com/" + actor["login"] + println event.created_at + actor = event.actor + println "User: https://github.com/" + actor.login println github_event_type(event) println "-" * 50 } def github_event_type(event) { - type = event["type"] - repo = "https://github.com/" + event["repo"]["name"] - payload = event["payload"] + type = event.type + repo = "https://github.com/" + event.repo.name + payload = event.payload if (type == "CommitCommentEvent") { - return "commented commit in " + repo + "\n" + payload["comment"]["body"] + return "commented commit in " + repo + "\n" + payload.comment.body } if (type == "CreateEvent") { - return "created " + payload["ref_type"] + " on " + repo + return "created " + payload.ref_type + " on " + repo } if (type == "DeleteEvent") { - return "deleted " + payload["ref_type"] + " on " + repo + return "deleted " + payload.ref_type + " on " + repo } if (type == "ForkEvent") { return "forked repository " + repo } if (type == "IssueCommentEvent") { - return "commented issue " + payload["issue"]["title"] + " on " + repo + "\n" + payload["comment"]["body"] + return "commented issue " + payload.issue.title + " on " + repo + "\n" + payload.comment.body } if (type == "IssuesEvent") { - return payload["action"] + " issue '" + payload["issue"]["title"] + "' on " + repo + return payload.action + " issue '" + payload.issue.title + "' on " + repo } if (type == "PullRequestEvent") { - pr = payload["pull_request"] - return payload["action"] + " pull request #" + payload["number"] + " '" + pr["title"] + "' on " + repo + pr = payload.pull_request + return payload.action + " pull request #" + payload.number + " '" + pr.title + "' on " + repo } if (type == "PushEvent") { - return "pushed " + length(payload["commits"]) + " commits to " + repo + return "pushed " + length(payload.commits) + " commits to " + repo } if (type == "WatchEvent") { return "start watching repository " + repo diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 2005dc3e..455f8e88 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -11,7 +11,7 @@ */ public final class Lexer { - private static final String OPERATOR_CHARS = "+-*/%()[]{}=<>!&|,^~?:"; + private static final String OPERATOR_CHARS = "+-*/%()[]{}=<>!&|.,^~?:"; private static final Map OPERATORS; static { @@ -30,6 +30,7 @@ public final class Lexer { OPERATORS.put("=", TokenType.EQ); OPERATORS.put("<", TokenType.LT); OPERATORS.put(">", TokenType.GT); + OPERATORS.put(".", TokenType.DOT); OPERATORS.put(",", TokenType.COMMA); OPERATORS.put("^", TokenType.CARET); OPERATORS.put("~", TokenType.TILDE); diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 9608ebfa..7b68c9af 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -237,6 +237,19 @@ private ArrayAccessExpression element() { return new ArrayAccessExpression(variable, indices); } + private ArrayAccessExpression object() { + // object.field1.field2 + // Syntaxic sugar for object["field1"]["field2"] + final String variable = consume(TokenType.WORD).getText(); + final List indices = new ArrayList<>(); + while (match(TokenType.DOT)) { + final String fieldName = consume(TokenType.WORD).getText(); + final Expression key = new ValueExpression(fieldName); + indices.add(key); + } + return new ArrayAccessExpression(variable, indices); + } + private Expression expression() { return ternary(); } @@ -459,6 +472,9 @@ private Expression primary() { if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return function(); } + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.DOT)) { + return object(); + } if (lookMatch(0, TokenType.LBRACKET)) { return array(); } diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index adc344a9..77e47239 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -61,6 +61,7 @@ public enum TokenType { LBRACE, // { RBRACE, // } COMMA, // , + DOT, // . EOF } From 4cb076ca6d447b891da4d0cfee0f4ece157bf379 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 15 Jan 2016 21:42:04 +0200 Subject: [PATCH 022/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=20match=20(Pattern=20Matching)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/network/github_timeline.own | 41 ++---- program.own | 19 ++- src/com/annimon/ownlang/lib/Variables.java | 4 + src/com/annimon/ownlang/parser/Lexer.java | 2 + src/com/annimon/ownlang/parser/Parser.java | 122 +++++++++++++----- src/com/annimon/ownlang/parser/TokenType.java | 2 + .../ownlang/parser/ast/MatchExpression.java | 119 +++++++++++++++++ .../annimon/ownlang/parser/ast/Visitor.java | 1 + .../parser/visitors/AbstractVisitor.java | 5 + 9 files changed, 253 insertions(+), 62 deletions(-) create mode 100644 src/com/annimon/ownlang/parser/ast/MatchExpression.java diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own index 41dfc0a8..59089fc1 100644 --- a/examples/network/github_timeline.own +++ b/examples/network/github_timeline.own @@ -27,37 +27,18 @@ def show_github_events(event) { } def github_event_type(event) { - type = event.type repo = "https://github.com/" + event.repo.name payload = event.payload - - if (type == "CommitCommentEvent") { - return "commented commit in " + repo + "\n" + payload.comment.body + return match event.type { + case "CommitCommentEvent": "commented commit in " + repo + "\n" + payload.comment.body + case "CreateEvent": "created " + payload.ref_type + " on " + repo + case "DeleteEvent": "deleted " + payload.ref_type + " on " + repo + case "ForkEvent": "forked repository " + repo + case "IssueCommentEvent": "commented issue " + payload.issue.title + " on " + repo + "\n" + payload.comment.body + case "IssuesEvent": payload.action + " issue '" + payload.issue.title + "' on " + repo + case "PullRequestEvent": payload.action + " pull request #" + payload.number + " '" + payload.pull_request.title + "' on " + repo + case "PushEvent": "pushed " + length(payload.commits) + " commits to " + repo + case "WatchEvent": "start watching repository " + repo + case type : type + " on " + repo } - if (type == "CreateEvent") { - return "created " + payload.ref_type + " on " + repo - } - if (type == "DeleteEvent") { - return "deleted " + payload.ref_type + " on " + repo - } - if (type == "ForkEvent") { - return "forked repository " + repo - } - if (type == "IssueCommentEvent") { - return "commented issue " + payload.issue.title + " on " + repo + "\n" + payload.comment.body - } - if (type == "IssuesEvent") { - return payload.action + " issue '" + payload.issue.title + "' on " + repo - } - if (type == "PullRequestEvent") { - pr = payload.pull_request - return payload.action + " pull request #" + payload.number + " '" + pr.title + "' on " + repo - } - if (type == "PushEvent") { - return "pushed " + length(payload.commits) + " commits to " + repo - } - if (type == "WatchEvent") { - return "start watching repository " + repo - } - return type + " on " + repo } diff --git a/program.own b/program.own index 7ac6f023..bc1bbb58 100644 --- a/program.own +++ b/program.own @@ -81,7 +81,7 @@ for i = 0, i < 4, i = i + 1 { } // map -map = {"+" : add, "-" : sub. "*" : mul, "/" : div} +map = {"+" : add, "-" : sub, "*" : mul, "/" : div} map["%"] = def(x,y) = x % y map["pow"] = def(x,y) = pow(x, y) println map["+"] @@ -149,4 +149,19 @@ println jsonencode({ "key": "value", 10: "1000" } -}) \ No newline at end of file +}) + +println "" + + +def fact1(n) = match n { + case 0: 1 + case n: n * fact1(n - 1) +} +def fact2(n) = match n { + case n if n == 0: 1 + case _: n * fact2(n - 1) +} + +println fact1(6) +println fact2(6) \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/Variables.java b/src/com/annimon/ownlang/lib/Variables.java index 543fd370..e11d3ffd 100644 --- a/src/com/annimon/ownlang/lib/Variables.java +++ b/src/com/annimon/ownlang/lib/Variables.java @@ -40,4 +40,8 @@ public static Value get(String key) { public static void set(String key, Value value) { variables.put(key, value); } + + public static void remove(String key) { + variables.remove(key); + } } diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 455f8e88..516a503d 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -173,6 +173,8 @@ private void tokenizeWord() { case "def": addToken(TokenType.DEF); break; case "return": addToken(TokenType.RETURN); break; case "use": addToken(TokenType.USE); break; + case "match": addToken(TokenType.MATCH); break; + case "case": addToken(TokenType.CASE); 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 7b68c9af..1c90a130 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.ArrayList; @@ -250,6 +252,53 @@ private ArrayAccessExpression object() { return new ArrayAccessExpression(variable, indices); } + private MatchExpression match() { + // match expression { + // case pattern1: result1 + // case pattern2 if extr: result2 + // } + final Expression expression = expression(); + consume(TokenType.LBRACE); + final List patterns = new ArrayList<>(); + do { + consume(TokenType.CASE); + MatchExpression.Pattern pattern = null; + final Token current = get(0); + if (match(TokenType.NUMBER)) { + pattern = new MatchExpression.ConstantPattern( + new NumberValue(Double.parseDouble(current.getText())) + ); + } else if (match(TokenType.HEX_NUMBER)) { + pattern = new MatchExpression.ConstantPattern( + new NumberValue(Long.parseLong(current.getText(), 16)) + ); + } else if (match(TokenType.TEXT)) { + pattern = new MatchExpression.ConstantPattern( + new StringValue(current.getText()) + ); + } else if (match(TokenType.WORD)) { + pattern = new MatchExpression.VariablePattern(current.getText()); + } + + if (pattern == null) { + throw new ParseException("Wrong pattern in match expression: " + current); + } + if (match(TokenType.IF)) { + pattern.optCondition = expression(); + } + + consume(TokenType.COLON); + if (lookMatch(0, TokenType.LBRACE)) { + pattern.result = block(); + } else { + pattern.result = new ReturnStatement(expression()); + } + patterns.add(pattern); + } while (!match(TokenType.RBRACE)); + + return new MatchExpression(expression, patterns); + } + private Expression expression() { return ternary(); } @@ -459,13 +508,40 @@ private Expression unary() { } private Expression primary() { - final Token current = get(0); - if (match(TokenType.NUMBER)) { - return new ValueExpression(Double.parseDouble(current.getText())); + if (match(TokenType.LPAREN)) { + Expression result = expression(); + match(TokenType.RPAREN); + return result; } - if (match(TokenType.HEX_NUMBER)) { - return new ValueExpression(Long.parseLong(current.getText(), 16)); + + if (match(TokenType.COLONCOLON)) { + final String functionName = consume(TokenType.WORD).getText(); + return new FunctionReferenceExpression(functionName); + } + if (match(TokenType.MATCH)) { + return match(); + } + if (match(TokenType.DEF)) { + consume(TokenType.LPAREN); + final List argNames = new ArrayList<>(); + while (!match(TokenType.RPAREN)) { + argNames.add(consume(TokenType.WORD).getText()); + match(TokenType.COMMA); + } + Statement statement; + if (lookMatch(0, TokenType.EQ)) { + match(TokenType.EQ); + statement = new ReturnStatement(expression()); + } else { + statement = statementOrBlock(); + } + return new ValueExpression(new UserDefinedFunction(argNames, statement)); } + return variable(); + } + + private Expression variable() { + final Token current = get(0); if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { return element(); } @@ -484,33 +560,19 @@ private Expression primary() { if (match(TokenType.WORD)) { return new VariableExpression(current.getText()); } - if (match(TokenType.TEXT)) { - return new ValueExpression(current.getText()); - } - if (match(TokenType.COLONCOLON)) { - final String functionName = consume(TokenType.WORD).getText(); - return new FunctionReferenceExpression(functionName); + return value(); + } + + private Expression value() { + final Token current = get(0); + if (match(TokenType.NUMBER)) { + return new ValueExpression(Double.parseDouble(current.getText())); } - if (match(TokenType.DEF)) { - consume(TokenType.LPAREN); - final List argNames = new ArrayList<>(); - while (!match(TokenType.RPAREN)) { - argNames.add(consume(TokenType.WORD).getText()); - match(TokenType.COMMA); - } - Statement statement; - if (lookMatch(0, TokenType.EQ)) { - match(TokenType.EQ); - statement = new ReturnStatement(expression()); - } else { - statement = statementOrBlock(); - } - return new ValueExpression(new UserDefinedFunction(argNames, statement)); + if (match(TokenType.HEX_NUMBER)) { + return new ValueExpression(Long.parseLong(current.getText(), 16)); } - if (match(TokenType.LPAREN)) { - Expression result = expression(); - match(TokenType.RPAREN); - return result; + if (match(TokenType.TEXT)) { + return new ValueExpression(current.getText()); } throw new ParseException("Unknown expression: " + current); } diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 77e47239..1507c8b6 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -24,6 +24,8 @@ public enum TokenType { DEF, RETURN, USE, + MATCH, + CASE, PLUS, // + MINUS, // - diff --git a/src/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/com/annimon/ownlang/parser/ast/MatchExpression.java new file mode 100644 index 00000000..541577ac --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -0,0 +1,119 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; +import java.util.List; + +/** + * + * @author aNNiMON + */ +public final class MatchExpression implements Expression { + + public final Expression expression; + public final List patterns; + + public MatchExpression(Expression expression, List patterns) { + this.expression = expression; + this.patterns = patterns; + } + + @Override + public Value eval() { + final Value value = expression.eval(); + for (Pattern p : patterns) { + if (p instanceof ConstantPattern) { + final ConstantPattern pattern = (ConstantPattern) p; + if (match(value, pattern.constant) && optMatches(p)) { + return evalResult(p.result); + } + } + if (p instanceof VariablePattern) { + final VariablePattern pattern = (VariablePattern) p; + if (pattern.variable.equals("_")) return evalResult(p.result); + + if (Variables.isExists(pattern.variable)) { + if (match(value, Variables.get(pattern.variable)) && optMatches(p)) { + return evalResult(p.result); + } + } else { + Variables.set(pattern.variable, value); + if (optMatches(p)) { + final Value result = evalResult(p.result);; + Variables.remove(pattern.variable); + return result; + } + Variables.remove(pattern.variable); + } + } + } + throw new RuntimeException("No pattern were matched"); + } + + private boolean match(Value value, Value constant) { + if (value.type() != constant.type()) return false; + return value.equals(constant); + } + + private boolean optMatches(Pattern pattern) { + if (pattern.optCondition == null) return true; + return pattern.optCondition.eval() != NumberValue.ZERO; + } + + private Value evalResult(Statement s) { + try { + s.execute(); + } catch (ReturnStatement ret) { + return ret.getResult(); + } + return NumberValue.ZERO; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("match ").append(expression).append(" {"); + for (Pattern p : patterns) { + sb.append("\n case ").append(p); + } + sb.append("\n}"); + return sb.toString(); + } + + public static abstract class Pattern { + public Statement result; + public Expression optCondition; + } + + public static class ConstantPattern extends Pattern { + public Value constant; + + public ConstantPattern(Value pattern) { + this.constant = pattern; + } + + @Override + public String toString() { + return constant + ": " + result; + } + } + + public static class VariablePattern extends Pattern { + public String variable; + + public VariablePattern(String pattern) { + this.variable = pattern; + } + + @Override + public String toString() { + return variable + ": " + result; + } + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index a09cf4f1..1c1735d1 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -25,6 +25,7 @@ public interface Visitor { void visit(FunctionalExpression s); void visit(IfStatement s); void visit(MapExpression s); + void visit(MatchExpression s); void visit(PrintStatement s); void visit(PrintlnStatement s); void visit(ReturnStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 8572117f..3dfe0eb8 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -125,6 +125,11 @@ public void visit(MapExpression s) { entry.getValue().accept(this); } } + + @Override + public void visit(MatchExpression s) { + s.expression.accept(this); + } @Override public void visit(PrintStatement s) { From 5970d20a6184ad0185d67b93d395da289d69427e Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 15 Jan 2016 22:20:25 +0200 Subject: [PATCH 023/448] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/parser/Lexer.java | 59 ++++++++++++++--------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 516a503d..6046e18d 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -55,11 +55,31 @@ public final class Lexer { OPERATORS.put(">>", TokenType.GTGT); OPERATORS.put(">>>", TokenType.GTGTGT); } + + private static final Map KEYWORDS; + static { + KEYWORDS = new HashMap(); + KEYWORDS.put("print", TokenType.PRINT); + KEYWORDS.put("println", TokenType.PRINTLN); + KEYWORDS.put("if", TokenType.IF); + KEYWORDS.put("else", TokenType.ELSE); + KEYWORDS.put("while", TokenType.WHILE); + KEYWORDS.put("for", TokenType.FOR); + KEYWORDS.put("do", TokenType.DO); + KEYWORDS.put("break", TokenType.BREAK); + KEYWORDS.put("continue", TokenType.CONTINUE); + KEYWORDS.put("def", TokenType.DEF); + KEYWORDS.put("return", TokenType.RETURN); + KEYWORDS.put("use", TokenType.USE); + KEYWORDS.put("match", TokenType.MATCH); + KEYWORDS.put("case", TokenType.CASE); + } private final String input; private final int length; private final List tokens; + private final StringBuilder buffer; private int pos; private int row, col; @@ -69,6 +89,7 @@ public Lexer(String input) { length = input.length(); tokens = new ArrayList<>(); + buffer = new StringBuilder(); row = col = 1; } @@ -93,7 +114,7 @@ else if (OPERATOR_CHARS.indexOf(current) != -1) { } private void tokenizeNumber() { - final StringBuilder buffer = new StringBuilder(); + clearBuffer(); char current = peek(0); while (true) { if (current == '.') { @@ -108,7 +129,7 @@ private void tokenizeNumber() { } private void tokenizeHexNumber() { - final StringBuilder buffer = new StringBuilder(); + clearBuffer(); char current = peek(0); while (Character.isDigit(current) || isHexNumber(current)) { buffer.append(current); @@ -136,7 +157,7 @@ private void tokenizeOperator() { return; } } - final StringBuilder buffer = new StringBuilder(); + clearBuffer(); while (true) { final String text = buffer.toString(); if (!OPERATORS.containsKey(text + current) && !text.isEmpty()) { @@ -149,7 +170,7 @@ private void tokenizeOperator() { } private void tokenizeWord() { - final StringBuilder buffer = new StringBuilder(); + clearBuffer(); char current = peek(0); while (true) { if (!Character.isLetterOrDigit(current) && (current != '_') && (current != '$')) { @@ -160,30 +181,16 @@ private void tokenizeWord() { } final String word = buffer.toString(); - switch (word) { - case "print": addToken(TokenType.PRINT); break; - case "println": addToken(TokenType.PRINTLN); break; - case "if": addToken(TokenType.IF); break; - case "else": addToken(TokenType.ELSE); break; - case "while": addToken(TokenType.WHILE); break; - case "for": addToken(TokenType.FOR); break; - case "do": addToken(TokenType.DO); break; - case "break": addToken(TokenType.BREAK); break; - case "continue": addToken(TokenType.CONTINUE); break; - case "def": addToken(TokenType.DEF); break; - case "return": addToken(TokenType.RETURN); break; - case "use": addToken(TokenType.USE); break; - case "match": addToken(TokenType.MATCH); break; - case "case": addToken(TokenType.CASE); break; - default: - addToken(TokenType.WORD, word); - break; + if (KEYWORDS.containsKey(word)) { + addToken(KEYWORDS.get(word)); + } else { + addToken(TokenType.WORD, word); } } private void tokenizeText() { next();// skip " - final StringBuilder buffer = new StringBuilder(); + clearBuffer(); char current = peek(0); while (true) { if (current == '\0') throw error("Reached end of file while parsing text string."); @@ -191,6 +198,8 @@ private void tokenizeText() { current = next(); switch (current) { case '"': current = next(); buffer.append('"'); continue; + case 'b': current = next(); buffer.append('\b'); continue; + case 'f': current = next(); buffer.append('\f'); continue; case 'n': current = next(); buffer.append('\n'); continue; case 't': current = next(); buffer.append('\t'); continue; } @@ -224,6 +233,10 @@ private void tokenizeMultilineComment() { next(); // / } + private void clearBuffer() { + buffer.setLength(0); + } + private char next() { pos++; final char result = peek(0); From b194a7b9b9bb4790ede5e47a02c1a99b313dbe50 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Jan 2016 13:38:56 +0200 Subject: [PATCH 024/448] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B8=D1=81=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ArgumentsMismatchException.java | 11 ++++ .../LexerException.java | 2 +- .../OperationIsNotSupportedException.java | 8 +++ .../ParseException.java | 2 +- .../exceptions/PatternMatchingException.java | 11 ++++ .../ownlang/exceptions/TypeException.java | 8 +++ .../exceptions/UnknownFunctionException.java | 15 +++++ .../VariableDoesNotExistsException.java | 15 +++++ src/com/annimon/ownlang/lib/ArrayValue.java | 3 +- .../annimon/ownlang/lib/FunctionValue.java | 3 +- src/com/annimon/ownlang/lib/Functions.java | 3 +- src/com/annimon/ownlang/lib/MapValue.java | 3 +- .../ownlang/lib/UserDefinedFunction.java | 3 +- .../annimon/ownlang/lib/modules/canvas.java | 5 +- .../modules/functions/functional_combine.java | 55 +++++++++++++++++++ .../modules/functions/functional_filter.java | 8 ++- .../modules/functions/functional_foreach.java | 8 ++- .../lib/modules/functions/functional_map.java | 12 ++-- .../modules/functions/functional_reduce.java | 8 ++- .../lib/modules/functions/http_http.java | 10 ++-- .../lib/modules/functions/http_urlencode.java | 3 +- .../lib/modules/functions/json_decode.java | 3 +- .../lib/modules/functions/json_encode.java | 3 +- .../lib/modules/functions/std_foreach.java | 8 ++- .../lib/modules/functions/std_length.java | 3 +- .../lib/modules/functions/std_sleep.java | 13 +++-- .../lib/modules/functions/std_thread.java | 10 +--- src/com/annimon/ownlang/lib/modules/math.java | 5 +- src/com/annimon/ownlang/parser/Lexer.java | 1 + src/com/annimon/ownlang/parser/Parser.java | 1 + .../parser/ast/ArrayAccessExpression.java | 5 +- .../ownlang/parser/ast/BinaryExpression.java | 6 +- .../parser/ast/ConditionalExpression.java | 4 +- .../ownlang/parser/ast/MatchExpression.java | 3 +- .../ownlang/parser/ast/UnaryExpression.java | 3 +- .../parser/ast/VariableExpression.java | 3 +- 36 files changed, 209 insertions(+), 58 deletions(-) create mode 100644 src/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java rename src/com/annimon/ownlang/{parser => exceptions}/LexerException.java (87%) create mode 100644 src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java rename src/com/annimon/ownlang/{parser => exceptions}/ParseException.java (85%) create mode 100644 src/com/annimon/ownlang/exceptions/PatternMatchingException.java create mode 100644 src/com/annimon/ownlang/exceptions/TypeException.java create mode 100644 src/com/annimon/ownlang/exceptions/UnknownFunctionException.java create mode 100644 src/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_combine.java diff --git a/src/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/src/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java new file mode 100644 index 00000000..165f8631 --- /dev/null +++ b/src/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java @@ -0,0 +1,11 @@ +package com.annimon.ownlang.exceptions; + +public final class ArgumentsMismatchException extends RuntimeException { + + public ArgumentsMismatchException() { + } + + public ArgumentsMismatchException(String message) { + super(message); + } +} diff --git a/src/com/annimon/ownlang/parser/LexerException.java b/src/com/annimon/ownlang/exceptions/LexerException.java similarity index 87% rename from src/com/annimon/ownlang/parser/LexerException.java rename to src/com/annimon/ownlang/exceptions/LexerException.java index caab0660..26689e14 100644 --- a/src/com/annimon/ownlang/parser/LexerException.java +++ b/src/com/annimon/ownlang/exceptions/LexerException.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.parser; +package com.annimon.ownlang.exceptions; /** * diff --git a/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java b/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java new file mode 100644 index 00000000..fffabcaf --- /dev/null +++ b/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java @@ -0,0 +1,8 @@ +package com.annimon.ownlang.exceptions; + +public final class OperationIsNotSupportedException extends RuntimeException { + + public OperationIsNotSupportedException(Object operation) { + super("Operation " + operation + " is not supported"); + } +} diff --git a/src/com/annimon/ownlang/parser/ParseException.java b/src/com/annimon/ownlang/exceptions/ParseException.java similarity index 85% rename from src/com/annimon/ownlang/parser/ParseException.java rename to src/com/annimon/ownlang/exceptions/ParseException.java index 40ae395c..ed2d47e4 100644 --- a/src/com/annimon/ownlang/parser/ParseException.java +++ b/src/com/annimon/ownlang/exceptions/ParseException.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.parser; +package com.annimon.ownlang.exceptions; /** * diff --git a/src/com/annimon/ownlang/exceptions/PatternMatchingException.java b/src/com/annimon/ownlang/exceptions/PatternMatchingException.java new file mode 100644 index 00000000..05075f89 --- /dev/null +++ b/src/com/annimon/ownlang/exceptions/PatternMatchingException.java @@ -0,0 +1,11 @@ +package com.annimon.ownlang.exceptions; + +public final class PatternMatchingException extends RuntimeException { + + public PatternMatchingException() { + } + + public PatternMatchingException(String message) { + super(message); + } +} diff --git a/src/com/annimon/ownlang/exceptions/TypeException.java b/src/com/annimon/ownlang/exceptions/TypeException.java new file mode 100644 index 00000000..c2e853b1 --- /dev/null +++ b/src/com/annimon/ownlang/exceptions/TypeException.java @@ -0,0 +1,8 @@ +package com.annimon.ownlang.exceptions; + +public final class TypeException extends RuntimeException { + + public TypeException(String message) { + super(message); + } +} diff --git a/src/com/annimon/ownlang/exceptions/UnknownFunctionException.java b/src/com/annimon/ownlang/exceptions/UnknownFunctionException.java new file mode 100644 index 00000000..74ad7d3e --- /dev/null +++ b/src/com/annimon/ownlang/exceptions/UnknownFunctionException.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.exceptions; + +public final class UnknownFunctionException extends RuntimeException { + + private final String functionName; + + public UnknownFunctionException(String name) { + super("Unknown function " + name); + this.functionName = name; + } + + public String getFunctionName() { + return functionName; + } +} diff --git a/src/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/src/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java new file mode 100644 index 00000000..0981de17 --- /dev/null +++ b/src/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.exceptions; + +public final class VariableDoesNotExistsException extends RuntimeException { + + private final String variable; + + public VariableDoesNotExistsException(String variable) { + super("Variable " + variable + " does not exists"); + this.variable = variable; + } + + public String getVariable() { + return variable; + } +} diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index e34b8ce6..fb28e8c7 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.TypeException; import java.util.Arrays; import java.util.Iterator; @@ -61,7 +62,7 @@ public void set(int index, Value value) { @Override public double asNumber() { - throw new RuntimeException("Cannot cast array to number"); + throw new TypeException("Cannot cast array to number"); } @Override diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index b2544f50..8ef1aa05 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.TypeException; import java.util.Objects; /** @@ -23,7 +24,7 @@ public int type() { @Override public double asNumber() { - throw new RuntimeException("Cannot cast function to number"); + throw new TypeException("Cannot cast function to number"); } @Override diff --git a/src/com/annimon/ownlang/lib/Functions.java b/src/com/annimon/ownlang/lib/Functions.java index 8aa188aa..eda7d0c0 100644 --- a/src/com/annimon/ownlang/lib/Functions.java +++ b/src/com/annimon/ownlang/lib/Functions.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.UnknownFunctionException; import java.util.HashMap; import java.util.Map; @@ -20,7 +21,7 @@ public static boolean isExists(String key) { } public static Function get(String key) { - if (!isExists(key)) throw new RuntimeException("Unknown function " + key); + if (!isExists(key)) throw new UnknownFunctionException(key); return functions.get(key); } diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index 3eff1c7b..369102d8 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.TypeException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -46,7 +47,7 @@ public void set(Value key, Value value) { @Override public double asNumber() { - throw new RuntimeException("Cannot cast map to number"); + throw new TypeException("Cannot cast map to number"); } @Override diff --git a/src/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/com/annimon/ownlang/lib/UserDefinedFunction.java index fabe27b2..0c440664 100644 --- a/src/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/src/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.parser.ast.ReturnStatement; import com.annimon.ownlang.parser.ast.Statement; import java.util.List; @@ -30,7 +31,7 @@ public String getArgsName(int index) { @Override public Value execute(Value... values) { final int size = values.length; - if (size != getArgsCount()) throw new RuntimeException("Args count mismatch"); + if (size != getArgsCount()) throw new ArgumentsMismatchException("Arguments count mismatch"); try { Variables.push(); diff --git a/src/com/annimon/ownlang/lib/modules/canvas.java b/src/com/annimon/ownlang/lib/modules/canvas.java index c9d0ee9c..b4e05c44 100644 --- a/src/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/com/annimon/ownlang/lib/modules/canvas.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.awt.Color; import java.awt.Dimension; @@ -89,7 +90,7 @@ private static void clip(int x, int y, int w, int h) { private static Function intConsumer4Convert(IntConsumer4 consumer) { return args -> { - if (args.length != 4) throw new RuntimeException("Four args expected"); + 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(); @@ -186,7 +187,7 @@ private static class DrawString implements Function { @Override public Value execute(Value... args) { - if (args.length != 3) throw new RuntimeException("Three args expected"); + if (args.length != 3) throw new ArgumentsMismatchException("Three args expected"); int x = (int) args[1].asNumber(); int y = (int) args[2].asNumber(); graphics.drawString(args[0].asString(), x, y); diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java b/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java new file mode 100644 index 00000000..980106c4 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; + +import java.util.Map; + +public final class functional_map implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2) throw new RuntimeException("At least two args expected"); + + final Value container = args[0]; + if (container.type() == Types.ARRAY) { + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + final Function mapper = ((FunctionValue) args[1]).getValue(); + return mapArray((ArrayValue) container, mapper); + } + + if (container.type() == Types.MAP) { + if (args[1].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in second arg"); + } + if (args[2].type() != Types.FUNCTION) { + throw new RuntimeException("Function expected in third arg"); + } + final Function keyMapper = ((FunctionValue) args[1]).getValue(); + final Function valueMapper = ((FunctionValue) args[2]).getValue(); + return mapMap((MapValue) container, keyMapper, valueMapper); + } + + throw new RuntimeException("Invalid first argument. Array or map exprected"); + } + + private Value mapArray(ArrayValue array, Function mapper) { + final int size = array.size(); + final ArrayValue result = new ArrayValue(size); + for (int i = 0; i < size; i++) { + result.set(i, mapper.execute(array.get(i))); + } + return result; + } + + private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + final Value newKey = keyMapper.execute(element.getKey()); + final Value newValue = valueMapper.execute(element.getValue()); + result.set(newKey, newValue); + } + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java b/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java index c1f7ed9b..86ff4814 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.List; @@ -10,10 +12,10 @@ public final class functional_filter implements Function { @Override public Value execute(Value... args) { - if (args.length < 2) throw new RuntimeException("At least two args expected"); + if (args.length < 2) throw new ArgumentsMismatchException("At least two args expected"); if (args[1].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in second arg"); + throw new TypeException("Function expected in second arg"); } final Value container = args[0]; final Function consumer = ((FunctionValue) args[1]).getValue(); @@ -25,7 +27,7 @@ public Value execute(Value... args) { return filterMap((MapValue) container, consumer); } - throw new RuntimeException("Invalid first argument. Array or map exprected"); + throw new TypeException("Invalid first argument. Array or map exprected"); } private Value filterArray(ArrayValue array, Function predicate) { diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java index bf9bf337..8bc26a05 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.Map; @@ -8,10 +10,10 @@ public final class functional_foreach implements Function { @Override public Value execute(Value... args) { - if (args.length != 2) throw new RuntimeException("Two args expected"); + if (args.length != 2) throw new ArgumentsMismatchException("Two args expected"); if (args[1].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in second arg"); + throw new TypeException("Function expected in second arg"); } final Value container = args[0]; final Function consumer = ((FunctionValue) args[1]).getValue(); @@ -29,6 +31,6 @@ public Value execute(Value... args) { } return NumberValue.ZERO; } - throw new RuntimeException("Invalid first argument. Array or map exprected"); + throw new TypeException("Invalid first argument. Array or map exprected"); } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_map.java b/src/com/annimon/ownlang/lib/modules/functions/functional_map.java index 980106c4..2c883ac9 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/functional_map.java +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_map.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.Map; @@ -8,12 +10,12 @@ public final class functional_map implements Function { @Override public Value execute(Value... args) { - if (args.length < 2) throw new RuntimeException("At least two args expected"); + if (args.length < 2) throw new ArgumentsMismatchException("At least two args expected"); final Value container = args[0]; if (container.type() == Types.ARRAY) { if (args[1].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in second arg"); + throw new TypeException("Function expected in second arg"); } final Function mapper = ((FunctionValue) args[1]).getValue(); return mapArray((ArrayValue) container, mapper); @@ -21,17 +23,17 @@ public Value execute(Value... args) { if (container.type() == Types.MAP) { if (args[1].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in second arg"); + throw new TypeException("Function expected in second arg"); } if (args[2].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in third arg"); + throw new TypeException("Function expected in third arg"); } final Function keyMapper = ((FunctionValue) args[1]).getValue(); final Function valueMapper = ((FunctionValue) args[2]).getValue(); return mapMap((MapValue) container, keyMapper, valueMapper); } - throw new RuntimeException("Invalid first argument. Array or map exprected"); + throw new TypeException("Invalid first argument. Array or map exprected"); } private Value mapArray(ArrayValue array, Function mapper) { diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java b/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java index 7a886ebc..781b3bdf 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.Map; @@ -8,10 +10,10 @@ public final class functional_reduce implements Function { @Override public Value execute(Value... args) { - if (args.length != 3) throw new RuntimeException("Three args expected"); + if (args.length != 3) throw new ArgumentsMismatchException("Three args expected"); if (args[2].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in third arg"); + throw new TypeException("Function expected in third arg"); } final Value container = args[0]; final Value identity = args[1]; @@ -32,6 +34,6 @@ public Value execute(Value... args) { } return result; } - throw new RuntimeException("Invalid first argument. Array or map exprected"); + throw new TypeException("Invalid first argument. Array or map exprected"); } } \ No newline at end of file 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 50df52e5..e89c61a5 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/http_http.java +++ b/src/com/annimon/ownlang/lib/modules/functions/http_http.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -49,7 +51,7 @@ public Value execute(Value... args) { case 4: // http(url, method, params, callback) if (args[3].type() != Types.FUNCTION) { - throw new RuntimeException("Fourth arg must be a function callback"); + throw new TypeException("Fourth arg must be a function callback"); } url = args[0].asString(); method = args[1].asString(); @@ -57,17 +59,17 @@ public Value execute(Value... args) { case 5: // http(url, method, params, headerParams, callback) if (args[3].type() != Types.MAP) { - throw new RuntimeException("Third arg must be a map"); + throw new TypeException("Third arg must be a map"); } if (args[4].type() != Types.FUNCTION) { - throw new RuntimeException("Fifth arg must be a function callback"); + throw new TypeException("Fifth arg must be a function callback"); } url = args[0].asString(); method = args[1].asString(); return process(url, method, args[2], (MapValue) args[3], (FunctionValue) args[4]); default: - throw new RuntimeException("Wrong number of arguments"); + throw new ArgumentsMismatchException("Wrong number of arguments"); } } diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java b/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java index 02ff8ff0..39a00c95 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java +++ b/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; @@ -10,7 +11,7 @@ public final class http_urlencode implements Function { @Override public Value execute(Value... args) { - if (args.length == 0) throw new RuntimeException("At least one arg expected"); + if (args.length == 0) throw new ArgumentsMismatchException("At least one arg expected"); String charset = "UTF-8"; if (args.length >= 2) { 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 97fb9d5c..57be629c 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/json_decode.java +++ b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.util.Iterator; import org.json.*; @@ -8,7 +9,7 @@ public final class json_decode implements Function { @Override public Value execute(Value... args) { - if (args.length != 1) throw new RuntimeException("One argument expected"); + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); try { final String jsonRaw = args[0].asString(); final Object root = new JSONTokener(jsonRaw).nextValue(); 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 8263ac1d..23d80e80 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/json_encode.java +++ b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.util.Map; import org.json.*; @@ -8,7 +9,7 @@ public final class json_encode implements Function { @Override public Value execute(Value... args) { - if (args.length != 1) throw new RuntimeException("One argument expected"); + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); try { final Object root = process(args[0]); final String jsonRaw = JSONObject.valueToString(root); diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java index dfb7493b..d07283be 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.Map; @@ -8,9 +10,9 @@ public final class std_foreach implements Function { @Override public Value execute(Value... args) { - if (args.length != 2) return NumberValue.ZERO; + if (args.length != 2) throw new ArgumentsMismatchException("Two arguments expected"); - if (args[1].type() != Types.FUNCTION) return NumberValue.ZERO; + if (args[1].type() != Types.FUNCTION) throw new TypeException("Second arg must be a function"); final Function function = ((FunctionValue) args[1]).getValue(); final Value container = args[0]; if (container.type() == Types.ARRAY) { @@ -27,6 +29,6 @@ public Value execute(Value... args) { } return NumberValue.ZERO; } - return NumberValue.ZERO; + throw new TypeException("First arg must be an array or map"); } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_length.java b/src/com/annimon/ownlang/lib/modules/functions/std_length.java index b6aa93d4..8bdf40b7 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_length.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_length.java @@ -1,12 +1,13 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; public final class std_length implements Function { @Override public Value execute(Value... args) { - if (args.length == 0) throw new RuntimeException("At least one arg expected"); + if (args.length == 0) throw new ArgumentsMismatchException("At least one arg expected"); final Value val = args[0]; int length; diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java b/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java index 4b25ea32..c86810d0 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; @@ -8,12 +9,12 @@ public final class std_sleep implements Function { @Override public Value execute(Value... args) { - if (args.length == 1) { - try { - Thread.sleep((long) args[0].asNumber()); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + + try { + Thread.sleep((long) args[0].asNumber()); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); } return NumberValue.ZERO; } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java index 1025e75e..785fce24 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java @@ -1,11 +1,7 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; public final class std_thread implements Function { @@ -13,7 +9,7 @@ public final class std_thread implements Function { public Value execute(Value... args) { // Создаём новый поток и передаём параметры, если есть. // Функция может передаваться как напрямую, так и по имени - if (args.length == 0) throw new RuntimeException("At least one arg expected"); + if (args.length == 0) throw new ArgumentsMismatchException("At least one arg expected"); Function body; if (args[0].type() == Types.FUNCTION) { diff --git a/src/com/annimon/ownlang/lib/modules/math.java b/src/com/annimon/ownlang/lib/modules/math.java index 270dfc46..37f08e94 100644 --- a/src/com/annimon/ownlang/lib/modules/math.java +++ b/src/com/annimon/ownlang/lib/modules/math.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleFunction; @@ -57,14 +58,14 @@ public void init() { private static Function functionConvert(DoubleUnaryOperator op) { return args -> { - if (args.length != 1) throw new RuntimeException("One arg expected"); + if (args.length != 1) throw new ArgumentsMismatchException("One arg expected"); return doubleToNumber.apply(op.applyAsDouble(args[0].asNumber())); }; } private static Function biFunctionConvert(DoubleBinaryOperator op) { return args -> { - if (args.length != 2) throw new RuntimeException("Two args expected"); + if (args.length != 2) throw new ArgumentsMismatchException("Two args expected"); return doubleToNumber.apply(op.applyAsDouble(args[0].asNumber(), args[1].asNumber())); }; } diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 6046e18d..fe206b69 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.exceptions.LexerException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 1c90a130..f3c75e1c 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.exceptions.ParseException; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.UserDefinedFunction; diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java index e5dfff81..2474c5ee 100644 --- a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.List; @@ -56,14 +57,14 @@ private Value index(int index) { private ArrayValue consumeArray(Value value) { if (value.type() != Types.ARRAY) { - throw new RuntimeException("Array expected"); + throw new TypeException("Array expected"); } return (ArrayValue) value; } public MapValue consumeMap(Value value) { if (value.type() != Types.MAP) { - throw new RuntimeException("Map expected"); + throw new TypeException("Map expected"); } return (MapValue) value; } diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index 985e6b85..e870ebbe 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; @@ -85,7 +87,7 @@ private Value eval(ArrayValue value1, Value value2) { switch (operation) { case LSHIFT: if (value2.type() != Types.ARRAY) - throw new RuntimeException("Cannot merge non array value to array"); + throw new TypeException("Cannot merge non array value to array"); return ArrayValue.merge(value1, (ArrayValue) value2); case PUSH: default: @@ -113,7 +115,7 @@ private Value eval(Value value1, Value value2) { case URSHIFT: result = (int)number1 >>> (int)number2; break; default: - throw new RuntimeException("Operation " + operation + " is not supported"); + throw new OperationIsNotSupportedException(operation); } return new NumberValue(result); } diff --git a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java index 5e8610b9..569df038 100644 --- a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -1,7 +1,7 @@ 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; @@ -76,7 +76,7 @@ public Value eval() { case GTEQ: result = number1 >= number2; break; default: - throw new RuntimeException("Operation " + operation + " is not supported"); + throw new OperationIsNotSupportedException(operation); } return NumberValue.fromBoolean(result); } diff --git a/src/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/com/annimon/ownlang/parser/ast/MatchExpression.java index 541577ac..877887d8 100644 --- a/src/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/src/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.PatternMatchingException; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; @@ -48,7 +49,7 @@ public Value eval() { } } } - throw new RuntimeException("No pattern were matched"); + throw new PatternMatchingException("No pattern were matched"); } private boolean match(Value value, Value constant) { diff --git a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java index fc016590..b9abd3b4 100644 --- a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; @@ -44,7 +45,7 @@ public Value eval() { case COMPLEMENT: return new NumberValue(~(int)value.asNumber()); case NOT: return new NumberValue(value.asNumber() != 0 ? 0 : 1); default: - throw new RuntimeException("Operation " + operation + " is not supported"); + throw new OperationIsNotSupportedException(operation); } } diff --git a/src/com/annimon/ownlang/parser/ast/VariableExpression.java b/src/com/annimon/ownlang/parser/ast/VariableExpression.java index 1971eea5..794f3a91 100644 --- a/src/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/src/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; @@ -17,7 +18,7 @@ public VariableExpression(String name) { @Override public Value eval() { - if (!Variables.isExists(name)) throw new RuntimeException("Variable does not exists: " + name); + if (!Variables.isExists(name)) throw new VariableDoesNotExistsException(name); return Variables.get(name); } From 1fac43b6ff7da6ef7be8ad795f61213bb0d98024 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Jan 2016 14:07:40 +0200 Subject: [PATCH 025/448] =?UTF-8?q?=D0=92=D1=8B=D0=B7=D0=BE=D0=B2=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B8=D0=B7=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=D0=B0=20=D0=B8=D0=BB=D0=B8=20?= =?UTF-8?q?=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/parser/Parser.java | 36 ++++++++++------ .../parser/ast/FunctionalExpression.java | 43 ++++++++++--------- .../parser/visitors/AbstractVisitor.java | 1 + 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index f3c75e1c..6c80ae9b 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -85,7 +85,7 @@ private Statement statement() { return functionDefine(); } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return new FunctionStatement(function()); + return new FunctionStatement(function(qualifiedName())); } return assignmentStatement(); } @@ -191,11 +191,10 @@ private FunctionDefineStatement functionDefine() { return new FunctionDefineStatement(name, argNames, body); } - private FunctionalExpression function() { + private FunctionalExpression function(Expression qualifiedNameExpr) { // function(arg1, arg2, ...) - final String name = consume(TokenType.WORD).getText(); consume(TokenType.LPAREN); - final FunctionalExpression function = new FunctionalExpression(name); + final FunctionalExpression function = new FunctionalExpression(qualifiedNameExpr); while (!match(TokenType.RPAREN)) { function.addArgument(expression()); match(TokenType.COMMA); @@ -542,26 +541,39 @@ private Expression primary() { } private Expression variable() { - final Token current = get(0); - if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { - return element(); - } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return function(); + return function(new ValueExpression(consume(TokenType.WORD).getText())); } - if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.DOT)) { - return object(); + final Expression qualifiedNameExpr = qualifiedName(); + if (qualifiedNameExpr != null) { + // variable(args) || arr["key"](args) || obj.key(args) + if (lookMatch(0, TokenType.LPAREN)) { + return function(qualifiedNameExpr); + } + return qualifiedNameExpr; } + if (lookMatch(0, TokenType.LBRACKET)) { return array(); } if (lookMatch(0, TokenType.LBRACE)) { return map(); } + return value(); + } + + private Expression qualifiedName() { + final Token current = get(0); + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { + return element(); + } + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.DOT)) { + return object(); + } if (match(TokenType.WORD)) { return new VariableExpression(current.getText()); } - return value(); + return null; } private Expression value() { diff --git a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 800a2d2b..53223800 100644 --- a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -1,11 +1,7 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; +import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.List; @@ -15,19 +11,14 @@ */ public final class FunctionalExpression implements Expression { - public final String name; + public final Expression functionExpr; public final List arguments; - public FunctionalExpression(String name) { - this.name = name; + public FunctionalExpression(Expression functionExpr) { + this.functionExpr = functionExpr; arguments = new ArrayList<>(); } - public FunctionalExpression(String name, List arguments) { - this.name = name; - this.arguments = arguments; - } - public void addArgument(Expression arg) { arguments.add(arg); } @@ -39,18 +30,30 @@ public Value eval() { for (int i = 0; i < size; i++) { values[i] = arguments.get(i).eval(); } - return getFunction(name).execute(values); + return consumeFunction(functionExpr).execute(values); + } + + private Function consumeFunction(Expression expr) { + try { + final Value value = expr.eval(); + if (value.type() == Types.FUNCTION) { + return ((FunctionValue) value).getValue(); + } + return getFunction(value.asString()); + } catch (VariableDoesNotExistsException ex) { + return getFunction(ex.getVariable()); + } } private Function getFunction(String key) { if (Functions.isExists(key)) return Functions.get(key); if (Variables.isExists(key)) { - final Value value = Variables.get(key); - if (value.type() == Types.FUNCTION) { - return ((FunctionValue)value).getValue(); + final Value variable = Variables.get(key); + if (variable.type() == Types.FUNCTION) { + return ((FunctionValue)variable).getValue(); } } - throw new RuntimeException("Unknown function " + key); + throw new UnknownFunctionException(key); } @Override @@ -60,6 +63,6 @@ public void accept(Visitor visitor) { @Override public String toString() { - return name + "(" + arguments.toString() + ")"; + return functionExpr + "(" + arguments.toString() + ")"; } } diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 3dfe0eb8..b6a960fa 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -104,6 +104,7 @@ public void visit(FunctionStatement s) { @Override public void visit(FunctionalExpression s) { + s.functionExpr.accept(this); for (Expression argument : s.arguments) { argument.accept(this); } From 47dfae5740067cd99ec44eaff6a0dc5699bb72f0 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Jan 2016 14:57:48 +0200 Subject: [PATCH 026/448] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/modules/functions/std_foreach.java | 34 ------------------- .../parser/ast/FunctionalExpression.java | 1 + 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_foreach.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java b/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java deleted file mode 100644 index d07283be..00000000 --- a/src/com/annimon/ownlang/lib/modules/functions/std_foreach.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.annimon.ownlang.lib.modules.functions; - -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; - -import java.util.Map; - -public final class std_foreach implements Function { - - @Override - public Value execute(Value... args) { - if (args.length != 2) throw new ArgumentsMismatchException("Two arguments expected"); - - if (args[1].type() != Types.FUNCTION) throw new TypeException("Second arg must be a function"); - final Function function = ((FunctionValue) args[1]).getValue(); - final Value container = args[0]; - if (container.type() == Types.ARRAY) { - final ArrayValue array = (ArrayValue) container; - for (Value element : array) { - function.execute(element); - } - return NumberValue.ZERO; - } - if (container.type() == Types.MAP) { - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - function.execute(element.getKey(), element.getValue()); - } - return NumberValue.ZERO; - } - throw new TypeException("First arg must be an array or map"); - } -} \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 53223800..a93bb6ee 100644 --- a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; +import com.annimon.ownlang.exceptions.UnknownFunctionException; import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.List; From 02fe4b6e0c504174cc4182afc3d77c5a2ea3df57 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Jan 2016 14:59:04 +0200 Subject: [PATCH 027/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BA=D0=BE=D0=BC=D0=B1=D0=B8=D0=BD=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D0=B8=D0=B9=20=D0=B8=20flatmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 16 +++++- src/com/annimon/ownlang/lib/ArrayValue.java | 6 ++ .../ownlang/lib/modules/functional.java | 5 ++ .../modules/functions/functional_combine.java | 56 +++++-------------- .../modules/functions/functional_flatmap.java | 40 +++++++++++++ 5 files changed, 81 insertions(+), 42 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java diff --git a/program.own b/program.own index bc1bbb58..f62f0e96 100644 --- a/program.own +++ b/program.own @@ -126,6 +126,11 @@ squares = map(nums, def(x) = x * x) foreach(squares, ::echo) println "Sum: " + reduce(squares, 0, def(x, y) = x + y) +nums = [[1, 2], [3], [], [4, 5]] +nums = flatmap(nums, IDENTITY) +println "flatmap" +foreach(nums, ::echo) + use "http" /*http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { @@ -164,4 +169,13 @@ def fact2(n) = match n { } println fact1(6) -println fact2(6) \ No newline at end of file +println fact2(6) + +class = { + "add": def(a, b) = a + b, + "sub": def(a, b) = a - b, + "mul": def(a, b) = a * b, + "div": def(a, b) = a / b +} + +println class.add(2, class.mul(2, 2)) \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index fb28e8c7..4183c50a 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.exceptions.TypeException; import java.util.Arrays; import java.util.Iterator; +import java.util.List; /** * @@ -39,6 +40,11 @@ public ArrayValue(Value[] elements) { System.arraycopy(elements, 0, this.elements, 0, elements.length); } + public ArrayValue(List values) { + final int size = values.size(); + this.elements = values.toArray(new Value[size]); + } + public ArrayValue(ArrayValue array) { this(array.elements); } diff --git a/src/com/annimon/ownlang/lib/modules/functional.java b/src/com/annimon/ownlang/lib/modules/functional.java index e5bc229a..8d3126cd 100644 --- a/src/com/annimon/ownlang/lib/modules/functional.java +++ b/src/com/annimon/ownlang/lib/modules/functional.java @@ -13,7 +13,12 @@ public final class functional implements Module { public void init() { Functions.set("foreach", new functional_foreach()); Functions.set("map", new functional_map()); + Functions.set("flatmap", new functional_flatmap()); Functions.set("reduce", new functional_reduce()); Functions.set("filter", new functional_filter()); + + Functions.set("combine", new functional_combine()); + + Variables.set("IDENTITY", new FunctionValue(args -> args[0])); } } diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java b/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java index 980106c4..1d127dff 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java @@ -1,55 +1,29 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; -import java.util.Map; - -public final class functional_map implements Function { +public final class functional_combine implements Function { @Override public Value execute(Value... args) { - if (args.length < 2) throw new RuntimeException("At least two args expected"); + if (args.length < 1) throw new ArgumentsMismatchException("At least one arg expected"); - final Value container = args[0]; - if (container.type() == Types.ARRAY) { - if (args[1].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in second arg"); + Function result = null; + for (Value arg : args) { + if (arg.type() != Types.FUNCTION) { + throw new TypeException(arg.toString() + " is not a function"); } - final Function mapper = ((FunctionValue) args[1]).getValue(); - return mapArray((ArrayValue) container, mapper); + final Function current = result; + final Function next = ((FunctionValue) arg).getValue(); + result = fArgs -> { + if (current == null) return next.execute(fArgs); + return next.execute(current.execute(fArgs)); + }; } - if (container.type() == Types.MAP) { - if (args[1].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in second arg"); - } - if (args[2].type() != Types.FUNCTION) { - throw new RuntimeException("Function expected in third arg"); - } - final Function keyMapper = ((FunctionValue) args[1]).getValue(); - final Function valueMapper = ((FunctionValue) args[2]).getValue(); - return mapMap((MapValue) container, keyMapper, valueMapper); - } - - throw new RuntimeException("Invalid first argument. Array or map exprected"); + return new FunctionValue(result); } - private Value mapArray(ArrayValue array, Function mapper) { - final int size = array.size(); - final ArrayValue result = new ArrayValue(size); - for (int i = 0; i < size; i++) { - result.set(i, mapper.execute(array.get(i))); - } - return result; - } - - private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { - final MapValue result = new MapValue(map.size()); - for (Map.Entry element : map) { - final Value newKey = keyMapper.execute(element.getKey()); - final Value newValue = valueMapper.execute(element.getValue()); - result.set(newKey, newValue); - } - return result; - } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java b/src/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java new file mode 100644 index 00000000..c9338923 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java @@ -0,0 +1,40 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.List; + +public final class functional_flatmap implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2) throw new ArgumentsMismatchException("At least two arguments expected"); + + 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 Function mapper = ((FunctionValue) args[1]).getValue(); + return flatMapArray((ArrayValue) args[0], mapper); + } + + private Value flatMapArray(ArrayValue array, Function mapper) { + final List values = new ArrayList<>(); + final int size = array.size(); + for (int i = 0; i < size; i++) { + final Value inner = mapper.execute(array.get(i)); + if (inner.type() != Types.ARRAY) { + throw new TypeException("Array expected " + inner); + } + for (Value value : (ArrayValue) inner) { + values.add(value); + } + } + return new ArrayValue(values); + } +} \ No newline at end of file From 5f25d6be071250ce0466af5562de849e43d151ce Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Jan 2016 17:59:23 +0200 Subject: [PATCH 028/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20std=20sort=20=D0=B8=20functional=20sortby?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 2 ++ src/com/annimon/ownlang/lib/ArrayValue.java | 15 ++++++++ .../annimon/ownlang/lib/FunctionValue.java | 5 +++ src/com/annimon/ownlang/lib/MapValue.java | 9 +++++ src/com/annimon/ownlang/lib/NumberValue.java | 8 ++++- src/com/annimon/ownlang/lib/StringValue.java | 8 +++++ src/com/annimon/ownlang/lib/Value.java | 2 +- .../ownlang/lib/modules/functional.java | 1 + .../modules/functions/functional_sortby.java | 27 ++++++++++++++ .../lib/modules/functions/std_sort.java | 36 +++++++++++++++++++ src/com/annimon/ownlang/lib/modules/std.java | 1 + 11 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/functional_sortby.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_sort.java diff --git a/program.own b/program.own index f62f0e96..11aafe2a 100644 --- a/program.own +++ b/program.own @@ -130,6 +130,8 @@ nums = [[1, 2], [3], [], [4, 5]] nums = flatmap(nums, IDENTITY) println "flatmap" foreach(nums, ::echo) +println "sort" +foreach(sort(nums, def(a,b) = b - a), ::echo) use "http" diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 4183c50a..ad593d0f 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -48,6 +48,12 @@ public ArrayValue(List values) { public ArrayValue(ArrayValue array) { this(array.elements); } + + public Value[] getCopyElements() { + final Value[] result = new Value[elements.length]; + System.arraycopy(elements, 0, result, 0, elements.length); + return result; + } @Override public int type() { @@ -97,6 +103,15 @@ public boolean equals(Object obj) { final ArrayValue other = (ArrayValue) obj; return Arrays.deepEquals(this.elements, other.elements); } + + @Override + public int compareTo(Value o) { + if (o.type() == Types.ARRAY) { + final int lengthCompare = Integer.compare(size(), ((ArrayValue) o).size()); + if (lengthCompare != 0) return lengthCompare; + } + return asString().compareTo(o.asString()); + } @Override public String toString() { diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index 8ef1aa05..18967951 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -53,6 +53,11 @@ public boolean equals(Object obj) { return Objects.equals(this.value, other.value); } + @Override + public int compareTo(Value o) { + return asString().compareTo(o.asString()); + } + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index 369102d8..d8cc3b9b 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -77,6 +77,15 @@ public boolean equals(Object obj) { return Objects.equals(this.map, other.map); } + @Override + public int compareTo(Value o) { + if (o.type() == Types.MAP) { + final int lengthCompare = Integer.compare(size(), ((MapValue) o).size()); + if (lengthCompare != 0) return lengthCompare; + } + return asString().compareTo(o.asString()); + } + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/com/annimon/ownlang/lib/NumberValue.java index 5744790f..3f43f762 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -51,7 +51,13 @@ public boolean equals(Object obj) { return Double.doubleToLongBits(this.value) == Double.doubleToLongBits(other.value); } - + @Override + public int compareTo(Value o) { + if (o.type() == Types.NUMBER) { + return Double.compare(value, ((NumberValue)o).value); + } + return asString().compareTo(o.asString()); + } @Override public String toString() { diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index f38206fa..39d83e1f 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -54,6 +54,14 @@ public boolean equals(Object obj) { return Objects.equals(this.value, other.value); } + @Override + public int compareTo(Value o) { + if (o.type() == Types.STRING) { + return value.compareTo(((StringValue) o).value); + } + return asString().compareTo(o.asString()); + } + @Override public String toString() { return asString(); diff --git a/src/com/annimon/ownlang/lib/Value.java b/src/com/annimon/ownlang/lib/Value.java index 7f09b5f8..832e9f7d 100644 --- a/src/com/annimon/ownlang/lib/Value.java +++ b/src/com/annimon/ownlang/lib/Value.java @@ -4,7 +4,7 @@ * * @author aNNiMON */ -public interface Value { +public interface Value extends Comparable { double asNumber(); diff --git a/src/com/annimon/ownlang/lib/modules/functional.java b/src/com/annimon/ownlang/lib/modules/functional.java index 8d3126cd..526ecba4 100644 --- a/src/com/annimon/ownlang/lib/modules/functional.java +++ b/src/com/annimon/ownlang/lib/modules/functional.java @@ -16,6 +16,7 @@ public void init() { Functions.set("flatmap", new functional_flatmap()); Functions.set("reduce", new functional_reduce()); Functions.set("filter", new functional_filter()); + Functions.set("sortby", new functional_sortby()); Functions.set("combine", new functional_combine()); diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_sortby.java b/src/com/annimon/ownlang/lib/modules/functions/functional_sortby.java new file mode 100644 index 00000000..2115c174 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/functional_sortby.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.Arrays; + +public final class functional_sortby implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 2) throw new ArgumentsMismatchException("Two arguments expected"); + + 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[] elements = ((ArrayValue) args[0]).getCopyElements(); + final Function function = ((FunctionValue) args[1]).getValue(); + Arrays.sort(elements, (o1, o2) -> function.execute(o1).compareTo(function.execute(o2))); + return new ArrayValue(elements); + } + +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sort.java b/src/com/annimon/ownlang/lib/modules/functions/std_sort.java new file mode 100644 index 00000000..edd0e40b --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sort.java @@ -0,0 +1,36 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.Arrays; + +public final class std_sort implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 1) throw new ArgumentsMismatchException("At least one argument expected"); + if (args[0].type() != Types.ARRAY) { + throw new TypeException("Array expected in first argument"); + } + final Value[] elements = ((ArrayValue) args[0]).getCopyElements(); + + switch (args.length) { + case 1: + Arrays.sort(elements); + break; + case 2: + if (args[1].type() != Types.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()); + break; + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + + return new ArrayValue(elements); + } + +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index d5e7746a..fe986ea7 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -13,6 +13,7 @@ public final class std implements Module { public void init() { Functions.set("echo", new std_echo()); Functions.set("newarray", new std_newarray()); + Functions.set("sort", new std_sort()); Functions.set("length", new std_length()); Functions.set("rand", new std_rand()); Functions.set("sleep", new std_sleep()); From da2bedad52f2d7642b7a455676e4f07e436bc8bf Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Jan 2016 18:25:19 +0200 Subject: [PATCH 029/448] =?UTF-8?q?=D0=97=D0=B0=D0=BF=D1=83=D1=81=D0=BA=20?= =?UTF-8?q?=D0=B8=D0=B7=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=BD=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/Main.java | 57 ++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 439582ff..03f4c872 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -18,18 +18,59 @@ public final class Main { public static void main(String[] args) throws IOException { - final String file = "program.own"; - final String input = new String( Files.readAllBytes(Paths.get(file)), "UTF-8"); + if (args.length == 0) { + run(readFile("program.own"), true, true); + return; + } + + boolean showTokens = false, showAst = false; + String input = null; + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "-a": + case "--showast": + showAst = true; + break; + + case "-t": + case "--showtokens": + showTokens = true; + break; + + case "-f": + case "--file": + if (i + 1 < args.length) { + input = readFile(args[i + 1]); + i++; + } + break; + + default: + input = args[i]; + } + } + if (input == null) { + throw new IllegalArgumentException("Empty input"); + } + run(input, showTokens, showAst); + } + + private static String readFile(String file) throws IOException { + return new String( Files.readAllBytes(Paths.get(file)), "UTF-8"); + } + + private static void run(String input, boolean showTokens, boolean showAst) { final List tokens = new Lexer(input).tokenize(); - for (int i = 0; i < tokens.size(); i++) { - System.out.println(i + " " + tokens.get(i)); + if (showTokens) { + for (int i = 0; i < tokens.size(); i++) { + System.out.println(i + " " + tokens.get(i)); + } } -// for (Token token : tokens) { -// System.out.println(token); -// } final Statement program = new Parser(tokens).parse(); - System.out.println(program.toString()); + if (showAst) { + System.out.println(program.toString()); + } program.accept(new FunctionAdder()); // program.accept(new VariablePrinter()); program.accept(new AssignValidator()); From 1957c8e7e06b4af37802acf80b758ffd7b3dfd4b Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jan 2016 18:15:58 +0200 Subject: [PATCH 030/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=D0=BE=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 7 +++- .../lib/modules/functions/std_charat.java | 17 ++++++++ .../lib/modules/functions/std_indexof.java | 18 ++++++++ .../lib/modules/functions/std_join.java | 41 +++++++++++++++++++ .../modules/functions/std_lastindexof.java | 18 ++++++++ .../lib/modules/functions/std_replace.java | 18 ++++++++ .../lib/modules/functions/std_replaceall.java | 18 ++++++++ .../modules/functions/std_replacefirst.java | 18 ++++++++ .../lib/modules/functions/std_split.java | 24 +++++++++++ .../lib/modules/functions/std_sprintf.java | 19 +++++++++ .../lib/modules/functions/std_substring.java | 25 +++++++++++ .../lib/modules/functions/std_tochar.java | 14 +++++++ .../modules/functions/std_tolowercase.java | 14 +++++++ .../modules/functions/std_touppercase.java | 14 +++++++ .../lib/modules/functions/std_trim.java | 14 +++++++ src/com/annimon/ownlang/lib/modules/std.java | 16 ++++++++ 16 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_charat.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_indexof.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_join.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_replace.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_replaceall.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_split.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_substring.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_tochar.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_touppercase.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_trim.java diff --git a/program.own b/program.own index 11aafe2a..c8c5f999 100644 --- a/program.own +++ b/program.own @@ -180,4 +180,9 @@ class = { "div": def(a, b) = a / b } -println class.add(2, class.mul(2, 2)) \ No newline at end of file +println class.add(2, class.mul(2, 2)) + +println split("1/2/3/4/5/6", "/") +println join(nums, ", ") +println join(nums, "|", "/") +println join(nums, ", ", "[", "]") \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_charat.java b/src/com/annimon/ownlang/lib/modules/functions/std_charat.java new file mode 100644 index 00000000..3a249317 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_charat.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_charat implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 2) throw new ArgumentsMismatchException("Two arguments expected"); + + final String input = args[0].asString(); + final int index = (int) args[1].asNumber(); + + return new NumberValue(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 new file mode 100644 index 00000000..032871a3 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_indexof.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_indexof implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2 || args.length > 3) throw new ArgumentsMismatchException("Two or three arguments expected"); + + final String input = args[0].asString(); + final String what = args[1].asString(); + final int index = (args.length == 3) ? ((int) args[2].asNumber()) : 0; + + return new NumberValue(input.indexOf(what, index)); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_join.java b/src/com/annimon/ownlang/lib/modules/functions/std_join.java new file mode 100644 index 00000000..3cff847c --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_join.java @@ -0,0 +1,41 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; + +public final class std_join implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 1) throw new ArgumentsMismatchException("At least one argument expected"); + if (args[0].type() != Types.ARRAY) { + throw new TypeException("Array expected in first argument"); + } + + final ArrayValue array = (ArrayValue) args[0]; + switch (args.length) { + case 1: + return join(array, "", "", ""); + case 2: + return join(array, args[1].asString(), "", ""); + case 3: + return join(array, args[1].asString(), args[2].asString(), args[2].asString()); + case 4: + return join(array, args[1].asString(), args[2].asString(), args[3].asString()); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } + + private static StringValue join(ArrayValue array, String delimiter, String prefix, String suffix) { + final StringBuilder sb = new StringBuilder(); + for (Value value : array) { + if (sb.length() > 0) sb.append(delimiter); + else sb.append(prefix); + sb.append(value.asString()); + } + sb.append(suffix); + return new StringValue(sb.toString()); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java b/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java new file mode 100644 index 00000000..5b10251d --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_lastindexof implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2 || args.length > 3) throw new ArgumentsMismatchException("Two or three arguments expected"); + + final String input = args[0].asString(); + final String what = args[1].asString(); + final int index = (args.length == 3) ? ((int) args[2].asNumber()) : 0; + + return new NumberValue(input.lastIndexOf(what, index)); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_replace.java b/src/com/annimon/ownlang/lib/modules/functions/std_replace.java new file mode 100644 index 00000000..b9746b5f --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_replace.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_replace implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 3) throw new ArgumentsMismatchException("Three arguments expected"); + + final String input = args[0].asString(); + final String target = args[1].asString(); + final String replacement = args[2].asString(); + + return new StringValue(input.replace(target, replacement)); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_replaceall.java b/src/com/annimon/ownlang/lib/modules/functions/std_replaceall.java new file mode 100644 index 00000000..9539c230 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_replaceall.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_replaceall implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 3) throw new ArgumentsMismatchException("Three arguments expected"); + + final String input = args[0].asString(); + final String regex = args[1].asString(); + final String replacement = args[2].asString(); + + return new StringValue(input.replaceAll(regex, replacement)); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java b/src/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java new file mode 100644 index 00000000..24557ed6 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_replacefirst implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 3) throw new ArgumentsMismatchException("Three arguments expected"); + + final String input = args[0].asString(); + final String regex = args[1].asString(); + final String replacement = args[2].asString(); + + return new StringValue(input.replaceFirst(regex, replacement)); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_split.java b/src/com/annimon/ownlang/lib/modules/functions/std_split.java new file mode 100644 index 00000000..13f2bde9 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_split.java @@ -0,0 +1,24 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_split implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 2 || args.length > 3) throw new ArgumentsMismatchException("Two or three arguments expected"); + + final String input = args[0].asString(); + final String regex = args[1].asString(); + final int limit = (args.length == 3) ? ((int) args[2].asNumber()) : 0; + + final String[] parts = input.split(regex, limit); + final ArrayValue result = new ArrayValue(parts.length); + for (int i = 0; i < parts.length; i++) { + result.set(i, new StringValue(parts[i])); + } + + return result; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java b/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java new file mode 100644 index 00000000..1767be67 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_sprintf implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 1) throw new ArgumentsMismatchException("At least one argument expected"); + + 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(); + } + return new StringValue(String.format(format, values)); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_substring.java b/src/com/annimon/ownlang/lib/modules/functions/std_substring.java new file mode 100644 index 00000000..22e697a2 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_substring.java @@ -0,0 +1,25 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_substring implements Function { + + @Override + public Value execute(Value... args) { + 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(); + + String result; + if (args.length == 2) { + result = input.substring(startIndex); + } else { + final int endIndex = (int) args[2].asNumber(); + result = input.substring(startIndex, endIndex); + } + + return new StringValue(result); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java b/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java new file mode 100644 index 00000000..00b5c25e --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_tochar implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + + return new StringValue(String.valueOf((char) args[0].asNumber())); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java b/src/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java new file mode 100644 index 00000000..ace22481 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_tolowercase implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + + return new StringValue(args[0].asString().toLowerCase()); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_touppercase.java b/src/com/annimon/ownlang/lib/modules/functions/std_touppercase.java new file mode 100644 index 00000000..3a9d67b9 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_touppercase.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_touppercase implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + + return new StringValue(args[0].asString().toUpperCase()); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_trim.java b/src/com/annimon/ownlang/lib/modules/functions/std_trim.java new file mode 100644 index 00000000..7f08e760 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_trim.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class std_trim implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + + return new StringValue(args[0].asString().trim()); + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index fe986ea7..6958732b 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -18,5 +18,21 @@ public void init() { Functions.set("rand", new std_rand()); Functions.set("sleep", new std_sleep()); Functions.set("thread", new std_thread()); + + // String + Functions.set("sprintf", new std_sprintf()); + Functions.set("split", new std_split()); + Functions.set("join", new std_join()); + Functions.set("indexOf", new std_indexof()); + Functions.set("lastIndexOf", new std_lastindexof()); + Functions.set("charAt", new std_charat()); + Functions.set("toChar", new std_tochar()); + Functions.set("substring", new std_substring()); + Functions.set("toLowerCase", new std_tolowercase()); + Functions.set("toUpperCase", new std_touppercase()); + Functions.set("trim", new std_trim()); + Functions.set("replace", new std_replace()); + Functions.set("replaceAll", new std_replaceall()); + Functions.set("replaceFirst", new std_replacefirst()); } } From 6b95ae54cfaaef941c931f880ca201632a3f7410 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jan 2016 18:51:29 +0200 Subject: [PATCH 031/448] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20?= =?UTF-8?q?robot=20(java.awt.Robot)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/robot/paint_lines.own | 19 +++ .../annimon/ownlang/lib/modules/robot.java | 138 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 examples/robot/paint_lines.own create mode 100644 src/com/annimon/ownlang/lib/modules/robot.java diff --git a/examples/robot/paint_lines.own b/examples/robot/paint_lines.own new file mode 100644 index 00000000..2d15be75 --- /dev/null +++ b/examples/robot/paint_lines.own @@ -0,0 +1,19 @@ +use "robot" + +pause = 5 +xstep = 50 ystep = 5 +startx = 50 +starty = 400 + ystep / 2 + +for (y = 0, y < 300, y = y + ystep) { + + mouseMove(startx, (starty+y)) + mousePress(BUTTON1) + for (i = 0, i < 600, i = i + xstep) { + mouseMove((startx+i), (starty+y)) + delay(pause) + } + mouseRelease(BUTTON1) + delay (pause*3) + +} diff --git a/src/com/annimon/ownlang/lib/modules/robot.java b/src/com/annimon/ownlang/lib/modules/robot.java new file mode 100644 index 00000000..b61a246c --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/robot.java @@ -0,0 +1,138 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; +import java.awt.AWTException; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class robot implements Module { + + private static final int CLICK_DELAY = 200; + private static final int TYPING_DELAY = 50; + + private static final Map SYMBOL_CODES; + static { + SYMBOL_CODES = new HashMap<>(10); + SYMBOL_CODES.put('_', KeyEvent.VK_MINUS); + SYMBOL_CODES.put(':', KeyEvent.VK_SEMICOLON); + } + + private static Robot awtRobot; + + @Override + public void init() { + initialize(); + + Functions.set("click", convertFunction(robot::click)); + Functions.set("delay", convertFunction(awtRobot::delay)); + Functions.set("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); + Functions.set("keyPress", convertFunction(awtRobot::keyPress)); + Functions.set("keyRelease", convertFunction(awtRobot::keyRelease)); + Functions.set("mousePress", convertFunction(awtRobot::mousePress)); + Functions.set("mouseRelease", convertFunction(awtRobot::mouseRelease)); + Functions.set("mouseWheel", convertFunction(awtRobot::mouseWheel)); + 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()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + }); + Functions.set("typeText", (args) -> { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + try { + typeText(args[0].asString()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + }); + + Variables.set("VK_DOWN", new NumberValue(KeyEvent.VK_DOWN)); + Variables.set("VK_LEFT", new NumberValue(KeyEvent.VK_LEFT)); + Variables.set("VK_RIGHT", new NumberValue(KeyEvent.VK_RIGHT)); + Variables.set("VK_FIRE", new NumberValue(KeyEvent.VK_ENTER)); + Variables.set("VK_ESCAPE", new NumberValue(KeyEvent.VK_ESCAPE)); + + Variables.set("BUTTON1", new NumberValue(InputEvent.BUTTON1_MASK)); + Variables.set("BUTTON2", new NumberValue(InputEvent.BUTTON2_MASK)); + Variables.set("BUTTON3", new NumberValue(InputEvent.BUTTON3_MASK)); + } + + private static void initialize() { + try { + awtRobot = new Robot(); + } catch (AWTException awte) { + throw new RuntimeException("Unable to create robot instance", awte); + } + } + + @FunctionalInterface + private interface RobotIntConsumer { + void accept(int value) throws IllegalArgumentException; + } + + private static Function convertFunction(RobotIntConsumer consumer) { + return args -> { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + try { + consumer.accept((int) args[0].asNumber()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + }; + } + + private static synchronized void click(int buttons) { + awtRobot.mousePress(buttons); + awtRobot.delay(CLICK_DELAY); + awtRobot.mouseRelease(buttons); + } + + private static synchronized void typeText(String text) { + for (char ch : text.toCharArray()) { + typeSymbol(ch); + } + } + + private static void typeSymbol(char ch) { + int code = KeyEvent.getExtendedKeyCodeForChar(ch); + + boolean isUpperCase = Character.isLetter(ch) && Character.isUpperCase(ch); + boolean needPressShift = isUpperCase; + if (!isUpperCase) { + final int symbolIndex = "!@#$%^&*()".indexOf(ch); + if (symbolIndex != -1) { + needPressShift = true; + code = '1' + symbolIndex; + } else if (SYMBOL_CODES.containsKey(ch)) { + needPressShift = true; + code = SYMBOL_CODES.get(ch); + } + } + + if (code == KeyEvent.VK_UNDEFINED) return; + + if (needPressShift) { + // press shift + awtRobot.keyPress(KeyEvent.VK_SHIFT); + awtRobot.delay(TYPING_DELAY); + } + + awtRobot.keyPress(code); + awtRobot.delay(TYPING_DELAY); + awtRobot.keyRelease(code); + + if (needPressShift) { + // release shift + awtRobot.delay(TYPING_DELAY); + awtRobot.keyRelease(KeyEvent.VK_SHIFT); + awtRobot.delay(TYPING_DELAY); + } + } +} From 95ca90824380731dfeda7bb130c92297d938ef66 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jan 2016 19:07:06 +0200 Subject: [PATCH 032/448] =?UTF-8?q?match=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=BC=D0=BE=D0=B6=D0=B5=D1=82=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B2=20=D0=BA=D0=B0=D1=87=D0=B5=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/canvas/animate_line.own | 14 +++++---- src/com/annimon/ownlang/parser/Parser.java | 5 +++- .../ownlang/parser/ast/ExprStatement.java | 30 +++++++++++++++++++ .../ownlang/parser/ast/FunctionStatement.java | 29 ------------------ .../annimon/ownlang/parser/ast/Visitor.java | 2 +- .../parser/visitors/AbstractVisitor.java | 4 +-- 6 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 src/com/annimon/ownlang/parser/ast/ExprStatement.java delete mode 100644 src/com/annimon/ownlang/parser/ast/FunctionStatement.java diff --git a/examples/canvas/animate_line.own b/examples/canvas/animate_line.own index d017e8a1..b16d3368 100644 --- a/examples/canvas/animate_line.own +++ b/examples/canvas/animate_line.own @@ -46,10 +46,12 @@ def sethsbcolor(h1) { hueindex = floor(qr) % 6 f = qr - floor(qr) - if hueindex == 0 color(255, f*255, 0) - else if hueindex == 1 color(255 - f*255, 255, 0) - else if hueindex == 2 color(0, 255, f*255) - else if hueindex == 3 color(0, 255-f*255, 255) - else if hueindex == 4 color(f*255, 0, 255) - else if hueindex == 5 color(255, 0, 255-f*255) + match hueindex { + case 0: color(255, f*255, 0) + case 1: color(255 - f*255, 255, 0) + case 2: color(0, 255, f*255) + case 3: color(0, 255-f*255, 255) + case 4: color(f*255, 0, 255) + case 5: color(255, 0, 255-f*255) + } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 6c80ae9b..ece352ab 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -84,8 +84,11 @@ private Statement statement() { if (match(TokenType.DEF)) { return functionDefine(); } + if (match(TokenType.MATCH)) { + return new ExprStatement(match()); + } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return new FunctionStatement(function(qualifiedName())); + return new ExprStatement(function(qualifiedName())); } return assignmentStatement(); } diff --git a/src/com/annimon/ownlang/parser/ast/ExprStatement.java b/src/com/annimon/ownlang/parser/ast/ExprStatement.java new file mode 100644 index 00000000..e7d8e8c6 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ExprStatement.java @@ -0,0 +1,30 @@ +package com.annimon.ownlang.parser.ast; + +/** + * Wrapper for expressions, which can be used as statements. + * + * @author aNNiMON + */ +public final class ExprStatement implements Statement { + + public final Expression expr; + + public ExprStatement(Expression function) { + this.expr = function; + } + + @Override + public void execute() { + expr.eval(); + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return expr.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/FunctionStatement.java b/src/com/annimon/ownlang/parser/ast/FunctionStatement.java deleted file mode 100644 index 54cda62d..00000000 --- a/src/com/annimon/ownlang/parser/ast/FunctionStatement.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.annimon.ownlang.parser.ast; - -/** - * - * @author aNNiMON - */ -public final class FunctionStatement implements Statement { - - public final FunctionalExpression function; - - public FunctionStatement(FunctionalExpression function) { - this.function = function; - } - - @Override - public void execute() { - function.eval(); - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public String toString() { - return function.toString(); - } -} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 1c1735d1..ab3356ae 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -21,7 +21,7 @@ public interface Visitor { void visit(ForeachMapStatement s); void visit(FunctionDefineStatement s); void visit(FunctionReferenceExpression e); - void visit(FunctionStatement s); + void visit(ExprStatement s); void visit(FunctionalExpression s); void visit(IfStatement s); void visit(MapExpression s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index b6a960fa..661c1ed9 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -98,8 +98,8 @@ public void visit(FunctionReferenceExpression e) { } @Override - public void visit(FunctionStatement s) { - s.function.accept(this); + public void visit(ExprStatement s) { + s.expr.accept(this); } @Override From 45ef0f88bbc85b35c67d0eb6dc8d4e456130a90e Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 23 Jan 2016 12:50:10 +0200 Subject: [PATCH 033/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BC=D0=BD=D0=BE=D0=B6=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=BE=20=D0=B2=D0=BE=D1=81=D1=85=D0=B8=D1=82=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/StringValue.java | 2 + .../lib/modules/functions/robot_exec.java | 54 +++++++++++++++++++ .../functions/robot_fromclipboard.java | 21 ++++++++ .../modules/functions/robot_toclipboard.java | 20 +++++++ .../lib/modules/functions/std_readln.java | 14 +++++ .../lib/modules/functions/std_time.java | 13 +++++ .../annimon/ownlang/lib/modules/robot.java | 7 +++ src/com/annimon/ownlang/lib/modules/std.java | 2 + .../ownlang/parser/ast/UseStatement.java | 4 +- 9 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/com/annimon/ownlang/lib/modules/functions/robot_exec.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_readln.java create mode 100644 src/com/annimon/ownlang/lib/modules/functions/std_time.java diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index 39d83e1f..8e820a90 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -8,6 +8,8 @@ */ public final class StringValue implements Value { + public static final StringValue EMPTY = new StringValue(""); + private final String value; public StringValue(String value) { diff --git a/src/com/annimon/ownlang/lib/modules/functions/robot_exec.java b/src/com/annimon/ownlang/lib/modules/functions/robot_exec.java new file mode 100644 index 00000000..07928484 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/robot_exec.java @@ -0,0 +1,54 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; + +public final class robot_exec implements Function { + + public static enum Mode { EXEC, EXEC_AND_WAIT }; + + private final Mode mode; + + public robot_exec(Mode mode) { + this.mode = mode; + } + + @Override + public Value execute(Value... args) { + if (args.length == 0) throw new ArgumentsMismatchException("At least one argument expected"); + + try { + final Process process; + if (args.length > 1) { + process = Runtime.getRuntime().exec(toStringArray(args)); + } else switch (args[0].type()) { + case Types.ARRAY: + final ArrayValue array = (ArrayValue) args[0]; + process = Runtime.getRuntime().exec(toStringArray(array.getCopyElements())); + break; + + default: + process = Runtime.getRuntime().exec(args[0].asString()); + } + + switch (mode) { + case EXEC_AND_WAIT: + return new NumberValue(process.waitFor()); + case EXEC: + default: + return NumberValue.ZERO; + } + } catch (Exception ex) { + ex.printStackTrace(); + return NumberValue.ZERO; + } + } + + private static String[] toStringArray(Value[] values) { + final String[] strings = new String[values.length]; + for (int i = 0; i < values.length; i++) { + strings[i] = values[i].asString(); + } + return strings; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java b/src/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java new file mode 100644 index 00000000..cf50b648 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java @@ -0,0 +1,21 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; + +public final class robot_fromclipboard implements Function { + + @Override + public Value execute(Value... args) { + try { + Object data = Toolkit.getDefaultToolkit().getSystemClipboard() + .getData(DataFlavor.stringFlavor); + return new StringValue(data.toString()); + } catch (Exception ex) { + return StringValue.EMPTY; + } + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java b/src/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java new file mode 100644 index 00000000..c7015942 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; + +public final class robot_toclipboard implements Function { + + @Override + public Value execute(Value... args) { + if (args.length != 1) throw new ArgumentsMismatchException("One argument expected"); + + Toolkit.getDefaultToolkit().getSystemClipboard() + .setContents(new StringSelection(args[0].asString()), null); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_readln.java b/src/com/annimon/ownlang/lib/modules/functions/std_readln.java new file mode 100644 index 00000000..0ec243ab --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_readln.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.*; +import java.util.Scanner; + +public final class std_readln implements Function { + + @Override + public Value execute(Value... args) { + try (Scanner sc = new Scanner(System.in)) { + return new StringValue(sc.nextLine()); + } + } +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_time.java b/src/com/annimon/ownlang/lib/modules/functions/std_time.java new file mode 100644 index 00000000..37b8ce1f --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/functions/std_time.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +public final class std_time implements Function { + + @Override + public Value execute(Value... args) { + return new NumberValue(System.currentTimeMillis()); + } +} \ 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 b61a246c..7a91a039 100644 --- a/src/com/annimon/ownlang/lib/modules/robot.java +++ b/src/com/annimon/ownlang/lib/modules/robot.java @@ -2,6 +2,9 @@ import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.robot_exec; +import com.annimon.ownlang.lib.modules.functions.robot_fromclipboard; +import com.annimon.ownlang.lib.modules.functions.robot_toclipboard; import java.awt.AWTException; import java.awt.Robot; import java.awt.event.InputEvent; @@ -53,6 +56,10 @@ public void init() { } catch (IllegalArgumentException iae) { } return NumberValue.ZERO; }); + Functions.set("toClipboard", new robot_toclipboard()); + Functions.set("fromClipboard", new robot_fromclipboard()); + Functions.set("execProcess", new robot_exec(robot_exec.Mode.EXEC)); + Functions.set("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); Variables.set("VK_DOWN", new NumberValue(KeyEvent.VK_DOWN)); Variables.set("VK_LEFT", new NumberValue(KeyEvent.VK_LEFT)); diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index 6958732b..9422a5f6 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -12,10 +12,12 @@ public final class std implements Module { @Override public void init() { Functions.set("echo", new std_echo()); + Functions.set("readln", new std_readln()); Functions.set("newarray", new std_newarray()); Functions.set("sort", new std_sort()); Functions.set("length", new std_length()); Functions.set("rand", new std_rand()); + Functions.set("time", new std_time()); Functions.set("sleep", new std_sleep()); Functions.set("thread", new std_thread()); diff --git a/src/com/annimon/ownlang/parser/ast/UseStatement.java b/src/com/annimon/ownlang/parser/ast/UseStatement.java index 39397fab..cd46154f 100644 --- a/src/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/src/com/annimon/ownlang/parser/ast/UseStatement.java @@ -22,7 +22,9 @@ public void execute() { final String moduleName = expression.eval().asString(); final Module module = (Module) Class.forName(PACKAGE + moduleName).newInstance(); module.init(); - } catch (Exception ex) { } + } catch (Exception ex) { + throw new RuntimeException(ex); + } } @Override From cf6f8c5fe8d4f03ddc76bfb8d6ccebc8a73573a2 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 23 Jan 2016 18:48:35 +0200 Subject: [PATCH 034/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20ant=20task=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=BE=D0=BD=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=B1=D0=B8?= =?UTF-8?q?=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA=20=D0=B2=20=D0=B5?= =?UTF-8?q?=D0=B4=D0=B8=D0=BD=D1=8B=D0=B9=20jar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- build.xml | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 838458f2..c6268979 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -/dist/ \ No newline at end of file +/dist/ +/store/ +/nbproject/private/ \ No newline at end of file diff --git a/build.xml b/build.xml index f3b3e8f2..51cd47d6 100644 --- a/build.xml +++ b/build.xml @@ -10,6 +10,34 @@ Builds, tests, and runs the project OwnLang. + + + + + + + + + + + + + + + + + + + + + + + + + + + Z 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 diff --git a/libs/okhttp-3.1.2.jar b/libs/okhttp-3.1.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..d5d584f6be8c4282019947681a9d7c5f5b0fbb7d GIT binary patch literal 333776 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

*h}miLXW|JDOOjt+fy>W?G*-p`6Oi+o~b9dpBKL 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

W2FbRd^ky@p&UmrH%sRbjC`^ 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

amv;iO+71WUG{H~ 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 literal 0 HcmV?d00001 diff --git a/libs/okio-1.6.0.jar b/libs/okio-1.6.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..c87be599f60608338eaed9228e1f39de19ab816c GIT binary patch literal 65928 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 entry : response.headers().toMultimap().entrySet()) { + final int valuesSize = entry.getValue().size(); + final ArrayValue values = new ArrayValue(valuesSize); + for (int i = 0; i < valuesSize; i++) { + values.set(i, new StringValue(entry.getValue().get(i))); + } + headers.set(new StringValue(entry.getKey()), values); + } + map.set(new StringValue("headers"), headers); + map.set(new StringValue("content_length"), new NumberValue(response.body().contentLength())); + map.set(CONTENT_TYPE, new StringValue(response.body().contentType().toString())); + return map; + } + return new StringValue(response.body().string()); + } - private void applyHeaderParams(MapValue headerParams, HttpRequestBase httpMethod) { + private void applyHeaderParams(MapValue headerParams, Request.Builder builder) { for (Map.Entry p : headerParams) { - httpMethod.addHeader(p.getKey().asString(), p.getValue().asString()); + builder.header(p.getKey().asString(), p.getValue().asString()); + } + } + + private RequestBody getRequestBody(String method, Value params, MapValue options) throws UnsupportedEncodingException { + if (!HttpMethod.permitsRequestBody(method)) return null; + + if (params.type() == Types.MAP) { + return getMapRequestBody((MapValue) params, options); } + return getStringRequestBody(params, options); } - private void applyMapRequestParams(HttpEntityEnclosingRequestBase h, MapValue params, MapValue options) - throws UnsupportedEncodingException { - final List entityParams = new ArrayList<>(params.size()); + 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); for (Map.Entry param : params) { final String name = param.getKey().asString(); final String value = param.getValue().asString(); - entityParams.add(new BasicNameValuePair(name, value)); + if (alreadyEncoded) + form.addEncoded(name, value); + else + form.add(name, value); } - HttpEntity entity; - if (options.containsKey(CHARSET_KEY)) { - entity = new UrlEncodedFormEntity(entityParams, options.get(CHARSET_KEY).asString()); - } else { - entity = new UrlEncodedFormEntity(entityParams); - } - h.setEntity(entity); + return form.build(); } - private void applyStringRequestParams(final HttpEntityEnclosingRequestBase heerb, Value requestParams, MapValue options) throws UnsupportedEncodingException, UnsupportedCharsetException { - HttpEntity entity; - if (options.containsKey(CHARSET_KEY)) { - entity = new StringEntity(requestParams.asString(), options.get(CHARSET_KEY).asString()); + private RequestBody getStringRequestBody(Value params, MapValue options) throws UnsupportedEncodingException { + final MediaType type; + if (options.containsKey(CONTENT_TYPE)) { + type = MediaType.parse(options.get(CONTENT_TYPE).asString()); } else { - entity = new StringEntity(requestParams.asString()); + type = URLENCODED_MEDIA_TYPE; } - heerb.setEntity(entity); + + if (options.containsKey(CHARSET_KEY)) { + final String charset = options.get(CHARSET_KEY).asString(); + return RequestBody.create(type, params.asString().getBytes(charset)); + } + + return RequestBody.create(type, params.asString()); } - } \ No newline at end of file From d737844913e1c06dedf4a07c636265259262e3df Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 16 Feb 2016 16:18:43 +0200 Subject: [PATCH 047/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/modules/files.java | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 src/com/annimon/ownlang/lib/modules/files.java diff --git a/src/com/annimon/ownlang/lib/modules/files.java b/src/com/annimon/ownlang/lib/modules/files.java new file mode 100644 index 00000000..9aea6296 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/files.java @@ -0,0 +1,369 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class files implements Module { + + private static Map files; + + @Override + public void init() { + files = new HashMap<>(); + + Functions.set("fopen", new fopen()); + Functions.set("readBoolean", new readBoolean()); + Functions.set("readByte", new readByte()); + Functions.set("readBytes", new readBytes()); + Functions.set("readAllBytes", new readAllBytes()); + Functions.set("readChar", new readChar()); + Functions.set("readShort", new readShort()); + Functions.set("readInt", new readInt()); + Functions.set("readLong", new readLong()); + Functions.set("readFloat", new readFloat()); + Functions.set("readDouble", new readDouble()); + Functions.set("readUTF", new readUTF()); + Functions.set("readLine", new readLine()); + Functions.set("readText", new readText()); + Functions.set("writeBoolean", new writeBoolean()); + Functions.set("writeByte", new writeByte()); + Functions.set("writeChar", new writeChar()); + Functions.set("writeShort", new writeShort()); + Functions.set("writeInt", new writeInt()); + Functions.set("writeLong", new writeLong()); + Functions.set("writeFloat", new writeFloat()); + Functions.set("writeDouble", new writeDouble()); + Functions.set("writeUTF", new writeUTF()); + Functions.set("writeLine", new writeLine()); + Functions.set("flush", new flush()); + Functions.set("fclose", new fclose()); + } + + private static class fopen implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 1) throw new ArgumentsMismatchException("At least one argument expected"); + + final File file = new File(args[0].asString()); + try { + if (args.length > 1) { + return process(file, args[1].asString().toLowerCase()); + } + return process(file, "r"); + } catch (IOException ioe) { + return new NumberValue(-1); + } + } + + private Value process(File file, String mode) throws IOException { + DataInputStream dis = null; + BufferedReader reader = null; + if (mode.contains("rb")) { + dis = new DataInputStream(new FileInputStream(file)); + } else if (mode.contains("r")) { + reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + } + + DataOutputStream dos = null; + BufferedWriter writer = null; + if (mode.contains("wb")) { + dos = new DataOutputStream(new FileOutputStream(file)); + } else if (mode.contains("w")) { + writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); + } + + final int key = files.size(); + files.put(key, new FileInfo(file, dis, dos, reader, writer)); + return new NumberValue(key); + } + } + + private static abstract class FileFunction implements Function { + + @Override + public Value execute(Value... args) { + if (args.length < 1) throw new ArgumentsMismatchException("File descriptor expected"); + final int key = (int) args[0].asNumber(); + try { + return execute(files.get(key), args); + } catch (IOException ioe) { + return NumberValue.ZERO; + } + } + + protected abstract Value execute(FileInfo fileInfo, Value[] args) throws IOException; + } + + private static class readBoolean extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readBoolean() ? 1 : 0); + } + } + + private static class readByte extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readByte()); + } + } + + private static class readBytes extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + 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(); + } + + final byte[] buffer = new byte[length]; + final int readed = fileInfo.dis.read(buffer, offset, length); + for (int i = 0; i < readed; i++) { + array.set(i, new NumberValue(buffer[i])); + } + return new NumberValue(readed); + } + } + + private static class readAllBytes extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + final int bufferSize = 4096; + final byte[] buffer = new byte[bufferSize]; + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int readed; + while ((readed = fileInfo.dis.read(buffer, 0, bufferSize)) != -1) { + baos.write(buffer, 0, readed); + } + baos.flush(); + baos.close(); + final byte[] bytes = baos.toByteArray(); + final int size = bytes.length; + final ArrayValue result = new ArrayValue(size); + for (int i = 0; i < size; i++) { + result.set(i, new NumberValue(bytes[i])); + } + return result; + } + } + + private static class readChar extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readChar()); + } + } + + private static class readShort extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readShort()); + } + } + + private static class readInt extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readInt()); + } + } + + private static class readLong extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readLong()); + } + } + + private static class readFloat extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readFloat()); + } + } + + private static class readDouble extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new NumberValue(fileInfo.dis.readDouble()); + } + } + + private static class readUTF extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new StringValue(fileInfo.dis.readUTF()); + } + } + + private static class readLine extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + return new StringValue(fileInfo.reader.readLine()); + } + } + + private static class readText extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + final StringBuilder sb = new StringBuilder(); + int ch; + while ((ch = fileInfo.reader.read()) != -1) { + sb.append((char) ch); + } + return new StringValue(sb.toString()); + } + } + + + private static class writeBoolean extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeBoolean(args[1].asNumber() != 0); + return NumberValue.ONE; + } + } + + private static class writeByte extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeByte((byte) args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class writeChar extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + final char ch = (args[1].type() == Types.NUMBER) + ? ((char) args[1].asNumber()) + : args[1].asString().charAt(0); + fileInfo.dos.writeChar(ch); + return NumberValue.ONE; + } + } + + private static class writeShort extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeShort((short) args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class writeInt extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeInt((int) args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class writeLong extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeLong((long) args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class writeFloat extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeFloat((float) args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class writeDouble extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeDouble(args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class writeUTF extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeUTF(args[1].asString()); + return NumberValue.ONE; + } + } + + private static class writeLine extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + fileInfo.dos.writeDouble(args[1].asNumber()); + return NumberValue.ONE; + } + } + + private static class flush extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + if (fileInfo.dos != null) { + fileInfo.dos.flush(); + } + if (fileInfo.writer != null) { + fileInfo.writer.flush(); + } + return NumberValue.ONE; + } + } + + private static class fclose extends FileFunction { + @Override + protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { + if (fileInfo.dis != null) { + fileInfo.dis.close(); + } + if (fileInfo.dos != null) { + fileInfo.dos.close(); + } + if (fileInfo.reader != null) { + fileInfo.reader.close(); + } + if (fileInfo.writer != null) { + fileInfo.writer.close(); + } + return NumberValue.ONE; + } + } + + private static class FileInfo { + File file; + DataInputStream dis; + DataOutputStream dos; + BufferedReader reader; + BufferedWriter writer; + + public FileInfo(File file, DataInputStream dis, DataOutputStream dos, BufferedReader reader, BufferedWriter writer) { + this.file = file; + this.dis = dis; + this.dos = dos; + this.reader = reader; + this.writer = writer; + } + } +} From 8a3719d67da9c3340a4323396174c1dfd19a5951 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 18 Feb 2016 12:35:20 +0200 Subject: [PATCH 048/448] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=87=D0=B8=D1=81=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=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 | 394 ++++++++++++++++-- .../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, 621 insertions(+), 124 deletions(-) diff --git a/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java b/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java index fffabcaf..6daec73e 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 ad593d0f..594c205f 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -72,6 +72,11 @@ public void set(int index, Value value) { 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 18967951..2cc89e7a 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -22,6 +22,11 @@ public int type() { 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 d8cc3b9b..d94cc118 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -44,6 +44,11 @@ public Value get(Value key) { 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 3f43f762..85c07f27 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -6,6 +6,7 @@ */ 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 static NumberValue fromBoolean(boolean b) { 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 int type() { 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 boolean equals(Object obj) { 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 8e820a90..fc6fa898 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -25,6 +25,15 @@ public int type() { 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 246b1c69..b588e52f 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 832e9f7d..e5853130 100644 --- a/src/com/annimon/ownlang/lib/Value.java +++ b/src/com/annimon/ownlang/lib/Value.java @@ -6,6 +6,8 @@ */ 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 b4e05c44..91d67363 100644 --- a/src/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/com/annimon/ownlang/lib/modules/canvas.java @@ -22,8 +22,6 @@ */ 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 void init() { 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 @@ private static void clip(int x, int y, int w, int h) { 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 void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { - lastKey = MINUS_ONE; + lastKey = NumberValue.MINUS_ONE; } }); addMouseMotionListener(new MouseMotionAdapter() { @@ -147,13 +141,13 @@ public Value execute(Value... args) { 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 @@ private static class DrawString implements Function { @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 @@ private static class SetColor implements Function { @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 9aea6296..5eaa00cd 100644 --- a/src/com/annimon/ownlang/lib/modules/files.java +++ b/src/com/annimon/ownlang/lib/modules/files.java @@ -69,7 +69,7 @@ public Value execute(Value... args) { } return process(file, "r"); } catch (IOException ioe) { - return new NumberValue(-1); + return NumberValue.MINUS_ONE; } } @@ -101,7 +101,7 @@ private static abstract class FileFunction implements Function { @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 Value execute(Value... args) { 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 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { 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 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { 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 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { 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 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { 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 @@ private static class writeChar extends FileFunction { @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 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { 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 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { 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 a81e73a7..4e801ef1 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 @@ private RequestBody getRequestBody(String method, Value params, MapValue options 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 57be629c..f9d102dd 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 @@ private Value process(Object obj) { 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 23d80e80..bf880ca3 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 @@ private Object process(Value val) { 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 3a249317..5ab4d9a9 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 Value execute(Value... args) { 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 032871a3..1fcce43b 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 Value execute(Value... args) { 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 5b10251d..14d780a6 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 Value execute(Value... args) { 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 f2a1fc2e..7b39f6c6 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 Value execute(Value... args) { } 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 cc673bd2..455beb34 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 Value execute(Value... args) { 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 edd0e40b..3d448615 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 Value execute(Value... args) { 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 13f2bde9..51ccdbe8 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 Value execute(Value... args) { 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 1767be67..cb282e72 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 Value execute(Value... args) { 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 22e697a2..62bb88a2 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 Value execute(Value... args) { 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 00b5c25e..6cf48281 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 7a91a039..4372729c 100644 --- a/src/com/annimon/ownlang/lib/modules/robot.java +++ b/src/com/annimon/ownlang/lib/modules/robot.java @@ -45,7 +45,7 @@ public void init() { 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 @@ private static Function convertFunction(RobotIntConsumer consumer) { 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 e82d2a29..4b390a58 100644 --- a/src/com/annimon/ownlang/lib/modules/types.java +++ b/src/com/annimon/ownlang/lib/modules/types.java @@ -20,5 +20,12 @@ public void init() { 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 47d22457..89011f45 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -327,12 +327,12 @@ private MatchExpression match() { 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 @@ private List variableSuffix() { 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 @@ private Expression value() { 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 e870ebbe..f55369e1 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -54,70 +54,384 @@ public BinaryExpression(Operator operation, Expression expr1, Expression expr2) public Value eval() { final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); - + 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: + throw new OperationIsNotSupportedException(operation); + } + } + + 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: - return eval((StringValue) value1, value2); - case Types.ARRAY: - return eval((ArrayValue) value1, value2); - case Types.NUMBER: default: - return eval(value1, value2); + // Concatenation strings + return new StringValue(value1.asString() + value2.asString()); } } - 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(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; - + 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()); + } + // 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); + 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(result); + 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 569df038..87668e82 100644 --- a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -48,9 +48,9 @@ public Value eval() { 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 f3c0f296..0f06adf0 100644 --- a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -29,7 +29,7 @@ public Value get() { 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 Value set(Value value) { 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 Value getContainer() { 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 783098ce..d3ee971d 100644 --- a/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java +++ b/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java @@ -25,7 +25,7 @@ public void execute() { // 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 cd3d03fa..eaf7df6d 100644 --- a/src/com/annimon/ownlang/parser/ast/ForStatement.java +++ b/src/com/annimon/ownlang/parser/ast/ForStatement.java @@ -20,7 +20,7 @@ public ForStatement(Statement initialization, Expression termination, 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 0ba8e233..64c87008 100644 --- a/src/com/annimon/ownlang/parser/ast/IfStatement.java +++ b/src/com/annimon/ownlang/parser/ast/IfStatement.java @@ -17,7 +17,7 @@ public IfStatement(Expression expression, Statement ifStatement, Statement elseS @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 6d7bcce2..5fec52ba 100644 --- a/src/com/annimon/ownlang/parser/ast/TernaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/TernaryExpression.java @@ -19,7 +19,7 @@ public TernaryExpression(Expression condition, Expression trueExpr, Expression f @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 1d688bd9..75b1f794 100644 --- a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -2,6 +2,8 @@ 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 Value eval() { 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 void accept(Visitor visitor) { @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 e7f894e0..1dc6477c 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 4b43e616..4ae348c0 100644 --- a/src/com/annimon/ownlang/parser/ast/WhileStatement.java +++ b/src/com/annimon/ownlang/parser/ast/WhileStatement.java @@ -16,7 +16,7 @@ public WhileStatement(Expression condition, Statement statement) { @Override public void execute() { - while (condition.eval().asNumber() != 0) { + while (condition.eval().asInt() != 0) { try { statement.execute(); } catch (BreakStatement bs) { From 1cc4dc8f773da55c9e16a94b7d5e5e6b570b3db3 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 18 Feb 2016 13:19:57 +0200 Subject: [PATCH 049/448] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B8=D0=B4=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=82=D0=BE=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 5 ++++- src/com/annimon/ownlang/parser/Lexer.java | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/program.own b/program.own index b7f63a17..860b6923 100644 --- a/program.own +++ b/program.own @@ -231,4 +231,7 @@ xarr = [0, 5, 2] xarr[0] += xarr[1] xarr[0] *= xarr[2] xarr[0]++ -println xarr[0] \ No newline at end of file +println xarr[0] + +`extended word variable` = 9 +println `extended word variable` \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 03c0af28..c93a86e2 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -116,6 +116,7 @@ public List tokenize() { final char current = peek(0); if (Character.isDigit(current)) tokenizeNumber(); else if (Character.isJavaIdentifierStart(current)) tokenizeWord(); + else if (current == '`') tokenizeExtendedWord(); else if (current == '"') tokenizeText(); else if (current == '#') { next(); @@ -206,6 +207,21 @@ private void tokenizeWord() { } } + private void tokenizeExtendedWord() { + next();// skip ` + clearBuffer(); + char current = peek(0); + while (true) { + if (current == '\0') throw error("Reached end of file while parsing extended word."); + if (current == '\n' || current == '\r') throw error("Reached end of line while parsing extended word."); + if (current == '`') break; + buffer.append(current); + current = next(); + } + next(); // skip closing ` + addToken(TokenType.WORD, buffer.toString()); + } + private void tokenizeText() { next();// skip " clearBuffer(); @@ -216,6 +232,7 @@ private void tokenizeText() { current = next(); switch (current) { case '"': current = next(); buffer.append('"'); continue; + case '0': current = next(); buffer.append('\0'); continue; case 'b': current = next(); buffer.append('\b'); continue; case 'f': current = next(); buffer.append('\f'); continue; case 'n': current = next(); buffer.append('\n'); continue; From 6c093e50b52b724268a1a890fa9a269185602bf2 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 18 Feb 2016 13:20:26 +0200 Subject: [PATCH 050/448] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 6 +++++- .../ownlang/parser/ast/BinaryExpression.java | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/program.own b/program.own index 860b6923..356be515 100644 --- a/program.own +++ b/program.own @@ -234,4 +234,8 @@ xarr[0]++ println xarr[0] `extended word variable` = 9 -println `extended word variable` \ No newline at end of file +println `extended word variable` + +// Operator overloading +def `::`(v1, v2) = string(v1) + string(v2) +println 1 :: 2 :: 3 \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index f55369e1..30876a12 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; @@ -54,6 +55,17 @@ public BinaryExpression(Operator operation, Expression expr1, Expression expr2) public Value eval() { final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); + try { + return eval(value1, value2); + } catch (OperationIsNotSupportedException ex) { + if (Functions.isExists(operation.toString())) { + return Functions.get(operation.toString()).execute(value1, value2); + } + throw ex; + } + } + + private Value eval(Value value1, Value value2) throws OperationIsNotSupportedException { switch (operation) { case ADD: return add(value1, value2); case SUBTRACT: return subtract(value1, value2); @@ -75,10 +87,10 @@ public Value eval() { private Value add(Value value1, Value value2) { switch (value1.type()) { case Types.NUMBER: return add((NumberValue) value1, value2); + case Types.STRING: return new StringValue(value1.asString() + value2.asString()); 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()); From e947ae01d5d404275e93f5f6a484069db2b2ea69 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 18 Feb 2016 22:02:37 +0200 Subject: [PATCH 051/448] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=81=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D1=81=D0=B8=D0=BC=D0=B2=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=20=D0=B2=20=D1=81=D1=82=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 4 +++- src/com/annimon/ownlang/parser/Lexer.java | 27 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/program.own b/program.own index 356be515..274fb6a9 100644 --- a/program.own +++ b/program.own @@ -238,4 +238,6 @@ println `extended word variable` // Operator overloading def `::`(v1, v2) = string(v1) + string(v2) -println 1 :: 2 :: 3 \ No newline at end of file +println 1 :: 2 :: 3 + +println "\u042a" \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index c93a86e2..f3c8c533 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -150,7 +150,7 @@ private void tokenizeNumber() { private void tokenizeHexNumber() { clearBuffer(); char current = peek(0); - while (Character.isDigit(current) || isHexNumber(current)) { + while (isHexNumber(current)) { buffer.append(current); current = next(); } @@ -158,7 +158,9 @@ private void tokenizeHexNumber() { } private static boolean isHexNumber(char current) { - return "abcdef".indexOf(Character.toLowerCase(current)) != -1; + return Character.isDigit(current) + || ('a' <= current && current <= 'f') + || ('A' <= current && current <= 'F'); } private void tokenizeOperator() { @@ -236,7 +238,28 @@ private void tokenizeText() { case 'b': current = next(); buffer.append('\b'); continue; case 'f': current = next(); buffer.append('\f'); continue; case 'n': current = next(); buffer.append('\n'); continue; + case 'r': current = next(); buffer.append('\r'); continue; case 't': current = next(); buffer.append('\t'); continue; + case 'u': // http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.3 + int rollbackPosition = pos; + while (current == 'u') current = next(); + int escapedValue = 0; + for (int i = 12; i >= 0 && escapedValue != -1; i -= 4) { + if (isHexNumber(current)) { + escapedValue |= (Character.digit(current, 16) << i); + } else { + escapedValue = -1; + } + current = next(); + } + if (escapedValue >= 0) { + buffer.append((char) escapedValue); + } else { + // rollback + buffer.append("\\u"); + pos = rollbackPosition; + } + continue; } buffer.append('\\'); continue; From 2783417d4b34af9e81b80da65fc27161316ed3a1 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 10:31:26 +0200 Subject: [PATCH 052/448] =?UTF-8?q?=D0=91=D1=8B=D1=81=D1=82=D1=80=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=20=D0=BA=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=D1=83=20Value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 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 | 1 + src/com/annimon/ownlang/lib/StringValue.java | 5 +++++ src/com/annimon/ownlang/lib/Value.java | 2 ++ .../lib/modules/functions/json_encode.java | 2 +- .../lib/modules/functions/std_sprintf.java | 2 +- .../ownlang/parser/ast/BinaryExpression.java | 22 +++++++++---------- .../ownlang/parser/ast/UnaryExpression.java | 8 +++---- 10 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java index 594c205f..3d67b80c 100644 --- a/src/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -72,6 +72,11 @@ public void set(int index, Value value) { elements[index] = value; } + @Override + public Object raw() { + return elements; + } + @Override public int asInt() { throw new TypeException("Cannot cast array to integer"); diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index 2cc89e7a..97ad62ae 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -22,6 +22,11 @@ public int type() { return Types.FUNCTION; } + @Override + public Object raw() { + return value; + } + @Override public int asInt() { throw new TypeException("Cannot cast function to integer"); diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/com/annimon/ownlang/lib/MapValue.java index d94cc118..3b48ddb6 100644 --- a/src/com/annimon/ownlang/lib/MapValue.java +++ b/src/com/annimon/ownlang/lib/MapValue.java @@ -45,6 +45,11 @@ public void set(Value key, Value value) { map.put(key, value); } + @Override + public Object raw() { + return map; + } + @Override public int asInt() { throw new TypeException("Cannot cast map to integer"); diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/com/annimon/ownlang/lib/NumberValue.java index 85c07f27..b1a919e1 100644 --- a/src/com/annimon/ownlang/lib/NumberValue.java +++ b/src/com/annimon/ownlang/lib/NumberValue.java @@ -25,6 +25,7 @@ public int type() { return Types.NUMBER; } + @Override public Number raw() { return value; } diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/com/annimon/ownlang/lib/StringValue.java index fc6fa898..8a1ac225 100644 --- a/src/com/annimon/ownlang/lib/StringValue.java +++ b/src/com/annimon/ownlang/lib/StringValue.java @@ -24,6 +24,11 @@ public int length() { public int type() { return Types.STRING; } + + @Override + public Object raw() { + return value; + } @Override public int asInt() { diff --git a/src/com/annimon/ownlang/lib/Value.java b/src/com/annimon/ownlang/lib/Value.java index e5853130..0bac3e4e 100644 --- a/src/com/annimon/ownlang/lib/Value.java +++ b/src/com/annimon/ownlang/lib/Value.java @@ -6,6 +6,8 @@ */ public interface Value extends Comparable { + Object raw(); + int asInt(); double asNumber(); 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 bf880ca3..660e4dcd 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 @@ private Object process(Value val) { case Types.MAP: return process((MapValue) val); case Types.NUMBER: - return ((NumberValue) val).raw(); + return val.raw(); case Types.STRING: return val.asString(); default: 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 cb282e72..e6c72565 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java @@ -13,7 +13,7 @@ public Value execute(Value... args) { final Object[] values = new Object[args.length - 1]; for (int i = 1; i < args.length; i++) { values[i - 1] = (args[i].type() == Types.NUMBER) - ? ((NumberValue) args[i]).raw() + ? args[i].raw() : args[i].asString(); } return new StringValue(String.format(format, values)); diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index 30876a12..df549c4a 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -101,7 +101,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Double || number2 instanceof Double) { return new NumberValue(number1.doubleValue() + number2.doubleValue()); } @@ -138,7 +138,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Double || number2 instanceof Double) { return new NumberValue(number1.doubleValue() - number2.doubleValue()); } @@ -184,7 +184,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Double || number2 instanceof Double) { return new NumberValue(number1.doubleValue() * number2.doubleValue()); } @@ -221,7 +221,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Double || number2 instanceof Double) { return new NumberValue(number1.doubleValue() / number2.doubleValue()); } @@ -258,7 +258,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Double || number2 instanceof Double) { return new NumberValue(number1.doubleValue() % number2.doubleValue()); } @@ -303,7 +303,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Long || number2 instanceof Long) { return new NumberValue(number1.longValue() & number2.longValue()); } @@ -328,7 +328,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Long || number2 instanceof Long) { return new NumberValue(number1.longValue() | number2.longValue()); } @@ -353,7 +353,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Long || number2 instanceof Long) { return new NumberValue(number1.longValue() ^ number2.longValue()); } @@ -383,7 +383,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Long || number2 instanceof Long) { return new NumberValue(number1.longValue() << number2.longValue()); } @@ -408,7 +408,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Long || number2 instanceof Long) { return new NumberValue(number1.longValue() >> number2.longValue()); } @@ -433,7 +433,7 @@ 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(); + final Number number2 = (Number) value2.raw(); if (number1 instanceof Long || number2 instanceof Long) { return new NumberValue(number1.longValue() >>> number2.longValue()); } diff --git a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java index 75b1f794..81825242 100644 --- a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -88,7 +88,7 @@ public Value eval() { private Value increment(Value value) { if (value.type() == Types.NUMBER) { - final Number number = ((NumberValue) value).raw(); + final Number number = (Number) value.raw(); if (number instanceof Double) { return new NumberValue(number.doubleValue() + 1); } @@ -104,7 +104,7 @@ private Value increment(Value value) { private Value decrement(Value value) { if (value.type() == Types.NUMBER) { - final Number number = ((NumberValue) value).raw(); + final Number number = (Number) value.raw(); if (number instanceof Double) { return new NumberValue(number.doubleValue() - 1); } @@ -124,7 +124,7 @@ private Value negate(Value value) { return new StringValue(sb.reverse().toString()); } if (value.type() == Types.NUMBER) { - final Number number = ((NumberValue) value).raw(); + final Number number = (Number) value.raw(); if (number instanceof Double) { return new NumberValue(-number.doubleValue()); } @@ -140,7 +140,7 @@ private Value negate(Value value) { private Value complement(Value value) { if (value.type() == Types.NUMBER) { - final Number number = ((NumberValue) value).raw(); + final Number number = (Number) value.raw(); if (number instanceof Long) { return new NumberValue(~number.longValue()); } From 3e16e49ce7e9fd78a9449a7df428bb47cbf6e34a Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 11:42:29 +0200 Subject: [PATCH 053/448] =?UTF-8?q?=D0=92=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BE?= =?UTF-8?q?=20=D0=B4=D0=BB=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D1=81=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/Main.java | 27 +++++-- src/com/annimon/ownlang/TimeMeasurement.java | 79 ++++++++++++++++++++ 2 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/com/annimon/ownlang/TimeMeasurement.java diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 390341d8..1da24f5b 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -1,8 +1,6 @@ package com.annimon.ownlang; import com.annimon.ownlang.lib.CallStack; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.Token; @@ -14,6 +12,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; +import java.util.concurrent.TimeUnit; /** * @author aNNiMON @@ -22,11 +21,11 @@ public final class Main { public static void main(String[] args) throws IOException { if (args.length == 0) { - run(readFile("program.own"), true, true); + run(readFile("program.own"), true, true, true); return; } - boolean showTokens = false, showAst = false; + boolean showTokens = false, showAst = false, showMeasurements = false; String input = null; for (int i = 0; i < args.length; i++) { switch (args[i]) { @@ -40,6 +39,11 @@ public static void main(String[] args) throws IOException { showTokens = true; break; + case "-m": + case "--showtime": + showMeasurements = true; + break; + case "-f": case "--file": if (i + 1 < args.length) { @@ -55,23 +59,28 @@ public static void main(String[] args) throws IOException { if (input == null) { throw new IllegalArgumentException("Empty input"); } - run(input, showTokens, showAst); + run(input, showTokens, showAst, showMeasurements); } private static String readFile(String file) throws IOException { return new String( Files.readAllBytes(Paths.get(file)), "UTF-8"); } - private static void run(String input, boolean showTokens, boolean showAst) { + private static void run(String input, boolean showTokens, boolean showAst, boolean showMeasurements) { + final TimeMeasurement measurement = new TimeMeasurement(); + measurement.start("Tokenize time"); final List tokens = new Lexer(input).tokenize(); + measurement.stop("Tokenize time"); if (showTokens) { for (int i = 0; i < tokens.size(); i++) { System.out.println(i + " " + tokens.get(i)); } } + measurement.start("Parse time"); final Parser parser = new Parser(tokens); final Statement program = parser.parse(); + measurement.stop("Parse time"); if (showAst) { System.out.println(program.toString()); } @@ -83,9 +92,15 @@ private static void run(String input, boolean showTokens, boolean showAst) { // program.accept(new VariablePrinter()); program.accept(new AssignValidator()); try { + measurement.start("Execution time"); program.execute(); } catch (Exception ex) { handleException(Thread.currentThread(), ex); + } finally { + if (showMeasurements) { + measurement.stop("Execution time"); + System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); + } } } diff --git a/src/com/annimon/ownlang/TimeMeasurement.java b/src/com/annimon/ownlang/TimeMeasurement.java new file mode 100644 index 00000000..0a7b5e48 --- /dev/null +++ b/src/com/annimon/ownlang/TimeMeasurement.java @@ -0,0 +1,79 @@ +package com.annimon.ownlang; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public final class TimeMeasurement { + + private final Map finished, running; + + public TimeMeasurement() { + finished = new HashMap<>(); + running = new HashMap<>(); + } + + public void clear() { + finished.clear(); + running.clear(); + } + + public void start(String... names) { + final long time = System.nanoTime(); + for (String name : names) { + running.put(name, time); + } + } + + public void pause(String... names) { + final long time = System.nanoTime(); + for (String name : names) { + if (running.containsKey(name)) { + addTime(name, time - running.get(name)); + running.remove(name); + } + } + } + + public void stop(String... names) { + final long time = System.nanoTime(); + for (String name : names) { + if (running.containsKey(name)) { + addTime(name, time - running.get(name)); + } + } + } + + public Map getFinished() { + return finished; + } + + public String summary() { + return summary(TimeUnit.SECONDS, true); + } + + public String summary(TimeUnit unit, boolean showSummary) { + final String unitName = unit.name().toLowerCase(); + final StringBuilder result = new StringBuilder(); + long summaryTime = 0; + for (Map.Entry entry : finished.entrySet()) { + final long convertedTime = unit.convert(entry.getValue(), TimeUnit.NANOSECONDS); + summaryTime += convertedTime; + + result.append(entry.getKey()).append(": ") + .append(convertedTime).append(' ').append(unitName) + .append(System.lineSeparator()); + } + if (showSummary) { + result.append("Summary: ") + .append(summaryTime).append(' ').append(unitName) + .append(System.lineSeparator()); + } + return result.toString(); + } + + private void addTime(String name, long time) { + final long alreadyElapsed = finished.getOrDefault(name, 0L); + finished.put(name, alreadyElapsed + time); + } +} From 8eb25ef8c8cca455afb53739476dd9f3a2704d5c Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 12:20:20 +0200 Subject: [PATCH 054/448] =?UTF-8?q?=D0=9E=D0=BF=D0=B5=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=20include?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 4 +- src/com/annimon/ownlang/Main.java | 11 ++--- src/com/annimon/ownlang/parser/Lexer.java | 1 + src/com/annimon/ownlang/parser/Parser.java | 3 ++ .../annimon/ownlang/parser/SourceLoader.java | 30 ++++++++++++ src/com/annimon/ownlang/parser/TokenType.java | 1 + .../ownlang/parser/ast/IncludeStatement.java | 47 +++++++++++++++++++ .../annimon/ownlang/parser/ast/Visitor.java | 1 + .../parser/visitors/AbstractVisitor.java | 5 ++ visitor.own | 4 +- 10 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 src/com/annimon/ownlang/parser/SourceLoader.java create mode 100644 src/com/annimon/ownlang/parser/ast/IncludeStatement.java diff --git a/program.own b/program.own index 274fb6a9..d49a5425 100644 --- a/program.own +++ b/program.own @@ -240,4 +240,6 @@ println `extended word variable` def `::`(v1, v2) = string(v1) + string(v2) println 1 :: 2 :: 3 -println "\u042a" \ No newline at end of file +println "\u042a" + +include "visitor.own" \ No newline at end of file diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 1da24f5b..dbd6a8b7 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -3,14 +3,13 @@ import com.annimon.ownlang.lib.CallStack; import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.SourceLoader; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.AssignValidator; import com.annimon.ownlang.parser.visitors.FunctionAdder; import com.annimon.ownlang.parser.visitors.VariablePrinter; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; import java.util.concurrent.TimeUnit; @@ -21,7 +20,7 @@ public final class Main { public static void main(String[] args) throws IOException { if (args.length == 0) { - run(readFile("program.own"), true, true, true); + run(SourceLoader.readSource("program.own"), true, true, true); return; } @@ -47,7 +46,7 @@ public static void main(String[] args) throws IOException { case "-f": case "--file": if (i + 1 < args.length) { - input = readFile(args[i + 1]); + input = SourceLoader.readSource(args[i + 1]); i++; } break; @@ -62,10 +61,6 @@ public static void main(String[] args) throws IOException { run(input, showTokens, showAst, showMeasurements); } - private static String readFile(String file) throws IOException { - return new String( Files.readAllBytes(Paths.get(file)), "UTF-8"); - } - private static void run(String input, boolean showTokens, boolean showAst, boolean showMeasurements) { final TimeMeasurement measurement = new TimeMeasurement(); measurement.start("Tokenize time"); diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index f3c8c533..f37c32c1 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -91,6 +91,7 @@ public final class Lexer { KEYWORDS.put("match", TokenType.MATCH); KEYWORDS.put("case", TokenType.CASE); KEYWORDS.put("extract", TokenType.EXTRACT); + KEYWORDS.put("include", TokenType.INCLUDE); } private final String input; diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 89011f45..5710c158 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -129,6 +129,9 @@ private Statement statement() { if (match(TokenType.USE)) { return new UseStatement(expression()); } + if (match(TokenType.INCLUDE)) { + return new IncludeStatement(expression()); + } if (match(TokenType.FOR)) { return forStatement(); } diff --git a/src/com/annimon/ownlang/parser/SourceLoader.java b/src/com/annimon/ownlang/parser/SourceLoader.java new file mode 100644 index 00000000..aed81e12 --- /dev/null +++ b/src/com/annimon/ownlang/parser/SourceLoader.java @@ -0,0 +1,30 @@ +package com.annimon.ownlang.parser; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public final class SourceLoader { + + public static String readSource(String name) throws IOException { + InputStream is = SourceLoader.class.getResourceAsStream(name); + if (is != null) return readStream(is); + + is = new FileInputStream(name); + return readStream(is); + } + + private static String readStream(InputStream is) throws IOException { + final StringBuilder text = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { + String line; + while ((line = reader.readLine()) != null) { + text.append(line); + text.append(System.lineSeparator()); + } + } + return text.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index c277896e..347e4747 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -27,6 +27,7 @@ public enum TokenType { MATCH, CASE, EXTRACT, + INCLUDE, PLUS, // + MINUS, // - diff --git a/src/com/annimon/ownlang/parser/ast/IncludeStatement.java b/src/com/annimon/ownlang/parser/ast/IncludeStatement.java new file mode 100644 index 00000000..1570d681 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -0,0 +1,47 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.SourceLoader; +import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.visitors.FunctionAdder; +import java.util.List; + +/** + * + * @author aNNiMON + */ +public final class IncludeStatement implements Statement { + + public final Expression expression; + + public IncludeStatement(Expression expression) { + this.expression = expression; + } + + @Override + public void execute() { + try { + final String input = SourceLoader.readSource(expression.eval().asString()); + final List tokens = new Lexer(input).tokenize(); + final Parser parser = new Parser(tokens); + final Statement program = parser.parse(); + if (!parser.getParseErrors().hasErrors()) { + program.accept(new FunctionAdder()); + program.execute(); + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return "include " + expression; + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 7ef79c63..31ad18c4 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -24,6 +24,7 @@ public interface Visitor { void visit(ExprStatement s); void visit(FunctionalExpression s); void visit(IfStatement s); + void visit(IncludeStatement s); void visit(MapExpression s); void visit(MatchExpression s); void visit(PrintStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index cc2c573a..c669939b 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -118,6 +118,11 @@ public void visit(IfStatement s) { } } + @Override + public void visit(IncludeStatement s) { + s.expression.accept(this); + } + @Override public void visit(MapExpression s) { for (Map.Entry entry : s.elements.entrySet()) { diff --git a/visitor.own b/visitor.own index 9a9f6d82..e9548e65 100644 --- a/visitor.own +++ b/visitor.own @@ -1,5 +1,5 @@ -func() -def func() print "function\n" +function() +def function() print "function\n" a = 2 + 3 * 4 print a \ No newline at end of file From 5aae3e2edf8551c54ff59608ba95ee80aafbc23e Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 13:27:54 +0200 Subject: [PATCH 055/448] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20?= =?UTF-8?q?=D1=8E=D0=BD=D0=B8=D1=82-=D1=82=D0=B5=D1=81=D1=82=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/Main.java | 1 + src/com/annimon/ownlang/lib/Functions.java | 5 +- .../annimon/ownlang/lib/modules/ounit.java | 142 ++++++++++++++++++ tests.own | 39 +++++ 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/com/annimon/ownlang/lib/modules/ounit.java create mode 100644 tests.own diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index dbd6a8b7..499de79a 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -94,6 +94,7 @@ private static void run(String input, boolean showTokens, boolean showAst, boole } finally { if (showMeasurements) { measurement.stop("Execution time"); + System.out.println("======================"); System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); } } diff --git a/src/com/annimon/ownlang/lib/Functions.java b/src/com/annimon/ownlang/lib/Functions.java index eda7d0c0..211de954 100644 --- a/src/com/annimon/ownlang/lib/Functions.java +++ b/src/com/annimon/ownlang/lib/Functions.java @@ -11,11 +11,14 @@ public final class Functions { private static final Map functions; - static { functions = new HashMap<>(); } + public static Map getFunctions() { + return functions; + } + public static boolean isExists(String key) { return functions.containsKey(key); } diff --git a/src/com/annimon/ownlang/lib/modules/ounit.java b/src/com/annimon/ownlang/lib/modules/ounit.java new file mode 100644 index 00000000..25c8f802 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/ounit.java @@ -0,0 +1,142 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; +import java.text.DecimalFormat; +import java.util.List; +import java.util.stream.Collectors; + +/** + * + * @author aNNiMON + */ +public final class ounit implements Module { + + @Override + public void init() { + Functions.set("assertEquals", new assertEquals()); + Functions.set("assertNotEquals", new assertNotEquals()); + Functions.set("assertSameType", new assertSameType()); + Functions.set("assertTrue", new assertTrue()); + Functions.set("assertFalse", new assertFalse()); + Functions.set("runTests", new runTests()); + } + + private static String microsToSeconds(long micros) { + return new DecimalFormat("#0.0000").format(micros / 1000d / 1000d) + " sec"; + } + + private static class assertEquals implements Function { + @Override + public Value execute(Value... args) { + if (args[0].equals(args[1])) return NumberValue.ONE; + throw new OUnitAssertionException("Values are not equals: " + + "1: " + args[0] + ", 2: " + args[1]); + } + } + + private static class assertNotEquals implements Function { + @Override + public Value execute(Value... args) { + if (!args[0].equals(args[1])) return NumberValue.ONE; + throw new OUnitAssertionException("Values are equals: " + args[0]); + } + } + + private static class assertSameType implements Function { + @Override + public Value execute(Value... args) { + if (args[0].type() == args[1].type()) return NumberValue.ONE; + throw new OUnitAssertionException("Types mismatch. " + + "1: " + Types.typeToString(args[0].type()) + + ", 2: " + Types.typeToString(args[1].type())); + } + } + + private static class assertTrue implements Function { + @Override + public Value execute(Value... args) { + if (args[0].asInt() != 0) return NumberValue.ONE; + throw new OUnitAssertionException("Expected true, but found false."); + } + } + + private static class assertFalse implements Function { + @Override + public Value execute(Value... args) { + if (args[0].asInt() == 0) return NumberValue.ONE; + throw new OUnitAssertionException("Expected false, but found true."); + } + } + + private static class runTests implements Function { + + @Override + public Value execute(Value... args) { + List tests = Functions.getFunctions().entrySet().stream() + .filter(e -> e.getKey().toLowerCase().startsWith("test")) + .map(e -> runTest(e.getKey(), e.getValue())) + .collect(Collectors.toList()); + + int failures = 0; + long summaryTime = 0; + final StringBuilder result = new StringBuilder(); + for (TestInfo test : tests) { + if (!test.isPassed) failures++; + summaryTime += test.elapsedTimeInMicros; + result.append(System.lineSeparator()); + result.append(test.info()); + } + result.append(System.lineSeparator()); + result.append(String.format("Tests run: %d, Failures: %d, Time elapsed: %s", + tests.size(), failures, + microsToSeconds(summaryTime))); + return new StringValue(result.toString()); + } + + private TestInfo runTest(String name, Function f) { + final long startTime = System.nanoTime(); + boolean isSuccessfull; + String failureDescription; + try { + f.execute(); + isSuccessfull = true; + failureDescription = ""; + } catch (OUnitAssertionException oae) { + isSuccessfull = false; + failureDescription = oae.getMessage(); + } + final long elapsedTime = System.nanoTime() - startTime; + return new TestInfo(name, isSuccessfull, failureDescription, elapsedTime / 1000); + } + } + + private static class OUnitAssertionException extends RuntimeException { + + public OUnitAssertionException(String message) { + super(message); + } + } + + private static class TestInfo { + String name; + boolean isPassed; + String failureDescription; + long elapsedTimeInMicros; + + public TestInfo(String name, boolean isPassed, String failureDescription, long elapsedTimeInMicros) { + this.name = name; + this.isPassed = isPassed; + this.failureDescription = failureDescription; + this.elapsedTimeInMicros = elapsedTimeInMicros; + } + + public String info() { + return String.format("%s [%s]\n%sElapsed: %s\n", + name, + isPassed ? "passed" : "FAILED", + isPassed ? "" : (failureDescription + "\n"), + microsToSeconds(elapsedTimeInMicros) + ); + } + } +} diff --git a/tests.own b/tests.own new file mode 100644 index 00000000..c3ea230d --- /dev/null +++ b/tests.own @@ -0,0 +1,39 @@ +use "ounit" + +def testAdditionOnNumbers() { + assertEquals(6, 0 + 1 + 2 + 3) +} + +def testSubtractionOnNumbers() { + assertEquals(-6, 0 - 1 - 2 - 3) +} + +def testPrefixIncrement() { + a = 8 + assertEquals(9, ++a) + assertEquals(9, a) +} + +def testPostfixIncrement() { + a = 8 + assertEquals(8, a++) + assertEquals(9, a) +} + +def testStringReversing() { + assertEquals("tset", -"test") +} + +def testStringMultiplication() { + assertEquals("******", "*" * 6) +} + +def testTypes() { + assertSameType(0, 0.0) +} + +def testFail() { + assertTrue(false) +} + +println runTests() \ No newline at end of file From 24ae7dae1dc606d332bd60b5881494a353661273 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 16:56:05 +0200 Subject: [PATCH 056/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B,=20=D0=BE?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D1=91=D0=BD=20=D0=BB=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nbproject/project.properties | 4 +- src/com/annimon/ownlang/Main.java | 2 +- src/com/annimon/ownlang/parser/Lexer.java | 21 ++- src/com/annimon/ownlang/parser/Parser.java | 9 ++ .../ownlang/parser/ast/IncludeStatement.java | 2 +- .../com/annimon/ownlang/parser/LexerTest.java | 150 ++++++++++++++++++ .../annimon/ownlang/parser/ParserTest.java | 61 +++++++ .../annimon/ownlang/parser/ast/ASTHelper.java | 73 +++++++++ .../parser/ast/OperatorExpressionTest.java | 78 +++++++++ .../parser/ast/ValueExpressionTest.java | 17 ++ .../parser/ast/VariableExpressionTest.java | 33 ++++ 11 files changed, 444 insertions(+), 6 deletions(-) create mode 100644 test/com/annimon/ownlang/parser/LexerTest.java create mode 100644 test/com/annimon/ownlang/parser/ParserTest.java create mode 100644 test/com/annimon/ownlang/parser/ast/ASTHelper.java create mode 100644 test/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java create mode 100644 test/com/annimon/ownlang/parser/ast/ValueExpressionTest.java create mode 100644 test/com/annimon/ownlang/parser/ast/VariableExpressionTest.java diff --git a/nbproject/project.properties b/nbproject/project.properties index 0815ae9f..c72e54c1 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -48,7 +48,9 @@ javac.source=1.8 javac.target=1.8 javac.test.classpath=\ ${javac.classpath}:\ - ${build.classes.dir} + ${build.classes.dir}:\ + ${libs.junit_4.classpath}:\ + ${libs.hamcrest.classpath} javac.test.processorpath=\ ${javac.test.classpath} javadoc.additionalparam= diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 499de79a..ae1fd81a 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -64,7 +64,7 @@ public static void main(String[] args) throws IOException { private static void run(String input, boolean showTokens, boolean showAst, boolean showMeasurements) { final TimeMeasurement measurement = new TimeMeasurement(); measurement.start("Tokenize time"); - final List tokens = new Lexer(input).tokenize(); + final List tokens = Lexer.tokenize(input); measurement.stop("Tokenize time"); if (showTokens) { for (int i = 0; i < tokens.size(); i++) { diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index f37c32c1..ebb94286 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -12,6 +12,10 @@ */ public final class Lexer { + public static List tokenize(String input) { + return new Lexer(input).tokenize(); + } + private static final String OPERATOR_CHARS = "+-*/%()[]{}=<>!&|.,^~?:"; private static final Map OPERATORS; @@ -136,6 +140,12 @@ else if (OPERATOR_CHARS.indexOf(current) != -1) { private void tokenizeNumber() { clearBuffer(); char current = peek(0); + if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { + next(); + next(); + tokenizeHexNumber(); + return; + } while (true) { if (current == '.') { if (buffer.indexOf(".") != -1) throw error("Invalid float number"); @@ -151,11 +161,16 @@ private void tokenizeNumber() { private void tokenizeHexNumber() { clearBuffer(); char current = peek(0); - while (isHexNumber(current)) { - buffer.append(current); + while (isHexNumber(current) || (current == '_')) { + if (current != '_') { + // allow _ symbol + buffer.append(current); + } current = next(); } - addToken(TokenType.HEX_NUMBER, buffer.toString()); + if (buffer.length() > 0) { + addToken(TokenType.HEX_NUMBER, buffer.toString()); + } } private static boolean isHexNumber(char current) { diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 5710c158..5bc70f3c 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -16,6 +16,15 @@ */ public final class Parser { + public static Statement parse(List tokens) { + final Parser parser = new Parser(tokens); + final Statement program = parser.parse(); + if (parser.getParseErrors().hasErrors()) { + throw new ParseException(); + } + return program; + } + private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); private static final Map assignOperator; diff --git a/src/com/annimon/ownlang/parser/ast/IncludeStatement.java b/src/com/annimon/ownlang/parser/ast/IncludeStatement.java index 1570d681..f43ec542 100644 --- a/src/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ b/src/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -23,7 +23,7 @@ public IncludeStatement(Expression expression) { public void execute() { try { final String input = SourceLoader.readSource(expression.eval().asString()); - final List tokens = new Lexer(input).tokenize(); + final List tokens = Lexer.tokenize(input); final Parser parser = new Parser(tokens); final Statement program = parser.parse(); if (!parser.getParseErrors().hasErrors()) { diff --git a/test/com/annimon/ownlang/parser/LexerTest.java b/test/com/annimon/ownlang/parser/LexerTest.java new file mode 100644 index 00000000..6633a23e --- /dev/null +++ b/test/com/annimon/ownlang/parser/LexerTest.java @@ -0,0 +1,150 @@ +package com.annimon.ownlang.parser; + +import com.annimon.ownlang.exceptions.LexerException; +import static com.annimon.ownlang.parser.TokenType.*; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author aNNiMON + */ +public class LexerTest { + + @Test + public void testNumbers() { + String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF #"; + List expList = list(NUMBER, NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("0", result.get(0).getText()); + assertEquals("3.1415", result.get(1).getText()); + assertEquals("CAFEBABE", result.get(2).getText()); + assertEquals("f7d6c5", result.get(3).getText()); + } + + @Test(expected = LexerException.class) + public void testNumbersError() { + String input = "3.14.15 0Xf7_p6_s5"; + Lexer.tokenize(input); + } + + @Test + public void testArithmetic() { + String input = "x = -1 + 2 * 3 % 4 / 5"; + List expList = list(WORD, EQ, MINUS, NUMBER, PLUS, NUMBER, STAR, NUMBER, PERCENT, NUMBER, SLASH, NUMBER); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("x", result.get(0).getText()); + } + + @Test + public void testKeywords() { + String input = "if else while for include"; + List expList = list(IF, ELSE, WHILE, FOR, INCLUDE); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + } + + @Test + public void testWord() { + String input = "if bool include \"text\n\ntext\""; + List expList = list(IF, WORD, INCLUDE, TEXT); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + } + + @Test + public void testString() { + String input = "\"1\\\"2\""; + List expList = list(TEXT); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("1\"2", result.get(0).getText()); + } + + @Test + public void testEmptyString() { + String input = "\"\""; + List expList = list(TEXT); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("", result.get(0).getText()); + } + + @Test(expected = LexerException.class) + public void testStringError() { + String input = "\"1\"\""; + List expList = list(TEXT); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("1", result.get(0).getText()); + } + + @Test + public void testOperators() { + String input = "=+-*/%<>!&|"; + List expList = list(EQ, PLUS, MINUS, STAR, SLASH, PERCENT, LT, GT, EXCL, AMP, BAR); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + } + + @Test + public void testOperators2Char() { + String input = "== != <= >= && || ==+ >=- ->"; + List expList = list(EQEQ, EXCLEQ, LTEQ, GTEQ, AMPAMP, BARBAR, + EQEQ, PLUS, GTEQ, MINUS, MINUS, GT); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + } + + @Test + public void testComments() { + String input = "// 1234 \n /* */ 123 /* \n 12345 \n\n\n */"; + List expList = list(NUMBER); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("123", result.get(0).getText()); + } + + @Test + public void testComments2() { + String input = "// /* 1234 \n */"; + List expList = list(STAR, SLASH); + List result = Lexer.tokenize(input); + assertTokens(expList, result); + } + + @Test(expected = LexerException.class) + public void testCommentsError() { + String input = "/* 1234 \n"; + Lexer.tokenize(input); + } + + private static void assertTokens(List expList, List result) { + final int length = expList.size(); + assertEquals(length, result.size()); + for (int i = 0; i < length; i++) { + assertEquals(expList.get(i).getType(), result.get(i).getType()); + } + } + + private static List list(TokenType... types) { + final List list = new ArrayList(); + for (TokenType t : types) { + list.add(token(t)); + } + return list; + } + + private static Token token(TokenType type) { + return token(type, "", 0, 0); + } + + private static Token token(TokenType type, String text, int row, int col) { + return new Token(type, text, row, col); + } + +} diff --git a/test/com/annimon/ownlang/parser/ParserTest.java b/test/com/annimon/ownlang/parser/ParserTest.java new file mode 100644 index 00000000..dfe031ec --- /dev/null +++ b/test/com/annimon/ownlang/parser/ParserTest.java @@ -0,0 +1,61 @@ +package com.annimon.ownlang.parser; + +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.parser.ast.*; +import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * @author aNNiMON + */ +public class ParserTest { + + @Test + public void testParsePrimary() { + assertEval(number(2), "2", value(2)); + assertEval(string("test"), "\"test\"", value("test")); + } + + @Test + public void testParseAdditive() { + assertEval( number(5), "2 + 3", operator(BinaryExpression.Operator.ADD, value(2), value(3)) ); + assertEval( number(-1), "2 - 3", operator(BinaryExpression.Operator.SUBTRACT, value(2), value(3)) ); + } + + @Test + public void testParseMultiplicative() { + assertEval( number(6), "2 * 3", operator(BinaryExpression.Operator.MULTIPLY, value(2), value(3)) ); + assertEval( number(4), "12 / 3", operator(BinaryExpression.Operator.DIVIDE, value(12), value(3)) ); + assertEval( number(2), "12 % 5", operator(BinaryExpression.Operator.REMAINDER, value(12), value(5)) ); + } + + private static void assertEval(Value expectedValue, String input, Expression expected) { + BlockStatement program = assertExpression(input, expected); + program.execute(); + final Value actual = Variables.get("a"); + assertEquals(expectedValue.asNumber(), actual.asNumber(), 0.001); + assertEquals(expectedValue.asString(), actual.asString()); + } + + private static BlockStatement assertExpression(String input, Expression expected) { + return assertProgram("a = " + input, block(assign("a", expected))); + } + + private static BlockStatement assertProgram(String input, BlockStatement actual) { + BlockStatement result = (BlockStatement) parse(input); + assertStatements(result, actual); + return result; + } + + private static void assertStatements(BlockStatement expected, BlockStatement actual) { + for (int i = 0; i < expected.statements.size(); i++) { + assertEquals(expected.statements.get(i).getClass(), actual.statements.get(i).getClass()); + } + } + + private static Statement parse(String input) { + return Parser.parse(Lexer.tokenize(input)); + } +} diff --git a/test/com/annimon/ownlang/parser/ast/ASTHelper.java b/test/com/annimon/ownlang/parser/ast/ASTHelper.java new file mode 100644 index 00000000..f6ea28b5 --- /dev/null +++ b/test/com/annimon/ownlang/parser/ast/ASTHelper.java @@ -0,0 +1,73 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import static org.junit.Assert.*; + +/** + * Helper for build and test AST nodes. + * @author aNNiMON + */ +public final class ASTHelper { + + public static void assertValue(NumberValue expected, Value actual) { + assertEquals(Types.NUMBER, actual.type()); + if (expected.raw() instanceof Double) { + assertEquals(expected.asNumber(), actual.asNumber(), 0.001); + } + assertEquals(expected.asInt(), actual.asInt()); + } + + public static void assertValue(StringValue expected, Value actual) { + assertEquals(Types.STRING, actual.type()); + assertEquals(expected.asString(), actual.asString()); + } + + + public static BlockStatement block(Statement... statements) { + final BlockStatement result = new BlockStatement(); + for (Statement statement : statements) { + result.add(statement); + } + return result; + } + + public static AssignmentExpression assign(String variable, Expression expr) { + return assign(var(variable), expr); + } + + public static AssignmentExpression assign(Accessible accessible, Expression expr) { + return assign(null, accessible, expr); + } + + public static AssignmentExpression assign(BinaryExpression.Operator op, Accessible accessible, Expression expr) { + return new AssignmentExpression(op, accessible, expr); + } + + public static BinaryExpression operator(BinaryExpression.Operator op, Expression left, Expression right) { + return new BinaryExpression(op, left, right); + } + + public static ValueExpression value(Number value) { + return new ValueExpression(value); + } + + public static ValueExpression value(String value) { + return new ValueExpression(value); + } + + public static VariableExpression var(String value) { + return new VariableExpression(value); + } + + + public static NumberValue number(Number value) { + return new NumberValue(value); + } + + public static StringValue string(String value) { + return new StringValue(value); + } +} diff --git a/test/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java b/test/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java new file mode 100644 index 00000000..239756c2 --- /dev/null +++ b/test/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java @@ -0,0 +1,78 @@ +package com.annimon.ownlang.parser.ast; + +import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import static com.annimon.ownlang.parser.ast.BinaryExpression.Operator.*; +import org.junit.Test; + +/** + * @author aNNiMON + */ +public class OperatorExpressionTest { + + @Test + public void testAddition() { + assertValue(number(4), operator(ADD, value(2), value(2)).eval()); + assertValue(number(6), operator(ADD, value(1), operator(ADD, value(2), value(3))).eval()); + assertValue(string("ABCD"), operator(ADD, value("AB"), value("CD")).eval()); + assertValue(string("AB12"), operator(ADD, value("AB"), operator(ADD, value(10), value(2))).eval()); + } + + @Test + public void testSubtraction() { + assertValue(number(0), operator(SUBTRACT, value(2), value(2)).eval()); + assertValue(number(110), operator(SUBTRACT, value(100), operator(SUBTRACT, value(20), value(30))).eval()); + } + + @Test + public void testMultiplication() { + assertValue(number(4), operator(MULTIPLY, value(2), value(2)).eval()); + assertValue(number(30), operator(MULTIPLY, value(5), operator(MULTIPLY, value(-2), value(-3))).eval()); + assertValue(string("ABABAB"), operator(MULTIPLY, value("AB"), value(3)).eval()); + } + + @Test + public void testDivision() { + assertValue(number(3), operator(DIVIDE, value(6), value(2)).eval()); + assertValue(number(30), operator(DIVIDE, value(-900), operator(DIVIDE, value(60), value(-2))).eval()); + } + + @Test() + public void testDivisionZero() { + assertValue(number(Double.POSITIVE_INFINITY), operator(DIVIDE, value(2.0), value(0.0)).eval()); + } + + @Test(expected = RuntimeException.class) + public void testDivisionZeroOnIntegers() { + operator(DIVIDE, value(2), value(0)).eval(); + } + + @Test + public void testRemainder() { + assertValue(number(2), operator(REMAINDER, value(10), value(4)).eval()); + assertValue(number(5), operator(REMAINDER, value(15), operator(REMAINDER, value(40), value(30))).eval()); + } + + @Test() + public void testRemainderZero() { + assertValue(number(Double.NaN), operator(REMAINDER, value(2.0), value(0.0)).eval()); + } + + @Test(expected = RuntimeException.class) + public void testRemainderZeroOnIntegers() { + operator(REMAINDER, value(2), value(0)).eval(); + } + + @Test + public void testAND() { + assertValue(number(0x04), operator(AND, value(0x04), value(0x0F)).eval()); + assertValue(number(0x00), operator(AND, value(0x04), value(0x08)).eval()); + assertValue(number(8), operator(AND, value(12), value(9)).eval()); + } + + @Test + public void testOR() { + assertValue(number(12), operator(OR, value(4), value(8)).eval()); + assertValue(number(0x0F), operator(OR, value(3), value(12)).eval()); + assertValue(number(0x0E), operator(OR, value(10), value(4)).eval()); + } +} diff --git a/test/com/annimon/ownlang/parser/ast/ValueExpressionTest.java b/test/com/annimon/ownlang/parser/ast/ValueExpressionTest.java new file mode 100644 index 00000000..50d41e89 --- /dev/null +++ b/test/com/annimon/ownlang/parser/ast/ValueExpressionTest.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.parser.ast; + +import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import org.junit.Test; + +/** + * + * @author aNNiMON + */ +public class ValueExpressionTest { + + @Test + public void testValue() { + assertValue(number(4), value(4).eval()); + assertValue(string("ABCD"), value("ABCD").eval()); + } +} diff --git a/test/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/test/com/annimon/ownlang/parser/ast/VariableExpressionTest.java new file mode 100644 index 00000000..14783684 --- /dev/null +++ b/test/com/annimon/ownlang/parser/ast/VariableExpressionTest.java @@ -0,0 +1,33 @@ +package com.annimon.ownlang.parser.ast; + +import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import org.junit.Test; + +/** + * + * @author aNNiMON + */ +public class VariableExpressionTest { + + @Test + public void testVariable() { + assign("a", value(4)).execute(); + assign("b", value("ABCD")).execute(); + + assertValue(number(4), var("a").eval()); + assertValue(string("ABCD"), var("b").eval()); + } + + @Test + public void testVariableReplace() { + assign("a", value(4)).execute(); + assign("a", value(8)).execute(); + + assertValue(number(8), var("a").eval()); + } + + @Test(expected = RuntimeException.class) + public void testUnknownVariable() { + var("a").eval(); + } +} From 9fb34ae09b65da63e02c1bfaa9ea68c22eb28723 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 17:26:35 +0200 Subject: [PATCH 057/448] =?UTF-8?q?=D0=90=D0=B1=D1=81=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B8=D0=BC=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20=D1=81=20Android-=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/Console.java | 56 +++++++++++++++++++ src/com/annimon/ownlang/Main.java | 11 +--- src/com/annimon/ownlang/lib/CallStack.java | 4 ++ src/com/annimon/ownlang/lib/Variables.java | 6 ++ .../lib/modules/functions/std_echo.java | 7 ++- .../lib/modules/functions/std_thread.java | 4 +- src/com/annimon/ownlang/parser/Parser.java | 6 ++ .../ownlang/parser/ast/PrintStatement.java | 4 +- .../ownlang/parser/ast/PrintlnStatement.java | 4 +- .../parser/visitors/VariablePrinter.java | 7 ++- 10 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 src/com/annimon/ownlang/Console.java diff --git a/src/com/annimon/ownlang/Console.java b/src/com/annimon/ownlang/Console.java new file mode 100644 index 00000000..06217d62 --- /dev/null +++ b/src/com/annimon/ownlang/Console.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang; + +import com.annimon.ownlang.lib.CallStack; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; + +public class Console { + + public static void print(String value) { + System.out.print(value); + } + + public static void print(Object value) { + print(value.toString()); + } + + public static void println() { + System.out.println(); + } + + public static void println(String value) { + System.out.println(value); + } + + public static void println(Object value) { + println(value.toString()); + } + + public static void error(Throwable throwable) { + error(throwable.toString()); + } + + public static void error(CharSequence value) { + System.err.println(value); + } + + public static void handleException(Thread thread, Throwable throwable) { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try(final PrintStream ps = new PrintStream(baos)) { + ps.printf("%s in %s\n", throwable.getMessage(), thread.getName()); + for (CallStack.CallInfo call : CallStack.getCalls()) { + ps.printf("\tat %s\n", call); + } + ps.println(); + throwable.printStackTrace(ps); + ps.flush(); + } + try { + error(baos.toString("UTF-8")); + } catch (UnsupportedEncodingException ex) { + error(baos.toString()); + } + } +} diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index ae1fd81a..b24e929b 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -1,6 +1,5 @@ package com.annimon.ownlang; -import com.annimon.ownlang.lib.CallStack; import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.SourceLoader; @@ -90,7 +89,7 @@ private static void run(String input, boolean showTokens, boolean showAst, boole measurement.start("Execution time"); program.execute(); } catch (Exception ex) { - handleException(Thread.currentThread(), ex); + Console.handleException(Thread.currentThread(), ex); } finally { if (showMeasurements) { measurement.stop("Execution time"); @@ -99,12 +98,4 @@ private static void run(String input, boolean showTokens, boolean showAst, boole } } } - - public static void handleException(Thread thread, Throwable throwable) { - System.err.printf("%s in %s\n", throwable.getMessage(), thread.getName()); - for (CallStack.CallInfo call : CallStack.getCalls()) { - System.err.printf("\tat %s\n", call); - } - throwable.printStackTrace(); - } } diff --git a/src/com/annimon/ownlang/lib/CallStack.java b/src/com/annimon/ownlang/lib/CallStack.java index cad247c0..e5b753b2 100644 --- a/src/com/annimon/ownlang/lib/CallStack.java +++ b/src/com/annimon/ownlang/lib/CallStack.java @@ -7,6 +7,10 @@ public final class CallStack { private static final Deque calls = new ConcurrentLinkedDeque();; + public static synchronized void clear() { + calls.clear(); + } + public static synchronized void enter(String name, Function function) { calls.push(new CallInfo(name, function)); } diff --git a/src/com/annimon/ownlang/lib/Variables.java b/src/com/annimon/ownlang/lib/Variables.java index e11d3ffd..6671cec9 100644 --- a/src/com/annimon/ownlang/lib/Variables.java +++ b/src/com/annimon/ownlang/lib/Variables.java @@ -16,6 +16,12 @@ public final class Variables { static { stack = new Stack<>(); variables = new ConcurrentHashMap<>(); + Variables.clear(); + } + + public static void clear() { + stack.clear(); + variables.clear(); variables.put("true", NumberValue.ONE); variables.put("false", NumberValue.ZERO); } diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_echo.java b/src/com/annimon/ownlang/lib/modules/functions/std_echo.java index 4aa15ba4..6ec9eb50 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_echo.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_echo.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules.functions; +import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; @@ -9,10 +10,10 @@ public final class std_echo implements Function { @Override public Value execute(Value... args) { for (Value arg : args) { - System.out.print(arg.asString()); - System.out.print(" "); + Console.print(arg.asString()); + Console.print(" "); } - System.out.println(); + Console.println(); return NumberValue.ZERO; } } \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java index 75a50fef..e789a67e 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_thread.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.Main; +import com.annimon.ownlang.Console; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; @@ -26,7 +26,7 @@ public Value execute(Value... args) { } final Thread thread = new Thread(() -> body.execute(params)); - thread.setUncaughtExceptionHandler(Main::handleException); + thread.setUncaughtExceptionHandler(Console::handleException); thread.start(); return NumberValue.ZERO; } diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 5bc70f3c..f0d0627a 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -48,6 +48,7 @@ public static Statement parse(List tokens) { private final List tokens; private final int size; private final ParseErrors parseErrors; + private Statement parsedStatement; private int pos; @@ -57,6 +58,10 @@ public Parser(List tokens) { parseErrors = new ParseErrors(); } + public Statement getParsedStatement() { + return parsedStatement; + } + public ParseErrors getParseErrors() { return parseErrors; } @@ -72,6 +77,7 @@ public Statement parse() { recover(); } } + parsedStatement = result; return result; } diff --git a/src/com/annimon/ownlang/parser/ast/PrintStatement.java b/src/com/annimon/ownlang/parser/ast/PrintStatement.java index e0088796..8b175d6b 100644 --- a/src/com/annimon/ownlang/parser/ast/PrintStatement.java +++ b/src/com/annimon/ownlang/parser/ast/PrintStatement.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.Console; + /** * * @author aNNiMON @@ -14,7 +16,7 @@ public PrintStatement(Expression expression) { @Override public void execute() { - System.out.print(expression.eval()); + Console.print(expression.eval()); } @Override diff --git a/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java b/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java index ced55fd3..6f7823e8 100644 --- a/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java +++ b/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.Console; + /** * * @author aNNiMON @@ -14,7 +16,7 @@ public PrintlnStatement(Expression expression) { @Override public void execute() { - System.out.println(expression.eval()); + Console.println(expression.eval()); } @Override diff --git a/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java b/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java index 82a60521..4f29f3a1 100644 --- a/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java +++ b/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.visitors; +import com.annimon.ownlang.Console; import com.annimon.ownlang.parser.ast.*; /** @@ -11,18 +12,18 @@ public final class VariablePrinter extends AbstractVisitor { @Override public void visit(AssignmentExpression s) { super.visit(s); - System.out.println(s.target); + Console.println(s.target); } @Override public void visit(ContainerAccessExpression s) { super.visit(s); - System.out.println(s.variable); + Console.println(s.variable); } @Override public void visit(VariableExpression s) { super.visit(s); - System.out.println(s.name); + Console.println(s.name); } } From 2d7feeb0cd85580d9067707a5f5f8add0394d58a Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Feb 2016 21:50:52 +0200 Subject: [PATCH 058/448] =?UTF-8?q?=D0=9E=D0=B1=D1=84=D1=83=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D1=80=D0=B8=20=D1=81=D0=B1?= =?UTF-8?q?=D0=BE=D1=80=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.xml | 24 ++++++++++++++++++++++- proguard.properties | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 proguard.properties diff --git a/build.xml b/build.xml index 51cd47d6..d1ca302a 100644 --- a/build.xml +++ b/build.xml @@ -13,13 +13,16 @@ + + - + + @@ -37,7 +40,26 @@ + + + + + + + + + + + + + + + + + + - - + + @@ -76,7 +76,7 @@ is divided into following sections: - + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index 1cc3671e..9e97b715 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -4,5 +4,5 @@ build.xml.stylesheet.CRC32=8064a381@1.78.0.48 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. nbproject/build-impl.xml.data.CRC32=48c1b1a1 -nbproject/build-impl.xml.script.CRC32=b8cd7788 -nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.0.48 +nbproject/build-impl.xml.script.CRC32=7fd56607 +nbproject/build-impl.xml.stylesheet.CRC32=2b19b096@1.80.0.48 diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 26456411..deccadd6 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -22,7 +22,7 @@ */ public final class Main { - private static final String VERSION = "1.1.0"; + private static final String VERSION = "1.2.0"; private static String[] ownlangArgs = new String[0]; @@ -34,11 +34,11 @@ public static void main(String[] args) throws IOException { if (args.length == 0) { try { final Options options = new Options(); - options.showAst = true; - options.showTokens = true; - options.showMeasurements = true; + options.showAst = false; + options.showTokens = false; + options.showMeasurements = false; options.lintMode = false; - options.optimizationLevel = 2; + options.optimizationLevel = 0; run(SourceLoader.readSource("program.own"), options); } catch (IOException ioe) { System.out.println("OwnLang version " + VERSION + "\n\n" + @@ -243,7 +243,7 @@ public Options() { showAst = false; showMeasurements = false; lintMode = false; - optimizationLevel = 1; + optimizationLevel = 0; } public void validate() { From ee57957140b979f90876a2b614d138bea42c7440 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 30 Jun 2016 14:26:53 +0300 Subject: [PATCH 120/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ae6b215d..29dde9a6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,121 @@ # OwnLang -OwnLang - dynamic functional programming language. Available for PC, Android and Java ME devices. +OwnLang - dynamic functional programming language inspired by Scala and Python. Available for PC, Android and Java ME devices. + +## Key features + +#### Function values + +Functions are values, so we can store them to variables for operating. + +```scala +operations = { + "+" : def(a,b) = a+b, + "-" : def(a,b) = a-b, + "*" : def(a,b) = a*b, + "/" : ::division +} +def division(v1, v2) { + if (v2 == 0) return "error" + return v1 / v2 +} + +for operation : operations { + println operation(2, 3) +} +``` + +#### Pattern Matching + +Pattern matching with value pattern, tuple pattern, list pattern and optional condition. + +```scala +def factorial(n) = match n { + case 0: 1 + case n if n < 0: 0 + case _: n * factorial(n - 1) +} + +def fizzbuzz(limit = 100) { + for i = 1, i <= limit, i++ { + println match [i % 3 == 0, i % 5 == 0] { + case (true, false): "Fizz" + case (false, true): "Buzz" + case (true, true): "FizzBuzz" + case _: i + } + } +} +``` + +#### Functional data operations + +Operate data in functional style. + +```scala +use "std" +use "functional" + +nums = [1,2,3,4,5,6,7,8,9,10] +nums = filter(nums, def(x) = x % 2 == 0) +// Squares of even numbers +squares = map(nums, def(x) = x * x) +foreach(squares, ::echo) +// Sum of squares +sum = reduce(squares, 0, def(x, y) = x + y) +println "Sum: " + sum +``` + +#### Operator overloading + +Why not? + +```scala +use "std" +use "types" +use "math" + +def `..`(a, b) = range(a, b - 1) +def `**`(a, b) = int(pow(a, b)) +for y : 1 .. 10 { + println sprintf("2 ^ %d = %d", y, 2 ** y) +} +``` + +#### Network module + +Easy async HTTP requests with `http` module. + +```scala +use "std" +use "http" +use "functional" + +// GET request +http("https://api.github.com/events", def(r) { + use "json" + events = jsondecode(r) +}) + +// POST request +http("http://jsonplaceholder.typicode.com/users", "POST", { + "name": "OwnLang", + "versionCode": 10 +}, ::echo) + +// PATCH request +http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Patched Name"}, ::patch_callback) + +def patch_callback(v) { + println v +} +``` + +## Language specification + +[Available on GitBook (English and Russian)](https://www.gitbook.com/book/annimon/ownlang/details) + +[Examples](examples/) ## Build @@ -23,13 +138,6 @@ or `java -jar OwnLang.jar < input.own` -## Language specification - -[Wiki (Russian)](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/wiki) - -[Examples](examples/) - - ## License MIT - see [MIT licence information](LICENSE) \ No newline at end of file From 4df44cadea9162a079084a00efb50068b5dc489a Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 1 Jul 2016 18:56:16 +0300 Subject: [PATCH 121/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20JMH-=D0=B1=D0=B5=D0=BD=D1=87=D0=BC=D0=B0?= =?UTF-8?q?=D1=80=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nbproject/project.properties | 3 +- .../ownlang/parser/LexerBenchmarkTest.java | 48 ++++++++++++++++++ .../ownlang/parser/ParserBenchmarkTest.java | 50 +++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 test/com/annimon/ownlang/parser/LexerBenchmarkTest.java create mode 100644 test/com/annimon/ownlang/parser/ParserBenchmarkTest.java diff --git a/nbproject/project.properties b/nbproject/project.properties index 75dc9616..1fdb04e5 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -50,7 +50,8 @@ javac.test.classpath=\ ${javac.classpath}:\ ${build.classes.dir}:\ ${libs.junit_4.classpath}:\ - ${libs.hamcrest.classpath} + ${libs.hamcrest.classpath}:\ + ${libs.JMH_1.12.classpath} javac.test.processorpath=\ ${javac.test.classpath} javadoc.additionalparam= diff --git a/test/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/test/com/annimon/ownlang/parser/LexerBenchmarkTest.java new file mode 100644 index 00000000..934b0fab --- /dev/null +++ b/test/com/annimon/ownlang/parser/LexerBenchmarkTest.java @@ -0,0 +1,48 @@ +package com.annimon.ownlang.parser; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import org.junit.Ignore; +import org.junit.Test; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Ignore +public class LexerBenchmarkTest { + + @Param({"1000"}) + private int iterations; + + private String input; + + @Setup(Level.Trial) + public void initializeTrial() throws IOException { + input = SourceLoader.readSource("program.own"); + } + + @Benchmark + public void lexerBenchmark() { + for (int i = 0; i < iterations; i++) { + Lexer.tokenize(input); + } + } + + @Test + public void executeBenchmark() throws RunnerException { + Options opt = new OptionsBuilder() + .include(LexerBenchmarkTest.class.getSimpleName()) + .warmupIterations(3) + .measurementIterations(5) + .threads(1) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} diff --git a/test/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/test/com/annimon/ownlang/parser/ParserBenchmarkTest.java new file mode 100644 index 00000000..f3595649 --- /dev/null +++ b/test/com/annimon/ownlang/parser/ParserBenchmarkTest.java @@ -0,0 +1,50 @@ +package com.annimon.ownlang.parser; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.Ignore; +import org.junit.Test; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Ignore +public class ParserBenchmarkTest { + + @Param({"1000"}) + private int iterations; + + private List input; + + @Setup(Level.Trial) + public void initializeTrial() throws IOException { + input = Lexer.tokenize(SourceLoader.readSource("program.own")); + } + + @Benchmark + public void parserBenchmark(Blackhole bh) { + for (int i = 0; i < iterations; i++) { + bh.consume(Parser.parse(input)); + } + } + + @Test + public void executeBenchmark() throws RunnerException { + Options opt = new OptionsBuilder() + .include(ParserBenchmarkTest.class.getSimpleName()) + .warmupIterations(3) + .measurementIterations(5) + .threads(1) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} From 03b56ce0191e5cd417bb68b88ebb87ce91d3d580 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 1 Jul 2016 18:57:12 +0300 Subject: [PATCH 122/448] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B2=D1=8B=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D0=BA=D0=BE=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/parser/Lexer.java | 8 ++++---- src/com/annimon/ownlang/parser/Parser.java | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 6c2e83e6..eea695ae 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -203,7 +203,7 @@ private void tokenizeOperator() { clearBuffer(); while (true) { final String text = buffer.toString(); - if (!OPERATORS.containsKey(text + current) && !text.isEmpty()) { + if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) { addToken(OPERATORS.get(text)); return; } @@ -237,9 +237,9 @@ private void tokenizeExtendedWord() { clearBuffer(); char current = peek(0); while (true) { + if (current == '`') break; if (current == '\0') throw error("Reached end of file while parsing extended word."); if (current == '\n' || current == '\r') throw error("Reached end of line while parsing extended word."); - if (current == '`') break; buffer.append(current); current = next(); } @@ -252,7 +252,6 @@ private void tokenizeText() { clearBuffer(); char current = peek(0); while (true) { - if (current == '\0') throw error("Reached end of file while parsing text string."); if (current == '\\') { current = next(); switch (current) { @@ -288,6 +287,7 @@ private void tokenizeText() { continue; } if (current == '"') break; + if (current == '\0') throw error("Reached end of file while parsing text string."); buffer.append(current); current = next(); } @@ -306,8 +306,8 @@ private void tokenizeComment() { private void tokenizeMultilineComment() { char current = peek(0); while (true) { - if (current == '\0') throw error("Reached end of file while parsing multiline comment"); if (current == '*' && peek(1) == '/') break; + if (current == '\0') throw error("Reached end of file while parsing multiline comment"); current = next(); } next(); // * diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index e80adde3..a89c9215 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -6,6 +6,7 @@ import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,10 +27,10 @@ public static Statement parse(List tokens) { } private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); - - private static final Map assignOperator; + + private static final EnumMap assignOperator; static { - assignOperator = new HashMap<>(BinaryExpression.Operator.values().length + 1); + assignOperator = new EnumMap(TokenType.class); assignOperator.put(TokenType.EQ, null); assignOperator.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD); assignOperator.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT); From b0f19e208ad4d6eecb90c2e38547413ea437dcfc Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 1 Jul 2016 19:05:33 +0300 Subject: [PATCH 123/448] =?UTF-8?q?=D0=97=D0=B0=D0=B2=D0=B8=D1=81=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20=D0=B4=D0=BB=D1=8F=20gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index d8e6d408..0758e119 100644 --- a/build.gradle +++ b/build.gradle @@ -37,4 +37,6 @@ repositories { dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.12' + testCompile group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.12' } From 13d320a3010bec1630c8be197d954f3422da9b4d Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 4 Jul 2016 12:10:02 +0300 Subject: [PATCH 124/448] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=BA=D0=B8=20=D0=B2=20std::range?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/modules/functions/std_range.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_range.java b/src/com/annimon/ownlang/lib/modules/functions/std_range.java index 4b95ed85..e5198dbc 100644 --- a/src/com/annimon/ownlang/lib/modules/functions/std_range.java +++ b/src/com/annimon/ownlang/lib/modules/functions/std_range.java @@ -82,9 +82,9 @@ public Value[] getCopyElements() { private boolean isIntegerRange() { if (to > 0) { - return (to < Integer.MAX_VALUE) && (from > Integer.MIN_VALUE && to < Integer.MAX_VALUE); + return (from > Integer.MIN_VALUE && to < Integer.MAX_VALUE); } - return (to > Integer.MIN_VALUE) && (to > Integer.MIN_VALUE && from < Integer.MAX_VALUE); + return (to > Integer.MIN_VALUE && from < Integer.MAX_VALUE); } @Override From a6efd831e0d56fc3cb79cc2c940e632dfda6402a Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 4 Jul 2016 22:01:06 +0300 Subject: [PATCH 125/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20assertFalse=20=D0=B2=20=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/com/annimon/ownlang/parser/ProgramsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/com/annimon/ownlang/parser/ProgramsTest.java b/test/com/annimon/ownlang/parser/ProgramsTest.java index 7ddda3c4..546e37d5 100644 --- a/test/com/annimon/ownlang/parser/ProgramsTest.java +++ b/test/com/annimon/ownlang/parser/ProgramsTest.java @@ -73,7 +73,7 @@ public void initialize() { return NumberValue.ONE; }); Functions.set("assertFalse", (args) -> { - assertFalse(args[0].asInt() == 0); + assertFalse(args[0].asInt() != 0); return NumberValue.ONE; }); } From 3b90e544a78b1546c0808b5c26a43d01d30b9bca Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 4 Jul 2016 22:01:42 +0300 Subject: [PATCH 126/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/FunctionValue.java | 2 +- src/com/annimon/ownlang/lib/modules/java.java | 459 ++++++++++++++++++ test/interop/Data.java | 39 ++ test/resources/modules/java/classes.own | 49 ++ 4 files changed, 548 insertions(+), 1 deletion(-) create mode 100644 src/com/annimon/ownlang/lib/modules/java.java create mode 100644 test/interop/Data.java create mode 100644 test/resources/modules/java/classes.own diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/com/annimon/ownlang/lib/FunctionValue.java index 97ad62ae..16108c6f 100644 --- a/src/com/annimon/ownlang/lib/FunctionValue.java +++ b/src/com/annimon/ownlang/lib/FunctionValue.java @@ -7,7 +7,7 @@ * * @author aNNiMON */ -public final class FunctionValue implements Value { +public class FunctionValue implements Value { public static final FunctionValue EMPTY = new FunctionValue(args -> NumberValue.ZERO); diff --git a/src/com/annimon/ownlang/lib/modules/java.java b/src/com/annimon/ownlang/lib/modules/java.java new file mode 100644 index 00000000..feb41478 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/java.java @@ -0,0 +1,459 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.lib.*; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Java interoperability module. + * + * @author aNNiMON + */ +public final class java implements Module { + + private static final Value NULL = new NullValue(); + + @Override + public void init() { + Variables.define("null", NULL); + Variables.define("boolean.class", new ClassValue(boolean.class)); + Variables.define("boolean[].class", new ClassValue(boolean[].class)); + Variables.define("boolean[][].class", new ClassValue(boolean[][].class)); + Variables.define("byte.class", new ClassValue(byte.class)); + Variables.define("byte[].class", new ClassValue(byte[].class)); + Variables.define("byte[][].class", new ClassValue(byte[][].class)); + Variables.define("short.class", new ClassValue(short.class)); + Variables.define("short[].class", new ClassValue(short[].class)); + Variables.define("short[][].class", new ClassValue(short[][].class)); + Variables.define("char.class", new ClassValue(char.class)); + Variables.define("char[].class", new ClassValue(char[].class)); + Variables.define("char[][].class", new ClassValue(char[][].class)); + Variables.define("int.class", new ClassValue(int.class)); + Variables.define("int[].class", new ClassValue(int[].class)); + Variables.define("int[][].class", new ClassValue(int[][].class)); + Variables.define("long.class", new ClassValue(long.class)); + Variables.define("long[].class", new ClassValue(long[].class)); + Variables.define("long[][].class", new ClassValue(long[][].class)); + Variables.define("float.class", new ClassValue(float.class)); + Variables.define("float[].class", new ClassValue(float[].class)); + Variables.define("float[][].class", new ClassValue(float[][].class)); + Variables.define("double.class", new ClassValue(double.class)); + Variables.define("double[].class", new ClassValue(double[].class)); + Variables.define("double[][].class", new ClassValue(double[][].class)); + Variables.define("String.class", new ClassValue(String.class)); + Variables.define("String[].class", new ClassValue(String[].class)); + Variables.define("String[][].class", new ClassValue(String[][].class)); + Variables.define("Object.class", new ClassValue(Object.class)); + Variables.define("Object[].class", new ClassValue(Object[].class)); + Variables.define("Object[][].class", new ClassValue(Object[][].class)); + + Functions.set("isNull", this::isNull); + Functions.set("newClass", this::newClass); + Functions.set("toObject", this::toObject); + Functions.set("toValue", this::toValue); + } + + // + private static class NullValue implements Value { + + @Override + public Object raw() { + return null; + } + + @Override + public int asInt() { + return 0; + } + + @Override + public double asNumber() { + return 0; + } + + @Override + public String asString() { + return "null"; + } + + @Override + public int type() { + return 482862660; + } + + @Override + public int compareTo(Value o) { + if (o.raw() == null) return 0; + return -1; + } + + @Override + public String toString() { + return asString(); + } + } + + private static class ClassValue extends MapValue { + + public static Value classOrNull(Class clazz) { + if (clazz == null) return NULL; + return new ClassValue(clazz); + } + + private final Class clazz; + + public ClassValue(Class clazz) { + super(25); + this.clazz = clazz; + init(clazz); + } + + private void init(Class clazz) { + set(new StringValue("isAnnotation"), NumberValue.fromBoolean(clazz.isAnnotation())); + set(new StringValue("isAnonymousClass"), NumberValue.fromBoolean(clazz.isAnonymousClass())); + set(new StringValue("isArray"), NumberValue.fromBoolean(clazz.isArray())); + set(new StringValue("isEnum"), NumberValue.fromBoolean(clazz.isEnum())); + set(new StringValue("isInterface"), NumberValue.fromBoolean(clazz.isInterface())); + set(new StringValue("isLocalClass"), NumberValue.fromBoolean(clazz.isLocalClass())); + set(new StringValue("isMemberClass"), NumberValue.fromBoolean(clazz.isMemberClass())); + set(new StringValue("isPrimitive"), NumberValue.fromBoolean(clazz.isPrimitive())); + set(new StringValue("isSynthetic"), NumberValue.fromBoolean(clazz.isSynthetic())); + + set(new StringValue("modifiers"), NumberValue.of(clazz.getModifiers())); + + set(new StringValue("canonicalName"), new StringValue(clazz.getCanonicalName())); + set(new StringValue("name"), new StringValue(clazz.getName())); + set(new StringValue("simpleName"), new StringValue(clazz.getSimpleName())); + set(new StringValue("typeName"), new StringValue(clazz.getTypeName())); + set(new StringValue("genericString"), new StringValue(clazz.toGenericString())); + + set(new StringValue("getComponentType"), new FunctionValue(v -> classOrNull(clazz.getComponentType()) )); + set(new StringValue("getDeclaringClass"), new FunctionValue(v -> classOrNull(clazz.getDeclaringClass()) )); + set(new StringValue("getEnclosingClass"), new FunctionValue(v -> classOrNull(clazz.getEnclosingClass()) )); + set(new StringValue("getSuperclass"), new FunctionValue(v -> new ClassValue(clazz.getSuperclass()) )); + + set(new StringValue("getClasses"), new FunctionValue(v -> array(clazz.getClasses()) )); + set(new StringValue("getDeclaredClasses"), new FunctionValue(v -> array(clazz.getDeclaredClasses()) )); + set(new StringValue("getInterfaces"), new FunctionValue(v -> array(clazz.getInterfaces()) )); + + set(new StringValue("asSubclass"), new FunctionValue(this::asSubclass)); + set(new StringValue("isAssignableFrom"), new FunctionValue(this::isAssignableFrom)); + set(new StringValue("new"), new FunctionValue(this::newInstance)); + set(new StringValue("cast"), new FunctionValue(this::cast)); + } + + private Value asSubclass(Value... args) { + Arguments.check(1, args.length); + return new ClassValue(clazz.asSubclass( ((ClassValue)args[0]).clazz )); + } + + private Value isAssignableFrom(Value... args) { + Arguments.check(1, args.length); + return NumberValue.fromBoolean(clazz.isAssignableFrom( ((ClassValue)args[0]).clazz )); + } + + private Value newInstance(Value... args) { + try { + return new ObjectValue(clazz.newInstance()); + } catch (InstantiationException | IllegalAccessException ex) { + return NULL; + } + } + + private Value cast(Value... args) { + Arguments.check(1, args.length); + return objectToValue(clazz, clazz.cast(((ObjectValue)args[0]).object)); + } + + @Override + public String toString() { + return "ClassValue " + clazz.toString(); + } + } + + private static class ObjectValue extends MapValue { + + public static Value objectOrNull(Object object) { + if (object == null) return NULL; + return new ObjectValue(object); + } + + private final Object object; + + public ObjectValue(Object object) { + super(2); + this.object = object; + } + + @Override + public boolean containsKey(Value key) { + return getValue(key.asString()) != null; + } + + @Override + public Value get(Value key) { + return getValue(key.asString()); + } + + private Value getValue(String key) { + // Trying to get field + try { + final Field field = object.getClass().getField(key); + return objectToValue(field.getType(), field.get(object)); + } catch (NoSuchFieldException | SecurityException | + IllegalArgumentException | IllegalAccessException ex) { + // ignore and go to the next step + } + + // Trying to invoke method + try { + final Method[] allMethods = object.getClass().getMethods(); + final List methods = new ArrayList<>(); + for (Method method : allMethods) { + if (method.getName().equals(key)) { + methods.add(method); + } + } + if (methods.size() == 0) { + return FunctionValue.EMPTY; + } + return new FunctionValue(methodsToFunction(methods)); + } catch (SecurityException ex) { + // ignore and go to the next step + } + + return NULL; + + } + + private Function methodsToFunction(List methods) { + return (args) -> { + for (Method method : methods) { + if (method.getParameterCount() != args.length) continue; + if (!isMatch(args, method.getParameterTypes())) continue; + try { + final Object result = method.invoke(object, valuesToObjects(args)); + if (method.getReturnType() != void.class) { + return objectToValue(result); + } + return NumberValue.ONE; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + // skip + } + } + return null; + }; + } + + private boolean isMatch(Value[] args, Class[] types) { + for (int i = 0; i < args.length; i++) { + final Value arg = args[i]; + final Class clazz = types[i]; + + if (arg == NULL) continue; + if (unboxed(clazz).isAssignableFrom(unboxed(valueToObject(arg).getClass()))) { + continue; + } + return false; + } + return true; + } + + @Override + public String asString() { + return object.toString(); + } + + @Override + public String toString() { + return "ObjectValue " + asString(); + } + + private Class unboxed(Class clazz) { + if (clazz == null) return null; + if (clazz.isPrimitive()) { + if (int.class == clazz) return Integer.class; + if (boolean.class == clazz) return Boolean.class; + if (double.class == clazz) return Double.class; + if (float.class == clazz) return Float.class; + if (long.class == clazz) return Long.class; + if (byte.class == clazz) return Byte.class; + if (char.class == clazz) return Character.class; + if (short.class == clazz) return Short.class; + if (void.class == clazz) return Void.class; + } + return clazz; + } + } +// + + private Value isNull(Value... args) { + Arguments.checkAtLeast(1, args.length); + for (Value arg : args) { + if (arg.raw() == null) return NumberValue.ONE; + } + return NumberValue.ZERO; + } + + private Value newClass(Value... args) { + Arguments.check(1, args.length); + + final String className = args[0].asString(); + try { + return new ClassValue(Class.forName(className)); + } catch (ClassNotFoundException ce) { + return NULL; + } + } + + private Value toObject(Value... args) { + Arguments.check(1, args.length); + if (args[0] == NULL) return NULL; + return new ObjectValue(valueToObject(args[0])); + } + + private Value toValue(Value... args) { + Arguments.check(1, args.length); + if (args[0] instanceof ObjectValue) { + return objectToValue( ((ObjectValue) args[0]).object ); + } + return NULL; + } + + + // + private static ArrayValue array(Class[] classes) { + final ArrayValue result = new ArrayValue(classes.length); + for (int i = 0; i < classes.length; i++) { + result.set(i, ClassValue.classOrNull(classes[i])); + } + return result; + } + + private static Value objectToValue(Object o) { + if (o == null) return NULL; + return objectToValue(o.getClass(), o); + } + + private static Value objectToValue(Class clazz, Object o) { + if (o == null | o == NULL) return NULL; + if (clazz.isPrimitive()) { + if (int.class.isAssignableFrom(clazz)) + return NumberValue.of((int) o); + if (boolean.class.isAssignableFrom(clazz)) + return NumberValue.fromBoolean((boolean) o); + if (double.class.isAssignableFrom(clazz)) + return NumberValue.of((double) o); + if (float.class.isAssignableFrom(clazz)) + return NumberValue.of((float) o); + if (long.class.isAssignableFrom(clazz)) + return NumberValue.of((long) o); + if (byte.class.isAssignableFrom(clazz)) + return NumberValue.of((byte) o); + if (char.class.isAssignableFrom(clazz)) + return NumberValue.of((char) o); + if (short.class.isAssignableFrom(clazz)) + return NumberValue.of((short) o); + } + if (Number.class.isAssignableFrom(clazz)) { + return NumberValue.of((Number) o); + } + if (String.class.isAssignableFrom(clazz)) { + return new StringValue((String) o); + } + if (CharSequence.class.isAssignableFrom(clazz)) { + return new StringValue( ((CharSequence) o).toString() ); + } + if (o instanceof Value) { + return (Value) o; + } + if (clazz.isArray()) { + final int length = Array.getLength(o); + final ArrayValue result = new ArrayValue(length); + final Class componentType = clazz.getComponentType(); + int i = 0; + if (boolean.class.isAssignableFrom(componentType)) { + for (boolean element : (boolean[]) o) { + result.set(i++, NumberValue.fromBoolean(element)); + } + } else if (byte.class.isAssignableFrom(componentType)) { + for (byte element : (byte[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (char.class.isAssignableFrom(componentType)) { + for (char element : (char[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (double.class.isAssignableFrom(componentType)) { + for (double element : (double[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (float.class.isAssignableFrom(componentType)) { + for (float element : (float[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (int.class.isAssignableFrom(componentType)) { + for (int element : (int[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (long.class.isAssignableFrom(componentType)) { + for (long element : (long[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (short.class.isAssignableFrom(componentType)) { + for (short element : (short[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else { + for (Object element : (Object[]) o) { + result.set(i++, objectToValue(element)); + } + } + return result; + } + final Class componentType = clazz.getComponentType(); + if (componentType != null) { + return objectToValue(componentType, o); + } + return new ObjectValue(o); + } + + private static Object[] valuesToObjects(Value[] args) { + Object[] result = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + result[i] = valueToObject(args[i]); + } + return result; + } + + private static Object valueToObject(Value value) { + if (value == NULL) return null; + switch (value.type()) { + case Types.NUMBER: + return value.raw(); + case Types.STRING: + return value.asString(); + case Types.ARRAY: { + final ArrayValue array = (ArrayValue) value; + final int size = array.size(); + final Object[] result = new Object[size]; + for (int i = 0; i < size; i++) { + result[i] = valueToObject(array.get(i)); + } + return result; + } + } + if (value instanceof ObjectValue) { + return ((ObjectValue) value).object; + } + if (value instanceof ClassValue) { + return ((ClassValue) value).clazz; + } + return value.raw(); + } +// +} diff --git a/test/interop/Data.java b/test/interop/Data.java new file mode 100644 index 00000000..693cfb87 --- /dev/null +++ b/test/interop/Data.java @@ -0,0 +1,39 @@ +package interop; + +public final class Data { + public final int intValue = 3228; + public final int[] intArrayValue = {1, 2, 3}; + public final Object nullObject = null; + public final String stringValue = "str"; + public final String[] stringArrayValue = {"abc", "test"}; + public final Object[] objectArray = new Object[] {intValue, intArrayValue, nullObject, stringValue, stringArrayValue}; + public final Object compoundObject = objectArray; + + public void method() { + System.out.println("method"); + } + + public String methodWithResult() { + return "result"; + } + + + private int value; + private String text; + + public void set(int value) { + this.value = value; + } + + public void set(String text) { + this.text = text; + } + + public int getValue() { + return value; + } + + public String getText() { + return text; + } +} diff --git a/test/resources/modules/java/classes.own b/test/resources/modules/java/classes.own new file mode 100644 index 00000000..04a8dd2a --- /dev/null +++ b/test/resources/modules/java/classes.own @@ -0,0 +1,49 @@ +use "std" +use "java" + +def testCheckNull() { + assertTrue(isNull(null)) + assertFalse(isNull(`byte[].class`)) + assertTrue(null == null) +} + +def testFieldAccess() { + data = createObject() + assertEquals(3228, data.intValue) + assertEquals([1, 2, 3], data.intArrayValue) + assertTrue(isNull(data.nullObject)) + assertEquals("str", data.stringValue) + assertEquals(["abc", "test"], data.stringArrayValue) + assertObjectArray(data.objectArray) +} + +def testCast() { + data = createObject() + assertObjectArray( `Object[].class`.cast(data.compoundObject) ) +} + +def testInvokeMethod() { + data = createObject() + data.method() + assertEquals("result", data.methodWithResult()) +} + +def testInvokeMethodSameName() { + data = createObject() + data.set(1) + data.set("text") + + assertEquals(1, data.getValue()) + assertEquals("text", data.getText()) +} + + +def createObject() { + dataClass = newClass("interop.Data") + return dataClass.new() +} + +def assertObjectArray(obj) { + assertEquals(5, length(obj)) + assertEquals([3228, [1, 2, 3], null, "str", ["abc", "test"]], obj) +} From eb2ab6ee503da1ec92b903665d76fc1752644738 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Jul 2016 23:28:18 +0300 Subject: [PATCH 127/448] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=BE=D1=88=D0=B8?= =?UTF-8?q?=D0=B1=D0=BA=D0=B8=20=D0=BF=D1=80=D0=B8=20=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=BF=D0=B0=D0=B4=D0=B5=D0=BD=D0=B8=D0=B8=20=D1=82?= =?UTF-8?q?=D0=B8=D0=BF=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/Types.java | 2 +- .../annimon/ownlang/parser/ast/ContainerAccessExpression.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/annimon/ownlang/lib/Types.java b/src/com/annimon/ownlang/lib/Types.java index 4f1696fc..a6da0333 100644 --- a/src/com/annimon/ownlang/lib/Types.java +++ b/src/com/annimon/ownlang/lib/Types.java @@ -17,6 +17,6 @@ public static String typeToString(int type) { if (FIRST <= type && type <= LAST) { return NAMES[type]; } - return "unknown"; + return "unknown (" + type + ")"; } } diff --git a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 3e9d5411..35266105 100644 --- a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -36,7 +36,7 @@ public Value get() { return ((MapValue) container).get(lastIndex); default: - throw new TypeException("Array or map expected. Got " + container.type()); + throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); } } From a22f50d6c0cc2158533ba5e33d6bf33f826aed7b Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 10 Jul 2016 00:02:05 +0300 Subject: [PATCH 128/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20jdbc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/modules/jdbc.java | 783 ++++++++++++++++++ 1 file changed, 783 insertions(+) create mode 100644 src/com/annimon/ownlang/lib/modules/jdbc.java diff --git a/src/com/annimon/ownlang/lib/modules/jdbc.java b/src/com/annimon/ownlang/lib/modules/jdbc.java new file mode 100644 index 00000000..aa2c0051 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/jdbc.java @@ -0,0 +1,783 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * + * @author aNNiMON + */ +public final class jdbc implements Module { + + @Override + public void init() { + Functions.set("getConnection", getConnectionFunction()); + Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); + Functions.set("mysql", getConnectionFunction("jdbc:", () -> Class.forName("com.mysql.jdbc.Driver"))); + } + + private static com.annimon.ownlang.lib.Function getConnectionFunction() { + return getConnectionFunction("", null); + } + + private static com.annimon.ownlang.lib.Function getConnectionFunction(String connectionPrefix) { + return getConnectionFunction(connectionPrefix, null); + } + + private static com.annimon.ownlang.lib.Function getConnectionFunction(String connectionPrefix, ThrowableRunnable preAction) { + return (args) -> { + try { + if (preAction != null) { + preAction.run(); + } + switch (args.length) { + case 1: + return new ConnectionValue(getConnection(connectionPrefix + args[0].asString())); + case 2: + Class.forName(args[1].asString()); + return new ConnectionValue(getConnection(connectionPrefix + args[0].asString())); + case 3: { + final String url = connectionPrefix + args[0].asString(); + return new ConnectionValue(getConnection(url, args[1].asString(), args[2].asString())); + } + case 4: { + Class.forName(args[3].asString()); + String url = connectionPrefix + args[0].asString(); + return new ConnectionValue(getConnection(url, args[1].asString(), args[2].asString())); + } + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }; + } + + private static Value getConnection(Value... args) { + try { + switch (args.length) { + case 1: + return new ConnectionValue(getConnection(args[0].asString())); + case 3: + return new ConnectionValue(getConnection(args[0].asString(), args[1].asString(), args[2].asString())); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private static Connection getConnection(String url) throws SQLException { + return DriverManager.getConnection(url); + } + + private static Connection getConnection(String url, String user, String password) throws SQLException { + return DriverManager.getConnection(url, user, password); + } + + // + private static class ConnectionValue extends MapValue { + + private final Connection connection; + + public ConnectionValue(Connection connection) { + super(20); + this.connection = connection; + init(); + } + + private void init() { + set(new StringValue("createStatement"), new FunctionValue(this::createStatement)); + set(new StringValue("prepareStatement"), new FunctionValue(this::prepareStatement)); + set(new StringValue("close"), new FunctionValue(this::close)); + + set(new StringValue("clearWarnings"), voidFunction(connection::clearWarnings)); + set(new StringValue("close"), voidFunction(connection::close)); + set(new StringValue("commit"), voidFunction(connection::commit)); + set(new StringValue("rollback"), voidFunction(connection::rollback)); + + set(new StringValue("setHoldability"), voidIntFunction(connection::setHoldability)); + set(new StringValue("setTransactionIsolation"), voidIntFunction(connection::setTransactionIsolation)); + + set(new StringValue("getAutoCommit"), booleanFunction(connection::getAutoCommit)); + set(new StringValue("isClosed"), booleanFunction(connection::isClosed)); + set(new StringValue("isReadOnly"), booleanFunction(connection::isReadOnly)); + + set(new StringValue("getHoldability"), intFunction(connection::getHoldability)); + set(new StringValue("getNetworkTimeout"), intFunction(connection::getNetworkTimeout)); + set(new StringValue("getTransactionIsolation"), intFunction(connection::getTransactionIsolation)); + set(new StringValue("getUpdateCount"), intFunction(connection::getHoldability)); + + set(new StringValue("getCatalog"), stringFunction(connection::getCatalog)); + set(new StringValue("getSchema"), stringFunction(connection::getSchema)); + } + + private Value createStatement(Value... args) { + try { + switch (args.length) { + case 0: + return new StatementValue(connection.createStatement()); + case 2: + return new StatementValue(connection.createStatement(args[0].asInt(), args[1].asInt())); + case 3: + return new StatementValue(connection.createStatement(args[0].asInt(), args[1].asInt(), args[2].asInt())); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value prepareStatement(Value... args) { + Arguments.checkRange(1, 4, args.length); + try { + final String sql = args[0].asString(); + switch (args.length) { + case 1: + return new StatementValue(connection.prepareStatement(sql)); + case 2: + final PreparedStatement ps = columnData(args[1], + (int autogeneratedKeys) -> connection.prepareStatement(sql, autogeneratedKeys), + (int[] columnIndices) -> connection.prepareStatement(sql, columnIndices), + (String[] columnNames) -> connection.prepareStatement(sql, columnNames)); + return new StatementValue(ps); + case 3: + return new StatementValue(connection.prepareStatement(sql, args[1].asInt(), args[2].asInt())); + case 4: + return new StatementValue(connection.prepareStatement(sql, args[1].asInt(), args[2].asInt(), args[3].asInt())); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value close(Value... args) { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException sqlex) { + // skip + } + return NumberValue.ZERO; + } + } + + private static class StatementValue extends MapValue { + + private final Statement statement; + private final PreparedStatement ps; + + public StatementValue(Statement statement) { + super(50); + this.statement = statement; + if (statement instanceof PreparedStatement) { + ps = (PreparedStatement) statement; + } else { + ps = null; + } + init(); + } + + private void init() { + set(new StringValue("execute"), new FunctionValue(this::execute)); + set(new StringValue("executeQuery"), new FunctionValue(this::executeQuery)); + set(new StringValue("executeUpdate"), new FunctionValue(this::executeUpdate)); + set(new StringValue("executeLargeUpdate"), new FunctionValue(this::executeLargeUpdate)); + + set(new StringValue("cancel"), voidFunction(statement::cancel)); + set(new StringValue("clearBatch"), voidFunction(statement::clearBatch)); + set(new StringValue("clearWarnings"), voidFunction(statement::clearWarnings)); + set(new StringValue("close"), voidFunction(statement::close)); + set(new StringValue("closeOnCompletion"), voidFunction(statement::closeOnCompletion)); + + set(new StringValue("setFetchDirection"), voidIntFunction(statement::setFetchDirection)); + set(new StringValue("setFetchSize"), voidIntFunction(statement::setFetchSize)); + set(new StringValue("setMaxFieldSize"), voidIntFunction(statement::setMaxFieldSize)); + set(new StringValue("setMaxRows"), voidIntFunction(statement::setMaxRows)); + set(new StringValue("setQueryTimeout"), voidIntFunction(statement::setQueryTimeout)); + + set(new StringValue("getMoreResults"), booleanFunction(statement::getMoreResults)); + set(new StringValue("isCloseOnCompletion"), booleanFunction(statement::isCloseOnCompletion)); + set(new StringValue("isClosed"), booleanFunction(statement::isClosed)); + set(new StringValue("isPoolable"), booleanFunction(statement::isPoolable)); + + set(new StringValue("getFetchDirection"), intFunction(statement::getFetchDirection)); + set(new StringValue("getFetchSize"), intFunction(statement::getFetchSize)); + set(new StringValue("getMaxFieldSize"), intFunction(statement::getMaxFieldSize)); + set(new StringValue("getMaxRows"), intFunction(statement::getMaxRows)); + set(new StringValue("getQueryTimeout"), intFunction(statement::getQueryTimeout)); + set(new StringValue("getResultSetConcurrency"), intFunction(statement::getResultSetConcurrency)); + set(new StringValue("getResultSetHoldability"), intFunction(statement::getResultSetHoldability)); + set(new StringValue("getResultSetType"), intFunction(statement::getResultSetType)); + set(new StringValue("getUpdateCount"), intFunction(statement::getUpdateCount)); + + set(new StringValue("addBatch"), updateData(statement::addBatch, (args) -> args[0].asString())); + set(new StringValue("setCursorName"), updateData(statement::setCursorName, (args) -> args[0].asString())); + set(new StringValue("setEscapeProcessing"), updateData(statement::setEscapeProcessing, (args) -> args[0].asInt() != 0)); + set(new StringValue("setLargeMaxRows"), updateData(statement::setLargeMaxRows, (args) -> getNumber(args[0]).longValue())); + set(new StringValue("setPoolable"), updateData(statement::setPoolable, (args) -> args[0].asInt() != 0)); + + set(new StringValue("getResultSet"), objectFunction(statement::getResultSet, ResultSetValue::new)); + + if (ps != null) { + set(new StringValue("setBigDecimal"), updateData(ps::setBigDecimal, (args) -> new BigDecimal(args[1].asString()))); + set(new StringValue("setBoolean"), updateData(ps::setBoolean, (args) -> args[1].asInt() != 0)); + set(new StringValue("setByte"), updateData(ps::setByte, (args) -> getNumber(args[1]).byteValue())); + set(new StringValue("setBytes"), updateData(ps::setBytes, (args) -> valueToByteArray(args[1]))); + set(new StringValue("setDate"), updateData(ps::setDate, (args) -> new Date(getNumber(args[1]).longValue()))); + set(new StringValue("setDouble"), updateData(ps::setDouble, (args) -> getNumber(args[1]).doubleValue())); + set(new StringValue("setFloat"), updateData(ps::setFloat, (args) -> getNumber(args[1]).floatValue())); + set(new StringValue("setInt"), updateData(ps::setInt, (args) -> args[1].asInt())); + set(new StringValue("setLong"), updateData(ps::setLong, (args) -> getNumber(args[1]).longValue())); + set(new StringValue("setNString"), updateData(ps::setNString, (args) -> args[1].asString())); + set(new StringValue("setNull"), updateData(ps::setNull, (args) -> args[1].asInt())); + set(new StringValue("setShort"), updateData(ps::setShort, (args) -> getNumber(args[1]).shortValue())); + set(new StringValue("setString"), updateData(ps::setString, (args) -> args[1].asString())); + set(new StringValue("setTime"), updateData(ps::setTime, (args) -> new Time(getNumber(args[1]).longValue()))); + set(new StringValue("setTimestamp"), updateData(ps::setTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); + set(new StringValue("setURL"), updateData(ps::setURL, (args) -> { + try { + return new URL(args[1].asString()); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + })); + } + } + + private Value execute(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + try { + final String sql = args[0].asString(); + if (args.length == 1) return NumberValue.fromBoolean(statement.execute(sql)); + + boolean result = columnData(args[1], + (int autogeneratedKeys) -> statement.execute(sql, autogeneratedKeys), + (int[] columnIndices) -> statement.execute(sql, columnIndices), + (String[] columnNames) -> statement.execute(sql, columnNames)); + return NumberValue.fromBoolean(result); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value executeQuery(Value... args) { + Arguments.check(1, args.length); + try { + return new ResultSetValue(statement.executeQuery(args[0].asString())); + } catch (SQLException sqlex) { + return NumberValue.ZERO; + } + } + + private Value executeUpdate(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + try { + final String sql = args[0].asString(); + if (args.length == 1) return NumberValue.of(statement.executeUpdate(sql)); + + int rowCount = columnData(args[1], + (int autogeneratedKeys) -> statement.executeUpdate(sql, autogeneratedKeys), + (int[] columnIndices) -> statement.executeUpdate(sql, columnIndices), + (String[] columnNames) -> statement.executeUpdate(sql, columnNames)); + return NumberValue.of(rowCount); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value executeLargeUpdate(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + try { + final String sql = args[0].asString(); + if (args.length == 1) return NumberValue.of(statement.executeLargeUpdate(sql)); + + long rowCount = columnData(args[1], + (int autogeneratedKeys) -> statement.executeLargeUpdate(sql, autogeneratedKeys), + (int[] columnIndices) -> statement.executeLargeUpdate(sql, columnIndices), + (String[] columnNames) -> statement.executeLargeUpdate(sql, columnNames)); + return NumberValue.of(rowCount); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + } + + private static class ResultSetValue extends MapValue { + + private final ResultSet rs; + + public ResultSetValue(ResultSet rs) { + super(70); + this.rs = rs; + init(); + } + + private void init() { + set(new StringValue("findColumn"), new FunctionValue(this::findColumn)); + + set(new StringValue("afterLast"), voidFunction(rs::afterLast)); + set(new StringValue("beforeFirst"), voidFunction(rs::beforeFirst)); + set(new StringValue("cancelRowUpdates"), voidFunction(rs::cancelRowUpdates)); + set(new StringValue("clearWarnings"), voidFunction(rs::clearWarnings)); + set(new StringValue("close"), voidFunction(rs::close)); + set(new StringValue("deleteRow"), voidFunction(rs::deleteRow)); + set(new StringValue("insertRow"), voidFunction(rs::insertRow)); + set(new StringValue("moveToCurrentRow"), voidFunction(rs::moveToCurrentRow)); + set(new StringValue("moveToInsertRow"), voidFunction(rs::moveToInsertRow)); + set(new StringValue("refreshRow"), voidFunction(rs::refreshRow)); + set(new StringValue("updateRow"), voidFunction(rs::updateRow)); + + set(new StringValue("absolute"), voidIntFunction(rs::absolute)); + set(new StringValue("relative"), voidIntFunction(rs::relative)); + set(new StringValue("setFetchDirection"), voidIntFunction(rs::setFetchDirection)); + set(new StringValue("setFetchSize"), voidIntFunction(rs::setFetchSize)); + + set(new StringValue("first"), booleanFunction(rs::first)); + set(new StringValue("isAfterLast"), booleanFunction(rs::isAfterLast)); + set(new StringValue("isBeforeFirst"), booleanFunction(rs::isBeforeFirst)); + set(new StringValue("isClosed"), booleanFunction(rs::isClosed)); + set(new StringValue("isFirst"), booleanFunction(rs::isFirst)); + set(new StringValue("isLast"), booleanFunction(rs::isLast)); + set(new StringValue("last"), booleanFunction(rs::last)); + set(new StringValue("next"), booleanFunction(rs::next)); + set(new StringValue("previous"), booleanFunction(rs::previous)); + set(new StringValue("rowDeleted"), booleanFunction(rs::rowDeleted)); + set(new StringValue("rowInserted"), booleanFunction(rs::rowInserted)); + set(new StringValue("rowUpdated"), booleanFunction(rs::rowUpdated)); + set(new StringValue("wasNull"), booleanFunction(rs::wasNull)); + + set(new StringValue("getConcurrency"), intFunction(rs::getConcurrency)); + set(new StringValue("getFetchDirection"), intFunction(rs::getFetchDirection)); + set(new StringValue("getFetchSize"), intFunction(rs::getFetchSize)); + set(new StringValue("getHoldability"), intFunction(rs::getHoldability)); + set(new StringValue("getRow"), intFunction(rs::getRow)); + set(new StringValue("getType"), intFunction(rs::getType)); + + set(new StringValue("getCursorName"), stringFunction(rs::getCursorName)); + set(new StringValue("getStatement"), objectFunction(rs::getStatement, StatementValue::new)); + + // Results + set(new StringValue("getArray"), getObjectResult(rs::getArray, rs::getArray, jdbc::arrayToResultSetValue)); + set(new StringValue("getBigDecimal"), getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); + set(new StringValue("getBoolean"), getBooleanResult(rs::getBoolean, rs::getBoolean)); + set(new StringValue("getByte"), getNumberResult(rs::getByte, rs::getByte)); + set(new StringValue("getBytes"), getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); + set(new StringValue("getDate"), getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); + set(new StringValue("getDouble"), getNumberResult(rs::getDouble, rs::getDouble)); + set(new StringValue("getFloat"), getNumberResult(rs::getFloat, rs::getFloat)); + set(new StringValue("getInt"), getNumberResult(rs::getInt, rs::getInt)); + set(new StringValue("getLong"), getNumberResult(rs::getLong, rs::getLong)); + set(new StringValue("getNString"), getStringResult(rs::getNString, rs::getNString)); + set(new StringValue("getRowId"), getObjectResult(rs::getRowId, rs::getRowId, (rowid) -> ArrayValue.of(rowid.getBytes()))); + set(new StringValue("getShort"), getNumberResult(rs::getShort, rs::getShort)); + set(new StringValue("getString"), getStringResult(rs::getString, rs::getString)); + set(new StringValue("getTime"), getObjectResult(rs::getTime, rs::getTime, (time) -> NumberValue.of(time.getTime()))); + set(new StringValue("getTimestamp"), getObjectResult(rs::getTimestamp, rs::getTimestamp, (timestamp) -> NumberValue.of(timestamp.getTime()))); + set(new StringValue("getURL"), getObjectResult(rs::getURL, rs::getURL, (url) -> new StringValue(url.toExternalForm()))); + + // Update + set(new StringValue("updateNull"), new FunctionValue(this::updateNull)); + set(new StringValue("updateBigDecimal"), updateData(rs::updateBigDecimal, rs::updateBigDecimal, (args) -> new BigDecimal(args[1].asString()))); + set(new StringValue("updateBoolean"), updateData(rs::updateBoolean, rs::updateBoolean, (args) -> args[1].asInt() != 0)); + set(new StringValue("updateByte"), updateData(rs::updateByte, rs::updateByte, (args) -> getNumber(args[1]).byteValue())); + set(new StringValue("updateBytes"), updateData(rs::updateBytes, rs::updateBytes, (args) -> valueToByteArray(args[1]))); + set(new StringValue("updateDate"), updateData(rs::updateDate, rs::updateDate, (args) -> new Date(getNumber(args[1]).longValue()))); + set(new StringValue("updateDouble"), updateData(rs::updateDouble, rs::updateDouble, (args) -> getNumber(args[1]).doubleValue())); + set(new StringValue("updateFloat"), updateData(rs::updateFloat, rs::updateFloat, (args) -> getNumber(args[1]).floatValue())); + set(new StringValue("updateInt"), updateData(rs::updateInt, rs::updateInt, (args) -> getNumber(args[1]).intValue())); + set(new StringValue("updateLong"), updateData(rs::updateLong, rs::updateLong, (args) -> getNumber(args[1]).longValue())); + set(new StringValue("updateNString"), updateData(rs::updateNString, rs::updateNString, (args) -> args[1].asString())); + set(new StringValue("updateShort"), updateData(rs::updateShort, rs::updateShort, (args) -> getNumber(args[1]).shortValue())); + set(new StringValue("updateString"), updateData(rs::updateString, rs::updateString, (args) -> args[1].asString())); + set(new StringValue("updateTime"), updateData(rs::updateTime, rs::updateTime, (args) -> new Time(getNumber(args[1]).longValue()))); + set(new StringValue("updateTimestamp"), updateData(rs::updateTimestamp, rs::updateTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); + } + + private Value findColumn(Value... args) { + try { + return NumberValue.of(rs.findColumn(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value updateNull(Value... args) { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + rs.updateNull(args[0].asInt()); + } + rs.updateNull(args[0].asString()); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + } +// + + private static Number getNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); + return value.asInt(); + } + + private static T columnData(Value value, AutogeneratedKeys autogeneratedKeysFunction, + ColumnIndices columnIndicesFunction, ColumnNames columnNamesFunction) { + try { + if (value.type() != Types.ARRAY) { + return autogeneratedKeysFunction.apply(value.asInt()); + } + + final ArrayValue array = (ArrayValue) value; + final int size = array.size(); + final boolean isIntArray = (size > 0) && (array.get(0).type() == Types.NUMBER); + int index = 0; + + if (isIntArray) { + final int[] columnIndices = new int[size]; + for (Value v : array) { + columnIndices[index++] = v.asInt(); + } + return columnIndicesFunction.apply(columnIndices); + } + + final String[] columnNames = new String[size]; + for (Value v : array) { + columnNames[index++] = v.asString(); + } + return columnNamesFunction.apply(columnNames); + + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private interface AutogeneratedKeys { + + T apply(int autogeneratedKeys) throws SQLException; + } + + private interface ColumnIndices { + + T apply(int[] columnIndices) throws SQLException; + } + + private interface ColumnNames { + + T apply(String[] columnNames) throws SQLException; + } + + private static FunctionValue voidFunction(VoidResult result) { + return new FunctionValue((args) -> { + try { + result.execute(); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static FunctionValue voidIntFunction(VoidResultInt result) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + result.execute(args[0].asInt()); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static FunctionValue booleanFunction(BooleanResult result) { + return new FunctionValue((args) -> { + try { + return NumberValue.fromBoolean(result.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value intFunction(IntResult numberResult) { + return new FunctionValue((args) -> { + try { + return NumberValue.of(numberResult.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value stringFunction(StringResult stringResult) { + return new FunctionValue((args) -> { + try { + return new StringValue(stringResult.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value objectFunction(ObjectResult objectResult, Function converter) { + return new FunctionValue((args) -> { + try { + return converter.apply(objectResult.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + + private static Value getBooleanResult(BooleanColumnResultInt numberResult, BooleanColumnResultString stringResult) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return NumberValue.fromBoolean(numberResult.get(args[0].asInt())); + } + return NumberValue.fromBoolean(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getNumberResult(NumberColumnResultInt numberResult, NumberColumnResultIntString stringResult) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return NumberValue.of(numberResult.get(args[0].asInt())); + } + return NumberValue.of(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getStringResult(StringColumnResultInt numberResult, StringColumnResultIntString stringResult) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return new StringValue(numberResult.get(args[0].asInt())); + } + return new StringValue(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getObjectResult(ObjectColumnResultInt numberResult, ObjectColumnResultString stringResult, + Function converter) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return converter.apply(numberResult.get(args[0].asInt())); + } + return converter.apply(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getObjectResult(ObjectColumnResultInt numberResult, ObjectColumnResultString stringResult, + BiFunction converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return converter.apply(numberResult.get(args[0].asInt()), args); + } + return converter.apply(stringResult.get(args[0].asString()), args); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value updateData(VoidResultObject result, Function converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(2, args.length); + try { + result.execute(converter.apply(args)); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value updateData(VoidColumnResultIntObject numberResult, Function converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(2, args.length); + try { + numberResult.execute(args[0].asInt(), converter.apply(args)); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value updateData(VoidColumnResultIntObject numberResult, VoidColumnResultStringObject stringResult, + Function converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(2, args.length); + try { + if (args[0].type() == Types.NUMBER) { + numberResult.execute(args[0].asInt(), converter.apply(args)); + } else { + stringResult.execute(args[0].asString(), converter.apply(args)); + } + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value arrayToResultSetValue(Array array, Value[] args) { + try { + final ResultSet result; + switch (args.length) { + case 1: + // column + result = array.getResultSet(); + break; + case 3: + // column, index, count + long index = (args[1].type() == Types.NUMBER) ? ((NumberValue) args[1]).asLong() : args[1].asInt(); + result = array.getResultSet(index, args[2].asInt()); + break; + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + return new ResultSetValue(result); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private static byte[] valueToByteArray(Value value) { + if (value.type() != Types.ARRAY) { + return new byte[0]; + } + final ArrayValue array = (ArrayValue) value; + final int size = array.size(); + final byte[] result = new byte[size]; + for (int i = 0; i < size; i++) { + result[i] = getNumber(array.get(i)).byteValue(); + } + return result; + } + + private interface ThrowableRunnable { + void run() throws Exception; + } + + private interface VoidResult { + void execute() throws SQLException; + } + + private interface VoidResultInt { + void execute(int index) throws SQLException; + } + + private interface VoidResultObject { + void execute(T data) throws SQLException; + } + + private interface BooleanResult { + boolean get() throws SQLException; + } + + private interface IntResult { + int get() throws SQLException; + } + + private interface StringResult { + String get() throws SQLException; + } + + private interface ObjectResult { + T get() throws SQLException; + } + + private interface BooleanColumnResultInt { + boolean get(int columnIndex) throws SQLException; + } + + private interface BooleanColumnResultString { + boolean get(String columnName) throws SQLException; + } + + private interface NumberColumnResultInt { + Number get(int columnIndex) throws SQLException; + } + + private interface NumberColumnResultIntString { + Number get(String columnName) throws SQLException; + } + + private interface StringColumnResultInt { + String get(int columnIndex) throws SQLException; + } + + private interface StringColumnResultIntString { + String get(String columnName) throws SQLException; + } + + private interface ObjectColumnResultInt { + T get(int columnIndex) throws SQLException; + } + + private interface ObjectColumnResultString { + T get(String columnName) throws SQLException; + } + + private interface VoidColumnResultIntObject { + void execute(int columnIndex, T value) throws SQLException; + } + + private interface VoidColumnResultStringObject { + void execute(String columnName, T value) throws SQLException; + } +} From d2c492540989948409a508e152bcf2d1bab1eb76 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 10 Jul 2016 01:06:07 +0300 Subject: [PATCH 129/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=B0=20=D0=B0=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B4=D0=BB=D1=8F=20PreparedStatement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/modules/jdbc.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/com/annimon/ownlang/lib/modules/jdbc.java b/src/com/annimon/ownlang/lib/modules/jdbc.java index aa2c0051..6405aeff 100644 --- a/src/com/annimon/ownlang/lib/modules/jdbc.java +++ b/src/com/annimon/ownlang/lib/modules/jdbc.java @@ -197,7 +197,7 @@ private static class StatementValue extends MapValue { private final PreparedStatement ps; public StatementValue(Statement statement) { - super(50); + super(55); this.statement = statement; if (statement instanceof PreparedStatement) { ps = (PreparedStatement) statement; @@ -208,6 +208,7 @@ public StatementValue(Statement statement) { } private void init() { + set(new StringValue("addBatch"), new FunctionValue(this::addBatch)); set(new StringValue("execute"), new FunctionValue(this::execute)); set(new StringValue("executeQuery"), new FunctionValue(this::executeQuery)); set(new StringValue("executeUpdate"), new FunctionValue(this::executeUpdate)); @@ -240,7 +241,6 @@ private void init() { set(new StringValue("getResultSetType"), intFunction(statement::getResultSetType)); set(new StringValue("getUpdateCount"), intFunction(statement::getUpdateCount)); - set(new StringValue("addBatch"), updateData(statement::addBatch, (args) -> args[0].asString())); set(new StringValue("setCursorName"), updateData(statement::setCursorName, (args) -> args[0].asString())); set(new StringValue("setEscapeProcessing"), updateData(statement::setEscapeProcessing, (args) -> args[0].asInt() != 0)); set(new StringValue("setLargeMaxRows"), updateData(statement::setLargeMaxRows, (args) -> getNumber(args[0]).longValue())); @@ -249,6 +249,8 @@ private void init() { set(new StringValue("getResultSet"), objectFunction(statement::getResultSet, ResultSetValue::new)); if (ps != null) { + set(new StringValue("clearParameters"), voidFunction(ps::clearParameters)); + set(new StringValue("setBigDecimal"), updateData(ps::setBigDecimal, (args) -> new BigDecimal(args[1].asString()))); set(new StringValue("setBoolean"), updateData(ps::setBoolean, (args) -> args[1].asInt() != 0)); set(new StringValue("setByte"), updateData(ps::setByte, (args) -> getNumber(args[1]).byteValue())); @@ -274,9 +276,23 @@ private void init() { } } + private Value addBatch(Value... args) { + if (ps != null) Arguments.checkOrOr(0, 1, args.length); + else Arguments.check(1, args.length); + try { + if (args.length == 0 && ps != null) ps.addBatch(); + else statement.addBatch(args[0].asString()); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + private Value execute(Value... args) { - Arguments.checkOrOr(1, 2, args.length); + if (ps != null) Arguments.checkRange(0, 2, args.length); + else Arguments.checkOrOr(1, 2, args.length); try { + if (args.length == 0 && ps != null) return NumberValue.fromBoolean(ps.execute()); final String sql = args[0].asString(); if (args.length == 1) return NumberValue.fromBoolean(statement.execute(sql)); @@ -291,8 +307,10 @@ private Value execute(Value... args) { } private Value executeQuery(Value... args) { - Arguments.check(1, args.length); + if (ps != null) Arguments.checkOrOr(0, 1, args.length); + else Arguments.check(1, args.length); try { + if (args.length == 0 && ps != null) return new ResultSetValue(ps.executeQuery()); return new ResultSetValue(statement.executeQuery(args[0].asString())); } catch (SQLException sqlex) { return NumberValue.ZERO; @@ -300,8 +318,10 @@ private Value executeQuery(Value... args) { } private Value executeUpdate(Value... args) { - Arguments.checkOrOr(1, 2, args.length); + if (ps != null) Arguments.checkRange(0, 2, args.length); + else Arguments.checkOrOr(1, 2, args.length); try { + if (args.length == 0 && ps != null) return NumberValue.of(ps.executeUpdate()); final String sql = args[0].asString(); if (args.length == 1) return NumberValue.of(statement.executeUpdate(sql)); @@ -316,8 +336,10 @@ private Value executeUpdate(Value... args) { } private Value executeLargeUpdate(Value... args) { - Arguments.checkOrOr(1, 2, args.length); + if (ps != null) Arguments.checkRange(0, 2, args.length); + else Arguments.checkOrOr(1, 2, args.length); try { + if (args.length == 0 && ps != null) return NumberValue.of(ps.executeLargeUpdate()); final String sql = args[0].asString(); if (args.length == 1) return NumberValue.of(statement.executeLargeUpdate(sql)); From acd13e7a16f1aad28a8336a0c8ad60952ae49f65 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 10 Jul 2016 13:37:13 +0300 Subject: [PATCH 130/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20getGeneratedKeys,=20executeBatch,=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/modules/jdbc.java | 60 ++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/com/annimon/ownlang/lib/modules/jdbc.java b/src/com/annimon/ownlang/lib/modules/jdbc.java index 6405aeff..dcf4b81e 100644 --- a/src/com/annimon/ownlang/lib/modules/jdbc.java +++ b/src/com/annimon/ownlang/lib/modules/jdbc.java @@ -10,6 +10,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; import java.io.IOException; import java.math.BigDecimal; import java.net.URL; @@ -34,6 +35,31 @@ public final class jdbc implements Module { @Override public void init() { + Variables.define("TRANSACTION_NONE", NumberValue.of(Connection.TRANSACTION_NONE)); + Variables.define("TRANSACTION_READ_COMMITTED", NumberValue.of(Connection.TRANSACTION_READ_COMMITTED)); + Variables.define("TRANSACTION_READ_UNCOMMITTED", NumberValue.of(Connection.TRANSACTION_READ_UNCOMMITTED)); + Variables.define("TRANSACTION_REPEATABLE_READ", NumberValue.of(Connection.TRANSACTION_REPEATABLE_READ)); + Variables.define("TRANSACTION_SERIALIZABLE", NumberValue.of(Connection.TRANSACTION_SERIALIZABLE)); + + Variables.define("CLOSE_ALL_RESULTS", NumberValue.of(Statement.CLOSE_ALL_RESULTS)); + Variables.define("CLOSE_CURRENT_RESULT", NumberValue.of(Statement.CLOSE_CURRENT_RESULT)); + Variables.define("EXECUTE_FAILED", NumberValue.of(Statement.EXECUTE_FAILED)); + Variables.define("KEEP_CURRENT_RESULT", NumberValue.of(Statement.KEEP_CURRENT_RESULT)); + Variables.define("NO_GENERATED_KEYS", NumberValue.of(Statement.NO_GENERATED_KEYS)); + Variables.define("RETURN_GENERATED_KEYS", NumberValue.of(Statement.RETURN_GENERATED_KEYS)); + Variables.define("SUCCESS_NO_INFO", NumberValue.of(Statement.SUCCESS_NO_INFO)); + + Variables.define("CLOSE_CURSORS_AT_COMMIT", NumberValue.of(ResultSet.CLOSE_CURSORS_AT_COMMIT)); + Variables.define("CONCUR_READ_ONLY", NumberValue.of(ResultSet.CONCUR_READ_ONLY)); + Variables.define("CONCUR_UPDATABLE", NumberValue.of(ResultSet.CONCUR_UPDATABLE)); + Variables.define("FETCH_FORWARD", NumberValue.of(ResultSet.FETCH_FORWARD)); + Variables.define("FETCH_REVERSE", NumberValue.of(ResultSet.FETCH_REVERSE)); + Variables.define("FETCH_UNKNOWN", NumberValue.of(ResultSet.FETCH_UNKNOWN)); + Variables.define("HOLD_CURSORS_OVER_COMMIT", NumberValue.of(ResultSet.HOLD_CURSORS_OVER_COMMIT)); + Variables.define("TYPE_FORWARD_ONLY", NumberValue.of(ResultSet.TYPE_FORWARD_ONLY)); + Variables.define("TYPE_SCROLL_INSENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_INSENSITIVE)); + Variables.define("TYPE_SCROLL_SENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_SENSITIVE)); + Functions.set("getConnection", getConnectionFunction()); Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); Functions.set("mysql", getConnectionFunction("jdbc:", () -> Class.forName("com.mysql.jdbc.Driver"))); @@ -77,21 +103,6 @@ private static com.annimon.ownlang.lib.Function getConnectionFunction(String con }; } - private static Value getConnection(Value... args) { - try { - switch (args.length) { - case 1: - return new ConnectionValue(getConnection(args[0].asString())); - case 3: - return new ConnectionValue(getConnection(args[0].asString(), args[1].asString(), args[2].asString())); - default: - throw new ArgumentsMismatchException("Wrong number of arguments"); - } - } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); - } - } - private static Connection getConnection(String url) throws SQLException { return DriverManager.getConnection(url); } @@ -247,6 +258,9 @@ private void init() { set(new StringValue("setPoolable"), updateData(statement::setPoolable, (args) -> args[0].asInt() != 0)); set(new StringValue("getResultSet"), objectFunction(statement::getResultSet, ResultSetValue::new)); + set(new StringValue("getGeneratedKeys"), objectFunction(statement::getGeneratedKeys, ResultSetValue::new)); + set(new StringValue("executeBatch"), objectFunction(statement::executeBatch, jdbc::intArrayToValue)); + set(new StringValue("executeLargeBatch"), objectFunction(statement::executeLargeBatch, jdbc::longArrayToValue)); if (ps != null) { set(new StringValue("clearParameters"), voidFunction(ps::clearParameters)); @@ -731,6 +745,22 @@ private static byte[] valueToByteArray(Value value) { return result; } + private static Value intArrayToValue(int[] array) { + final ArrayValue result = new ArrayValue(array.length); + for (int i = 0; i < array.length; i++) { + result.set(i, NumberValue.of(array[i])); + } + return result; + } + + private static Value longArrayToValue(long[] array) { + final ArrayValue result = new ArrayValue(array.length); + for (int i = 0; i < array.length; i++) { + result.set(i, NumberValue.of(array[i])); + } + return result; + } + private interface ThrowableRunnable { void run() throws Exception; } From 846050b104cc77ab823907dc23398af81fdd659d Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Sat, 16 Jul 2016 11:54:21 +0300 Subject: [PATCH 131/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 29dde9a6..3147b7e3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # OwnLang +[![Build Status](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial.svg?branch=latest)](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial) + +| Free | Pro | Desktop | +| :--: | :-: | :-----: | +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.2.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.2.0) + OwnLang - dynamic functional programming language inspired by Scala and Python. Available for PC, Android and Java ME devices. ## Key features @@ -140,4 +146,4 @@ or ## License -MIT - see [MIT licence information](LICENSE) \ No newline at end of file +MIT - see [MIT licence information](LICENSE) From bc93f73eab71714913ca64163a396be0bd543d57 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jul 2016 18:04:50 +0300 Subject: [PATCH 132/448] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=B5=D1=87=D0=B0=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/parser/ast/InterruptableNode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/annimon/ownlang/parser/ast/InterruptableNode.java b/src/com/annimon/ownlang/parser/ast/InterruptableNode.java index 56bdccf4..6e2203e4 100644 --- a/src/com/annimon/ownlang/parser/ast/InterruptableNode.java +++ b/src/com/annimon/ownlang/parser/ast/InterruptableNode.java @@ -4,12 +4,12 @@ public abstract class InterruptableNode implements Node { - public static final int RUNNIMG = 0, PAUSED = 1, STOPPED = 2; + public static final int RUNNING = 0, PAUSED = 1, STOPPED = 2; private static volatile int state; public static void run() { - state = RUNNIMG; + state = RUNNING; } public static void pause() { @@ -21,7 +21,7 @@ public static void stop() { } protected void interruptionCheck() { - if (state == RUNNIMG) return; + if (state == RUNNING) return; if (state == STOPPED) { throw new StoppedException(); } From 0921d8cb179c6a2dbbe26df31fa8aaccd1f83f63 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jul 2016 19:03:55 +0300 Subject: [PATCH 133/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20OptimizationVisitor=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=B0=D1=82=D1=82=D0=B5=D1=80=D0=BD=20=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D1=87=D0=B8=D0=BD=D0=B3=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../optimization/OptimizationVisitor.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 5ac7cab6..aac97814 100644 --- a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.ast.*; +import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -246,11 +248,37 @@ public Node visit(MatchExpression s, T t) { boolean changed = expression != s.expression; final List patterns = new ArrayList<>(s.patterns.size()); for (MatchExpression.Pattern pattern : s.patterns) { + if (pattern instanceof MatchExpression.VariablePattern) { + final String variable = ((MatchExpression.VariablePattern) pattern).variable; + final VariableExpression expr = new VariableExpression(variable); + final Node node = expr.accept(this, t); + if (node != expr) { + if (isValue(node)) { + changed = true; + final Value value = ((ValueExpression) node).value; + final Expression optCondition = pattern.optCondition; + final Statement result = pattern.result; + pattern = new MatchExpression.ConstantPattern(value); + pattern.optCondition = optCondition; + pattern.result = result; + } + } + } + final Node patternResult = pattern.result.accept(this, t); if (patternResult != pattern.result) { changed = true; pattern.result = consumeStatement(patternResult); } + + if (pattern.optCondition != null) { + Node optCond = pattern.optCondition.accept(this, t); + if (optCond != pattern.optCondition) { + changed = true; + pattern.optCondition = (Expression) optCond; + } + } + patterns.add(pattern); } if (changed) { From 6d6db550552faf54c9d4519a46cf03181efd4aea Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jul 2016 20:31:23 +0300 Subject: [PATCH 134/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20OptimizationVisitor=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=D0=B5=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D1=81=D0=B2=D0=B0=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/parser/ast/Accessible.java | 2 +- .../ownlang/parser/optimization/OptimizationVisitor.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/com/annimon/ownlang/parser/ast/Accessible.java b/src/com/annimon/ownlang/parser/ast/Accessible.java index 77ccac9e..67980895 100644 --- a/src/com/annimon/ownlang/parser/ast/Accessible.java +++ b/src/com/annimon/ownlang/parser/ast/Accessible.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.lib.Value; -public interface Accessible { +public interface Accessible extends Node { Value get(); diff --git a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index aac97814..164664e8 100644 --- a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -29,9 +29,10 @@ public Node visit(ArrayExpression s, T t) { @Override public Node visit(AssignmentExpression s, T t) { - final Node node = s.expression.accept(this, t); - if (node != s.expression) { - return new AssignmentExpression(s.operation, s.target, (Expression) node); + final Node exprNode = s.expression.accept(this, t); + final Node targetNode = s.target.accept(this, t); + if ( (exprNode != s.expression || targetNode != s.target) && (targetNode instanceof Accessible) ) { + return new AssignmentExpression(s.operation, (Accessible) targetNode, (Expression) exprNode); } return s; } From 4ac0943805fa04dac66d96436fd18103c11f08f1 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jul 2016 21:18:37 +0300 Subject: [PATCH 135/448] =?UTF-8?q?=D0=92=D1=8B=D0=B2=D0=BE=D0=B4=20=D1=83?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=D0=B5=20=D0=BF=D0=B0?= =?UTF-8?q?=D1=82=D1=82=D0=B5=D1=80=D0=BD=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/parser/ast/MatchExpression.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/com/annimon/ownlang/parser/ast/MatchExpression.java index 3a0ca3c7..51c30913 100644 --- a/src/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/src/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -208,6 +208,16 @@ public String toString() { public static abstract class Pattern { public Statement result; public Expression optCondition; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + if (optCondition != null) { + sb.append(" if ").append(optCondition); + } + sb.append(": ").append(result); + return sb.toString(); + } } public static class ConstantPattern extends Pattern { @@ -219,7 +229,7 @@ public ConstantPattern(Value pattern) { @Override public String toString() { - return constant + ": " + result; + return constant.toString().concat(super.toString()); } } @@ -232,7 +242,7 @@ public VariablePattern(String pattern) { @Override public String toString() { - return variable + ": " + result; + return variable.concat(super.toString()); } } @@ -260,10 +270,10 @@ public String toString() { while (it.hasNext()) { sb.append(" :: ").append(it.next()); } - sb.append("]: ").append(result); + sb.append("]").append(super.toString()); return sb.toString(); } - return "[]: " + result; + return "[]".concat(super.toString()); } } @@ -291,14 +301,14 @@ public String toString() { final Iterator it = values.iterator(); if (it.hasNext()) { final StringBuilder sb = new StringBuilder(); - sb.append("(").append(it.next()); + sb.append('(').append(it.next()); while (it.hasNext()) { sb.append(", ").append(it.next()); } - sb.append("): ").append(result); + sb.append(')').append(super.toString()); return sb.toString(); } - return "(): " + result; + return "()".concat(super.toString()); } private static final Expression ANY = new Expression() { @@ -318,7 +328,7 @@ public R accept(ResultVisitor visitor, T input) { @Override public String toString() { - return "_"; + return "_".concat(super.toString()); } }; } From 839f571b2c5004fb955f09647c13fca17f993110 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jul 2016 21:43:23 +0300 Subject: [PATCH 136/448] =?UTF-8?q?=D0=9E=D0=B1=D1=85=D0=BE=D0=B4=20TupleP?= =?UTF-8?q?attern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../optimization/OptimizationVisitor.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 164664e8..d4cb8187 100644 --- a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -265,6 +265,28 @@ public Node visit(MatchExpression s, T t) { } } } + + if (pattern instanceof MatchExpression.TuplePattern) { + final MatchExpression.TuplePattern tuple = (MatchExpression.TuplePattern) pattern; + final List newValues = new ArrayList<>(tuple.values.size()); + boolean valuesChanged = false; + for (Expression value : tuple.values) { + final Node node = value.accept(this, t); + if (node != value) { + valuesChanged = true; + value = (Expression) node; + } + newValues.add(value); + } + if (valuesChanged) { + changed = true; + final Expression optCondition = pattern.optCondition; + final Statement result = pattern.result; + pattern = new MatchExpression.TuplePattern(newValues); + pattern.optCondition = optCondition; + pattern.result = result; + } + } final Node patternResult = pattern.result.accept(this, t); if (patternResult != pattern.result) { From a484df12e3d495f8652607c36a94cba91a5830d0 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 22 Jul 2016 22:44:46 +0300 Subject: [PATCH 137/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=BE=D0=B4=D1=81=D1=87=D1=91=D1=82?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BF=D1=80=D0=B8=20=D1=83=D0=BD=D0=B0=D1=80=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/optimization/VariablesGrabber.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index d46f8241..e980f680 100644 --- a/src/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,11 +1,16 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.parser.ast.Accessible; import com.annimon.ownlang.parser.ast.Argument; import com.annimon.ownlang.parser.ast.AssignmentExpression; +import com.annimon.ownlang.parser.ast.ContainerAccessExpression; import com.annimon.ownlang.parser.ast.DestructuringAssignmentStatement; +import com.annimon.ownlang.parser.ast.ForeachArrayStatement; +import com.annimon.ownlang.parser.ast.ForeachMapStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.MatchExpression; import com.annimon.ownlang.parser.ast.Node; +import com.annimon.ownlang.parser.ast.UnaryExpression; import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.VariableExpression; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; @@ -46,6 +51,19 @@ public Node visit(DestructuringAssignmentStatement s, Map return super.visit(s, t); } + @Override + public Node visit(ForeachArrayStatement s, Map t) { + t.put(s.variable, variableInfo(t, s.variable)); + return super.visit(s, t); + } + + @Override + public Node visit(ForeachMapStatement s, Map t) { + t.put(s.key, variableInfo(t, s.key)); + t.put(s.value, variableInfo(t, s.value)); + return super.visit(s, t); + } + @Override public Node visit(FunctionDefineStatement s, Map t) { for (Argument argument : s.arguments) { @@ -57,10 +75,32 @@ public Node visit(FunctionDefineStatement s, Map t) { @Override public Node visit(MatchExpression s, Map t) { - // no visit match expression - return s; + for (MatchExpression.Pattern pattern : s.patterns) { + if (pattern instanceof MatchExpression.VariablePattern) { + final String variableName = ((MatchExpression.VariablePattern) pattern).variable; + t.put(variableName, variableInfo(t, variableName)); + } + } + return super.visit(s, t); + } + + @Override + public Node visit(UnaryExpression s, Map t) { + if (s.expr1 instanceof Accessible) { + if (s.expr1 instanceof VariableExpression) { + final String variableName = ((VariableExpression) s.expr1).name; + t.put(variableName, variableInfo(t, variableName)); + } + if (s.expr1 instanceof ContainerAccessExpression) { + final String variableName = ((ContainerAccessExpression) s.expr1).variable; + t.put(variableName, variableInfo(t, variableName)); + } + } + return super.visit(s, t); } + + private VariableInfo variableInfo(Map t, final String variableName) { final VariableInfo var; if (t.containsKey(variableName)) { From 792e6e6db4a87ffaf2cd15e8a97be323ac305c5e Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 23 Jul 2016 23:52:30 +0300 Subject: [PATCH 138/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotations/ConstantInitializer.java | 12 +++++++++ .../annimon/ownlang/lib/modules/Module.java | 2 +- .../annimon/ownlang/lib/modules/canvas.java | 22 ++++++++------- .../annimon/ownlang/lib/modules/canvasfx.java | 6 +++++ src/com/annimon/ownlang/lib/modules/date.java | 9 +++++-- .../annimon/ownlang/lib/modules/files.java | 8 +++++- .../ownlang/lib/modules/functional.java | 9 +++++-- src/com/annimon/ownlang/lib/modules/http.java | 6 +++++ src/com/annimon/ownlang/lib/modules/java.java | 6 +++++ src/com/annimon/ownlang/lib/modules/jdbc.java | 9 +++++-- src/com/annimon/ownlang/lib/modules/json.java | 4 +++ src/com/annimon/ownlang/lib/modules/math.java | 11 +++++--- .../annimon/ownlang/lib/modules/ounit.java | 4 +++ .../annimon/ownlang/lib/modules/robot.java | 27 +++++++++++-------- src/com/annimon/ownlang/lib/modules/std.java | 9 +++++-- .../annimon/ownlang/lib/modules/types.java | 11 +++++--- 16 files changed, 119 insertions(+), 36 deletions(-) create mode 100644 src/com/annimon/ownlang/annotations/ConstantInitializer.java diff --git a/src/com/annimon/ownlang/annotations/ConstantInitializer.java b/src/com/annimon/ownlang/annotations/ConstantInitializer.java new file mode 100644 index 00000000..fd1c781e --- /dev/null +++ b/src/com/annimon/ownlang/annotations/ConstantInitializer.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface ConstantInitializer { + +} diff --git a/src/com/annimon/ownlang/lib/modules/Module.java b/src/com/annimon/ownlang/lib/modules/Module.java index b2cd826d..d022d1fb 100644 --- a/src/com/annimon/ownlang/lib/modules/Module.java +++ b/src/com/annimon/ownlang/lib/modules/Module.java @@ -5,6 +5,6 @@ * @author aNNiMON */ public interface Module { - + void init(); } diff --git a/src/com/annimon/ownlang/lib/modules/canvas.java b/src/com/annimon/ownlang/lib/modules/canvas.java index fbe2950c..7a9fc05c 100644 --- a/src/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/com/annimon/ownlang/lib/modules/canvas.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import java.awt.Color; import java.awt.Dimension; @@ -20,6 +20,7 @@ * * @author aNNiMON */ +@ConstantInitializer public final class canvas implements Module { private static JFrame frame; @@ -30,8 +31,18 @@ public final class canvas implements Module { private static NumberValue lastKey; private static ArrayValue mouseHover; + public static void initConstants() { + Variables.set("VK_UP", NumberValue.of(KeyEvent.VK_UP)); + Variables.set("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); + Variables.set("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); + Variables.set("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); + Variables.set("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); + Variables.set("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + } + @Override public void init() { + initConstants(); Functions.set("window", new CreateWindow()); Functions.set("prompt", new Prompt()); Functions.set("keypressed", new KeyPressed()); @@ -45,14 +56,7 @@ public void init() { Functions.set("drawstring", new DrawString()); Functions.set("color", new SetColor()); Functions.set("repaint", new Repaint()); - - Variables.set("VK_UP", NumberValue.of(KeyEvent.VK_UP)); - Variables.set("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.set("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.set("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.set("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.set("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); - + lastKey = NumberValue.MINUS_ONE; mouseHover = new ArrayValue(new Value[] { NumberValue.ZERO, NumberValue.ZERO }); } diff --git a/src/com/annimon/ownlang/lib/modules/canvasfx.java b/src/com/annimon/ownlang/lib/modules/canvasfx.java index d4cc2f47..7f74aac9 100644 --- a/src/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/com/annimon/ownlang/lib/modules/canvasfx.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.awt.Dimension; @@ -42,6 +43,7 @@ * * @author aNNiMON */ +@ConstantInitializer public final class canvasfx implements Module { private static final int FX_EFFECT_TYPE = 5301; @@ -84,8 +86,12 @@ public EventType getHandler() { } } + public static void initConstants() { + } + @Override public void init() { + initConstants(); Functions.set("window", new CreateWindow()); Functions.set("repaint", new Repaint()); diff --git a/src/com/annimon/ownlang/lib/modules/date.java b/src/com/annimon/ownlang/lib/modules/date.java index 0ab1f727..73376aa1 100644 --- a/src/com/annimon/ownlang/lib/modules/date.java +++ b/src/com/annimon/ownlang/lib/modules/date.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.text.DateFormat; @@ -13,6 +14,7 @@ * * @author aNNiMON */ +@ConstantInitializer public final class date implements Module { private static final int DATE_DATE_FORMAT = 9829; @@ -26,13 +28,16 @@ public final class date implements Module { SECOND = new StringValue("second"), MILLISECOND = new StringValue("millisecond"); - @Override - public void init() { + public static void initConstants() { Variables.set("STYLE_FULL", NumberValue.of(DateFormat.FULL)); Variables.set("STYLE_LONG", NumberValue.of(DateFormat.LONG)); Variables.set("STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM)); Variables.set("STYLE_SHORT", NumberValue.of(DateFormat.SHORT)); + } + @Override + public void init() { + initConstants(); Functions.set("newDate", new date_newDate()); Functions.set("newFormat", new date_newFormat()); Functions.set("formatDate", new date_format()); diff --git a/src/com/annimon/ownlang/lib/modules/files.java b/src/com/annimon/ownlang/lib/modules/files.java index 3ae2b2b8..04c1010e 100644 --- a/src/com/annimon/ownlang/lib/modules/files.java +++ b/src/com/annimon/ownlang/lib/modules/files.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.io.BufferedReader; @@ -20,13 +21,18 @@ * * @author aNNiMON */ +@ConstantInitializer public final class files implements Module { private static Map files; - + + public static void initConstants() { + } + @Override public void init() { files = new HashMap<>(); + initConstants(); Variables.set("FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction())); Functions.set("fopen", new fopen()); diff --git a/src/com/annimon/ownlang/lib/modules/functional.java b/src/com/annimon/ownlang/lib/modules/functional.java index c0aeb05f..f3db9c8f 100644 --- a/src/com/annimon/ownlang/lib/modules/functional.java +++ b/src/com/annimon/ownlang/lib/modules/functional.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.*; @@ -7,10 +8,16 @@ * * @author aNNiMON */ +@ConstantInitializer public final class functional implements Module { + public static void initConstants() { + Variables.set("IDENTITY", new FunctionValue(args -> args[0])); + } + @Override public void init() { + initConstants(); Functions.set("foreach", new functional_foreach()); Functions.set("map", new functional_map()); Functions.set("flatmap", new functional_flatmap()); @@ -20,7 +27,5 @@ public void init() { Functions.set("chain", new functional_chain()); Functions.set("combine", new functional_combine()); - - Variables.set("IDENTITY", new FunctionValue(args -> args[0])); } } diff --git a/src/com/annimon/ownlang/lib/modules/http.java b/src/com/annimon/ownlang/lib/modules/http.java index ceaa1935..a8ffe17e 100644 --- a/src/com/annimon/ownlang/lib/modules/http.java +++ b/src/com/annimon/ownlang/lib/modules/http.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.*; @@ -7,10 +8,15 @@ * * @author aNNiMON */ +@ConstantInitializer public final class http implements Module { + public static void initConstants() { + } + @Override public void init() { + initConstants(); Functions.set("urlencode", new http_urlencode()); Functions.set("http", new http_http()); Functions.set("download", new http_download()); diff --git a/src/com/annimon/ownlang/lib/modules/java.java b/src/com/annimon/ownlang/lib/modules/java.java index feb41478..4b315704 100644 --- a/src/com/annimon/ownlang/lib/modules/java.java +++ b/src/com/annimon/ownlang/lib/modules/java.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -13,12 +14,17 @@ * * @author aNNiMON */ +@ConstantInitializer public final class java implements Module { private static final Value NULL = new NullValue(); + public static void initConstants() { + } + @Override public void init() { + initConstants(); Variables.define("null", NULL); Variables.define("boolean.class", new ClassValue(boolean.class)); Variables.define("boolean[].class", new ClassValue(boolean[].class)); diff --git a/src/com/annimon/ownlang/lib/modules/jdbc.java b/src/com/annimon/ownlang/lib/modules/jdbc.java index dcf4b81e..aa7f4875 100644 --- a/src/com/annimon/ownlang/lib/modules/jdbc.java +++ b/src/com/annimon/ownlang/lib/modules/jdbc.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; @@ -31,10 +32,10 @@ * * @author aNNiMON */ +@ConstantInitializer public final class jdbc implements Module { - @Override - public void init() { + public static void initConstants() { Variables.define("TRANSACTION_NONE", NumberValue.of(Connection.TRANSACTION_NONE)); Variables.define("TRANSACTION_READ_COMMITTED", NumberValue.of(Connection.TRANSACTION_READ_COMMITTED)); Variables.define("TRANSACTION_READ_UNCOMMITTED", NumberValue.of(Connection.TRANSACTION_READ_UNCOMMITTED)); @@ -59,7 +60,11 @@ public void init() { Variables.define("TYPE_FORWARD_ONLY", NumberValue.of(ResultSet.TYPE_FORWARD_ONLY)); Variables.define("TYPE_SCROLL_INSENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_INSENSITIVE)); Variables.define("TYPE_SCROLL_SENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_SENSITIVE)); + } + @Override + public void init() { + initConstants(); Functions.set("getConnection", getConnectionFunction()); Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); Functions.set("mysql", getConnectionFunction("jdbc:", () -> Class.forName("com.mysql.jdbc.Driver"))); diff --git a/src/com/annimon/ownlang/lib/modules/json.java b/src/com/annimon/ownlang/lib/modules/json.java index 6af5997f..6b5abb66 100644 --- a/src/com/annimon/ownlang/lib/modules/json.java +++ b/src/com/annimon/ownlang/lib/modules/json.java @@ -9,8 +9,12 @@ */ public final class json implements Module { + public static void initConstants() { + } + @Override public void init() { + initConstants(); Functions.set("jsonencode", new json_encode()); Functions.set("jsondecode", new json_decode()); } diff --git a/src/com/annimon/ownlang/lib/modules/math.java b/src/com/annimon/ownlang/lib/modules/math.java index b549ea77..33b48cb2 100644 --- a/src/com/annimon/ownlang/lib/modules/math.java +++ b/src/com/annimon/ownlang/lib/modules/math.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleFunction; @@ -10,12 +11,19 @@ * * @author aNNiMON */ +@ConstantInitializer public final class math implements Module { private static final DoubleFunction doubleToNumber = NumberValue::of; + public static void initConstants() { + Variables.set("PI", NumberValue.of(Math.PI)); + Variables.set("E", NumberValue.of(Math.E)); + } + @Override public void init() { + initConstants(); Functions.set("abs", math::abs); Functions.set("acos", functionConvert(Math::acos)); Functions.set("asin", functionConvert(Math::asin)); @@ -52,9 +60,6 @@ public void init() { Functions.set("toDegrees", functionConvert(Math::toDegrees)); Functions.set("toRadians", functionConvert(Math::toRadians)); Functions.set("ulp", functionConvert(Math::ulp, Math::ulp)); - - Variables.set("PI", NumberValue.of(Math.PI)); - Variables.set("E", NumberValue.of(Math.E)); } private static Value abs(Value... args) { diff --git a/src/com/annimon/ownlang/lib/modules/ounit.java b/src/com/annimon/ownlang/lib/modules/ounit.java index 45e33f9c..3fa0caef 100644 --- a/src/com/annimon/ownlang/lib/modules/ounit.java +++ b/src/com/annimon/ownlang/lib/modules/ounit.java @@ -11,8 +11,12 @@ */ public final class ounit implements Module { + public static void initConstants() { + } + @Override public void init() { + initConstants(); Functions.set("assertEquals", new assertEquals()); Functions.set("assertNotEquals", new assertNotEquals()); Functions.set("assertSameType", new assertSameType()); diff --git a/src/com/annimon/ownlang/lib/modules/robot.java b/src/com/annimon/ownlang/lib/modules/robot.java index b870ba21..7e33bbc9 100644 --- a/src/com/annimon/ownlang/lib/modules/robot.java +++ b/src/com/annimon/ownlang/lib/modules/robot.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.robot_exec; import com.annimon.ownlang.lib.modules.functions.robot_fromclipboard; @@ -15,6 +16,7 @@ * * @author aNNiMON */ +@ConstantInitializer public final class robot implements Module { private static final int CLICK_DELAY = 200; @@ -28,9 +30,22 @@ public final class robot implements Module { } private static Robot awtRobot; - + + public static void initConstants() { + Variables.set("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); + Variables.set("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); + Variables.set("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); + Variables.set("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); + Variables.set("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + + Variables.set("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); + Variables.set("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); + Variables.set("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); + } + @Override public void init() { + initConstants(); initialize(); Functions.set("click", convertFunction(robot::click)); @@ -59,16 +74,6 @@ public void init() { Functions.set("fromClipboard", new robot_fromclipboard()); Functions.set("execProcess", new robot_exec(robot_exec.Mode.EXEC)); Functions.set("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); - - Variables.set("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.set("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.set("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.set("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.set("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); - - Variables.set("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); - Variables.set("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); - Variables.set("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); } private static void initialize() { diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/com/annimon/ownlang/lib/modules/std.java index ed4b0465..21cb0dfd 100644 --- a/src/com/annimon/ownlang/lib/modules/std.java +++ b/src/com/annimon/ownlang/lib/modules/std.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.lib.modules; import com.annimon.ownlang.Main; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.*; @@ -8,12 +9,16 @@ * * @author aNNiMON */ +@ConstantInitializer public final class std implements Module { - @Override - public void init() { + public static void initConstants() { Variables.set("ARGS", ArrayValue.of(Main.getOwnlangArgs())); + } + @Override + public void init() { + initConstants(); Functions.set("echo", new std_echo()); Functions.set("readln", new std_readln()); Functions.set("length", new std_length()); diff --git a/src/com/annimon/ownlang/lib/modules/types.java b/src/com/annimon/ownlang/lib/modules/types.java index e399d031..17c3ec74 100644 --- a/src/com/annimon/ownlang/lib/modules/types.java +++ b/src/com/annimon/ownlang/lib/modules/types.java @@ -1,22 +1,27 @@ package com.annimon.ownlang.lib.modules; +import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; /** * * @author aNNiMON */ +@ConstantInitializer public final class types implements Module { - @Override - public void init() { + public static void initConstants() { Variables.set("OBJECT", NumberValue.of(Types.OBJECT)); Variables.set("NUMBER", NumberValue.of(Types.NUMBER)); Variables.set("STRING", NumberValue.of(Types.STRING)); Variables.set("ARRAY", NumberValue.of(Types.ARRAY)); Variables.set("MAP", NumberValue.of(Types.MAP)); Variables.set("FUNCTION", NumberValue.of(Types.FUNCTION)); - + } + + @Override + public void init() { + initConstants(); Functions.set("typeof", args -> NumberValue.of(args[0].type())); Functions.set("string", args -> new StringValue(args[0].asString())); Functions.set("number", args -> NumberValue.of(args[0].asNumber())); From 21cefc70dd92f125a55e81e54330725f4d9ef7bc Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 00:02:41 +0300 Subject: [PATCH 139/448] =?UTF-8?q?=D0=98=D0=BD=D0=B8=D1=86=D0=B8=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BD=D1=82=20=D0=B2=20Java=20FX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/modules/canvasfx.java | 73 +++++++++---------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/src/com/annimon/ownlang/lib/modules/canvasfx.java b/src/com/annimon/ownlang/lib/modules/canvasfx.java index 7f74aac9..c175121c 100644 --- a/src/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/com/annimon/ownlang/lib/modules/canvasfx.java @@ -87,14 +87,6 @@ public EventType getHandler() { } public static void initConstants() { - } - - @Override - public void init() { - initConstants(); - Functions.set("window", new CreateWindow()); - Functions.set("repaint", new Repaint()); - // Color class final Map colors = Arrays.stream(Color.class.getDeclaredFields()) .filter(f -> Modifier.isStatic(f.getModifiers())) @@ -110,87 +102,94 @@ public void init() { colors.put(new StringValue("hsb"), new FunctionValue(new hsbColor())); colors.put(new StringValue("web"), new FunctionValue(new webColor())); Variables.set("Color", new MapValue(colors)); - - Functions.set("BlendEffect", new BlendEffect()); - Functions.set("BloomEffect", new BloomEffect()); - Functions.set("BoxBlurEffect", new BoxBlurEffect()); - Functions.set("ColorAdjustEffect", new ColorAdjustEffect()); - Functions.set("ColorInputEffect", new ColorInputEffect()); - Functions.set("DropShadowEffect", new DropShadowEffect()); - Functions.set("GaussianBlurEffect", new GaussianBlurEffect()); - Functions.set("GlowEffect", new GlowEffect()); - Functions.set("InnerShadowEffect", new InnerShadowEffect()); - Functions.set("LightingEffect", new LightingEffect()); - Functions.set("MotionBlurEffect", new MotionBlurEffect()); - Functions.set("PerspectiveTransformEffect", new PerspectiveTransformEffect()); - Functions.set("ReflectionEffect", new ReflectionEffect()); - Functions.set("SepiaToneEffect", new SepiaToneEffect()); - Functions.set("ShadowEffect", new ShadowEffect()); - - Functions.set("addEventFilter", new addEventFilter()); - Functions.set("addEventHandler", new addEventHandler()); - Functions.set("createImage", new createImage()); final MapValue arcType = new MapValue(ArcType.values().length); for (ArcType value : ArcType.values()) { arcType.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("ArcType", arcType); - + final MapValue fillRule = new MapValue(FillRule.values().length); for (FillRule value : FillRule.values()) { fillRule.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("FillRule", fillRule); - + final MapValue blendMode = new MapValue(BlendMode.values().length); for (BlendMode value : BlendMode.values()) { blendMode.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("BlendMode", blendMode); - + final MapValue lineCap = new MapValue(StrokeLineCap.values().length); for (StrokeLineCap value : StrokeLineCap.values()) { lineCap.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("StrokeLineCap", lineCap); - + final MapValue lineJoin = new MapValue(StrokeLineJoin.values().length); for (StrokeLineJoin value : StrokeLineJoin.values()) { lineJoin.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("StrokeLineJoin", lineJoin); - + final MapValue textAlignment = new MapValue(TextAlignment.values().length); for (TextAlignment value : TextAlignment.values()) { textAlignment.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("TextAlignment", textAlignment); - + final MapValue vPos = new MapValue(VPos.values().length); for (VPos value : VPos.values()) { vPos.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("VPos", vPos); - + final MapValue events = new MapValue(Events.values().length); for (Events value : Events.values()) { events.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("Events", events); - + final MapValue mouseButton = new MapValue(MouseButton.values().length); for (MouseButton value : MouseButton.values()) { mouseButton.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("MouseButton", mouseButton); - + final MapValue keyCodes = new MapValue(KeyCode.values().length); for (KeyCode value : KeyCode.values()) { keyCodes.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } Variables.set("KeyCode", keyCodes); } + + @Override + public void init() { + initConstants(); + Functions.set("window", new CreateWindow()); + Functions.set("repaint", new Repaint()); + + Functions.set("BlendEffect", new BlendEffect()); + Functions.set("BloomEffect", new BloomEffect()); + Functions.set("BoxBlurEffect", new BoxBlurEffect()); + Functions.set("ColorAdjustEffect", new ColorAdjustEffect()); + Functions.set("ColorInputEffect", new ColorInputEffect()); + Functions.set("DropShadowEffect", new DropShadowEffect()); + Functions.set("GaussianBlurEffect", new GaussianBlurEffect()); + Functions.set("GlowEffect", new GlowEffect()); + Functions.set("InnerShadowEffect", new InnerShadowEffect()); + Functions.set("LightingEffect", new LightingEffect()); + Functions.set("MotionBlurEffect", new MotionBlurEffect()); + Functions.set("PerspectiveTransformEffect", new PerspectiveTransformEffect()); + Functions.set("ReflectionEffect", new ReflectionEffect()); + Functions.set("SepiaToneEffect", new SepiaToneEffect()); + Functions.set("ShadowEffect", new ShadowEffect()); + + Functions.set("addEventFilter", new addEventFilter()); + Functions.set("addEventHandler", new addEventHandler()); + Functions.set("createImage", new createImage()); + } private static class ColorValue implements Value { From 4f1068670bdee1dc3b5b2c152453aae304d39370 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 10:39:41 +0300 Subject: [PATCH 140/448] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BA=20=D0=BC=D0=B8=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B0=20Gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 - nbproject/project.properties | 4 +- .../java}/com/annimon/ownlang/Console.java | 0 .../java}/com/annimon/ownlang/Main.java | 0 .../annotations/ConstantInitializer.java | 0 ...onstantInitializerAnnotationProcessor.java | 59 ++++ .../annimon/ownlang/annotations/Modules.java | 0 .../ArgumentsMismatchException.java | 0 .../ownlang/exceptions/LexerException.java | 0 .../OperationIsNotSupportedException.java | 0 .../ownlang/exceptions/ParseException.java | 0 .../exceptions/PatternMatchingException.java | 0 .../ownlang/exceptions/StoppedException.java | 0 .../ownlang/exceptions/TypeException.java | 0 .../exceptions/UnknownFunctionException.java | 0 .../VariableDoesNotExistsException.java | 0 .../com/annimon/ownlang/lib/Arguments.java | 0 .../com/annimon/ownlang/lib/ArrayValue.java | 0 .../com/annimon/ownlang/lib/CallStack.java | 0 .../com/annimon/ownlang/lib/Function.java | 0 .../annimon/ownlang/lib/FunctionValue.java | 0 .../com/annimon/ownlang/lib/Functions.java | 0 .../com/annimon/ownlang/lib/MapValue.java | 0 .../com/annimon/ownlang/lib/NumberValue.java | 0 .../com/annimon/ownlang/lib/StringValue.java | 0 .../java}/com/annimon/ownlang/lib/Types.java | 0 .../ownlang/lib/UserDefinedFunction.java | 0 .../java}/com/annimon/ownlang/lib/Value.java | 0 .../com/annimon/ownlang/lib/Variables.java | 0 .../annimon/ownlang/lib/modules/Module.java | 0 .../annimon/ownlang/lib/modules/canvas.java | 0 .../annimon/ownlang/lib/modules/canvasfx.java | 0 .../com/annimon/ownlang/lib/modules/date.java | 0 .../annimon/ownlang/lib/modules/files.java | 0 .../ownlang/lib/modules/functional.java | 0 .../modules/functions/functional_chain.java | 0 .../modules/functions/functional_combine.java | 0 .../modules/functions/functional_filter.java | 0 .../modules/functions/functional_flatmap.java | 0 .../modules/functions/functional_foreach.java | 0 .../lib/modules/functions/functional_map.java | 0 .../modules/functions/functional_reduce.java | 0 .../modules/functions/functional_sortby.java | 0 .../lib/modules/functions/http_download.java | 0 .../lib/modules/functions/http_http.java | 0 .../lib/modules/functions/http_urlencode.java | 0 .../lib/modules/functions/json_decode.java | 0 .../lib/modules/functions/json_encode.java | 0 .../lib/modules/functions/robot_exec.java | 0 .../functions/robot_fromclipboard.java | 0 .../modules/functions/robot_toclipboard.java | 0 .../modules/functions/std_arrayCombine.java | 0 .../modules/functions/std_arrayKeyExists.java | 0 .../lib/modules/functions/std_arrayKeys.java | 0 .../modules/functions/std_arrayValues.java | 0 .../lib/modules/functions/std_charat.java | 0 .../lib/modules/functions/std_echo.java | 0 .../lib/modules/functions/std_indexof.java | 0 .../lib/modules/functions/std_join.java | 0 .../modules/functions/std_lastindexof.java | 0 .../lib/modules/functions/std_length.java | 0 .../lib/modules/functions/std_newarray.java | 0 .../lib/modules/functions/std_rand.java | 0 .../lib/modules/functions/std_range.java | 0 .../lib/modules/functions/std_readln.java | 0 .../lib/modules/functions/std_replace.java | 0 .../lib/modules/functions/std_replaceall.java | 0 .../modules/functions/std_replacefirst.java | 0 .../lib/modules/functions/std_sleep.java | 0 .../lib/modules/functions/std_sort.java | 0 .../lib/modules/functions/std_split.java | 0 .../lib/modules/functions/std_sprintf.java | 0 .../lib/modules/functions/std_substring.java | 0 .../lib/modules/functions/std_sync.java | 0 .../lib/modules/functions/std_thread.java | 0 .../lib/modules/functions/std_time.java | 0 .../lib/modules/functions/std_tochar.java | 0 .../modules/functions/std_tolowercase.java | 0 .../modules/functions/std_touppercase.java | 0 .../lib/modules/functions/std_trim.java | 0 .../lib/modules/functions/std_try.java | 0 .../com/annimon/ownlang/lib/modules/http.java | 0 .../com/annimon/ownlang/lib/modules/java.java | 0 .../com/annimon/ownlang/lib/modules/jdbc.java | 0 .../com/annimon/ownlang/lib/modules/json.java | 0 .../com/annimon/ownlang/lib/modules/math.java | 0 .../annimon/ownlang/lib/modules/ounit.java | 0 .../ownlang/lib/modules/package-info.java | 0 .../annimon/ownlang/lib/modules/robot.java | 0 .../com/annimon/ownlang/lib/modules/std.java | 0 .../annimon/ownlang/lib/modules/types.java | 0 .../annimon/ownlang/parser/Beautifier.java | 0 .../com/annimon/ownlang/parser/Lexer.java | 0 .../com/annimon/ownlang/parser/Linter.java | 0 .../com/annimon/ownlang/parser/Optimizer.java | 0 .../annimon/ownlang/parser/ParseError.java | 0 .../annimon/ownlang/parser/ParseErrors.java | 0 .../com/annimon/ownlang/parser/Parser.java | 0 .../annimon/ownlang/parser/SourceLoader.java | 0 .../com/annimon/ownlang/parser/Token.java | 0 .../com/annimon/ownlang/parser/TokenType.java | 0 .../ownlang/parser/ast/Accessible.java | 0 .../annimon/ownlang/parser/ast/Argument.java | 0 .../annimon/ownlang/parser/ast/Arguments.java | 0 .../ownlang/parser/ast/ArrayExpression.java | 0 .../parser/ast/AssignmentExpression.java | 0 .../ownlang/parser/ast/BinaryExpression.java | 0 .../ownlang/parser/ast/BlockStatement.java | 0 .../ownlang/parser/ast/BreakStatement.java | 0 .../parser/ast/ConditionalExpression.java | 0 .../parser/ast/ContainerAccessExpression.java | 0 .../ownlang/parser/ast/ContinueStatement.java | 0 .../ast/DestructuringAssignmentStatement.java | 0 .../ownlang/parser/ast/DoWhileStatement.java | 0 .../ownlang/parser/ast/ExprStatement.java | 0 .../ownlang/parser/ast/Expression.java | 0 .../ownlang/parser/ast/ForStatement.java | 0 .../parser/ast/ForeachArrayStatement.java | 0 .../parser/ast/ForeachMapStatement.java | 0 .../parser/ast/FunctionDefineStatement.java | 0 .../ast/FunctionReferenceExpression.java | 0 .../parser/ast/FunctionalExpression.java | 0 .../ownlang/parser/ast/IfStatement.java | 0 .../ownlang/parser/ast/IncludeStatement.java | 0 .../ownlang/parser/ast/InterruptableNode.java | 0 .../ownlang/parser/ast/MapExpression.java | 0 .../ownlang/parser/ast/MatchExpression.java | 0 .../com/annimon/ownlang/parser/ast/Node.java | 0 .../ownlang/parser/ast/PrintStatement.java | 0 .../ownlang/parser/ast/PrintlnStatement.java | 0 .../ownlang/parser/ast/ResultVisitor.java | 0 .../ownlang/parser/ast/ReturnStatement.java | 0 .../annimon/ownlang/parser/ast/Statement.java | 0 .../ownlang/parser/ast/TernaryExpression.java | 0 .../ownlang/parser/ast/UnaryExpression.java | 0 .../ownlang/parser/ast/UseStatement.java | 0 .../ownlang/parser/ast/ValueExpression.java | 0 .../parser/ast/VariableExpression.java | 0 .../annimon/ownlang/parser/ast/Visitor.java | 0 .../ownlang/parser/ast/WhileStatement.java | 0 .../parser/linters/AssignValidator.java | 0 .../DefaultFunctionsOverrideValidator.java | 0 .../ownlang/parser/linters/LintVisitor.java | 0 .../UseWithNonStringValueValidator.java | 0 .../parser/optimization/ConstantFolding.java | 0 .../optimization/ConstantPropagation.java | 0 .../optimization/DeadCodeElimination.java | 0 .../ExpressionSimplification.java | 0 .../optimization/InstructionCombining.java | 0 .../parser/optimization/Optimizable.java | 0 .../optimization/OptimizationVisitor.java | 0 .../optimization/SummaryOptimization.java | 0 .../parser/optimization/VariableInfo.java | 0 .../parser/optimization/VariablesGrabber.java | 0 .../parser/visitors/AbstractVisitor.java | 0 .../parser/visitors/FunctionAdder.java | 0 .../ownlang/parser/visitors/PrintVisitor.java | 317 ++++++++++++++++++ .../parser/visitors/VariablePrinter.java | 0 .../ownlang/parser/visitors/VisitorUtils.java | 0 .../ownlang/utils/ModulesInfoCreator.java | 0 .../ownlang/utils/TimeMeasurement.java | 0 .../ownlang/parser/LexerBenchmarkTest.java | 0 .../com/annimon/ownlang/parser/LexerTest.java | 0 .../ownlang/parser/ParserBenchmarkTest.java | 0 .../annimon/ownlang/parser/ParserTest.java | 0 .../annimon/ownlang/parser/ProgramsTest.java | 0 .../annimon/ownlang/parser/ast/ASTHelper.java | 0 .../parser/ast/OperatorExpressionTest.java | 0 .../parser/ast/ValueExpressionTest.java | 0 .../parser/ast/VariableExpressionTest.java | 0 {test => src/test/java}/interop/Data.java | 0 .../expressions/assignmentExpression.own | 0 .../expressions/binaryExpressionOnNumbers.own | 0 .../expressions/binaryExpressionOnStrings.own | 0 .../resources/expressions/binaryUnaryExpr.own | 0 .../expressions/functionReference.own | 0 .../expressions/unaryExpressionOnStrings.own | 0 .../resources/expressions/varFuncSameName.own | 0 .../resources/modules/date/compareDates.own | 0 .../resources/modules/date/dateFormat.own | 0 .../resources/modules/date/dateParse.own | 0 .../java}/resources/modules/date/newDate.own | 0 .../java}/resources/modules/files/files.own | 0 .../resources/modules/functional/chain.own | 0 .../java}/resources/modules/java/classes.own | 0 .../java}/resources/modules/std/range.own | 0 .../test/java}/resources/other/recursion.own | 0 .../test/java}/resources/other/scope.own | 0 .../test/java}/resources/other/types.own | 0 189 files changed, 378 insertions(+), 7 deletions(-) rename src/{ => main/java}/com/annimon/ownlang/Console.java (100%) rename src/{ => main/java}/com/annimon/ownlang/Main.java (100%) rename src/{ => main/java}/com/annimon/ownlang/annotations/ConstantInitializer.java (100%) create mode 100644 src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java rename src/{ => main/java}/com/annimon/ownlang/annotations/Modules.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/LexerException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/ParseException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/PatternMatchingException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/StoppedException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/TypeException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/UnknownFunctionException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/Arguments.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/ArrayValue.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/CallStack.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/Function.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/FunctionValue.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/Functions.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/MapValue.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/NumberValue.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/StringValue.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/Types.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/UserDefinedFunction.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/Value.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/Variables.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/Module.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/canvas.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/canvasfx.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/date.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/files.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functional.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_chain.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_combine.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_filter.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_foreach.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_map.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_reduce.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/functional_sortby.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/http_download.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/http_http.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/http_urlencode.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/json_decode.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/json_encode.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/robot_exec.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_charat.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_echo.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_indexof.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_join.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_length.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_newarray.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_rand.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_range.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_readln.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_replace.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_replaceall.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_sleep.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_sort.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_split.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_sprintf.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_substring.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_sync.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_thread.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_time.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_tochar.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_touppercase.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_trim.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/functions/std_try.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/http.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/java.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/jdbc.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/json.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/math.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/ounit.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/package-info.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/robot.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/std.java (100%) rename src/{ => main/java}/com/annimon/ownlang/lib/modules/types.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/Beautifier.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/Lexer.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/Linter.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/Optimizer.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ParseError.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ParseErrors.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/Parser.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/SourceLoader.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/Token.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/TokenType.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Accessible.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Argument.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Arguments.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ArrayExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/AssignmentExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/BinaryExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/BlockStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/BreakStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ConditionalExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ContinueStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/DoWhileStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ExprStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Expression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ForStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ForeachMapStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/FunctionalExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/IfStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/IncludeStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/InterruptableNode.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/MapExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/MatchExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Node.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/PrintStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/PrintlnStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ResultVisitor.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ReturnStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Statement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/TernaryExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/UnaryExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/UseStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/ValueExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/VariableExpression.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/Visitor.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/ast/WhileStatement.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/linters/AssignValidator.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/linters/LintVisitor.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/ConstantFolding.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/ConstantPropagation.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/InstructionCombining.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/Optimizable.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/SummaryOptimization.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/VariableInfo.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/optimization/VariablesGrabber.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/visitors/AbstractVisitor.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/visitors/FunctionAdder.java (100%) create mode 100644 src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java rename src/{ => main/java}/com/annimon/ownlang/parser/visitors/VariablePrinter.java (100%) rename src/{ => main/java}/com/annimon/ownlang/parser/visitors/VisitorUtils.java (100%) rename src/{ => main/java}/com/annimon/ownlang/utils/ModulesInfoCreator.java (100%) rename src/{ => main/java}/com/annimon/ownlang/utils/TimeMeasurement.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/LexerBenchmarkTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/LexerTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ParserBenchmarkTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ParserTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ProgramsTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ast/ASTHelper.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ast/ValueExpressionTest.java (100%) rename {test => src/test/java}/com/annimon/ownlang/parser/ast/VariableExpressionTest.java (100%) rename {test => src/test/java}/interop/Data.java (100%) rename {test => src/test/java}/resources/expressions/assignmentExpression.own (100%) rename {test => src/test/java}/resources/expressions/binaryExpressionOnNumbers.own (100%) rename {test => src/test/java}/resources/expressions/binaryExpressionOnStrings.own (100%) rename {test => src/test/java}/resources/expressions/binaryUnaryExpr.own (100%) rename {test => src/test/java}/resources/expressions/functionReference.own (100%) rename {test => src/test/java}/resources/expressions/unaryExpressionOnStrings.own (100%) rename {test => src/test/java}/resources/expressions/varFuncSameName.own (100%) rename {test => src/test/java}/resources/modules/date/compareDates.own (100%) rename {test => src/test/java}/resources/modules/date/dateFormat.own (100%) rename {test => src/test/java}/resources/modules/date/dateParse.own (100%) rename {test => src/test/java}/resources/modules/date/newDate.own (100%) rename {test => src/test/java}/resources/modules/files/files.own (100%) rename {test => src/test/java}/resources/modules/functional/chain.own (100%) rename {test => src/test/java}/resources/modules/java/classes.own (100%) rename {test => src/test/java}/resources/modules/std/range.own (100%) rename {test => src/test/java}/resources/other/recursion.own (100%) rename {test => src/test/java}/resources/other/scope.own (100%) rename {test => src/test/java}/resources/other/types.own (100%) diff --git a/build.gradle b/build.gradle index 0758e119..478a3ae5 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,6 @@ sourceCompatibility = '1.8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' project.ext.mainClass = 'com.annimon.ownlang.Main' -sourceSets { - main.java.srcDir "src/" - test.java.srcDir "test/" -} - dependencies{ compile fileTree(dir: 'libs', include: '*.jar') } diff --git a/nbproject/project.properties b/nbproject/project.properties index 1fdb04e5..993fecb2 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -81,5 +81,5 @@ run.test.classpath=\ ${javac.test.classpath}:\ ${build.test.classes.dir} source.encoding=UTF-8 -src.dir=src -test.src.dir=test +src.dir=src/main/java +test.src.dir=src/test/java diff --git a/src/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java similarity index 100% rename from src/com/annimon/ownlang/Console.java rename to src/main/java/com/annimon/ownlang/Console.java diff --git a/src/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java similarity index 100% rename from src/com/annimon/ownlang/Main.java rename to src/main/java/com/annimon/ownlang/Main.java diff --git a/src/com/annimon/ownlang/annotations/ConstantInitializer.java b/src/main/java/com/annimon/ownlang/annotations/ConstantInitializer.java similarity index 100% rename from src/com/annimon/ownlang/annotations/ConstantInitializer.java rename to src/main/java/com/annimon/ownlang/annotations/ConstantInitializer.java diff --git a/src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java b/src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java new file mode 100644 index 00000000..3293f44d --- /dev/null +++ b/src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java @@ -0,0 +1,59 @@ +package com.annimon.ownlang.annotations; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Optional; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.util.SimpleTypeVisitor6; +import javax.tools.Diagnostic.Kind; + +@SupportedAnnotationTypes("com.annimon.ownlang.annotations.ConstantInitializer") +@SupportedSourceVersion(SourceVersion.RELEASE_6) +public class ConstantInitializerAnnotationProcessor extends AbstractProcessor { + + // https://github.com/corgrath/abandoned-Requires-Static-Method-Annotation + private static final String METHOD_NAME = "initConstants"; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + Set elements = roundEnv.getElementsAnnotatedWith(ConstantInitializer.class); + for (Element element : elements) { + final Optional result = element.getEnclosedElements().stream() + .filter(e -> e.getKind() == ElementKind.METHOD) + .filter(m -> m.getSimpleName().contentEquals(METHOD_NAME)) + .filter(m -> m.getModifiers().containsAll(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC))) + .filter(m -> m.asType().accept(visitor, null)) + .findFirst(); + if (result.isPresent()) { + showError(element); + return true; + } + } + return true; + } + + private void showError(Element element) { + final String message = String.format("Class %s requires a method" + + " `public static void %s() {}`", element.getSimpleName(), METHOD_NAME); + processingEnv.getMessager().printMessage(Kind.ERROR, message, element); + } + + private final SimpleTypeVisitor6 visitor = new SimpleTypeVisitor6() { + @Override + public Boolean visitExecutable(ExecutableType t, Void v) { + if (t.getReturnType().getKind() != TypeKind.VOID) return false; + if (!t.getParameterTypes().isEmpty()) return false; + return true; + } + }; +} diff --git a/src/com/annimon/ownlang/annotations/Modules.java b/src/main/java/com/annimon/ownlang/annotations/Modules.java similarity index 100% rename from src/com/annimon/ownlang/annotations/Modules.java rename to src/main/java/com/annimon/ownlang/annotations/Modules.java diff --git a/src/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java rename to src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java diff --git a/src/com/annimon/ownlang/exceptions/LexerException.java b/src/main/java/com/annimon/ownlang/exceptions/LexerException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/LexerException.java rename to src/main/java/com/annimon/ownlang/exceptions/LexerException.java diff --git a/src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java b/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java rename to src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java diff --git a/src/com/annimon/ownlang/exceptions/ParseException.java b/src/main/java/com/annimon/ownlang/exceptions/ParseException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/ParseException.java rename to src/main/java/com/annimon/ownlang/exceptions/ParseException.java diff --git a/src/com/annimon/ownlang/exceptions/PatternMatchingException.java b/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/PatternMatchingException.java rename to src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java diff --git a/src/com/annimon/ownlang/exceptions/StoppedException.java b/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/StoppedException.java rename to src/main/java/com/annimon/ownlang/exceptions/StoppedException.java diff --git a/src/com/annimon/ownlang/exceptions/TypeException.java b/src/main/java/com/annimon/ownlang/exceptions/TypeException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/TypeException.java rename to src/main/java/com/annimon/ownlang/exceptions/TypeException.java diff --git a/src/com/annimon/ownlang/exceptions/UnknownFunctionException.java b/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/UnknownFunctionException.java rename to src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java diff --git a/src/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java similarity index 100% rename from src/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java rename to src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java diff --git a/src/com/annimon/ownlang/lib/Arguments.java b/src/main/java/com/annimon/ownlang/lib/Arguments.java similarity index 100% rename from src/com/annimon/ownlang/lib/Arguments.java rename to src/main/java/com/annimon/ownlang/lib/Arguments.java diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java similarity index 100% rename from src/com/annimon/ownlang/lib/ArrayValue.java rename to src/main/java/com/annimon/ownlang/lib/ArrayValue.java diff --git a/src/com/annimon/ownlang/lib/CallStack.java b/src/main/java/com/annimon/ownlang/lib/CallStack.java similarity index 100% rename from src/com/annimon/ownlang/lib/CallStack.java rename to src/main/java/com/annimon/ownlang/lib/CallStack.java diff --git a/src/com/annimon/ownlang/lib/Function.java b/src/main/java/com/annimon/ownlang/lib/Function.java similarity index 100% rename from src/com/annimon/ownlang/lib/Function.java rename to src/main/java/com/annimon/ownlang/lib/Function.java diff --git a/src/com/annimon/ownlang/lib/FunctionValue.java b/src/main/java/com/annimon/ownlang/lib/FunctionValue.java similarity index 100% rename from src/com/annimon/ownlang/lib/FunctionValue.java rename to src/main/java/com/annimon/ownlang/lib/FunctionValue.java diff --git a/src/com/annimon/ownlang/lib/Functions.java b/src/main/java/com/annimon/ownlang/lib/Functions.java similarity index 100% rename from src/com/annimon/ownlang/lib/Functions.java rename to src/main/java/com/annimon/ownlang/lib/Functions.java diff --git a/src/com/annimon/ownlang/lib/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java similarity index 100% rename from src/com/annimon/ownlang/lib/MapValue.java rename to src/main/java/com/annimon/ownlang/lib/MapValue.java diff --git a/src/com/annimon/ownlang/lib/NumberValue.java b/src/main/java/com/annimon/ownlang/lib/NumberValue.java similarity index 100% rename from src/com/annimon/ownlang/lib/NumberValue.java rename to src/main/java/com/annimon/ownlang/lib/NumberValue.java diff --git a/src/com/annimon/ownlang/lib/StringValue.java b/src/main/java/com/annimon/ownlang/lib/StringValue.java similarity index 100% rename from src/com/annimon/ownlang/lib/StringValue.java rename to src/main/java/com/annimon/ownlang/lib/StringValue.java diff --git a/src/com/annimon/ownlang/lib/Types.java b/src/main/java/com/annimon/ownlang/lib/Types.java similarity index 100% rename from src/com/annimon/ownlang/lib/Types.java rename to src/main/java/com/annimon/ownlang/lib/Types.java diff --git a/src/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java similarity index 100% rename from src/com/annimon/ownlang/lib/UserDefinedFunction.java rename to src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java diff --git a/src/com/annimon/ownlang/lib/Value.java b/src/main/java/com/annimon/ownlang/lib/Value.java similarity index 100% rename from src/com/annimon/ownlang/lib/Value.java rename to src/main/java/com/annimon/ownlang/lib/Value.java diff --git a/src/com/annimon/ownlang/lib/Variables.java b/src/main/java/com/annimon/ownlang/lib/Variables.java similarity index 100% rename from src/com/annimon/ownlang/lib/Variables.java rename to src/main/java/com/annimon/ownlang/lib/Variables.java diff --git a/src/com/annimon/ownlang/lib/modules/Module.java b/src/main/java/com/annimon/ownlang/lib/modules/Module.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/Module.java rename to src/main/java/com/annimon/ownlang/lib/modules/Module.java diff --git a/src/com/annimon/ownlang/lib/modules/canvas.java b/src/main/java/com/annimon/ownlang/lib/modules/canvas.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/canvas.java rename to src/main/java/com/annimon/ownlang/lib/modules/canvas.java diff --git a/src/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/canvasfx.java rename to src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java diff --git a/src/com/annimon/ownlang/lib/modules/date.java b/src/main/java/com/annimon/ownlang/lib/modules/date.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/date.java rename to src/main/java/com/annimon/ownlang/lib/modules/date.java diff --git a/src/com/annimon/ownlang/lib/modules/files.java b/src/main/java/com/annimon/ownlang/lib/modules/files.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/files.java rename to src/main/java/com/annimon/ownlang/lib/modules/files.java diff --git a/src/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/lib/modules/functional.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functional.java rename to src/main/java/com/annimon/ownlang/lib/modules/functional.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_chain.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_chain.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_combine.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_combine.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_filter.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_filter.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_foreach.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_map.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_map.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_reduce.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/functional_sortby.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/functional_sortby.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_download.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_download.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/http_download.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/http_download.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_http.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/http_http.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_urlencode.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/http_urlencode.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/http_urlencode.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/json_decode.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/json_encode.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/robot_exec.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/robot_exec.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_charat.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_charat.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_echo.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_echo.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_indexof.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_indexof.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_join.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_join.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_length.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_length.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_length.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_length.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_newarray.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_newarray.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_newarray.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_newarray.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_rand.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_rand.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_range.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_range.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_range.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_range.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_readln.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_readln.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_replace.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_replace.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_replaceall.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_replaceall.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sleep.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sleep.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_sleep.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_sleep.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sort.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_sort.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_split.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_split.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_sprintf.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_substring.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_substring.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_sync.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sync.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_sync.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_sync.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_thread.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_time.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_time.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_time.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_time.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_tochar.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_tochar.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_touppercase.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_touppercase.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_trim.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_trim.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java diff --git a/src/com/annimon/ownlang/lib/modules/functions/std_try.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_try.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/functions/std_try.java rename to src/main/java/com/annimon/ownlang/lib/modules/functions/std_try.java diff --git a/src/com/annimon/ownlang/lib/modules/http.java b/src/main/java/com/annimon/ownlang/lib/modules/http.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/http.java rename to src/main/java/com/annimon/ownlang/lib/modules/http.java diff --git a/src/com/annimon/ownlang/lib/modules/java.java b/src/main/java/com/annimon/ownlang/lib/modules/java.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/java.java rename to src/main/java/com/annimon/ownlang/lib/modules/java.java diff --git a/src/com/annimon/ownlang/lib/modules/jdbc.java b/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/jdbc.java rename to src/main/java/com/annimon/ownlang/lib/modules/jdbc.java diff --git a/src/com/annimon/ownlang/lib/modules/json.java b/src/main/java/com/annimon/ownlang/lib/modules/json.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/json.java rename to src/main/java/com/annimon/ownlang/lib/modules/json.java diff --git a/src/com/annimon/ownlang/lib/modules/math.java b/src/main/java/com/annimon/ownlang/lib/modules/math.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/math.java rename to src/main/java/com/annimon/ownlang/lib/modules/math.java diff --git a/src/com/annimon/ownlang/lib/modules/ounit.java b/src/main/java/com/annimon/ownlang/lib/modules/ounit.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/ounit.java rename to src/main/java/com/annimon/ownlang/lib/modules/ounit.java diff --git a/src/com/annimon/ownlang/lib/modules/package-info.java b/src/main/java/com/annimon/ownlang/lib/modules/package-info.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/package-info.java rename to src/main/java/com/annimon/ownlang/lib/modules/package-info.java diff --git a/src/com/annimon/ownlang/lib/modules/robot.java b/src/main/java/com/annimon/ownlang/lib/modules/robot.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/robot.java rename to src/main/java/com/annimon/ownlang/lib/modules/robot.java diff --git a/src/com/annimon/ownlang/lib/modules/std.java b/src/main/java/com/annimon/ownlang/lib/modules/std.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/std.java rename to src/main/java/com/annimon/ownlang/lib/modules/std.java diff --git a/src/com/annimon/ownlang/lib/modules/types.java b/src/main/java/com/annimon/ownlang/lib/modules/types.java similarity index 100% rename from src/com/annimon/ownlang/lib/modules/types.java rename to src/main/java/com/annimon/ownlang/lib/modules/types.java diff --git a/src/com/annimon/ownlang/parser/Beautifier.java b/src/main/java/com/annimon/ownlang/parser/Beautifier.java similarity index 100% rename from src/com/annimon/ownlang/parser/Beautifier.java rename to src/main/java/com/annimon/ownlang/parser/Beautifier.java diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java similarity index 100% rename from src/com/annimon/ownlang/parser/Lexer.java rename to src/main/java/com/annimon/ownlang/parser/Lexer.java diff --git a/src/com/annimon/ownlang/parser/Linter.java b/src/main/java/com/annimon/ownlang/parser/Linter.java similarity index 100% rename from src/com/annimon/ownlang/parser/Linter.java rename to src/main/java/com/annimon/ownlang/parser/Linter.java diff --git a/src/com/annimon/ownlang/parser/Optimizer.java b/src/main/java/com/annimon/ownlang/parser/Optimizer.java similarity index 100% rename from src/com/annimon/ownlang/parser/Optimizer.java rename to src/main/java/com/annimon/ownlang/parser/Optimizer.java diff --git a/src/com/annimon/ownlang/parser/ParseError.java b/src/main/java/com/annimon/ownlang/parser/ParseError.java similarity index 100% rename from src/com/annimon/ownlang/parser/ParseError.java rename to src/main/java/com/annimon/ownlang/parser/ParseError.java diff --git a/src/com/annimon/ownlang/parser/ParseErrors.java b/src/main/java/com/annimon/ownlang/parser/ParseErrors.java similarity index 100% rename from src/com/annimon/ownlang/parser/ParseErrors.java rename to src/main/java/com/annimon/ownlang/parser/ParseErrors.java diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java similarity index 100% rename from src/com/annimon/ownlang/parser/Parser.java rename to src/main/java/com/annimon/ownlang/parser/Parser.java diff --git a/src/com/annimon/ownlang/parser/SourceLoader.java b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java similarity index 100% rename from src/com/annimon/ownlang/parser/SourceLoader.java rename to src/main/java/com/annimon/ownlang/parser/SourceLoader.java diff --git a/src/com/annimon/ownlang/parser/Token.java b/src/main/java/com/annimon/ownlang/parser/Token.java similarity index 100% rename from src/com/annimon/ownlang/parser/Token.java rename to src/main/java/com/annimon/ownlang/parser/Token.java diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/main/java/com/annimon/ownlang/parser/TokenType.java similarity index 100% rename from src/com/annimon/ownlang/parser/TokenType.java rename to src/main/java/com/annimon/ownlang/parser/TokenType.java diff --git a/src/com/annimon/ownlang/parser/ast/Accessible.java b/src/main/java/com/annimon/ownlang/parser/ast/Accessible.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Accessible.java rename to src/main/java/com/annimon/ownlang/parser/ast/Accessible.java diff --git a/src/com/annimon/ownlang/parser/ast/Argument.java b/src/main/java/com/annimon/ownlang/parser/ast/Argument.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Argument.java rename to src/main/java/com/annimon/ownlang/parser/ast/Argument.java diff --git a/src/com/annimon/ownlang/parser/ast/Arguments.java b/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Arguments.java rename to src/main/java/com/annimon/ownlang/parser/ast/Arguments.java diff --git a/src/com/annimon/ownlang/parser/ast/ArrayExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ArrayExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/AssignmentExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/AssignmentExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/BinaryExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/BlockStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/BlockStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/BreakStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/BreakStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ConditionalExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/ContinueStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ContinueStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/DoWhileStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/DoWhileStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/ExprStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ExprStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/Expression.java b/src/main/java/com/annimon/ownlang/parser/ast/Expression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Expression.java rename to src/main/java/com/annimon/ownlang/parser/ast/Expression.java diff --git a/src/com/annimon/ownlang/parser/ast/ForStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ForStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/FunctionalExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/IfStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/IfStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/IncludeStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/IncludeStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/InterruptableNode.java b/src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/InterruptableNode.java rename to src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java diff --git a/src/com/annimon/ownlang/parser/ast/MapExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/MapExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/MatchExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/Node.java b/src/main/java/com/annimon/ownlang/parser/ast/Node.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Node.java rename to src/main/java/com/annimon/ownlang/parser/ast/Node.java diff --git a/src/com/annimon/ownlang/parser/ast/PrintStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/PrintStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/PrintlnStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/PrintlnStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/ResultVisitor.java b/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ResultVisitor.java rename to src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java diff --git a/src/com/annimon/ownlang/parser/ast/ReturnStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ReturnStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/Statement.java b/src/main/java/com/annimon/ownlang/parser/ast/Statement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Statement.java rename to src/main/java/com/annimon/ownlang/parser/ast/Statement.java diff --git a/src/com/annimon/ownlang/parser/ast/TernaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/TernaryExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/UnaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/UnaryExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/UseStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java diff --git a/src/com/annimon/ownlang/parser/ast/ValueExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/ValueExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/VariableExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/VariableExpression.java rename to src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/Visitor.java rename to src/main/java/com/annimon/ownlang/parser/ast/Visitor.java diff --git a/src/com/annimon/ownlang/parser/ast/WhileStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java similarity index 100% rename from src/com/annimon/ownlang/parser/ast/WhileStatement.java rename to src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java diff --git a/src/com/annimon/ownlang/parser/linters/AssignValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java similarity index 100% rename from src/com/annimon/ownlang/parser/linters/AssignValidator.java rename to src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java diff --git a/src/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java similarity index 100% rename from src/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java rename to src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java diff --git a/src/com/annimon/ownlang/parser/linters/LintVisitor.java b/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java similarity index 100% rename from src/com/annimon/ownlang/parser/linters/LintVisitor.java rename to src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java diff --git a/src/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java similarity index 100% rename from src/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java rename to src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java diff --git a/src/com/annimon/ownlang/parser/optimization/ConstantFolding.java b/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/ConstantFolding.java rename to src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java diff --git a/src/com/annimon/ownlang/parser/optimization/ConstantPropagation.java b/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/ConstantPropagation.java rename to src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java diff --git a/src/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java b/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java rename to src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java diff --git a/src/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java b/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java rename to src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java diff --git a/src/com/annimon/ownlang/parser/optimization/InstructionCombining.java b/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/InstructionCombining.java rename to src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java diff --git a/src/com/annimon/ownlang/parser/optimization/Optimizable.java b/src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/Optimizable.java rename to src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java diff --git a/src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java rename to src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java diff --git a/src/com/annimon/ownlang/parser/optimization/SummaryOptimization.java b/src/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/SummaryOptimization.java rename to src/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java diff --git a/src/com/annimon/ownlang/parser/optimization/VariableInfo.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/VariableInfo.java rename to src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java diff --git a/src/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java similarity index 100% rename from src/com/annimon/ownlang/parser/optimization/VariablesGrabber.java rename to src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java similarity index 100% rename from src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java rename to src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java diff --git a/src/com/annimon/ownlang/parser/visitors/FunctionAdder.java b/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java similarity index 100% rename from src/com/annimon/ownlang/parser/visitors/FunctionAdder.java rename to src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java new file mode 100644 index 00000000..91f89108 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -0,0 +1,317 @@ +package com.annimon.ownlang.parser.visitors; + +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.parser.ast.*; +import java.util.Iterator; + +public class PrintVisitor implements ResultVisitor { + + private int indent; + + public PrintVisitor() { + indent = -2; + } + + @Override + public StringBuilder visit(ArrayExpression s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(AssignmentExpression s, StringBuilder t) { + newLine(t); + printIndent(t); + ((Node) s.target).accept(this, t); + t.append(' ').append((s.operation == null) ? "" : s.operation); + t.append("= "); + s.expression.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(BinaryExpression s, StringBuilder t) { + s.expr1.accept(this, t); + t.append(' ').append(s.operation).append(' '); + s.expr2.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(BlockStatement s, StringBuilder t) { + if (indent > 0) { + t.append('{'); + } + increaseIndent(); + for (Statement statement : s.statements) { + statement.accept(this, t); + } + decreaseIndent(); + if (indent > 0) { + newLine(t); + t.append('}'); + } + return t; + } + + @Override + public StringBuilder visit(BreakStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("break"); + return t; + } + + @Override + public StringBuilder visit(ConditionalExpression s, StringBuilder t) { + s.expr1.accept(this, t); + t.append(' ').append(s.operation.getName()).append(' '); + s.expr2.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(ContinueStatement s, StringBuilder t) { + printIndent(t); + t.append("continue"); + newLine(t); + return t; + } + + @Override + public StringBuilder visit(DoWhileStatement s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(DestructuringAssignmentStatement s, StringBuilder t) { + printIndent(t); + t.append("extract ("); + final Iterator it = s.variables.iterator(); + if (it.hasNext()) { + String variable = it.next(); + t.append(variable == null ? " " : variable); + while (it.hasNext()) { + variable = it.next(); + t.append(variable == null ? " " : variable); + } + } + t.append(") = "); + s.containerExpression.accept(this, t); + newLine(t); + return t; + } + + @Override + public StringBuilder visit(ForStatement s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(ForeachArrayStatement s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(ForeachMapStatement s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(FunctionDefineStatement s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(FunctionReferenceExpression s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(ExprStatement s, StringBuilder t) { + printIndent(t); + s.expr.accept(this, t); + newLine(t); + return t; + } + + @Override + public StringBuilder visit(FunctionalExpression s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(IfStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("if ("); + s.expression.accept(this, t); + t.append(") "); + increaseIndent(); + s.ifStatement.accept(this, t); + decreaseIndent(); + if (s.elseStatement != null) { + newLine(t); + printIndent(t); + t.append("else "); + increaseIndent(); + s.elseStatement.accept(this, t); + decreaseIndent(); + } + return t; + } + + @Override + public StringBuilder visit(IncludeStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("include "); + s.expression.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(MapExpression s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(MatchExpression s, StringBuilder t) { + return t; + } + + @Override + public StringBuilder visit(PrintStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("print "); + s.expression.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(PrintlnStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("println "); + s.expression.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(ReturnStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("return "); + s.expression.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(TernaryExpression s, StringBuilder t) { + s.condition.accept(this, t); + t.append(" ? "); + s.trueExpr.accept(this, t); + t.append(" : "); + s.falseExpr.accept(this, t); + return t; + } + + @Override + public StringBuilder visit(UnaryExpression s, StringBuilder t) { + switch (s.operation) { + case INCREMENT_POSTFIX: + case DECREMENT_POSTFIX: + s.expr1.accept(this, t); + t.append(s.operation); + default: + t.append(s.operation); + s.expr1.accept(this, t); + } + return t; + } + + @Override + public StringBuilder visit(ValueExpression s, StringBuilder t) { + switch (s.value.type()) { + case Types.STRING: + String str = s.value.raw().toString(); + str = str.replace("\n", "\\n"); + str = str.replace("\t", "\\t"); + t.append('"').append(str).append('"'); + break; + default: + t.append(s.value.raw()); + break; + } + return t; + } + + @Override + public StringBuilder visit(VariableExpression s, StringBuilder t) { + boolean extendedWordVariable = false; + for (char ch : s.name.toCharArray()) { + if (!Character.isLetterOrDigit(ch)) { + extendedWordVariable = true; + break; + } + } + if (extendedWordVariable) { + t.append('`').append(s.name).append('`'); + } else { + t.append(s.name); + } + return t; + } + + @Override + public StringBuilder visit(WhileStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("while ("); + s.condition.accept(this, t); + t.append(") {"); + newLine(t); + increaseIndent(); + s.statement.accept(this, t); + decreaseIndent(); + newLine(t); + t.append('}'); + return t; + } + + @Override + public StringBuilder visit(UseStatement s, StringBuilder t) { + newLine(t); + printIndent(t); + t.append("use "); + s.expression.accept(this, t); + return t; + } + + + private void newLine(StringBuilder t) { + t.append(System.lineSeparator()); + } + + private void printIndent(StringBuilder sb) { + for (int i = 0; i < indent; i++) { + sb.append(' '); + } + } + + private void increaseIndent() { + indent += 2; + } + + private void decreaseIndent() { + // Allow dedent to -2 + if (indent >= 0) + indent -= 2; + } +} diff --git a/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java b/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java similarity index 100% rename from src/com/annimon/ownlang/parser/visitors/VariablePrinter.java rename to src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java diff --git a/src/com/annimon/ownlang/parser/visitors/VisitorUtils.java b/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java similarity index 100% rename from src/com/annimon/ownlang/parser/visitors/VisitorUtils.java rename to src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java diff --git a/src/com/annimon/ownlang/utils/ModulesInfoCreator.java b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java similarity index 100% rename from src/com/annimon/ownlang/utils/ModulesInfoCreator.java rename to src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java diff --git a/src/com/annimon/ownlang/utils/TimeMeasurement.java b/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java similarity index 100% rename from src/com/annimon/ownlang/utils/TimeMeasurement.java rename to src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java diff --git a/test/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/LexerBenchmarkTest.java rename to src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java diff --git a/test/com/annimon/ownlang/parser/LexerTest.java b/src/test/java/com/annimon/ownlang/parser/LexerTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/LexerTest.java rename to src/test/java/com/annimon/ownlang/parser/LexerTest.java diff --git a/test/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/ParserBenchmarkTest.java rename to src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java diff --git a/test/com/annimon/ownlang/parser/ParserTest.java b/src/test/java/com/annimon/ownlang/parser/ParserTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/ParserTest.java rename to src/test/java/com/annimon/ownlang/parser/ParserTest.java diff --git a/test/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/ProgramsTest.java rename to src/test/java/com/annimon/ownlang/parser/ProgramsTest.java diff --git a/test/com/annimon/ownlang/parser/ast/ASTHelper.java b/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java similarity index 100% rename from test/com/annimon/ownlang/parser/ast/ASTHelper.java rename to src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java diff --git a/test/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java rename to src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java diff --git a/test/com/annimon/ownlang/parser/ast/ValueExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/ast/ValueExpressionTest.java rename to src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java diff --git a/test/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java similarity index 100% rename from test/com/annimon/ownlang/parser/ast/VariableExpressionTest.java rename to src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java diff --git a/test/interop/Data.java b/src/test/java/interop/Data.java similarity index 100% rename from test/interop/Data.java rename to src/test/java/interop/Data.java diff --git a/test/resources/expressions/assignmentExpression.own b/src/test/java/resources/expressions/assignmentExpression.own similarity index 100% rename from test/resources/expressions/assignmentExpression.own rename to src/test/java/resources/expressions/assignmentExpression.own diff --git a/test/resources/expressions/binaryExpressionOnNumbers.own b/src/test/java/resources/expressions/binaryExpressionOnNumbers.own similarity index 100% rename from test/resources/expressions/binaryExpressionOnNumbers.own rename to src/test/java/resources/expressions/binaryExpressionOnNumbers.own diff --git a/test/resources/expressions/binaryExpressionOnStrings.own b/src/test/java/resources/expressions/binaryExpressionOnStrings.own similarity index 100% rename from test/resources/expressions/binaryExpressionOnStrings.own rename to src/test/java/resources/expressions/binaryExpressionOnStrings.own diff --git a/test/resources/expressions/binaryUnaryExpr.own b/src/test/java/resources/expressions/binaryUnaryExpr.own similarity index 100% rename from test/resources/expressions/binaryUnaryExpr.own rename to src/test/java/resources/expressions/binaryUnaryExpr.own diff --git a/test/resources/expressions/functionReference.own b/src/test/java/resources/expressions/functionReference.own similarity index 100% rename from test/resources/expressions/functionReference.own rename to src/test/java/resources/expressions/functionReference.own diff --git a/test/resources/expressions/unaryExpressionOnStrings.own b/src/test/java/resources/expressions/unaryExpressionOnStrings.own similarity index 100% rename from test/resources/expressions/unaryExpressionOnStrings.own rename to src/test/java/resources/expressions/unaryExpressionOnStrings.own diff --git a/test/resources/expressions/varFuncSameName.own b/src/test/java/resources/expressions/varFuncSameName.own similarity index 100% rename from test/resources/expressions/varFuncSameName.own rename to src/test/java/resources/expressions/varFuncSameName.own diff --git a/test/resources/modules/date/compareDates.own b/src/test/java/resources/modules/date/compareDates.own similarity index 100% rename from test/resources/modules/date/compareDates.own rename to src/test/java/resources/modules/date/compareDates.own diff --git a/test/resources/modules/date/dateFormat.own b/src/test/java/resources/modules/date/dateFormat.own similarity index 100% rename from test/resources/modules/date/dateFormat.own rename to src/test/java/resources/modules/date/dateFormat.own diff --git a/test/resources/modules/date/dateParse.own b/src/test/java/resources/modules/date/dateParse.own similarity index 100% rename from test/resources/modules/date/dateParse.own rename to src/test/java/resources/modules/date/dateParse.own diff --git a/test/resources/modules/date/newDate.own b/src/test/java/resources/modules/date/newDate.own similarity index 100% rename from test/resources/modules/date/newDate.own rename to src/test/java/resources/modules/date/newDate.own diff --git a/test/resources/modules/files/files.own b/src/test/java/resources/modules/files/files.own similarity index 100% rename from test/resources/modules/files/files.own rename to src/test/java/resources/modules/files/files.own diff --git a/test/resources/modules/functional/chain.own b/src/test/java/resources/modules/functional/chain.own similarity index 100% rename from test/resources/modules/functional/chain.own rename to src/test/java/resources/modules/functional/chain.own diff --git a/test/resources/modules/java/classes.own b/src/test/java/resources/modules/java/classes.own similarity index 100% rename from test/resources/modules/java/classes.own rename to src/test/java/resources/modules/java/classes.own diff --git a/test/resources/modules/std/range.own b/src/test/java/resources/modules/std/range.own similarity index 100% rename from test/resources/modules/std/range.own rename to src/test/java/resources/modules/std/range.own diff --git a/test/resources/other/recursion.own b/src/test/java/resources/other/recursion.own similarity index 100% rename from test/resources/other/recursion.own rename to src/test/java/resources/other/recursion.own diff --git a/test/resources/other/scope.own b/src/test/java/resources/other/scope.own similarity index 100% rename from test/resources/other/scope.own rename to src/test/java/resources/other/scope.own diff --git a/test/resources/other/types.own b/src/test/java/resources/other/types.own similarity index 100% rename from test/resources/other/types.own rename to src/test/java/resources/other/types.own From 7ed3ab9729f659504b8a2584c5551a7f699ec91d Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 11:58:04 +0300 Subject: [PATCH 141/448] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=20Gra?= =?UTF-8?q?dle=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- build.gradle | 44 +- build.xml | 123 --- libs/json-20151123.jar | Bin 48224 -> 0 bytes libs/okhttp-3.1.2.jar | Bin 333776 -> 0 bytes libs/okio-1.6.0.jar | Bin 65928 -> 0 bytes nbproject/build-impl.xml | 1419 --------------------------------- nbproject/genfiles.properties | 8 - nbproject/project.properties | 85 -- nbproject/project.xml | 15 - proguard.properties | 2 + settings.gradle | 1 + 12 files changed, 38 insertions(+), 1661 deletions(-) delete mode 100644 build.xml delete mode 100644 libs/json-20151123.jar delete mode 100644 libs/okhttp-3.1.2.jar delete mode 100644 libs/okio-1.6.0.jar delete mode 100644 nbproject/build-impl.xml delete mode 100644 nbproject/genfiles.properties delete mode 100644 nbproject/project.properties delete mode 100644 nbproject/project.xml create mode 100644 settings.gradle diff --git a/.travis.yml b/.travis.yml index 819f7d05..87c3c798 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,5 +14,5 @@ before_install: - chmod +x gradlew after_success: - - ./gradlew dist + - ./gradlew proguard - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@dist/Own-Programming-Language-Tutorial.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang \ No newline at end of file diff --git a/build.gradle b/build.gradle index 478a3ae5..5f3177b5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,23 @@ -apply plugin: "java" +apply plugin: 'java' sourceCompatibility = '1.8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -project.ext.mainClass = 'com.annimon.ownlang.Main' -dependencies{ - compile fileTree(dir: 'libs', include: '*.jar') +if (!hasProperty('mainClass')) { + ext.mainClass = 'com.annimon.ownlang.Main' +} + +repositories { + jcenter() +} + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'net.sf.proguard:proguard-gradle:5.2.1' + } } task run(dependsOn: classes, type: JavaExec) { @@ -15,23 +27,35 @@ task run(dependsOn: classes, type: JavaExec) { ignoreExitValue = true } +task runOptimizing(dependsOn: classes, type: JavaExec) { + main = project.mainClass + classpath = sourceSets.main.runtimeClasspath + ignoreExitValue = true + args '-o 9 -m -a -f program.own'.split(' ') +} + task dist(dependsOn: classes, type: Jar) { from files(sourceSets.main.output.classesDir) from {configurations.compile.collect {zipTree(it)}} from files(sourceSets.main.resources) libsDirName = "$rootProject.projectDir/dist" - + manifest { attributes 'Main-Class': project.mainClass } } -repositories { - jcenter() +task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) { + configuration "$rootProject.projectDir/proguard.properties" + injars "$rootProject.projectDir/dist/OwnLang.jar" + outjars "$rootProject.projectDir/store/OwnLang.jar" } dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.12' - testCompile group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.12' + compile 'com.squareup.okhttp3:okhttp:3.4.1' + compile 'org.json:json:20160212' + + testCompile 'junit:junit:4.12' + testCompile 'org.openjdk.jmh:jmh-core:1.12' + testCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.12' } diff --git a/build.xml b/build.xml deleted file mode 100644 index 50045cd9..00000000 --- a/build.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - Builds, tests, and runs the project OwnLang. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libs/json-20151123.jar b/libs/json-20151123.jar deleted file mode 100644 index 472b253051df5ee5c3aac9c34a575aaf40fc70c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48224 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 diff --git a/libs/okhttp-3.1.2.jar b/libs/okhttp-3.1.2.jar deleted file mode 100644 index d5d584f6be8c4282019947681a9d7c5f5b0fbb7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 333776 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

*h}miLXW|JDOOjt+fy>W?G*-p`6Oi+o~b9dpBKL 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

W2FbRd^ky@p&UmrH%sRbjC`^ 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

amv;iO+71WUG{H~ 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 diff --git a/libs/okio-1.6.0.jar b/libs/okio-1.6.0.jar deleted file mode 100644 index c87be599f60608338eaed9228e1f39de19ab816c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65928 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set test.src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No tests executed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - Must select one file in the IDE or set profile.class - This target only works when run from inside the NetBeans IDE. - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - - - Must select some files in the IDE or set test.includes - - - - - Must select one file in the IDE or set run.class - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - Must select some files in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - Must select one file in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties deleted file mode 100644 index 9e97b715..00000000 --- a/nbproject/genfiles.properties +++ /dev/null @@ -1,8 +0,0 @@ -build.xml.data.CRC32=48c1b1a1 -build.xml.script.CRC32=dd12e417 -build.xml.stylesheet.CRC32=8064a381@1.78.0.48 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=48c1b1a1 -nbproject/build-impl.xml.script.CRC32=7fd56607 -nbproject/build-impl.xml.stylesheet.CRC32=2b19b096@1.80.0.48 diff --git a/nbproject/project.properties b/nbproject/project.properties deleted file mode 100644 index 993fecb2..00000000 --- a/nbproject/project.properties +++ /dev/null @@ -1,85 +0,0 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.processors.list= -annotation.processing.run.all.processors=true -annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output -application.title=OwnLang -application.vendor=aNNiMON -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.dir=${build.dir}/generated -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -# Uncomment to specify the preferred debugger connection transport: -#debug.transport=dt_socket -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# Files in build.classes.dir which should be excluded from distribution jar -dist.archive.excludes= -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/OwnLang.jar -dist.javadoc.dir=${dist.dir}/javadoc -endorsed.classpath= -excludes= -file.reference.json-20151123.jar=libs/json-20151123.jar -file.reference.okhttp-3.1.2.jar=libs/okhttp-3.1.2.jar -file.reference.okio-1.6.0.jar=libs/okio-1.6.0.jar -includes=** -jar.compress=false -javac.classpath=\ - ${file.reference.json-20151123.jar}:\ - ${file.reference.okhttp-3.1.2.jar}:\ - ${file.reference.okio-1.6.0.jar} -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.external.vm=true -javac.processorpath=\ - ${javac.classpath} -javac.source=1.8 -javac.target=1.8 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir}:\ - ${libs.junit_4.classpath}:\ - ${libs.hamcrest.classpath}:\ - ${libs.JMH_1.12.classpath} -javac.test.processorpath=\ - ${javac.test.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding=${source.encoding} -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle= -main.class=com.annimon.ownlang.Main -manifest.file=manifest.mf -meta.inf.dir=${src.dir}/META-INF -mkdist.disabled=false -platform.active=default_platform -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project. -# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. -# To set system properties for unit tests define test-sys-prop.name=value: -run.jvmargs= -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -source.encoding=UTF-8 -src.dir=src/main/java -test.src.dir=src/test/java diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index 16733a78..00000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - OwnLang - - - - - - - - - diff --git a/proguard.properties b/proguard.properties index aa2842bd..5a0ee201 100644 --- a/proguard.properties +++ b/proguard.properties @@ -1,4 +1,6 @@ -target 1.8 +-libraryjars /lib/rt.jar +-libraryjars /lib/ext/jfxrt.jar -printmapping store/out.map -printusage store/out.txt diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..b699976e --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'OwnLang' From c28aa4004a994c0b2ea6eb1ac1ea976cc1b2521a Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 12:17:05 +0300 Subject: [PATCH 142/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++-- src/test/java/com/annimon/ownlang/parser/ProgramsTest.java | 2 +- .../{java => }/resources/expressions/assignmentExpression.own | 0 .../resources/expressions/binaryExpressionOnNumbers.own | 0 .../resources/expressions/binaryExpressionOnStrings.own | 0 src/test/{java => }/resources/expressions/binaryUnaryExpr.own | 0 .../{java => }/resources/expressions/functionReference.own | 0 .../resources/expressions/unaryExpressionOnStrings.own | 0 src/test/{java => }/resources/expressions/varFuncSameName.own | 0 src/test/{java => }/resources/modules/date/compareDates.own | 0 src/test/{java => }/resources/modules/date/dateFormat.own | 0 src/test/{java => }/resources/modules/date/dateParse.own | 0 src/test/{java => }/resources/modules/date/newDate.own | 0 src/test/{java => }/resources/modules/files/files.own | 0 src/test/{java => }/resources/modules/functional/chain.own | 0 src/test/{java => }/resources/modules/java/classes.own | 0 src/test/{java => }/resources/modules/std/range.own | 0 src/test/{java => }/resources/other/recursion.own | 0 src/test/{java => }/resources/other/scope.own | 0 src/test/{java => }/resources/other/types.own | 0 20 files changed, 3 insertions(+), 3 deletions(-) rename src/test/{java => }/resources/expressions/assignmentExpression.own (100%) rename src/test/{java => }/resources/expressions/binaryExpressionOnNumbers.own (100%) rename src/test/{java => }/resources/expressions/binaryExpressionOnStrings.own (100%) rename src/test/{java => }/resources/expressions/binaryUnaryExpr.own (100%) rename src/test/{java => }/resources/expressions/functionReference.own (100%) rename src/test/{java => }/resources/expressions/unaryExpressionOnStrings.own (100%) rename src/test/{java => }/resources/expressions/varFuncSameName.own (100%) rename src/test/{java => }/resources/modules/date/compareDates.own (100%) rename src/test/{java => }/resources/modules/date/dateFormat.own (100%) rename src/test/{java => }/resources/modules/date/dateParse.own (100%) rename src/test/{java => }/resources/modules/date/newDate.own (100%) rename src/test/{java => }/resources/modules/files/files.own (100%) rename src/test/{java => }/resources/modules/functional/chain.own (100%) rename src/test/{java => }/resources/modules/java/classes.own (100%) rename src/test/{java => }/resources/modules/std/range.own (100%) rename src/test/{java => }/resources/other/recursion.own (100%) rename src/test/{java => }/resources/other/scope.own (100%) rename src/test/{java => }/resources/other/types.own (100%) diff --git a/build.gradle b/build.gradle index 5f3177b5..6bbfc055 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,6 @@ dependencies { compile 'org.json:json:20160212' testCompile 'junit:junit:4.12' - testCompile 'org.openjdk.jmh:jmh-core:1.12' - testCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.12' + testCompile 'org.openjdk.jmh:jmh-core:1.13' + testCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.13' } diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 546e37d5..c8ed605e 100644 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -24,7 +24,7 @@ @RunWith(value = Parameterized.class) public class ProgramsTest { - private static final String RES_DIR = "test/resources"; + private static final String RES_DIR = "src/test/resources"; private final String programPath; diff --git a/src/test/java/resources/expressions/assignmentExpression.own b/src/test/resources/expressions/assignmentExpression.own similarity index 100% rename from src/test/java/resources/expressions/assignmentExpression.own rename to src/test/resources/expressions/assignmentExpression.own diff --git a/src/test/java/resources/expressions/binaryExpressionOnNumbers.own b/src/test/resources/expressions/binaryExpressionOnNumbers.own similarity index 100% rename from src/test/java/resources/expressions/binaryExpressionOnNumbers.own rename to src/test/resources/expressions/binaryExpressionOnNumbers.own diff --git a/src/test/java/resources/expressions/binaryExpressionOnStrings.own b/src/test/resources/expressions/binaryExpressionOnStrings.own similarity index 100% rename from src/test/java/resources/expressions/binaryExpressionOnStrings.own rename to src/test/resources/expressions/binaryExpressionOnStrings.own diff --git a/src/test/java/resources/expressions/binaryUnaryExpr.own b/src/test/resources/expressions/binaryUnaryExpr.own similarity index 100% rename from src/test/java/resources/expressions/binaryUnaryExpr.own rename to src/test/resources/expressions/binaryUnaryExpr.own diff --git a/src/test/java/resources/expressions/functionReference.own b/src/test/resources/expressions/functionReference.own similarity index 100% rename from src/test/java/resources/expressions/functionReference.own rename to src/test/resources/expressions/functionReference.own diff --git a/src/test/java/resources/expressions/unaryExpressionOnStrings.own b/src/test/resources/expressions/unaryExpressionOnStrings.own similarity index 100% rename from src/test/java/resources/expressions/unaryExpressionOnStrings.own rename to src/test/resources/expressions/unaryExpressionOnStrings.own diff --git a/src/test/java/resources/expressions/varFuncSameName.own b/src/test/resources/expressions/varFuncSameName.own similarity index 100% rename from src/test/java/resources/expressions/varFuncSameName.own rename to src/test/resources/expressions/varFuncSameName.own diff --git a/src/test/java/resources/modules/date/compareDates.own b/src/test/resources/modules/date/compareDates.own similarity index 100% rename from src/test/java/resources/modules/date/compareDates.own rename to src/test/resources/modules/date/compareDates.own diff --git a/src/test/java/resources/modules/date/dateFormat.own b/src/test/resources/modules/date/dateFormat.own similarity index 100% rename from src/test/java/resources/modules/date/dateFormat.own rename to src/test/resources/modules/date/dateFormat.own diff --git a/src/test/java/resources/modules/date/dateParse.own b/src/test/resources/modules/date/dateParse.own similarity index 100% rename from src/test/java/resources/modules/date/dateParse.own rename to src/test/resources/modules/date/dateParse.own diff --git a/src/test/java/resources/modules/date/newDate.own b/src/test/resources/modules/date/newDate.own similarity index 100% rename from src/test/java/resources/modules/date/newDate.own rename to src/test/resources/modules/date/newDate.own diff --git a/src/test/java/resources/modules/files/files.own b/src/test/resources/modules/files/files.own similarity index 100% rename from src/test/java/resources/modules/files/files.own rename to src/test/resources/modules/files/files.own diff --git a/src/test/java/resources/modules/functional/chain.own b/src/test/resources/modules/functional/chain.own similarity index 100% rename from src/test/java/resources/modules/functional/chain.own rename to src/test/resources/modules/functional/chain.own diff --git a/src/test/java/resources/modules/java/classes.own b/src/test/resources/modules/java/classes.own similarity index 100% rename from src/test/java/resources/modules/java/classes.own rename to src/test/resources/modules/java/classes.own diff --git a/src/test/java/resources/modules/std/range.own b/src/test/resources/modules/std/range.own similarity index 100% rename from src/test/java/resources/modules/std/range.own rename to src/test/resources/modules/std/range.own diff --git a/src/test/java/resources/other/recursion.own b/src/test/resources/other/recursion.own similarity index 100% rename from src/test/java/resources/other/recursion.own rename to src/test/resources/other/recursion.own diff --git a/src/test/java/resources/other/scope.own b/src/test/resources/other/scope.own similarity index 100% rename from src/test/java/resources/other/scope.own rename to src/test/resources/other/scope.own diff --git a/src/test/java/resources/other/types.own b/src/test/resources/other/types.own similarity index 100% rename from src/test/java/resources/other/types.own rename to src/test/resources/other/types.own From 24b598a0b344b3e3fd09bb6048211abec3f11b25 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 12:34:30 +0300 Subject: [PATCH 143/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20travis.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 87c3c798..cc0f6611 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ before_install: after_success: - ./gradlew proguard - - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@dist/Own-Programming-Language-Tutorial.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang \ No newline at end of file + - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@store/OwnLang.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang \ No newline at end of file From 8077b643bf19aa2c0466ee4ee2c28360f4bd15f9 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 14:01:22 +0300 Subject: [PATCH 144/448] =?UTF-8?q?=D0=A3=D1=81=D0=BA=D0=BE=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/modules/canvas.java | 12 +++++----- .../annimon/ownlang/lib/modules/canvasfx.java | 22 +++++++++---------- .../com/annimon/ownlang/lib/modules/date.java | 8 +++---- .../annimon/ownlang/lib/modules/files.java | 2 +- .../ownlang/lib/modules/functional.java | 2 +- .../com/annimon/ownlang/lib/modules/math.java | 4 ++-- .../annimon/ownlang/lib/modules/robot.java | 16 +++++++------- .../com/annimon/ownlang/lib/modules/std.java | 2 +- .../annimon/ownlang/lib/modules/types.java | 12 +++++----- 9 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvas.java b/src/main/java/com/annimon/ownlang/lib/modules/canvas.java index 7a9fc05c..1f1ca36d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvas.java @@ -32,12 +32,12 @@ public final class canvas implements Module { private static ArrayValue mouseHover; public static void initConstants() { - Variables.set("VK_UP", NumberValue.of(KeyEvent.VK_UP)); - Variables.set("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.set("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.set("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.set("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.set("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + Variables.define("VK_UP", NumberValue.of(KeyEvent.VK_UP)); + Variables.define("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); + Variables.define("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); + Variables.define("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); + Variables.define("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); + Variables.define("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java index c175121c..c8fb30a2 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java @@ -101,67 +101,67 @@ public static void initConstants() { colors.put(new StringValue("rgb"), new FunctionValue(new rgbColor())); colors.put(new StringValue("hsb"), new FunctionValue(new hsbColor())); colors.put(new StringValue("web"), new FunctionValue(new webColor())); - Variables.set("Color", new MapValue(colors)); + Variables.define("Color", new MapValue(colors)); final MapValue arcType = new MapValue(ArcType.values().length); for (ArcType value : ArcType.values()) { arcType.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("ArcType", arcType); + Variables.define("ArcType", arcType); final MapValue fillRule = new MapValue(FillRule.values().length); for (FillRule value : FillRule.values()) { fillRule.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("FillRule", fillRule); + Variables.define("FillRule", fillRule); final MapValue blendMode = new MapValue(BlendMode.values().length); for (BlendMode value : BlendMode.values()) { blendMode.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("BlendMode", blendMode); + Variables.define("BlendMode", blendMode); final MapValue lineCap = new MapValue(StrokeLineCap.values().length); for (StrokeLineCap value : StrokeLineCap.values()) { lineCap.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("StrokeLineCap", lineCap); + Variables.define("StrokeLineCap", lineCap); final MapValue lineJoin = new MapValue(StrokeLineJoin.values().length); for (StrokeLineJoin value : StrokeLineJoin.values()) { lineJoin.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("StrokeLineJoin", lineJoin); + Variables.define("StrokeLineJoin", lineJoin); final MapValue textAlignment = new MapValue(TextAlignment.values().length); for (TextAlignment value : TextAlignment.values()) { textAlignment.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("TextAlignment", textAlignment); + Variables.define("TextAlignment", textAlignment); final MapValue vPos = new MapValue(VPos.values().length); for (VPos value : VPos.values()) { vPos.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("VPos", vPos); + Variables.define("VPos", vPos); final MapValue events = new MapValue(Events.values().length); for (Events value : Events.values()) { events.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("Events", events); + Variables.define("Events", events); final MapValue mouseButton = new MapValue(MouseButton.values().length); for (MouseButton value : MouseButton.values()) { mouseButton.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("MouseButton", mouseButton); + Variables.define("MouseButton", mouseButton); final MapValue keyCodes = new MapValue(KeyCode.values().length); for (KeyCode value : KeyCode.values()) { keyCodes.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); } - Variables.set("KeyCode", keyCodes); + Variables.define("KeyCode", keyCodes); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/date.java b/src/main/java/com/annimon/ownlang/lib/modules/date.java index 73376aa1..a3de9f82 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/date.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/date.java @@ -29,10 +29,10 @@ public final class date implements Module { MILLISECOND = new StringValue("millisecond"); public static void initConstants() { - Variables.set("STYLE_FULL", NumberValue.of(DateFormat.FULL)); - Variables.set("STYLE_LONG", NumberValue.of(DateFormat.LONG)); - Variables.set("STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM)); - Variables.set("STYLE_SHORT", NumberValue.of(DateFormat.SHORT)); + Variables.define("STYLE_FULL", NumberValue.of(DateFormat.FULL)); + Variables.define("STYLE_LONG", NumberValue.of(DateFormat.LONG)); + Variables.define("STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM)); + Variables.define("STYLE_SHORT", NumberValue.of(DateFormat.SHORT)); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/files.java b/src/main/java/com/annimon/ownlang/lib/modules/files.java index 04c1010e..f65d8482 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/files.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/files.java @@ -27,13 +27,13 @@ public final class files implements Module { private static Map files; public static void initConstants() { + Variables.define("FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction())); } @Override public void init() { files = new HashMap<>(); initConstants(); - Variables.set("FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction())); Functions.set("fopen", new fopen()); Functions.set("flush", new flush()); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/lib/modules/functional.java index f3db9c8f..8bedd852 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functional.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functional.java @@ -12,7 +12,7 @@ public final class functional implements Module { public static void initConstants() { - Variables.set("IDENTITY", new FunctionValue(args -> args[0])); + Variables.define("IDENTITY", new FunctionValue(args -> args[0])); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/math.java b/src/main/java/com/annimon/ownlang/lib/modules/math.java index 33b48cb2..826a624b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/math.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/math.java @@ -17,8 +17,8 @@ public final class math implements Module { private static final DoubleFunction doubleToNumber = NumberValue::of; public static void initConstants() { - Variables.set("PI", NumberValue.of(Math.PI)); - Variables.set("E", NumberValue.of(Math.E)); + Variables.define("PI", NumberValue.of(Math.PI)); + Variables.define("E", NumberValue.of(Math.E)); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/robot.java b/src/main/java/com/annimon/ownlang/lib/modules/robot.java index 7e33bbc9..10990c6c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/robot.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/robot.java @@ -32,15 +32,15 @@ public final class robot implements Module { private static Robot awtRobot; public static void initConstants() { - Variables.set("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.set("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.set("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.set("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.set("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + Variables.define("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); + Variables.define("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); + Variables.define("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); + Variables.define("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); + Variables.define("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); - Variables.set("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); - Variables.set("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); - Variables.set("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); + Variables.define("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); + Variables.define("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); + Variables.define("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/std.java b/src/main/java/com/annimon/ownlang/lib/modules/std.java index 21cb0dfd..00be346b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/std.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/std.java @@ -13,7 +13,7 @@ public final class std implements Module { public static void initConstants() { - Variables.set("ARGS", ArrayValue.of(Main.getOwnlangArgs())); + Variables.define("ARGS", ArrayValue.of(Main.getOwnlangArgs())); } @Override diff --git a/src/main/java/com/annimon/ownlang/lib/modules/types.java b/src/main/java/com/annimon/ownlang/lib/modules/types.java index 17c3ec74..f207d7dc 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/types.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/types.java @@ -11,12 +11,12 @@ public final class types implements Module { public static void initConstants() { - Variables.set("OBJECT", NumberValue.of(Types.OBJECT)); - Variables.set("NUMBER", NumberValue.of(Types.NUMBER)); - Variables.set("STRING", NumberValue.of(Types.STRING)); - Variables.set("ARRAY", NumberValue.of(Types.ARRAY)); - Variables.set("MAP", NumberValue.of(Types.MAP)); - Variables.set("FUNCTION", NumberValue.of(Types.FUNCTION)); + Variables.define("OBJECT", NumberValue.of(Types.OBJECT)); + Variables.define("NUMBER", NumberValue.of(Types.NUMBER)); + Variables.define("STRING", NumberValue.of(Types.STRING)); + Variables.define("ARRAY", NumberValue.of(Types.ARRAY)); + Variables.define("MAP", NumberValue.of(Types.MAP)); + Variables.define("FUNCTION", NumberValue.of(Types.FUNCTION)); } @Override From 1842c4c38788a752bf9a7a4dca456b176a2d07c4 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 24 Jul 2016 14:30:55 +0300 Subject: [PATCH 145/448] =?UTF-8?q?=D0=92=D1=81=D1=82=D1=80=D0=B0=D0=B8?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D1=82=20=D0=B8=D0=B7=20=D0=BC=D0=BE=D0=B4=D1=83?= =?UTF-8?q?=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + ...onstantInitializerAnnotationProcessor.java | 59 ------------------- .../annimon/ownlang/lib/modules/canvasfx.java | 6 +- .../ownlang/parser/ast/UseStatement.java | 14 +++++ .../optimization/ConstantPropagation.java | 2 +- .../parser/optimization/VariablesGrabber.java | 40 ++++++++++++- 6 files changed, 58 insertions(+), 64 deletions(-) delete mode 100644 src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java diff --git a/build.gradle b/build.gradle index 6bbfc055..02e87254 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,7 @@ task runOptimizing(dependsOn: classes, type: JavaExec) { main = project.mainClass classpath = sourceSets.main.runtimeClasspath ignoreExitValue = true + // args '-o 9 -m -a -f examples/game/minesweeper.own'.split(' ') args '-o 9 -m -a -f program.own'.split(' ') } diff --git a/src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java b/src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java deleted file mode 100644 index 3293f44d..00000000 --- a/src/main/java/com/annimon/ownlang/annotations/ConstantInitializerAnnotationProcessor.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.annimon.ownlang.annotations; - -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.Optional; -import java.util.Set; -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.annotation.processing.SupportedSourceVersion; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.ExecutableType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.util.SimpleTypeVisitor6; -import javax.tools.Diagnostic.Kind; - -@SupportedAnnotationTypes("com.annimon.ownlang.annotations.ConstantInitializer") -@SupportedSourceVersion(SourceVersion.RELEASE_6) -public class ConstantInitializerAnnotationProcessor extends AbstractProcessor { - - // https://github.com/corgrath/abandoned-Requires-Static-Method-Annotation - private static final String METHOD_NAME = "initConstants"; - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - Set elements = roundEnv.getElementsAnnotatedWith(ConstantInitializer.class); - for (Element element : elements) { - final Optional result = element.getEnclosedElements().stream() - .filter(e -> e.getKind() == ElementKind.METHOD) - .filter(m -> m.getSimpleName().contentEquals(METHOD_NAME)) - .filter(m -> m.getModifiers().containsAll(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC))) - .filter(m -> m.asType().accept(visitor, null)) - .findFirst(); - if (result.isPresent()) { - showError(element); - return true; - } - } - return true; - } - - private void showError(Element element) { - final String message = String.format("Class %s requires a method" - + " `public static void %s() {}`", element.getSimpleName(), METHOD_NAME); - processingEnv.getMessager().printMessage(Kind.ERROR, message, element); - } - - private final SimpleTypeVisitor6 visitor = new SimpleTypeVisitor6() { - @Override - public Boolean visitExecutable(ExecutableType t, Void v) { - if (t.getReturnType().getKind() != TypeKind.VOID) return false; - if (!t.getParameterTypes().isEmpty()) return false; - return true; - } - }; -} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java index c8fb30a2..1b129eb7 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java @@ -207,9 +207,9 @@ public Object raw() { @Override public int asInt() { final int a = (int) (color.getOpacity() * 255) & 0xFF; - final int r = (int) (color.getRed()* 255) & 0xFF; - final int g = (int) (color.getGreen()* 255) & 0xFF; - final int b = (int) (color.getBlue()* 255) & 0xFF; + final int r = (int) (color.getRed() * 255) & 0xFF; + final int g = (int) (color.getGreen() * 255) & 0xFF; + final int b = (int) (color.getBlue() * 255) & 0xFF; return ((a << 24) | (r << 16) | (g << 8) | b); } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 1fcd841a..095d53a0 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.modules.Module; +import java.lang.reflect.Method; /** * @@ -9,6 +10,7 @@ public final class UseStatement extends InterruptableNode implements Statement { private static final String PACKAGE = "com.annimon.ownlang.lib.modules."; + private static final String INIT_CONSTANTS_METHOD = "initConstants"; public final Expression expression; @@ -27,6 +29,18 @@ public void execute() { throw new RuntimeException(ex); } } + + public void loadConstants() { + try { + final String moduleName = expression.eval().asString(); + final Class moduleClass = Class.forName(PACKAGE + moduleName); + final Method method = moduleClass.getMethod(INIT_CONSTANTS_METHOD); + if (method != null) { + method.invoke(this); + } + } catch (Exception ex) { + } + } @Override public void accept(Visitor visitor) { diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java b/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java index f42435c4..7a2b00c6 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java @@ -23,7 +23,7 @@ public ConstantPropagation() { public Node optimize(Node node) { final Map variables = new HashMap<>(); // Find variables - node.accept(new VariablesGrabber(), variables); + node.accept(new VariablesGrabber(true), variables); // Filter only string/number values with 1 modification final Map candidates = new HashMap<>(); for (Map.Entry e : variables.entrySet()) { diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index e980f680..8fab658d 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.Accessible; import com.annimon.ownlang.parser.ast.Argument; import com.annimon.ownlang.parser.ast.AssignmentExpression; @@ -11,6 +13,7 @@ import com.annimon.ownlang.parser.ast.MatchExpression; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.UnaryExpression; +import com.annimon.ownlang.parser.ast.UseStatement; import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.VariableExpression; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; @@ -21,11 +24,25 @@ public class VariablesGrabber extends OptimizationVisitor> { public static Map getInfo(Node node) { + return getInfo(node, false); + } + + public static Map getInfo(Node node, boolean grabModuleConstants) { Map variableInfos = new HashMap<>(); - node.accept(new VariablesGrabber(), variableInfos); + node.accept(new VariablesGrabber(grabModuleConstants), variableInfos); return variableInfos; } + private final boolean grabModuleConstants; + + public VariablesGrabber() { + this(false); + } + + public VariablesGrabber(boolean grabModuleConstants) { + this.grabModuleConstants = grabModuleConstants; + } + @Override public Node visit(AssignmentExpression s, Map t) { if (!isVariable((Node)s.target)) { @@ -99,6 +116,27 @@ public Node visit(UnaryExpression s, Map t) { return super.visit(s, t); } + @Override + public Node visit(UseStatement s, Map t) { + if (grabModuleConstants) { + // To get module variables we need to store current variables, clear all, then load module. + final Map currentVariables = new HashMap<>(Variables.variables()); + Variables.variables().clear(); + if (isValue(s.expression)) { + s.loadConstants(); + } + // Grab module variables + for (Map.Entry entry : Variables.variables().entrySet()) { + final VariableInfo var = variableInfo(t, entry.getKey()); + var.value = entry.getValue(); + t.put(entry.getKey(), var); + } + // Restore previous variables + Variables.variables().putAll(currentVariables); + } + return super.visit(s, t); + } + private VariableInfo variableInfo(Map t, final String variableName) { From 506760a02e04297c1be1c9e4ba3ec54780e77c91 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 25 Jul 2016 17:33:37 +0300 Subject: [PATCH 146/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=20twitch=20?= =?UTF-8?q?tools?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/network/twitch_tools.own | 191 ++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 examples/network/twitch_tools.own diff --git a/examples/network/twitch_tools.own b/examples/network/twitch_tools.own new file mode 100644 index 00000000..f5308703 --- /dev/null +++ b/examples/network/twitch_tools.own @@ -0,0 +1,191 @@ +// Twitch Tools +use "std" +use "math" +use "http" +use "json" +use "date" +use "types" +use "functional" + +match ARGS { + case (): usage() + case ("status", _): status(ARGS[1]) + case ("liveinfo", _): liveInfo(ARGS[1]) + case ("chat", _): chat(ARGS[1]) + case ("list", "past", _): listPastBroadcasts(ARGS[2]) + case ("m3u", "live", _): m3uPlaylistLive(ARGS[2]) + case ("m3u", _): m3uPlaylist(ARGS[1]) + case ("url", "live", _): urlPlaylistLive("source", ARGS[2]) + case ("url", "live", _, _): urlPlaylistLive(ARGS[2], ARGS[3]) + case ("url", _): urlPlaylist("source", ARGS[1]) + case ("url", _, _): urlPlaylist(ARGS[1], ARGS[2]) + case _: usage() +} + +def usage() { + println "Usage: twitch_tools [mode] [args*] + mode: + status [channel] - prints channel status (online or offline) + liveinfo [channel] - prints live stream info + chat [vod id] - shows chat + list past [channel] - prints past broadcasts of channel + m3u [vod id] - gets m3u playlist for past broadcast + m3u live [channel] - gets m3u playlist for live channel + url [vod id] - gets url for past broadcast with source quality + url [quality] [vod id] - gets url for past broadcast with given quality + url live [channel] - gets url for live channel with source quality + url live [quality] [vod id] - gets playlist url for live channel with given quality + " +} + +def status(channel) { + extract (status, ) = getStreamInfo(channel) + print status +} + +def liveInfo(channel) { + extract (status, info) = getStreamInfo(channel) + if (status == "offline") { + println "This channel is offline" + return 0 + } + stream = info.stream + channel = stream.channel + // Stream + print (arrayKeyExists("mature", channel) && channel.mature) ? "[NSFW] " : "" + println channel.status + println "Date: " + formatTzDate(stream.created_at) + println "Game: " + (arrayKeyExists("game", stream) ? stream.game : "unknown") + println "Viewers: " + stream.viewers + println sprintf("Language: %s, broadcaster: %s", channel.language, channel.broadcaster_language) + // Previews + preview = stream.preview + println "Previews:" + for resolution : ["small", "medium", "large"] { + if (!arrayKeyExists(resolution, preview)) continue + println sprintf("%8s %s", resolution, preview[resolution]) + } + template = preview.template + def formatResolution(w, h) = replace(replace(template, "{width}", w), "{height}", h) + println sprintf("%8s %s", "HD", formatResolution(1280, 720)) + println sprintf("%8s %s", "Full HD", formatResolution(1920, 1080)) + + println sprintf("height: %d, %d fps, %d delay", stream.video_height, int(round(stream.average_fps)), stream.delay) +} + +def listPastBroadcasts(channel) { + url = sprintf("https://api.twitch.tv/kraken/channels/%s/videos?limit=100&broadcasts=true", channel) + pastVods = getJsonSync(url) + pastVods = pastVods.videos + for vod : pastVods { + println "=" * 30 + println vod._id + if (arrayKeyExists("title", vod) { + println vod.title + } + println "Date: " + formatTzDate(vod.recorded_at) + println "Game: " + (arrayKeyExists("game", vod) ? vod.game : "unknown") + println "Duration: " + lengthToTime(vod.length) + println "Views: " + vod.views + println "Previews:" + println vod.preview + if (arrayKeyExists("animated_preview", vod) { + println vod.animated_preview + } + for quality : ["chunked", "high", "medium", "low", "mobile"] { + if (!arrayKeyExists(quality, vod.resolutions) + || vod.resolutions[quality] == "0x0") continue + println sprintf("%s: %s %d fps", quality, vod.resolutions[quality], round(vod.fps[quality])) + } + } +} + +def m3uPlaylist(vodId) { + print m3uPlaylistString(vodId) +} +def urlPlaylist(quality, vodId) { + print searchInPlaylist(quality, m3uPlaylistString(vodId)) +} +def m3uPlaylistString(vodId) { + id = (indexOf(vodId, "v") == 0) ? substring(vodId, 1) : vodId + // Get token + url = sprintf("https://api.twitch.tv/api/vods/%s/access_token", id) + token = getJsonSync(url) + // Get playlist + url = sprintf("http://usher.twitch.tv/vod/%s?player=twitchweb&allow_source=true" + + "&nauthsig=%s&nauth=%s", id, token.sig, urlencode(token.token)) + return sync(def(ret) = http(url, ret)) +} + +def m3uPlaylistLive(channel) { + print m3uPlaylistLiveString(channel) +} +def urlPlaylistLive(quality, channel) { + print searchInPlaylist(quality, m3uPlaylistLiveString(channel)) +} +def m3uPlaylistLiveString(channel) { + // Get token + url = sprintf("http://api.twitch.tv/api/channels/%s/access_token", channel) + token = getJsonSync(url) + // Get playlist + url = sprintf("http://usher.twitch.tv/api/channel/hls/%s.m3u8?player=twitchweb" + + "&token=%s&sig=%s&allow_audio_only=true&allow_source=true&type=any&p=%d", + channel, token.token, token.sig, rand(1000000)) + return sync(def(ret) = http(url, ret)) +} + +def chat(vodId) { + id = (indexOf(vodId, "v") == 0) ? substring(vodId, 1) : vodId + url = sprintf("https://api.twitch.tv/kraken/videos/v%s", id) + vodInfo = getJsonSync(url) + if (arrayKeyExists("error", vodInfo)) { + println "Error: " + vodInfo.message + return 0 + } + recordDate = parseTzDate(vodInfo.recorded_at) + startTime = toTimestamp(recordDate) / 1000 + startTime += (3 * 60 * 60) + endTime = long(startTime + ceil(vodInfo.length / 30.0) * 30) + maxIterations = (endTime - startTime) / 30 + for i = 0, i < maxIterations, i++ { + timestamp = startTime + i * 30 + url = sprintf("http://rechat.twitch.tv/rechat-messages?start=%d&video_id=v%s", timestamp, id) + chunksString = sync(def(ret) = http(url, ret)) + chunksObject = jsondecode(chunksString) + for chunk : chunksObject.data { + attr = chunk.attributes + formattedDate = formatDate(newDate(attr.timestamp)) + println sprintf("%s (%s)\n%s\n\n====\n", + attr.tags["display-name"], formattedDate, attr.message) + } + } +} + + +def searchInPlaylist(quality, m3u) { + quality = toLowerCase(quality) + lines = split(m3u, "\n") + found = false + for line : lines { + lower = toLowerCase(line) + if (contains(line, "#EXT-X-MEDIA") && contains(lower, quality)) { + found = true + } else if (found && contains(lower, "http")) { + return line + } + } + return "" +} +def getStreamInfo(channel) { + url = sprintf("https://api.twitch.tv/kraken/streams/%s", channel) + stream = getJsonSync(url) + return try(def() { + arrayKeyExists("_id", stream.stream) + return ["online", stream] + }, def(type, message) = ["offline", stream]) +} +def contains(what, where) = indexOf(what, where) >= 0 +def formatTzDate(str) = formatDate(parseTzDate(str), newFormat("yyyy-MM-dd HH:mm:ss")) +def parseTzDate(str) = parseDate(str, newFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")) +def lengthToTime(len) = sprintf("%02d:%02d:%02d", len / 3600, len / 60 % 60, len % 60) +def getJsonSync(url) = sync(def(ret) = http(url, def(r) = ret(jsondecode(r)) )) \ No newline at end of file From e08f1d3df939dde5fc06cb7704025ab0884aa32e Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 26 Jul 2016 15:26:44 +0300 Subject: [PATCH 147/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20lastIndexOf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/lib/modules/functions/std_lastindexof.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java index 4aec76f0..c5849ccc 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java @@ -10,8 +10,10 @@ public Value execute(Value... args) { final String input = args[0].asString(); final String what = args[1].asString(); - final int index = (args.length == 3) ? args[2].asInt() : 0; - + if (args.length == 2) { + return NumberValue.of(input.lastIndexOf(what)); + } + final int index = args[2].asInt(); return NumberValue.of(input.lastIndexOf(what, index)); } } \ No newline at end of file From 35f859ade349591a8ba1f91e7e1add6e9369cea0 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 28 Jul 2016 12:10:37 +0300 Subject: [PATCH 148/448] =?UTF-8?q?=D0=9F=D1=80=D0=B8=20=D0=BE=D0=BF=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D1=83=D1=87=D0=B8=D1=82=D1=8B=D0=B2=D0=B0=D0=BB=D0=B8=D1=81?= =?UTF-8?q?=D1=8C=20FunctionValue=20=D0=B8=20=D0=B0=D1=80=D0=B3=D1=83?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=BF=D0=BE=20=D1=83=D0=BC=D0=BE=D0=BB=D1=87?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/lib/UserDefinedFunction.java | 4 +- .../optimization/OptimizationVisitor.java | 46 ++++++++++++++++++- .../parser/optimization/VariablesGrabber.java | 26 +++++++---- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index b3c2937c..ab954c3d 100644 --- a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -12,8 +12,8 @@ */ public final class UserDefinedFunction implements Function { - private final Arguments arguments; - private final Statement body; + public final Arguments arguments; + public final Statement body; public UserDefinedFunction(Arguments arguments, Statement body) { this.arguments = arguments; diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index d4cb8187..399bd218 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.ast.*; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; @@ -160,9 +162,12 @@ public Node visit(ForeachMapStatement s, T t) { @Override public Node visit(FunctionDefineStatement s, T t) { + final Arguments newArgs = new Arguments(); + boolean changed = visit(s.arguments, newArgs, t); + final Node body = s.body.accept(this, t); - if (body != s.body) { - return new FunctionDefineStatement(s.name, s.arguments, consumeStatement(body)); + if (changed || body != s.body) { + return new FunctionDefineStatement(s.name, newArgs, consumeStatement(body)); } return s; } @@ -359,6 +364,13 @@ public Node visit(UnaryExpression s, T t) { @Override public Node visit(ValueExpression s, T t) { + if ( (s.value.type() == Types.FUNCTION) && (s.value.raw() instanceof UserDefinedFunction) ) { + final UserDefinedFunction function = (UserDefinedFunction) s.value.raw(); + final UserDefinedFunction accepted = visit(function, t); + if (accepted != function) { + return new ValueExpression(accepted); + } + } return s; } @@ -386,6 +398,36 @@ public Node visit(UseStatement s, T t) { return s; } + public UserDefinedFunction visit(UserDefinedFunction s, T t) { + final Arguments newArgs = new Arguments(); + boolean changed = visit(s.arguments, newArgs, t); + + final Node body = s.body.accept(this, t); + if (changed || body != s.body) { + return new UserDefinedFunction(newArgs, consumeStatement(body)); + } + return s; + } + + + + protected boolean visit(final Arguments in, final Arguments out, T t) { + boolean changed = false; + for (Argument argument : in) { + final Expression valueExpr = argument.getValueExpr(); + if (valueExpr == null) { + out.addRequired(argument.getName()); + } else { + final Node expr = valueExpr.accept(this, t); + if (expr != valueExpr) { + changed = true; + } + out.addOptional(argument.getName(), (Expression) expr); + } + } + return changed; + } + protected Statement consumeStatement(Node node) { if (node instanceof Statement) { return (Statement) node; diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 8fab658d..c1584621 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,12 +1,15 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.Accessible; import com.annimon.ownlang.parser.ast.Argument; +import com.annimon.ownlang.parser.ast.Arguments; import com.annimon.ownlang.parser.ast.AssignmentExpression; import com.annimon.ownlang.parser.ast.ContainerAccessExpression; import com.annimon.ownlang.parser.ast.DestructuringAssignmentStatement; +import com.annimon.ownlang.parser.ast.Expression; import com.annimon.ownlang.parser.ast.ForeachArrayStatement; import com.annimon.ownlang.parser.ast.ForeachMapStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; @@ -81,15 +84,6 @@ public Node visit(ForeachMapStatement s, Map t) { return super.visit(s, t); } - @Override - public Node visit(FunctionDefineStatement s, Map t) { - for (Argument argument : s.arguments) { - final String variableName = argument.getName(); - t.put(variableName, variableInfo(t, variableName)); - } - return super.visit(s, t); - } - @Override public Node visit(MatchExpression s, Map t) { for (MatchExpression.Pattern pattern : s.patterns) { @@ -137,6 +131,20 @@ public Node visit(UseStatement s, Map t) { return super.visit(s, t); } + @Override + protected boolean visit(Arguments in, Arguments out, Map t) { + for (Argument argument : in) { + final String variableName = argument.getName(); + final VariableInfo var = variableInfo(t, variableName); + final Expression expr = argument.getValueExpr(); + if (expr != null && isValue(expr)) { + var.value = ((ValueExpression) expr).value; + } + t.put(variableName, var); + } + return super.visit(in, out, t); + } + private VariableInfo variableInfo(Map t, final String variableName) { From c5bd92cb0f4aa311e8eaaf0a511c04dac31fc232 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 29 Jul 2016 18:43:06 +0300 Subject: [PATCH 149/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20forms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Converters.java | 130 ++++++++++++++ .../annimon/ownlang/lib/modules/forms.java | 91 ++++++++++ .../functions/forms/ComponentValue.java | 162 ++++++++++++++++++ .../modules/functions/forms/Components.java | 52 ++++++ .../functions/forms/ContainerValue.java | 81 +++++++++ .../modules/functions/forms/JButtonValue.java | 38 ++++ .../functions/forms/JComponentValue.java | 21 +++ .../modules/functions/forms/JFrameValue.java | 28 +++ .../modules/functions/forms/JLabelValue.java | 36 ++++ .../modules/functions/forms/JPanelValue.java | 17 ++ .../functions/forms/JTextFieldValue.java | 53 ++++++ .../functions/forms/LayoutManagerValue.java | 14 ++ .../functions/forms/LayoutManagers.java | 100 +++++++++++ 13 files changed, 823 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/lib/Converters.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/forms.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java new file mode 100644 index 00000000..e0dc686a --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -0,0 +1,130 @@ +package com.annimon.ownlang.lib; + +/** + * Wrapper functions and interfaces. + */ +public final class Converters { + + public interface VoidToVoidFunction { + void apply(); + } + + public interface VoidToBooleanFunction { + boolean apply(); + } + + public interface VoidToIntFunction { + int apply(); + } + + public interface VoidToFloatFunction { + float apply(); + } + + public interface VoidToStringFunction { + String apply(); + } + + public interface BooleanToVoidFunction { + void apply(boolean b); + } + + public interface IntToVoidFunction { + void apply(int i); + } + + public interface FloatToVoidFunction { + void apply(float f); + } + + public interface Float4ToVoidFunction { + void apply(float f1, float f2, float f3, float f4); + } + + public interface StringToVoidFunction { + void apply(String s); + } + + + public static FunctionValue voidToVoid(VoidToVoidFunction f) { + return new FunctionValue(args -> { + f.apply(); + return NumberValue.ZERO; + }); + } + + public static FunctionValue voidToBoolean(VoidToBooleanFunction f) { + return new FunctionValue(args -> NumberValue.fromBoolean(f.apply())); + } + + public static FunctionValue voidToInt(VoidToIntFunction f) { + return new FunctionValue(args -> NumberValue.of(f.apply())); + } + + public static FunctionValue voidToFloat(VoidToFloatFunction f) { + return new FunctionValue(args -> NumberValue.of(f.apply())); + } + + public static FunctionValue voidToString(VoidToStringFunction f) { + return new FunctionValue(args -> new StringValue(f.apply())); + } + + public static FunctionValue booleanToVoid(BooleanToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + f.apply(args[0].asInt() != 0); + return NumberValue.ZERO; + }); + } + + public static FunctionValue booleanOptToVoid(BooleanToVoidFunction f) { + return booleanOptToVoid(f, true); + } + public static FunctionValue booleanOptToVoid(BooleanToVoidFunction f, final boolean def) { + return new FunctionValue(args -> { + Arguments.checkOrOr(0, 1, args.length); + f.apply( (args.length == 1) ? (args[0].asInt() != 0) : def ); + return NumberValue.ZERO; + }); + } + + public static FunctionValue intToVoid(IntToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + f.apply(args[0].asInt()); + return NumberValue.ZERO; + }); + } + + public static FunctionValue floatToVoid(FloatToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + f.apply(getNumber(args[0]).floatValue()); + return NumberValue.ZERO; + }); + } + + public static FunctionValue float4ToVoid(Float4ToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(4, args.length); + f.apply(getNumber(args[0]).floatValue(), + getNumber(args[1]).floatValue(), + getNumber(args[2]).floatValue(), + getNumber(args[3]).floatValue()); + return NumberValue.ZERO; + }); + } + + public static FunctionValue stringToVoid(StringToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + f.apply(args[0].asString()); + return NumberValue.ZERO; + }); + } + + public static Number getNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); + return value.asInt(); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/forms.java b/src/main/java/com/annimon/ownlang/lib/modules/forms.java new file mode 100644 index 00000000..04e4a982 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/forms.java @@ -0,0 +1,91 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.annotations.ConstantInitializer; +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.modules.functions.forms.Components; +import com.annimon.ownlang.lib.modules.functions.forms.LayoutManagers; +import java.awt.BorderLayout; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.SwingConstants; + +/** + * + * @author aNNiMON + */ +@ConstantInitializer +public final class forms implements Module { + + public static void initConstants() { + // JFrame constants + Variables.define("DISPOSE_ON_CLOSE", NumberValue.of(JFrame.DISPOSE_ON_CLOSE)); + Variables.define("DO_NOTHING_ON_CLOSE", NumberValue.of(JFrame.DO_NOTHING_ON_CLOSE)); + Variables.define("EXIT_ON_CLOSE", NumberValue.of(JFrame.EXIT_ON_CLOSE)); + Variables.define("HIDE_ON_CLOSE", NumberValue.of(JFrame.HIDE_ON_CLOSE)); + + // SwinfConstants + final MapValue swing = new MapValue(20); + swing.set(new StringValue("BOTTOM"), NumberValue.of(SwingConstants.BOTTOM)); + swing.set(new StringValue("CENTER"), NumberValue.of(SwingConstants.CENTER)); + swing.set(new StringValue("EAST"), NumberValue.of(SwingConstants.EAST)); + swing.set(new StringValue("HORIZONTAL"), NumberValue.of(SwingConstants.HORIZONTAL)); + swing.set(new StringValue("LEADING"), NumberValue.of(SwingConstants.LEADING)); + swing.set(new StringValue("LEFT"), NumberValue.of(SwingConstants.LEFT)); + swing.set(new StringValue("NEXT"), NumberValue.of(SwingConstants.NEXT)); + swing.set(new StringValue("NORTH"), NumberValue.of(SwingConstants.NORTH)); + swing.set(new StringValue("NORTH_EAST"), NumberValue.of(SwingConstants.NORTH_EAST)); + swing.set(new StringValue("NORTH_WEST"), NumberValue.of(SwingConstants.NORTH_WEST)); + swing.set(new StringValue("PREVIOUS"), NumberValue.of(SwingConstants.PREVIOUS)); + swing.set(new StringValue("RIGHT"), NumberValue.of(SwingConstants.RIGHT)); + swing.set(new StringValue("SOUTH"), NumberValue.of(SwingConstants.SOUTH)); + swing.set(new StringValue("SOUTH_EAST"), NumberValue.of(SwingConstants.SOUTH_EAST)); + swing.set(new StringValue("SOUTH_WEST"), NumberValue.of(SwingConstants.SOUTH_WEST)); + swing.set(new StringValue("TOP"), NumberValue.of(SwingConstants.TOP)); + swing.set(new StringValue("TRAILING"), NumberValue.of(SwingConstants.TRAILING)); + swing.set(new StringValue("VERTICAL"), NumberValue.of(SwingConstants.VERTICAL)); + swing.set(new StringValue("WEST"), NumberValue.of(SwingConstants.WEST)); + Variables.define("SwingConstants", swing); + + // LayoutManagers constants + final MapValue border = new MapValue(13); + border.set(new StringValue("AFTER_LAST_LINE"), new StringValue(BorderLayout.AFTER_LAST_LINE)); + border.set(new StringValue("AFTER_LINE_ENDS"), new StringValue(BorderLayout.AFTER_LINE_ENDS)); + border.set(new StringValue("BEFORE_FIRST_LINE"), new StringValue(BorderLayout.BEFORE_FIRST_LINE)); + border.set(new StringValue("BEFORE_LINE_BEGINS"), new StringValue(BorderLayout.BEFORE_LINE_BEGINS)); + border.set(new StringValue("CENTER"), new StringValue(BorderLayout.CENTER)); + border.set(new StringValue("EAST"), new StringValue(BorderLayout.EAST)); + border.set(new StringValue("LINE_END"), new StringValue(BorderLayout.LINE_END)); + border.set(new StringValue("LINE_START"), new StringValue(BorderLayout.LINE_START)); + border.set(new StringValue("NORTH"), new StringValue(BorderLayout.NORTH)); + border.set(new StringValue("PAGE_END"), new StringValue(BorderLayout.PAGE_END)); + border.set(new StringValue("PAGE_START"), new StringValue(BorderLayout.PAGE_START)); + border.set(new StringValue("SOUTH"), new StringValue(BorderLayout.SOUTH)); + border.set(new StringValue("WEST"), new StringValue(BorderLayout.WEST)); + Variables.define("BorderLayout", border); + + final MapValue box = new MapValue(4); + box.set(new StringValue("LINE_AXIS"), NumberValue.of(BoxLayout.LINE_AXIS)); + box.set(new StringValue("PAGE_AXIS"), NumberValue.of(BoxLayout.PAGE_AXIS)); + box.set(new StringValue("X_AXIS"), NumberValue.of(BoxLayout.X_AXIS)); + box.set(new StringValue("Y_AXIS"), NumberValue.of(BoxLayout.Y_AXIS)); + Variables.define("BoxLayout", box); + } + + @Override + public void init() { + initConstants(); + // Components + Functions.set("newButton", Components::newButton); + Functions.set("newLabel", Components::newLabel); + Functions.set("newPanel", Components::newPanel); + Functions.set("newTextField", Components::newTextField); + Functions.set("newWindow", Components::newWindow); + + // LayoutManagers + Functions.set("borderLayout", LayoutManagers::borderLayout); + Functions.set("boxLayout", LayoutManagers::boxLayout); + Functions.set("cardLayout", LayoutManagers::cardLayout); + Functions.set("gridLayout", LayoutManagers::gridLayout); + Functions.set("flowLayout", LayoutManagers::flowLayout); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java new file mode 100644 index 00000000..66f58337 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java @@ -0,0 +1,162 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public abstract class ComponentValue extends MapValue { + + final Component component; + + public ComponentValue(int functionsCount, Component component) { + super(functionsCount + 42); + this.component = component; + init(); + } + + private void init() { + set(new StringValue("onKeyAction"), new FunctionValue(this::addKeyListener)); + set(new StringValue("addKeyListener"), new FunctionValue(this::addKeyListener)); + set(new StringValue("getFocusTraversalKeysEnabled"), voidToBoolean(component::getFocusTraversalKeysEnabled)); + set(new StringValue("getHeight"), voidToInt(component::getHeight)); + set(new StringValue("getIgnoreRepaint"), voidToBoolean(component::getIgnoreRepaint)); + set(new StringValue("getLocation"), new FunctionValue(this::getLocation)); + set(new StringValue("getLocationOnScreen"), new FunctionValue(this::getLocationOnScreen)); + set(new StringValue("getMinimumSize"), dimensionFunction(component::getMinimumSize)); + set(new StringValue("getMaximumSize"), dimensionFunction(component::getMaximumSize)); + set(new StringValue("getName"), voidToString(component::getName)); + set(new StringValue("getPreferredSize"), dimensionFunction(component::getPreferredSize)); + set(new StringValue("getSize"), dimensionFunction(component::getSize)); + set(new StringValue("getWidth"), voidToInt(component::getWidth)); + set(new StringValue("getX"), voidToInt(component::getX)); + set(new StringValue("getY"), voidToInt(component::getY)); + set(new StringValue("hasFocus"), voidToBoolean(component::hasFocus)); + set(new StringValue("invalidate"), voidToVoid(component::invalidate)); + + set(new StringValue("isDisplayable"), voidToBoolean(component::isDisplayable)); + set(new StringValue("isDoubleBuffered"), voidToBoolean(component::isDoubleBuffered)); + set(new StringValue("isEnabled"), voidToBoolean(component::isEnabled)); + set(new StringValue("isFocusOwner"), voidToBoolean(component::isFocusOwner)); + set(new StringValue("isFocusable"), voidToBoolean(component::isFocusable)); + set(new StringValue("isLightweight"), voidToBoolean(component::isLightweight)); + set(new StringValue("isOpaque"), voidToBoolean(component::isOpaque)); + set(new StringValue("isShowing"), voidToBoolean(component::isShowing)); + set(new StringValue("isValid"), voidToBoolean(component::isValid)); + set(new StringValue("isVisible"), voidToBoolean(component::isVisible)); + + set(new StringValue("requestFocus"), voidToVoid(component::requestFocus)); + set(new StringValue("requestFocusInWindow"), voidToBoolean(component::requestFocusInWindow)); + set(new StringValue("repaint"), voidToVoid(component::repaint)); + set(new StringValue("revalidate"), voidToVoid(component::revalidate)); + set(new StringValue("setMaximumSize"), voidDimensionFunction(component::setMaximumSize)); + set(new StringValue("setMinimumSize"), voidDimensionFunction(component::setMinimumSize)); + set(new StringValue("setName"), stringToVoid(component::setName)); + set(new StringValue("setPreferredSize"), voidDimensionFunction(component::setPreferredSize)); + set(new StringValue("setSize"), voidDimensionFunction(component::setSize)); + set(new StringValue("setVisible"), booleanOptToVoid(component::setVisible)); + set(new StringValue("setLocation"), new FunctionValue(this::setLocation)); + set(new StringValue("validate"), voidToVoid(component::validate)); + } + + private Value addKeyListener(Value... args) { + Arguments.check(1, args.length); + final int type = args[0].type(); + if (type != Types.FUNCTION) { + throw new TypeException("Function expected, but found " + Types.typeToString(type)); + } + final Function action = ((FunctionValue) args[0]).getValue(); + component.addKeyListener(new KeyListener() { + + @Override + public void keyTyped(KeyEvent e) { + handleKeyEvent("typed", e); + } + + @Override + public void keyPressed(KeyEvent e) { + handleKeyEvent("pressed", e); + } + + @Override + public void keyReleased(KeyEvent e) { + handleKeyEvent("released", e); + } + + private void handleKeyEvent(String type, final KeyEvent e) { + final MapValue map = new MapValue(15); + map.set(new StringValue("extendedKeyCode"), NumberValue.of(e.getExtendedKeyCode())); + map.set(new StringValue("keyChar"), NumberValue.of(e.getKeyChar())); + map.set(new StringValue("keyCode"), NumberValue.of(e.getKeyCode())); + map.set(new StringValue("keyLocation"), NumberValue.of(e.getKeyLocation())); + map.set(new StringValue("id"), NumberValue.of(e.getID())); + map.set(new StringValue("isActionKey"), NumberValue.fromBoolean(e.isActionKey())); + map.set(new StringValue("isAltDown"), NumberValue.fromBoolean(e.isAltDown())); + map.set(new StringValue("isAltGraphDown"), NumberValue.fromBoolean(e.isAltGraphDown())); + map.set(new StringValue("isConsumed"), NumberValue.fromBoolean(e.isConsumed())); + map.set(new StringValue("isControlDown"), NumberValue.fromBoolean(e.isControlDown())); + map.set(new StringValue("isMetaDown"), NumberValue.fromBoolean(e.isMetaDown())); + map.set(new StringValue("isShiftDown"), NumberValue.fromBoolean(e.isShiftDown())); + map.set(new StringValue("modifiers"), NumberValue.of(e.getModifiers())); + action.execute(new StringValue(type), map); + } + }); + return NumberValue.ZERO; + } + + private Value getLocation(Value... args) { + final Point location = component.getLocation(); + final ArrayValue result = new ArrayValue(2); + result.set(0, NumberValue.of(location.x)); + result.set(1, NumberValue.of(location.y)); + return result; + } + + private Value getLocationOnScreen(Value... args) { + final Point location = component.getLocationOnScreen(); + final ArrayValue result = new ArrayValue(2); + result.set(0, NumberValue.of(location.x)); + result.set(1, NumberValue.of(location.y)); + return result; + } + + private Value setLocation(Value... args) { + Arguments.check(2, args.length); + component.setLocation(args[0].asInt(), args[1].asInt()); + return NumberValue.ZERO; + } + + + + protected static FunctionValue dimensionFunction(Supplier s) { + return new FunctionValue(args -> { + final Dimension dimension = s.get(); + final ArrayValue result = new ArrayValue(2); + result.set(0, NumberValue.of(dimension.getWidth())); + result.set(1, NumberValue.of(dimension.getHeight())); + return result; + }); + } + + protected static FunctionValue voidDimensionFunction(Consumer s) { + return new FunctionValue(args -> { + Arguments.check(2, args.length); + s.accept(new Dimension(args[0].asInt(), args[1].asInt())); + return NumberValue.ZERO; + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java new file mode 100644 index 00000000..6639af93 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Value; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; + +/** + * Functions for working with components. + */ +public final class Components { + + public static Value newWindow(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + String title = (args.length == 1) ? args[0].asString() : ""; + final JFrame frame = new JFrame(title); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + return new JFrameValue(frame); + } + + public static Value newPanel(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + final JPanel panel = new JPanel(); + if (args.length == 1) { + panel.setLayout( ((LayoutManagerValue) args[0]).layout ); + } + return new JPanelValue(panel); + } + + public static Value newButton(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + String text = (args.length == 1) ? args[0].asString() : ""; + return new JButtonValue(new JButton(text)); + } + + public static Value newLabel(Value... args) { + Arguments.checkRange(0, 2, args.length); + String text = (args.length >= 1) ? args[0].asString() : ""; + int align = (args.length == 2) ? args[0].asInt() : SwingConstants.LEADING; + return new JLabelValue(new JLabel(text, align)); + } + + public static Value newTextField(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + String text = (args.length == 1) ? args[0].asString() : ""; + return new JTextFieldValue(new JTextField(text)); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java new file mode 100644 index 00000000..7fc3b361 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java @@ -0,0 +1,81 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.awt.Component; +import java.awt.Container; +import javax.swing.JLabel; + +public abstract class ContainerValue extends ComponentValue { + + final Container container; + + public ContainerValue(int functionsCount, Container container) { + super(functionsCount + 10, container); + this.container = container; + init(); + } + + private void init() { + set(new StringValue("add"), new FunctionValue(this::add)); + set(new StringValue("remove"), new FunctionValue(this::remove)); + set(new StringValue("removeAll"), voidToVoid(container::removeAll)); + set(new StringValue("getAlignmentX"), voidToFloat(container::getAlignmentX)); + set(new StringValue("getAlignmentY"), voidToFloat(container::getAlignmentY)); + set(new StringValue("getComponentCount"), voidToInt(container::getComponentCount)); + set(new StringValue("isFocusCycleRoot"), voidToBoolean(container::isFocusCycleRoot)); + set(new StringValue("isValidateRoot"), voidToBoolean(container::isValidateRoot)); + set(new StringValue("setLayout"), new FunctionValue(this::setLayout)); + } + + private Value add(Value... args) { + Arguments.checkRange(1, 3, args.length); + + final Component newComponent; + if (args[0] instanceof ComponentValue) { + newComponent = ((ComponentValue) args[0]).component; + } else { + newComponent = new JLabel(args[0].asString()); + } + switch (args.length) { + case 1: + container.add(newComponent); + break; + case 2: + if (args[1].type() == Types.NUMBER) { + // add(component, index) + container.add(newComponent, args[1].asInt()); + } else { + // add(component, constraints) + container.add(newComponent, args[1].raw()); + } + break; + case 3: + // add(component, constraints, index) + container.add(newComponent, args[1].raw(), args[2].asInt()); + break; + } + return NumberValue.ZERO; + } + + private Value remove(Value... args) { + Arguments.check(1, args.length); + if (args[0] instanceof JComponentValue) { + container.remove(((JComponentValue) args[0]).component); + } else { + container.remove(args[0].asInt()); + } + return NumberValue.ZERO; + } + + private Value setLayout(Value... args) { + Arguments.check(1, args.length); + container.setLayout(((LayoutManagerValue) args[0]).layout); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java new file mode 100644 index 00000000..0e00c409 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java @@ -0,0 +1,38 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +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.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import javax.swing.JButton; + +public class JButtonValue extends JComponentValue { + + final JButton button; + + public JButtonValue(JButton button) { + super(2, button); + this.button = button; + init(); + } + + private void init() { + set(new StringValue("onClick"), new FunctionValue(this::addActionListener)); + set(new StringValue("addActionListener"), new FunctionValue(this::addActionListener)); + } + + private Value addActionListener(Value... args) { + Arguments.check(1, args.length); + final int type = args[0].type(); + if (type != Types.FUNCTION) { + throw new TypeException("Function expected, but found " + Types.typeToString(type)); + } + final Function action = ((FunctionValue) args[0]).getValue(); + button.addActionListener(e -> action.execute()); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java new file mode 100644 index 00000000..eb5e67e2 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java @@ -0,0 +1,21 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.StringValue; +import javax.swing.JComponent; + +public abstract class JComponentValue extends ContainerValue { + + final JComponent jComponent; + + public JComponentValue(int functionsCount, JComponent jComponent) { + super(functionsCount + 2, jComponent); + this.jComponent = jComponent; + init(); + } + + private void init() { + set(new StringValue("getToolTipText"), voidToString(jComponent::getToolTipText)); + set(new StringValue("setToolTipText"), stringToVoid(jComponent::setToolTipText)); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java new file mode 100644 index 00000000..c41a2cf2 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java @@ -0,0 +1,28 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.StringValue; +import javax.swing.JFrame; + +public class JFrameValue extends ContainerValue { + + final JFrame frame; + + public JFrameValue(JFrame frame) { + super(9, frame); + this.frame = frame; + init(); + } + + private void init() { + set(new StringValue("dispose"), voidToVoid(frame::dispose)); + set(new StringValue("getTitle"), voidToString(frame::getTitle)); + set(new StringValue("getDefaultCloseOperation"), voidToInt(frame::getDefaultCloseOperation)); + set(new StringValue("pack"), voidToVoid(frame::pack)); + set(new StringValue("setAlwaysOnTop"), booleanOptToVoid(frame::setAlwaysOnTop)); + set(new StringValue("setDefaultCloseOperation"), intToVoid(frame::setDefaultCloseOperation)); + set(new StringValue("setLocationByPlatform"), booleanOptToVoid(frame::setLocationByPlatform)); + set(new StringValue("setResizable"), booleanOptToVoid(frame::setResizable)); + set(new StringValue("setTitle"), stringToVoid(frame::setTitle)); + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java new file mode 100644 index 00000000..e7a29046 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java @@ -0,0 +1,36 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.StringValue; +import javax.swing.JLabel; + +public class JLabelValue extends JComponentValue { + + final JLabel label; + + public JLabelValue(JLabel label) { + super(17, label); + this.label = label; + init(); + } + + private void init() { + set(new StringValue("getDisplayedMnemonic"), voidToInt(label::getDisplayedMnemonic)); + set(new StringValue("getDisplayedMnemonicIndex"), voidToInt(label::getDisplayedMnemonicIndex)); + set(new StringValue("getHorizontalAlignment"), voidToInt(label::getHorizontalAlignment)); + set(new StringValue("getHorizontalTextPosition"), voidToInt(label::getHorizontalTextPosition)); + set(new StringValue("getIconTextGap"), voidToInt(label::getIconTextGap)); + set(new StringValue("getVerticalAlignment"), voidToInt(label::getVerticalAlignment)); + set(new StringValue("getVerticalTextPosition"), voidToInt(label::getVerticalTextPosition)); + + set(new StringValue("getText"), voidToString(label::getText)); + set(new StringValue("setDisplayedMnemonic"), intToVoid(label::setDisplayedMnemonic)); + set(new StringValue("setDisplayedMnemonicIndex"), intToVoid(label::setDisplayedMnemonicIndex)); + set(new StringValue("setHorizontalAlignment"), intToVoid(label::setHorizontalAlignment)); + set(new StringValue("setHorizontalTextPosition"), intToVoid(label::setHorizontalTextPosition)); + set(new StringValue("setIconTextGap"), intToVoid(label::setIconTextGap)); + set(new StringValue("setVerticalAlignment"), intToVoid(label::setVerticalAlignment)); + set(new StringValue("setVerticalTextPosition"), intToVoid(label::setVerticalTextPosition)); + set(new StringValue("setText"), stringToVoid(label::setText)); + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java new file mode 100644 index 00000000..7e6ad92b --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import javax.swing.JPanel; + +public class JPanelValue extends JComponentValue { + + final JPanel panel; + + public JPanelValue(JPanel panel) { + super(0, panel); + this.panel = panel; + init(); + } + + private void init() { + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java new file mode 100644 index 00000000..8147a266 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java @@ -0,0 +1,53 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import javax.swing.JTextField; + +public class JTextFieldValue extends JComponentValue { + + private final JTextField textField; + + public JTextFieldValue(JTextField textField) { + super(16, textField); + this.textField = textField; + init(); + } + + private void init() { + set(new StringValue("onAction"), new FunctionValue(this::addActionListener)); + set(new StringValue("addActionListener"), new FunctionValue(this::addActionListener)); + set(new StringValue("getCaretPosition"), voidToInt(textField::getCaretPosition)); + set(new StringValue("getColumns"), voidToInt(textField::getColumns)); + set(new StringValue("getHorizontalAlignment"), voidToInt(textField::getHorizontalAlignment)); + set(new StringValue("getSelectionEnd"), voidToInt(textField::getSelectionEnd)); + set(new StringValue("getSelectionStart"), voidToInt(textField::getSelectionStart)); + set(new StringValue("getScrollOffset"), voidToInt(textField::getScrollOffset)); + set(new StringValue("getText"), voidToString(textField::getText)); + set(new StringValue("setCaretPosition"), intToVoid(textField::setCaretPosition)); + set(new StringValue("setColumns"), intToVoid(textField::setColumns)); + set(new StringValue("setHorizontalAlignment"), intToVoid(textField::setHorizontalAlignment)); + set(new StringValue("setScrollOffset"), intToVoid(textField::setScrollOffset)); + set(new StringValue("setSelectionEnd"), intToVoid(textField::setSelectionEnd)); + set(new StringValue("setSelectionStart"), intToVoid(textField::setSelectionStart)); + set(new StringValue("setText"), stringToVoid(textField::setText)); + } + + private Value addActionListener(Value... args) { + Arguments.check(1, args.length); + final int type = args[0].type(); + if (type != Types.FUNCTION) { + throw new TypeException("Function expected, but found " + Types.typeToString(type)); + } + final Function action = ((FunctionValue) args[0]).getValue(); + textField.addActionListener(e -> action.execute()); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java new file mode 100644 index 00000000..3ed2c423 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import com.annimon.ownlang.lib.MapValue; +import java.awt.LayoutManager; + +public class LayoutManagerValue extends MapValue { + + final LayoutManager layout; + + public LayoutManagerValue(LayoutManager layout) { + super(0); + this.layout = layout; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java new file mode 100644 index 00000000..0791d289 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java @@ -0,0 +1,100 @@ +package com.annimon.ownlang.lib.modules.functions.forms; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Value; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import javax.swing.BoxLayout; + +/** + * Functions for working with layout managers. + */ +public final class LayoutManagers { + + public static Value borderLayout(Value... args) { + Arguments.checkOrOr(0, 2, args.length); + int hgap = (args.length == 2) ? args[0].asInt() : 0; + int vgap = (args.length == 2) ? args[1].asInt() : 0; + return new LayoutManagerValue( + new BorderLayout(hgap, vgap) + ); + } + + public static Value boxLayout(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + int axis = (args.length == 2) ? args[1].asInt() : BoxLayout.PAGE_AXIS; + return new LayoutManagerValue( + new BoxLayout(((JPanelValue) args[0]).panel, axis) + ); + } + + public static Value cardLayout(Value... args) { + Arguments.checkOrOr(0, 2, args.length); + int hgap = (args.length == 2) ? args[0].asInt() : 0; + int vgap = (args.length == 2) ? args[1].asInt() : 0; + return new LayoutManagerValue( + new CardLayout(hgap, vgap) + ); + } + + public static Value gridLayout(Value... args) { + Arguments.checkRange(0, 4, args.length); + int rows = 1, cols = 0, hgap = 0, vgap = 0; + switch (args.length) { + case 1: + rows = args[0].asInt(); + break; + case 2: + rows = args[0].asInt(); + cols = args[1].asInt(); + break; + case 3: + rows = args[0].asInt(); + cols = args[1].asInt(); + hgap = args[2].asInt(); + break; + case 4: + rows = args[0].asInt(); + cols = args[1].asInt(); + hgap = args[2].asInt(); + vgap = args[3].asInt(); + break; + } + return new LayoutManagerValue( + new GridLayout(rows, cols, hgap, vgap) + ); + } + + public static Value flowLayout(Value... args) { + Arguments.checkRange(0, 3, args.length); + final int align, hgap, vgap; + switch (args.length) { + case 1: + align = args[0].asInt(); + hgap = 5; + vgap = 5; + break; + case 2: + align = FlowLayout.CENTER; + hgap = args[0].asInt(); + vgap = args[1].asInt(); + break; + case 3: + align = args[0].asInt(); + hgap = args[1].asInt(); + vgap = args[2].asInt(); + break; + default: + align = FlowLayout.CENTER; + hgap = 5; + vgap = 5; + break; + } + + return new LayoutManagerValue( + new FlowLayout(align, hgap, vgap) + ); + } +} From 134e0fe4ca18d05a0423089122b5691bcf4135c6 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 29 Jul 2016 18:49:53 +0300 Subject: [PATCH 150/448] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3:=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20Map?= =?UTF-8?q?Value=20set(String,=20Value)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/MapValue.java | 4 + .../annimon/ownlang/lib/modules/forms.java | 72 ++++++------ .../functions/forms/ComponentValue.java | 108 +++++++++--------- .../functions/forms/ContainerValue.java | 19 ++- .../modules/functions/forms/JButtonValue.java | 5 +- .../functions/forms/JComponentValue.java | 5 +- .../modules/functions/forms/JFrameValue.java | 19 ++- .../modules/functions/forms/JLabelValue.java | 33 +++--- .../functions/forms/JTextFieldValue.java | 33 +++--- 9 files changed, 148 insertions(+), 150 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java index dbf4a632..72b1e879 100644 --- a/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -47,6 +47,10 @@ public boolean containsKey(Value key) { public Value get(Value key) { return map.get(key); } + + public void set(String key, Value value) { + set(new StringValue(key), value); + } public void set(Value key, Value value) { map.put(key, value); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/forms.java b/src/main/java/com/annimon/ownlang/lib/modules/forms.java index 04e4a982..8f2b2686 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/forms.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/forms.java @@ -25,49 +25,49 @@ public static void initConstants() { // SwinfConstants final MapValue swing = new MapValue(20); - swing.set(new StringValue("BOTTOM"), NumberValue.of(SwingConstants.BOTTOM)); - swing.set(new StringValue("CENTER"), NumberValue.of(SwingConstants.CENTER)); - swing.set(new StringValue("EAST"), NumberValue.of(SwingConstants.EAST)); - swing.set(new StringValue("HORIZONTAL"), NumberValue.of(SwingConstants.HORIZONTAL)); - swing.set(new StringValue("LEADING"), NumberValue.of(SwingConstants.LEADING)); - swing.set(new StringValue("LEFT"), NumberValue.of(SwingConstants.LEFT)); - swing.set(new StringValue("NEXT"), NumberValue.of(SwingConstants.NEXT)); - swing.set(new StringValue("NORTH"), NumberValue.of(SwingConstants.NORTH)); - swing.set(new StringValue("NORTH_EAST"), NumberValue.of(SwingConstants.NORTH_EAST)); - swing.set(new StringValue("NORTH_WEST"), NumberValue.of(SwingConstants.NORTH_WEST)); - swing.set(new StringValue("PREVIOUS"), NumberValue.of(SwingConstants.PREVIOUS)); - swing.set(new StringValue("RIGHT"), NumberValue.of(SwingConstants.RIGHT)); - swing.set(new StringValue("SOUTH"), NumberValue.of(SwingConstants.SOUTH)); - swing.set(new StringValue("SOUTH_EAST"), NumberValue.of(SwingConstants.SOUTH_EAST)); - swing.set(new StringValue("SOUTH_WEST"), NumberValue.of(SwingConstants.SOUTH_WEST)); - swing.set(new StringValue("TOP"), NumberValue.of(SwingConstants.TOP)); - swing.set(new StringValue("TRAILING"), NumberValue.of(SwingConstants.TRAILING)); - swing.set(new StringValue("VERTICAL"), NumberValue.of(SwingConstants.VERTICAL)); - swing.set(new StringValue("WEST"), NumberValue.of(SwingConstants.WEST)); + swing.set("BOTTOM", NumberValue.of(SwingConstants.BOTTOM)); + swing.set("CENTER", NumberValue.of(SwingConstants.CENTER)); + swing.set("EAST", NumberValue.of(SwingConstants.EAST)); + swing.set("HORIZONTAL", NumberValue.of(SwingConstants.HORIZONTAL)); + swing.set("LEADING", NumberValue.of(SwingConstants.LEADING)); + swing.set("LEFT", NumberValue.of(SwingConstants.LEFT)); + swing.set("NEXT", NumberValue.of(SwingConstants.NEXT)); + swing.set("NORTH", NumberValue.of(SwingConstants.NORTH)); + swing.set("NORTH_EAST", NumberValue.of(SwingConstants.NORTH_EAST)); + swing.set("NORTH_WEST", NumberValue.of(SwingConstants.NORTH_WEST)); + swing.set("PREVIOUS", NumberValue.of(SwingConstants.PREVIOUS)); + swing.set("RIGHT", NumberValue.of(SwingConstants.RIGHT)); + swing.set("SOUTH", NumberValue.of(SwingConstants.SOUTH)); + swing.set("SOUTH_EAST", NumberValue.of(SwingConstants.SOUTH_EAST)); + swing.set("SOUTH_WEST", NumberValue.of(SwingConstants.SOUTH_WEST)); + swing.set("TOP", NumberValue.of(SwingConstants.TOP)); + swing.set("TRAILING", NumberValue.of(SwingConstants.TRAILING)); + swing.set("VERTICAL", NumberValue.of(SwingConstants.VERTICAL)); + swing.set("WEST", NumberValue.of(SwingConstants.WEST)); Variables.define("SwingConstants", swing); // LayoutManagers constants final MapValue border = new MapValue(13); - border.set(new StringValue("AFTER_LAST_LINE"), new StringValue(BorderLayout.AFTER_LAST_LINE)); - border.set(new StringValue("AFTER_LINE_ENDS"), new StringValue(BorderLayout.AFTER_LINE_ENDS)); - border.set(new StringValue("BEFORE_FIRST_LINE"), new StringValue(BorderLayout.BEFORE_FIRST_LINE)); - border.set(new StringValue("BEFORE_LINE_BEGINS"), new StringValue(BorderLayout.BEFORE_LINE_BEGINS)); - border.set(new StringValue("CENTER"), new StringValue(BorderLayout.CENTER)); - border.set(new StringValue("EAST"), new StringValue(BorderLayout.EAST)); - border.set(new StringValue("LINE_END"), new StringValue(BorderLayout.LINE_END)); - border.set(new StringValue("LINE_START"), new StringValue(BorderLayout.LINE_START)); - border.set(new StringValue("NORTH"), new StringValue(BorderLayout.NORTH)); - border.set(new StringValue("PAGE_END"), new StringValue(BorderLayout.PAGE_END)); - border.set(new StringValue("PAGE_START"), new StringValue(BorderLayout.PAGE_START)); - border.set(new StringValue("SOUTH"), new StringValue(BorderLayout.SOUTH)); - border.set(new StringValue("WEST"), new StringValue(BorderLayout.WEST)); + border.set("AFTER_LAST_LINE", new StringValue(BorderLayout.AFTER_LAST_LINE)); + border.set("AFTER_LINE_ENDS", new StringValue(BorderLayout.AFTER_LINE_ENDS)); + border.set("BEFORE_FIRST_LINE", new StringValue(BorderLayout.BEFORE_FIRST_LINE)); + border.set("BEFORE_LINE_BEGINS", new StringValue(BorderLayout.BEFORE_LINE_BEGINS)); + border.set("CENTER", new StringValue(BorderLayout.CENTER)); + border.set("EAST", new StringValue(BorderLayout.EAST)); + border.set("LINE_END", new StringValue(BorderLayout.LINE_END)); + border.set("LINE_START", new StringValue(BorderLayout.LINE_START)); + border.set("NORTH", new StringValue(BorderLayout.NORTH)); + border.set("PAGE_END", new StringValue(BorderLayout.PAGE_END)); + border.set("PAGE_START", new StringValue(BorderLayout.PAGE_START)); + border.set("SOUTH", new StringValue(BorderLayout.SOUTH)); + border.set("WEST", new StringValue(BorderLayout.WEST)); Variables.define("BorderLayout", border); final MapValue box = new MapValue(4); - box.set(new StringValue("LINE_AXIS"), NumberValue.of(BoxLayout.LINE_AXIS)); - box.set(new StringValue("PAGE_AXIS"), NumberValue.of(BoxLayout.PAGE_AXIS)); - box.set(new StringValue("X_AXIS"), NumberValue.of(BoxLayout.X_AXIS)); - box.set(new StringValue("Y_AXIS"), NumberValue.of(BoxLayout.Y_AXIS)); + box.set("LINE_AXIS", NumberValue.of(BoxLayout.LINE_AXIS)); + box.set("PAGE_AXIS", NumberValue.of(BoxLayout.PAGE_AXIS)); + box.set("X_AXIS", NumberValue.of(BoxLayout.X_AXIS)); + box.set("Y_AXIS", NumberValue.of(BoxLayout.Y_AXIS)); Variables.define("BoxLayout", box); } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java index 66f58337..3fcf8aea 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java @@ -30,47 +30,47 @@ public ComponentValue(int functionsCount, Component component) { } private void init() { - set(new StringValue("onKeyAction"), new FunctionValue(this::addKeyListener)); - set(new StringValue("addKeyListener"), new FunctionValue(this::addKeyListener)); - set(new StringValue("getFocusTraversalKeysEnabled"), voidToBoolean(component::getFocusTraversalKeysEnabled)); - set(new StringValue("getHeight"), voidToInt(component::getHeight)); - set(new StringValue("getIgnoreRepaint"), voidToBoolean(component::getIgnoreRepaint)); - set(new StringValue("getLocation"), new FunctionValue(this::getLocation)); - set(new StringValue("getLocationOnScreen"), new FunctionValue(this::getLocationOnScreen)); - set(new StringValue("getMinimumSize"), dimensionFunction(component::getMinimumSize)); - set(new StringValue("getMaximumSize"), dimensionFunction(component::getMaximumSize)); - set(new StringValue("getName"), voidToString(component::getName)); - set(new StringValue("getPreferredSize"), dimensionFunction(component::getPreferredSize)); - set(new StringValue("getSize"), dimensionFunction(component::getSize)); - set(new StringValue("getWidth"), voidToInt(component::getWidth)); - set(new StringValue("getX"), voidToInt(component::getX)); - set(new StringValue("getY"), voidToInt(component::getY)); - set(new StringValue("hasFocus"), voidToBoolean(component::hasFocus)); - set(new StringValue("invalidate"), voidToVoid(component::invalidate)); - - set(new StringValue("isDisplayable"), voidToBoolean(component::isDisplayable)); - set(new StringValue("isDoubleBuffered"), voidToBoolean(component::isDoubleBuffered)); - set(new StringValue("isEnabled"), voidToBoolean(component::isEnabled)); - set(new StringValue("isFocusOwner"), voidToBoolean(component::isFocusOwner)); - set(new StringValue("isFocusable"), voidToBoolean(component::isFocusable)); - set(new StringValue("isLightweight"), voidToBoolean(component::isLightweight)); - set(new StringValue("isOpaque"), voidToBoolean(component::isOpaque)); - set(new StringValue("isShowing"), voidToBoolean(component::isShowing)); - set(new StringValue("isValid"), voidToBoolean(component::isValid)); - set(new StringValue("isVisible"), voidToBoolean(component::isVisible)); - - set(new StringValue("requestFocus"), voidToVoid(component::requestFocus)); - set(new StringValue("requestFocusInWindow"), voidToBoolean(component::requestFocusInWindow)); - set(new StringValue("repaint"), voidToVoid(component::repaint)); - set(new StringValue("revalidate"), voidToVoid(component::revalidate)); - set(new StringValue("setMaximumSize"), voidDimensionFunction(component::setMaximumSize)); - set(new StringValue("setMinimumSize"), voidDimensionFunction(component::setMinimumSize)); - set(new StringValue("setName"), stringToVoid(component::setName)); - set(new StringValue("setPreferredSize"), voidDimensionFunction(component::setPreferredSize)); - set(new StringValue("setSize"), voidDimensionFunction(component::setSize)); - set(new StringValue("setVisible"), booleanOptToVoid(component::setVisible)); - set(new StringValue("setLocation"), new FunctionValue(this::setLocation)); - set(new StringValue("validate"), voidToVoid(component::validate)); + set("onKeyAction", new FunctionValue(this::addKeyListener)); + set("addKeyListener", new FunctionValue(this::addKeyListener)); + set("getFocusTraversalKeysEnabled", voidToBoolean(component::getFocusTraversalKeysEnabled)); + set("getHeight", voidToInt(component::getHeight)); + set("getIgnoreRepaint", voidToBoolean(component::getIgnoreRepaint)); + set("getLocation", new FunctionValue(this::getLocation)); + set("getLocationOnScreen", new FunctionValue(this::getLocationOnScreen)); + set("getMinimumSize", dimensionFunction(component::getMinimumSize)); + set("getMaximumSize", dimensionFunction(component::getMaximumSize)); + set("getName", voidToString(component::getName)); + set("getPreferredSize", dimensionFunction(component::getPreferredSize)); + set("getSize", dimensionFunction(component::getSize)); + set("getWidth", voidToInt(component::getWidth)); + set("getX", voidToInt(component::getX)); + set("getY", voidToInt(component::getY)); + set("hasFocus", voidToBoolean(component::hasFocus)); + set("invalidate", voidToVoid(component::invalidate)); + + set("isDisplayable", voidToBoolean(component::isDisplayable)); + set("isDoubleBuffered", voidToBoolean(component::isDoubleBuffered)); + set("isEnabled", voidToBoolean(component::isEnabled)); + set("isFocusOwner", voidToBoolean(component::isFocusOwner)); + set("isFocusable", voidToBoolean(component::isFocusable)); + set("isLightweight", voidToBoolean(component::isLightweight)); + set("isOpaque", voidToBoolean(component::isOpaque)); + set("isShowing", voidToBoolean(component::isShowing)); + set("isValid", voidToBoolean(component::isValid)); + set("isVisible", voidToBoolean(component::isVisible)); + + set("requestFocus", voidToVoid(component::requestFocus)); + set("requestFocusInWindow", voidToBoolean(component::requestFocusInWindow)); + set("repaint", voidToVoid(component::repaint)); + set("revalidate", voidToVoid(component::revalidate)); + set("setMaximumSize", voidDimensionFunction(component::setMaximumSize)); + set("setMinimumSize", voidDimensionFunction(component::setMinimumSize)); + set("setName", stringToVoid(component::setName)); + set("setPreferredSize", voidDimensionFunction(component::setPreferredSize)); + set("setSize", voidDimensionFunction(component::setSize)); + set("setVisible", booleanOptToVoid(component::setVisible)); + set("setLocation", new FunctionValue(this::setLocation)); + set("validate", voidToVoid(component::validate)); } private Value addKeyListener(Value... args) { @@ -99,19 +99,19 @@ public void keyReleased(KeyEvent e) { private void handleKeyEvent(String type, final KeyEvent e) { final MapValue map = new MapValue(15); - map.set(new StringValue("extendedKeyCode"), NumberValue.of(e.getExtendedKeyCode())); - map.set(new StringValue("keyChar"), NumberValue.of(e.getKeyChar())); - map.set(new StringValue("keyCode"), NumberValue.of(e.getKeyCode())); - map.set(new StringValue("keyLocation"), NumberValue.of(e.getKeyLocation())); - map.set(new StringValue("id"), NumberValue.of(e.getID())); - map.set(new StringValue("isActionKey"), NumberValue.fromBoolean(e.isActionKey())); - map.set(new StringValue("isAltDown"), NumberValue.fromBoolean(e.isAltDown())); - map.set(new StringValue("isAltGraphDown"), NumberValue.fromBoolean(e.isAltGraphDown())); - map.set(new StringValue("isConsumed"), NumberValue.fromBoolean(e.isConsumed())); - map.set(new StringValue("isControlDown"), NumberValue.fromBoolean(e.isControlDown())); - map.set(new StringValue("isMetaDown"), NumberValue.fromBoolean(e.isMetaDown())); - map.set(new StringValue("isShiftDown"), NumberValue.fromBoolean(e.isShiftDown())); - map.set(new StringValue("modifiers"), NumberValue.of(e.getModifiers())); + map.set("extendedKeyCode", NumberValue.of(e.getExtendedKeyCode())); + map.set("keyChar", NumberValue.of(e.getKeyChar())); + map.set("keyCode", NumberValue.of(e.getKeyCode())); + map.set("keyLocation", NumberValue.of(e.getKeyLocation())); + map.set("id", NumberValue.of(e.getID())); + map.set("isActionKey", NumberValue.fromBoolean(e.isActionKey())); + map.set("isAltDown", NumberValue.fromBoolean(e.isAltDown())); + map.set("isAltGraphDown", NumberValue.fromBoolean(e.isAltGraphDown())); + map.set("isConsumed", NumberValue.fromBoolean(e.isConsumed())); + map.set("isControlDown", NumberValue.fromBoolean(e.isControlDown())); + map.set("isMetaDown", NumberValue.fromBoolean(e.isMetaDown())); + map.set("isShiftDown", NumberValue.fromBoolean(e.isShiftDown())); + map.set("modifiers", NumberValue.of(e.getModifiers())); action.execute(new StringValue(type), map); } }); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java index 7fc3b361..a677b282 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java @@ -4,7 +4,6 @@ import static com.annimon.ownlang.lib.Converters.*; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import java.awt.Component; @@ -22,15 +21,15 @@ public ContainerValue(int functionsCount, Container container) { } private void init() { - set(new StringValue("add"), new FunctionValue(this::add)); - set(new StringValue("remove"), new FunctionValue(this::remove)); - set(new StringValue("removeAll"), voidToVoid(container::removeAll)); - set(new StringValue("getAlignmentX"), voidToFloat(container::getAlignmentX)); - set(new StringValue("getAlignmentY"), voidToFloat(container::getAlignmentY)); - set(new StringValue("getComponentCount"), voidToInt(container::getComponentCount)); - set(new StringValue("isFocusCycleRoot"), voidToBoolean(container::isFocusCycleRoot)); - set(new StringValue("isValidateRoot"), voidToBoolean(container::isValidateRoot)); - set(new StringValue("setLayout"), new FunctionValue(this::setLayout)); + set("add", new FunctionValue(this::add)); + set("remove", new FunctionValue(this::remove)); + set("removeAll", voidToVoid(container::removeAll)); + set("getAlignmentX", voidToFloat(container::getAlignmentX)); + set("getAlignmentY", voidToFloat(container::getAlignmentY)); + set("getComponentCount", voidToInt(container::getComponentCount)); + set("isFocusCycleRoot", voidToBoolean(container::isFocusCycleRoot)); + set("isValidateRoot", voidToBoolean(container::isValidateRoot)); + set("setLayout", new FunctionValue(this::setLayout)); } private Value add(Value... args) { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java index 0e00c409..796c34f1 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java @@ -5,7 +5,6 @@ import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import javax.swing.JButton; @@ -21,8 +20,8 @@ public JButtonValue(JButton button) { } private void init() { - set(new StringValue("onClick"), new FunctionValue(this::addActionListener)); - set(new StringValue("addActionListener"), new FunctionValue(this::addActionListener)); + set("onClick", new FunctionValue(this::addActionListener)); + set("addActionListener", new FunctionValue(this::addActionListener)); } private Value addActionListener(Value... args) { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java index eb5e67e2..f3f04596 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.lib.modules.functions.forms; import static com.annimon.ownlang.lib.Converters.*; -import com.annimon.ownlang.lib.StringValue; import javax.swing.JComponent; public abstract class JComponentValue extends ContainerValue { @@ -15,7 +14,7 @@ public JComponentValue(int functionsCount, JComponent jComponent) { } private void init() { - set(new StringValue("getToolTipText"), voidToString(jComponent::getToolTipText)); - set(new StringValue("setToolTipText"), stringToVoid(jComponent::setToolTipText)); + set("getToolTipText", voidToString(jComponent::getToolTipText)); + set("setToolTipText", stringToVoid(jComponent::setToolTipText)); } } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java index c41a2cf2..747cd6c5 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.lib.modules.functions.forms; import static com.annimon.ownlang.lib.Converters.*; -import com.annimon.ownlang.lib.StringValue; import javax.swing.JFrame; public class JFrameValue extends ContainerValue { @@ -15,14 +14,14 @@ public JFrameValue(JFrame frame) { } private void init() { - set(new StringValue("dispose"), voidToVoid(frame::dispose)); - set(new StringValue("getTitle"), voidToString(frame::getTitle)); - set(new StringValue("getDefaultCloseOperation"), voidToInt(frame::getDefaultCloseOperation)); - set(new StringValue("pack"), voidToVoid(frame::pack)); - set(new StringValue("setAlwaysOnTop"), booleanOptToVoid(frame::setAlwaysOnTop)); - set(new StringValue("setDefaultCloseOperation"), intToVoid(frame::setDefaultCloseOperation)); - set(new StringValue("setLocationByPlatform"), booleanOptToVoid(frame::setLocationByPlatform)); - set(new StringValue("setResizable"), booleanOptToVoid(frame::setResizable)); - set(new StringValue("setTitle"), stringToVoid(frame::setTitle)); + set("dispose", voidToVoid(frame::dispose)); + set("getTitle", voidToString(frame::getTitle)); + set("getDefaultCloseOperation", voidToInt(frame::getDefaultCloseOperation)); + set("pack", voidToVoid(frame::pack)); + set("setAlwaysOnTop", booleanOptToVoid(frame::setAlwaysOnTop)); + set("setDefaultCloseOperation", intToVoid(frame::setDefaultCloseOperation)); + set("setLocationByPlatform", booleanOptToVoid(frame::setLocationByPlatform)); + set("setResizable", booleanOptToVoid(frame::setResizable)); + set("setTitle", stringToVoid(frame::setTitle)); } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java index e7a29046..2256b34a 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.lib.modules.functions.forms; import static com.annimon.ownlang.lib.Converters.*; -import com.annimon.ownlang.lib.StringValue; import javax.swing.JLabel; public class JLabelValue extends JComponentValue { @@ -15,22 +14,22 @@ public JLabelValue(JLabel label) { } private void init() { - set(new StringValue("getDisplayedMnemonic"), voidToInt(label::getDisplayedMnemonic)); - set(new StringValue("getDisplayedMnemonicIndex"), voidToInt(label::getDisplayedMnemonicIndex)); - set(new StringValue("getHorizontalAlignment"), voidToInt(label::getHorizontalAlignment)); - set(new StringValue("getHorizontalTextPosition"), voidToInt(label::getHorizontalTextPosition)); - set(new StringValue("getIconTextGap"), voidToInt(label::getIconTextGap)); - set(new StringValue("getVerticalAlignment"), voidToInt(label::getVerticalAlignment)); - set(new StringValue("getVerticalTextPosition"), voidToInt(label::getVerticalTextPosition)); + set("getDisplayedMnemonic", voidToInt(label::getDisplayedMnemonic)); + set("getDisplayedMnemonicIndex", voidToInt(label::getDisplayedMnemonicIndex)); + set("getHorizontalAlignment", voidToInt(label::getHorizontalAlignment)); + set("getHorizontalTextPosition", voidToInt(label::getHorizontalTextPosition)); + set("getIconTextGap", voidToInt(label::getIconTextGap)); + set("getVerticalAlignment", voidToInt(label::getVerticalAlignment)); + set("getVerticalTextPosition", voidToInt(label::getVerticalTextPosition)); - set(new StringValue("getText"), voidToString(label::getText)); - set(new StringValue("setDisplayedMnemonic"), intToVoid(label::setDisplayedMnemonic)); - set(new StringValue("setDisplayedMnemonicIndex"), intToVoid(label::setDisplayedMnemonicIndex)); - set(new StringValue("setHorizontalAlignment"), intToVoid(label::setHorizontalAlignment)); - set(new StringValue("setHorizontalTextPosition"), intToVoid(label::setHorizontalTextPosition)); - set(new StringValue("setIconTextGap"), intToVoid(label::setIconTextGap)); - set(new StringValue("setVerticalAlignment"), intToVoid(label::setVerticalAlignment)); - set(new StringValue("setVerticalTextPosition"), intToVoid(label::setVerticalTextPosition)); - set(new StringValue("setText"), stringToVoid(label::setText)); + set("getText", voidToString(label::getText)); + set("setDisplayedMnemonic", intToVoid(label::setDisplayedMnemonic)); + set("setDisplayedMnemonicIndex", intToVoid(label::setDisplayedMnemonicIndex)); + set("setHorizontalAlignment", intToVoid(label::setHorizontalAlignment)); + set("setHorizontalTextPosition", intToVoid(label::setHorizontalTextPosition)); + set("setIconTextGap", intToVoid(label::setIconTextGap)); + set("setVerticalAlignment", intToVoid(label::setVerticalAlignment)); + set("setVerticalTextPosition", intToVoid(label::setVerticalTextPosition)); + set("setText", stringToVoid(label::setText)); } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java index 8147a266..94400db6 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java @@ -6,7 +6,6 @@ import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import javax.swing.JTextField; @@ -22,22 +21,22 @@ public JTextFieldValue(JTextField textField) { } private void init() { - set(new StringValue("onAction"), new FunctionValue(this::addActionListener)); - set(new StringValue("addActionListener"), new FunctionValue(this::addActionListener)); - set(new StringValue("getCaretPosition"), voidToInt(textField::getCaretPosition)); - set(new StringValue("getColumns"), voidToInt(textField::getColumns)); - set(new StringValue("getHorizontalAlignment"), voidToInt(textField::getHorizontalAlignment)); - set(new StringValue("getSelectionEnd"), voidToInt(textField::getSelectionEnd)); - set(new StringValue("getSelectionStart"), voidToInt(textField::getSelectionStart)); - set(new StringValue("getScrollOffset"), voidToInt(textField::getScrollOffset)); - set(new StringValue("getText"), voidToString(textField::getText)); - set(new StringValue("setCaretPosition"), intToVoid(textField::setCaretPosition)); - set(new StringValue("setColumns"), intToVoid(textField::setColumns)); - set(new StringValue("setHorizontalAlignment"), intToVoid(textField::setHorizontalAlignment)); - set(new StringValue("setScrollOffset"), intToVoid(textField::setScrollOffset)); - set(new StringValue("setSelectionEnd"), intToVoid(textField::setSelectionEnd)); - set(new StringValue("setSelectionStart"), intToVoid(textField::setSelectionStart)); - set(new StringValue("setText"), stringToVoid(textField::setText)); + set("onAction", new FunctionValue(this::addActionListener)); + set("addActionListener", new FunctionValue(this::addActionListener)); + set("getCaretPosition", voidToInt(textField::getCaretPosition)); + set("getColumns", voidToInt(textField::getColumns)); + set("getHorizontalAlignment", voidToInt(textField::getHorizontalAlignment)); + set("getSelectionEnd", voidToInt(textField::getSelectionEnd)); + set("getSelectionStart", voidToInt(textField::getSelectionStart)); + set("getScrollOffset", voidToInt(textField::getScrollOffset)); + set("getText", voidToString(textField::getText)); + set("setCaretPosition", intToVoid(textField::setCaretPosition)); + set("setColumns", intToVoid(textField::setColumns)); + set("setHorizontalAlignment", intToVoid(textField::setHorizontalAlignment)); + set("setScrollOffset", intToVoid(textField::setScrollOffset)); + set("setSelectionEnd", intToVoid(textField::setSelectionEnd)); + set("setSelectionStart", intToVoid(textField::setSelectionStart)); + set("setText", stringToVoid(textField::setText)); } private Value addActionListener(Value... args) { From adce68e59850c7f1390557df19490ffbbd494475 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 29 Jul 2016 19:29:01 +0300 Subject: [PATCH 151/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/forms/basic.own | 6 +++++ examples/forms/button.own | 11 +++++++++ examples/forms/complicatedForm.own | 38 ++++++++++++++++++++++++++++++ examples/forms/panel.own | 37 +++++++++++++++++++++++++++++ examples/forms/textfield.own | 23 ++++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 examples/forms/basic.own create mode 100644 examples/forms/button.own create mode 100644 examples/forms/complicatedForm.own create mode 100644 examples/forms/panel.own create mode 100644 examples/forms/textfield.own diff --git a/examples/forms/basic.own b/examples/forms/basic.own new file mode 100644 index 00000000..59eb3ee9 --- /dev/null +++ b/examples/forms/basic.own @@ -0,0 +1,6 @@ +use "forms" + +window = newWindow("Basic form example") +window.add("Hello, world") +window.pack() +window.setVisible() \ No newline at end of file diff --git a/examples/forms/button.own b/examples/forms/button.own new file mode 100644 index 00000000..d30f4f92 --- /dev/null +++ b/examples/forms/button.own @@ -0,0 +1,11 @@ +use "forms" + +button = newButton("Click me") +button.onClick(def() { + println "Oh, you clicked me." +}) + +window = newWindow("Button example") +window.add(button) +window.pack() +window.setVisible() \ No newline at end of file diff --git a/examples/forms/complicatedForm.own b/examples/forms/complicatedForm.own new file mode 100644 index 00000000..7796b588 --- /dev/null +++ b/examples/forms/complicatedForm.own @@ -0,0 +1,38 @@ +use "std" +use "forms" + +actionsPanel = newPanel() +actionsPanel.setLayout(boxLayout(actionsPanel, BoxLayout.PAGE_AXIS)) +actionsPanel.add("Actions:") +actionsPanel.add(newButton("Action 1")) +actionsPanel.add(newButton("Action 2")) +actionsPanel.add(newButton("Action 3")) +actionsPanel.add(newButton("Action 4")) + +enterTextLabel = newLabel("Enter a text", SwingConstants.CENTER) + +textField = newTextField() +textField.addKeyListener(def(type, event) { + lengthLabel.setText(length(textField.getText())) +}) + +statusPanel = newPanel() +statusPanel.setLayout(boxLayout(statusPanel, BoxLayout.LINE_AXIS)) +statusPanel.add("Length: ") +lengthLabel = newLabel() +statusPanel.add(lengthLabel) + +mainPanel = newPanel(borderLayout(10, 10)) +mainPanel.setPreferredSize(400, 250) +mainPanel.add(actionsPanel, BorderLayout.WEST) +mainPanel.add(enterTextLabel, BorderLayout.NORTH) +mainPanel.add(textField, BorderLayout.CENTER) +mainPanel.add(statusPanel, BorderLayout.SOUTH) + + +window = newWindow("Complicated Form Example") +window.setMinimumSize(200, 220) +window.setLocationByPlatform() +window.add(mainPanel) +window.pack() +window.setVisible() diff --git a/examples/forms/panel.own b/examples/forms/panel.own new file mode 100644 index 00000000..3f7de8d8 --- /dev/null +++ b/examples/forms/panel.own @@ -0,0 +1,37 @@ +use "forms" + +// Create Panel with BoxLayout +panel = newPanel() +panel.setLayout(boxLayout(panel, BoxLayout.PAGE_AXIS)) +// String label (alias to JLabel) +panel.add("String label") + +// Add label +label = newLabel("Label") +label.setHorizontalAlignment(SwingConstants.CENTER) +panel.add(label) + +// Add text field +textField = newTextField("Some text") +textField.setColumns(20) +panel.add(textField) + +// Add button +button = newButton("Button") +panel.add(button) + +// Add another button +clearBtn = newButton("Clear panel") +clearBtn.onClick(def() { + panel.removeAll() + panel.revalidate() + panel.repaint() +}) +panel.add(clearBtn) + +window = newWindow("Panel Example") +window.setLocation(400, 200) +window.add(panel) +window.pack() +window.setAlwaysOnTop() +window.setVisible() \ No newline at end of file diff --git a/examples/forms/textfield.own b/examples/forms/textfield.own new file mode 100644 index 00000000..33fd1c64 --- /dev/null +++ b/examples/forms/textfield.own @@ -0,0 +1,23 @@ +use "std" +use "forms" + +textField = newTextField("Some text") + +button = newButton("Click me") +button.onClick(def() { + println "TextField text: " + textField.getText() + textField.setText(textField.getText() + " Let's add new line") +}) + +window = newWindow("Text field example") +window.add(textField) +window.add(button, BorderLayout.SOUTH) +window.pack() +window.setLocationByPlatform() +window.setVisible() + +textField.onAction(def() = echo("I am a TextField")) +textField.addKeyListener(def(type, event) { + println sprintf("%s %d %s", + type, event.keyCode, toChar(event.keyChar)) +}) \ No newline at end of file From 1c0686c03af2dfddc77e789b6fed6eba3407eed2 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 10:40:46 +0300 Subject: [PATCH 152/448] =?UTF-8?q?=D0=92=D1=8B=D0=B7=D0=BE=D0=B2=D1=8B=20?= =?UTF-8?q?set=20StringValue=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BD=D0=B0=20set=20String=20=D0=B2=20MapValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/modules/canvasfx.java | 114 +++---- .../lib/modules/functions/http_http.java | 12 +- .../com/annimon/ownlang/lib/modules/java.java | 62 ++-- .../com/annimon/ownlang/lib/modules/jdbc.java | 304 +++++++++--------- .../com/annimon/ownlang/lib/modules/std.java | 2 +- 5 files changed, 247 insertions(+), 247 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java index 1b129eb7..7cdf426c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java @@ -105,61 +105,61 @@ public static void initConstants() { final MapValue arcType = new MapValue(ArcType.values().length); for (ArcType value : ArcType.values()) { - arcType.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + arcType.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("ArcType", arcType); final MapValue fillRule = new MapValue(FillRule.values().length); for (FillRule value : FillRule.values()) { - fillRule.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + fillRule.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("FillRule", fillRule); final MapValue blendMode = new MapValue(BlendMode.values().length); for (BlendMode value : BlendMode.values()) { - blendMode.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + blendMode.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("BlendMode", blendMode); final MapValue lineCap = new MapValue(StrokeLineCap.values().length); for (StrokeLineCap value : StrokeLineCap.values()) { - lineCap.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + lineCap.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("StrokeLineCap", lineCap); final MapValue lineJoin = new MapValue(StrokeLineJoin.values().length); for (StrokeLineJoin value : StrokeLineJoin.values()) { - lineJoin.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + lineJoin.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("StrokeLineJoin", lineJoin); final MapValue textAlignment = new MapValue(TextAlignment.values().length); for (TextAlignment value : TextAlignment.values()) { - textAlignment.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + textAlignment.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("TextAlignment", textAlignment); final MapValue vPos = new MapValue(VPos.values().length); for (VPos value : VPos.values()) { - vPos.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + vPos.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("VPos", vPos); final MapValue events = new MapValue(Events.values().length); for (Events value : Events.values()) { - events.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + events.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("Events", events); final MapValue mouseButton = new MapValue(MouseButton.values().length); for (MouseButton value : MouseButton.values()) { - mouseButton.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + mouseButton.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("MouseButton", mouseButton); final MapValue keyCodes = new MapValue(KeyCode.values().length); for (KeyCode value : KeyCode.values()) { - keyCodes.set(new StringValue(value.name()), NumberValue.of(value.ordinal())); + keyCodes.set(value.name(), NumberValue.of(value.ordinal())); } Variables.define("KeyCode", keyCodes); } @@ -594,11 +594,11 @@ public ImageFXValue(Image image) { } private void init() { - set(new StringValue("width"), NumberValue.of(image.getWidth())); - set(new StringValue("height"), NumberValue.of(image.getHeight())); - set(new StringValue("preserveRatio"), NumberValue.fromBoolean(image.isPreserveRatio())); - set(new StringValue("smooth"), NumberValue.fromBoolean(image.isSmooth())); - set(new StringValue("getPixels"), new FunctionValue(this::getPixels)); + set("width", NumberValue.of(image.getWidth())); + set("height", NumberValue.of(image.getHeight())); + set("preserveRatio", NumberValue.fromBoolean(image.isPreserveRatio())); + set("smooth", NumberValue.fromBoolean(image.isSmooth())); + set("getPixels", new FunctionValue(this::getPixels)); } private Value getPixels(Value... args) { @@ -737,7 +737,7 @@ private void init() { functions.put("translate", this::translate); for (Map.Entry entry : functions.entrySet()) { - set(new StringValue(entry.getKey()), new FunctionValue(entry.getValue())); + set(entry.getKey(), new FunctionValue(entry.getValue())); } } @@ -1226,57 +1226,57 @@ private static void handleEvent(Event event, final Function handler) { private static void handleMouseEvent(MouseEvent e, final Function handler) { final MapValue map = new MapValue(25); - map.set(new StringValue("button"), NumberValue.of(e.getButton().ordinal())); - map.set(new StringValue("clickCount"), NumberValue.of(e.getClickCount())); - map.set(new StringValue("sceneX"), NumberValue.of(e.getSceneX())); - map.set(new StringValue("sceneY"), NumberValue.of(e.getSceneY())); - map.set(new StringValue("screenX"), NumberValue.of(e.getScreenX())); - map.set(new StringValue("screenY"), NumberValue.of(e.getScreenY())); - map.set(new StringValue("x"), NumberValue.of(e.getX())); - map.set(new StringValue("y"), NumberValue.of(e.getY())); - map.set(new StringValue("z"), NumberValue.of(e.getZ())); - map.set(new StringValue("isAltDown"), NumberValue.fromBoolean(e.isAltDown())); - map.set(new StringValue("isConsumed"), NumberValue.fromBoolean(e.isConsumed())); - map.set(new StringValue("isControlDown"), NumberValue.fromBoolean(e.isControlDown())); - map.set(new StringValue("isDragDetect"), NumberValue.fromBoolean(e.isDragDetect())); - map.set(new StringValue("isMetaDown"), NumberValue.fromBoolean(e.isMetaDown())); - map.set(new StringValue("isMiddleButtonDown"), NumberValue.fromBoolean(e.isMiddleButtonDown())); - map.set(new StringValue("isPopupTrigger"), NumberValue.fromBoolean(e.isPopupTrigger())); - map.set(new StringValue("isPrimaryButtonDown"), NumberValue.fromBoolean(e.isPrimaryButtonDown())); - map.set(new StringValue("isSecondaryButtonDown"), NumberValue.fromBoolean(e.isSecondaryButtonDown())); - map.set(new StringValue("isShiftDown"), NumberValue.fromBoolean(e.isShiftDown())); - map.set(new StringValue("isShortcutDown"), NumberValue.fromBoolean(e.isShortcutDown())); - map.set(new StringValue("isStillSincePress"), NumberValue.fromBoolean(e.isStillSincePress())); - map.set(new StringValue("isSynthesized"), NumberValue.fromBoolean(e.isSynthesized())); + map.set("button", NumberValue.of(e.getButton().ordinal())); + map.set("clickCount", NumberValue.of(e.getClickCount())); + map.set("sceneX", NumberValue.of(e.getSceneX())); + map.set("sceneY", NumberValue.of(e.getSceneY())); + map.set("screenX", NumberValue.of(e.getScreenX())); + map.set("screenY", NumberValue.of(e.getScreenY())); + map.set("x", NumberValue.of(e.getX())); + map.set("y", NumberValue.of(e.getY())); + map.set("z", NumberValue.of(e.getZ())); + map.set("isAltDown", NumberValue.fromBoolean(e.isAltDown())); + map.set("isConsumed", NumberValue.fromBoolean(e.isConsumed())); + map.set("isControlDown", NumberValue.fromBoolean(e.isControlDown())); + map.set("isDragDetect", NumberValue.fromBoolean(e.isDragDetect())); + map.set("isMetaDown", NumberValue.fromBoolean(e.isMetaDown())); + map.set("isMiddleButtonDown", NumberValue.fromBoolean(e.isMiddleButtonDown())); + map.set("isPopupTrigger", NumberValue.fromBoolean(e.isPopupTrigger())); + map.set("isPrimaryButtonDown", NumberValue.fromBoolean(e.isPrimaryButtonDown())); + map.set("isSecondaryButtonDown", NumberValue.fromBoolean(e.isSecondaryButtonDown())); + map.set("isShiftDown", NumberValue.fromBoolean(e.isShiftDown())); + map.set("isShortcutDown", NumberValue.fromBoolean(e.isShortcutDown())); + map.set("isStillSincePress", NumberValue.fromBoolean(e.isStillSincePress())); + map.set("isSynthesized", NumberValue.fromBoolean(e.isSynthesized())); handler.execute(map); } private static void handleKeyEvent(final KeyEvent e, final Function handler) { final MapValue map = new MapValue(10); - map.set(new StringValue("code"), NumberValue.of(e.getCode().ordinal())); - map.set(new StringValue("character"), new StringValue(e.getCharacter())); - map.set(new StringValue("text"), new StringValue(e.getText())); - map.set(new StringValue("isAltDown"), NumberValue.fromBoolean(e.isAltDown())); - map.set(new StringValue("isConsumed"), NumberValue.fromBoolean(e.isConsumed())); - map.set(new StringValue("isControlDown"), NumberValue.fromBoolean(e.isControlDown())); - map.set(new StringValue("isMetaDown"), NumberValue.fromBoolean(e.isMetaDown())); - map.set(new StringValue("isShiftDown"), NumberValue.fromBoolean(e.isShiftDown())); - map.set(new StringValue("isShortcutDown"), NumberValue.fromBoolean(e.isShortcutDown())); + map.set("code", NumberValue.of(e.getCode().ordinal())); + map.set("character", new StringValue(e.getCharacter())); + map.set("text", new StringValue(e.getText())); + map.set("isAltDown", NumberValue.fromBoolean(e.isAltDown())); + map.set("isConsumed", NumberValue.fromBoolean(e.isConsumed())); + map.set("isControlDown", NumberValue.fromBoolean(e.isControlDown())); + map.set("isMetaDown", NumberValue.fromBoolean(e.isMetaDown())); + map.set("isShiftDown", NumberValue.fromBoolean(e.isShiftDown())); + map.set("isShortcutDown", NumberValue.fromBoolean(e.isShortcutDown())); handler.execute(map); } private static void handleDragEvent(final DragEvent e, final Function handler) { final MapValue map = new MapValue(10); - map.set(new StringValue("sceneX"), NumberValue.of(e.getSceneX())); - map.set(new StringValue("sceneY"), NumberValue.of(e.getSceneY())); - map.set(new StringValue("screenX"), NumberValue.of(e.getScreenX())); - map.set(new StringValue("screenY"), NumberValue.of(e.getScreenY())); - map.set(new StringValue("x"), NumberValue.of(e.getX())); - map.set(new StringValue("y"), NumberValue.of(e.getY())); - map.set(new StringValue("z"), NumberValue.of(e.getZ())); - map.set(new StringValue("isAccepted"), NumberValue.fromBoolean(e.isAccepted())); - map.set(new StringValue("isConsumed"), NumberValue.fromBoolean(e.isConsumed())); - map.set(new StringValue("isDropCompleted"), NumberValue.fromBoolean(e.isDropCompleted())); + map.set("sceneX", NumberValue.of(e.getSceneX())); + map.set("sceneY", NumberValue.of(e.getSceneY())); + map.set("screenX", NumberValue.of(e.getScreenX())); + map.set("screenY", NumberValue.of(e.getScreenY())); + map.set("x", NumberValue.of(e.getX())); + map.set("y", NumberValue.of(e.getY())); + map.set("z", NumberValue.of(e.getZ())); + map.set("isAccepted", NumberValue.fromBoolean(e.isAccepted())); + map.set("isConsumed", NumberValue.fromBoolean(e.isConsumed())); + map.set("isDropCompleted", NumberValue.fromBoolean(e.isDropCompleted())); handler.execute(map); } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java index f67360d2..aaf62097 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java @@ -104,9 +104,9 @@ private Value process(String url, String methodStr, Value requestParams, MapValu private Value getResult(Response response, MapValue options) throws IOException { if (options.containsKey(EXTENDED_RESULT)) { final MapValue map = new MapValue(10); - map.set(new StringValue("text"), new StringValue(response.body().string())); - map.set(new StringValue("message"), new StringValue(response.message())); - map.set(new StringValue("code"), NumberValue.of(response.code())); + map.set("text", new StringValue(response.body().string())); + map.set("message", new StringValue(response.message())); + map.set("code", NumberValue.of(response.code())); final MapValue headers = new MapValue(response.headers().size()); for (Map.Entry> entry : response.headers().toMultimap().entrySet()) { final int valuesSize = entry.getValue().size(); @@ -114,10 +114,10 @@ private Value getResult(Response response, MapValue options) throws IOException for (int i = 0; i < valuesSize; i++) { values.set(i, new StringValue(entry.getValue().get(i))); } - headers.set(new StringValue(entry.getKey()), values); + headers.set(entry.getKey(), values); } - map.set(new StringValue("headers"), headers); - map.set(new StringValue("content_length"), NumberValue.of(response.body().contentLength())); + map.set("headers", headers); + map.set("content_length", NumberValue.of(response.body().contentLength())); map.set(CONTENT_TYPE, new StringValue(response.body().contentType().toString())); return map; } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/java.java b/src/main/java/com/annimon/ownlang/lib/modules/java.java index 4b315704..2e4eeaea 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/java.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/java.java @@ -119,37 +119,37 @@ public ClassValue(Class clazz) { } private void init(Class clazz) { - set(new StringValue("isAnnotation"), NumberValue.fromBoolean(clazz.isAnnotation())); - set(new StringValue("isAnonymousClass"), NumberValue.fromBoolean(clazz.isAnonymousClass())); - set(new StringValue("isArray"), NumberValue.fromBoolean(clazz.isArray())); - set(new StringValue("isEnum"), NumberValue.fromBoolean(clazz.isEnum())); - set(new StringValue("isInterface"), NumberValue.fromBoolean(clazz.isInterface())); - set(new StringValue("isLocalClass"), NumberValue.fromBoolean(clazz.isLocalClass())); - set(new StringValue("isMemberClass"), NumberValue.fromBoolean(clazz.isMemberClass())); - set(new StringValue("isPrimitive"), NumberValue.fromBoolean(clazz.isPrimitive())); - set(new StringValue("isSynthetic"), NumberValue.fromBoolean(clazz.isSynthetic())); - - set(new StringValue("modifiers"), NumberValue.of(clazz.getModifiers())); - - set(new StringValue("canonicalName"), new StringValue(clazz.getCanonicalName())); - set(new StringValue("name"), new StringValue(clazz.getName())); - set(new StringValue("simpleName"), new StringValue(clazz.getSimpleName())); - set(new StringValue("typeName"), new StringValue(clazz.getTypeName())); - set(new StringValue("genericString"), new StringValue(clazz.toGenericString())); - - set(new StringValue("getComponentType"), new FunctionValue(v -> classOrNull(clazz.getComponentType()) )); - set(new StringValue("getDeclaringClass"), new FunctionValue(v -> classOrNull(clazz.getDeclaringClass()) )); - set(new StringValue("getEnclosingClass"), new FunctionValue(v -> classOrNull(clazz.getEnclosingClass()) )); - set(new StringValue("getSuperclass"), new FunctionValue(v -> new ClassValue(clazz.getSuperclass()) )); - - set(new StringValue("getClasses"), new FunctionValue(v -> array(clazz.getClasses()) )); - set(new StringValue("getDeclaredClasses"), new FunctionValue(v -> array(clazz.getDeclaredClasses()) )); - set(new StringValue("getInterfaces"), new FunctionValue(v -> array(clazz.getInterfaces()) )); - - set(new StringValue("asSubclass"), new FunctionValue(this::asSubclass)); - set(new StringValue("isAssignableFrom"), new FunctionValue(this::isAssignableFrom)); - set(new StringValue("new"), new FunctionValue(this::newInstance)); - set(new StringValue("cast"), new FunctionValue(this::cast)); + set("isAnnotation", NumberValue.fromBoolean(clazz.isAnnotation())); + set("isAnonymousClass", NumberValue.fromBoolean(clazz.isAnonymousClass())); + set("isArray", NumberValue.fromBoolean(clazz.isArray())); + set("isEnum", NumberValue.fromBoolean(clazz.isEnum())); + set("isInterface", NumberValue.fromBoolean(clazz.isInterface())); + set("isLocalClass", NumberValue.fromBoolean(clazz.isLocalClass())); + set("isMemberClass", NumberValue.fromBoolean(clazz.isMemberClass())); + set("isPrimitive", NumberValue.fromBoolean(clazz.isPrimitive())); + set("isSynthetic", NumberValue.fromBoolean(clazz.isSynthetic())); + + set("modifiers", NumberValue.of(clazz.getModifiers())); + + set("canonicalName", new StringValue(clazz.getCanonicalName())); + set("name", new StringValue(clazz.getName())); + set("simpleName", new StringValue(clazz.getSimpleName())); + set("typeName", new StringValue(clazz.getTypeName())); + set("genericString", new StringValue(clazz.toGenericString())); + + set("getComponentType", new FunctionValue(v -> classOrNull(clazz.getComponentType()) )); + set("getDeclaringClass", new FunctionValue(v -> classOrNull(clazz.getDeclaringClass()) )); + set("getEnclosingClass", new FunctionValue(v -> classOrNull(clazz.getEnclosingClass()) )); + set("getSuperclass", new FunctionValue(v -> new ClassValue(clazz.getSuperclass()) )); + + set("getClasses", new FunctionValue(v -> array(clazz.getClasses()) )); + set("getDeclaredClasses", new FunctionValue(v -> array(clazz.getDeclaredClasses()) )); + set("getInterfaces", new FunctionValue(v -> array(clazz.getInterfaces()) )); + + set("asSubclass", new FunctionValue(this::asSubclass)); + set("isAssignableFrom", new FunctionValue(this::isAssignableFrom)); + set("new", new FunctionValue(this::newInstance)); + set("cast", new FunctionValue(this::cast)); } private Value asSubclass(Value... args) { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java b/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java index aa7f4875..7f55f9e4 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java @@ -128,29 +128,29 @@ public ConnectionValue(Connection connection) { } private void init() { - set(new StringValue("createStatement"), new FunctionValue(this::createStatement)); - set(new StringValue("prepareStatement"), new FunctionValue(this::prepareStatement)); - set(new StringValue("close"), new FunctionValue(this::close)); + set("createStatement", new FunctionValue(this::createStatement)); + set("prepareStatement", new FunctionValue(this::prepareStatement)); + set("close", new FunctionValue(this::close)); - set(new StringValue("clearWarnings"), voidFunction(connection::clearWarnings)); - set(new StringValue("close"), voidFunction(connection::close)); - set(new StringValue("commit"), voidFunction(connection::commit)); - set(new StringValue("rollback"), voidFunction(connection::rollback)); + set("clearWarnings", voidFunction(connection::clearWarnings)); + set("close", voidFunction(connection::close)); + set("commit", voidFunction(connection::commit)); + set("rollback", voidFunction(connection::rollback)); - set(new StringValue("setHoldability"), voidIntFunction(connection::setHoldability)); - set(new StringValue("setTransactionIsolation"), voidIntFunction(connection::setTransactionIsolation)); + set("setHoldability", voidIntFunction(connection::setHoldability)); + set("setTransactionIsolation", voidIntFunction(connection::setTransactionIsolation)); - set(new StringValue("getAutoCommit"), booleanFunction(connection::getAutoCommit)); - set(new StringValue("isClosed"), booleanFunction(connection::isClosed)); - set(new StringValue("isReadOnly"), booleanFunction(connection::isReadOnly)); + set("getAutoCommit", booleanFunction(connection::getAutoCommit)); + set("isClosed", booleanFunction(connection::isClosed)); + set("isReadOnly", booleanFunction(connection::isReadOnly)); - set(new StringValue("getHoldability"), intFunction(connection::getHoldability)); - set(new StringValue("getNetworkTimeout"), intFunction(connection::getNetworkTimeout)); - set(new StringValue("getTransactionIsolation"), intFunction(connection::getTransactionIsolation)); - set(new StringValue("getUpdateCount"), intFunction(connection::getHoldability)); + set("getHoldability", intFunction(connection::getHoldability)); + set("getNetworkTimeout", intFunction(connection::getNetworkTimeout)); + set("getTransactionIsolation", intFunction(connection::getTransactionIsolation)); + set("getUpdateCount", intFunction(connection::getHoldability)); - set(new StringValue("getCatalog"), stringFunction(connection::getCatalog)); - set(new StringValue("getSchema"), stringFunction(connection::getSchema)); + set("getCatalog", stringFunction(connection::getCatalog)); + set("getSchema", stringFunction(connection::getSchema)); } private Value createStatement(Value... args) { @@ -224,68 +224,68 @@ public StatementValue(Statement statement) { } private void init() { - set(new StringValue("addBatch"), new FunctionValue(this::addBatch)); - set(new StringValue("execute"), new FunctionValue(this::execute)); - set(new StringValue("executeQuery"), new FunctionValue(this::executeQuery)); - set(new StringValue("executeUpdate"), new FunctionValue(this::executeUpdate)); - set(new StringValue("executeLargeUpdate"), new FunctionValue(this::executeLargeUpdate)); - - set(new StringValue("cancel"), voidFunction(statement::cancel)); - set(new StringValue("clearBatch"), voidFunction(statement::clearBatch)); - set(new StringValue("clearWarnings"), voidFunction(statement::clearWarnings)); - set(new StringValue("close"), voidFunction(statement::close)); - set(new StringValue("closeOnCompletion"), voidFunction(statement::closeOnCompletion)); - - set(new StringValue("setFetchDirection"), voidIntFunction(statement::setFetchDirection)); - set(new StringValue("setFetchSize"), voidIntFunction(statement::setFetchSize)); - set(new StringValue("setMaxFieldSize"), voidIntFunction(statement::setMaxFieldSize)); - set(new StringValue("setMaxRows"), voidIntFunction(statement::setMaxRows)); - set(new StringValue("setQueryTimeout"), voidIntFunction(statement::setQueryTimeout)); - - set(new StringValue("getMoreResults"), booleanFunction(statement::getMoreResults)); - set(new StringValue("isCloseOnCompletion"), booleanFunction(statement::isCloseOnCompletion)); - set(new StringValue("isClosed"), booleanFunction(statement::isClosed)); - set(new StringValue("isPoolable"), booleanFunction(statement::isPoolable)); - - set(new StringValue("getFetchDirection"), intFunction(statement::getFetchDirection)); - set(new StringValue("getFetchSize"), intFunction(statement::getFetchSize)); - set(new StringValue("getMaxFieldSize"), intFunction(statement::getMaxFieldSize)); - set(new StringValue("getMaxRows"), intFunction(statement::getMaxRows)); - set(new StringValue("getQueryTimeout"), intFunction(statement::getQueryTimeout)); - set(new StringValue("getResultSetConcurrency"), intFunction(statement::getResultSetConcurrency)); - set(new StringValue("getResultSetHoldability"), intFunction(statement::getResultSetHoldability)); - set(new StringValue("getResultSetType"), intFunction(statement::getResultSetType)); - set(new StringValue("getUpdateCount"), intFunction(statement::getUpdateCount)); - - set(new StringValue("setCursorName"), updateData(statement::setCursorName, (args) -> args[0].asString())); - set(new StringValue("setEscapeProcessing"), updateData(statement::setEscapeProcessing, (args) -> args[0].asInt() != 0)); - set(new StringValue("setLargeMaxRows"), updateData(statement::setLargeMaxRows, (args) -> getNumber(args[0]).longValue())); - set(new StringValue("setPoolable"), updateData(statement::setPoolable, (args) -> args[0].asInt() != 0)); - - set(new StringValue("getResultSet"), objectFunction(statement::getResultSet, ResultSetValue::new)); - set(new StringValue("getGeneratedKeys"), objectFunction(statement::getGeneratedKeys, ResultSetValue::new)); - set(new StringValue("executeBatch"), objectFunction(statement::executeBatch, jdbc::intArrayToValue)); - set(new StringValue("executeLargeBatch"), objectFunction(statement::executeLargeBatch, jdbc::longArrayToValue)); + set("addBatch", new FunctionValue(this::addBatch)); + set("execute", new FunctionValue(this::execute)); + set("executeQuery", new FunctionValue(this::executeQuery)); + set("executeUpdate", new FunctionValue(this::executeUpdate)); + set("executeLargeUpdate", new FunctionValue(this::executeLargeUpdate)); + + set("cancel", voidFunction(statement::cancel)); + set("clearBatch", voidFunction(statement::clearBatch)); + set("clearWarnings", voidFunction(statement::clearWarnings)); + set("close", voidFunction(statement::close)); + set("closeOnCompletion", voidFunction(statement::closeOnCompletion)); + + set("setFetchDirection", voidIntFunction(statement::setFetchDirection)); + set("setFetchSize", voidIntFunction(statement::setFetchSize)); + set("setMaxFieldSize", voidIntFunction(statement::setMaxFieldSize)); + set("setMaxRows", voidIntFunction(statement::setMaxRows)); + set("setQueryTimeout", voidIntFunction(statement::setQueryTimeout)); + + set("getMoreResults", booleanFunction(statement::getMoreResults)); + set("isCloseOnCompletion", booleanFunction(statement::isCloseOnCompletion)); + set("isClosed", booleanFunction(statement::isClosed)); + set("isPoolable", booleanFunction(statement::isPoolable)); + + set("getFetchDirection", intFunction(statement::getFetchDirection)); + set("getFetchSize", intFunction(statement::getFetchSize)); + set("getMaxFieldSize", intFunction(statement::getMaxFieldSize)); + set("getMaxRows", intFunction(statement::getMaxRows)); + set("getQueryTimeout", intFunction(statement::getQueryTimeout)); + set("getResultSetConcurrency", intFunction(statement::getResultSetConcurrency)); + set("getResultSetHoldability", intFunction(statement::getResultSetHoldability)); + set("getResultSetType", intFunction(statement::getResultSetType)); + set("getUpdateCount", intFunction(statement::getUpdateCount)); + + set("setCursorName", updateData(statement::setCursorName, (args) -> args[0].asString())); + set("setEscapeProcessing", updateData(statement::setEscapeProcessing, (args) -> args[0].asInt() != 0)); + set("setLargeMaxRows", updateData(statement::setLargeMaxRows, (args) -> getNumber(args[0]).longValue())); + set("setPoolable", updateData(statement::setPoolable, (args) -> args[0].asInt() != 0)); + + set("getResultSet", objectFunction(statement::getResultSet, ResultSetValue::new)); + set("getGeneratedKeys", objectFunction(statement::getGeneratedKeys, ResultSetValue::new)); + set("executeBatch", objectFunction(statement::executeBatch, jdbc::intArrayToValue)); + set("executeLargeBatch", objectFunction(statement::executeLargeBatch, jdbc::longArrayToValue)); if (ps != null) { - set(new StringValue("clearParameters"), voidFunction(ps::clearParameters)); - - set(new StringValue("setBigDecimal"), updateData(ps::setBigDecimal, (args) -> new BigDecimal(args[1].asString()))); - set(new StringValue("setBoolean"), updateData(ps::setBoolean, (args) -> args[1].asInt() != 0)); - set(new StringValue("setByte"), updateData(ps::setByte, (args) -> getNumber(args[1]).byteValue())); - set(new StringValue("setBytes"), updateData(ps::setBytes, (args) -> valueToByteArray(args[1]))); - set(new StringValue("setDate"), updateData(ps::setDate, (args) -> new Date(getNumber(args[1]).longValue()))); - set(new StringValue("setDouble"), updateData(ps::setDouble, (args) -> getNumber(args[1]).doubleValue())); - set(new StringValue("setFloat"), updateData(ps::setFloat, (args) -> getNumber(args[1]).floatValue())); - set(new StringValue("setInt"), updateData(ps::setInt, (args) -> args[1].asInt())); - set(new StringValue("setLong"), updateData(ps::setLong, (args) -> getNumber(args[1]).longValue())); - set(new StringValue("setNString"), updateData(ps::setNString, (args) -> args[1].asString())); - set(new StringValue("setNull"), updateData(ps::setNull, (args) -> args[1].asInt())); - set(new StringValue("setShort"), updateData(ps::setShort, (args) -> getNumber(args[1]).shortValue())); - set(new StringValue("setString"), updateData(ps::setString, (args) -> args[1].asString())); - set(new StringValue("setTime"), updateData(ps::setTime, (args) -> new Time(getNumber(args[1]).longValue()))); - set(new StringValue("setTimestamp"), updateData(ps::setTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); - set(new StringValue("setURL"), updateData(ps::setURL, (args) -> { + set("clearParameters", voidFunction(ps::clearParameters)); + + set("setBigDecimal", updateData(ps::setBigDecimal, (args) -> new BigDecimal(args[1].asString()))); + set("setBoolean", updateData(ps::setBoolean, (args) -> args[1].asInt() != 0)); + set("setByte", updateData(ps::setByte, (args) -> getNumber(args[1]).byteValue())); + set("setBytes", updateData(ps::setBytes, (args) -> valueToByteArray(args[1]))); + set("setDate", updateData(ps::setDate, (args) -> new Date(getNumber(args[1]).longValue()))); + set("setDouble", updateData(ps::setDouble, (args) -> getNumber(args[1]).doubleValue())); + set("setFloat", updateData(ps::setFloat, (args) -> getNumber(args[1]).floatValue())); + set("setInt", updateData(ps::setInt, (args) -> args[1].asInt())); + set("setLong", updateData(ps::setLong, (args) -> getNumber(args[1]).longValue())); + set("setNString", updateData(ps::setNString, (args) -> args[1].asString())); + set("setNull", updateData(ps::setNull, (args) -> args[1].asInt())); + set("setShort", updateData(ps::setShort, (args) -> getNumber(args[1]).shortValue())); + set("setString", updateData(ps::setString, (args) -> args[1].asString())); + set("setTime", updateData(ps::setTime, (args) -> new Time(getNumber(args[1]).longValue()))); + set("setTimestamp", updateData(ps::setTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); + set("setURL", updateData(ps::setURL, (args) -> { try { return new URL(args[1].asString()); } catch (IOException ioe) { @@ -384,84 +384,84 @@ public ResultSetValue(ResultSet rs) { } private void init() { - set(new StringValue("findColumn"), new FunctionValue(this::findColumn)); - - set(new StringValue("afterLast"), voidFunction(rs::afterLast)); - set(new StringValue("beforeFirst"), voidFunction(rs::beforeFirst)); - set(new StringValue("cancelRowUpdates"), voidFunction(rs::cancelRowUpdates)); - set(new StringValue("clearWarnings"), voidFunction(rs::clearWarnings)); - set(new StringValue("close"), voidFunction(rs::close)); - set(new StringValue("deleteRow"), voidFunction(rs::deleteRow)); - set(new StringValue("insertRow"), voidFunction(rs::insertRow)); - set(new StringValue("moveToCurrentRow"), voidFunction(rs::moveToCurrentRow)); - set(new StringValue("moveToInsertRow"), voidFunction(rs::moveToInsertRow)); - set(new StringValue("refreshRow"), voidFunction(rs::refreshRow)); - set(new StringValue("updateRow"), voidFunction(rs::updateRow)); - - set(new StringValue("absolute"), voidIntFunction(rs::absolute)); - set(new StringValue("relative"), voidIntFunction(rs::relative)); - set(new StringValue("setFetchDirection"), voidIntFunction(rs::setFetchDirection)); - set(new StringValue("setFetchSize"), voidIntFunction(rs::setFetchSize)); - - set(new StringValue("first"), booleanFunction(rs::first)); - set(new StringValue("isAfterLast"), booleanFunction(rs::isAfterLast)); - set(new StringValue("isBeforeFirst"), booleanFunction(rs::isBeforeFirst)); - set(new StringValue("isClosed"), booleanFunction(rs::isClosed)); - set(new StringValue("isFirst"), booleanFunction(rs::isFirst)); - set(new StringValue("isLast"), booleanFunction(rs::isLast)); - set(new StringValue("last"), booleanFunction(rs::last)); - set(new StringValue("next"), booleanFunction(rs::next)); - set(new StringValue("previous"), booleanFunction(rs::previous)); - set(new StringValue("rowDeleted"), booleanFunction(rs::rowDeleted)); - set(new StringValue("rowInserted"), booleanFunction(rs::rowInserted)); - set(new StringValue("rowUpdated"), booleanFunction(rs::rowUpdated)); - set(new StringValue("wasNull"), booleanFunction(rs::wasNull)); - - set(new StringValue("getConcurrency"), intFunction(rs::getConcurrency)); - set(new StringValue("getFetchDirection"), intFunction(rs::getFetchDirection)); - set(new StringValue("getFetchSize"), intFunction(rs::getFetchSize)); - set(new StringValue("getHoldability"), intFunction(rs::getHoldability)); - set(new StringValue("getRow"), intFunction(rs::getRow)); - set(new StringValue("getType"), intFunction(rs::getType)); - - set(new StringValue("getCursorName"), stringFunction(rs::getCursorName)); - set(new StringValue("getStatement"), objectFunction(rs::getStatement, StatementValue::new)); + set("findColumn", new FunctionValue(this::findColumn)); + + set("afterLast", voidFunction(rs::afterLast)); + set("beforeFirst", voidFunction(rs::beforeFirst)); + set("cancelRowUpdates", voidFunction(rs::cancelRowUpdates)); + set("clearWarnings", voidFunction(rs::clearWarnings)); + set("close", voidFunction(rs::close)); + set("deleteRow", voidFunction(rs::deleteRow)); + set("insertRow", voidFunction(rs::insertRow)); + set("moveToCurrentRow", voidFunction(rs::moveToCurrentRow)); + set("moveToInsertRow", voidFunction(rs::moveToInsertRow)); + set("refreshRow", voidFunction(rs::refreshRow)); + set("updateRow", voidFunction(rs::updateRow)); + + set("absolute", voidIntFunction(rs::absolute)); + set("relative", voidIntFunction(rs::relative)); + set("setFetchDirection", voidIntFunction(rs::setFetchDirection)); + set("setFetchSize", voidIntFunction(rs::setFetchSize)); + + set("first", booleanFunction(rs::first)); + set("isAfterLast", booleanFunction(rs::isAfterLast)); + set("isBeforeFirst", booleanFunction(rs::isBeforeFirst)); + set("isClosed", booleanFunction(rs::isClosed)); + set("isFirst", booleanFunction(rs::isFirst)); + set("isLast", booleanFunction(rs::isLast)); + set("last", booleanFunction(rs::last)); + set("next", booleanFunction(rs::next)); + set("previous", booleanFunction(rs::previous)); + set("rowDeleted", booleanFunction(rs::rowDeleted)); + set("rowInserted", booleanFunction(rs::rowInserted)); + set("rowUpdated", booleanFunction(rs::rowUpdated)); + set("wasNull", booleanFunction(rs::wasNull)); + + set("getConcurrency", intFunction(rs::getConcurrency)); + set("getFetchDirection", intFunction(rs::getFetchDirection)); + set("getFetchSize", intFunction(rs::getFetchSize)); + set("getHoldability", intFunction(rs::getHoldability)); + set("getRow", intFunction(rs::getRow)); + set("getType", intFunction(rs::getType)); + + set("getCursorName", stringFunction(rs::getCursorName)); + set("getStatement", objectFunction(rs::getStatement, StatementValue::new)); // Results - set(new StringValue("getArray"), getObjectResult(rs::getArray, rs::getArray, jdbc::arrayToResultSetValue)); - set(new StringValue("getBigDecimal"), getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); - set(new StringValue("getBoolean"), getBooleanResult(rs::getBoolean, rs::getBoolean)); - set(new StringValue("getByte"), getNumberResult(rs::getByte, rs::getByte)); - set(new StringValue("getBytes"), getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); - set(new StringValue("getDate"), getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); - set(new StringValue("getDouble"), getNumberResult(rs::getDouble, rs::getDouble)); - set(new StringValue("getFloat"), getNumberResult(rs::getFloat, rs::getFloat)); - set(new StringValue("getInt"), getNumberResult(rs::getInt, rs::getInt)); - set(new StringValue("getLong"), getNumberResult(rs::getLong, rs::getLong)); - set(new StringValue("getNString"), getStringResult(rs::getNString, rs::getNString)); - set(new StringValue("getRowId"), getObjectResult(rs::getRowId, rs::getRowId, (rowid) -> ArrayValue.of(rowid.getBytes()))); - set(new StringValue("getShort"), getNumberResult(rs::getShort, rs::getShort)); - set(new StringValue("getString"), getStringResult(rs::getString, rs::getString)); - set(new StringValue("getTime"), getObjectResult(rs::getTime, rs::getTime, (time) -> NumberValue.of(time.getTime()))); - set(new StringValue("getTimestamp"), getObjectResult(rs::getTimestamp, rs::getTimestamp, (timestamp) -> NumberValue.of(timestamp.getTime()))); - set(new StringValue("getURL"), getObjectResult(rs::getURL, rs::getURL, (url) -> new StringValue(url.toExternalForm()))); + set("getArray", getObjectResult(rs::getArray, rs::getArray, jdbc::arrayToResultSetValue)); + set("getBigDecimal", getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); + set("getBoolean", getBooleanResult(rs::getBoolean, rs::getBoolean)); + set("getByte", getNumberResult(rs::getByte, rs::getByte)); + set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); + set("getDate", getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); + set("getDouble", getNumberResult(rs::getDouble, rs::getDouble)); + set("getFloat", getNumberResult(rs::getFloat, rs::getFloat)); + set("getInt", getNumberResult(rs::getInt, rs::getInt)); + set("getLong", getNumberResult(rs::getLong, rs::getLong)); + set("getNString", getStringResult(rs::getNString, rs::getNString)); + set("getRowId", getObjectResult(rs::getRowId, rs::getRowId, (rowid) -> ArrayValue.of(rowid.getBytes()))); + set("getShort", getNumberResult(rs::getShort, rs::getShort)); + set("getString", getStringResult(rs::getString, rs::getString)); + set("getTime", getObjectResult(rs::getTime, rs::getTime, (time) -> NumberValue.of(time.getTime()))); + set("getTimestamp", getObjectResult(rs::getTimestamp, rs::getTimestamp, (timestamp) -> NumberValue.of(timestamp.getTime()))); + set("getURL", getObjectResult(rs::getURL, rs::getURL, (url) -> new StringValue(url.toExternalForm()))); // Update - set(new StringValue("updateNull"), new FunctionValue(this::updateNull)); - set(new StringValue("updateBigDecimal"), updateData(rs::updateBigDecimal, rs::updateBigDecimal, (args) -> new BigDecimal(args[1].asString()))); - set(new StringValue("updateBoolean"), updateData(rs::updateBoolean, rs::updateBoolean, (args) -> args[1].asInt() != 0)); - set(new StringValue("updateByte"), updateData(rs::updateByte, rs::updateByte, (args) -> getNumber(args[1]).byteValue())); - set(new StringValue("updateBytes"), updateData(rs::updateBytes, rs::updateBytes, (args) -> valueToByteArray(args[1]))); - set(new StringValue("updateDate"), updateData(rs::updateDate, rs::updateDate, (args) -> new Date(getNumber(args[1]).longValue()))); - set(new StringValue("updateDouble"), updateData(rs::updateDouble, rs::updateDouble, (args) -> getNumber(args[1]).doubleValue())); - set(new StringValue("updateFloat"), updateData(rs::updateFloat, rs::updateFloat, (args) -> getNumber(args[1]).floatValue())); - set(new StringValue("updateInt"), updateData(rs::updateInt, rs::updateInt, (args) -> getNumber(args[1]).intValue())); - set(new StringValue("updateLong"), updateData(rs::updateLong, rs::updateLong, (args) -> getNumber(args[1]).longValue())); - set(new StringValue("updateNString"), updateData(rs::updateNString, rs::updateNString, (args) -> args[1].asString())); - set(new StringValue("updateShort"), updateData(rs::updateShort, rs::updateShort, (args) -> getNumber(args[1]).shortValue())); - set(new StringValue("updateString"), updateData(rs::updateString, rs::updateString, (args) -> args[1].asString())); - set(new StringValue("updateTime"), updateData(rs::updateTime, rs::updateTime, (args) -> new Time(getNumber(args[1]).longValue()))); - set(new StringValue("updateTimestamp"), updateData(rs::updateTimestamp, rs::updateTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); + set("updateNull", new FunctionValue(this::updateNull)); + set("updateBigDecimal", updateData(rs::updateBigDecimal, rs::updateBigDecimal, (args) -> new BigDecimal(args[1].asString()))); + set("updateBoolean", updateData(rs::updateBoolean, rs::updateBoolean, (args) -> args[1].asInt() != 0)); + set("updateByte", updateData(rs::updateByte, rs::updateByte, (args) -> getNumber(args[1]).byteValue())); + set("updateBytes", updateData(rs::updateBytes, rs::updateBytes, (args) -> valueToByteArray(args[1]))); + set("updateDate", updateData(rs::updateDate, rs::updateDate, (args) -> new Date(getNumber(args[1]).longValue()))); + set("updateDouble", updateData(rs::updateDouble, rs::updateDouble, (args) -> getNumber(args[1]).doubleValue())); + set("updateFloat", updateData(rs::updateFloat, rs::updateFloat, (args) -> getNumber(args[1]).floatValue())); + set("updateInt", updateData(rs::updateInt, rs::updateInt, (args) -> getNumber(args[1]).intValue())); + set("updateLong", updateData(rs::updateLong, rs::updateLong, (args) -> getNumber(args[1]).longValue())); + set("updateNString", updateData(rs::updateNString, rs::updateNString, (args) -> args[1].asString())); + set("updateShort", updateData(rs::updateShort, rs::updateShort, (args) -> getNumber(args[1]).shortValue())); + set("updateString", updateData(rs::updateString, rs::updateString, (args) -> args[1].asString())); + set("updateTime", updateData(rs::updateTime, rs::updateTime, (args) -> new Time(getNumber(args[1]).longValue()))); + set("updateTimestamp", updateData(rs::updateTimestamp, rs::updateTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); } private Value findColumn(Value... args) { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/std.java b/src/main/java/com/annimon/ownlang/lib/modules/std.java index 00be346b..7534d92c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/std.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/std.java @@ -13,12 +13,12 @@ public final class std implements Module { public static void initConstants() { - Variables.define("ARGS", ArrayValue.of(Main.getOwnlangArgs())); } @Override public void init() { initConstants(); + Variables.define("ARGS", ArrayValue.of(Main.getOwnlangArgs())); // is not constant Functions.set("echo", new std_echo()); Functions.set("readln", new std_readln()); Functions.set("length", new std_length()); From a0b6bc168d6abc583e9d76d4ee9e12abf78c24ad Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 11:23:51 +0300 Subject: [PATCH 153/448] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20canvasfx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Converters.java | 45 +++ .../com/annimon/ownlang/lib/MapValue.java | 4 + .../annimon/ownlang/lib/modules/canvasfx.java | 341 +++++------------- 3 files changed, 149 insertions(+), 241 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java index e0dc686a..b8956813 100644 --- a/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -21,6 +21,10 @@ public interface VoidToFloatFunction { float apply(); } + public interface VoidToDoubleFunction { + double apply(); + } + public interface VoidToStringFunction { String apply(); } @@ -41,6 +45,18 @@ public interface Float4ToVoidFunction { void apply(float f1, float f2, float f3, float f4); } + public interface DoubleToVoidFunction { + void apply(double d); + } + + public interface Double2ToVoidFunction { + void apply(double d1, double d2); + } + + public interface Double4ToVoidFunction { + void apply(double d1, double d2, double d3, double d4); + } + public interface StringToVoidFunction { void apply(String s); } @@ -64,6 +80,10 @@ public static FunctionValue voidToInt(VoidToIntFunction f) { public static FunctionValue voidToFloat(VoidToFloatFunction f) { return new FunctionValue(args -> NumberValue.of(f.apply())); } + + public static FunctionValue voidToDouble(VoidToDoubleFunction f) { + return new FunctionValue(args -> NumberValue.of(f.apply())); + } public static FunctionValue voidToString(VoidToStringFunction f) { return new FunctionValue(args -> new StringValue(f.apply())); @@ -115,6 +135,31 @@ public static FunctionValue float4ToVoid(Float4ToVoidFunction f) { }); } + public static FunctionValue doubleToVoid(DoubleToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + f.apply(args[0].asNumber()); + return NumberValue.ZERO; + }); + } + + public static FunctionValue double2ToVoid(Double2ToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(2, args.length); + f.apply(args[0].asNumber(), args[1].asNumber()); + return NumberValue.ZERO; + }); + } + + public static FunctionValue double4ToVoid(Double4ToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(4, args.length); + f.apply(args[0].asNumber(), args[1].asNumber(), + args[2].asNumber(), args[3].asNumber()); + 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/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java index 72b1e879..dd2d3b90 100644 --- a/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -51,6 +51,10 @@ public Value get(Value key) { public void set(String key, Value value) { set(new StringValue(key), value); } + + public void set(String key, Function function) { + set(new StringValue(key), new FunctionValue(function)); + } public void set(Value key, Value value) { map.put(key, value); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java index 7cdf426c..660ceac8 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java @@ -3,11 +3,11 @@ import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; +import static com.annimon.ownlang.lib.Converters.*; import java.awt.Dimension; import java.lang.reflect.Modifier; import java.nio.IntBuffer; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import javafx.application.Platform; @@ -598,7 +598,7 @@ private void init() { set("height", NumberValue.of(image.getHeight())); set("preserveRatio", NumberValue.fromBoolean(image.isPreserveRatio())); set("smooth", NumberValue.fromBoolean(image.isSmooth())); - set("getPixels", new FunctionValue(this::getPixels)); + set("getPixels", this::getPixels); } private Value getPixels(Value... args) { @@ -671,77 +671,71 @@ public GraphicsFXValue(GraphicsContext graphics) { } private void init() { - Map functions = new HashMap<>(); - functions.put("applyEffect", this::applyEffect); - functions.put("appendSVGPath", this::appendSVGPath); - functions.put("arc", this::arc); - functions.put("arcTo", this::arcTo); - functions.put("beginPath", this::beginPath); - functions.put("bezierCurveTo", this::bezierCurveTo); - functions.put("clearRect", this::clearRect); - functions.put("clip", this::clip); - functions.put("closePath", this::closePath); - functions.put("drawImage", this::drawImage); - functions.put("fill", this::fill); - functions.put("fillArc", this::fillArc); - functions.put("fillOval", this::fillOval); - functions.put("fillPolygon", this::fillPolygon); - functions.put("fillRect", this::fillRect); - functions.put("fillRoundRect", this::fillRoundRect); - functions.put("fillText", this::fillText); - functions.put("getGlobalAlpha", this::getGlobalAlpha); - functions.put("getLineWidth", this::getLineWidth); - functions.put("getMiterLimit", this::getMiterLimit); - functions.put("getFill", this::getFill); - functions.put("getFillRule", this::getFillRule); - functions.put("getGlobalAlpha", this::getGlobalAlpha); - functions.put("getGlobalBlendMode", this::getGlobalBlendMode); - functions.put("getLineCap", this::getLineCap); - functions.put("getLineJoin", this::getLineJoin); - functions.put("getLineWidth", this::getLineWidth); - functions.put("getMiterLimit", this::getMiterLimit); - functions.put("getStroke", this::getStroke); - functions.put("getTextAlign", this::getTextAlign); - functions.put("getTextBaseline", this::getTextBaseline); - functions.put("isPointInPath", this::isPointInPath); - functions.put("lineTo", this::lineTo); - functions.put("moveTo", this::moveTo); - functions.put("quadraticCurveTo", this::quadraticCurveTo); - functions.put("rect", this::rect); - functions.put("restore", this::restore); - functions.put("rotate", this::rotate); - functions.put("save", this::save); - functions.put("scale", this::scale); - functions.put("setEffect", this::setEffect); - functions.put("setFill", this::setFill); - functions.put("setFillRule", this::setFillRule); - functions.put("setGlobalAlpha", this::setGlobalAlpha); - functions.put("setGlobalBlendMode", this::setGlobalBlendMode); - functions.put("setLineCap", this::setLineCap); - functions.put("setLineJoin", this::setLineJoin); - functions.put("setLineWidth", this::setLineWidth); - functions.put("setMiterLimit", this::setMiterLimit); - functions.put("setStroke", this::setStroke); - functions.put("setTextAlign", this::setTextAlign); - functions.put("setTextBaseline", this::setTextBaseline); - functions.put("stroke", this::stroke); - functions.put("strokeArc", this::strokeArc); - functions.put("strokeLine", this::strokeLine); - functions.put("strokeOval", this::strokeOval); - functions.put("strokePolygon", this::strokePolygon); - functions.put("strokePolyline", this::strokePolyline); - functions.put("strokeRect", this::strokeRect); - functions.put("strokeRoundRect", this::strokeRoundRect); - functions.put("strokeText", this::strokeText); - functions.put("transform", this::transform); - functions.put("translate", this::translate); - - for (Map.Entry entry : functions.entrySet()) { - set(entry.getKey(), new FunctionValue(entry.getValue())); - } - } - - public Value applyEffect(Value... args) { + set("applyEffect", this::applyEffect); + set("appendSVGPath", this::appendSVGPath); + set("arc", this::arc); + set("arcTo", this::arcTo); + set("beginPath", voidToVoid(graphics::beginPath)); + set("bezierCurveTo", this::bezierCurveTo); + set("clearRect", double4ToVoid(graphics::clearRect)); + set("clip", voidToVoid(graphics::clip)); + set("closePath", voidToVoid(graphics::closePath)); + set("drawImage", this::drawImage); + set("fill", voidToVoid(graphics::fill)); + set("fillArc", this::fillArc); + set("fillOval", double4ToVoid(graphics::fillOval)); + set("fillPolygon", this::fillPolygon); + set("fillRect", double4ToVoid(graphics::fillRect)); + set("fillRoundRect", this::fillRoundRect); + set("fillText", this::fillText); + set("getFill", this::getFill); + set("getFillRule", this::getFillRule); + set("getGlobalAlpha", voidToDouble(graphics::getGlobalAlpha)); + set("getGlobalBlendMode", this::getGlobalBlendMode); + set("getLineCap", this::getLineCap); + set("getLineDashOffset", voidToDouble(graphics::getLineDashOffset)); + set("getLineJoin", this::getLineJoin); + set("getLineWidth", voidToDouble(graphics::getLineWidth)); + set("getMiterLimit", voidToDouble(graphics::getMiterLimit)); + set("getStroke", this::getStroke); + set("getTextAlign", this::getTextAlign); + set("getTextBaseline", this::getTextBaseline); + set("isPointInPath", this::isPointInPath); + set("lineTo", double2ToVoid(graphics::lineTo)); + set("moveTo", double2ToVoid(graphics::moveTo)); + set("quadraticCurveTo", double4ToVoid(graphics::quadraticCurveTo)); + set("rect", double4ToVoid(graphics::rect)); + set("restore", voidToVoid(graphics::restore)); + set("rotate", doubleToVoid(graphics::rotate)); + set("save", voidToVoid(graphics::save)); + set("scale", double2ToVoid(graphics::scale)); + set("setEffect", this::setEffect); + set("setFill", this::setFill); + set("setFillRule", this::setFillRule); + set("setGlobalAlpha", doubleToVoid(graphics::setGlobalAlpha)); + set("setGlobalBlendMode", this::setGlobalBlendMode); + set("setLineCap", this::setLineCap); + set("setLineDashOffset", doubleToVoid(graphics::setLineDashOffset)); + set("setLineJoin", this::setLineJoin); + set("setLineWidth", doubleToVoid(graphics::setLineWidth)); + set("setMiterLimit", doubleToVoid(graphics::setMiterLimit)); + set("setStroke", this::setStroke); + set("setTextAlign", this::setTextAlign); + set("setTextBaseline", this::setTextBaseline); + set("stroke", voidToVoid(graphics::stroke)); + set("strokeArc", this::strokeArc); + set("strokeLine", double4ToVoid(graphics::strokeLine)); + set("strokeOval", double4ToVoid(graphics::strokeOval)); + set("strokePolygon", this::strokePolygon); + set("strokePolyline", this::strokePolyline); + set("strokeRect", double4ToVoid(graphics::strokeRect)); + set("strokeRoundRect", this::strokeRoundRect); + set("strokeText", this::strokeText); + set("transform", this::transform); + set("translate", double2ToVoid(graphics::translate)); + } + + private Value applyEffect(Value... args) { if (args[0].type() != FX_EFFECT_TYPE) { throw new TypeException("Effect expected, found " + Types.typeToString(args[0].type())); } @@ -749,54 +743,33 @@ public Value applyEffect(Value... args) { return NumberValue.ZERO; } - public Value arc(Value... args) { + private Value arc(Value... args) { graphics.arc(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); return NumberValue.ZERO; } - public Value appendSVGPath(Value... args) { + private Value appendSVGPath(Value... args) { graphics.appendSVGPath(args[0].asString()); return NumberValue.ZERO; } - public Value arcTo(Value... args) { + private Value arcTo(Value... args) { graphics.arcTo(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber()); return NumberValue.ZERO; } - public Value beginPath(Value... args) { - graphics.beginPath(); - return NumberValue.ZERO; - } - - public Value bezierCurveTo(Value... args) { + private Value bezierCurveTo(Value... args) { graphics.bezierCurveTo(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); return NumberValue.ZERO; } - public Value clearRect(Value... args) { - graphics.clearRect(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value clip(Value... args) { - graphics.clip(); - return NumberValue.ZERO; - } - - public Value closePath(Value... args) { - graphics.closePath(); - return NumberValue.ZERO; - } - - public Value drawImage(Value... args) { + private Value drawImage(Value... args) { Arguments.checkAtLeast(3, args.length); if (!(args[0] instanceof ImageFXValue)) { throw new TypeException("ImageFX expected"); @@ -826,12 +799,7 @@ public Value drawImage(Value... args) { return NumberValue.ZERO; } - public Value fill(Value... args) { - graphics.fill(); - return NumberValue.ZERO; - } - - public Value fillArc(Value... args) { + private Value fillArc(Value... args) { graphics.fillArc(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber(), @@ -839,13 +807,7 @@ public Value fillArc(Value... args) { return NumberValue.ZERO; } - public Value fillOval(Value... args) { - graphics.fillOval(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value fillPolygon(Value... args) { + private Value fillPolygon(Value... args) { final ArrayValue xarr = (ArrayValue) args[0]; final ArrayValue yarr = (ArrayValue) args[1]; @@ -861,20 +823,14 @@ public Value fillPolygon(Value... args) { return NumberValue.ZERO; } - public Value fillRect(Value... args) { - graphics.fillRect(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value fillRoundRect(Value... args) { + private Value fillRoundRect(Value... args) { graphics.fillRoundRect(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber() ); return NumberValue.ZERO; } - public Value fillText(Value... args) { + private Value fillText(Value... args) { if (args.length < 4) { // str x y graphics.fillText(args[0].asString(), args[1].asNumber(), @@ -886,97 +842,43 @@ public Value fillText(Value... args) { return NumberValue.ZERO; } - public Value getFill(Value... args) { + private Value getFill(Value... args) { return new ColorValue((Color)graphics.getFill()); } - public Value getFillRule(Value... args) { + private Value getFillRule(Value... args) { return NumberValue.of(graphics.getFillRule().ordinal()); } - public Value getGlobalAlpha(Value... args) { - return NumberValue.of(graphics.getGlobalAlpha()); - } - - public Value getGlobalBlendMode(Value... args) { + private Value getGlobalBlendMode(Value... args) { return NumberValue.of(graphics.getGlobalBlendMode().ordinal()); } - public Value getLineCap(Value... args) { + private Value getLineCap(Value... args) { return NumberValue.of(graphics.getLineCap().ordinal()); } - public Value getLineJoin(Value... args) { + private Value getLineJoin(Value... args) { return NumberValue.of(graphics.getLineJoin().ordinal()); } - public Value getLineWidth(Value... args) { - return NumberValue.of(graphics.getLineWidth()); - } - - public Value getMiterLimit(Value... args) { - return NumberValue.of(graphics.getMiterLimit()); - } - - public Value getStroke(Value... args) { + private Value getStroke(Value... args) { return new ColorValue((Color)graphics.getStroke()); } - public Value getTextAlign(Value... args) { + private Value getTextAlign(Value... args) { return NumberValue.of(graphics.getTextAlign().ordinal()); } - public Value getTextBaseline(Value... args) { + private Value getTextBaseline(Value... args) { return NumberValue.of(graphics.getTextBaseline().ordinal()); } - public Value isPointInPath(Value... args) { + private Value isPointInPath(Value... args) { return NumberValue.fromBoolean(graphics.isPointInPath(args[0].asNumber(), args[1].asNumber())); } - public Value lineTo(Value... args) { - graphics.lineTo(args[0].asNumber(), args[1].asNumber()); - return NumberValue.ZERO; - } - - public Value moveTo(Value... args) { - graphics.moveTo(args[0].asNumber(), args[1].asNumber()); - return NumberValue.ZERO; - } - - public Value quadraticCurveTo(Value... args) { - graphics.quadraticCurveTo(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value rect(Value... args) { - graphics.rect(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value restore(Value... args) { - graphics.restore(); - return NumberValue.ZERO; - } - - public Value rotate(Value... args) { - graphics.rotate(args[0].asNumber()); - return NumberValue.ZERO; - } - - public Value save(Value... args) { - graphics.save(); - return NumberValue.ZERO; - } - - public Value scale(Value... args) { - graphics.scale(args[0].asNumber(), args[1].asNumber()); - return NumberValue.ZERO; - } - - public Value setEffect(Value... args) { + private Value setEffect(Value... args) { if (args[0].type() != FX_EFFECT_TYPE) { throw new TypeException("Effect expected, found " + Types.typeToString(args[0].type())); } @@ -984,67 +886,47 @@ public Value setEffect(Value... args) { return NumberValue.ZERO; } - public Value setFill(Value... args) { + private Value setFill(Value... args) { graphics.setFill((Color) args[0].raw()); return NumberValue.ZERO; } - public Value setFillRule(Value... args) { + private Value setFillRule(Value... args) { graphics.setFillRule(FillRule.values()[args[0].asInt()]); return NumberValue.ZERO; } - public Value setGlobalAlpha(Value... args) { - graphics.setGlobalAlpha(args[0].asNumber()); - return NumberValue.ZERO; - } - - public Value setGlobalBlendMode(Value... args) { + private Value setGlobalBlendMode(Value... args) { graphics.setGlobalBlendMode(BlendMode.values()[args[0].asInt()]); return NumberValue.ZERO; } - public Value setLineCap(Value... args) { + private Value setLineCap(Value... args) { graphics.setLineCap(StrokeLineCap.values()[args[0].asInt()]); return NumberValue.ZERO; } - public Value setLineJoin(Value... args) { + private Value setLineJoin(Value... args) { graphics.setLineJoin(StrokeLineJoin.values()[args[0].asInt()]); return NumberValue.ZERO; } - public Value setLineWidth(Value... args) { - graphics.setLineWidth(args[0].asNumber()); - return NumberValue.ZERO; - } - - public Value setMiterLimit(Value... args) { - graphics.setMiterLimit(args[0].asNumber()); - return NumberValue.ZERO; - } - - public Value setStroke(Value... args) { + private Value setStroke(Value... args) { graphics.setStroke((Color) args[0].raw()); return NumberValue.ZERO; } - public Value setTextAlign(Value... args) { + private Value setTextAlign(Value... args) { graphics.setTextAlign(TextAlignment.values()[args[0].asInt()]); return NumberValue.ZERO; } - public Value setTextBaseline(Value... args) { + private Value setTextBaseline(Value... args) { graphics.setTextBaseline(VPos.values()[args[0].asInt()]); return NumberValue.ZERO; } - public Value stroke(Value... args) { - graphics.stroke(); - return NumberValue.ZERO; - } - - public Value strokeArc(Value... args) { + private Value strokeArc(Value... args) { graphics.strokeArc(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber(), @@ -1052,19 +934,7 @@ public Value strokeArc(Value... args) { return NumberValue.ZERO; } - public Value strokeLine(Value... args) { - graphics.strokeLine(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value strokeOval(Value... args) { - graphics.strokeOval(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value strokePolygon(Value... args) { + private Value strokePolygon(Value... args) { final ArrayValue xarr = (ArrayValue) args[0]; final ArrayValue yarr = (ArrayValue) args[1]; @@ -1080,7 +950,7 @@ public Value strokePolygon(Value... args) { return NumberValue.ZERO; } - public Value strokePolyline(Value... args) { + private Value strokePolyline(Value... args) { final ArrayValue xarr = (ArrayValue) args[0]; final ArrayValue yarr = (ArrayValue) args[1]; @@ -1096,20 +966,14 @@ public Value strokePolyline(Value... args) { return NumberValue.ZERO; } - public Value strokeRect(Value... args) { - graphics.strokeRect(args[0].asNumber(), args[1].asNumber(), - args[2].asNumber(), args[3].asNumber()); - return NumberValue.ZERO; - } - - public Value strokeRoundRect(Value... args) { + private Value strokeRoundRect(Value... args) { graphics.strokeRoundRect(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber() ); return NumberValue.ZERO; } - public Value strokeText(Value... args) { + private Value strokeText(Value... args) { if (args.length < 4) { // str x y graphics.strokeText(args[0].asString(), args[1].asNumber(), @@ -1121,18 +985,13 @@ public Value strokeText(Value... args) { return NumberValue.ZERO; } - public Value transform(Value... args) { + private Value transform(Value... args) { graphics.transform(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); return NumberValue.ZERO; } - public Value translate(Value... args) { - graphics.translate(args[0].asNumber(), args[1].asNumber()); - return NumberValue.ZERO; - } - @Override public String toString() { return "GraphicsFXValue " + asString(); From 33f1057ebd242e2303f93b47a4df1b7653c18c8b Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 11:39:26 +0300 Subject: [PATCH 154/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82=D0=BE?= =?UTF-8?q?=D1=80=20enumOrdinal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Converters.java | 8 +++++ .../annimon/ownlang/lib/modules/canvasfx.java | 36 ++++--------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java index b8956813..9a91825a 100644 --- a/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -29,6 +29,10 @@ public interface VoidToStringFunction { String apply(); } + public interface VoidToEnumFunction> { + Enum apply(); + } + public interface BooleanToVoidFunction { void apply(boolean b); } @@ -89,6 +93,10 @@ public static FunctionValue voidToString(VoidToStringFunction f) { return new FunctionValue(args -> new StringValue(f.apply())); } + public static > FunctionValue enumOrdinal(VoidToEnumFunction f) { + return new FunctionValue(args -> NumberValue.of(f.apply().ordinal())); + } + public static FunctionValue booleanToVoid(BooleanToVoidFunction f) { return new FunctionValue(args -> { Arguments.check(1, args.length); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java index 660ceac8..c842c45c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java @@ -689,17 +689,17 @@ private void init() { set("fillRoundRect", this::fillRoundRect); set("fillText", this::fillText); set("getFill", this::getFill); - set("getFillRule", this::getFillRule); + set("getFillRule", enumOrdinal(graphics::getFillRule)); set("getGlobalAlpha", voidToDouble(graphics::getGlobalAlpha)); - set("getGlobalBlendMode", this::getGlobalBlendMode); - set("getLineCap", this::getLineCap); + set("getGlobalBlendMode", enumOrdinal(graphics::getGlobalBlendMode)); + set("getLineCap", enumOrdinal(graphics::getLineCap)); set("getLineDashOffset", voidToDouble(graphics::getLineDashOffset)); - set("getLineJoin", this::getLineJoin); + set("getLineJoin", enumOrdinal(graphics::getLineJoin)); set("getLineWidth", voidToDouble(graphics::getLineWidth)); set("getMiterLimit", voidToDouble(graphics::getMiterLimit)); set("getStroke", this::getStroke); - set("getTextAlign", this::getTextAlign); - set("getTextBaseline", this::getTextBaseline); + set("getTextAlign", enumOrdinal(graphics::getTextAlign)); + set("getTextBaseline", enumOrdinal(graphics::getTextBaseline)); set("isPointInPath", this::isPointInPath); set("lineTo", double2ToVoid(graphics::lineTo)); set("moveTo", double2ToVoid(graphics::moveTo)); @@ -846,34 +846,10 @@ private Value getFill(Value... args) { return new ColorValue((Color)graphics.getFill()); } - private Value getFillRule(Value... args) { - return NumberValue.of(graphics.getFillRule().ordinal()); - } - - private Value getGlobalBlendMode(Value... args) { - return NumberValue.of(graphics.getGlobalBlendMode().ordinal()); - } - - private Value getLineCap(Value... args) { - return NumberValue.of(graphics.getLineCap().ordinal()); - } - - private Value getLineJoin(Value... args) { - return NumberValue.of(graphics.getLineJoin().ordinal()); - } - private Value getStroke(Value... args) { return new ColorValue((Color)graphics.getStroke()); } - private Value getTextAlign(Value... args) { - return NumberValue.of(graphics.getTextAlign().ordinal()); - } - - private Value getTextBaseline(Value... args) { - return NumberValue.of(graphics.getTextBaseline().ordinal()); - } - private Value isPointInPath(Value... args) { return NumberValue.fromBoolean(graphics.isPointInPath(args[0].asNumber(), args[1].asNumber())); } From deab3beaf85064ee1c5ff56eb7627610ad5ba715 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 12:58:13 +0300 Subject: [PATCH 155/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B4=D0=B0=D0=BC=D0=BF=D0=B5=D1=80=20=D1=88?= =?UTF-8?q?=D0=B0=D0=B3=D0=BE=D0=B2=20=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/utils/OptimizationDumper.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java diff --git a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java new file mode 100644 index 00000000..52bb4630 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java @@ -0,0 +1,107 @@ +package com.annimon.ownlang.utils; + +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.SourceLoader; +import com.annimon.ownlang.parser.ast.Node; +import com.annimon.ownlang.parser.optimization.ConstantFolding; +import com.annimon.ownlang.parser.optimization.ConstantPropagation; +import com.annimon.ownlang.parser.optimization.DeadCodeElimination; +import com.annimon.ownlang.parser.optimization.ExpressionSimplification; +import com.annimon.ownlang.parser.optimization.InstructionCombining; +import com.annimon.ownlang.parser.optimization.Optimizable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class OptimizationDumper { + + private static final Optimizable[] OPTIMIZATIONS = new Optimizable[] { + new ConstantFolding(), + new ConstantPropagation(), + new DeadCodeElimination(), + new ExpressionSimplification(), + new InstructionCombining() + }; + private static final File WORK_DIR = new File("optimizations"); + + public static void main(String[] args) throws Exception { + WORK_DIR.mkdir(); + final String input = (args.length >= 1) ? args[0] : "program.own"; + final Map optimizationSteps = getOptimizationSteps(input); + writeStepsToFile(optimizationSteps); + writeSummary(optimizationSteps); + } + + private static Map getOptimizationSteps(String input) throws IOException { + final Map result = new LinkedHashMap<>(); + Node node = Parser.parse(Lexer.tokenize(SourceLoader.readSource(input))); + int optimizationPasses = 1; + int lastBatchModificationCount; + int batchModificationCount = 0; + result.put("Source", node.toString()); + do { + lastBatchModificationCount = batchModificationCount; + batchModificationCount = 0; + for (Optimizable optimization : OPTIMIZATIONS) { + final String lastSource = node.toString(); + node = optimization.optimize(node); + final String currentSource = node.toString(); + if (!lastSource.equals(currentSource)) { + final String optName = String.format("%s, %d pass", + optimization.getClass().getSimpleName(), + optimizationPasses); + result.put(optName, node.toString()); + } + batchModificationCount += optimization.optimizationsCount(); + } + optimizationPasses++; + } while (lastBatchModificationCount != batchModificationCount); + return result; + } + + private static void writeStepsToFile(Map optimizationSteps) throws IOException { + Arrays.stream(WORK_DIR.listFiles((d, name) -> name.endsWith(".txt"))) + .forEach(File::delete); + + int counter = 1; + for (Map.Entry entry : optimizationSteps.entrySet()) { + final String filename = String.format("file_%d.txt", counter++); + final File file = new File(WORK_DIR, filename); + writeContent(file, writer -> { + writer.append(entry.getKey()); + writer.append("\n\n"); + writer.append(entry.getValue()); + }); + } + } + + private static void writeSummary(final Map optimizationSteps) throws IOException { + final StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : optimizationSteps.entrySet()) { + sb.append(entry.getKey()); + sb.append("\n\n"); + sb.append(entry.getValue()); + sb.append("\n\n-----------\n\n"); + } + writeContent(new File(WORK_DIR, "summary.txt"), + writer -> writer.write(sb.toString())); + } + + private static void writeContent(File file, ThrowableConsumer consumer) throws IOException { + try (OutputStream out = new FileOutputStream(file); + OutputStreamWriter writer = new OutputStreamWriter(out)) { + consumer.accept(writer); + } + } + + interface ThrowableConsumer { + void accept(T t) throws IOException; + } +} From c220450ac2d91b3e0e4386b630d625c36c9cc731 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 13:02:14 +0300 Subject: [PATCH 156/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D1=81=D0=B2=D1=91=D1=80=D1=82=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=B0=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D1=83=D0=BC=D0=BE=D0=BB=D1=87=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/parser/optimization/VariablesGrabber.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index c1584621..cd4e2a10 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.parser.optimization; -import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.Accessible; @@ -9,10 +8,8 @@ import com.annimon.ownlang.parser.ast.AssignmentExpression; import com.annimon.ownlang.parser.ast.ContainerAccessExpression; import com.annimon.ownlang.parser.ast.DestructuringAssignmentStatement; -import com.annimon.ownlang.parser.ast.Expression; import com.annimon.ownlang.parser.ast.ForeachArrayStatement; import com.annimon.ownlang.parser.ast.ForeachMapStatement; -import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.MatchExpression; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.UnaryExpression; @@ -136,10 +133,11 @@ protected boolean visit(Arguments in, Arguments out, Map t for (Argument argument : in) { final String variableName = argument.getName(); final VariableInfo var = variableInfo(t, variableName); + /* No need to add value - it is optional arguments final Expression expr = argument.getValueExpr(); if (expr != null && isValue(expr)) { var.value = ((ValueExpression) expr).value; - } + }*/ t.put(variableName, var); } return super.visit(in, out, t); From 32d4025f23876de921d7bd7c354f95db19fba968 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 13:05:32 +0300 Subject: [PATCH 157/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=B4=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=20=D0=B4=D0=B0=D0=BC=D0=BF=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=20=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java index 52bb4630..8babf0b8 100644 --- a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java +++ b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java @@ -96,7 +96,7 @@ private static void writeSummary(final Map optimizationSteps) th private static void writeContent(File file, ThrowableConsumer consumer) throws IOException { try (OutputStream out = new FileOutputStream(file); - OutputStreamWriter writer = new OutputStreamWriter(out)) { + OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8")) { consumer.accept(writer); } } From 28ceb44722ea9d9035bb08d2e2ed616847260922 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 13:09:35 +0300 Subject: [PATCH 158/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 9b7e46f3..d060063b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /.gradle/ +/build/ /dist/ /store/ +/optimizations/ /nbproject/private/ \ No newline at end of file From 33991e3db83e1100c31927723f55171a914c2684 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 15:37:53 +0300 Subject: [PATCH 159/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20PrintVisitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/parser/visitors/PrintVisitor.java | 250 +++++++++++++----- 1 file changed, 188 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 91f89108..57c1da01 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -1,9 +1,12 @@ package com.annimon.ownlang.parser.visitors; +import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.Iterator; +import java.util.Map; public class PrintVisitor implements ResultVisitor { @@ -15,13 +18,21 @@ public PrintVisitor() { @Override public StringBuilder visit(ArrayExpression s, StringBuilder t) { + t.append('['); + final Iterator it = s.elements.iterator(); + if (it.hasNext()) { + it.next().accept(this, t); + while (it.hasNext()) { + t.append(", "); + it.next().accept(this, t); + } + } + t.append(']'); return t; } @Override public StringBuilder visit(AssignmentExpression s, StringBuilder t) { - newLine(t); - printIndent(t); ((Node) s.target).accept(this, t); t.append(' ').append((s.operation == null) ? "" : s.operation); t.append("= "); @@ -39,25 +50,30 @@ public StringBuilder visit(BinaryExpression s, StringBuilder t) { @Override public StringBuilder visit(BlockStatement s, StringBuilder t) { - if (indent > 0) { + decreaseIndent(); + if (indent >= 0) { t.append('{'); } increaseIndent(); + for (Statement statement : s.statements) { + newLine(t); + printIndent(t); statement.accept(this, t); } + decreaseIndent(); - if (indent > 0) { + if (indent >= 0) { newLine(t); + printIndent(t); t.append('}'); } + increaseIndent(); return t; } @Override public StringBuilder visit(BreakStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("break"); return t; } @@ -72,104 +88,157 @@ public StringBuilder visit(ConditionalExpression s, StringBuilder t) { @Override public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) { + visitVariable(s.variable, t); + for (Expression index : s.indices) { + t.append('['); + index.accept(this, t); + t.append(']'); + } return t; } @Override public StringBuilder visit(ContinueStatement s, StringBuilder t) { - printIndent(t); t.append("continue"); - newLine(t); return t; } @Override public StringBuilder visit(DoWhileStatement s, StringBuilder t) { + t.append("do "); + + increaseIndent(); + s.statement.accept(this, t); + decreaseIndent(); + + t.append(" while ("); + s.condition.accept(this, t); + t.append(")"); return t; } @Override public StringBuilder visit(DestructuringAssignmentStatement s, StringBuilder t) { - printIndent(t); - t.append("extract ("); + t.append("extract("); final Iterator it = s.variables.iterator(); if (it.hasNext()) { - String variable = it.next(); - t.append(variable == null ? " " : variable); + visitNullableVariable(it.next(), t); while (it.hasNext()) { - variable = it.next(); - t.append(variable == null ? " " : variable); + t.append(", "); + visitNullableVariable(it.next(), t); } } t.append(") = "); s.containerExpression.accept(this, t); - newLine(t); + return t; + } + + @Override + public StringBuilder visit(ExprStatement s, StringBuilder t) { + s.expr.accept(this, t); return t; } @Override public StringBuilder visit(ForStatement s, StringBuilder t) { + t.append("for ("); + s.initialization.accept(this, t); + t.append(", "); + s.termination.accept(this, t); + t.append(", "); + s.increment.accept(this, t); + t.append(") "); + + increaseIndent(); + s.statement.accept(this, t); + decreaseIndent(); return t; } @Override public StringBuilder visit(ForeachArrayStatement s, StringBuilder t) { + t.append("for ("); + visitVariable(s.variable, t); + t.append(" : "); + s.container.accept(this, t); + t.append(") "); + + increaseIndent(); + s.body.accept(this, t); + decreaseIndent(); return t; } @Override public StringBuilder visit(ForeachMapStatement s, StringBuilder t) { + t.append("for ("); + visitVariable(s.key, t); + t.append(", "); + visitVariable(s.value, t); + t.append(" : "); + s.container.accept(this, t); + t.append(") "); + + increaseIndent(); + s.body.accept(this, t); + decreaseIndent(); return t; } @Override public StringBuilder visit(FunctionDefineStatement s, StringBuilder t) { - return t; + t.append("def "); + visitVariable(s.name, t); + t.append(s.arguments); + return visitFunctionBody(s.body, t); } @Override public StringBuilder visit(FunctionReferenceExpression s, StringBuilder t) { - return t; - } - - @Override - public StringBuilder visit(ExprStatement s, StringBuilder t) { - printIndent(t); - s.expr.accept(this, t); - newLine(t); + t.append("::"); + visitVariable(s.name, t); return t; } @Override public StringBuilder visit(FunctionalExpression s, StringBuilder t) { + if (s.functionExpr instanceof ValueExpression && ((ValueExpression)s.functionExpr).value.type() == Types.STRING) { + t.append(((ValueExpression)s.functionExpr).value.asString()); + } else { + s.functionExpr.accept(this, t); + } + t.append("("); + boolean firstElement = true; + for (Expression expr : s.arguments) { + if (firstElement) firstElement = false; + else t.append(", "); + expr.accept(this, t); + } + t.append(")"); return t; } @Override public StringBuilder visit(IfStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("if ("); s.expression.accept(this, t); t.append(") "); + increaseIndent(); s.ifStatement.accept(this, t); decreaseIndent(); + if (s.elseStatement != null) { newLine(t); printIndent(t); t.append("else "); - increaseIndent(); s.elseStatement.accept(this, t); - decreaseIndent(); } return t; } @Override public StringBuilder visit(IncludeStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("include "); s.expression.accept(this, t); return t; @@ -177,18 +246,51 @@ public StringBuilder visit(IncludeStatement s, StringBuilder t) { @Override public StringBuilder visit(MapExpression s, StringBuilder t) { + if (s.elements.isEmpty()) { + t.append("{ }"); + return t; + } + t.append('{'); + increaseIndent(); + boolean firstElement = true; + for (Map.Entry entry : s.elements.entrySet()) { + if (firstElement) firstElement = false; + else t.append(","); + newLine(t); + printIndent(t); + entry.getKey().accept(this, t); + t.append(" : "); + entry.getValue().accept(this, t); + } + decreaseIndent(); + newLine(t); + printIndent(t); + t.append('}'); return t; } @Override public StringBuilder visit(MatchExpression s, StringBuilder t) { + t.append("match "); + s.expression.accept(this, t); + t.append(" {"); + + increaseIndent(); + for (MatchExpression.Pattern pattern : s.patterns) { + newLine(t); + printIndent(t); + t.append("case "); + t.append(pattern); + } + decreaseIndent(); + newLine(t); + printIndent(t); + t.append("}"); return t; } @Override public StringBuilder visit(PrintStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("print "); s.expression.accept(this, t); return t; @@ -196,8 +298,6 @@ public StringBuilder visit(PrintStatement s, StringBuilder t) { @Override public StringBuilder visit(PrintlnStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("println "); s.expression.accept(this, t); return t; @@ -205,8 +305,6 @@ public StringBuilder visit(PrintlnStatement s, StringBuilder t) { @Override public StringBuilder visit(ReturnStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("return "); s.expression.accept(this, t); return t; @@ -229,6 +327,7 @@ public StringBuilder visit(UnaryExpression s, StringBuilder t) { case DECREMENT_POSTFIX: s.expr1.accept(this, t); t.append(s.operation); + break; default: t.append(s.operation); s.expr1.accept(this, t); @@ -236,6 +335,13 @@ public StringBuilder visit(UnaryExpression s, StringBuilder t) { return t; } + @Override + public StringBuilder visit(UseStatement s, StringBuilder t) { + t.append("use "); + s.expression.accept(this, t); + return t; + } + @Override public StringBuilder visit(ValueExpression s, StringBuilder t) { switch (s.value.type()) { @@ -245,6 +351,16 @@ public StringBuilder visit(ValueExpression s, StringBuilder t) { str = str.replace("\t", "\\t"); t.append('"').append(str).append('"'); break; + case Types.FUNCTION: { + final Function function = ((FunctionValue) s.value).getValue(); + if (function instanceof UserDefinedFunction) { + UserDefinedFunction f = (UserDefinedFunction) function; + t.append("def"); + t.append(f.arguments); + return visitFunctionBody(f.body, t); + } else t.append(function); + break; + } default: t.append(s.value.raw()); break; @@ -254,51 +370,61 @@ public StringBuilder visit(ValueExpression s, StringBuilder t) { @Override public StringBuilder visit(VariableExpression s, StringBuilder t) { - boolean extendedWordVariable = false; - for (char ch : s.name.toCharArray()) { - if (!Character.isLetterOrDigit(ch)) { - extendedWordVariable = true; - break; - } - } - if (extendedWordVariable) { - t.append('`').append(s.name).append('`'); - } else { - t.append(s.name); - } - return t; + return visitVariable(s.name, t); } @Override public StringBuilder visit(WhileStatement s, StringBuilder t) { - newLine(t); - printIndent(t); t.append("while ("); s.condition.accept(this, t); - t.append(") {"); - newLine(t); + t.append(") "); + increaseIndent(); s.statement.accept(this, t); decreaseIndent(); - newLine(t); - t.append('}'); return t; } - @Override - public StringBuilder visit(UseStatement s, StringBuilder t) { - newLine(t); - printIndent(t); - t.append("use "); - s.expression.accept(this, t); + public StringBuilder visitNullableVariable(String name, StringBuilder t) { + if (name == null) { + t.append(' '); + return t; + } + return visitVariable(name, t); + } + + public StringBuilder visitVariable(String name, StringBuilder t) { + boolean extendedWordVariable = false; + for (char ch : name.toCharArray()) { + if (!Character.isLetterOrDigit(ch)) { + extendedWordVariable = true; + break; + } + } + if (extendedWordVariable) { + t.append('`').append(name).append('`'); + } else { + t.append(name); + } return t; } + private StringBuilder visitFunctionBody(Statement s, StringBuilder t) { + if (s instanceof ReturnStatement) { + t.append(" = "); + ((ReturnStatement)s).expression.accept(this, t); + } else { + increaseIndent(); + s.accept(this, t); + decreaseIndent(); + } + return t; + } private void newLine(StringBuilder t) { t.append(System.lineSeparator()); } - + private void printIndent(StringBuilder sb) { for (int i = 0; i < indent; i++) { sb.append(' '); From f2d96ef8956436f5af13b1d73f1635919a4945ad Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 30 Jul 2016 15:39:47 +0300 Subject: [PATCH 160/448] =?UTF-8?q?OptimizationDumper=20=D1=82=D0=B5=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D1=8C=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D1=82=20PrintVisitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/utils/OptimizationDumper.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java index 8babf0b8..45df8bba 100644 --- a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java +++ b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java @@ -10,6 +10,7 @@ import com.annimon.ownlang.parser.optimization.ExpressionSimplification; import com.annimon.ownlang.parser.optimization.InstructionCombining; import com.annimon.ownlang.parser.optimization.Optimizable; +import com.annimon.ownlang.parser.visitors.PrintVisitor; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -45,19 +46,19 @@ private static Map getOptimizationSteps(String input) throws IOE int optimizationPasses = 1; int lastBatchModificationCount; int batchModificationCount = 0; - result.put("Source", node.toString()); + result.put("Source", nodeToString(node)); do { lastBatchModificationCount = batchModificationCount; batchModificationCount = 0; for (Optimizable optimization : OPTIMIZATIONS) { - final String lastSource = node.toString(); + final String lastSource = nodeToString(node); node = optimization.optimize(node); - final String currentSource = node.toString(); + final String currentSource = nodeToString(node); if (!lastSource.equals(currentSource)) { final String optName = String.format("%s, %d pass", optimization.getClass().getSimpleName(), optimizationPasses); - result.put(optName, node.toString()); + result.put(optName, nodeToString(node)); } batchModificationCount += optimization.optimizationsCount(); } @@ -66,6 +67,11 @@ private static Map getOptimizationSteps(String input) throws IOE return result; } + private static String nodeToString(Node n) { +// return n.toString(); + return n.accept(new PrintVisitor(), new StringBuilder()).toString(); + } + private static void writeStepsToFile(Map optimizationSteps) throws IOException { Arrays.stream(WORK_DIR.listFiles((d, name) -> name.endsWith(".txt"))) .forEach(File::delete); @@ -84,12 +90,14 @@ private static void writeStepsToFile(Map optimizationSteps) thro private static void writeSummary(final Map optimizationSteps) throws IOException { final StringBuilder sb = new StringBuilder(); + sb.append("[pr]"); for (Map.Entry entry : optimizationSteps.entrySet()) { sb.append(entry.getKey()); - sb.append("\n\n"); + sb.append("\n[code own]"); sb.append(entry.getValue()); - sb.append("\n\n-----------\n\n"); + sb.append("[/code][sl]\n"); } + sb.append("[/pr]"); writeContent(new File(WORK_DIR, "summary.txt"), writer -> writer.write(sb.toString())); } From 75b5766d3231baf7502a31ddd748de742bb38f4e Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Sat, 30 Jul 2016 20:26:12 +0300 Subject: [PATCH 161/448] =?UTF-8?q?=D0=A3=D1=81=D1=82=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=89=D0=B8=D0=BA=20JDK=20=D0=B4=D0=BB=D1=8F=20travis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cc0f6611..b2f44baf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,9 @@ before_install: after_success: - ./gradlew proguard - - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@store/OwnLang.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang \ No newline at end of file + - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@store/OwnLang.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang + +addons: + apt: + packages: + - oracle-java8-installer From 55d892727ddf7a45edf029bb983e09dabdf40e3c Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 10:55:06 +0300 Subject: [PATCH 162/448] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=20=D0=BF=D0=B0=D1=80=D1=81=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Parser.java | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index a89c9215..0490fe4c 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -28,23 +28,23 @@ public static Statement parse(List tokens) { private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); - private static final EnumMap assignOperator; + private static final EnumMap ASSIGN_OPERATORS; static { - assignOperator = new EnumMap(TokenType.class); - assignOperator.put(TokenType.EQ, null); - assignOperator.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD); - assignOperator.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT); - assignOperator.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY); - assignOperator.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE); - assignOperator.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER); - assignOperator.put(TokenType.AMPEQ, BinaryExpression.Operator.AND); - assignOperator.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR); - assignOperator.put(TokenType.BAREQ, BinaryExpression.Operator.OR); - assignOperator.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH); - assignOperator.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT); - assignOperator.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT); - assignOperator.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT); - assignOperator.put(TokenType.ATEQ, BinaryExpression.Operator.AT); + ASSIGN_OPERATORS = new EnumMap(TokenType.class); + ASSIGN_OPERATORS.put(TokenType.EQ, null); + ASSIGN_OPERATORS.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD); + ASSIGN_OPERATORS.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT); + ASSIGN_OPERATORS.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY); + ASSIGN_OPERATORS.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE); + ASSIGN_OPERATORS.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER); + ASSIGN_OPERATORS.put(TokenType.AMPEQ, BinaryExpression.Operator.AND); + ASSIGN_OPERATORS.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR); + ASSIGN_OPERATORS.put(TokenType.BAREQ, BinaryExpression.Operator.OR); + ASSIGN_OPERATORS.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH); + ASSIGN_OPERATORS.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT); + ASSIGN_OPERATORS.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT); + ASSIGN_OPERATORS.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT); + ASSIGN_OPERATORS.put(TokenType.ATEQ, BinaryExpression.Operator.AT); } private final List tokens; @@ -185,7 +185,7 @@ private DestructuringAssignmentStatement destructuringAssignment() { } else { variables.add(null); } - match(TokenType.COMMA); + consume(TokenType.COMMA); } consume(TokenType.EQ); return new DestructuringAssignmentStatement(variables, expression()); @@ -227,36 +227,37 @@ && lookMatch(foreachIndex + 2, TokenType.WORD) && lookMatch(foreachIndex + 3, To // for key, value : arr || for (key, value : arr) return foreachMapStatement(); } - - boolean openParen = match(TokenType.LPAREN); // необязательные скобки + + // for (init, condition, increment) body + boolean optParentheses = match(TokenType.LPAREN); final Statement initialization = assignmentStatement(); consume(TokenType.COMMA); final Expression termination = expression(); consume(TokenType.COMMA); final Statement increment = assignmentStatement(); - if (openParen) consume(TokenType.RPAREN); // скобки + if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses final Statement statement = statementOrBlock(); return new ForStatement(initialization, termination, increment, statement); } private ForeachArrayStatement foreachArrayStatement() { - boolean openParen = match(TokenType.LPAREN); // необязательные скобки + boolean optParentheses = match(TokenType.LPAREN); final String variable = consume(TokenType.WORD).getText(); consume(TokenType.COLON); final Expression container = expression(); - if (openParen) consume(TokenType.RPAREN); // скобки + if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses final Statement statement = statementOrBlock(); return new ForeachArrayStatement(variable, container, statement); } private ForeachMapStatement foreachMapStatement() { - boolean openParen = match(TokenType.LPAREN); // необязательные скобки + boolean optParentheses = match(TokenType.LPAREN); final String key = consume(TokenType.WORD).getText(); consume(TokenType.COMMA); final String value = consume(TokenType.WORD).getText(); consume(TokenType.COLON); final Expression container = expression(); - if (openParen) consume(TokenType.RPAREN); // скобки + if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses final Statement statement = statementOrBlock(); return new ForeachMapStatement(key, value, container, statement); } @@ -426,13 +427,13 @@ private Expression assignmentStrict() { } final TokenType currentType = get(0).getType(); - if (!assignOperator.containsKey(currentType)) { + if (!ASSIGN_OPERATORS.containsKey(currentType)) { pos = position; return null; } match(currentType); - final BinaryExpression.Operator op = assignOperator.get(currentType); + final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType); final Expression expression = expression(); return new AssignmentExpression(op, (Accessible) targetExpr, expression); From 06fe09ce98f3820cc7d1e4e7b5384f58d5c873bd Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 12:48:17 +0300 Subject: [PATCH 163/448] =?UTF-8?q?ContainerAccessExpression=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D0=B5=D1=82=20=D1=81=20Expression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/ast/ContainerAccessExpression.java | 24 +++++++++++++++---- .../optimization/OptimizationVisitor.java | 6 +++-- .../parser/optimization/VariablesGrabber.java | 7 ++++-- .../parser/visitors/AbstractVisitor.java | 1 + .../ownlang/parser/visitors/PrintVisitor.java | 2 +- .../parser/visitors/VariablePrinter.java | 6 ----- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 35266105..794b8429 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -10,14 +10,28 @@ */ public final class ContainerAccessExpression implements Expression, Accessible { - public final String variable; + public final Expression root; public final List indices; + private boolean rootIsVariable; public ContainerAccessExpression(String variable, List indices) { - this.variable = variable; + this(new VariableExpression(variable), indices); + } + + public ContainerAccessExpression(Expression root, List indices) { + rootIsVariable = root instanceof VariableExpression; + this.root = root; this.indices = indices; } - + + public boolean rootIsVariable() { + return rootIsVariable; + } + + public Expression getRoot() { + return root; + } + @Override public Value eval() { return get(); @@ -60,7 +74,7 @@ public Value set(Value value) { } public Value getContainer() { - Value container = Variables.get(variable); + Value container = root.eval(); final int last = indices.size() - 1; for (int i = 0; i < last; i++) { final Value index = index(i); @@ -108,6 +122,6 @@ public R accept(ResultVisitor visitor, T t) { @Override public String toString() { - return variable + indices; + return root.toString() + indices; } } diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 399bd218..46696ada 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -87,8 +87,10 @@ public Node visit(ConditionalExpression s, T t) { @Override public Node visit(ContainerAccessExpression s, T t) { + final Node root = s.root.accept(this, t); + boolean changed = (root != s.root); + final List indices = new ArrayList<>(s.indices.size()); - boolean changed = false; for (Expression expression : s.indices) { final Node node = expression.accept(this, t); if (node != expression) { @@ -97,7 +99,7 @@ public Node visit(ContainerAccessExpression s, T t) { indices.add((Expression) node); } if (changed) { - return new ContainerAccessExpression(s.variable, indices); + return new ContainerAccessExpression((Expression) root, indices); } return s; } diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index cd4e2a10..7ea484eb 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -100,8 +100,11 @@ public Node visit(UnaryExpression s, Map t) { t.put(variableName, variableInfo(t, variableName)); } if (s.expr1 instanceof ContainerAccessExpression) { - final String variableName = ((ContainerAccessExpression) s.expr1).variable; - t.put(variableName, variableInfo(t, variableName)); + ContainerAccessExpression conExpr = (ContainerAccessExpression) s.expr1; + if (conExpr.rootIsVariable()) { + final String variableName = ((VariableExpression) conExpr.root).name; + t.put(variableName, variableInfo(t, variableName)); + } } } return super.visit(s, t); diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index c669939b..3dbc9c36 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -47,6 +47,7 @@ public void visit(ConditionalExpression s) { @Override public void visit(ContainerAccessExpression s) { + s.root.accept(this); for (Expression index : s.indices) { index.accept(this); } diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 57c1da01..b98bf9f8 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -88,7 +88,7 @@ public StringBuilder visit(ConditionalExpression s, StringBuilder t) { @Override public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) { - visitVariable(s.variable, t); + s.root.accept(this, t); for (Expression index : s.indices) { t.append('['); index.accept(this, t); diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java b/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java index 4f29f3a1..ed2b6e2b 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java @@ -14,12 +14,6 @@ public void visit(AssignmentExpression s) { super.visit(s); Console.println(s.target); } - - @Override - public void visit(ContainerAccessExpression s) { - super.visit(s); - Console.println(s.variable); - } @Override public void visit(VariableExpression s) { From 5f71e9a9be87ee09735ed199afd4d9a9168cbbe0 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 14:09:23 +0300 Subject: [PATCH 164/448] =?UTF-8?q?=D0=A6=D0=B5=D0=BF=D0=BE=D1=87=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=BE=D0=B2=20=D1=84?= =?UTF-8?q?=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Parser.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 0490fe4c..8c51dc9e 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -159,7 +159,7 @@ private Statement statement() { return new ExprStatement(match()); } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return new ExprStatement(function(qualifiedName())); + return new ExprStatement(functionChain(qualifiedName())); } return assignmentStatement(); } @@ -297,6 +297,23 @@ private Statement statementBody() { return statementOrBlock(); } + private Expression functionChain(Expression qualifiedNameExpr) { + // f1().f2().f3() || f1().key + final Expression expr = function(qualifiedNameExpr); + if (lookMatch(0, TokenType.DOT)) { + final List indices = variableSuffix(); + if (indices == null | indices.isEmpty()) return expr; + + if (lookMatch(0, TokenType.LPAREN)) { + // next function call + return functionChain(new ContainerAccessExpression(expr, indices)); + } + // container access + return new ContainerAccessExpression(expr, indices); + } + return expr; + } + private FunctionalExpression function(Expression qualifiedNameExpr) { // function(arg1, arg2, ...) consume(TokenType.LPAREN); @@ -684,18 +701,18 @@ private Expression primary() { } return variable(); } - + private Expression variable() { // function(... if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return function(new ValueExpression(consume(TokenType.WORD).getText())); + return functionChain(new ValueExpression(consume(TokenType.WORD).getText())); } final Expression qualifiedNameExpr = qualifiedName(); if (qualifiedNameExpr != null) { // variable(args) || arr["key"](args) || obj.key(args) if (lookMatch(0, TokenType.LPAREN)) { - return function(qualifiedNameExpr); + return functionChain(qualifiedNameExpr); } // postfix increment/decrement if (match(TokenType.PLUSPLUS)) { @@ -727,7 +744,7 @@ private Expression qualifiedName() { } return new ContainerAccessExpression(current.getText(), indices); } - + private List variableSuffix() { // .key1.arr1[expr1][expr2].key2 if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) { From b0ee12d4b895943f2ee2cb4c23d3c312a7602447 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 14:10:59 +0300 Subject: [PATCH 165/448] =?UTF-8?q?=D0=9F=D0=BE=D1=81=D0=BB=D0=B5=D0=B4?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 8c51dc9e..a3c65b30 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -298,8 +298,11 @@ private Statement statementBody() { } private Expression functionChain(Expression qualifiedNameExpr) { - // f1().f2().f3() || f1().key + // f1()()() || f1().f2().f3() || f1().key final Expression expr = function(qualifiedNameExpr); + if (lookMatch(0, TokenType.LPAREN)) { + return functionChain(expr); + } if (lookMatch(0, TokenType.DOT)) { final List indices = variableSuffix(); if (indices == null | indices.isEmpty()) return expr; From d7936c43ca3fafdc57539db2ec1b667564e777a0 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 14:11:40 +0300 Subject: [PATCH 166/448] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D1=8F=D1=8F=20=D0=BE=D0=B1=D1=91=D1=80?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20MatchExpr=20=D0=B2=20ExprStatement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index a3c65b30..c6df60c6 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -156,7 +156,7 @@ private Statement statement() { return functionDefine(); } if (match(TokenType.MATCH)) { - return new ExprStatement(match()); + return match(); } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return new ExprStatement(functionChain(qualifiedName())); From 4961cd20f6fb0c1f77ea8153a17aa16369db2d83 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 15:15:31 +0300 Subject: [PATCH 167/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/modules/std/indexOf.own | 13 +++++++++++ .../resources/modules/std/lastIndexOf.own | 13 +++++++++++ src/test/resources/other/functionChain.own | 23 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/test/resources/modules/std/indexOf.own create mode 100644 src/test/resources/modules/std/lastIndexOf.own create mode 100644 src/test/resources/other/functionChain.own diff --git a/src/test/resources/modules/std/indexOf.own b/src/test/resources/modules/std/indexOf.own new file mode 100644 index 00000000..176a40bc --- /dev/null +++ b/src/test/resources/modules/std/indexOf.own @@ -0,0 +1,13 @@ +use "std" + +def testIndexOf() { + assertEquals(3, indexOf("123/456/789", "/")) +} + +def testIndexOfIndex() { + assertEquals(7, indexOf("123/456/789", "/", 4)) +} + +def testIndexOfNonMatch() { + assertEquals(-1, indexOf("123", "/")) +} \ No newline at end of file diff --git a/src/test/resources/modules/std/lastIndexOf.own b/src/test/resources/modules/std/lastIndexOf.own new file mode 100644 index 00000000..fb194084 --- /dev/null +++ b/src/test/resources/modules/std/lastIndexOf.own @@ -0,0 +1,13 @@ +use "std" + +def testLastIndexOf() { + assertEquals(8, lastIndexOf("/123/456/789", "/")) +} + +def testLastIndexOfIndex() { + assertEquals(4, lastIndexOf("/123/456/789", "/", 6)) +} + +def testLastIndexOfNonMatch() { + assertEquals(-1, lastIndexOf("123", "/")) +} \ No newline at end of file diff --git a/src/test/resources/other/functionChain.own b/src/test/resources/other/functionChain.own new file mode 100644 index 00000000..a270a8f1 --- /dev/null +++ b/src/test/resources/other/functionChain.own @@ -0,0 +1,23 @@ +def f1() = {"func": ::f2} +def f2() = { + "functions" : { + "add" : def(a, b) = a + b + "mul" : def(a, b) = a * b + "negate" : def(a) = {"result" : -a} + } +} +def f3() = def() = def() = def() = "test" +def f4() = def() = ::f1 + +def testFunctionChain() { + assertEquals(5, f1().func().`functions`.add(2, 3)) + assertEquals(6, f1().func().`functions`.mul(2, 3)) +} + +def testCallChain() { + assertEquals("test", f3()()()()) +} + +def testBoth() { + assertEquals(-123, f4()()().func().`functions`.negate(123).result) +} \ No newline at end of file From f048da832528947bbff7c3e0160882aca3816fd0 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 14:47:08 +0300 Subject: [PATCH 168/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20functional::takewhile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/lib/modules/functional.java | 5 +- .../functions/functional_dropwhile.java | 53 +++++++++++++++++++ .../modules/functions/functional_filter.java | 21 +++++--- .../resources/modules/functional/stream.own | 11 ++++ 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java create mode 100644 src/test/resources/modules/functional/stream.own diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/lib/modules/functional.java index 8bedd852..7d95178c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functional.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functional.java @@ -22,9 +22,10 @@ public void init() { Functions.set("map", new functional_map()); Functions.set("flatmap", new functional_flatmap()); Functions.set("reduce", new functional_reduce()); - Functions.set("filter", new functional_filter()); + Functions.set("filter", new functional_filter(false)); Functions.set("sortby", new functional_sortby()); - + Functions.set("takewhile", new functional_filter(true)); + Functions.set("chain", new functional_chain()); Functions.set("combine", new functional_combine()); } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java new file mode 100644 index 00000000..8645b719 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java @@ -0,0 +1,53 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.List; + +import java.util.Map; + +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 consumer = ((FunctionValue) args[1]).getValue(); + if (container.type() == Types.ARRAY) { + return filterArray((ArrayValue) container, consumer); + } + + if (container.type() == Types.MAP) { + return filterMap((MapValue) container, consumer); + } + + throw new TypeException("Invalid first argument. Array or map expected"); + } + + private Value filterArray(ArrayValue array, Function predicate) { + final int size = array.size(); + final List values = new ArrayList(size); + for (Value value : array) { + if (predicate.execute(value) != NumberValue.ZERO) { + values.add(value); + } + } + final int newSize = values.size(); + return new ArrayValue(values.toArray(new Value[newSize])); + } + + private Value filterMap(MapValue map, Function predicate) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { + result.set(element.getKey(), element.getValue()); + } + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java index 8645b719..aa4ee0ce 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java @@ -4,11 +4,16 @@ import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.List; - import java.util.Map; public final class functional_filter implements Function { + private final boolean takeWhile; + + public functional_filter(boolean takeWhile) { + this.takeWhile = takeWhile; + } + @Override public Value execute(Value... args) { Arguments.check(2, args.length); @@ -17,36 +22,36 @@ public Value execute(Value... args) { } final Value container = args[0]; - final Function consumer = ((FunctionValue) args[1]).getValue(); + final Function predicate = ((FunctionValue) args[1]).getValue(); if (container.type() == Types.ARRAY) { - return filterArray((ArrayValue) container, consumer); + return filterArray((ArrayValue) container, predicate, takeWhile); } if (container.type() == Types.MAP) { - return filterMap((MapValue) container, consumer); + return filterMap((MapValue) container, predicate, takeWhile); } throw new TypeException("Invalid first argument. Array or map expected"); } - private Value filterArray(ArrayValue array, Function predicate) { + private Value filterArray(ArrayValue array, Function predicate, boolean takeWhile) { final int size = array.size(); final List values = new ArrayList(size); for (Value value : array) { if (predicate.execute(value) != NumberValue.ZERO) { values.add(value); - } + } else if (takeWhile) break; } final int newSize = values.size(); return new ArrayValue(values.toArray(new Value[newSize])); } - private Value filterMap(MapValue map, Function predicate) { + private Value filterMap(MapValue map, Function predicate, boolean takeWhile) { final MapValue result = new MapValue(map.size()); for (Map.Entry element : map) { if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { result.set(element.getKey(), element.getValue()); - } + } else if (takeWhile) break; } return result; } diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own new file mode 100644 index 00000000..19692093 --- /dev/null +++ b/src/test/resources/modules/functional/stream.own @@ -0,0 +1,11 @@ +use "functional" + +def testFunctionalChain() { + data = [1,2,3,4,5,6,7] + result = chain(data, + ::filter, def(x) = x <= 4, + ::sortby, def(x) = -x, + ::map, def(x) = x * 2, + ) + assertEquals([8,6,4,2], result) +} From 4e5fc7b73820109276ce3f830821146b9d513975 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 14:48:10 +0300 Subject: [PATCH 169/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20functional::dropwhile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/lib/modules/functional.java | 1 + .../functions/functional_dropwhile.java | 49 ++++++------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/lib/modules/functional.java index 7d95178c..0b2fe41a 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functional.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functional.java @@ -25,6 +25,7 @@ public void init() { Functions.set("filter", new functional_filter(false)); Functions.set("sortby", new functional_sortby()); Functions.set("takewhile", new functional_filter(true)); + Functions.set("dropwhile", new functional_dropwhile()); Functions.set("chain", new functional_chain()); Functions.set("combine", new functional_combine()); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java index 8645b719..ecafe92b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java @@ -2,52 +2,35 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public final class functional_filter implements Function { +public final class functional_dropwhile implements Function { @Override public Value execute(Value... args) { Arguments.check(2, args.length); + 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 consumer = ((FunctionValue) args[1]).getValue(); - if (container.type() == Types.ARRAY) { - return filterArray((ArrayValue) container, consumer); - } - - if (container.type() == Types.MAP) { - return filterMap((MapValue) container, consumer); - } - - throw new TypeException("Invalid first argument. Array or map expected"); + final Function predicate = ((FunctionValue) args[1]).getValue(); + return dropWhileArray((ArrayValue) container, predicate); } - private Value filterArray(ArrayValue array, Function predicate) { - final int size = array.size(); - final List values = new ArrayList(size); + private Value dropWhileArray(ArrayValue array, Function predicate) { + int skipCount = 0; for (Value value : array) { - if (predicate.execute(value) != NumberValue.ZERO) { - values.add(value); - } + if (predicate.execute(value) != NumberValue.ZERO) + skipCount++; + else break; } - final int newSize = values.size(); - return new ArrayValue(values.toArray(new Value[newSize])); - } - - private Value filterMap(MapValue map, Function predicate) { - final MapValue result = new MapValue(map.size()); - for (Map.Entry element : map) { - if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { - result.set(element.getKey(), element.getValue()); - } - } - return result; + + final int size = array.size(); + final Value[] result = new Value[size - skipCount]; + System.arraycopy(array.getCopyElements(), skipCount, result, 0, result.length); + return new ArrayValue(result); } } \ No newline at end of file From de07d86dd3e6ea9bb23fe84e9dfaca1c84c87e7b Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 14:49:02 +0300 Subject: [PATCH 170/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20functional::stream?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/MapValue.java | 14 ++- .../ownlang/lib/modules/functional.java | 1 + .../modules/functions/functional_stream.java | 106 ++++++++++++++++++ .../resources/modules/functional/stream.own | 40 ++++++- 4 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java diff --git a/src/main/java/com/annimon/ownlang/lib/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java index dd2d3b90..40ba519d 100644 --- a/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -20,7 +20,7 @@ public static MapValue merge(MapValue map1, MapValue map2) { result.map.putAll(map2.map); return result; } - + private final Map map; public MapValue(int size) { @@ -30,6 +30,18 @@ public MapValue(int size) { public MapValue(Map map) { this.map = map; } + + public ArrayValue toPairs() { + final int size = map.size(); + final ArrayValue result = new ArrayValue(size); + int index = 0; + for (Map.Entry entry : map.entrySet()) { + result.set(index++, new ArrayValue(new Value[] { + entry.getKey(), entry.getValue() + })); + } + return result; + } @Override public int type() { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/lib/modules/functional.java index 0b2fe41a..1ff29edc 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functional.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functional.java @@ -28,6 +28,7 @@ public void init() { Functions.set("dropwhile", new functional_dropwhile()); Functions.set("chain", new functional_chain()); + Functions.set("stream", new functional_stream()); Functions.set("combine", new functional_combine()); } } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java new file mode 100644 index 00000000..ea5fd1ef --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java @@ -0,0 +1,106 @@ +package com.annimon.ownlang.lib.modules.functions; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; + +public final class functional_stream implements Function { + + @Override + public Value execute(Value... args) { + Arguments.checkAtLeast(1, args.length); + + if (args.length > 1) { + return new StreamValue(new ArrayValue(args)); + } + + final Value value = args[0]; + switch (value.type()) { + case Types.MAP: + return new StreamValue(((MapValue) value).toPairs()); + case Types.ARRAY: + return new StreamValue((ArrayValue) value); + default: + throw new TypeException("Invalid argument. Array or map expected"); + } + } + + private static class StreamValue extends MapValue { + + private final ArrayValue container; + + public StreamValue(ArrayValue container) { + super(12); + this.container = container; + init(); + } + + private void init() { + set("filter", wrapIntermediate(new functional_filter(false))); + set("map", wrapIntermediate(new functional_map())); + set("flatMap", wrapIntermediate(new functional_flatmap())); + set("sortBy", wrapIntermediate(new functional_sortby())); + set("takeWhile", wrapIntermediate(new functional_filter(true))); + set("dropWhile", wrapIntermediate(new functional_dropwhile())); + set("skip", this::skip); + set("limit", this::limit); + + set("reduce", wrapTerminal(new functional_reduce())); + set("forEach", wrapTerminal(new functional_foreach())); + set("toArray", args -> container); + set("count", args -> NumberValue.of(container.size())); + } + + private Value skip(Value... args) { + Arguments.check(1, args.length); + + final int skipCount = args[0].asInt(); + final int size = container.size(); + + if (skipCount <= 0) return this; + if (skipCount >= size) { + return new StreamValue(new ArrayValue(0)); + } + + final Value[] result = new Value[size - skipCount]; + System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length); + return new StreamValue(new ArrayValue(result)); + } + + private Value limit(Value... args) { + Arguments.check(1, args.length); + + final int limitCount = args[0].asInt(); + final int size = container.size(); + + if (limitCount >= size) return this; + if (limitCount <= 0) { + return new StreamValue(new ArrayValue(0)); + } + + final Value[] result = new Value[limitCount]; + System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount); + return new StreamValue(new ArrayValue(result)); + } + + private FunctionValue wrapIntermediate(Function f) { + return wrap(f, true); + } + + private FunctionValue wrapTerminal(Function f) { + return wrap(f, false); + } + + private FunctionValue wrap(Function f, boolean intermediate) { + return new FunctionValue(args -> { + final Value[] newArgs = new Value[args.length + 1]; + System.arraycopy(args, 0, newArgs, 1, args.length); + newArgs[0] = container; + final Value result = f.execute(newArgs); + if (intermediate && result.type() == Types.ARRAY) { + return new StreamValue((ArrayValue) result); + } + return result; + }); + } + } +} diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own index 19692093..48647c23 100644 --- a/src/test/resources/modules/functional/stream.own +++ b/src/test/resources/modules/functional/stream.own @@ -1,11 +1,39 @@ use "functional" -def testFunctionalChain() { +def testStream() { data = [1,2,3,4,5,6,7] - result = chain(data, - ::filter, def(x) = x <= 4, - ::sortby, def(x) = -x, - ::map, def(x) = x * 2, - ) + result = stream(data) + .filter(def(x) = x <= 4) + .sortBy(def(x) = -x) + .map(def(x) = x * 2) + .toArray() assertEquals([8,6,4,2], result) } + +def testSkip() { + data = [1,2,3,4,5,6,7] + assertEquals(7, stream(data).skip(0).count()) + assertEquals(5, stream(data).skip(2).count()) + assertEquals(0, stream(data).skip(7).count()) + assertEquals(0, stream(data).skip(20).count()) +} + +def testLimit() { + data = [1,2,3,4,5,6,7] + assertEquals(0, stream(data).limit(0).count()) + assertEquals(2, stream(data).limit(2).count()) + assertEquals(7, stream(data).limit(7).count()) + assertEquals(7, stream(data).limit(20).count()) +} + +def testTakeWhile() { + data = [2,4,6,5,6,7,8] + assertEquals([2, 4, 6], stream(data).takeWhile(def(x) = x % 2 == 0).toArray()) + assertEquals(0, stream(data).takeWhile(def(x) = x % 2 != 0).count()) +} + +def testDropWhile() { + data = [2,4,6,5,6,7,8] + assertEquals([5, 6, 7, 8], stream(data).dropWhile(def(x) = x % 2 == 0).toArray()) + assertEquals(data, stream(data).dropWhile(def(x) = x % 2 != 0).toArray()) +} From 909691dc1a1bbabd50137642eda4efb7b43849bb Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 15:01:59 +0300 Subject: [PATCH 171/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20custom=20=D0=B2=20StreamValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/modules/functions/functional_stream.java | 16 +++++++++++++++- src/test/resources/modules/functional/stream.own | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java index ea5fd1ef..aac84086 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java @@ -29,7 +29,7 @@ private static class StreamValue extends MapValue { private final ArrayValue container; public StreamValue(ArrayValue container) { - super(12); + super(13); this.container = container; init(); } @@ -43,6 +43,7 @@ private void init() { set("dropWhile", wrapIntermediate(new functional_dropwhile())); set("skip", this::skip); set("limit", this::limit); + set("custom", this::custom); set("reduce", wrapTerminal(new functional_reduce())); set("forEach", wrapTerminal(new functional_foreach())); @@ -82,6 +83,19 @@ private Value limit(Value... args) { return new StreamValue(new ArrayValue(result)); } + 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 Value result = f.execute(container); + if (result.type() == Types.ARRAY) { + return new StreamValue((ArrayValue) result); + } + return result; + } + private FunctionValue wrapIntermediate(Function f) { return wrap(f, true); } diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own index 48647c23..19fa66d5 100644 --- a/src/test/resources/modules/functional/stream.own +++ b/src/test/resources/modules/functional/stream.own @@ -1,3 +1,4 @@ +use "std" use "functional" def testStream() { @@ -37,3 +38,17 @@ def testDropWhile() { assertEquals([5, 6, 7, 8], stream(data).dropWhile(def(x) = x % 2 == 0).toArray()) assertEquals(data, stream(data).dropWhile(def(x) = x % 2 != 0).toArray()) } + +def testCustom() { + data = [2,4,6,5] + assertEquals([5,6,4,2], stream(data).custom(::reverse).toArray()) +} + +def reverse(container) { + size = length(container) + result = newarray(size) + for i : range(size) { + result[size - i - 1] = container[i] + } + return result +} \ No newline at end of file From e326cb7d5df20edcd5dffaee4aca56453a4778bd Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 18:06:52 +0300 Subject: [PATCH 172/448] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D1=80=D1=83?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80=20=D1=83=20NumberValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/NumberValue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/lib/NumberValue.java b/src/main/java/com/annimon/ownlang/lib/NumberValue.java index d92714e7..00542816 100644 --- a/src/main/java/com/annimon/ownlang/lib/NumberValue.java +++ b/src/main/java/com/annimon/ownlang/lib/NumberValue.java @@ -41,7 +41,7 @@ public static NumberValue of(Number value) { private final Number value; - public NumberValue(Number value) { + private NumberValue(Number value) { this.value = value; } From c9249e857761951821249a4e05aafd0299db5da8 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 1 Aug 2016 19:45:14 +0300 Subject: [PATCH 173/448] =?UTF-8?q?=D0=92=20REPL=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4?= =?UTF-8?q?=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9,=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B8=20?= =?UTF-8?q?=D0=B2=D1=81=D0=B5=D0=B3=D0=BE=20=D0=B8=D1=81=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/Main.java | 47 +----- .../java/com/annimon/ownlang/utils/Repl.java | 140 ++++++++++++++++++ 2 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/utils/Repl.java diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index deccadd6..8b8335b8 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -1,6 +1,5 @@ package com.annimon.ownlang; -import com.annimon.ownlang.exceptions.LexerException; import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.parser.Beautifier; import com.annimon.ownlang.parser.Lexer; @@ -11,10 +10,10 @@ import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.FunctionAdder; +import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.TimeMeasurement; import java.io.IOException; import java.util.List; -import java.util.Scanner; import java.util.concurrent.TimeUnit; /** @@ -22,7 +21,7 @@ */ public final class Main { - private static final String VERSION = "1.2.0"; + public static final String VERSION = "1.2.0"; private static String[] ownlangArgs = new String[0]; @@ -97,7 +96,7 @@ public static void main(String[] args) throws IOException { case "-r": case "--repl": - repl(); + Repl.main(new String[0]); return; case "-l": @@ -192,46 +191,6 @@ private static void run(String input, Options options) { } } } - - private static void repl() { - final StringBuilder buffer = new StringBuilder(); - final Scanner scanner = new Scanner(System.in); - System.out.println("Welcome to OwnLang " + VERSION + " REPL\n" - + "Type in expressions to have them evaluated.\n" - + "Type :reset to clear buffer.\n" - + "Type :exit to exit REPL."); - while (true) { - System.out.print((buffer.length() == 0) ? "\n> " : " "); - - if (!scanner.hasNextLine()) break; - - final String line = scanner.nextLine(); - if (":exit".equalsIgnoreCase(line)) break; - if (":reset".equalsIgnoreCase(line)) { - buffer.setLength(0); - continue; - } - - buffer.append(line).append(System.lineSeparator()); - try { - final List tokens = Lexer.tokenize(buffer.toString()); - final Parser parser = new Parser(tokens); - final Statement program = parser.parse(); - if (parser.getParseErrors().hasErrors()) { - continue; - } - program.execute(); - } catch (LexerException lex) { - continue; - } catch (StoppedException ex) { - // skip - } catch (Exception ex) { - Console.handleException(Thread.currentThread(), ex); - } - buffer.setLength(0); - } - scanner.close(); - } private static class Options { boolean showTokens, showAst, showMeasurements; diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java new file mode 100644 index 00000000..765b4712 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -0,0 +1,140 @@ +package com.annimon.ownlang.utils; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.Main; +import com.annimon.ownlang.exceptions.LexerException; +import com.annimon.ownlang.exceptions.StoppedException; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.UserDefinedFunction; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.ast.BlockStatement; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.visitors.PrintVisitor; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Scanner; +import java.util.stream.Collectors; + +public final class Repl { + + private static final String + HELP = ":help", + VARS = ":vars", + FUNCS = ":funcs", + SOURCE = ":source", + RESET = ":reset", + EXIT = ":exit"; + + public static void main(String[] args) { + System.out.println("Welcome to OwnLang " + Main.VERSION + " REPL"); + printHelp(false); + + final BlockStatement history = new BlockStatement(); + final StringBuilder buffer = new StringBuilder(); + final Scanner scanner = new Scanner(System.in); + while (true) { + System.out.print((buffer.length() == 0) ? "\n> " : " "); + + if (!scanner.hasNextLine()) break; + + final String line = scanner.nextLine(); + if (EXIT.equalsIgnoreCase(line)) break; + switch (line.toLowerCase(Locale.ENGLISH)) { + case RESET: + buffer.setLength(0); + continue; + case HELP: + printHelp(true); + continue; + case VARS: + printVariables(); + continue; + case FUNCS: + printFunctions(); + continue; + case SOURCE: + System.out.println(history.accept(new PrintVisitor(), new StringBuilder())); + continue; + } + + buffer.append(line).append(System.lineSeparator()); + Statement program = null; + try { + final List tokens = Lexer.tokenize(buffer.toString()); + final Parser parser = new Parser(tokens); + program = parser.parse(); + if (parser.getParseErrors().hasErrors()) { + continue; + } + program.execute(); + } catch (LexerException lex) { + continue; + } catch (StoppedException ex) { + // skip + } catch (Exception ex) { + Console.handleException(Thread.currentThread(), ex); + } + if (program != null) { + history.add(program); + } + buffer.setLength(0); + } + scanner.close(); + } + + private static void printHelp(boolean full) { + System.out.println("Type in expressions to have them evaluated."); + final List commands = new ArrayList<>(); + if (full) { + commands.add(VARS + " - listing variables"); + commands.add(FUNCS + " - listing functions"); + commands.add(SOURCE + " - listing source"); + } + commands.add(HELP + " - show help"); + commands.add(RESET + " - clear buffer"); + commands.add(EXIT + " - exit REPL"); + + int maxLength = commands.stream() + .mapToInt(String::length) + .max().getAsInt(); + + final int maxCols = 2; + final int size = commands.size(); + for (int i = 0; i < size; i += maxCols) { + // Pad to max length and print in tab-separatex maxCols columns + System.out.println(commands + .subList(i, Math.min(size, i + maxCols)) + .stream() + .map(str -> String.format("%-" + maxLength + "s", str)) + .collect(Collectors.joining("\t", " ", "")) + ); + } + } + + private static void printVariables() { + Variables.variables().entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf("\t%s = %s%n", + e.getKey(), e.getValue().toString())); + } + + private static void printFunctions() { + System.out.println("User functions:"); + Functions.getFunctions().entrySet().stream() + .filter(p -> p.getValue() instanceof UserDefinedFunction) + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf("\t%s%s%n", + e.getKey(), ((UserDefinedFunction)e.getValue()).arguments)); + + System.out.println("Library functions:"); + Functions.getFunctions().entrySet().stream() + .filter(p -> !(p.getValue() instanceof UserDefinedFunction)) + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf("\t%s%n", e.getKey())); + } +} From 07fd22914ba845be72c696f0078a072e903581a2 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 21:29:41 +0300 Subject: [PATCH 174/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20ifPresent=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20MapValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/MapValue.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/annimon/ownlang/lib/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java index 40ba519d..5d3d33e6 100644 --- a/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -5,6 +5,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.function.Consumer; /** * @@ -31,6 +32,18 @@ public MapValue(Map map) { this.map = map; } + public boolean ifPresent(String key, Consumer consumer) { + return ifPresent(new StringValue(key), consumer); + } + + public boolean ifPresent(Value key, Consumer consumer) { + if (map.containsKey(key)) { + consumer.accept(map.get(key)); + return true; + } + return false; + } + public ArrayValue toPairs() { final int size = map.size(); final ArrayValue result = new ArrayValue(size); From 148b6eff32e06c8c7a4d1583e7298a685cdb4ad1 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 23:06:21 +0300 Subject: [PATCH 175/448] =?UTF-8?q?=D0=9A=D0=BE=D0=BD=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=20=D0=BE=D0=B1=D1=8A?= =?UTF-8?q?=D0=B5=D0=BA=D1=82/=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/ValueUtils.java | 83 +++++++++++++++++++ .../lib/modules/functions/json_decode.java | 53 ++---------- .../lib/modules/functions/json_encode.java | 46 ++-------- 3 files changed, 100 insertions(+), 82 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/ValueUtils.java diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java new file mode 100644 index 00000000..90feac5c --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -0,0 +1,83 @@ +package com.annimon.ownlang.lib; + +import java.util.Iterator; +import java.util.Map; +import org.json.JSONArray; +import org.json.JSONObject; + +public final class ValueUtils { + + public static Object toObject(Value val) { + switch (val.type()) { + case Types.ARRAY: + return toObject((ArrayValue) val); + case Types.MAP: + return toObject((MapValue) val); + case Types.NUMBER: + return val.raw(); + case Types.STRING: + return val.asString(); + default: + return JSONObject.NULL; + } + } + + public static Object toObject(MapValue map) { + final JSONObject result = new JSONObject(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = toObject(entry.getValue()); + result.put(key, value); + } + return result; + } + + public static Object toObject(ArrayValue array) { + final JSONArray result = new JSONArray(); + for (Value value : array) { + result.put(toObject(value)); + } + return result; + } + + public static Value toValue(Object obj) { + if (obj instanceof JSONObject) { + return toValue((JSONObject) obj); + } + if (obj instanceof JSONArray) { + return toValue((JSONArray) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return NumberValue.of(((Number) obj)); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + public static MapValue toValue(JSONObject json) { + final MapValue result = new MapValue(json.length()); + final Iterator it = json.keys(); + while(it.hasNext()) { + final String key = it.next(); + final Value value = toValue(json.get(key)); + result.set(new StringValue(key), value); + } + return result; + } + + public static ArrayValue toValue(JSONArray json) { + final int length = json.length(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = toValue(json.get(i)); + result.set(i, value); + } + return result; + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java index 5130a42e..8bd415a1 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java @@ -1,8 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; -import java.util.Iterator; -import org.json.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import org.json.JSONException; +import org.json.JSONTokener; + public final class json_decode implements Function { @@ -12,50 +16,9 @@ public Value execute(Value... args) { try { final String jsonRaw = args[0].asString(); final Object root = new JSONTokener(jsonRaw).nextValue(); - return process(root); + return ValueUtils.toValue(root); } catch (JSONException ex) { throw new RuntimeException("Error while parsing json", ex); } } - - private Value process(Object obj) { - if (obj instanceof JSONObject) { - return process((JSONObject) obj); - } - if (obj instanceof JSONArray) { - return process((JSONArray) obj); - } - if (obj instanceof String) { - return new StringValue((String) obj); - } - if (obj instanceof Number) { - return NumberValue.of(((Number) obj)); - } - if (obj instanceof Boolean) { - return NumberValue.fromBoolean((Boolean) obj); - } - // NULL or other - return NumberValue.ZERO; - } - - private MapValue process(JSONObject json) { - final MapValue result = new MapValue(json.length()); - final Iterator it = json.keys(); - while(it.hasNext()) { - final String key = it.next(); - final Value value = process(json.get(key)); - result.set(new StringValue(key), value); - } - return result; - } - - private ArrayValue process(JSONArray json) { - final int length = json.length(); - final ArrayValue result = new ArrayValue(length); - for (int i = 0; i < length; i++) { - final Value value = process(json.get(i)); - result.set(i, value); - } - return result; - } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java index 33951c41..7907b14e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java @@ -1,8 +1,13 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; -import java.util.Map; -import org.json.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import org.json.JSONException; +import org.json.JSONObject; + public final class json_encode implements Function { @@ -10,44 +15,11 @@ public final class json_encode implements Function { public Value execute(Value... args) { Arguments.check(1, args.length); try { - final Object root = process(args[0]); + final Object root = ValueUtils.toObject(args[0]); final String jsonRaw = JSONObject.valueToString(root); return new StringValue(jsonRaw); } catch (JSONException ex) { throw new RuntimeException("Error while creating json", ex); } } - - private Object process(Value val) { - switch (val.type()) { - case Types.ARRAY: - return process((ArrayValue) val); - case Types.MAP: - return process((MapValue) val); - case Types.NUMBER: - return val.raw(); - case Types.STRING: - return val.asString(); - default: - return JSONObject.NULL; - } - } - - private Object process(MapValue map) { - final JSONObject result = new JSONObject(); - for (Map.Entry entry : map) { - final String key = entry.getKey().asString(); - final Object value = process(entry.getValue()); - result.put(key, value); - } - return result; - } - - private Object process(ArrayValue array) { - final JSONArray result = new JSONArray(); - for (Value value : array) { - result.put(process(value)); - } - return result; - } } \ No newline at end of file From fcd509efa8d4c103092cf2b5e3834e38d57d5594 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 23:18:02 +0300 Subject: [PATCH 176/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20socket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +- .../annimon/ownlang/lib/modules/socket.java | 225 ++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/socket.java diff --git a/build.gradle b/build.gradle index 02e87254..c7e17fd1 100644 --- a/build.gradle +++ b/build.gradle @@ -53,7 +53,9 @@ task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) { } dependencies { - compile 'com.squareup.okhttp3:okhttp:3.4.1' + compile ('io.socket:socket.io-client:0.7.0') { + exclude group: 'org.json', module: 'json' + } compile 'org.json:json:20160212' testCompile 'junit:junit:4.12' diff --git a/src/main/java/com/annimon/ownlang/lib/modules/socket.java b/src/main/java/com/annimon/ownlang/lib/modules/socket.java new file mode 100644 index 00000000..8781fe02 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/socket.java @@ -0,0 +1,225 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.annotations.ConstantInitializer; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import io.socket.client.IO; +import io.socket.client.Socket; +import java.net.URISyntaxException; + +/** + * socket.io module. + * + * @author aNNiMON + */ +@ConstantInitializer +public final class socket implements Module { + + public static void initConstants() { + Variables.define("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); + Variables.define("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); + Variables.define("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); + Variables.define("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); + Variables.define("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); + Variables.define("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); + Variables.define("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); + Variables.define("EVENT_PING", new StringValue(Socket.EVENT_PING)); + Variables.define("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); + Variables.define("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); + Variables.define("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); + Variables.define("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); + Variables.define("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); + Variables.define("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); + } + + @Override + public void init() { + initConstants(); + Functions.set("newSocket", socket::newSocket); + } + + private static Value newSocket(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + + try { + final String url = args[0].asString(); + if (args.length == 1) { + return new SocketValue(IO.socket(url)); + } + + if (args[1].type() != Types.MAP) { + throw new TypeException("Map expected in second argument"); + } + IO.Options options = parseOptions((MapValue) args[1]); + return new SocketValue(IO.socket(url, options)); + } catch (URISyntaxException ue) { + return NumberValue.MINUS_ONE; + } + } + + private static class SocketValue extends MapValue { + + private final Socket socket; + + public SocketValue(Socket socket) { + super(11); + this.socket = socket; + init(); + } + + private void init() { + set("close", this::close); + set("connect", this::connect); + set("connected", this::connected); + set("disconnect", this::disconnect); + set("emit", this::emit); + set("hasListeners", this::hasListeners); + set("id", this::id); + set("off", this::off); + set("on", this::on); + set("once", this::once); + set("open", this::open); + set("send", this::send); + } + + private Value close(Value... args) { + socket.close(); + return this; + } + + private Value connect(Value... args) { + socket.connect(); + return this; + } + + private Value connected(Value... args) { + return NumberValue.fromBoolean(socket.connected()); + } + + private Value disconnect(Value... args) { + socket.disconnect(); + return this; + } + + private Value hasListeners(Value... args) { + Arguments.check(1, args.length); + return NumberValue.fromBoolean( + socket.hasListeners(args[0].asString()) + ); + } + + private Value emit(Value... args) { + Arguments.checkOrOr(2, 3, args.length); + final String event = args[0].asString(); + final Value value = args[1]; + if (args.length == 3) { + // TODO ack + } + socket.emit(event, ValueUtils.toObject(value)); + return this; + } + + private Value id(Value... args) { + return new StringValue(socket.id()); + } + + private Value off(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 1) { + socket.off(args[0].asString()); + } else { + socket.off(); + } + return this; + } + + private Value on(Value... args) { + Arguments.check(2, args.length); + final String event = args[0].asString(); + final Function listener = ((FunctionValue) args[1]).getValue(); + socket.on(event, sArgs -> { + executeSocketListener(listener, sArgs); + }); + return this; + } + + private Value once(Value... args) { + Arguments.check(2, args.length); + final String event = args[0].asString(); + final Function listener = ((FunctionValue) args[1]).getValue(); + socket.once(event, sArgs -> { + executeSocketListener(listener, sArgs); + }); + return this; + } + + private Value open(Value... args) { + socket.open(); + return this; + } + + private Value send(Value... args) { + Arguments.check(1, args.length); + socket.send(ValueUtils.toObject(args[0])); + return this; + } + + private void executeSocketListener(Function listener, Object[] sArgs) { + if (sArgs == null) { + listener.execute(new ArrayValue(0)); + } else { + int size = sArgs.length; + final Value[] fArgs = new Value[size]; + for (int i = 0; i < size; i++) { + fArgs[i] = ValueUtils.toValue(sArgs[i]); + } + listener.execute(new ArrayValue(fArgs)); + } + } + } + + private static IO.Options parseOptions(MapValue map) { + final IO.Options result = new IO.Options(); + map.ifPresent("forceNew", v -> result.forceNew = v.asInt() != 0); + map.ifPresent("multiplex", v -> result.multiplex = v.asInt() != 0); + map.ifPresent("reconnection", v -> result.reconnection = v.asInt() != 0); + map.ifPresent("rememberUpgrade", v -> result.rememberUpgrade = v.asInt() != 0); + map.ifPresent("secure", v -> result.secure = v.asInt() != 0); + map.ifPresent("timestampRequests", v -> result.timestampRequests = v.asInt() != 0); + map.ifPresent("upgrade", v -> result.upgrade = v.asInt() != 0); + + map.ifPresent("policyPort", v -> result.policyPort = v.asInt()); + map.ifPresent("port", v -> result.port = v.asInt()); + map.ifPresent("reconnectionAttempts", v -> result.reconnectionAttempts = v.asInt()); + + map.ifPresent("reconnectionDelay", v -> result.reconnectionDelay = getNumber(v).longValue()); + map.ifPresent("reconnectionDelayMax", v -> result.reconnectionDelayMax = getNumber(v).longValue()); + map.ifPresent("timeout", v -> result.timeout = getNumber(v).longValue()); + + map.ifPresent("randomizationFactor", v -> result.randomizationFactor = v.asNumber()); + + map.ifPresent("host", v -> result.host = v.asString()); + map.ifPresent("hostname", v -> result.hostname = v.asString()); + map.ifPresent("path", v -> result.path = v.asString()); + map.ifPresent("query", v -> result.query = v.asString()); + map.ifPresent("timestampParam", v -> result.timestampParam = v.asString()); + + map.ifPresent("transports", v -> { + if (v.type() != Types.ARRAY) return; + + final ArrayValue arr = (ArrayValue) v; + final String[] values = new String[arr.size()]; + int index = 0; + for (Value value : arr) { + values[index++] = value.asString(); + } + result.transports = values; + }); + return result; + } + + private static Number getNumber(Value value) { + if (value.type() != Types.NUMBER) return value.asInt(); + return ((NumberValue) value).raw(); + } +} From 54c822e7d23e049e64ff75c88902fbe6dd26151f Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 23:20:49 +0300 Subject: [PATCH 177/448] =?UTF-8?q?=D0=91=D1=8B=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/modules/socket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/socket.java b/src/main/java/com/annimon/ownlang/lib/modules/socket.java index 8781fe02..8b035067 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/socket.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/socket.java @@ -62,7 +62,7 @@ private static class SocketValue extends MapValue { private final Socket socket; public SocketValue(Socket socket) { - super(11); + super(12); this.socket = socket; init(); } From 0a6086949bbfe0fcb0815b89cc1b4315db0d3383 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 10 Aug 2016 16:50:37 +0300 Subject: [PATCH 178/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=20=D0=BE?= =?UTF-8?q?=D0=BD=D0=BB=D0=B0=D0=B9=D0=BD=20=D0=B8=D0=B3=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/game/pipes-online/pipes_online.own | 176 ++++++++++++++++ examples/game/pipes-online/server/main.js | 188 ++++++++++++++++++ .../game/pipes-online/server/package.json | 17 ++ 3 files changed, 381 insertions(+) create mode 100644 examples/game/pipes-online/pipes_online.own create mode 100644 examples/game/pipes-online/server/main.js create mode 100644 examples/game/pipes-online/server/package.json diff --git a/examples/game/pipes-online/pipes_online.own b/examples/game/pipes-online/pipes_online.own new file mode 100644 index 00000000..fc5548ab --- /dev/null +++ b/examples/game/pipes-online/pipes_online.own @@ -0,0 +1,176 @@ +use "std" +use "canvas" +use "socket" + +/// --- PIPES CELL --- +CELL_START = 0 +HORIZONTAL = 0 +VERTICAL = 1 +LEFT_TO_DOWN = 2 +LEFT_TO_UP = 3 +RIGHT_TO_UP = 4 +RIGHT_TO_DOWN = 5 +CROSS = 6 +CELL_LAST = 6 + +Cells = [ + {"index": HORIZONTAL, "next": VERTICAL}, + {"index": VERTICAL, "next": HORIZONTAL}, + {"index": LEFT_TO_DOWN, "next": LEFT_TO_UP}, + {"index": LEFT_TO_UP, "next": RIGHT_TO_UP}, + {"index": RIGHT_TO_UP, "next": RIGHT_TO_DOWN}, + {"index": RIGHT_TO_DOWN, "next": LEFT_TO_DOWN}, + {"index": CROSS, "next": CROSS} +]; + + +def draw(v, cellSize) { + c2 = cellSize / 2 + match v { + case HORIZONTAL : fillRect(0, c2 - 2, cellSize, 4) + case VERTICAL : fillRect(c2 - 2, 0, 4, cellSize) + case LEFT_TO_DOWN : { + fillRect(0, c2 - 2, c2, 4) + fillRect(c2 - 2, c2 - 2, 4, c2 + 2) + } + case LEFT_TO_UP : { + fillRect(0, c2 - 2, c2, 4) + fillRect(c2 - 2, 0, 4, c2 + 2) + } + case RIGHT_TO_UP : { + fillRect(c2 - 2, c2 - 2, c2 + 2, 4) + fillRect(c2 - 2, 0, 4, c2 + 2) + } + case RIGHT_TO_DOWN : { + fillRect(c2 - 2, c2 - 2, c2 + 2, 4) + fillRect(c2 - 2, c2 - 2, 4, c2 + 2) + } + case CROSS : { + fillRect(c2 - 2, 0, 4, cellSize) + fillRect(0, c2 - 2, cellSize, 4) + } + } +} + + +/// --- PIPES BOARD --- +SIZE = 10 + +// Creating game board +board = newarray(SIZE, SIZE) +boardGhost = newarray(SIZE, SIZE) + +def switchCell(x, y) { + board[x][y] = Cells[board[x][y]].next +} +def setGhostCell(x, y) { + boardGhost[x][y] = Cells[boardGhost[x][y]].next +} + + +/// --- PIPES MAIN --- +translateX = 0 translateY = 0 +isGameFinished = false +isWin = false + +/* frect with translate ability */ +def fillRect(x,y,w,h) { + frect(translateX+x, translateY+y, w, h) +} + +WIDTH = 320 HEIGHT = 320 +WINDOW_WIDTH = WIDTH * 2 +window("Pipes Online", WINDOW_WIDTH, HEIGHT) +cellSize = WIDTH / SIZE + +// cursor +curX = 0 curY = 0 +curGhostX = 0 curGhostY = 0 + +// Initialize client +socket = newSocket("http://localhost:6469") +socket.on("gameStart", def(data) { + data = data[0] + for i=0, i 0) { + curX-- + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_RIGHT && curX < SIZE - 1) { + curX++ + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_UP && curY > 0) { + curY-- + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_DOWN && curY < SIZE - 1) { + curY++ + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_FIRE) { + switchCell(curX, curY) + socket.emit("switchCell", {"x": curX, "y": curY}) + } + else if (key == 48) run = 0 + } + + // background + color(isGameFinished ? (isWin ? #66FF66 : #FF6666) : #FFFFFF) + frect(0, 0, WIDTH, HEIGHT) + color(isGameFinished ? (!isWin ? #66FF66 : #FF6666) : #DDDDDD) + frect(WIDTH, 0, WIDTH, HEIGHT) + // cursor + color(#4444FF) + frect(curX*cellSize, curY*cellSize, cellSize, cellSize) + color(#4040DD) + frect(WIDTH + curGhostX*cellSize, curGhostY*cellSize, cellSize, cellSize) + for (i=0, i 0) && (supportRight(board[curX - 1][curY])) ) { + if (isConnected(board, curX - 1, curY, visited)) return true; + } + if ( supportRight(current) && (curX < SIZE - 1) && (supportLeft(board[curX + 1][curY])) ) { + if (isConnected(board, curX + 1, curY, visited)) return true; + } + if ( supportUp(current) && (curY > 0) && (supportDown(board[curX][curY - 1])) ) { + if (isConnected(board, curX, curY - 1, visited)) return true; + } + if ( supportDown(current) && (curY < SIZE - 1) && (supportUp(board[curX][curY + 1])) ) { + if (isConnected(board, curX, curY + 1, visited)) return true; + } + return false; + }; + + var isFinished = function(board) { + // Start pipe must have left touchpoint + if (!supportLeft(board[0][0])) return false; + // Finish pipe - right touchpoint + if (!supportRight(board[SIZE - 1][SIZE - 1])) return false; + + var visited = new Array(SIZE); + for (var i = 0; i < SIZE; i++) { + visited[i] = new Array(SIZE); + for (var j = 0; j < SIZE; j++) visited[i][j] = false; + } + // Recursive traversal from left upper pipe + return isConnected(board, 0, 0, visited); + }; + + self.isGameFinished = function (isGhost) { + return isFinished(isGhost ? self.board2 : self.board1); + }; + + return self; +}; + +var board = {}; +var players = []; + +io.on('connection', function (socket) { + if (players.length >= 2) { + console.log('Sorry, server is full.'); + return; + } + + socket.playerId = players.length + 1; + players.push({socket: socket}); + console.log('Player ' + socket.playerId + ' Connected!'); + + socket.on('switchCell', function (data) { + var isGhost = socket.playerId === 2; + var current = isGhost ? 1 : 0; + var opposite = isGhost ? 0 : 1; + board.switchCell(data.x, data.y, isGhost); + players[opposite].socket.emit('updateGhostCell', data); + if (board.isGameFinished(isGhost)) { + players[current].socket.emit('gameFinished', true); + players[opposite].socket.emit('gameFinished', false); + } else if (board.isGameFinished(!isGhost)) { + players[opposite].socket.emit('gameFinished', true); + players[current].socket.emit('gameFinished', false); + } + }); + socket.on('updateCursor', function (data) { + var isGhost = socket.playerId === 2; + var opposite = isGhost ? 0 : 1; + players[opposite].socket.emit('updateGhostCursor', data); + }); + socket.on('connect_timeout', function (exception) { + console.log('SOCKET TIMEOUT ' + exception); + socket.destroy(); + }); + socket.on('disconnect', function () { + console.log('disconnect Player ' + socket.playerId); + players.splice(socket.playerId - 1, 1); + }); + + // start game + if (players.length === 2) { + board = Board(SIZE); + board.create(); + + players[0].socket.emit('gameStart', board.board1); + players[1].socket.emit('gameStart', board.board2); + } +}); + +/*io.on('connection', function(socket) { + console.log('New connection'); + socket.on('greetings', function(data) { + console.log('Got greetings from client'); + socket.emit('pong', "Hello from server"); + }); + socket.on('complex_object', function(data) { + console.log('Got object: ' + data); + socket.emit('complex_object', {key1: data.key2, key2: data.key1, arr: [0,1,2,"34"]}); + }); + });*/ +/*io.on('connection', function(socket) { + console.log('New connection'); + socket.emit('pong', "Hello"); + + socket.on('ping', function(data) { + console.log('Got ping from client'); + socket.emit('pong', "Hello from server, " + data); + }); + socket.on('pong', function(data) { + console.log('Got pong from client'); + console.log('pong'); + }); + });*/ \ No newline at end of file diff --git a/examples/game/pipes-online/server/package.json b/examples/game/pipes-online/server/package.json new file mode 100644 index 00000000..7d5139de --- /dev/null +++ b/examples/game/pipes-online/server/package.json @@ -0,0 +1,17 @@ +{ + "name": "TestSocketIOServer", + "version": "1.0.0", + "keywords": [ + "util", + "functional", + "server", + "client", + "browser" + ], + "author": "aNNiMON", + "contributors": [], + "dependencies": { + "express": "^4.13.4", + "socket.io": "^1.4.5" + } +} From 25d335a5127f16b7a02f315234a2af7a1f0ec73b Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 10 Aug 2016 17:04:02 +0300 Subject: [PATCH 179/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B8=D0=B3=D1=80=D0=B0=20pipes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/game/pipes.own | 90 +++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/examples/game/pipes.own b/examples/game/pipes.own index efcd2c7b..ff4d2d5f 100644 --- a/examples/game/pipes.own +++ b/examples/game/pipes.own @@ -1,9 +1,7 @@ use "std" use "canvas" -///------------------------------------- -/// PIPES CELL -///------------------------------------- +/// --- PIPES CELL --- CELL_START = 0 HORIZONTAL = 0 VERTICAL = 1 @@ -14,23 +12,16 @@ RIGHT_TO_DOWN = 5 CROSS = 6 CELL_LAST = 6 -// лево, право, верх, низ -HorizontalCell = [1,1,0,0] -VerticalCell = [0,0,1,1] -LeftToDownCell = [1,0,0,1] -LeftToUpCell = [1,0,1,0] -RightToUpCell = [0,1,1,0] -RightToDownCell = [0,1,0,1] -CrossCell = [1,1,1,1] - -support = [ - HorizontalCell, VerticalCell, - LeftToDownCell, LeftToUpCell, - RightToUpCell, RightToDownCell, - CrossCell +Cells = [ + {"support": [1, 1, 0, 0], "index": HORIZONTAL, "next": VERTICAL}, + {"support": [0, 0, 1, 1], "index": VERTICAL, "next": HORIZONTAL}, + {"support": [1, 0, 0, 1], "index": LEFT_TO_DOWN, "next": LEFT_TO_UP}, + {"support": [1, 0, 1, 0], "index": LEFT_TO_UP, "next": RIGHT_TO_UP}, + {"support": [0, 1, 1, 0], "index": RIGHT_TO_UP, "next": RIGHT_TO_DOWN}, + {"support": [0, 1, 0, 1], "index": RIGHT_TO_DOWN, "next": LEFT_TO_DOWN}, + {"support": [1, 1, 1, 1], "index": CROSS, "next": CROSS} ] - def draw(v, cellSize) { c2 = cellSize / 2 match v { @@ -42,7 +33,7 @@ def draw(v, cellSize) { } case LEFT_TO_UP : { fillRect(0, c2 - 2, c2, 4) - fillRect(c2 - 2, c2 - 2, 4, c2 + 2) + fillRect(c2 - 2, 0, 4, c2 + 2) } case RIGHT_TO_UP : { fillRect(c2 - 2, c2 - 2, c2 + 2, 4) @@ -59,19 +50,16 @@ def draw(v, cellSize) { } } -def supportLeft(v) = support[v][0] -def supportRight(v) = support[v][1] -def supportUp(v) = support[v][2] -def supportDown(v) = support[v][3] -///------------------------------------- +def supportLeft(v) = Cells[v].support[0] +def supportRight(v) = Cells[v].support[1] +def supportUp(v) = Cells[v].support[2] +def supportDown(v) = Cells[v].support[3] -///------------------------------------- -/// PIPES BOARD -///------------------------------------- +/// --- PIPES BOARD --- SIZE = 10 -// Создаём игровое поле +// Creating game board board = newarray(SIZE, SIZE) def createBoard() { @@ -81,51 +69,49 @@ def createBoard() { } def switchCell(x, y) { - nextType = board[x][y] + 1 - board[x][y] = nextType > CELL_LAST ? CELL_START : nextType + board[x][y] = Cells[board[x][y]].next } def isFinished() { - // Стартовая труба должна иметь левую точку соприкосновения - if (!supportLeft(board[0][0])) return 0 - // А конечная труба - правую - if (!supportRight(board[SIZE - 1][SIZE - 1])) return 0 + // Start pipe must have left touchpoint + if (!supportLeft(board[0][0])) return false + // Finish pipe - right touchpoint + if (!supportRight(board[SIZE - 1][SIZE - 1])) return false visited = newarray(SIZE, SIZE) + // Recursive traversal from left upper pipe return isConnected(0, 0, visited) } def isConnected(curX, curY, visited) { - // Если достигли конечной ячейки - выходим. - if ( (curX == SIZE - 1) && (curY == SIZE - 1) ) return 1 + // If it is a last cell - game is finished + if ( (curX == SIZE - 1) && (curY == SIZE - 1) ) return true - // Если уже посещали - выходим. - if (visited[curX][curY]) return 0 - // Отмечаем посещение. + // Already visited - exit + if (visited[curX][curY]) return false + // Mark visited visited[curX][curY] = 1 + // Check pipes matching current = board[curX][curY] if ( supportLeft(current) && (curX > 0) && (supportRight(board[curX - 1][curY])) ) { - if (isConnected(curX - 1, curY, visited)) return 1 + if (isConnected(curX - 1, curY, visited)) return true } if ( supportRight(current) && (curX < SIZE - 1) && (supportLeft(board[curX + 1][curY])) ) { - if (isConnected(curX + 1, curY, visited)) return 1 + if (isConnected(curX + 1, curY, visited)) return true } if ( supportUp(current) && (curY > 0) && (supportDown(board[curX][curY - 1])) ) { - if (isConnected(curX, curY - 1, visited)) return 1 + if (isConnected(curX, curY - 1, visited)) return true } if ( supportDown(current) && (curY < SIZE - 1) && (supportUp(board[curX][curY + 1])) ) { - if (isConnected(curX, curY + 1, visited)) return 1 + if (isConnected(curX, curY + 1, visited)) return true } - return 0 + return false } -///------------------------------------- -///------------------------------------- -/// PIPES MAIN -///------------------------------------- +/// --- PIPES MAIN --- translateX = 0 translateY = 0 -/* frect с поддержкой translate*/ +/* frect with translate ability */ def fillRect(x,y,w,h) { frect(translateX+x, translateY+y, w, h) } @@ -138,7 +124,7 @@ window("Pipes", WIDTH, HEIGHT) cellSize = WIDTH / SIZE createBoard() -// курсор +// cursor curX = 0 curY = 0 @@ -153,10 +139,10 @@ while run { else if key == VK_FIRE switchCell(curX,curY) else if key == 48 run = 0 - // фон + // background color(isFinished() ? #00FF00 : #FFFFFF) frect(0,0,WIDTH,HEIGHT) - // курсор + // cursor color(#4444FF) frect(curX*cellSize, curY*cellSize, cellSize, cellSize) for (i=0, i Date: Sun, 11 Sep 2016 13:05:31 +0300 Subject: [PATCH 180/448] =?UTF-8?q?=D0=A1=D0=BE=D0=B2=D0=BC=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8C=20=D1=81=20Android?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/Console.java | 4 ++++ .../ownlang/annotations/ConstantInitializer.java | 12 ------------ .../java/com/annimon/ownlang/lib/NumberValue.java | 4 ++-- .../com/annimon/ownlang/lib/modules/canvas.java | 2 -- .../com/annimon/ownlang/lib/modules/canvasfx.java | 2 -- .../java/com/annimon/ownlang/lib/modules/date.java | 6 ++---- .../java/com/annimon/ownlang/lib/modules/files.java | 8 +++----- .../java/com/annimon/ownlang/lib/modules/forms.java | 2 -- .../com/annimon/ownlang/lib/modules/functional.java | 6 +++--- .../lib/modules/functions/functional_chain.java | 8 ++++++-- .../lib/modules/functions/functional_combine.java | 8 ++++++-- .../lib/modules/functions/functional_flatmap.java | 9 +++++++-- .../lib/modules/functions/functional_foreach.java | 3 +-- .../lib/modules/functions/functional_map.java | 11 ++++++++--- .../lib/modules/functions/functional_reduce.java | 11 ++++++++--- .../lib/modules/functions/functional_sortby.java | 9 +++++++-- .../ownlang/lib/modules/functions/http_http.java | 9 +++++++-- .../ownlang/lib/modules/functions/json_decode.java | 3 +-- .../ownlang/lib/modules/functions/robot_exec.java | 9 +++++++-- .../lib/modules/functions/std_arrayCombine.java | 9 +++++++-- .../lib/modules/functions/std_arrayKeyExists.java | 9 +++++++-- .../lib/modules/functions/std_arrayKeys.java | 9 +++++++-- .../lib/modules/functions/std_arrayValues.java | 9 +++++++-- .../ownlang/lib/modules/functions/std_charat.java | 8 ++++++-- .../ownlang/lib/modules/functions/std_echo.java | 9 +++++---- .../ownlang/lib/modules/functions/std_indexof.java | 9 ++++++--- .../ownlang/lib/modules/functions/std_join.java | 9 +++++++-- .../lib/modules/functions/std_lastindexof.java | 7 +++++-- .../ownlang/lib/modules/functions/std_rand.java | 3 +-- .../ownlang/lib/modules/functions/std_readln.java | 6 ++++-- .../ownlang/lib/modules/functions/std_replace.java | 7 +++++-- .../lib/modules/functions/std_replaceall.java | 7 +++++-- .../lib/modules/functions/std_replacefirst.java | 9 ++++++--- .../ownlang/lib/modules/functions/std_sort.java | 9 +++++++-- .../ownlang/lib/modules/functions/std_split.java | 8 ++++++-- .../ownlang/lib/modules/functions/std_sprintf.java | 8 ++++++-- .../lib/modules/functions/std_substring.java | 7 +++++-- .../ownlang/lib/modules/functions/std_thread.java | 12 +++++++++--- .../ownlang/lib/modules/functions/std_tochar.java | 7 +++++-- .../lib/modules/functions/std_tolowercase.java | 7 +++++-- .../lib/modules/functions/std_touppercase.java | 7 +++++-- .../ownlang/lib/modules/functions/std_trim.java | 7 +++++-- .../java/com/annimon/ownlang/lib/modules/http.java | 8 ++++---- .../java/com/annimon/ownlang/lib/modules/java.java | 4 +--- .../java/com/annimon/ownlang/lib/modules/jdbc.java | 2 -- .../java/com/annimon/ownlang/lib/modules/json.java | 5 +++-- .../java/com/annimon/ownlang/lib/modules/math.java | 4 +--- .../java/com/annimon/ownlang/lib/modules/ounit.java | 13 ++++++++++--- .../java/com/annimon/ownlang/lib/modules/robot.java | 2 -- .../com/annimon/ownlang/lib/modules/socket.java | 2 -- .../java/com/annimon/ownlang/lib/modules/std.java | 4 +--- .../java/com/annimon/ownlang/lib/modules/types.java | 2 -- .../java/com/annimon/ownlang/parser/Beautifier.java | 3 ++- .../com/annimon/ownlang/parser/ParseErrors.java | 3 ++- .../annimon/ownlang/parser/ast/BlockStatement.java | 3 ++- .../parser/optimization/InstructionCombining.java | 5 +++-- .../ownlang/parser/visitors/PrintVisitor.java | 3 ++- src/main/java/com/annimon/ownlang/utils/Repl.java | 2 +- .../com/annimon/ownlang/utils/TimeMeasurement.java | 5 +++-- 59 files changed, 238 insertions(+), 140 deletions(-) delete mode 100644 src/main/java/com/annimon/ownlang/annotations/ConstantInitializer.java diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index 06217d62..6beab639 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -8,6 +8,10 @@ public class Console { + public static String newline() { + return System.lineSeparator(); + } + public static void print(String value) { System.out.print(value); } diff --git a/src/main/java/com/annimon/ownlang/annotations/ConstantInitializer.java b/src/main/java/com/annimon/ownlang/annotations/ConstantInitializer.java deleted file mode 100644 index fd1c781e..00000000 --- a/src/main/java/com/annimon/ownlang/annotations/ConstantInitializer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.annimon.ownlang.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface ConstantInitializer { - -} diff --git a/src/main/java/com/annimon/ownlang/lib/NumberValue.java b/src/main/java/com/annimon/ownlang/lib/NumberValue.java index 00542816..3febab7a 100644 --- a/src/main/java/com/annimon/ownlang/lib/NumberValue.java +++ b/src/main/java/com/annimon/ownlang/lib/NumberValue.java @@ -5,9 +5,9 @@ * @author aNNiMON */ public final class NumberValue implements Value { - + public static final NumberValue MINUS_ONE, ZERO, ONE; - + private static final int CACHE_MIN = -128, CACHE_MAX = 127; private static final NumberValue[] NUMBER_CACHE; static { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvas.java b/src/main/java/com/annimon/ownlang/lib/modules/canvas.java index 1f1ca36d..a76e79b9 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvas.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import java.awt.Color; import java.awt.Dimension; @@ -20,7 +19,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class canvas implements Module { private static JFrame frame; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java index c842c45c..8934f331 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import static com.annimon.ownlang.lib.Converters.*; @@ -43,7 +42,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class canvasfx implements Module { private static final int FX_EFFECT_TYPE = 5301; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/date.java b/src/main/java/com/annimon/ownlang/lib/modules/date.java index a3de9f82..a869e452 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/date.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/date.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.text.DateFormat; @@ -14,7 +13,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class date implements Module { private static final int DATE_DATE_FORMAT = 9829; @@ -197,7 +195,7 @@ private static void date(Calendar calendar, Value arg1) { } try { calendar.setTime(DateFormat.getDateTimeInstance().parse(arg1.asString())); - } catch (ParseException ex) { } + } catch (ParseException ignore) { } } private static void date(Calendar calendar, Value arg1, Value arg2) { @@ -207,7 +205,7 @@ private static void date(Calendar calendar, Value arg1, Value arg2) { } try { calendar.setTime(new SimpleDateFormat(arg1.asString()).parse(arg2.asString())); - } catch (ParseException ex) { } + } catch (ParseException ignore) { } } } diff --git a/src/main/java/com/annimon/ownlang/lib/modules/files.java b/src/main/java/com/annimon/ownlang/lib/modules/files.java index f65d8482..bcb2515c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/files.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/files.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.io.BufferedReader; @@ -21,7 +20,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class files implements Module { private static Map files; @@ -34,7 +32,7 @@ public static void initConstants() { public void init() { files = new HashMap<>(); initConstants(); - + Functions.set("fopen", new fopen()); Functions.set("flush", new flush()); Functions.set("fclose", new fclose()); @@ -169,7 +167,7 @@ public Value execute(Value... args) { protected abstract Value execute(FileInfo fileInfo, Value[] args) throws IOException; } - + private static class listFiles extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { @@ -559,7 +557,7 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { return NumberValue.ONE; } } - + private static class writeText extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/forms.java b/src/main/java/com/annimon/ownlang/lib/modules/forms.java index 8f2b2686..1216449b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/forms.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/forms.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.forms.Components; import com.annimon.ownlang.lib.modules.functions.forms.LayoutManagers; @@ -13,7 +12,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class forms implements Module { public static void initConstants() { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/lib/modules/functional.java index 1ff29edc..2e778cb7 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functional.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functional.java @@ -1,14 +1,14 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.lib.modules.functions.*; /** * * @author aNNiMON */ -@ConstantInitializer public final class functional implements Module { public static void initConstants() { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java index 7a88c55c..d844e1d2 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java @@ -1,7 +1,11 @@ package com.annimon.ownlang.lib.modules.functions; 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.Types; +import com.annimon.ownlang.lib.Value; public final class functional_chain implements Function { @@ -21,4 +25,4 @@ public Value execute(Value... args) { return result; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java index b469d13f..8a1b7574 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java @@ -1,7 +1,11 @@ package com.annimon.ownlang.lib.modules.functions; 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.Types; +import com.annimon.ownlang.lib.Value; public final class functional_combine implements Function { @@ -24,4 +28,4 @@ public Value execute(Value... args) { return new FunctionValue(result); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java index 4ba1ede6..5e025c21 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java @@ -1,7 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; 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.FunctionValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.ArrayList; import java.util.List; @@ -35,4 +40,4 @@ private Value flatMapArray(ArrayValue array, Function mapper) { } return new ArrayValue(values); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java index 76bb7c33..7c19faa7 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java @@ -2,7 +2,6 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; - import java.util.Map; public final class functional_foreach implements Function { @@ -32,4 +31,4 @@ public Value execute(Value... args) { } throw new TypeException("Invalid first argument. Array or map expected"); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java index 4a349a19..5cbfbe69 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java @@ -1,8 +1,13 @@ package com.annimon.ownlang.lib.modules.functions; 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.FunctionValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.Map; public final class functional_map implements Function { @@ -53,4 +58,4 @@ private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { } return result; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java index 71f4d14f..282d0163 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java @@ -1,8 +1,13 @@ package com.annimon.ownlang.lib.modules.functions; 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.FunctionValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.Map; public final class functional_reduce implements Function { @@ -35,4 +40,4 @@ public Value execute(Value... args) { } throw new TypeException("Invalid first argument. Array or map expected"); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java index 43b1beb0..066c4a14 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java @@ -1,7 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; 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.FunctionValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.Arrays; public final class functional_sortby implements Function { @@ -22,4 +27,4 @@ public Value execute(Value... args) { return new ArrayValue(elements); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java index aaf62097..aa75a044 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java @@ -7,7 +7,12 @@ import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Map; -import okhttp3.*; +import okhttp3.FormBody; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import okhttp3.internal.http.HttpMethod; public final class http_http implements Function { @@ -168,4 +173,4 @@ private RequestBody getStringRequestBody(Value params, MapValue options) throws return RequestBody.create(type, params.asString()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java index 8bd415a1..c6f56a70 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java @@ -7,7 +7,6 @@ import org.json.JSONException; import org.json.JSONTokener; - public final class json_decode implements Function { @Override @@ -21,4 +20,4 @@ public Value execute(Value... args) { throw new RuntimeException("Error while parsing json", ex); } } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java index 3b7aa910..9f4f5ae3 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java @@ -1,6 +1,11 @@ package com.annimon.ownlang.lib.modules.functions; -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; public final class robot_exec implements Function { @@ -50,4 +55,4 @@ private static String[] toStringArray(Value[] values) { } return strings; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java index 850ec414..7cbbcb1b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java @@ -1,7 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; 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.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; public final class std_arrayCombine implements Function { @@ -26,4 +31,4 @@ public Value execute(Value... args) { return result; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java index acfe0d51..946547c1 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java @@ -1,7 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; 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.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; public final class std_arrayKeyExists implements Function { @@ -15,4 +20,4 @@ public Value execute(Value... args) { return NumberValue.fromBoolean(map.containsKey(args[0])); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java index 3eacbc27..b83b12b3 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java @@ -1,7 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; 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.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -22,4 +27,4 @@ public Value execute(Value... args) { return new ArrayValue(keys); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java index abf89656..c5671509 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java @@ -1,7 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; 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.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -22,4 +27,4 @@ public Value execute(Value... args) { return new ArrayValue(values); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java index 85addc0a..8a753996 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java @@ -1,15 +1,19 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; public final class std_charat implements Function { @Override public Value execute(Value... args) { Arguments.check(2, args.length); + final String input = args[0].asString(); final int index = args[1].asInt(); return NumberValue.of((short)input.charAt(index)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java index 6ec9eb50..ab8c54df 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java @@ -9,11 +9,12 @@ public final class std_echo implements Function { @Override public Value execute(Value... args) { + final StringBuilder sb = new StringBuilder(); for (Value arg : args) { - Console.print(arg.asString()); - Console.print(" "); + sb.append(arg.asString()); + sb.append(" "); } - Console.println(); + Console.println(sb.toString()); return NumberValue.ZERO; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java index b3333cdd..660a32c5 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java @@ -1,17 +1,20 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; public final class std_indexof implements Function { @Override public Value execute(Value... args) { Arguments.checkOrOr(2, 3, args.length); - + final String input = args[0].asString(); final String what = args[1].asString(); final int index = (args.length == 3) ? args[2].asInt() : 0; return NumberValue.of(input.indexOf(what, index)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java index 8cfa688c..3be01d40 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java @@ -2,7 +2,12 @@ import com.annimon.ownlang.exceptions.ArgumentsMismatchException; 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.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; public final class std_join implements Function { @@ -38,4 +43,4 @@ private static StringValue join(ArrayValue array, String delimiter, String prefi sb.append(suffix); return new StringValue(sb.toString()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java index c5849ccc..607746ef 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; public final class std_lastindexof implements Function { @@ -16,4 +19,4 @@ public Value execute(Value... args) { final int index = args[2].asInt(); return NumberValue.of(input.lastIndexOf(what, index)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java index dc11b15f..cdab6136 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java @@ -4,7 +4,6 @@ import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; - import java.util.Random; public final class std_rand implements Function { @@ -40,4 +39,4 @@ public Value execute(Value... args) { } return NumberValue.of(RND.nextInt(to - from) + from); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java index 0ec243ab..28ec1529 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; import java.util.Scanner; public final class std_readln implements Function { @@ -11,4 +13,4 @@ public Value execute(Value... args) { return new StringValue(sc.nextLine()); } } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java index 0474941f..927e0f21 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_replace implements Function { @@ -14,4 +17,4 @@ public Value execute(Value... args) { return new StringValue(input.replace(target, replacement)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java index bad6e63c..f12134f1 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_replaceall implements Function { @@ -14,4 +17,4 @@ public Value execute(Value... args) { return new StringValue(input.replaceAll(regex, replacement)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java index ce56e547..1d62736d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java @@ -1,17 +1,20 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_replacefirst implements Function { @Override public Value execute(Value... args) { Arguments.check(3, args.length); - + final String input = args[0].asString(); final String regex = args[1].asString(); final String replacement = args[2].asString(); return new StringValue(input.replaceFirst(regex, replacement)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java index 29582b87..6c5c69aa 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java @@ -2,7 +2,12 @@ import com.annimon.ownlang.exceptions.ArgumentsMismatchException; 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.FunctionValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.util.Arrays; public final class std_sort implements Function { @@ -33,4 +38,4 @@ public Value execute(Value... args) { return new ArrayValue(elements); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java index a078a108..7300fbfe 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java @@ -1,6 +1,10 @@ package com.annimon.ownlang.lib.modules.functions; -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.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_split implements Function { @@ -15,4 +19,4 @@ public Value execute(Value... args) { final String[] parts = input.split(regex, limit); return ArrayValue.of(parts); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java index 8a6ea9d4..37b37931 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java @@ -1,6 +1,10 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; public final class std_sprintf implements Function { @@ -17,4 +21,4 @@ public Value execute(Value... args) { } return new StringValue(String.format(format, values)); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java index 56cbb626..54394a2e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_substring implements Function { @@ -21,4 +24,4 @@ public Value execute(Value... args) { return new StringValue(result); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java index 22fdb4f6..cecbee1d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java @@ -1,7 +1,13 @@ package com.annimon.ownlang.lib.modules.functions; import com.annimon.ownlang.Console; -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.Functions; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; public final class std_thread implements Function { @@ -21,10 +27,10 @@ public Value execute(Value... args) { if (params.length > 0) { System.arraycopy(args, 1, params, 0, params.length); } - + final Thread thread = new Thread(() -> body.execute(params)); thread.setUncaughtExceptionHandler(Console::handleException); thread.start(); return NumberValue.ZERO; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java index 1929ecba..4ca26120 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_tochar implements Function { @@ -9,4 +12,4 @@ public Value execute(Value... args) { Arguments.check(1, args.length); return new StringValue(String.valueOf((char) args[0].asInt())); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java index 87126006..41e58638 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_tolowercase implements Function { @@ -9,4 +12,4 @@ public Value execute(Value... args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().toLowerCase()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java index fc97c4dd..9723d72d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_touppercase implements Function { @@ -9,4 +12,4 @@ public Value execute(Value... args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().toUpperCase()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java index c6446e6c..840fb640 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java @@ -1,6 +1,9 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; public final class std_trim implements Function { @@ -9,4 +12,4 @@ public Value execute(Value... args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().trim()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/http.java b/src/main/java/com/annimon/ownlang/lib/modules/http.java index a8ffe17e..f73d2878 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/http.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/http.java @@ -1,14 +1,14 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.lib.modules.functions.*; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.modules.functions.http_download; +import com.annimon.ownlang.lib.modules.functions.http_http; +import com.annimon.ownlang.lib.modules.functions.http_urlencode; /** * * @author aNNiMON */ -@ConstantInitializer public final class http implements Module { public static void initConstants() { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/java.java b/src/main/java/com/annimon/ownlang/lib/modules/java.java index 2e4eeaea..73c0c7ca 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/java.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/java.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -11,10 +10,9 @@ /** * Java interoperability module. - * + * * @author aNNiMON */ -@ConstantInitializer public final class java implements Module { private static final Value NULL = new NullValue(); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java b/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java index 7f55f9e4..db24a570 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; @@ -32,7 +31,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class jdbc implements Module { public static void initConstants() { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/json.java b/src/main/java/com/annimon/ownlang/lib/modules/json.java index 6b5abb66..f3789651 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/json.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/json.java @@ -1,7 +1,8 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.lib.modules.functions.*; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.modules.functions.json_decode; +import com.annimon.ownlang.lib.modules.functions.json_encode; /** * diff --git a/src/main/java/com/annimon/ownlang/lib/modules/math.java b/src/main/java/com/annimon/ownlang/lib/modules/math.java index 826a624b..7b162722 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/math.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/math.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleFunction; @@ -11,7 +10,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class math implements Module { private static final DoubleFunction doubleToNumber = NumberValue::of; @@ -151,7 +149,7 @@ private static Function functionConvert(DoubleUnaryOperator op) { }; } - private static Function functionConvert(DoubleUnaryOperator opDouble, UnaryOperator opFloat) { + private static Function functionConvert(DoubleUnaryOperator opDouble, UnaryOperator opFloat) { return args -> { Arguments.check(1, args.length); final Object raw = args[0].raw(); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/ounit.java b/src/main/java/com/annimon/ownlang/lib/modules/ounit.java index 3fa0caef..7cf76d6d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/ounit.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/ounit.java @@ -1,6 +1,13 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import java.text.DecimalFormat; import java.util.List; import java.util.stream.Collectors; @@ -92,10 +99,10 @@ public Value execute(Value... args) { for (TestInfo test : tests) { if (!test.isPassed) failures++; summaryTime += test.elapsedTimeInMicros; - result.append(System.lineSeparator()); + result.append(Console.newline()); result.append(test.info()); } - result.append(System.lineSeparator()); + result.append(Console.newline()); result.append(String.format("Tests run: %d, Failures: %d, Time elapsed: %s", tests.size(), failures, microsToSeconds(summaryTime))); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/robot.java b/src/main/java/com/annimon/ownlang/lib/modules/robot.java index 10990c6c..e497f1a6 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/robot.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/robot.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.robot_exec; import com.annimon.ownlang.lib.modules.functions.robot_fromclipboard; @@ -16,7 +15,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class robot implements Module { private static final int CLICK_DELAY = 200; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/socket.java b/src/main/java/com/annimon/ownlang/lib/modules/socket.java index 8b035067..7d01e686 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/socket.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/socket.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import io.socket.client.IO; @@ -12,7 +11,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class socket implements Module { public static void initConstants() { diff --git a/src/main/java/com/annimon/ownlang/lib/modules/std.java b/src/main/java/com/annimon/ownlang/lib/modules/std.java index 7534d92c..1c3c7fc2 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/std.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/std.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.lib.modules; import com.annimon.ownlang.Main; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.lib.modules.functions.*; @@ -9,7 +8,6 @@ * * @author aNNiMON */ -@ConstantInitializer public final class std implements Module { public static void initConstants() { @@ -28,7 +26,7 @@ public void init() { Functions.set("thread", new std_thread()); Functions.set("sync", new std_sync()); Functions.set("try", new std_try()); - + // String Functions.set("sprintf", new std_sprintf()); Functions.set("split", new std_split()); diff --git a/src/main/java/com/annimon/ownlang/lib/modules/types.java b/src/main/java/com/annimon/ownlang/lib/modules/types.java index f207d7dc..5f07171e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/types.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/types.java @@ -1,13 +1,11 @@ package com.annimon.ownlang.lib.modules; -import com.annimon.ownlang.annotations.ConstantInitializer; import com.annimon.ownlang.lib.*; /** * * @author aNNiMON */ -@ConstantInitializer public final class types implements Module { public static void initConstants() { diff --git a/src/main/java/com/annimon/ownlang/parser/Beautifier.java b/src/main/java/com/annimon/ownlang/parser/Beautifier.java index 967b2142..064506eb 100644 --- a/src/main/java/com/annimon/ownlang/parser/Beautifier.java +++ b/src/main/java/com/annimon/ownlang/parser/Beautifier.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.Console; import java.util.HashMap; import java.util.Map; @@ -233,7 +234,7 @@ private boolean isSpace(char ch) { } private void newLineStrict() { - beautifiedCode.append(System.lineSeparator()); + beautifiedCode.append(Console.newline()); indent(); do { next(); diff --git a/src/main/java/com/annimon/ownlang/parser/ParseErrors.java b/src/main/java/com/annimon/ownlang/parser/ParseErrors.java index 345bb2d6..83873491 100644 --- a/src/main/java/com/annimon/ownlang/parser/ParseErrors.java +++ b/src/main/java/com/annimon/ownlang/parser/ParseErrors.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.Console; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -33,7 +34,7 @@ public Iterator iterator() { public String toString() { final StringBuilder result = new StringBuilder(); for (ParseError error : errors) { - result.append(error).append(System.lineSeparator()); + result.append(error).append(Console.newline()); } return result.toString(); } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java index 124b9c23..17ddc23c 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.Console; import java.util.ArrayList; import java.util.List; @@ -41,7 +42,7 @@ public R accept(ResultVisitor visitor, T t) { public String toString() { final StringBuilder result = new StringBuilder(); for (Statement statement : statements) { - result.append(statement.toString()).append(System.lineSeparator()); + result.append(statement.toString()).append(Console.newline()); } return result.toString(); } diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java b/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java index c5ad4970..75004dfa 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.Console; import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.Expression; import com.annimon.ownlang.parser.ast.Node; @@ -98,9 +99,9 @@ private Node tryCombine(Node n1, Node n2) { : ((PrintlnStatement) n2).expression; if (isConstantValue(e1) && isConstantValue(e2)) { String s1 = e1.eval().asString(); - if (n1Type == 2) s1 += System.lineSeparator(); + if (n1Type == 2) s1 += Console.newline(); String s2 = e2.eval().asString(); - if (n2Type == 2) s2 += System.lineSeparator(); + if (n2Type == 2) s2 += Console.newline(); printCombinedCount++; return new PrintStatement(new ValueExpression(s1 + s2)); } diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index b98bf9f8..4ecc6f02 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.visitors; +import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.Types; @@ -422,7 +423,7 @@ private StringBuilder visitFunctionBody(Statement s, StringBuilder t) { } private void newLine(StringBuilder t) { - t.append(System.lineSeparator()); + t.append(Console.newline()); } private void printIndent(StringBuilder sb) { diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java index 765b4712..bef51f4e 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -62,7 +62,7 @@ public static void main(String[] args) { continue; } - buffer.append(line).append(System.lineSeparator()); + buffer.append(line).append(Console.newline()); Statement program = null; try { final List tokens = Lexer.tokenize(buffer.toString()); diff --git a/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java b/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java index eed33b81..2c1e4e28 100644 --- a/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java +++ b/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.utils; +import com.annimon.ownlang.Console; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -62,12 +63,12 @@ public String summary(TimeUnit unit, boolean showSummary) { result.append(entry.getKey()).append(": ") .append(convertedTime).append(' ').append(unitName) - .append(System.lineSeparator()); + .append(Console.newline()); } if (showSummary) { result.append("Summary: ") .append(summaryTime).append(' ').append(unitName) - .append(System.lineSeparator()); + .append(Console.newline()); } return result.toString(); } From c382c4eab6b3df07f88f905c938dec7a1c23802a Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 13 Sep 2016 14:28:28 +0300 Subject: [PATCH 181/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B4=D0=BE=D1=81=D1=82=D1=83?= =?UTF-8?q?=D0=BF=D0=B0=20=D0=BA=20=D1=81=D1=82=D0=B0=D1=82=D1=82=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=BC=20=D0=BF=D0=BE=D0=BB=D1=8F?= =?UTF-8?q?=D0=BC=20=D0=B8=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/modules/java.java | 176 ++++++++++-------- 1 file changed, 94 insertions(+), 82 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/modules/java.java b/src/main/java/com/annimon/ownlang/lib/modules/java.java index 73c0c7ca..4f489024 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/java.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/java.java @@ -173,6 +173,19 @@ private Value cast(Value... args) { return objectToValue(clazz, clazz.cast(((ObjectValue)args[0]).object)); } + @Override + public boolean containsKey(Value key) { + return getValue(clazz, null, key.asString()) != null; + } + + @Override + public Value get(Value key) { + if (super.containsKey(key)) { + return super.get(key); + } + return getValue(clazz, null, key.asString()); + } + @Override public String toString() { return "ClassValue " + clazz.toString(); @@ -195,76 +208,12 @@ public ObjectValue(Object object) { @Override public boolean containsKey(Value key) { - return getValue(key.asString()) != null; + return getValue(object.getClass(), object, key.asString()) != null; } @Override public Value get(Value key) { - return getValue(key.asString()); - } - - private Value getValue(String key) { - // Trying to get field - try { - final Field field = object.getClass().getField(key); - return objectToValue(field.getType(), field.get(object)); - } catch (NoSuchFieldException | SecurityException | - IllegalArgumentException | IllegalAccessException ex) { - // ignore and go to the next step - } - - // Trying to invoke method - try { - final Method[] allMethods = object.getClass().getMethods(); - final List methods = new ArrayList<>(); - for (Method method : allMethods) { - if (method.getName().equals(key)) { - methods.add(method); - } - } - if (methods.size() == 0) { - return FunctionValue.EMPTY; - } - return new FunctionValue(methodsToFunction(methods)); - } catch (SecurityException ex) { - // ignore and go to the next step - } - - return NULL; - - } - - private Function methodsToFunction(List methods) { - return (args) -> { - for (Method method : methods) { - if (method.getParameterCount() != args.length) continue; - if (!isMatch(args, method.getParameterTypes())) continue; - try { - final Object result = method.invoke(object, valuesToObjects(args)); - if (method.getReturnType() != void.class) { - return objectToValue(result); - } - return NumberValue.ONE; - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - // skip - } - } - return null; - }; - } - - private boolean isMatch(Value[] args, Class[] types) { - for (int i = 0; i < args.length; i++) { - final Value arg = args[i]; - final Class clazz = types[i]; - - if (arg == NULL) continue; - if (unboxed(clazz).isAssignableFrom(unboxed(valueToObject(arg).getClass()))) { - continue; - } - return false; - } - return true; + return getValue(object.getClass(), object, key.asString()); } @Override @@ -276,22 +225,6 @@ public String asString() { public String toString() { return "ObjectValue " + asString(); } - - private Class unboxed(Class clazz) { - if (clazz == null) return null; - if (clazz.isPrimitive()) { - if (int.class == clazz) return Integer.class; - if (boolean.class == clazz) return Boolean.class; - if (double.class == clazz) return Double.class; - if (float.class == clazz) return Float.class; - if (long.class == clazz) return Long.class; - if (byte.class == clazz) return Byte.class; - if (char.class == clazz) return Character.class; - if (short.class == clazz) return Short.class; - if (void.class == clazz) return Void.class; - } - return clazz; - } } // @@ -330,6 +263,85 @@ private Value toValue(Value... args) { // + private static Value getValue(Class clazz, Object object, String key) { + // Trying to get field + try { + final Field field = clazz.getField(key); + return objectToValue(field.getType(), field.get(object)); + } catch (NoSuchFieldException | SecurityException | + IllegalArgumentException | IllegalAccessException ex) { + // ignore and go to the next step + } + + // Trying to invoke method + try { + final Method[] allMethods = clazz.getMethods(); + final List methods = new ArrayList<>(); + for (Method method : allMethods) { + if (method.getName().equals(key)) { + methods.add(method); + } + } + if (methods.size() == 0) { + return FunctionValue.EMPTY; + } + return new FunctionValue(methodsToFunction(object, methods)); + } catch (SecurityException ex) { + // ignore and go to the next step + } + + return NULL; + } + + private static Function methodsToFunction(Object object, List methods) { + return (args) -> { + for (Method method : methods) { + if (method.getParameterCount() != args.length) continue; + if (!isMatch(args, method.getParameterTypes())) continue; + try { + final Object result = method.invoke(object, valuesToObjects(args)); + if (method.getReturnType() != void.class) { + return objectToValue(result); + } + return NumberValue.ONE; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + // skip + } + } + return null; + }; + } + + private static boolean isMatch(Value[] args, Class[] types) { + for (int i = 0; i < args.length; i++) { + final Value arg = args[i]; + final Class clazz = types[i]; + + if (arg == NULL) continue; + if (unboxed(clazz).isAssignableFrom(unboxed(valueToObject(arg).getClass()))) { + continue; + } + return false; + } + return true; + } + + private static Class unboxed(Class clazz) { + if (clazz == null) return null; + if (clazz.isPrimitive()) { + if (int.class == clazz) return Integer.class; + if (boolean.class == clazz) return Boolean.class; + if (double.class == clazz) return Double.class; + if (float.class == clazz) return Float.class; + if (long.class == clazz) return Long.class; + if (byte.class == clazz) return Byte.class; + if (char.class == clazz) return Character.class; + if (short.class == clazz) return Short.class; + if (void.class == clazz) return Void.class; + } + return clazz; + } + private static ArrayValue array(Class[] classes) { final ArrayValue result = new ArrayValue(classes.length); for (int i = 0; i < classes.length; i++) { From ba60a498d7012eb5754cf65b924e7c1810d41ce0 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 13 Sep 2016 14:29:00 +0300 Subject: [PATCH 182/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/functions/function_call_chain.own | 3 +++ examples/functions/function_chain.own | 9 ++++++++ examples/functions/stream.own | 26 ++++++++++++++++++++++ examples/java/system_info.own | 5 +++++ 4 files changed, 43 insertions(+) create mode 100644 examples/functions/function_call_chain.own create mode 100644 examples/functions/function_chain.own create mode 100644 examples/functions/stream.own create mode 100644 examples/java/system_info.own diff --git a/examples/functions/function_call_chain.own b/examples/functions/function_call_chain.own new file mode 100644 index 00000000..a3791d4b --- /dev/null +++ b/examples/functions/function_call_chain.own @@ -0,0 +1,3 @@ +def f() = def() = def() = def() = "Function call chaining: f()()()()" + +println f()()()() \ No newline at end of file diff --git a/examples/functions/function_chain.own b/examples/functions/function_chain.own new file mode 100644 index 00000000..c73b16de --- /dev/null +++ b/examples/functions/function_chain.own @@ -0,0 +1,9 @@ +def calc() = { + "add": ::add, + "sub": ::minus +} +def add(x, y) = x + y +def minus(x, y) = x - y + +println calc().add(77, 42) +println calc().sub(77, 42) \ No newline at end of file diff --git a/examples/functions/stream.own b/examples/functions/stream.own new file mode 100644 index 00000000..2deb5cc6 --- /dev/null +++ b/examples/functions/stream.own @@ -0,0 +1,26 @@ +use "std" +use "functional" + +println "x, square(x), cube(x) for even numbers" +data = [1,2,3,4,5,6,7,8,9] +stream(data) + .filter(def(x) = x % 2 == 0) + .map(def(x) = [x, x * x, x * x * x]) + .sortBy(def(x) = -x[2]) + .forEach(::echo) + + +println "\nReverse custom operator" +data = [2, 4, 6, 5, 12, 34, 0, 18] +rev = stream(data).custom(::reverse).toArray() +println data +println rev + +def reverse(container) { + size = length(container) + result = newarray(size) + for i : range(size) { + result[size - i - 1] = container[i] + } + return result +} \ No newline at end of file diff --git a/examples/java/system_info.own b/examples/java/system_info.own new file mode 100644 index 00000000..d8fc3a0c --- /dev/null +++ b/examples/java/system_info.own @@ -0,0 +1,5 @@ +use "java" +System = newClass("java.lang.System") +println "OS name: " + System.getProperty("os.name") +println "OS version: " + System.getProperty("os.version") +println "User home: " + System.getProperty("user.home") \ No newline at end of file From 29d40a554fad176deb497e337814284ef0ab6376 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 16 Sep 2016 16:25:12 +0300 Subject: [PATCH 183/448] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proguard.properties | 2 +- .../annimon/ownlang/annotations/Modules.java | 13 ------------ .../ownlang/lib/modules/package-info.java | 17 ---------------- .../ownlang/{lib => }/modules/Module.java | 2 +- .../modules => modules/canvas}/canvas.java | 3 ++- .../canvasfx}/canvasfx.java | 3 ++- .../{lib/modules => modules/date}/date.java | 3 ++- .../{lib/modules => modules/files}/files.java | 3 ++- .../forms/ComponentValue.java | 2 +- .../forms/Components.java | 2 +- .../forms/ContainerValue.java | 2 +- .../forms/JButtonValue.java | 2 +- .../forms/JComponentValue.java | 2 +- .../forms/JFrameValue.java | 2 +- .../forms/JLabelValue.java | 2 +- .../forms/JPanelValue.java | 2 +- .../forms/JTextFieldValue.java | 2 +- .../forms/LayoutManagerValue.java | 2 +- .../forms/LayoutManagers.java | 2 +- .../{lib/modules => modules/forms}/forms.java | 5 ++--- .../functional}/functional.java | 4 ++-- .../functional}/functional_chain.java | 2 +- .../functional}/functional_combine.java | 2 +- .../functional}/functional_dropwhile.java | 2 +- .../functional}/functional_filter.java | 2 +- .../functional}/functional_flatmap.java | 2 +- .../functional}/functional_foreach.java | 2 +- .../functional}/functional_map.java | 2 +- .../functional}/functional_reduce.java | 2 +- .../functional}/functional_sortby.java | 2 +- .../functional}/functional_stream.java | 2 +- .../{lib/modules => modules/http}/http.java | 6 ++---- .../http}/http_download.java | 2 +- .../functions => modules/http}/http_http.java | 2 +- .../http}/http_urlencode.java | 2 +- .../{lib/modules => modules/java}/java.java | 3 ++- .../{lib/modules => modules/jdbc}/jdbc.java | 3 ++- .../{lib/modules => modules/json}/json.java | 5 ++--- .../json}/json_decode.java | 2 +- .../json}/json_encode.java | 2 +- .../{lib/modules => modules/math}/math.java | 3 ++- .../{lib/modules => modules/ounit}/ounit.java | 3 ++- .../{lib/modules => modules/robot}/robot.java | 6 ++---- .../robot}/robot_exec.java | 2 +- .../robot}/robot_fromclipboard.java | 2 +- .../robot}/robot_toclipboard.java | 2 +- .../modules => modules/socket}/socket.java | 3 ++- .../{lib/modules => modules/std}/std.java | 4 ++-- .../std}/std_arrayCombine.java | 2 +- .../std}/std_arrayKeyExists.java | 2 +- .../std}/std_arrayKeys.java | 2 +- .../std}/std_arrayValues.java | 2 +- .../functions => modules/std}/std_charat.java | 2 +- .../functions => modules/std}/std_echo.java | 2 +- .../std}/std_indexof.java | 2 +- .../functions => modules/std}/std_join.java | 2 +- .../std}/std_lastindexof.java | 2 +- .../functions => modules/std}/std_length.java | 2 +- .../std}/std_newarray.java | 2 +- .../functions => modules/std}/std_rand.java | 2 +- .../functions => modules/std}/std_range.java | 2 +- .../functions => modules/std}/std_readln.java | 2 +- .../std}/std_replace.java | 2 +- .../std}/std_replaceall.java | 2 +- .../std}/std_replacefirst.java | 2 +- .../functions => modules/std}/std_sleep.java | 2 +- .../functions => modules/std}/std_sort.java | 2 +- .../functions => modules/std}/std_split.java | 2 +- .../std}/std_sprintf.java | 2 +- .../std}/std_substring.java | 2 +- .../functions => modules/std}/std_sync.java | 2 +- .../functions => modules/std}/std_thread.java | 2 +- .../functions => modules/std}/std_time.java | 2 +- .../functions => modules/std}/std_tochar.java | 2 +- .../std}/std_tolowercase.java | 2 +- .../std}/std_touppercase.java | 2 +- .../functions => modules/std}/std_trim.java | 2 +- .../functions => modules/std}/std_try.java | 2 +- .../{lib/modules => modules/types}/types.java | 3 ++- .../ownlang/parser/ast/UseStatement.java | 8 ++++---- .../ownlang/utils/ModulesInfoCreator.java | 20 ++++++++++++------- 81 files changed, 110 insertions(+), 130 deletions(-) delete mode 100644 src/main/java/com/annimon/ownlang/annotations/Modules.java delete mode 100644 src/main/java/com/annimon/ownlang/lib/modules/package-info.java rename src/main/java/com/annimon/ownlang/{lib => }/modules/Module.java (65%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/canvas}/canvas.java (98%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/canvasfx}/canvasfx.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/date}/date.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/files}/files.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/ComponentValue.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/Components.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/ContainerValue.java (97%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/JButtonValue.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/JComponentValue.java (90%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/JFrameValue.java (94%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/JLabelValue.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/JPanelValue.java (81%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/JTextFieldValue.java (97%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/LayoutManagerValue.java (82%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules}/forms/LayoutManagers.java (98%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/forms}/forms.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/functional}/functional.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_chain.java (94%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_combine.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_dropwhile.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_filter.java (97%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_flatmap.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_foreach.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_map.java (97%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_reduce.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_sortby.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/functional}/functional_stream.java (98%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/http}/http.java (63%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/http}/http_download.java (92%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/http}/http_http.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/http}/http_urlencode.java (93%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/java}/java.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/jdbc}/jdbc.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/json}/json.java (68%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/json}/json_decode.java (93%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/json}/json_encode.java (93%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/math}/math.java (98%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/ounit}/ounit.java (98%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/robot}/robot.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/robot}/robot_exec.java (97%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/robot}/robot_fromclipboard.java (92%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/robot}/robot_toclipboard.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/socket}/socket.java (98%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/std}/std.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_arrayCombine.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_arrayKeyExists.java (93%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_arrayKeys.java (94%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_arrayValues.java (94%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_charat.java (90%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_echo.java (90%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_indexof.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_join.java (97%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_lastindexof.java (92%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_length.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_newarray.java (94%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_rand.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_range.java (99%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_readln.java (88%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_replace.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_replaceall.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_replacefirst.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_sleep.java (91%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_sort.java (96%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_split.java (92%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_sprintf.java (93%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_substring.java (93%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_sync.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_thread.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_time.java (85%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_tochar.java (88%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_tolowercase.java (88%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_touppercase.java (88%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_trim.java (88%) rename src/main/java/com/annimon/ownlang/{lib/modules/functions => modules/std}/std_try.java (95%) rename src/main/java/com/annimon/ownlang/{lib/modules => modules/types}/types.java (93%) diff --git a/proguard.properties b/proguard.properties index 5a0ee201..be30bc34 100644 --- a/proguard.properties +++ b/proguard.properties @@ -34,7 +34,7 @@ native ; } --keep public class * implements com.annimon.ownlang.lib.modules.Module +-keep public class * implements com.annimon.ownlang.modules.Module # Soft obfuscation -keep public class * { diff --git a/src/main/java/com/annimon/ownlang/annotations/Modules.java b/src/main/java/com/annimon/ownlang/annotations/Modules.java deleted file mode 100644 index bf9682a2..00000000 --- a/src/main/java/com/annimon/ownlang/annotations/Modules.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.annimon.ownlang.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PACKAGE) -public @interface Modules { - - Class[] modules(); -} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/package-info.java b/src/main/java/com/annimon/ownlang/lib/modules/package-info.java deleted file mode 100644 index aafa2e51..00000000 --- a/src/main/java/com/annimon/ownlang/lib/modules/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -@Modules(modules = { - canvas.class, - canvasfx.class, - date.class, - files.class, - functional.class, - http.class, - json.class, - math.class, - ounit.class, - robot.class, - std.class, - types.class -}) -package com.annimon.ownlang.lib.modules; - -import com.annimon.ownlang.annotations.Modules; \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/Module.java b/src/main/java/com/annimon/ownlang/modules/Module.java similarity index 65% rename from src/main/java/com/annimon/ownlang/lib/modules/Module.java rename to src/main/java/com/annimon/ownlang/modules/Module.java index d022d1fb..cda5ca4f 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/Module.java +++ b/src/main/java/com/annimon/ownlang/modules/Module.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules; /** * diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvas.java b/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java similarity index 98% rename from src/main/java/com/annimon/ownlang/lib/modules/canvas.java rename to src/main/java/com/annimon/ownlang/modules/canvas/canvas.java index a76e79b9..a6f0bc54 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvas.java +++ b/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java @@ -1,6 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.canvas; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java b/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java rename to src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java index 8934f331..973f4ae8 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java @@ -1,7 +1,8 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.canvasfx; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import static com.annimon.ownlang.lib.Converters.*; import java.awt.Dimension; import java.lang.reflect.Modifier; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/date.java b/src/main/java/com/annimon/ownlang/modules/date/date.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/date.java rename to src/main/java/com/annimon/ownlang/modules/date/date.java index a869e452..b83e24cb 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/date.java +++ b/src/main/java/com/annimon/ownlang/modules/date/date.java @@ -1,7 +1,8 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.date; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/files.java b/src/main/java/com/annimon/ownlang/modules/files/files.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/files.java rename to src/main/java/com/annimon/ownlang/modules/files/files.java index bcb2515c..a2e2a0e6 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/files.java +++ b/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -1,7 +1,8 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.files; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java index 3fcf8aea..5ab56f82 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ComponentValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java b/src/main/java/com/annimon/ownlang/modules/forms/Components.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java rename to src/main/java/com/annimon/ownlang/modules/forms/Components.java index 6639af93..1da492ab 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/Components.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/Components.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Value; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java b/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java index a677b282..0ccb0716 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/ContainerValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.lib.Arguments; import static com.annimon.ownlang.lib.Converters.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java index 796c34f1..5afce2fa 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JButtonValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java similarity index 90% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java index f3f04596..63b5829f 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JComponentValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import static com.annimon.ownlang.lib.Converters.*; import javax.swing.JComponent; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java similarity index 94% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java index 747cd6c5..bb547d17 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JFrameValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import static com.annimon.ownlang.lib.Converters.*; import javax.swing.JFrame; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java index 2256b34a..0b2cba1d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JLabelValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import static com.annimon.ownlang.lib.Converters.*; import javax.swing.JLabel; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java similarity index 81% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java index 7e6ad92b..8071a25e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JPanelValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import javax.swing.JPanel; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java index 94400db6..d6fed172 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/JTextFieldValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java similarity index 82% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java rename to src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java index 3ed2c423..a44cc54c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagerValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.lib.MapValue; import java.awt.LayoutManager; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java similarity index 98% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java rename to src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java index 0791d289..8191e691 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/forms/LayoutManagers.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions.forms; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Value; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/forms.java b/src/main/java/com/annimon/ownlang/modules/forms/forms.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/forms.java rename to src/main/java/com/annimon/ownlang/modules/forms/forms.java index 1216449b..477f7ff2 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/forms.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/forms.java @@ -1,8 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.forms; import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.lib.modules.functions.forms.Components; -import com.annimon.ownlang.lib.modules.functions.forms.LayoutManagers; +import com.annimon.ownlang.modules.Module; import java.awt.BorderLayout; import javax.swing.BoxLayout; import javax.swing.JFrame; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functional.java b/src/main/java/com/annimon/ownlang/modules/functional/functional.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functional.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional.java index 2e778cb7..f75d0080 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functional.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -1,9 +1,9 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.Variables; -import com.annimon.ownlang.lib.modules.functions.*; +import com.annimon.ownlang.modules.Module; /** * diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java similarity index 94% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java index d844e1d2..fce2bc8e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_chain.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java index 8a1b7574..33458de1 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_combine.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java index ecafe92b..5f4dc900 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_dropwhile.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java similarity index 97% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java index aa4ee0ce..d0e631b0 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_filter.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java index 5e025c21..5130810e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_flatmap.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java index 7c19faa7..4e0ceed8 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_foreach.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java similarity index 97% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_map.java index 5cbfbe69..9016e69d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_map.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java index 282d0163..a128066a 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_reduce.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java index 066c4a14..926047d0 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_sortby.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java similarity index 98% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java rename to src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java index aac84086..82e590a6 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/functional_stream.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/http.java b/src/main/java/com/annimon/ownlang/modules/http/http.java similarity index 63% rename from src/main/java/com/annimon/ownlang/lib/modules/http.java rename to src/main/java/com/annimon/ownlang/modules/http/http.java index f73d2878..b8d22cad 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/http.java +++ b/src/main/java/com/annimon/ownlang/modules/http/http.java @@ -1,9 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.http; import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.modules.functions.http_download; -import com.annimon.ownlang.lib.modules.functions.http_http; -import com.annimon.ownlang.lib.modules.functions.http_urlencode; +import com.annimon.ownlang.modules.Module; /** * diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_download.java b/src/main/java/com/annimon/ownlang/modules/http/http_download.java similarity index 92% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/http_download.java rename to src/main/java/com/annimon/ownlang/modules/http/http_download.java index 46077976..37de2115 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_download.java +++ b/src/main/java/com/annimon/ownlang/modules/http/http_download.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.http; import com.annimon.ownlang.lib.*; import java.io.IOException; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java b/src/main/java/com/annimon/ownlang/modules/http/http_http.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java rename to src/main/java/com/annimon/ownlang/modules/http/http_http.java index aa75a044..bddc3c5d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_http.java +++ b/src/main/java/com/annimon/ownlang/modules/http/http_http.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.http; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_urlencode.java b/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/http_urlencode.java rename to src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java index 7cf3257c..c21c5f58 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/http_urlencode.java +++ b/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.http; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/java.java rename to src/main/java/com/annimon/ownlang/modules/java/java.java index 4f489024..8de7c317 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -1,6 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.java; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/jdbc.java rename to src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index db24a570..ff9e9b67 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/jdbc.java +++ b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.jdbc; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.Arguments; @@ -11,6 +11,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.modules.Module; import java.io.IOException; import java.math.BigDecimal; import java.net.URL; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/json.java b/src/main/java/com/annimon/ownlang/modules/json/json.java similarity index 68% rename from src/main/java/com/annimon/ownlang/lib/modules/json.java rename to src/main/java/com/annimon/ownlang/modules/json/json.java index f3789651..1ff77282 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/json.java +++ b/src/main/java/com/annimon/ownlang/modules/json/json.java @@ -1,8 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.json; import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.modules.functions.json_decode; -import com.annimon.ownlang.lib.modules.functions.json_encode; +import com.annimon.ownlang.modules.Module; /** * diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/main/java/com/annimon/ownlang/modules/json/json_decode.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java rename to src/main/java/com/annimon/ownlang/modules/json/json_decode.java index c6f56a70..c75e4c9c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java +++ b/src/main/java/com/annimon/ownlang/modules/json/json_decode.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.json; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/main/java/com/annimon/ownlang/modules/json/json_encode.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java rename to src/main/java/com/annimon/ownlang/modules/json/json_encode.java index 7907b14e..7222124d 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java +++ b/src/main/java/com/annimon/ownlang/modules/json/json_encode.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.json; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/math.java b/src/main/java/com/annimon/ownlang/modules/math/math.java similarity index 98% rename from src/main/java/com/annimon/ownlang/lib/modules/math.java rename to src/main/java/com/annimon/ownlang/modules/math/math.java index 7b162722..ac3f42ea 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/math.java +++ b/src/main/java/com/annimon/ownlang/modules/math/math.java @@ -1,6 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.math; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleFunction; import java.util.function.DoubleUnaryOperator; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/ounit.java b/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java similarity index 98% rename from src/main/java/com/annimon/ownlang/lib/modules/ounit.java rename to src/main/java/com/annimon/ownlang/modules/ounit/ounit.java index 7cf76d6d..1a129032 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/ounit.java +++ b/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.ounit; import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Arguments; @@ -8,6 +8,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.modules.Module; import java.text.DecimalFormat; import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/robot.java b/src/main/java/com/annimon/ownlang/modules/robot/robot.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/robot.java rename to src/main/java/com/annimon/ownlang/modules/robot/robot.java index e497f1a6..090838e5 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/robot.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot.java @@ -1,9 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.robot; import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.lib.modules.functions.robot_exec; -import com.annimon.ownlang.lib.modules.functions.robot_fromclipboard; -import com.annimon.ownlang.lib.modules.functions.robot_toclipboard; +import com.annimon.ownlang.modules.Module; import java.awt.AWTException; import java.awt.Robot; import java.awt.event.InputEvent; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java b/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java similarity index 97% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java rename to src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java index 9f4f5ae3..3533a341 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_exec.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.robot; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java b/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java similarity index 92% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java rename to src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java index cf50b648..6e959d44 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_fromclipboard.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.robot; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.StringValue; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java b/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java rename to src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java index d70d3eea..a1be3d16 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/robot_toclipboard.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.robot; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/socket.java b/src/main/java/com/annimon/ownlang/modules/socket/socket.java similarity index 98% rename from src/main/java/com/annimon/ownlang/lib/modules/socket.java rename to src/main/java/com/annimon/ownlang/modules/socket/socket.java index 7d01e686..858bb4ad 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/socket.java +++ b/src/main/java/com/annimon/ownlang/modules/socket/socket.java @@ -1,7 +1,8 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.socket; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; import io.socket.client.IO; import io.socket.client.Socket; import java.net.URISyntaxException; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/std.java rename to src/main/java/com/annimon/ownlang/modules/std/std.java index 1c3c7fc2..a28149ca 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -1,8 +1,8 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.Main; import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.lib.modules.functions.*; +import com.annimon.ownlang.modules.Module; /** * diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java b/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java rename to src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java index 7cbbcb1b..ac72c83f 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayCombine.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java b/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java rename to src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java index 946547c1..b54eb0c7 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeyExists.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java b/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java similarity index 94% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java rename to src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java index b83b12b3..9a32de01 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayKeys.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java b/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java similarity index 94% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java rename to src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java index c5671509..a56ee5c7 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_arrayValues.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java b/src/main/java/com/annimon/ownlang/modules/std/std_charat.java similarity index 90% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java rename to src/main/java/com/annimon/ownlang/modules/std/std_charat.java index 8a753996..e1b45a73 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_charat.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_charat.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java b/src/main/java/com/annimon/ownlang/modules/std/std_echo.java similarity index 90% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java rename to src/main/java/com/annimon/ownlang/modules/std/std_echo.java index ab8c54df..c296e8ea 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_echo.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_echo.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java b/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java rename to src/main/java/com/annimon/ownlang/modules/std/std_indexof.java index 660a32c5..cf905de5 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_indexof.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java b/src/main/java/com/annimon/ownlang/modules/std/std_join.java similarity index 97% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java rename to src/main/java/com/annimon/ownlang/modules/std/std_join.java index 3be01d40..6bf417e4 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_join.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_join.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java b/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java similarity index 92% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java rename to src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java index 607746ef..54902092 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_lastindexof.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_length.java b/src/main/java/com/annimon/ownlang/modules/std/std_length.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_length.java rename to src/main/java/com/annimon/ownlang/modules/std/std_length.java index 107da1f7..bdd7a210 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_length.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_length.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_newarray.java b/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java similarity index 94% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_newarray.java rename to src/main/java/com/annimon/ownlang/modules/std/std_newarray.java index 7b39f6c6..83839d7c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_newarray.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java b/src/main/java/com/annimon/ownlang/modules/std/std_rand.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java rename to src/main/java/com/annimon/ownlang/modules/std/std_rand.java index cdab6136..542b521c 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_rand.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_rand.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_range.java b/src/main/java/com/annimon/ownlang/modules/std/std_range.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_range.java rename to src/main/java/com/annimon/ownlang/modules/std/std_range.java index e5198dbc..19e8c9c0 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_range.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_range.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.*; import java.util.Iterator; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java b/src/main/java/com/annimon/ownlang/modules/std/std_readln.java similarity index 88% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java rename to src/main/java/com/annimon/ownlang/modules/std/std_readln.java index 28ec1529..082b7dbd 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_readln.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_readln.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.StringValue; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java b/src/main/java/com/annimon/ownlang/modules/std/std_replace.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java rename to src/main/java/com/annimon/ownlang/modules/std/std_replace.java index 927e0f21..a52cf0bb 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replace.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_replace.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java b/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java rename to src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java index f12134f1..4d6407e3 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replaceall.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java b/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java rename to src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java index 1d62736d..060b8a1b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_replacefirst.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sleep.java b/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java similarity index 91% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_sleep.java rename to src/main/java/com/annimon/ownlang/modules/std/std_sleep.java index a1d1eca2..62566808 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sleep.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java b/src/main/java/com/annimon/ownlang/modules/std/std_sort.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java rename to src/main/java/com/annimon/ownlang/modules/std/std_sort.java index 6c5c69aa..4dc39b86 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sort.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_sort.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java b/src/main/java/com/annimon/ownlang/modules/std/std_split.java similarity index 92% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java rename to src/main/java/com/annimon/ownlang/modules/std/std_split.java index 7300fbfe..d0e1b5e1 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_split.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_split.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java b/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java rename to src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java index 37b37931..1a6dbcdd 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sprintf.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java b/src/main/java/com/annimon/ownlang/modules/std/std_substring.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java rename to src/main/java/com/annimon/ownlang/modules/std/std_substring.java index 54394a2e..8c65e58a 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_substring.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_substring.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sync.java b/src/main/java/com/annimon/ownlang/modules/std/std_sync.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_sync.java rename to src/main/java/com/annimon/ownlang/modules/std/std_sync.java index 0dd01c8d..d4fef543 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_sync.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_sync.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java b/src/main/java/com/annimon/ownlang/modules/std/std_thread.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java rename to src/main/java/com/annimon/ownlang/modules/std/std_thread.java index cecbee1d..e23bdef3 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_thread.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_thread.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Arguments; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_time.java b/src/main/java/com/annimon/ownlang/modules/std/std_time.java similarity index 85% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_time.java rename to src/main/java/com/annimon/ownlang/modules/std/std_time.java index ad4c9f66..3a5e81ce 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_time.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_time.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.NumberValue; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java b/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java similarity index 88% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java rename to src/main/java/com/annimon/ownlang/modules/std/std_tochar.java index 4ca26120..ef31d18e 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tochar.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java b/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java similarity index 88% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java rename to src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java index 41e58638..a419719a 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_tolowercase.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java b/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java similarity index 88% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java rename to src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java index 9723d72d..d6f102af 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_touppercase.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java b/src/main/java/com/annimon/ownlang/modules/std/std_trim.java similarity index 88% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java rename to src/main/java/com/annimon/ownlang/modules/std/std_trim.java index 840fb640..2853142b 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_trim.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_trim.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_try.java b/src/main/java/com/annimon/ownlang/modules/std/std_try.java similarity index 95% rename from src/main/java/com/annimon/ownlang/lib/modules/functions/std_try.java rename to src/main/java/com/annimon/ownlang/modules/std/std_try.java index b95f849b..c748b8f2 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/std_try.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_try.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.lib.modules.functions; +package com.annimon.ownlang.modules.std; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; diff --git a/src/main/java/com/annimon/ownlang/lib/modules/types.java b/src/main/java/com/annimon/ownlang/modules/types/types.java similarity index 93% rename from src/main/java/com/annimon/ownlang/lib/modules/types.java rename to src/main/java/com/annimon/ownlang/modules/types/types.java index 5f07171e..fcdc1388 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/types.java +++ b/src/main/java/com/annimon/ownlang/modules/types/types.java @@ -1,6 +1,7 @@ -package com.annimon.ownlang.lib.modules; +package com.annimon.ownlang.modules.types; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; /** * diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 095d53a0..7a40d23a 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.modules.Module; +import com.annimon.ownlang.modules.Module; import java.lang.reflect.Method; /** @@ -9,7 +9,7 @@ */ public final class UseStatement extends InterruptableNode implements Statement { - private static final String PACKAGE = "com.annimon.ownlang.lib.modules."; + private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s"; private static final String INIT_CONSTANTS_METHOD = "initConstants"; public final Expression expression; @@ -23,7 +23,7 @@ public void execute() { super.interruptionCheck(); try { final String moduleName = expression.eval().asString(); - final Module module = (Module) Class.forName(PACKAGE + moduleName).newInstance(); + final Module module = (Module) Class.forName(String.format(PACKAGE, moduleName, moduleName)).newInstance(); module.init(); } catch (Exception ex) { throw new RuntimeException(ex); @@ -33,7 +33,7 @@ public void execute() { public void loadConstants() { try { final String moduleName = expression.eval().asString(); - final Class moduleClass = Class.forName(PACKAGE + moduleName); + final Class moduleClass = Class.forName(String.format(PACKAGE, moduleName, moduleName)); final Method method = moduleClass.getMethod(INIT_CONSTANTS_METHOD); if (method != null) { method.invoke(this); diff --git a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index 6e6a91b6..4eef0f0f 100644 --- a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -1,12 +1,12 @@ package com.annimon.ownlang.utils; -import com.annimon.ownlang.annotations.Modules; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.MapValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; -import com.annimon.ownlang.lib.modules.Module; +import com.annimon.ownlang.modules.Module; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -22,20 +22,26 @@ public final class ModulesInfoCreator { - public static void main(String[] args) throws InstantiationException, IllegalAccessException { + private static final String MODULES_PATH = "src/main/java/com/annimon/ownlang/modules"; + + public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { final Class clazz = Module.class; // get classloader for package final List moduleInfos = new ArrayList<>(); - final Package modulesPackage = Package.getPackage("com.annimon.ownlang.lib.modules"); - final Modules annotation = modulesPackage.getAnnotation(Modules.class); - for (Class moduleClass : annotation.modules()) { + String[] moduleNames = Arrays.stream(new File(MODULES_PATH).listFiles()) + .filter(p -> p.isDirectory()) + .map(p -> p.getName()) + .toArray(String[]::new); + for (String moduleName : moduleNames) { + final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName); + Class moduleClass = Class.forName(moduleClassPath); Functions.getFunctions().clear(); Variables.variables().clear(); final Module module = (Module) moduleClass.newInstance(); module.init(); - final ModuleInfo moduleInfo = new ModuleInfo(moduleClass.getSimpleName()); + final ModuleInfo moduleInfo = new ModuleInfo(moduleName); moduleInfo.functions.addAll(Functions.getFunctions().keySet()); moduleInfo.constants.putAll(Variables.variables()); moduleInfo.types.addAll(listValues(moduleClass)); From 31b4539fd82b15905511e79d1436fa16fda741a6 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 16 Sep 2016 17:32:35 +0300 Subject: [PATCH 184/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + examples/formats/yaml.own | 44 +++++++++++++ .../annimon/ownlang/modules/yaml/yaml.java | 17 +++++ .../ownlang/modules/yaml/yaml_decode.java | 62 +++++++++++++++++++ .../ownlang/modules/yaml/yaml_encode.java | 56 +++++++++++++++++ .../resources/modules/yaml/yamldecode.own | 33 ++++++++++ .../resources/modules/yaml/yamlencode.own | 22 +++++++ 7 files changed, 235 insertions(+) create mode 100644 examples/formats/yaml.own create mode 100644 src/main/java/com/annimon/ownlang/modules/yaml/yaml.java create mode 100644 src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java create mode 100644 src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java create mode 100644 src/test/resources/modules/yaml/yamldecode.own create mode 100644 src/test/resources/modules/yaml/yamlencode.own diff --git a/build.gradle b/build.gradle index c7e17fd1..33a77193 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ dependencies { exclude group: 'org.json', module: 'json' } compile 'org.json:json:20160212' + compile 'org.yaml:snakeyaml:1.17' testCompile 'junit:junit:4.12' testCompile 'org.openjdk.jmh:jmh-core:1.13' diff --git a/examples/formats/yaml.own b/examples/formats/yaml.own new file mode 100644 index 00000000..cd2c08d5 --- /dev/null +++ b/examples/formats/yaml.own @@ -0,0 +1,44 @@ +use "yaml" + +println "Yaml encode" +println yamlencode({ + "name": "Yaml Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } +}) + +println "\nYaml decode" +x = yamldecode(" + name: \"std\" + scope: \"both\" + desc: \"Contains common functions\" + desc_ru: \"Содержит вспомогательные функции общего назначения\" + constants: [] + functions: + - + name: \"arrayCombine\" + args: \"keys, values\" + desc: \"creates map by combining two arrays\" + desc_ru: \"создаёт объект на основе двух массивов\" + - + name: \"typeof\" + args: \"value\" + desc: \"returns the type of value\" + desc_ru: \"возвращает тип переданного значения\" + example: |- + print typeof(1) // 1 (NUMBER) + print typeof(\"text\") // 2 (STRING) + print typeof([]) // 3 (ARRAY) +") +println x.name + ", scope: " + x.scope +println x.desc +for func : x.functions { + println " - " + func.name + "(" + func.args + ")" + println " " + func.desc +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java new file mode 100644 index 00000000..97ddf2f2 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; + +/** + * + * @author aNNiMON + */ +public final class yaml implements Module { + + @Override + public void init() { + Functions.set("yamlencode", new yaml_encode()); + Functions.set("yamldecode", new yaml_decode()); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java new file mode 100644 index 00000000..fa3d3fcd --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import java.util.List; +import java.util.Map; +import org.yaml.snakeyaml.Yaml; + +public final class yaml_decode implements Function { + + @Override + public Value execute(Value... args) { + Arguments.check(1, args.length); + try { + final String yamlRaw = args[0].asString(); + final Object root = new Yaml().load(yamlRaw); + final Value process = process(root); + return process; + } catch (Exception ex) { + throw new RuntimeException("Error while parsing yaml", ex); + } + } + + private Value process(Object obj) { + if (obj instanceof Map) { + return process((Map) obj); + } + if (obj instanceof List) { + return process((List) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return NumberValue.of(((Number) obj)); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + private MapValue process(Map map) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry entry : map.entrySet()) { + final String key = entry.getKey().toString(); + final Value value = process(entry.getValue()); + result.set(new StringValue(key), value); + } + return result; + } + + private ArrayValue process(List list) { + final int length = list.size(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = process(list.get(i)); + result.set(i, value); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java new file mode 100644 index 00000000..9c00be4c --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.yaml.snakeyaml.Yaml; + +public final class yaml_encode implements Function { + + @Override + public Value execute(Value... args) { + Arguments.check(1, args.length); + try { + final Object root = process(args[0]); + final String yamlRaw = new Yaml().dump(root); + return new StringValue(yamlRaw); + } catch (Exception ex) { + throw new RuntimeException("Error while creating yaml", ex); + } + } + + private Object process(Value val) { + switch (val.type()) { + case Types.ARRAY: + return process((ArrayValue) val); + case Types.MAP: + return process((MapValue) val); + case Types.NUMBER: + return val.raw(); + case Types.STRING: + return val.asString(); + default: + return null; + } + } + + private Object process(MapValue map) { + final Map result = new HashMap<>(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = process(entry.getValue()); + result.put(key, value); + } + return result; + } + + private Object process(ArrayValue array) { + final List result = new ArrayList<>(); + for (Value value : array) { + result.add(process(value)); + } + return result; + } +} \ No newline at end of file diff --git a/src/test/resources/modules/yaml/yamldecode.own b/src/test/resources/modules/yaml/yamldecode.own new file mode 100644 index 00000000..1cc25655 --- /dev/null +++ b/src/test/resources/modules/yaml/yamldecode.own @@ -0,0 +1,33 @@ +use "std" +use "yaml" +use "ounit" + +x = yamldecode(" + name: \"std\" + scope: \"both\" + desc: \"Contains common functions\" + desc_ru: \" \" + constants: [] + functions: + - + name: \"arrayCombine\" + args: \"keys, values\" + desc: \"creates map by combining two arrays\" + desc_ru: \" \" + - + name: \"typeof\" + args: \"value\" + desc: \"returns the type of value\" + desc_ru: \" \" + example: |- + print typeof(1) // 1 (NUMBER) + print typeof(\"text\") // 2 (STRING) + print typeof([]) // 3 (ARRAY) +") + +assertEquals("std", x.name) +assertEquals("both", x.scope) +assertEquals(0, length(x.constants)) +assertEquals(2, length(x.functions)) +assertEquals("arrayCombine", x.functions[0].name) +assertEquals(" ", x.functions[1].desc_ru) \ No newline at end of file diff --git a/src/test/resources/modules/yaml/yamlencode.own b/src/test/resources/modules/yaml/yamlencode.own new file mode 100644 index 00000000..a54e33d2 --- /dev/null +++ b/src/test/resources/modules/yaml/yamlencode.own @@ -0,0 +1,22 @@ +use "std" +use "yaml" +use "ounit" + +yml = yamlencode({ + "name": "Yaml Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } +}) +obj = yamldecode(yml) + +assertEquals("Yaml Example", obj.name) +assertEquals(1, obj.version) +assertEquals(4, length(obj.arrayData)) +assertEquals("value", obj.objectData.key) +assertEquals("1000", obj.objectData["10"]) \ No newline at end of file From dba0c46ff0e009ea96487274e1947239aef27b39 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 16 Sep 2016 17:34:33 +0300 Subject: [PATCH 185/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D1=80=D0=B5=D1=81=D1=82=D1=80=D1=83?= =?UTF-8?q?=D0=BA=D1=82=D1=83=D1=80=D0=B8=D0=B7=D1=83=D1=8E=D1=89=D0=B5?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=B8=D1=81=D0=B2=D0=B0=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index c6df60c6..6fe57249 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -185,7 +185,7 @@ private DestructuringAssignmentStatement destructuringAssignment() { } else { variables.add(null); } - consume(TokenType.COMMA); + match(TokenType.COMMA); } consume(TokenType.EQ); return new DestructuringAssignmentStatement(variables, expression()); From fa83c2072dddf4cdb594d7ce832e59373b5d5b43 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 18 Sep 2016 11:46:09 +0300 Subject: [PATCH 186/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20getFloat=20=D0=B8=20=D0=B2=D1=8B=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=B5=D0=BD=20=D0=B2=20ValueUtils,=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20int4ToVoid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Converters.java | 32 +++++++++++++------ .../com/annimon/ownlang/lib/ValueUtils.java | 10 ++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java index 9a91825a..fbef1d57 100644 --- a/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.lib; +import static com.annimon.ownlang.lib.ValueUtils.getFloatNumber; + /** * Wrapper functions and interfaces. */ @@ -41,6 +43,10 @@ public interface IntToVoidFunction { void apply(int i); } + public interface Int4ToVoidFunction { + void apply(int i1, int i2, int i3, int i4); + } + public interface FloatToVoidFunction { void apply(float f); } @@ -124,10 +130,21 @@ public static FunctionValue intToVoid(IntToVoidFunction f) { }); } + public static FunctionValue int4ToVoid(Int4ToVoidFunction f) { + return new FunctionValue(args -> { + Arguments.check(4, args.length); + f.apply(args[0].asInt(), + args[1].asInt(), + args[2].asInt(), + args[3].asInt()); + return NumberValue.ZERO; + }); + } + public static FunctionValue floatToVoid(FloatToVoidFunction f) { return new FunctionValue(args -> { Arguments.check(1, args.length); - f.apply(getNumber(args[0]).floatValue()); + f.apply(getFloatNumber(args[0])); return NumberValue.ZERO; }); } @@ -135,10 +152,10 @@ public static FunctionValue floatToVoid(FloatToVoidFunction f) { public static FunctionValue float4ToVoid(Float4ToVoidFunction f) { return new FunctionValue(args -> { Arguments.check(4, args.length); - f.apply(getNumber(args[0]).floatValue(), - getNumber(args[1]).floatValue(), - getNumber(args[2]).floatValue(), - getNumber(args[3]).floatValue()); + f.apply(getFloatNumber(args[0]), + getFloatNumber(args[1]), + getFloatNumber(args[2]), + getFloatNumber(args[3])); return NumberValue.ZERO; }); } @@ -175,9 +192,4 @@ public static FunctionValue stringToVoid(StringToVoidFunction f) { return NumberValue.ZERO; }); } - - public static Number getNumber(Value value) { - if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); - return value.asInt(); - } } diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 90feac5c..0e879fab 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -80,4 +80,14 @@ public static ArrayValue toValue(JSONArray json) { } return result; } + + public static Number getNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); + return value.asInt(); + } + + public static float getFloatNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw().floatValue(); + return (float) value.asNumber(); + } } From 879ffb7e525fd3ee03b0dfadab845de7b74dc707 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 18 Sep 2016 14:39:48 +0300 Subject: [PATCH 187/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D0=B0=20=D0=B2=20?= =?UTF-8?q?=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=20=D0=B1=D0=B0=D0=B9=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/ValueUtils.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 0e879fab..57aba130 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -90,4 +90,13 @@ public static float getFloatNumber(Value value) { if (value.type() == Types.NUMBER) return ((NumberValue) value).raw().floatValue(); return (float) value.asNumber(); } + + public static byte[] toByteArray(ArrayValue array) { + final int size = array.size(); + final byte[] result = new byte[size]; + for (int i = 0; i < size; i++) { + result[i] = (byte) array.get(i).asInt(); + } + return result; + } } From cd90045fd2d8cb7c5d4c7cc601a70a9ebce4c1e3 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 18 Sep 2016 14:40:14 +0300 Subject: [PATCH 188/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20base64?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/modules/base64/base64.java | 74 +++++++++++++++++++ src/test/resources/modules/base64/base64.own | 31 ++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/modules/base64/base64.java create mode 100644 src/test/resources/modules/base64/base64.own diff --git a/src/main/java/com/annimon/ownlang/modules/base64/base64.java b/src/main/java/com/annimon/ownlang/modules/base64/base64.java new file mode 100644 index 00000000..328ffa93 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/base64/base64.java @@ -0,0 +1,74 @@ +package com.annimon.ownlang.modules.base64; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import java.io.UnsupportedEncodingException; +import java.util.Base64; + +public class base64 implements Module { + + private static final int TYPE_URL_SAFE = 8; + + public static void initConstants() { + Variables.define("BASE64_URL_SAFE", NumberValue.of(TYPE_URL_SAFE)); + } + + @Override + public void init() { + initConstants(); + Functions.set("base64encode", this::base64encode); + Functions.set("base64decode", this::base64decode); + Functions.set("base64encodeToString", this::base64encodeToString); + } + + private Value base64encode(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + return ArrayValue.of(getEncoder(args).encode(getInputToEncode(args))); + } + + private Value base64encodeToString(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + return new StringValue(getEncoder(args).encodeToString(getInputToEncode(args))); + } + + private Value base64decode(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + final Base64.Decoder decoder = getDecoder(args); + final byte[] result; + if (args[0].type() == Types.ARRAY) { + result = decoder.decode(ValueUtils.toByteArray((ArrayValue) args[0])); + } else { + result = decoder.decode(args[0].asString()); + } + return ArrayValue.of(result); + } + + + private byte[] getInputToEncode(Value[] args) { + byte[] input; + if (args[0].type() == Types.ARRAY) { + input = ValueUtils.toByteArray((ArrayValue) args[0]); + } else { + try { + input = args[0].asString().getBytes("UTF-8"); + } catch (UnsupportedEncodingException ex) { + input = args[0].asString().getBytes(); + } + } + return input; + } + + private Base64.Encoder getEncoder(Value[] args) { + if (args.length == 2 && args[1].asInt() == TYPE_URL_SAFE) { + return Base64.getUrlEncoder(); + } + return Base64.getEncoder(); + } + + private Base64.Decoder getDecoder(Value[] args) { + if (args.length == 2 && args[1].asInt() == TYPE_URL_SAFE) { + return Base64.getUrlDecoder(); + } + return Base64.getDecoder(); + } +} diff --git a/src/test/resources/modules/base64/base64.own b/src/test/resources/modules/base64/base64.own new file mode 100644 index 00000000..841dfddd --- /dev/null +++ b/src/test/resources/modules/base64/base64.own @@ -0,0 +1,31 @@ +use "base64" +use "functional" +use "types" + +base64Example = [0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x20, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65] +base64Example_enc = [0x51, 0x6D, 0x46, 0x7A, 0x5A, 0x54, 0x59, 0x30, + 0x49, 0x45, 0x56, 0x34, 0x59, 0x57, 0x31, 0x77, + 0x62, 0x47, 0x55, 0x3D] +unicode = map([0xD0, 0xAE, 0xD0, 0xBD, 0xD0, 0xB8, 0xD0, 0xBA, 0xD0, 0xBE, 0xD0, 0xB4], ::byte) +unicode_enc = [0x30, 0x4B, 0x37, 0x51, 0x76, 0x64, 0x43, 0x34, 0x30, 0x4C, 0x72, 0x51, 0x76, 0x74, 0x43, 0x30] + +def testBase64Encode() { + assertEquals(base64Example_enc, base64encode(base64Example)) + assertEquals(base64Example_enc, base64encode("Base64 Example")) + assertEquals(unicode_enc, base64encode(unicode)) + assertEquals(unicode_enc, base64encode("Юникод")) +} + +def testBase64EncodeToString() { + assertEquals("QmFzZTY0IEV4YW1wbGU=", base64encodeToString(base64Example)) + assertEquals("QmFzZTY0IEV4YW1wbGU=", base64encodeToString("Base64 Example")) + assertEquals("0K7QvdC40LrQvtC0", base64encodeToString(unicode)) + assertEquals("0K7QvdC40LrQvtC0", base64encodeToString("Юникод")) +} + +def testBase64Decode() { + assertEquals(base64Example, base64decode("QmFzZTY0IEV4YW1wbGU=")) + assertEquals(base64Example, base64decode(base64Example_enc)) + assertEquals(unicode, base64decode("0K7QvdC40LrQvtC0")) + assertEquals(unicode, base64decode(unicode_enc)) +} \ No newline at end of file From d40c4d5284fcf0e09ce17bb637bae0364ffe6981 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 20 Sep 2016 18:58:03 +0300 Subject: [PATCH 189/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20parseInt,=20parseLong?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/modules/std/StringFunctions.java | 20 +++++++++++++++++++ .../com/annimon/ownlang/modules/std/std.java | 6 ++++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java new file mode 100644 index 00000000..b9b57945 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + +public final class StringFunctions { + + public static Value parseInt(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + final int radix = (args.length == 2) ? args[1].asInt() : 10; + return NumberValue.of(Integer.parseInt(args[0].asString(), radix)); + } + + public static Value parseLong(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + final int radix = (args.length == 2) ? args[1].asInt() : 10; + return NumberValue.of(Long.parseLong(args[0].asString(), radix)); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index a28149ca..ffce7228 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -30,7 +30,6 @@ public void init() { // String Functions.set("sprintf", new std_sprintf()); Functions.set("split", new std_split()); - Functions.set("join", new std_join()); Functions.set("indexOf", new std_indexof()); Functions.set("lastIndexOf", new std_lastindexof()); Functions.set("charAt", new std_charat()); @@ -42,9 +41,12 @@ public void init() { Functions.set("replace", new std_replace()); Functions.set("replaceAll", new std_replaceall()); Functions.set("replaceFirst", new std_replacefirst()); - + Functions.set("parseInt", StringFunctions::parseInt); + Functions.set("parseLong", StringFunctions::parseLong); + // Arrays and maps Functions.set("newarray", new std_newarray()); + Functions.set("join", new std_join()); Functions.set("sort", new std_sort()); Functions.set("arrayCombine", new std_arrayCombine()); Functions.set("arrayKeyExists", new std_arrayKeyExists()); From ac8cd4d325889f99d8cba0b432b674cbfc9ce288 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 20 Sep 2016 18:58:32 +0300 Subject: [PATCH 190/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20toHexString?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/modules/std/NumberFunctions.java | 21 +++++++++++++++++++ .../com/annimon/ownlang/modules/std/std.java | 3 +++ 2 files changed, 24 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java new file mode 100644 index 00000000..31d2cd6f --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java @@ -0,0 +1,21 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; + +public final class NumberFunctions { + + public static Value toHexString(Value... args) { + Arguments.check(1, args.length); + long value; + if (args[0].type() == Types.NUMBER) { + value = ((NumberValue) args[0]).asLong(); + } else { + value = (long) args[0].asNumber(); + } + return new StringValue(Long.toHexString(value)); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index ffce7228..a8618db4 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -27,6 +27,9 @@ public void init() { Functions.set("sync", new std_sync()); Functions.set("try", new std_try()); + // Numbers + Functions.set("toHexString", NumberFunctions::toHexString); + // String Functions.set("sprintf", new std_sprintf()); Functions.set("split", new std_split()); From 404c4abfddc7c18a9db198714ed3412ac80df253 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 20 Sep 2016 19:09:06 +0300 Subject: [PATCH 191/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/modules/std/parseInt.own | 17 +++++++++++++++++ src/test/resources/modules/std/parseLong.own | 18 ++++++++++++++++++ src/test/resources/modules/std/toHexString.own | 7 +++++++ 3 files changed, 42 insertions(+) create mode 100644 src/test/resources/modules/std/parseInt.own create mode 100644 src/test/resources/modules/std/parseLong.own create mode 100644 src/test/resources/modules/std/toHexString.own diff --git a/src/test/resources/modules/std/parseInt.own b/src/test/resources/modules/std/parseInt.own new file mode 100644 index 00000000..7aa4a299 --- /dev/null +++ b/src/test/resources/modules/std/parseInt.own @@ -0,0 +1,17 @@ +use "std" + +def testParseInt() { + assertEquals(141, parseInt("141")) +} + +def testParseIntBin() { + assertEquals(141, parseInt("10001101", 2)) +} + +def testParseIntOct() { + assertEquals(141, parseInt("215", 8)) +} + +def testParseIntHex() { + assertEquals(141, parseInt("8D", 16)) +} \ No newline at end of file diff --git a/src/test/resources/modules/std/parseLong.own b/src/test/resources/modules/std/parseLong.own new file mode 100644 index 00000000..77d426d5 --- /dev/null +++ b/src/test/resources/modules/std/parseLong.own @@ -0,0 +1,18 @@ +use "std" + +def testParseInt() { + assertEquals(12345654321, parseLong("12345654321")) +} + +def testParseIntBin() { + assertEquals(12345654321, parseLong("1011011111110110111011110000110001", 2)) +} + +def testParseIntOct() { + assertEquals(12345654321, parseLong("133766736061", 8)) +} + +def testParseIntHex() { + assertEquals(#2DFDBBC31, parseLong("2DFDBBC31", 16)) + assertEquals(12345654321, parseLong("2DFDBBC31", 16)) +} \ No newline at end of file diff --git a/src/test/resources/modules/std/toHexString.own b/src/test/resources/modules/std/toHexString.own new file mode 100644 index 00000000..f557fc17 --- /dev/null +++ b/src/test/resources/modules/std/toHexString.own @@ -0,0 +1,7 @@ +use "std" + +def testToHexString() { + assertEquals("8d", toHexString(141)) + assertEquals("cafebabe", toHexString(#CAFEBABE)) + assertEquals("2dfdbbc31", toHexString(12345654321)) +} From fd92fa3d3de10711e500915ec5fd2d2ebc7b193c Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 20 Sep 2016 20:54:31 +0300 Subject: [PATCH 192/448] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20?= =?UTF-8?q?files:=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=20copy,=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BA=D0=BE=D0=B2=D1=8B=D1=85=20=D0=B0=D1=80?= =?UTF-8?q?=D0=B3=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2,=20=D0=B2?= =?UTF-8?q?=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D0=B4=D0=B5=D1=81=D0=BA=D1=80?= =?UTF-8?q?=D0=B8=D0=BF=D1=82=D0=BE=D1=80=D0=BE=D0=B2=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/modules/files/files.java | 127 +++++++----------- 1 file changed, 49 insertions(+), 78 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/files/files.java b/src/main/java/com/annimon/ownlang/modules/files/files.java index a2e2a0e6..6efa05d7 100644 --- a/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Map; @@ -39,25 +40,26 @@ public void init() { Functions.set("fclose", new fclose()); // Operations - Functions.set("delete", new delete()); + Functions.set("copy", new copy()); + Functions.set("delete", fileToBoolean(File::delete)); Functions.set("listFiles", new listFiles()); - Functions.set("mkdir", new mkdir()); - Functions.set("mkdirs", new mkdirs()); + Functions.set("mkdir", fileToBoolean(File::mkdir)); + Functions.set("mkdirs", fileToBoolean(File::mkdirs)); Functions.set("rename", new rename()); // Permissions and statuses - Functions.set("canExecute", new canExecute()); - Functions.set("canRead", new canRead()); - Functions.set("canWrite", new canWrite()); - Functions.set("isDirectory", new isDirectory()); - Functions.set("isFile", new isFile()); - Functions.set("isHidden", new isHidden()); + Functions.set("canExecute", fileToBoolean(File::canExecute)); + Functions.set("canRead", fileToBoolean(File::canRead)); + Functions.set("canWrite", fileToBoolean(File::canWrite)); + Functions.set("isDirectory", fileToBoolean(File::isDirectory)); + Functions.set("isFile", fileToBoolean(File::isFile)); + Functions.set("isHidden", fileToBoolean(File::isHidden)); Functions.set("setExecutable", new setExecutable()); Functions.set("setReadable", new setReadable()); Functions.set("setReadOnly", new setReadOnly()); Functions.set("setWritable", new setWritable()); - Functions.set("exists", new exists()); + Functions.set("exists", fileToBoolean(File::exists)); Functions.set("fileSize", new fileSize()); Functions.set("getParent", new getParent()); Functions.set("lastModified", new lastModified()); @@ -176,81 +178,31 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { } } - private static class canExecute extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.canExecute()); - } - } - - private static class canRead extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.canRead()); - } - } - - private static class canWrite extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.canWrite()); - } - } - - private static class delete extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.delete()); - } - } - - private static class exists extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.exists()); - } - } + private static class copy implements Function { - private static class isDirectory extends FileFunction { @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.isDirectory()); - } - } - - private static class isFile extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.isFile()); - } - } - - private static class isHidden extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.isHidden()); + public Value execute(Value... args) { + Arguments.check(2, args.length); + try { + final FileInputStream is = new FileInputStream(fileFrom(args[0])); + final FileOutputStream os = new FileOutputStream(fileFrom(args[1])); + final FileChannel ic = is.getChannel(); + ic.transferTo(0, ic.size(), os.getChannel()); + is.close(); + os.close(); + return NumberValue.ONE; + } catch (IOException ioe) { + return NumberValue.MINUS_ONE; + } } } - private static class mkdir extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.mkdir()); - } - } + private static class rename implements Function { - private static class mkdirs extends FileFunction { @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - return NumberValue.fromBoolean(fileInfo.file.mkdirs()); - } - } - - private static class rename extends FileFunction { - @Override - protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - final File dest = files.get(args[1].asInt()).file; - return NumberValue.fromBoolean(fileInfo.file.renameTo(dest)); + public Value execute(Value... args) { + Arguments.check(2, args.length); + return NumberValue.fromBoolean( fileFrom(args[0]).renameTo(fileFrom(args[1])) ); } } @@ -598,6 +550,25 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { return NumberValue.ONE; } } + + private static File fileFrom(Value value) { + if (value.type() == Types.NUMBER) { + return files.get(value.asInt()).file; + } + return new File(value.asString()); + } + + private interface FileToBooleanFunction { + + boolean apply(File file); + } + + private static Function fileToBoolean(FileToBooleanFunction f) { + return args -> { + Arguments.check(1, args.length); + return NumberValue.fromBoolean(f.apply(fileFrom(args[0]))); + }; + } private static class FileInfo { File file; From 033a4deb29719a2a82ba33ae037ed114dfd7f189 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 21 Sep 2016 13:07:16 +0300 Subject: [PATCH 193/448] =?UTF-8?q?=D0=94=D0=B0=D0=BC=D0=BF=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BE=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8F=D1=85=20=D0=B2=20yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/utils/ModulesInfoCreator.java | 69 ++++++++++++++----- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index 4eef0f0f..420ec0b5 100644 --- a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -12,13 +12,17 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; public final class ModulesInfoCreator { @@ -48,11 +52,8 @@ public static void main(String[] args) throws InstantiationException, IllegalAcc moduleInfos.add(moduleInfo); } - final JSONArray modulesJson = new JSONArray(); - for (ModuleInfo moduleInfo : moduleInfos) { - modulesJson.put(moduleInfo.toJSON()); - } - System.out.println(modulesJson.toString(2)); + // printAsJson(moduleInfos); + printAsYaml(moduleInfos); System.out.println("Total modules: " + moduleInfos.size()); System.out.println("Total functions: " + moduleInfos.stream() @@ -65,6 +66,26 @@ public static void main(String[] args) throws InstantiationException, IllegalAcc ); } + private static void printAsJson(List moduleInfos) throws JSONException { + final JSONArray modulesJson = new JSONArray(); + for (ModuleInfo moduleInfo : moduleInfos) { + modulesJson.put(new JSONObject(moduleInfo.info())); + } + System.out.println(modulesJson.toString(2)); + } + + private static void printAsYaml(List moduleInfos) { + DumperOptions options = new DumperOptions(); + options.setIndent(2); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + final List> infos = new ArrayList<>(); + for (ModuleInfo moduleInfo : moduleInfos) { + infos.add(moduleInfo.info()); + } + System.out.println(new Yaml(options).dump(infos)); + } + private static List listValues(Class moduleClass) { return Arrays.stream(moduleClass.getDeclaredClasses()) .filter(clazz -> getAllInterfaces(clazz).stream().anyMatch(i -> i.equals(Value.class))) @@ -91,14 +112,27 @@ public ModuleInfo(String name) { types = new ArrayList<>(); } - public JSONArray constantsJSON() { - final JSONArray result = new JSONArray(); + public List> functions() { + return functions.stream().sorted() + .map(f -> { + final Map function = new LinkedHashMap<>(); + function.put("name", f); + function.put("args", ""); + function.put("desc", ""); + function.put("desc_ru", ""); + return function; + }) + .collect(Collectors.toList()); + } + + public List> constants() { + final List> result = new ArrayList<>(); constants.entrySet().stream() .sorted(Comparator.comparing(e -> e.getKey())) .forEach(entry -> { final Value value = entry.getValue(); - final JSONObject constant = new JSONObject(); + final Map constant = new LinkedHashMap<>(); constant.put("name", entry.getKey()); constant.put("type", value.type()); constant.put("typeName", Types.typeToString(value.type())); @@ -112,26 +146,27 @@ public JSONArray constantsJSON() { } else { constant.put("value", value.asString()); } - result.put(constant); + result.add(constant); }); return result; } - public JSONObject toJSON() { - final JSONObject json = new JSONObject(); - json.put("name", name); - json.put("functions", functions.stream().sorted().toArray()); - json.put("constants", constantsJSON()); + public Map info() { + final Map result = new LinkedHashMap<>(); + result.put("name", name); + result.put("scope", "both"); + result.put("constants", constants()); + result.put("functions", functions()); if (!types.isEmpty()) { - json.put("types", types.stream().sorted() + result.put("types", types.stream().sorted() .map(s -> { - final JSONObject type = new JSONObject(); + final Map type = new HashMap<>(); type.put("name", s); return type; }) .toArray()); } - return json; + return result; } } } From 409893200fd46ee90543a96dc22f55630c9c9103 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 22 Sep 2016 11:54:46 +0300 Subject: [PATCH 194/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20close=20=D0=B2=20jdbc=20ConnectionValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index ff9e9b67..9518e7c0 100644 --- a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -132,7 +132,6 @@ private void init() { set("close", new FunctionValue(this::close)); set("clearWarnings", voidFunction(connection::clearWarnings)); - set("close", voidFunction(connection::close)); set("commit", voidFunction(connection::commit)); set("rollback", voidFunction(connection::rollback)); From 0439b719b448f6b1bce545df29429bd1ed731d14 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 22 Sep 2016 12:29:28 +0300 Subject: [PATCH 195/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=20=D0=B2=20forms::newLabel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/modules/forms/Components.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/modules/forms/Components.java b/src/main/java/com/annimon/ownlang/modules/forms/Components.java index 1da492ab..5d1b6b5b 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/Components.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/Components.java @@ -40,7 +40,7 @@ public static Value newButton(Value... args) { public static Value newLabel(Value... args) { Arguments.checkRange(0, 2, args.length); String text = (args.length >= 1) ? args[0].asString() : ""; - int align = (args.length == 2) ? args[0].asInt() : SwingConstants.LEADING; + int align = (args.length == 2) ? args[1].asInt() : SwingConstants.LEADING; return new JLabelValue(new JLabel(text, align)); } From c21dfe911bdbea005439e63d5cddadbb8904001e Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 24 Sep 2016 15:45:16 +0300 Subject: [PATCH 196/448] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=BC=D0=B5=D1=80=20?= =?UTF-8?q?=D1=87=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/forms/samobot_chat.own | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 examples/forms/samobot_chat.own diff --git a/examples/forms/samobot_chat.own b/examples/forms/samobot_chat.own new file mode 100644 index 00000000..5f37748f --- /dev/null +++ b/examples/forms/samobot_chat.own @@ -0,0 +1,37 @@ +use "std" +use "http" +use "forms" + +chatHistory = newLabel("Чат с самоботом
") +messageField = newTextField() +sendButton = newButton("Отправить") + +messageField.onAction(::onSend) +sendButton.onClick(::onSend) +def onSend() { + text = messageField.getText() + if (length(text) == 0) return 0 + messageField.setText("") + chatHistory.setText(chatHistory.getText() + "
вы > " + text) + thread(::http, "http://annimon.com/json/bot.php", "POST", {"text": text}, def(answer) { + chatHistory.setText(chatHistory.getText() + "
бот > " + answer) + }) +} + +messagePanel = newPanel() +messagePanel.setLayout(boxLayout(messagePanel, BoxLayout.LINE_AXIS)) +messagePanel.add(messageField) +messagePanel.add(sendButton) + +mainPanel = newPanel(borderLayout(10, 10)) +mainPanel.setPreferredSize(400, 250) +mainPanel.add(chatHistory, BorderLayout.CENTER) +mainPanel.add(messagePanel, BorderLayout.SOUTH) + + +window = newWindow("Чат с самоботом") +window.setMinimumSize(200, 220) +window.setLocationByPlatform() +window.add(mainPanel) +window.pack() +window.setVisible() From e579cfcf9575956434842e378c9189577e57a2a9 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 24 Sep 2016 15:46:12 +0300 Subject: [PATCH 197/448] =?UTF-8?q?=D0=92=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20?= =?UTF-8?q?1.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index 8b8335b8..bf6aeb5b 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -21,7 +21,7 @@ */ public final class Main { - public static final String VERSION = "1.2.0"; + public static final String VERSION = "1.3.0"; private static String[] ownlangArgs = new String[0]; From 92a2159e68a73a6cbfd196be783d32e3a456b71a Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Sat, 24 Sep 2016 16:21:13 +0300 Subject: [PATCH 198/448] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3147b7e3..6ddcd41b 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ | Free | Pro | Desktop | | :--: | :-: | :-----: | -| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.2.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.2.0) +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.3.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.3.0) OwnLang - dynamic functional programming language inspired by Scala and Python. Available for PC, Android and Java ME devices. ## Key features -#### Function values +#### Higher-order functions Functions are values, so we can store them to variables for operating. From b8a22422723aa0d1665ee041edb26ca4ba30f33f Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 25 Nov 2016 12:16:01 +0200 Subject: [PATCH 199/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=20Sandbox=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BE=D0=BD=D0=BB=D0=B0=D0=B9=D0=BD=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D0=BF=D1=80=D0=B5=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/annimon/ownlang/Console.java | 17 +++++- src/main/java/com/annimon/ownlang/Main.java | 8 +++ .../annimon/ownlang/modules/files/files.java | 5 +- .../annimon/ownlang/parser/SourceLoader.java | 2 +- .../com/annimon/ownlang/utils/Sandbox.java | 56 +++++++++++++++++++ 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/utils/Sandbox.java diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index 6beab639..68769af7 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -1,13 +1,16 @@ package com.annimon.ownlang; import com.annimon.ownlang.lib.CallStack; - import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.PrintStream; import java.io.UnsupportedEncodingException; public class Console { + private static final String FILE_PREFIX = "tmp/"; + public static boolean filePrefixEnabled = false; + public static String newline() { return System.lineSeparator(); } @@ -39,7 +42,7 @@ public static void error(Throwable throwable) { public static void error(CharSequence value) { System.err.println(value); } - + public static void handleException(Thread thread, Throwable throwable) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try(final PrintStream ps = new PrintStream(baos)) { @@ -57,4 +60,14 @@ public static void handleException(Thread thread, Throwable throwable) { error(baos.toString()); } } + + public static File fileInstance(String path) { + final String filepath; + if (filePrefixEnabled) { + filepath = FILE_PREFIX.concat(path); + } else { + filepath = path; + } + return new File(filepath); + } } diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index bf6aeb5b..d5fc0f1b 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -11,6 +11,7 @@ import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.FunctionAdder; import com.annimon.ownlang.utils.Repl; +import com.annimon.ownlang.utils.Sandbox; import com.annimon.ownlang.utils.TimeMeasurement; import java.io.IOException; import java.util.List; @@ -112,6 +113,13 @@ public static void main(String[] args) throws IOException { i++; } break; + + case "--sandbox": + createOwnLangArgs(args, i + 1); + String[] newArgs = new String[ownlangArgs.length]; + System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length); + Sandbox.main(newArgs); + return; default: if (input == null) { diff --git a/src/main/java/com/annimon/ownlang/modules/files/files.java b/src/main/java/com/annimon/ownlang/modules/files/files.java index 6efa05d7..66373239 100644 --- a/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.files; +import com.annimon.ownlang.Console; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; @@ -120,7 +121,7 @@ private static class fopen implements Function { public Value execute(Value... args) { Arguments.checkAtLeast(1, args.length); - final File file = new File(args[0].asString()); + final File file = Console.fileInstance(args[0].asString()); try { if (args.length > 1) { return process(file, args[1].asString().toLowerCase()); @@ -555,7 +556,7 @@ private static File fileFrom(Value value) { if (value.type() == Types.NUMBER) { return files.get(value.asInt()).file; } - return new File(value.asString()); + return Console.fileInstance(value.asString()); } private interface FileToBooleanFunction { diff --git a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java index 600799e0..9545b529 100644 --- a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java +++ b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java @@ -15,7 +15,7 @@ public static String readSource(String name) throws IOException { return readAndCloseStream(is); } - private static String readAndCloseStream(InputStream is) throws IOException { + public static String readAndCloseStream(InputStream is) throws IOException { final ByteArrayOutputStream result = new ByteArrayOutputStream(); final int bufferSize = 1024; final byte[] buffer = new byte[bufferSize]; diff --git a/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/src/main/java/com/annimon/ownlang/utils/Sandbox.java new file mode 100644 index 00000000..b064efe9 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.utils; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.StoppedException; +import com.annimon.ownlang.lib.CallStack; +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.SourceLoader; +import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.visitors.FunctionAdder; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public final class Sandbox { + + public static void main(String[] args) throws IOException { + Console.filePrefixEnabled = true; + final String input = SourceLoader.readAndCloseStream(System.in); + dumpInputArguments(input, args); + + final List tokens = Lexer.tokenize(input); + final Parser parser = new Parser(tokens); + final Statement program = parser.parse(); + if (parser.getParseErrors().hasErrors()) { + System.out.print(parser.getParseErrors()); + return; + } + + program.accept(new FunctionAdder()); + + try { + program.execute(); + } catch (StoppedException ex) { + // skip + } catch (Exception ex) { + // ownlang call stack to stdout + System.out.format("%s in %s%n", ex.getMessage(), Thread.currentThread().getName()); + CallStack.getCalls().forEach(call -> System.out.format("\tat %s%n", call)); + // java stack trace to stderr + Console.handleException(Thread.currentThread(), ex); + } + } + + private static void dumpInputArguments(String source, String[] args) { + System.err.println(); + System.err.println(); + System.err.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + Arrays.stream(args).forEach(System.err::println); + System.err.println(); + System.err.println(source); + } +} From 78c601cb6d274ec5a524b50613b9365c21fd4709 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Nov 2016 15:44:17 +0200 Subject: [PATCH 200/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20peek=20=D0=BA=20StreamValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/functional/functional_foreach.java | 4 ++-- .../modules/functional/functional_stream.java | 3 ++- src/test/resources/modules/functional/stream.own | 13 +++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java index 4e0ceed8..239e3f38 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java @@ -20,14 +20,14 @@ public Value execute(Value... args) { for (Value element : array) { consumer.execute(element); } - return NumberValue.ZERO; + return array; } if (container.type() == Types.MAP) { final MapValue map = (MapValue) container; for (Map.Entry element : map) { consumer.execute(element.getKey(), element.getValue()); } - return NumberValue.ZERO; + return map; } throw new TypeException("Invalid first argument. Array or map expected"); } 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 82e590a6..544fe497 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 @@ -29,7 +29,7 @@ private static class StreamValue extends MapValue { private final ArrayValue container; public StreamValue(ArrayValue container) { - super(13); + super(15); this.container = container; init(); } @@ -41,6 +41,7 @@ private void init() { set("sortBy", wrapIntermediate(new functional_sortby())); set("takeWhile", wrapIntermediate(new functional_filter(true))); set("dropWhile", wrapIntermediate(new functional_dropwhile())); + set("peek", wrapIntermediate(new functional_foreach())); set("skip", this::skip); set("limit", this::limit); set("custom", this::custom); diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own index 19fa66d5..5d60078c 100644 --- a/src/test/resources/modules/functional/stream.own +++ b/src/test/resources/modules/functional/stream.own @@ -44,6 +44,19 @@ def testCustom() { assertEquals([5,6,4,2], stream(data).custom(::reverse).toArray()) } +def testPeek() { + data = [2,3,4,5,6,7] + expected = [2,4,6] + index = 0 + + actual = stream(data) + .filter(def(x) = x % 2 == 0) + .peek(def(x) = assertEquals(expected[index++], x)) + .toArray() + assertEquals(expected, actual) + assertEquals(length(expected), index) +} + def reverse(container) { size = length(container) result = newarray(size) From 73485edfb7d7a5d45237499553825418c9039533 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Nov 2016 15:50:39 +0200 Subject: [PATCH 201/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20sorted=20=D0=BA=20StreamValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/functional/functional_stream.java | 29 +++++++++++++++++-- .../resources/modules/functional/stream.own | 7 +++++ 2 files changed, 34 insertions(+), 2 deletions(-) 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 544fe497..f600bb9d 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 @@ -1,7 +1,9 @@ package com.annimon.ownlang.modules.functional; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; +import java.util.Arrays; public final class functional_stream implements Function { @@ -12,7 +14,7 @@ public Value execute(Value... args) { if (args.length > 1) { return new StreamValue(new ArrayValue(args)); } - + final Value value = args[0]; switch (value.type()) { case Types.MAP: @@ -29,7 +31,7 @@ private static class StreamValue extends MapValue { private final ArrayValue container; public StreamValue(ArrayValue container) { - super(15); + super(16); this.container = container; init(); } @@ -38,6 +40,7 @@ private void init() { set("filter", wrapIntermediate(new functional_filter(false))); set("map", wrapIntermediate(new functional_map())); set("flatMap", wrapIntermediate(new functional_flatmap())); + set("sorted", this::sorted); set("sortBy", wrapIntermediate(new functional_sortby())); set("takeWhile", wrapIntermediate(new functional_filter(true))); set("dropWhile", wrapIntermediate(new functional_dropwhile())); @@ -84,6 +87,28 @@ private Value limit(Value... args) { return new StreamValue(new ArrayValue(result)); } + private Value sorted(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + final Value[] elements = container.getCopyElements(); + + switch (args.length) { + case 0: + 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(); + Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); + break; + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + + return new StreamValue(new ArrayValue(elements)); + } + private Value custom(Value... args) { Arguments.check(1, args.length); if (args[0].type() != Types.FUNCTION) { diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own index 5d60078c..c3c5ae37 100644 --- a/src/test/resources/modules/functional/stream.own +++ b/src/test/resources/modules/functional/stream.own @@ -1,5 +1,6 @@ use "std" use "functional" +use "math" def testStream() { data = [1,2,3,4,5,6,7] @@ -57,6 +58,12 @@ def testPeek() { assertEquals(length(expected), index) } +def testSorted() { + data = [-2,4,6,-5,6,3,-8] + assertEquals([-8,-5,-2,3,4,6,6], stream(data).sorted().toArray()) + assertEquals([-2,3,4,-5,6,6,-8], stream(data).sorted(def(a,b) = abs(a) - abs(b)).toArray()) +} + def reverse(container) { size = length(container) result = newarray(size) From 4d7f59b7e6631ffbb05dbe71e8dad5bf317a4da6 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Nov 2016 17:33:17 +0200 Subject: [PATCH 202/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20std::arraySplice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/modules/std/std.java | 1 + .../ownlang/modules/std/std_arraySplice.java | 45 ++++++++ .../resources/modules/std/arraySplice.own | 107 ++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java create mode 100644 src/test/resources/modules/std/arraySplice.own diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index a8618db4..470be66f 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -55,6 +55,7 @@ public void init() { Functions.set("arrayKeyExists", new std_arrayKeyExists()); Functions.set("arrayKeys", new std_arrayKeys()); Functions.set("arrayValues", new std_arrayValues()); + Functions.set("arraySplice", new std_arraySplice()); Functions.set("range", new std_range()); } } diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java b/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java new file mode 100644 index 00000000..f16f04ff --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java @@ -0,0 +1,45 @@ +package com.annimon.ownlang.modules.std; + +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.Types; +import com.annimon.ownlang.lib.Value; + +public final class std_arraySplice implements Function { + + @Override + public Value execute(Value... args) { + Arguments.checkRange(2, 4, args.length); + if (args[0].type() != Types.ARRAY) { + throw new TypeException("Array expected at first argument"); + } + final Value[] input = ((ArrayValue) args[0]).getCopyElements(); + final int size = input.length; + + int start = args[1].asInt(); + if (start < 0) start = size - Math.abs(start); + start = Math.max(0, Math.min(size, start)); + + final int deleteCount = (args.length >= 3) + ? Math.max(0, Math.min(size - start, args[2].asInt())) // [0..size - start) + : (size - start); + + final Value[] additions; + if (args.length != 4) { + additions = new Value[0]; + } else if (args[3].type() == Types.ARRAY) { + additions = ((ArrayValue) args[3]).getCopyElements(); + } else { + throw new TypeException("Array expected at fourth argument"); + } + + Value[] result = new Value[start + (size - start - deleteCount) + additions.length]; + System.arraycopy(input, 0, result, 0, start); // [0, start) + System.arraycopy(additions, 0, result, start, additions.length); // insert new + System.arraycopy(input, start + deleteCount, result, start + additions.length, size - start - deleteCount); + return new ArrayValue(result); + } + +} diff --git a/src/test/resources/modules/std/arraySplice.own b/src/test/resources/modules/std/arraySplice.own new file mode 100644 index 00000000..0ec3d500 --- /dev/null +++ b/src/test/resources/modules/std/arraySplice.own @@ -0,0 +1,107 @@ +use "std" + +def testArraySpliceFromStart() { + arr = [1,2,3,4,5] + assertEquals([1, 2], arraySplice(arr, 2)) +} + +def testArraySpliceFromStartAndInsert() { + arr = [1,2,3,4,5] + assertEquals([1, 2, 6,7,8], arraySplice(arr, 2, 3, [6,7,8])) +} + +def testArraySpliceRemoveOneElementByIndex() { + arr = [1,2,3,4,5] + assertEquals([1, 2, 4, 5], arraySplice(arr, 2, 1)) +} + +def testArraySpliceRemoveOneElementByIndexAndInsert() { + arr = [1,2,3,4,5] + assertEquals([1, 2, 6,7,8, 4, 5], arraySplice(arr, 2, 1, [6,7,8])) +} + +def testArraySpliceReplaceFirstElement() { + arr = [1,2,3,4,5] + assertEquals([6,7,8, 2, 3, 4, 5], arraySplice(arr, 0, 1, [6,7,8])) +} + +def testArraySpliceInsertAtStart() { + arr = [1,2,3,4,5] + assertEquals([6,7,8, 1, 2, 3, 4, 5], arraySplice(arr, 0, 0, [6,7,8])) +} + +def testArraySpliceReplaceAll() { + arr = [1,2,3,4,5] + assertEquals([6,7,8], arraySplice(arr, 0, 5, [6,7,8])) +} + +def testArraySpliceRemoveMoreThanSizeAtTheEnd() { + arr = [1,2,3,4,5] + assertEquals([1, 2, 3], arraySplice(arr, 3, 4)) +} + +def testArraySpliceRemoveMoreThanSizeAtTheEndAndInsert() { + arr = [1,2,3,4,5] + assertEquals([1, 2, 3, 6,7,8], arraySplice(arr, 3, 4, [6,7,8])) +} + +def testArraySpliceRemoveMoreThanSize() { + arr = [1,2,3,4,5] + assertEquals([], arraySplice(arr, 0, 15)) +} + +def testArraySpliceRemoveMoreThanSizeAndInsert() { + arr = [1,2,3,4,5] + assertEquals([1,2], arraySplice(arr, 0, 15, [1,2])) +} + +def testArraySpliceStartMoreThanSize() { + arr = [1,2,3,4,5] + assertEquals([1,2,3,4,5], arraySplice(arr, 15)) +} + +def testArraySpliceStartAndRemoveMoreThanSize() { + arr = [1,2,3,4,5] + assertEquals([1,2,3,4,5], arraySplice(arr, 15, 15)) +} + +def testArraySpliceStartAndRemoveMoreThanSizeAndInsert() { + arr = [1,2,3,4,5] + assertEquals([1,2,3,4,5,1], arraySplice(arr, 15, 15, [1])) +} + +def testArraySpliceFromJSExamples1() { + arr = ["angel", "clown", "mandarin", "surgeon"] + res = ["angel", "clown", "drum", "mandarin", "surgeon"] + assertEquals(res, arraySplice(arr, 2, 0, ["drum"])) +} + +def testArraySpliceFromJSExamples2() { + arr = ["angel", "clown", "drum", "mandarin", "surgeon"] + res = ["angel", "clown", "drum", "surgeon"] + assertEquals(res, arraySplice(arr, 3, 1)) +} + +def testArraySpliceFromJSExamples3() { + arr = ["angel", "clown", "drum", "surgeon"] + res = ["angel", "clown", "trumpet", "surgeon"] + assertEquals(res, arraySplice(arr, 2, 1, ["trumpet"])) +} + +def testArraySpliceFromJSExamples4() { + arr = ["angel", "clown", "trumpet", "surgeon"] + res = ["parrot", "anemone", "blue", "trumpet", "surgeon"] + assertEquals(res, arraySplice(arr, 0, 2, ["parrot", "anemone", "blue"])) +} + +def testArraySpliceFromJSExamples5() { + arr = ["parrot", "anemone", "blue", "trumpet", "surgeon"] + res = ["parrot", "anemone", "surgeon"] + assertEquals(res, arraySplice(arr, length(arr) - 3, 2)) +} + +def testArraySpliceFromJSExamples6() { + arr = ["angel", "clown", "mandarin", "surgeon"] + res = ["angel", "clown", "surgeon"] + assertEquals(res, arraySplice(arr, -2, 1)) +} \ No newline at end of file From 719ed8694b756ef51f5e5baef063760067517e87 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Nov 2016 19:57:27 +0200 Subject: [PATCH 203/448] =?UTF-8?q?Gradle=20task=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B8=20Sandbox-=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/build.gradle b/build.gradle index 33a77193..1f2b23a7 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,21 @@ task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) { outjars "$rootProject.projectDir/store/OwnLang.jar" } +task sandbox(dependsOn: proguard, type: Jar) { + from zipTree("$rootProject.projectDir/store/OwnLang.jar") + libsDirName = "$rootProject.projectDir/store" + appendix = "Sandbox" + + exclude "**/modules/canvas/**", "**/modules/canvasfx/**", "**/modules/forms/**", + "**/modules/java/**", "**/modules/jdbc/**", "**/modules/robot/**", + "**/modules/socket/**", "io/**", + "**/modules/aimp/**", "aimpremote/**" + + manifest { + attributes 'Main-Class': project.mainClass + } +} + dependencies { compile ('io.socket:socket.io-client:0.7.0') { exclude group: 'org.json', module: 'json' From 59a86c7d54c2620bc19f3e742e46f8008506cbb6 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 7 Dec 2016 12:19:17 +0200 Subject: [PATCH 204/448] =?UTF-8?q?Gradle=20wrapper=20=D0=BE=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D1=91=D0=BD=20=D0=B4=D0=BE=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=203.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/wrapper/gradle-wrapper.jar | Bin 53638 -> 52818 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 51 +++++++++++++---------- gradlew.bat | 12 ++---- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5ccda13e9cb94678ba179b32452cf3d60dc36353..deedc7fa5e6310eac3148a7dd0b1f069b07364cb 100644 GIT binary patch delta 11907 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 akUwbu`(^eoE5vWH1^Q}ZM`UXKE&PAL%^fZP delta 12768 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 OAxj%Og2AWXgZ~eVUY6Sc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 34639b26..7b066a5c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Feb 25 22:28:48 EET 2016 +#Wed Dec 07 12:17:35 EET 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip diff --git a/gradlew b/gradlew index 9d82f789..9aa616c2 100644 --- a/gradlew +++ b/gradlew @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -157,4 +161,9 @@ function splitJvmOpts() { eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then + cd "$(dirname "$0")" +fi + exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat index 5f192121..f9553162 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -49,7 +49,6 @@ goto fail @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line From 7b8c6ad40684fcac116e19a11af9693680ee61d2 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 7 Dec 2016 15:04:59 +0200 Subject: [PATCH 205/448] =?UTF-8?q?=D0=93=D0=B5=D0=BD=D0=B5=D1=80=D0=B8?= =?UTF-8?q?=D1=80=D1=83=D0=B5=D0=BC=D0=B0=D1=8F=20=D0=B4=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B8=20=D0=B2=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- build.gradle | 29 +++++++++++++++++---- src/main/java/com/annimon/ownlang/Main.java | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index d060063b..afb32114 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /dist/ /store/ /optimizations/ -/nbproject/private/ \ No newline at end of file +/nbproject/private/ +/src/main/generatedJava/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 1f2b23a7..4148ea5c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,9 @@ if (!hasProperty('mainClass')) { ext.mainClass = 'com.annimon.ownlang.Main' } +ext.generatedJavaDir = "${rootProject.projectDir}/src/main/generatedJava" +sourceSets.main.java.srcDirs += project.generatedJavaDir + repositories { jcenter() } @@ -20,6 +23,21 @@ buildscript { } } +task generateJavaSources() { + doLast { + def source = """ + package com.annimon.ownlang; + class Gen { + public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; + } + """ + def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") + genFile.getParentFile().mkdirs() + genFile.write(source) + } +} +compileJava.dependsOn(generateJavaSources) + task run(dependsOn: classes, type: JavaExec) { main = project.mainClass classpath = sourceSets.main.runtimeClasspath @@ -37,13 +55,14 @@ task runOptimizing(dependsOn: classes, type: JavaExec) { task dist(dependsOn: classes, type: Jar) { from files(sourceSets.main.output.classesDir) + from files(sourceSets.main.output.resourcesDir) from {configurations.compile.collect {zipTree(it)}} - from files(sourceSets.main.resources) - libsDirName = "$rootProject.projectDir/dist" + destinationDir file("$rootProject.projectDir/dist") - manifest { - attributes 'Main-Class': project.mainClass - } + manifest.attributes( + 'Main-Class': project.mainClass, + 'Build-Date': new Date().format('YYMMdd') + ) } task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) { diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index d5fc0f1b..9edd6b0d 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -22,7 +22,7 @@ */ public final class Main { - public static final String VERSION = "1.3.0"; + public static final String VERSION = "1.3.0_" + Gen.BUILD_DATE; private static String[] ownlangArgs = new String[0]; From d902fe550aebb7ea905c6bacc562c920257b9e30 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 7 Dec 2016 19:32:32 +0200 Subject: [PATCH 206/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20downloade?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +- .../modules/downloader/downloader.java | 98 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/downloader/downloader.java diff --git a/build.gradle b/build.gradle index 4148ea5c..0fed5469 100644 --- a/build.gradle +++ b/build.gradle @@ -79,7 +79,8 @@ task sandbox(dependsOn: proguard, type: Jar) { exclude "**/modules/canvas/**", "**/modules/canvasfx/**", "**/modules/forms/**", "**/modules/java/**", "**/modules/jdbc/**", "**/modules/robot/**", "**/modules/socket/**", "io/**", - "**/modules/aimp/**", "aimpremote/**" + "**/modules/aimp/**", "aimpremote/**", + "**/modules/downloader/**" manifest { attributes 'Main-Class': project.mainClass diff --git a/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java b/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java new file mode 100644 index 00000000..62910a9b --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java @@ -0,0 +1,98 @@ +package com.annimon.ownlang.modules.downloader; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.modules.Module; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +public final class downloader implements Module { + + @Override + public void init() { + Functions.set("getContentLength", this::getContentLength); + Functions.set("downloader", this::downloader); + } + + private Value getContentLength(Value... args) { + Arguments.check(1, args.length); + return NumberValue.of(getContentLength(args[0].asString())); + } + + private Value downloader(Value... args) { + Arguments.checkRange(2, 4, args.length); + final String downloadUrl = args[0].asString(); + final String filePath = args[1].asString(); + final Function progressCallback; + final int contentLength; + if ( (args.length >= 3) && (args[2].type() == Types.FUNCTION) ) { + progressCallback = ((FunctionValue) args[2]).getValue(); + // For showing progress we need to get content length + contentLength = getContentLength(downloadUrl); + } else { + progressCallback = null; + contentLength = -1; + } + final int bufferSize = (args.length == 4) ? Math.max(1024, args[3].asInt()) : 16384; + + final NumberValue contentLengthBoxed = NumberValue.of(contentLength); + final boolean calculateProgressEnabled = contentLength > 0 && progressCallback != null; + if (calculateProgressEnabled) { + progressCallback.execute( + NumberValue.ZERO /*%*/, + NumberValue.ZERO /*bytes downloaded*/, + contentLengthBoxed /*file size*/); + } + + try (InputStream is = new URL(downloadUrl).openStream(); + OutputStream os = new FileOutputStream(Console.fileInstance(filePath))) { + int downloaded = 0; + final byte[] buffer = new byte[bufferSize]; + int readed; + while ((readed = is.read(buffer, 0, bufferSize)) != -1) { + os.write(buffer, 0, readed); + downloaded += readed; + if (calculateProgressEnabled) { + final int percent = downloaded * 100 / contentLength; + progressCallback.execute( + NumberValue.of(percent), + NumberValue.of(downloaded), + contentLengthBoxed); + } + } + } catch (IOException ioe) { + return NumberValue.ZERO; + } finally { + if (progressCallback != null) { + progressCallback.execute(NumberValue.of(100), contentLengthBoxed, contentLengthBoxed); + } + } + return NumberValue.ONE; + } + + private static int getContentLength(String url) { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("HEAD"); + connection.connect(); + return connection.getContentLength(); + } catch (IOException ioe) { + return -1; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } +} From 926ae528151de955b53c8e7a4d49f521a68bad83 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 8 Dec 2016 13:46:00 +0200 Subject: [PATCH 207/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=20downloader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/annimon/ownlang/modules/downloader/downloader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java b/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java index 62910a9b..c1a82ec6 100644 --- a/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java +++ b/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java @@ -63,7 +63,7 @@ private Value downloader(Value... args) { os.write(buffer, 0, readed); downloaded += readed; if (calculateProgressEnabled) { - final int percent = downloaded * 100 / contentLength; + final int percent = (int) (downloaded / ((double) contentLength) * 100.0); progressCallback.execute( NumberValue.of(percent), NumberValue.of(downloaded), From db939ec9ee44638c58ba25ab82481d0a38c8d10c Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 10 Dec 2016 21:17:27 +0200 Subject: [PATCH 208/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B9=20=D0=B0=D0=BD=D0=B3?= =?UTF-8?q?=D0=BB=D0=B8=D0=B9=D1=81=D0=BA=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/modules/downloader/downloader.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java b/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java index c1a82ec6..60110322 100644 --- a/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java +++ b/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java @@ -58,10 +58,10 @@ private Value downloader(Value... args) { OutputStream os = new FileOutputStream(Console.fileInstance(filePath))) { int downloaded = 0; final byte[] buffer = new byte[bufferSize]; - int readed; - while ((readed = is.read(buffer, 0, bufferSize)) != -1) { - os.write(buffer, 0, readed); - downloaded += readed; + int read; + while ((read = is.read(buffer, 0, bufferSize)) != -1) { + os.write(buffer, 0, read); + downloaded += read; if (calculateProgressEnabled) { final int percent = (int) (downloaded / ((double) contentLength) * 100.0); progressCallback.execute( From c7f87f75f6068f19ec7ea797d45575ef38e386b8 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 10 Nov 2017 11:13:46 +0200 Subject: [PATCH 209/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/canvas/animate_line.own | 2 +- examples/canvas/fx_koch_snowflake.own | 70 +++++++++++++++++++ .../com/annimon/ownlang/lib/ValueUtils.java | 10 +++ .../ownlang/modules/forms/ComponentValue.java | 16 ++--- .../ownlang/modules/forms/JButtonValue.java | 9 +-- .../modules/forms/JTextFieldValue.java | 9 +-- .../functional/functional_flatmap.java | 8 +-- .../functional/functional_foreach.java | 14 ++-- .../modules/functional/functional_sortby.java | 8 +-- .../annimon/ownlang/modules/robot/robot.java | 59 ++++++++-------- .../annimon/ownlang/modules/std/std_sort.java | 7 +- .../ownlang/parser/LexerBenchmarkTest.java | 4 +- .../ownlang/parser/ParserBenchmarkTest.java | 4 +- 13 files changed, 140 insertions(+), 80 deletions(-) create mode 100644 examples/canvas/fx_koch_snowflake.own diff --git a/examples/canvas/animate_line.own b/examples/canvas/animate_line.own index d5edc193..758d5b61 100644 --- a/examples/canvas/animate_line.own +++ b/examples/canvas/animate_line.own @@ -53,4 +53,4 @@ def sethsbcolor(h1) { case 4: color(f*255, 0, 255) case 5: color(255, 0, 255-f*255) } -} \ No newline at end of file +} diff --git a/examples/canvas/fx_koch_snowflake.own b/examples/canvas/fx_koch_snowflake.own new file mode 100644 index 00000000..7e96b6b3 --- /dev/null +++ b/examples/canvas/fx_koch_snowflake.own @@ -0,0 +1,70 @@ +use "canvasfx" +use "math" +use "functional" + +// https://github.com/SeTSeR/KochSnowflake + +width = 675 height = 500 +context = window("Koch Snowflake", width, height) + +GO = 0 TURN = 1 + +def Fractal(startStep = 0) { + result = {} + result.fract = startStep ? [[GO, startStep]] : [] + result.next = def(fract) { + fractal = Fractal() + def translate(input) = input[0] == GO ? [input[0], input[1] / 3] : input + fractlist = map(fract, ::translate) + fractal.fract = fractlist + fractal.fract ::= [TURN, -PI / 3] + fractal.fract <<= fractlist + fractal.fract ::= [TURN, 2*PI / 3] + fractal.fract <<= fractlist + fractal.fract ::= [TURN, -PI / 3] + fractal.fract <<= fractlist + return fractal + } + result.toDraw = def(fract) { + res = Fractal() + res.fract = fract + res.fract ::= [TURN, 2*PI / 3] + res.fract <<= fract + res.fract ::= [TURN, 2*PI / 3] + res.fract <<= fract + return res + } + return result +} + +def draw(fractal) { + x = 200 + y = height - 100 / sqrt(3) + angle = -PI / 2 + context.setFill(Color.BLACK) + context.fillRect(0, 0, width, height) + context.setStroke(Color.GREEN) + context.beginPath() + context.moveTo(x, y) + for action : fractal.fract { + match action[0] { + case GO: { + x += action[1] * cos(angle) + y += action[1] * sin(angle) + context.lineTo(x, y) + } + case TURN: angle += action[1] + } + } + context.closePath() + context.stroke() +} + +fractal = Fractal(400.0) +draw(fractal.toDraw(fractal.fract)) +addEventHandler(Events.KEY_PRESSED, def(e) { + if (e.code == KeyCode.SPACE) { + fractal = fractal.next(fractal.fract) + draw(fractal.toDraw(fractal.fract)) + } +}) \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 57aba130..c11bbfbd 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.TypeException; import java.util.Iterator; import java.util.Map; import org.json.JSONArray; @@ -99,4 +100,13 @@ public static byte[] toByteArray(ArrayValue array) { } return result; } + + public static Function consumeFunction(Value value, int argumentNumber) throws TypeException { + final int type = value.type(); + if (type != Types.FUNCTION) { + throw new TypeException("Function expected at argument " + (argumentNumber + 1) + + ", but found " + Types.typeToString(type)); + } + return ((FunctionValue) value).getValue(); + } } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java index 5ab56f82..15a0de2d 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java @@ -1,16 +1,14 @@ package com.annimon.ownlang.modules.forms; -import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; -import static com.annimon.ownlang.lib.Converters.*; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.MapValue; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; @@ -18,6 +16,12 @@ import java.awt.event.KeyListener; import java.util.function.Consumer; import java.util.function.Supplier; +import static com.annimon.ownlang.lib.Converters.booleanOptToVoid; +import static com.annimon.ownlang.lib.Converters.stringToVoid; +import static com.annimon.ownlang.lib.Converters.voidToBoolean; +import static com.annimon.ownlang.lib.Converters.voidToInt; +import static com.annimon.ownlang.lib.Converters.voidToString; +import static com.annimon.ownlang.lib.Converters.voidToVoid; public abstract class ComponentValue extends MapValue { @@ -75,11 +79,7 @@ private void init() { private Value addKeyListener(Value... args) { Arguments.check(1, args.length); - final int type = args[0].type(); - if (type != Types.FUNCTION) { - throw new TypeException("Function expected, but found " + Types.typeToString(type)); - } - final Function action = ((FunctionValue) args[0]).getValue(); + final Function action = ValueUtils.consumeFunction(args[0], 0); component.addKeyListener(new KeyListener() { @Override diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java index 5afce2fa..87766fb5 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java @@ -1,12 +1,11 @@ package com.annimon.ownlang.modules.forms; -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.NumberValue; -import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import javax.swing.JButton; public class JButtonValue extends JComponentValue { @@ -26,11 +25,7 @@ private void init() { private Value addActionListener(Value... args) { Arguments.check(1, args.length); - final int type = args[0].type(); - if (type != Types.FUNCTION) { - throw new TypeException("Function expected, but found " + Types.typeToString(type)); - } - final Function action = ((FunctionValue) args[0]).getValue(); + final Function action = ValueUtils.consumeFunction(args[0], 0); button.addActionListener(e -> action.execute()); return NumberValue.ZERO; } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java index d6fed172..841b92a7 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java @@ -1,14 +1,13 @@ package com.annimon.ownlang.modules.forms; -import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; import static com.annimon.ownlang.lib.Converters.*; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import javax.swing.JTextField; +import static com.annimon.ownlang.lib.ValueUtils.consumeFunction; public class JTextFieldValue extends JComponentValue { @@ -41,11 +40,7 @@ private void init() { private Value addActionListener(Value... args) { Arguments.check(1, args.length); - final int type = args[0].type(); - if (type != Types.FUNCTION) { - throw new TypeException("Function expected, but found " + Types.typeToString(type)); - } - final Function action = ((FunctionValue) args[0]).getValue(); + Function action = consumeFunction(args[0], 1); textField.addActionListener(e -> action.execute()); return NumberValue.ZERO; } diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java index 5130810e..333e1333 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java @@ -4,9 +4,9 @@ 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.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.util.ArrayList; import java.util.List; @@ -18,11 +18,7 @@ public Value execute(Value... args) { 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 Function mapper = ((FunctionValue) args[1]).getValue(); + final Function mapper = ValueUtils.consumeFunction(args[1], 1); return flatMapArray((ArrayValue) args[0], mapper); } diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java index 239e3f38..fc5117e1 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.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.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_foreach implements Function { @@ -9,12 +15,8 @@ public final class functional_foreach 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 arg"); - } final Value container = args[0]; - final Function consumer = ((FunctionValue) args[1]).getValue(); + final Function consumer = ValueUtils.consumeFunction(args[1], 1); if (container.type() == Types.ARRAY) { final ArrayValue array = (ArrayValue) container; for (Value element : array) { 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 926047d0..5e87598f 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 @@ -4,9 +4,9 @@ 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.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.util.Arrays; public final class functional_sortby implements Function { @@ -17,12 +17,8 @@ public Value execute(Value... args) { 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[] elements = ((ArrayValue) args[0]).getCopyElements(); - final Function function = ((FunctionValue) args[1]).getValue(); + final Function function = ValueUtils.consumeFunction(args[1], 1); Arrays.sort(elements, (o1, o2) -> function.execute(o1).compareTo(function.execute(o2))); return new ArrayValue(elements); } diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot.java b/src/main/java/com/annimon/ownlang/modules/robot/robot.java index 090838e5..0d3fa8e9 100644 --- a/src/main/java/com/annimon/ownlang/modules/robot/robot.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot.java @@ -42,41 +42,44 @@ public static void initConstants() { @Override public void init() { initConstants(); - initialize(); - - Functions.set("click", convertFunction(robot::click)); - Functions.set("delay", convertFunction(awtRobot::delay)); - Functions.set("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); - Functions.set("keyPress", convertFunction(awtRobot::keyPress)); - Functions.set("keyRelease", convertFunction(awtRobot::keyRelease)); - Functions.set("mousePress", convertFunction(awtRobot::mousePress)); - Functions.set("mouseRelease", convertFunction(awtRobot::mouseRelease)); - Functions.set("mouseWheel", convertFunction(awtRobot::mouseWheel)); - Functions.set("mouseMove", (args) -> { - Arguments.check(2, args.length); - try { - awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); - } catch (IllegalArgumentException iae) { } - return NumberValue.ZERO; - }); - Functions.set("typeText", (args) -> { - Arguments.check(1, args.length); - try { - typeText(args[0].asString()); - } catch (IllegalArgumentException iae) { } - return NumberValue.ZERO; - }); - Functions.set("toClipboard", new robot_toclipboard()); - Functions.set("fromClipboard", new robot_fromclipboard()); + boolean isRobotInitialized = initialize(); + if (isRobotInitialized) { + Functions.set("click", convertFunction(robot::click)); + Functions.set("delay", convertFunction(awtRobot::delay)); + Functions.set("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); + Functions.set("keyPress", convertFunction(awtRobot::keyPress)); + Functions.set("keyRelease", convertFunction(awtRobot::keyRelease)); + Functions.set("mousePress", convertFunction(awtRobot::mousePress)); + Functions.set("mouseRelease", convertFunction(awtRobot::mouseRelease)); + Functions.set("mouseWheel", convertFunction(awtRobot::mouseWheel)); + Functions.set("mouseMove", (args) -> { + Arguments.check(2, args.length); + try { + awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + }); + Functions.set("typeText", (args) -> { + Arguments.check(1, args.length); + try { + typeText(args[0].asString()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + }); + Functions.set("toClipboard", new robot_toclipboard()); + Functions.set("fromClipboard", new robot_fromclipboard()); + } Functions.set("execProcess", new robot_exec(robot_exec.Mode.EXEC)); Functions.set("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); } - private static void initialize() { + private static boolean initialize() { try { awtRobot = new Robot(); + return true; } catch (AWTException awte) { - throw new RuntimeException("Unable to create robot instance", awte); + //throw new RuntimeException("Unable to create robot instance", awte); + return false; } } diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_sort.java b/src/main/java/com/annimon/ownlang/modules/std/std_sort.java index 4dc39b86..22608b4d 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_sort.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_sort.java @@ -5,9 +5,9 @@ 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.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; import java.util.Arrays; public final class std_sort implements Function { @@ -25,10 +25,7 @@ public Value execute(Value... args) { Arrays.sort(elements); break; case 2: - if (args[1].type() != Types.FUNCTION) { - throw new TypeException("Function expected in second argument"); - } - final Function comparator = ((FunctionValue) args[1]).getValue(); + final Function comparator = ValueUtils.consumeFunction(args[1], 1); Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); break; default: diff --git a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java index 934b0fab..13930e06 100644 --- a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java +++ b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; -import org.junit.Ignore; import org.junit.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; @@ -13,7 +12,6 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Benchmark) -@Ignore public class LexerBenchmarkTest { @Param({"1000"}) @@ -33,7 +31,7 @@ public void lexerBenchmark() { } } - @Test + //@Test public void executeBenchmark() throws RunnerException { Options opt = new OptionsBuilder() .include(LexerBenchmarkTest.class.getSimpleName()) diff --git a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java index f3595649..fe2528f7 100644 --- a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; -import org.junit.Ignore; import org.junit.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; @@ -15,7 +14,6 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Benchmark) -@Ignore public class ParserBenchmarkTest { @Param({"1000"}) @@ -35,7 +33,7 @@ public void parserBenchmark(Blackhole bh) { } } - @Test + //@Test public void executeBenchmark() throws RunnerException { Options opt = new OptionsBuilder() .include(ParserBenchmarkTest.class.getSimpleName()) From 473e82a54b73d3b603b1932acde1f4cd847220ca Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Sun, 7 Jan 2018 14:47:50 +0200 Subject: [PATCH 210/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=20=D1=86?= =?UTF-8?q?=D0=B2=D0=B5=D1=82=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B2=D1=8B=D0=B2?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE?= =?UTF-8?q?=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/console/colors.own | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/console/colors.own diff --git a/examples/console/colors.own b/examples/console/colors.own new file mode 100644 index 00000000..f6a832a9 --- /dev/null +++ b/examples/console/colors.own @@ -0,0 +1,26 @@ +use "std" +for b : range(8) + print sprintf(" 4%dm ", b) +println "" +for f : range(30, 38) { + for s : ["", "1;"] { + print sprintf("%4sm", s+f) + print sprintf(" \u001B[%sm%s\u001B[0m", s+f, "gYw ") + for b : range(8) + print sprintf(" \u001B[4%s;%sm%s\u001B[0m", b, s+f, " gYw ") + println "" + } +} + +/*use "functional" +stream(range(30, 38)) + .flatMap(def(f) = [[f, ""], [f, "1;"]]) + .forEach(def(a) { + extract(f, s) = a + print sprintf("%4sm", s+f) + print sprintf(" \u001B[%sm%s\u001B[0m", s+f, "gYw ") + for b : range(8) + print sprintf(" \u001B[4%s;%sm%s\u001B[0m", b, s+f, " gYw ") + println "" + }) +*/ From ea398978284dd87d5f5c763ef3acafb030c97b6e Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 10:55:06 +0200 Subject: [PATCH 211/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D1=91=D0=BD=20Gradle=20wrapper=20=D0=B4=D0=BE=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D0=B8=D0=B8=204.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/wrapper/gradle-wrapper.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7b066a5c..d4f7b5ea 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Dec 07 12:17:35 EET 2016 +#Fri Mar 09 10:54:07 EET 2018 +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip +zipStoreBase=GRADLE_USER_HOME From 865fe465239c68d20dd069b1b525002bab040f4b Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 10:57:08 +0200 Subject: [PATCH 212/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20kawaii-=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=20^^?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Lexer.java | 1 + src/main/java/com/annimon/ownlang/parser/Parser.java | 4 ++++ src/main/java/com/annimon/ownlang/parser/TokenType.java | 1 + .../java/com/annimon/ownlang/parser/ast/BinaryExpression.java | 1 + 4 files changed, 7 insertions(+) diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java index eea695ae..ca4332a8 100644 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -80,6 +80,7 @@ public static List tokenize(String input) { OPERATORS.put("@=", TokenType.ATEQ); OPERATORS.put("..", TokenType.DOTDOT); OPERATORS.put("**", TokenType.STARSTAR); + OPERATORS.put("^^", TokenType.CARETCARET); OPERATORS.put("?:", TokenType.QUESTIONCOLON); } diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 6fe57249..7f5c8603 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -629,6 +629,10 @@ private Expression additive() { result = new BinaryExpression(BinaryExpression.Operator.AT, result, multiplicative()); continue; } + if (match(TokenType.CARETCARET)) { + result = new BinaryExpression(BinaryExpression.Operator.CARETCARET, result, multiplicative()); + continue; + } break; } diff --git a/src/main/java/com/annimon/ownlang/parser/TokenType.java b/src/main/java/com/annimon/ownlang/parser/TokenType.java index f9496518..d30bbd29 100644 --- a/src/main/java/com/annimon/ownlang/parser/TokenType.java +++ b/src/main/java/com/annimon/ownlang/parser/TokenType.java @@ -72,6 +72,7 @@ public enum TokenType { TILDE, // ~ CARET, // ^ + CARETCARET, // ^^ BAR, // | BARBAR, // || AMP, // & diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java index 347ceb8d..3749ae76 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -33,6 +33,7 @@ public static enum Operator { // Addition operators for future usage or overloading AT("@"), + CARETCARET("^^"), RANGE(".."), POWER("**"), ELVIS("?:"); From cc4bd1f6af65106a96c8ca5394151fa4fbc7b497 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 10:59:00 +0200 Subject: [PATCH 213/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=D1=8B=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9=20=D1=81=20=D0=BF=D0=BE=D0=B1=D0=B8=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=BC=20=D1=81=D0=B4=D0=B2=D0=B8=D0=B3=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/optimization/ExpressionSimplification.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java b/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java index 7e0255e5..cd1b0421 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java @@ -117,6 +117,14 @@ public Node visit(BinaryExpression s, Void t) { return new ValueExpression(0); } + // x >> 0 to x, x << 0 to x + if (isIntegerValue(s.expr2, 0) && + (s.operation == BinaryExpression.Operator.LSHIFT || + s.operation == BinaryExpression.Operator.RSHIFT)) { + simplificationsCount++; + return s.expr1; + } + return super.visit(s, t); } From 9353bf9d203c0361ce9c95b08efbff70df881b8b Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 14:23:09 +0200 Subject: [PATCH 214/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=87?= =?UTF-8?q?=D0=B8=D0=BA=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=20JLine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../java/com/annimon/ownlang/utils/Repl.java | 24 +++++++---- .../ownlang/utils/repl/JLineConsole.java | 41 +++++++++++++++++++ .../ownlang/utils/repl/ReplConsole.java | 10 +++++ .../ownlang/utils/repl/SystemConsole.java | 31 ++++++++++++++ 5 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java create mode 100644 src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java create mode 100644 src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java diff --git a/build.gradle b/build.gradle index 0fed5469..5e72a558 100644 --- a/build.gradle +++ b/build.gradle @@ -93,6 +93,7 @@ dependencies { } compile 'org.json:json:20160212' compile 'org.yaml:snakeyaml:1.17' + compile 'jline:jline:2.14.5' testCompile 'junit:junit:4.12' testCompile 'org.openjdk.jmh:jmh-core:1.13' diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java index bef51f4e..43d39861 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -13,11 +13,14 @@ import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.PrintVisitor; +import com.annimon.ownlang.utils.repl.JLineConsole; +import com.annimon.ownlang.utils.repl.ReplConsole; +import com.annimon.ownlang.utils.repl.SystemConsole; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Scanner; import java.util.stream.Collectors; public final class Repl { @@ -36,14 +39,13 @@ public static void main(String[] args) { final BlockStatement history = new BlockStatement(); final StringBuilder buffer = new StringBuilder(); - final Scanner scanner = new Scanner(System.in); + final ReplConsole console = initReplConsole(); while (true) { - System.out.print((buffer.length() == 0) ? "\n> " : " "); + console.setPrompt((buffer.length() == 0) ? "\n> " : " "); - if (!scanner.hasNextLine()) break; + final String line = console.readLine(); + if (line == null || EXIT.equalsIgnoreCase(line)) break; - final String line = scanner.nextLine(); - if (EXIT.equalsIgnoreCase(line)) break; switch (line.toLowerCase(Locale.ENGLISH)) { case RESET: buffer.setLength(0); @@ -84,7 +86,15 @@ public static void main(String[] args) { } buffer.setLength(0); } - scanner.close(); + console.close(); + } + + private static ReplConsole initReplConsole() { + try { + return new JLineConsole(); + } catch (IOException ioe) { + return new SystemConsole(); + } } private static void printHelp(boolean full) { diff --git a/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java b/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java new file mode 100644 index 00000000..04b06119 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java @@ -0,0 +1,41 @@ +package com.annimon.ownlang.utils.repl; + +import java.io.IOException; +import jline.TerminalFactory; +import jline.console.ConsoleReader; + +public class JLineConsole implements ReplConsole { + + private final ConsoleReader console; + + public JLineConsole() throws IOException { + console = new ConsoleReader(); + } + + public ConsoleReader getConsole() { + return console; + } + + @Override + public void setPrompt(String prompt) { + console.setPrompt(prompt); + } + + @Override + public String readLine() { + try { + return console.readLine(); + } catch (IOException ex) { + return null; + } + } + + @Override + public void close() { + try { + TerminalFactory.get().restore(); + } catch (Exception ignored) { + } + } + +} diff --git a/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java b/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java new file mode 100644 index 00000000..ac65bae3 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java @@ -0,0 +1,10 @@ +package com.annimon.ownlang.utils.repl; + +public interface ReplConsole { + + void setPrompt(String prompt); + + String readLine(); + + void close(); +} diff --git a/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java b/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java new file mode 100644 index 00000000..6816fe64 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java @@ -0,0 +1,31 @@ +package com.annimon.ownlang.utils.repl; + +import java.util.Scanner; + +public class SystemConsole implements ReplConsole { + + private final Scanner scanner; + + public SystemConsole() { + scanner = new Scanner(System.in); + } + + @Override + public void setPrompt(String prompt) { + System.out.print(prompt); + } + + @Override + public String readLine() { + if (!scanner.hasNextLine()) { + return null; + } + return scanner.nextLine(); + } + + @Override + public void close() { + scanner.close(); + } + +} From a89b02ee8db970357c1192a7c864c0eb42dc7bb5 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 14:23:57 +0200 Subject: [PATCH 215/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D0=BE=D0=B4=D1=81=D0=BA=D0=B0=D0=B7?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B4=D0=BE=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Lexer.java | 5 +++ .../java/com/annimon/ownlang/utils/Repl.java | 9 ++++- .../ownlang/utils/repl/OwnLangCompleter.java | 34 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java index ca4332a8..e0ca7ab9 100644 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * @@ -105,6 +106,10 @@ public static List tokenize(String input) { KEYWORDS.put("include", TokenType.INCLUDE); } + public static Set getKeywords() { + return KEYWORDS.keySet(); + } + private final String input; private final int length; diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java index 43d39861..551bc1e5 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -14,6 +14,7 @@ import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.PrintVisitor; import com.annimon.ownlang.utils.repl.JLineConsole; +import com.annimon.ownlang.utils.repl.OwnLangCompleter; import com.annimon.ownlang.utils.repl.ReplConsole; import com.annimon.ownlang.utils.repl.SystemConsole; import java.io.IOException; @@ -22,6 +23,7 @@ import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; +import jline.console.completer.CandidateListCompletionHandler; public final class Repl { @@ -91,7 +93,12 @@ public static void main(String[] args) { private static ReplConsole initReplConsole() { try { - return new JLineConsole(); + JLineConsole jline = new JLineConsole(); + CandidateListCompletionHandler handler = new CandidateListCompletionHandler(); + handler.setPrintSpaceAfterFullCompletion(false); + jline.getConsole().setCompletionHandler(handler); + jline.getConsole().addCompleter(new OwnLangCompleter(HELP, VARS, FUNCS, SOURCE, RESET, EXIT)); + return jline; } catch (IOException ioe) { return new SystemConsole(); } diff --git a/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java b/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java new file mode 100644 index 00000000..e0a2f9e9 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java @@ -0,0 +1,34 @@ +package com.annimon.ownlang.utils.repl; + +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.parser.Lexer; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import jline.console.completer.StringsCompleter; + +public final class OwnLangCompleter extends StringsCompleter { + + private final Set staticCandidates; + + public OwnLangCompleter(String... candidates) { + staticCandidates = new HashSet<>(); + Collections.addAll(staticCandidates, candidates); + } + + @Override + public int complete(String buffer, int cursor, List candidates) { + updateCandidates(); + return super.complete(buffer, cursor, candidates); + } + + private void updateCandidates() { + getStrings().clear(); + getStrings().addAll(Lexer.getKeywords()); + getStrings().addAll(staticCandidates); + getStrings().addAll(Variables.variables().keySet()); + getStrings().addAll(Functions.getFunctions().keySet()); + } +} From d58bf6f2715ac02115b466acb46df0ecb4faf974 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 15:38:36 +0200 Subject: [PATCH 216/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=BE=D1=81=D1=82=D1=8B=D1=85=20=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B1=D0=B5=D0=B7?= =?UTF-8?q?=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=B0=20pri?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/utils/Repl.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java index 551bc1e5..e56e0ac4 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -10,6 +10,7 @@ import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.TokenType; import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.PrintVisitor; @@ -35,6 +36,8 @@ public final class Repl { RESET = ":reset", EXIT = ":exit"; + private static final Token PRINTLN_TOKEN = new Token(TokenType.PRINTLN, "", 0, 0); + public static void main(String[] args) { System.out.println("Welcome to OwnLang " + Main.VERSION + " REPL"); printHelp(false); @@ -73,7 +76,15 @@ public static void main(String[] args) { final Parser parser = new Parser(tokens); program = parser.parse(); if (parser.getParseErrors().hasErrors()) { - continue; + // Try to print value + List tokens2 = new ArrayList<>(); + tokens2.add(PRINTLN_TOKEN); + tokens2.addAll(tokens); + Parser parser2 = new Parser(tokens2); + program = parser2.parse(); + if (parser2.getParseErrors().hasErrors()) { + continue; + } } program.execute(); } catch (LexerException lex) { From a4d5b43003aa3f8bef892e3bf4eefb589ae81576 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 9 Mar 2018 17:12:43 +0200 Subject: [PATCH 217/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA?= =?UTF-8?q?=D0=B8,=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 68 ++++++++++++++------- proguard.properties | 3 +- src/main/java/com/annimon/ownlang/Main.java | 2 +- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/build.gradle b/build.gradle index 5e72a558..323da226 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,20 @@ -apply plugin: 'java' +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'net.sf.proguard:proguard-gradle:6.0.1' + } +} + +plugins { + id "java" + id "com.github.johnrengelman.shadow" version "2.0.2" +} + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import proguard.gradle.ProGuardTask + sourceCompatibility = '1.8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' @@ -14,15 +30,6 @@ repositories { jcenter() } -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'net.sf.proguard:proguard-gradle:5.2.1' - } -} - task generateJavaSources() { doLast { def source = """ @@ -53,22 +60,38 @@ task runOptimizing(dependsOn: classes, type: JavaExec) { args '-o 9 -m -a -f program.own'.split(' ') } -task dist(dependsOn: classes, type: Jar) { - from files(sourceSets.main.output.classesDir) - from files(sourceSets.main.output.resourcesDir) - from {configurations.compile.collect {zipTree(it)}} +task dist(type: ShadowJar) { + from sourceSets.main.output + configurations = [project.configurations.runtime] destinationDir file("$rootProject.projectDir/dist") + exclude 'META-INF/*.DSA' + exclude 'META-INF/*.RSA' + exclude 'META-INF/maven/**' + exclude 'LICENSE*' + manifest.attributes( 'Main-Class': project.mainClass, 'Build-Date': new Date().format('YYMMdd') ) } -task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) { +task proguard(dependsOn: dist, type: ProGuardTask) { configuration "$rootProject.projectDir/proguard.properties" injars "$rootProject.projectDir/dist/OwnLang.jar" outjars "$rootProject.projectDir/store/OwnLang.jar" + + // Automatically handle the Java version of this build. + if (System.getProperty('java.version').startsWith('1.')) { + // Before Java 9, the runtime classes were packaged in a single jar file. + libraryjars "${System.getProperty('java.home')}/lib/rt.jar" + } else { + // As of Java 9, the runtime classes are packaged in modular jmod files. + def jmods = files { file("${System.getProperty('java.home')}/jmods").listFiles() } + jmods.each { + libraryjars it, jarfilter: '!**.jar', filter: '!module-info.class' + } + } } task sandbox(dependsOn: proguard, type: Jar) { @@ -80,7 +103,8 @@ task sandbox(dependsOn: proguard, type: Jar) { "**/modules/java/**", "**/modules/jdbc/**", "**/modules/robot/**", "**/modules/socket/**", "io/**", "**/modules/aimp/**", "aimpremote/**", - "**/modules/downloader/**" + "**/modules/downloader/**", + "jline/**", "org/fusesource/**", "META-INF/native/**" manifest { attributes 'Main-Class': project.mainClass @@ -88,14 +112,14 @@ task sandbox(dependsOn: proguard, type: Jar) { } dependencies { - compile ('io.socket:socket.io-client:0.7.0') { + compile ('io.socket:socket.io-client:1.0.0') { exclude group: 'org.json', module: 'json' } - compile 'org.json:json:20160212' - compile 'org.yaml:snakeyaml:1.17' + compile 'org.json:json:20180130' + compile 'org.yaml:snakeyaml:1.20' compile 'jline:jline:2.14.5' - testCompile 'junit:junit:4.12' - testCompile 'org.openjdk.jmh:jmh-core:1.13' - testCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.13' + testImplementation 'junit:junit:4.12' + testImplementation 'org.openjdk.jmh:jmh-core:1.13' + testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.13' } diff --git a/proguard.properties b/proguard.properties index be30bc34..ca1ee694 100644 --- a/proguard.properties +++ b/proguard.properties @@ -1,6 +1,4 @@ -target 1.8 --libraryjars /lib/rt.jar --libraryjars /lib/ext/jfxrt.jar -printmapping store/out.map -printusage store/out.txt @@ -9,6 +7,7 @@ -dontwarn okio.** -dontwarn okhttp3.** +-dontwarn org.fusesource.jansi.internal.** -keepclasseswithmembers public class * { public static void main(java.lang.String[]); diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index 9edd6b0d..25cae184 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -22,7 +22,7 @@ */ public final class Main { - public static final String VERSION = "1.3.0_" + Gen.BUILD_DATE; + public static final String VERSION = "1.3.1_" + Gen.BUILD_DATE; private static String[] ownlangArgs = new String[0]; From fe7ac6a9b5d1e39ebc45b12300f67761d1edebab Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Mon, 1 Oct 2018 23:06:43 +0300 Subject: [PATCH 218/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D1=83=D0=BF=D1=80=D0=BE=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B2=D1=8B=D1=80=D0=B0=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExpressionSimplification.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java b/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java index cd1b0421..4b777052 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java @@ -58,26 +58,26 @@ public Node visit(BinaryExpression s, Void t) { if (expr1IsZero || isIntegerValue(s.expr2, 0)) { switch (s.operation) { case ADD: - // 0 + x2 to x2, x1 + 0 to x1 + // 0 + x = x + 0 = x simplificationsCount++; return expr1IsZero ? s.expr2 : s.expr1; case SUBTRACT: simplificationsCount++; if (expr1IsZero) { - // 0 - x2 to -x2 + // 0 - x = -x return new UnaryExpression(UnaryExpression.Operator.NEGATE, s.expr2); } - // x1 - 0 to x1 + // x - 0 = x return s.expr1; case MULTIPLY: - // 0 * x2 to 0, x1 * 0 to 0 + // 0 * x = x * 0 = 0 simplificationsCount++; return new ValueExpression(0); case DIVIDE: - // 0 / x2 to 0 + // 0 / x = 0 if (expr1IsZero) { simplificationsCount++; return new ValueExpression(0); @@ -91,12 +91,12 @@ public Node visit(BinaryExpression s, Void t) { if (expr1IsOne || isIntegerValue(s.expr2, 1)) { switch (s.operation) { case MULTIPLY: - // 1 * x2 to x2, x1 * 1 to x1 + // 1 * x = x * 1 = x simplificationsCount++; return expr1IsOne ? s.expr2 : s.expr1; case DIVIDE: - // x1 / 1 to x1 + // x / 1 = x if (!expr1IsOne) { simplificationsCount++; return s.expr1; @@ -105,19 +105,26 @@ public Node visit(BinaryExpression s, Void t) { } } - // x1 / -1 to -x1 - if (isIntegerValue(s.expr2, -1)) { + // x / -1 = -x + if (isIntegerValue(s.expr2, -1) && s.operation == BinaryExpression.Operator.DIVIDE) { simplificationsCount++; return new UnaryExpression(UnaryExpression.Operator.NEGATE, s.expr1); } - // x - x to 0 + // -1 * x = x * -1 = -x + final boolean expr1IsMinusOne = isIntegerValue(s.expr1, -1); + if ((expr1IsMinusOne || isIntegerValue(s.expr2, -1)) && s.operation == BinaryExpression.Operator.MULTIPLY) { + simplificationsCount++; + return new UnaryExpression(UnaryExpression.Operator.NEGATE, expr1IsMinusOne ? s.expr2 : s.expr1); + } + + // x - x = 0 if (isSameVariables(s.expr1, s.expr2) && s.operation == BinaryExpression.Operator.SUBTRACT) { simplificationsCount++; return new ValueExpression(0); } - // x >> 0 to x, x << 0 to x + // x >> 0 = x, x << 0 = x if (isIntegerValue(s.expr2, 0) && (s.operation == BinaryExpression.Operator.LSHIFT || s.operation == BinaryExpression.Operator.RSHIFT)) { @@ -134,12 +141,12 @@ public Node visit(ConditionalExpression s, Void t) { return super.visit(s, t); } if (isIntegerValue(s.expr1, 0) && s.operation == ConditionalExpression.Operator.AND) { - // 0 && x2 to 0 + // 0 && x = 0 simplificationsCount++; return new ValueExpression(0); } if (isIntegerValue(s.expr1, 1) && s.operation == ConditionalExpression.Operator.OR) { - // 1 || x2 to 1 + // 1 || x = 1 simplificationsCount++; return new ValueExpression(1); } From 670b4b8718e48aec0c218e4fc5144f3cec5bfead Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Oct 2018 14:24:13 +0300 Subject: [PATCH 219/448] Add SonarCloud integration, update gradle wrapper --- .gitignore | 6 +++++- .travis.yml | 16 ++++++++-------- build.gradle | 9 +++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- proguard.properties | 1 + 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index afb32114..bf174c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ /.gradle/ +/.idea/ +/.nb-gradle/ /build/ /dist/ /store/ /optimizations/ /nbproject/private/ -/src/main/generatedJava/ \ No newline at end of file +/src/main/generatedJava/ +OwnLang.iml +.nb-gradle-properties diff --git a/.travis.yml b/.travis.yml index b2f44baf..4c78dead 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,23 @@ language: java +sudo: false +install: true jdk: - oraclejdk8 +addons: + sonarcloud: + organization: "annimon-github" + cache: directories: - $HOME/.m2 - $HOME/.gradle + - $HOME/.sonar/cache -sudo: false - before_install: - chmod +x gradlew after_success: - - ./gradlew proguard + - ./gradlew proguard sonarqube - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@store/OwnLang.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang - -addons: - apt: - packages: - - oracle-java8-installer diff --git a/build.gradle b/build.gradle index 323da226..d7cef786 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { plugins { id "java" id "com.github.johnrengelman.shadow" version "2.0.2" + id "org.sonarqube" version "2.6.2" } import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar @@ -123,3 +124,11 @@ dependencies { testImplementation 'org.openjdk.jmh:jmh-core:1.13' testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.13' } + +sonarqube { + properties { + property "sonar.projectName", "Own-Programming-Language-Tutorial" + property "sonar.projectKey", "aNNiMON_Own-Programming-Language-Tutorial" + property "sonar.host.url", "https://sonarcloud.io" + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4f7b5ea..95b89ea2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Fri Mar 09 10:54:07 EET 2018 -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/proguard.properties b/proguard.properties index ca1ee694..a75f2fe4 100644 --- a/proguard.properties +++ b/proguard.properties @@ -8,6 +8,7 @@ -dontwarn okio.** -dontwarn okhttp3.** -dontwarn org.fusesource.jansi.internal.** +-dontwarn javafx.** -keepclasseswithmembers public class * { public static void main(java.lang.String[]); From be9bdb0311959aa5a396ae2f5b00b124d915a10f Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Oct 2018 16:10:29 +0300 Subject: [PATCH 220/448] =?UTF-8?q?=D0=9D=D0=B5=D0=B7=D0=BD=D0=B0=D1=87?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B8=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ .../java/com/annimon/ownlang/Console.java | 6 +++++- .../com/annimon/ownlang/lib/Variables.java | 19 +++++++++---------- .../annimon/ownlang/modules/java/java.java | 11 ++++++++--- .../ownlang/modules/robot/robot_exec.java | 2 +- .../annimon/ownlang/modules/std/std_sync.java | 1 + .../com/annimon/ownlang/parser/Parser.java | 4 ++-- .../ownlang/parser/ast/MatchExpression.java | 6 +++--- .../parser/optimization/VariableInfo.java | 2 +- .../com/annimon/ownlang/utils/Sandbox.java | 2 +- 10 files changed, 33 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 6ddcd41b..ec9d0100 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # OwnLang [![Build Status](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial.svg?branch=latest)](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial) +[![SonarCloud Status](https://sonarcloud.io/api/project_badges/measure?project=aNNiMON_Own-Programming-Language-Tutorial&metric=alert_status)](https://sonarcloud.io/dashboard?id=aNNiMON_Own-Programming-Language-Tutorial) +[![SonarCloud Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=aNNiMON_Own-Programming-Language-Tutorial&metric=sqale_rating)](https://sonarcloud.io/dashboard/?id=aNNiMON_Own-Programming-Language-Tutorial) | Free | Pro | Desktop | | :--: | :-: | :-----: | diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index 68769af7..d9e5c4f5 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -9,7 +9,11 @@ public class Console { private static final String FILE_PREFIX = "tmp/"; - public static boolean filePrefixEnabled = false; + private static boolean filePrefixEnabled; + + public static void enableFilePrefix() { + Console.filePrefixEnabled = true; + } public static String newline() { return System.lineSeparator(); diff --git a/src/main/java/com/annimon/ownlang/lib/Variables.java b/src/main/java/com/annimon/ownlang/lib/Variables.java index f307c613..f827ec3d 100644 --- a/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -12,22 +12,22 @@ public final class Variables { private static final Object lock = new Object(); private static class Scope { - public final Scope parent; - public final Map variables; + final Scope parent; + final Map variables; - public Scope() { + Scope() { this(null); } - public Scope(Scope parent) { + Scope(Scope parent) { this.parent = parent; variables = new ConcurrentHashMap<>(); } } private static class ScopeFindData { - public boolean isFound; - public Scope scope; + boolean isFound; + Scope scope; } private static volatile Scope scope; @@ -46,14 +46,13 @@ public static void clear() { scope.variables.put("false", NumberValue.ZERO); } - public static void push() { + static void push() { synchronized (lock) { - final Scope newScope = new Scope(scope); - scope = newScope; + scope = new Scope(scope); } } - public static void pop() { + static void pop() { synchronized (lock) { if (scope.parent != null) { scope = scope.parent; diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index 8de7c317..d236fc34 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -319,9 +319,14 @@ private static boolean isMatch(Value[] args, Class[] types) { final Class clazz = types[i]; if (arg == NULL) continue; - if (unboxed(clazz).isAssignableFrom(unboxed(valueToObject(arg).getClass()))) { - continue; - } + + final Class unboxed = unboxed(clazz); + boolean assignable = unboxed != null; + final Object object = valueToObject(arg); + assignable = assignable && (object != null); + assignable = assignable && (unboxed.isAssignableFrom(object.getClass())); + if (assignable) continue; + return false; } return true; diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java b/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java index 3533a341..abdf5458 100644 --- a/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.robot; +import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; @@ -43,7 +44,6 @@ public Value execute(Value... args) { return NumberValue.ZERO; } } catch (Exception ex) { - ex.printStackTrace(); return NumberValue.ZERO; } } 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 d4fef543..b1677a87 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 @@ -29,6 +29,7 @@ public Value execute(Value... args) { try { return queue.take(); } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); throw new RuntimeException(ex); } } diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 7f5c8603..f461a77a 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -30,7 +30,7 @@ public static Statement parse(List tokens) { private static final EnumMap ASSIGN_OPERATORS; static { - ASSIGN_OPERATORS = new EnumMap(TokenType.class); + ASSIGN_OPERATORS = new EnumMap<>(TokenType.class); ASSIGN_OPERATORS.put(TokenType.EQ, null); ASSIGN_OPERATORS.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD); ASSIGN_OPERATORS.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT); @@ -305,7 +305,7 @@ private Expression functionChain(Expression qualifiedNameExpr) { } if (lookMatch(0, TokenType.DOT)) { final List indices = variableSuffix(); - if (indices == null | indices.isEmpty()) return expr; + if (indices == null || indices.isEmpty()) return expr; if (lookMatch(0, TokenType.LPAREN)) { // next function call diff --git a/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 51c30913..14dd026f 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -221,7 +221,7 @@ public String toString() { } public static class ConstantPattern extends Pattern { - public Value constant; + Value constant; public ConstantPattern(Value pattern) { this.constant = pattern; @@ -247,13 +247,13 @@ public String toString() { } public static class ListPattern extends Pattern { - public List parts; + List parts; public ListPattern() { this(new ArrayList<>()); } - public ListPattern(List parts) { + ListPattern(List parts) { this.parts = parts; } diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java index 665115ea..82980351 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java @@ -4,7 +4,7 @@ public final class VariableInfo { public Value value; - public int modifications; + int modifications; @Override public String toString() { diff --git a/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/src/main/java/com/annimon/ownlang/utils/Sandbox.java index b064efe9..ee09be80 100644 --- a/src/main/java/com/annimon/ownlang/utils/Sandbox.java +++ b/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -18,7 +18,7 @@ public final class Sandbox { public static void main(String[] args) throws IOException { - Console.filePrefixEnabled = true; + Console.enableFilePrefix(); final String input = SourceLoader.readAndCloseStream(System.in); dumpInputArguments(input, args); From c7dd50c9ada5038bbbc087bb70bc7eb2b3b643ce Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Oct 2018 19:45:30 +0300 Subject: [PATCH 221/448] =?UTF-8?q?=D0=97=D0=BD=D0=B0=D1=87=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../java/com/annimon/ownlang/Console.java | 6 +- src/main/java/com/annimon/ownlang/Main.java | 53 +++++---- .../com/annimon/ownlang/lib/Arguments.java | 2 + .../com/annimon/ownlang/lib/CallStack.java | 4 +- .../com/annimon/ownlang/lib/Functions.java | 2 + .../com/annimon/ownlang/lib/NumberValue.java | 9 +- .../java/com/annimon/ownlang/lib/Types.java | 5 +- .../com/annimon/ownlang/lib/ValueUtils.java | 4 +- .../com/annimon/ownlang/lib/Variables.java | 2 + .../ownlang/modules/canvasfx/canvasfx.java | 2 +- .../annimon/ownlang/modules/files/files.java | 8 +- .../ownlang/modules/forms/Components.java | 12 +- .../ownlang/modules/forms/LayoutManagers.java | 12 +- .../modules/functional/functional_filter.java | 2 +- .../ownlang/modules/http/http_http.java | 5 +- .../annimon/ownlang/modules/java/java.java | 112 +++++++++--------- .../annimon/ownlang/modules/jdbc/jdbc.java | 2 +- .../annimon/ownlang/modules/robot/robot.java | 8 +- .../ownlang/modules/robot/robot_exec.java | 3 +- .../ownlang/modules/std/NumberFunctions.java | 4 +- .../ownlang/modules/std/StringFunctions.java | 6 +- .../ownlang/modules/std/std_split.java | 1 - .../ownlang/modules/yaml/yaml_decode.java | 3 +- .../com/annimon/ownlang/parser/Lexer.java | 2 +- .../com/annimon/ownlang/parser/Optimizer.java | 2 + .../com/annimon/ownlang/parser/Parser.java | 9 +- .../annimon/ownlang/parser/SourceLoader.java | 8 +- .../ownlang/parser/ast/BinaryExpression.java | 73 +++++++----- .../parser/ast/ConditionalExpression.java | 2 +- .../ownlang/parser/ast/MatchExpression.java | 2 +- .../ownlang/parser/ast/UnaryExpression.java | 2 +- .../optimization/OptimizationVisitor.java | 18 ++- .../ownlang/parser/visitors/PrintVisitor.java | 5 +- .../ownlang/parser/visitors/VisitorUtils.java | 2 + .../ownlang/utils/ModulesInfoCreator.java | 36 +++--- .../ownlang/utils/OptimizationDumper.java | 1 - .../java/com/annimon/ownlang/utils/Repl.java | 3 +- .../ownlang/parser/LexerBenchmarkTest.java | 4 +- .../ownlang/parser/ParserBenchmarkTest.java | 4 +- 40 files changed, 249 insertions(+), 192 deletions(-) diff --git a/build.gradle b/build.gradle index d7cef786..53a1e753 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,7 @@ task generateJavaSources() { def source = """ package com.annimon.ownlang; class Gen { + private Gen() {} public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; } """ diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index d9e5c4f5..6bf4a3e5 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -50,9 +50,9 @@ public static void error(CharSequence value) { public static void handleException(Thread thread, Throwable throwable) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try(final PrintStream ps = new PrintStream(baos)) { - ps.printf("%s in %s\n", throwable.getMessage(), thread.getName()); + ps.printf("%s in %s%n", throwable.getMessage(), thread.getName()); for (CallStack.CallInfo call : CallStack.getCalls()) { - ps.printf("\tat %s\n", call); + ps.printf("\tat %s%n", call); } ps.println(); throwable.printStackTrace(ps); @@ -74,4 +74,6 @@ public static File fileInstance(String path) { } return new File(filepath); } + + } diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index 25cae184..d9998340 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -33,25 +33,9 @@ public static String[] getOwnlangArgs() { public static void main(String[] args) throws IOException { if (args.length == 0) { try { - final Options options = new Options(); - options.showAst = false; - options.showTokens = false; - options.showMeasurements = false; - options.lintMode = false; - options.optimizationLevel = 0; - run(SourceLoader.readSource("program.own"), options); + runDefault(); } catch (IOException ioe) { - System.out.println("OwnLang version " + VERSION + "\n\n" + - "Usage: ownlang [options]\n" + - " options:\n" + - " -f, --file [input] Run program file. Required.\n" + - " -r, --repl Enter to a REPL mode\n" + - " -l, --lint Find bugs in code\n" + - " -o N, --optimize N Perform optimization with N passes\n" + - " -b, --beautify Beautify source code\n" + - " -a, --showast Show AST of program\n" + - " -t, --showtokens Show lexical tokens\n" + - " -m, --showtime Show elapsed time of parsing and execution"); + printUsage(); } return; } @@ -139,6 +123,30 @@ public static void main(String[] args) throws IOException { run(input, options); } + private static void runDefault() throws IOException { + final Options options = new Options(); + options.showAst = false; + options.showTokens = false; + options.showMeasurements = false; + options.lintMode = false; + options.optimizationLevel = 0; + run(SourceLoader.readSource("program.own"), options); + } + + private static void printUsage() { + System.out.println("OwnLang version " + VERSION + "\n\n" + + "Usage: ownlang [options]\n" + + " options:\n" + + " -f, --file [input] Run program file. Required.\n" + + " -r, --repl Enter to a REPL mode\n" + + " -l, --lint Find bugs in code\n" + + " -o N, --optimize N Perform optimization with N passes\n" + + " -b, --beautify Beautify source code\n" + + " -a, --showast Show AST of program\n" + + " -t, --showtokens Show lexical tokens\n" + + " -m, --showtime Show elapsed time of parsing and execution"); + } + private static void createOwnLangArgs(String[] javaArgs, int index) { if (index >= javaArgs.length) return; ownlangArgs = new String[javaArgs.length - index]; @@ -152,7 +160,8 @@ private static void run(String input, Options options) { final List tokens = Lexer.tokenize(input); measurement.stop("Tokenize time"); if (options.showTokens) { - for (int i = 0; i < tokens.size(); i++) { + final int tokensCount = tokens.size(); + for (int i = 0; i < tokensCount; i++) { System.out.println(i + " " + tokens.get(i)); } } @@ -205,7 +214,7 @@ private static class Options { boolean lintMode; int optimizationLevel; - public Options() { + Options() { showTokens = false; showAst = false; showMeasurements = false; @@ -213,8 +222,8 @@ public Options() { optimizationLevel = 0; } - public void validate() { - if (lintMode == true) { + void validate() { + if (lintMode) { showTokens = false; showAst = false; showMeasurements = false; diff --git a/src/main/java/com/annimon/ownlang/lib/Arguments.java b/src/main/java/com/annimon/ownlang/lib/Arguments.java index b4c11287..f63c70fd 100644 --- a/src/main/java/com/annimon/ownlang/lib/Arguments.java +++ b/src/main/java/com/annimon/ownlang/lib/Arguments.java @@ -4,6 +4,8 @@ public final class Arguments { + private Arguments() { } + public static void check(int expected, int got) { if (got != expected) throw new ArgumentsMismatchException(String.format( "%d %s expected, got %d", expected, pluralize(expected), got)); diff --git a/src/main/java/com/annimon/ownlang/lib/CallStack.java b/src/main/java/com/annimon/ownlang/lib/CallStack.java index e5b753b2..c7517bd3 100644 --- a/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ b/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -5,7 +5,9 @@ public final class CallStack { - private static final Deque calls = new ConcurrentLinkedDeque();; + private static final Deque calls = new ConcurrentLinkedDeque<>(); + + private CallStack() { } public static synchronized void clear() { calls.clear(); diff --git a/src/main/java/com/annimon/ownlang/lib/Functions.java b/src/main/java/com/annimon/ownlang/lib/Functions.java index 8d185694..f43d0297 100644 --- a/src/main/java/com/annimon/ownlang/lib/Functions.java +++ b/src/main/java/com/annimon/ownlang/lib/Functions.java @@ -15,6 +15,8 @@ public final class Functions { functions = new HashMap<>(); } + private Functions() { } + public static void clear() { functions.clear(); } diff --git a/src/main/java/com/annimon/ownlang/lib/NumberValue.java b/src/main/java/com/annimon/ownlang/lib/NumberValue.java index 3febab7a..68e005dd 100644 --- a/src/main/java/com/annimon/ownlang/lib/NumberValue.java +++ b/src/main/java/com/annimon/ownlang/lib/NumberValue.java @@ -6,10 +6,15 @@ */ public final class NumberValue implements Value { - public static final NumberValue MINUS_ONE, ZERO, ONE; + private static final int CACHE_MIN = -128; + private static final int CACHE_MAX = 127; + + public static final NumberValue MINUS_ONE; + public static final NumberValue ZERO; + public static final NumberValue ONE; - private static final int CACHE_MIN = -128, CACHE_MAX = 127; private static final NumberValue[] NUMBER_CACHE; + static { final int length = CACHE_MAX - CACHE_MIN + 1; NUMBER_CACHE = new NumberValue[length]; diff --git a/src/main/java/com/annimon/ownlang/lib/Types.java b/src/main/java/com/annimon/ownlang/lib/Types.java index a6da0333..8c0b2160 100644 --- a/src/main/java/com/annimon/ownlang/lib/Types.java +++ b/src/main/java/com/annimon/ownlang/lib/Types.java @@ -10,7 +10,8 @@ public final class Types { MAP = 4, FUNCTION = 5; - private static final int FIRST = OBJECT, LAST = FUNCTION; + private static final int FIRST = OBJECT; + private static final int LAST = FUNCTION; private static final String[] NAMES = {"object", "number", "string", "array", "map", "function"}; public static String typeToString(int type) { @@ -19,4 +20,6 @@ public static String typeToString(int type) { } return "unknown (" + type + ")"; } + + private Types() { } } diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index c11bbfbd..5d051bc7 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -8,6 +8,8 @@ public final class ValueUtils { + private ValueUtils() { } + public static Object toObject(Value val) { switch (val.type()) { case Types.ARRAY: @@ -101,7 +103,7 @@ public static byte[] toByteArray(ArrayValue array) { return result; } - public static Function consumeFunction(Value value, int argumentNumber) throws TypeException { + public static Function consumeFunction(Value value, int argumentNumber) { final int type = value.type(); if (type != Types.FUNCTION) { throw new TypeException("Function expected at argument " + (argumentNumber + 1) diff --git a/src/main/java/com/annimon/ownlang/lib/Variables.java b/src/main/java/com/annimon/ownlang/lib/Variables.java index f827ec3d..4c6f9514 100644 --- a/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -35,6 +35,8 @@ private static class ScopeFindData { Variables.clear(); } + private Variables() { } + public static Map variables() { return scope.variables; } diff --git a/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java b/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java index 973f4ae8..e1568025 100644 --- a/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java @@ -53,7 +53,7 @@ public final class canvasfx implements Module { private static GraphicsContext graphics; private static Canvas canvas; - private static enum Events { + private enum Events { DRAG_DETECTED(MouseEvent.DRAG_DETECTED), MOUSE_CLICKED(MouseEvent.MOUSE_CLICKED), MOUSE_DRAGGED(MouseEvent.MOUSE_DRAGGED), diff --git a/src/main/java/com/annimon/ownlang/modules/files/files.java b/src/main/java/com/annimon/ownlang/modules/files/files.java index 66373239..00947c4c 100644 --- a/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -156,7 +156,7 @@ private Value process(File file, String mode) throws IOException { } } - private static abstract class FileFunction implements Function { + private abstract static class FileFunction implements Function { @Override public Value execute(Value... args) { @@ -252,7 +252,7 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { private static class setExecutable extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - final boolean ownerOnly = (args.length >= 3) ? (args[2].asInt() != 0) : true; + final boolean ownerOnly = (args.length < 3) || (args[2].asInt() != 0); return NumberValue.fromBoolean( fileInfo.file.setExecutable(args[1].asInt() != 0, ownerOnly)); } @@ -261,7 +261,7 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { private static class setReadable extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - final boolean ownerOnly = (args.length >= 3) ? (args[2].asInt() != 0) : true; + final boolean ownerOnly = (args.length < 3) || (args[2].asInt() != 0); return NumberValue.fromBoolean( fileInfo.file.setReadable(args[1].asInt() != 0, ownerOnly)); } @@ -270,7 +270,7 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { private static class setWritable extends FileFunction { @Override protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { - final boolean ownerOnly = (args.length >= 3) ? (args[2].asInt() != 0) : true; + final boolean ownerOnly = (args.length < 3) || (args[2].asInt() != 0); return NumberValue.fromBoolean( fileInfo.file.setWritable(args[1].asInt() != 0, ownerOnly)); } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/Components.java b/src/main/java/com/annimon/ownlang/modules/forms/Components.java index 5d1b6b5b..f283e5a4 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/Components.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/Components.java @@ -14,7 +14,9 @@ */ public final class Components { - public static Value newWindow(Value... args) { + private Components() { } + + static Value newWindow(Value... args) { Arguments.checkOrOr(0, 1, args.length); String title = (args.length == 1) ? args[0].asString() : ""; final JFrame frame = new JFrame(title); @@ -22,7 +24,7 @@ public static Value newWindow(Value... args) { return new JFrameValue(frame); } - public static Value newPanel(Value... args) { + static Value newPanel(Value... args) { Arguments.checkOrOr(0, 1, args.length); final JPanel panel = new JPanel(); if (args.length == 1) { @@ -31,20 +33,20 @@ public static Value newPanel(Value... args) { return new JPanelValue(panel); } - public static Value newButton(Value... args) { + static Value newButton(Value... args) { Arguments.checkOrOr(0, 1, args.length); String text = (args.length == 1) ? args[0].asString() : ""; return new JButtonValue(new JButton(text)); } - public static Value newLabel(Value... args) { + static Value newLabel(Value... args) { Arguments.checkRange(0, 2, args.length); String text = (args.length >= 1) ? args[0].asString() : ""; int align = (args.length == 2) ? args[1].asInt() : SwingConstants.LEADING; return new JLabelValue(new JLabel(text, align)); } - public static Value newTextField(Value... args) { + static Value newTextField(Value... args) { Arguments.checkOrOr(0, 1, args.length); String text = (args.length == 1) ? args[0].asString() : ""; return new JTextFieldValue(new JTextField(text)); diff --git a/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java index 8191e691..45558f29 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java @@ -13,7 +13,9 @@ */ public final class LayoutManagers { - public static Value borderLayout(Value... args) { + private LayoutManagers() { } + + static Value borderLayout(Value... args) { Arguments.checkOrOr(0, 2, args.length); int hgap = (args.length == 2) ? args[0].asInt() : 0; int vgap = (args.length == 2) ? args[1].asInt() : 0; @@ -22,7 +24,7 @@ public static Value borderLayout(Value... args) { ); } - public static Value boxLayout(Value... args) { + static Value boxLayout(Value... args) { Arguments.checkOrOr(1, 2, args.length); int axis = (args.length == 2) ? args[1].asInt() : BoxLayout.PAGE_AXIS; return new LayoutManagerValue( @@ -30,7 +32,7 @@ public static Value boxLayout(Value... args) { ); } - public static Value cardLayout(Value... args) { + static Value cardLayout(Value... args) { Arguments.checkOrOr(0, 2, args.length); int hgap = (args.length == 2) ? args[0].asInt() : 0; int vgap = (args.length == 2) ? args[1].asInt() : 0; @@ -39,7 +41,7 @@ public static Value cardLayout(Value... args) { ); } - public static Value gridLayout(Value... args) { + static Value gridLayout(Value... args) { Arguments.checkRange(0, 4, args.length); int rows = 1, cols = 0, hgap = 0, vgap = 0; switch (args.length) { @@ -67,7 +69,7 @@ public static Value gridLayout(Value... args) { ); } - public static Value flowLayout(Value... args) { + static Value flowLayout(Value... args) { Arguments.checkRange(0, 3, args.length); final int align, hgap, vgap; switch (args.length) { 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 d0e631b0..c4749ce2 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 @@ -36,7 +36,7 @@ public Value execute(Value... args) { private Value filterArray(ArrayValue array, Function predicate, boolean takeWhile) { final int size = array.size(); - final List values = new ArrayList(size); + final List values = new ArrayList<>(size); for (Value value : array) { if (predicate.execute(value) != NumberValue.ZERO) { values.add(value); diff --git a/src/main/java/com/annimon/ownlang/modules/http/http_http.java b/src/main/java/com/annimon/ownlang/modules/http/http_http.java index bddc3c5d..a247a41a 100644 --- a/src/main/java/com/annimon/ownlang/modules/http/http_http.java +++ b/src/main/java/com/annimon/ownlang/modules/http/http_http.java @@ -144,9 +144,10 @@ private RequestBody getRequestBody(String method, Value params, MapValue options return getStringRequestBody(params, options); } - private RequestBody getMapRequestBody(MapValue params, MapValue options) throws UnsupportedEncodingException { + private RequestBody getMapRequestBody(MapValue params, MapValue options) { final FormBody.Builder form = new FormBody.Builder(); - final boolean alreadyEncoded = (options.containsKey(ENCODED_KEY) && options.get(ENCODED_KEY).asInt() != 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/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index d236fc34..953ae2cb 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -283,7 +283,7 @@ private static Value getValue(Class clazz, Object object, String key) { methods.add(method); } } - if (methods.size() == 0) { + if (methods.isEmpty()) { return FunctionValue.EMPTY; } return new FunctionValue(methodsToFunction(object, methods)); @@ -362,7 +362,7 @@ private static Value objectToValue(Object o) { } private static Value objectToValue(Class clazz, Object o) { - if (o == null | o == NULL) return NULL; + if (o == null || o == NULL) return NULL; if (clazz.isPrimitive()) { if (int.class.isAssignableFrom(clazz)) return NumberValue.of((int) o); @@ -394,48 +394,7 @@ private static Value objectToValue(Class clazz, Object o) { return (Value) o; } if (clazz.isArray()) { - final int length = Array.getLength(o); - final ArrayValue result = new ArrayValue(length); - final Class componentType = clazz.getComponentType(); - int i = 0; - if (boolean.class.isAssignableFrom(componentType)) { - for (boolean element : (boolean[]) o) { - result.set(i++, NumberValue.fromBoolean(element)); - } - } else if (byte.class.isAssignableFrom(componentType)) { - for (byte element : (byte[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else if (char.class.isAssignableFrom(componentType)) { - for (char element : (char[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else if (double.class.isAssignableFrom(componentType)) { - for (double element : (double[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else if (float.class.isAssignableFrom(componentType)) { - for (float element : (float[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else if (int.class.isAssignableFrom(componentType)) { - for (int element : (int[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else if (long.class.isAssignableFrom(componentType)) { - for (long element : (long[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else if (short.class.isAssignableFrom(componentType)) { - for (short element : (short[]) o) { - result.set(i++, NumberValue.of(element)); - } - } else { - for (Object element : (Object[]) o) { - result.set(i++, objectToValue(element)); - } - } - return result; + return arrayToValue(clazz, o); } final Class componentType = clazz.getComponentType(); if (componentType != null) { @@ -444,6 +403,51 @@ private static Value objectToValue(Class clazz, Object o) { return new ObjectValue(o); } + private static Value arrayToValue(Class clazz, Object o) { + final int length = Array.getLength(o); + final ArrayValue result = new ArrayValue(length); + final Class componentType = clazz.getComponentType(); + int i = 0; + if (boolean.class.isAssignableFrom(componentType)) { + for (boolean element : (boolean[]) o) { + result.set(i++, NumberValue.fromBoolean(element)); + } + } else if (byte.class.isAssignableFrom(componentType)) { + for (byte element : (byte[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (char.class.isAssignableFrom(componentType)) { + for (char element : (char[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (double.class.isAssignableFrom(componentType)) { + for (double element : (double[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (float.class.isAssignableFrom(componentType)) { + for (float element : (float[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (int.class.isAssignableFrom(componentType)) { + for (int element : (int[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (long.class.isAssignableFrom(componentType)) { + for (long element : (long[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else if (short.class.isAssignableFrom(componentType)) { + for (short element : (short[]) o) { + result.set(i++, NumberValue.of(element)); + } + } else { + for (Object element : (Object[]) o) { + result.set(i++, objectToValue(element)); + } + } + return result; + } + private static Object[] valuesToObjects(Value[] args) { Object[] result = new Object[args.length]; for (int i = 0; i < args.length; i++) { @@ -459,15 +463,8 @@ private static Object valueToObject(Value value) { return value.raw(); case Types.STRING: return value.asString(); - case Types.ARRAY: { - final ArrayValue array = (ArrayValue) value; - final int size = array.size(); - final Object[] result = new Object[size]; - for (int i = 0; i < size; i++) { - result[i] = valueToObject(array.get(i)); - } - return result; - } + case Types.ARRAY: + return arrayToObject((ArrayValue) value); } if (value instanceof ObjectValue) { return ((ObjectValue) value).object; @@ -477,5 +474,14 @@ private static Object valueToObject(Value value) { } return value.raw(); } + + private static Object arrayToObject(ArrayValue value) { + final int size = value.size(); + final Object[] result = new Object[size]; + for (int i = 0; i < size; i++) { + result[i] = valueToObject(value.get(i)); + } + return result; + } // } diff --git a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index 9518e7c0..3bb3c78d 100644 --- a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -430,7 +430,7 @@ private void init() { set("getBigDecimal", getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); set("getBoolean", getBooleanResult(rs::getBoolean, rs::getBoolean)); set("getByte", getNumberResult(rs::getByte, rs::getByte)); - set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); + set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, ArrayValue::of)); set("getDate", getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); set("getDouble", getNumberResult(rs::getDouble, rs::getDouble)); set("getFloat", getNumberResult(rs::getFloat, rs::getFloat)); diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot.java b/src/main/java/com/annimon/ownlang/modules/robot/robot.java index 0d3fa8e9..901c7bd2 100644 --- a/src/main/java/com/annimon/ownlang/modules/robot/robot.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot.java @@ -8,6 +8,7 @@ import java.awt.event.KeyEvent; import java.util.HashMap; import java.util.Map; +import java.util.function.IntConsumer; /** * @@ -83,12 +84,7 @@ private static boolean initialize() { } } - @FunctionalInterface - private interface RobotIntConsumer { - void accept(int value) throws IllegalArgumentException; - } - - private static Function convertFunction(RobotIntConsumer consumer) { + private static Function convertFunction(IntConsumer consumer) { return args -> { Arguments.check(1, args.length); try { diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java b/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java index abdf5458..322dde16 100644 --- a/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java +++ b/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.modules.robot; -import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; @@ -10,7 +9,7 @@ public final class robot_exec implements Function { - public static enum Mode { EXEC, EXEC_AND_WAIT }; + public enum Mode { EXEC, EXEC_AND_WAIT } private final Mode mode; diff --git a/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java index 31d2cd6f..743732ed 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java +++ b/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java @@ -8,7 +8,9 @@ public final class NumberFunctions { - public static Value toHexString(Value... args) { + private NumberFunctions() { } + + static Value toHexString(Value... args) { Arguments.check(1, args.length); long value; if (args[0].type() == Types.NUMBER) { diff --git a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java index b9b57945..d850d6de 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java +++ b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java @@ -6,13 +6,15 @@ public final class StringFunctions { - public static Value parseInt(Value... args) { + private StringFunctions() { } + + static Value parseInt(Value... args) { Arguments.checkOrOr(1, 2, args.length); final int radix = (args.length == 2) ? args[1].asInt() : 10; return NumberValue.of(Integer.parseInt(args[0].asString(), radix)); } - public static Value parseLong(Value... args) { + static Value parseLong(Value... args) { Arguments.checkOrOr(1, 2, args.length); final int radix = (args.length == 2) ? args[1].asInt() : 10; return NumberValue.of(Long.parseLong(args[0].asString(), radix)); diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_split.java b/src/main/java/com/annimon/ownlang/modules/std/std_split.java index d0e1b5e1..0c2c4688 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_split.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_split.java @@ -3,7 +3,6 @@ import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; public final class std_split implements Function { diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java index fa3d3fcd..07e3f198 100644 --- a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -13,8 +13,7 @@ public Value execute(Value... args) { try { final String yamlRaw = args[0].asString(); final Object root = new Yaml().load(yamlRaw); - final Value process = process(root); - return process; + return process(root); } catch (Exception ex) { throw new RuntimeException("Error while parsing yaml", ex); } diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java index e0ca7ab9..2a644771 100644 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -87,7 +87,7 @@ public static List tokenize(String input) { private static final Map KEYWORDS; static { - KEYWORDS = new HashMap(); + KEYWORDS = new HashMap<>(); KEYWORDS.put("print", TokenType.PRINT); KEYWORDS.put("println", TokenType.PRINTLN); KEYWORDS.put("if", TokenType.IF); diff --git a/src/main/java/com/annimon/ownlang/parser/Optimizer.java b/src/main/java/com/annimon/ownlang/parser/Optimizer.java index a41da8db..a4f2ff61 100644 --- a/src/main/java/com/annimon/ownlang/parser/Optimizer.java +++ b/src/main/java/com/annimon/ownlang/parser/Optimizer.java @@ -13,6 +13,8 @@ public final class Optimizer { + private Optimizer() { } + public static Statement optimize(Statement statement, int level, boolean showSummary) { if (level == 0) return statement; diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index f461a77a..2f5a270c 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -6,6 +6,7 @@ import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.List; @@ -305,7 +306,7 @@ private Expression functionChain(Expression qualifiedNameExpr) { } if (lookMatch(0, TokenType.DOT)) { final List indices = variableSuffix(); - if (indices == null || indices.isEmpty()) return expr; + if (indices.isEmpty()) return expr; if (lookMatch(0, TokenType.LPAREN)) { // next function call @@ -441,7 +442,7 @@ private Expression assignment() { private Expression assignmentStrict() { final int position = pos; final Expression targetExpr = qualifiedName(); - if ((targetExpr == null) || !(targetExpr instanceof Accessible)) { + if (!(targetExpr instanceof Accessible)) { pos = position; return null; } @@ -746,7 +747,7 @@ private Expression qualifiedName() { if (!match(TokenType.WORD)) return null; final List indices = variableSuffix(); - if ((indices == null) || indices.isEmpty()) { + if (indices.isEmpty()) { return new VariableExpression(current.getText()); } return new ContainerAccessExpression(current.getText(), indices); @@ -755,7 +756,7 @@ private Expression qualifiedName() { private List variableSuffix() { // .key1.arr1[expr1][expr2].key2 if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) { - return null; + return Collections.emptyList(); } final List indices = new ArrayList<>(); while (lookMatch(0, TokenType.DOT) || lookMatch(0, TokenType.LBRACKET)) { diff --git a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java index 9545b529..67e49cf9 100644 --- a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java +++ b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java @@ -7,6 +7,8 @@ public final class SourceLoader { + private SourceLoader() { } + public static String readSource(String name) throws IOException { InputStream is = SourceLoader.class.getResourceAsStream(name); if (is != null) return readAndCloseStream(is); @@ -19,9 +21,9 @@ public static String readAndCloseStream(InputStream is) throws IOException { final ByteArrayOutputStream result = new ByteArrayOutputStream(); final int bufferSize = 1024; final byte[] buffer = new byte[bufferSize]; - int readed; - while ((readed = is.read(buffer)) != -1) { - result.write(buffer, 0, readed); + int read; + while ((read = is.read(buffer)) != -1) { + result.write(buffer, 0, read); } is.close(); return result.toString("UTF-8"); diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java index 3749ae76..999e94e8 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -16,7 +16,7 @@ */ public final class BinaryExpression implements Expression { - public static enum Operator { + public enum Operator { ADD("+"), SUBTRACT("-"), MULTIPLY("*"), @@ -40,7 +40,7 @@ public static enum Operator { private final String name; - private Operator(String name) { + Operator(String name) { this.name = name; } @@ -73,7 +73,7 @@ public Value eval() { } } - private Value eval(Value value1, Value value2) throws OperationIsNotSupportedException { + private Value eval(Value value1, Value value2) { switch (operation) { case ADD: return add(value1, value2); case SUBTRACT: return subtract(value1, value2); @@ -141,7 +141,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -177,17 +178,10 @@ private Value subtract(NumberValue value1, Value value2) { 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 Types.STRING: return multiply((StringValue) value1, value2); default: - throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -219,12 +213,23 @@ private Value multiply(NumberValue value1, Value value2) { } return NumberValue.of(number1.intValue() * value2.asInt()); } + + private Value multiply(StringValue value1, Value value2) { + 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()); + } 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -261,7 +266,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -298,7 +304,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -306,7 +313,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -331,7 +339,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -356,7 +365,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -380,16 +390,13 @@ private Value xor(NumberValue value1, Value value2) { 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((ArrayValue) value1, (ArrayValue) value2); - } + case Types.ARRAY: return lshift((ArrayValue) value1, value2); default: - throw new OperationIsNotSupportedException(operation, "for " + Types.typeToString(value1.type())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } - + private Value lshift(NumberValue value1, Value value2) { final Number number1 = value1.raw(); if (value2.type() == Types.NUMBER) { @@ -406,12 +413,19 @@ private Value lshift(NumberValue value1, Value value2) { } return NumberValue.of(number1.intValue() << value2.asInt()); } + + private Value lshift(ArrayValue value1, Value value2) { + if (value2.type() != Types.ARRAY) + throw new TypeException("Cannot merge non array value to array"); + return ArrayValue.merge(value1, (ArrayValue) value2); + } 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } @@ -436,7 +450,8 @@ 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())); + throw new OperationIsNotSupportedException(operation, + "for " + Types.typeToString(value1.type())); } } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java index 1ee89252..b39d7964 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -11,7 +11,7 @@ */ public final class ConditionalExpression implements Expression { - public static enum Operator { + public enum Operator { EQUALS("=="), NOT_EQUALS("!="), diff --git a/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 14dd026f..4a334404 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -205,7 +205,7 @@ public String toString() { return sb.toString(); } - public static abstract class Pattern { + public abstract static class Pattern { public Statement result; public Expression optCondition; diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java index f7d48396..551ba6ac 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -12,7 +12,7 @@ */ public final class UnaryExpression implements Expression, Statement { - public static enum Operator { + public enum Operator { INCREMENT_PREFIX("++"), DECREMENT_PREFIX("--"), INCREMENT_POSTFIX("++"), diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 46696ada..8db0e84e 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -260,16 +260,14 @@ public Node visit(MatchExpression s, T t) { final String variable = ((MatchExpression.VariablePattern) pattern).variable; final VariableExpression expr = new VariableExpression(variable); final Node node = expr.accept(this, t); - if (node != expr) { - if (isValue(node)) { - changed = true; - final Value value = ((ValueExpression) node).value; - final Expression optCondition = pattern.optCondition; - final Statement result = pattern.result; - pattern = new MatchExpression.ConstantPattern(value); - pattern.optCondition = optCondition; - pattern.result = result; - } + if ((node != expr) && isValue(node)) { + changed = true; + final Value value = ((ValueExpression) node).value; + final Expression optCondition = pattern.optCondition; + final Statement result = pattern.result; + pattern = new MatchExpression.ConstantPattern(value); + pattern.optCondition = optCondition; + pattern.result = result; } } diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 4ecc6f02..2e0f9ca4 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -34,7 +34,7 @@ public StringBuilder visit(ArrayExpression s, StringBuilder t) { @Override public StringBuilder visit(AssignmentExpression s, StringBuilder t) { - ((Node) s.target).accept(this, t); + s.target.accept(this, t); t.append(' ').append((s.operation == null) ? "" : s.operation); t.append("= "); s.expression.accept(this, t); @@ -203,7 +203,8 @@ public StringBuilder visit(FunctionReferenceExpression s, StringBuilder t) { @Override public StringBuilder visit(FunctionalExpression s, StringBuilder t) { - if (s.functionExpr instanceof ValueExpression && ((ValueExpression)s.functionExpr).value.type() == Types.STRING) { + if (s.functionExpr instanceof ValueExpression + && ((ValueExpression)s.functionExpr).value.type() == Types.STRING) { t.append(((ValueExpression)s.functionExpr).value.asString()); } else { s.functionExpr.accept(this, t); diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java b/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java index 408e5693..1c593b81 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java @@ -17,6 +17,8 @@ public final class VisitorUtils { + private VisitorUtils() { } + public static boolean isValue(Node node) { return (node instanceof ValueExpression); } diff --git a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index 420ec0b5..b9ec788e 100644 --- a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -7,19 +7,10 @@ import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.modules.Module; import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; @@ -28,14 +19,17 @@ public final class ModulesInfoCreator { private static final String MODULES_PATH = "src/main/java/com/annimon/ownlang/modules"; - public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { + public static void main(String[] args) + throws InstantiationException, IllegalAccessException, ClassNotFoundException { final Class clazz = Module.class; // get classloader for package final List moduleInfos = new ArrayList<>(); - String[] moduleNames = Arrays.stream(new File(MODULES_PATH).listFiles()) - .filter(p -> p.isDirectory()) - .map(p -> p.getName()) + String[] moduleNames = Optional.ofNullable(new File(MODULES_PATH).listFiles()) + .map(Arrays::stream) + .orElse(Stream.empty()) + .filter(File::isDirectory) + .map(File::getName) .toArray(String[]::new); for (String moduleName : moduleNames) { final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName); @@ -57,16 +51,16 @@ public static void main(String[] args) throws InstantiationException, IllegalAcc System.out.println("Total modules: " + moduleInfos.size()); System.out.println("Total functions: " + moduleInfos.stream() - .flatMap(m -> m.functions.stream()) - .count() + .mapToLong(m -> m.functions.size()) + .sum() ); System.out.println("Total constants: " + moduleInfos.stream() - .flatMap(m -> m.constants.keySet().stream()) - .count() + .mapToLong(m -> m.constants.keySet().size()) + .sum() ); } - private static void printAsJson(List moduleInfos) throws JSONException { + private static void printAsJson(List moduleInfos) { final JSONArray modulesJson = new JSONArray(); for (ModuleInfo moduleInfo : moduleInfos) { modulesJson.put(new JSONObject(moduleInfo.info())); @@ -128,7 +122,7 @@ public List> functions() { public List> constants() { final List> result = new ArrayList<>(); constants.entrySet().stream() - .sorted(Comparator.comparing(e -> e.getKey())) + .sorted(Comparator.comparing(Map.Entry::getKey)) .forEach(entry -> { final Value value = entry.getValue(); @@ -140,7 +134,7 @@ public List> constants() { String text = ((Map) value.raw()).entrySet().stream() .sorted(Comparator.comparing( e -> ((MapValue)value).size() > 16 ? e.getKey() : e.getValue())) - .map(s -> s.toString()) + .map(Object::toString) .collect(Collectors.joining(", ", "{", "}")); constant.put("value", text); } else { diff --git a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java index 45df8bba..c4d7797c 100644 --- a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java +++ b/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java @@ -68,7 +68,6 @@ private static Map getOptimizationSteps(String input) throws IOE } private static String nodeToString(Node n) { -// return n.toString(); return n.accept(new PrintVisitor(), new StringBuilder()).toString(); } diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/src/main/java/com/annimon/ownlang/utils/Repl.java index e56e0ac4..2c06f94e 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -129,7 +129,8 @@ private static void printHelp(boolean full) { int maxLength = commands.stream() .mapToInt(String::length) - .max().getAsInt(); + .max() + .orElse(20); final int maxCols = 2; final int size = commands.size(); diff --git a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java index 13930e06..fed985b6 100644 --- a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java +++ b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; +import org.junit.Ignore; import org.junit.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; @@ -31,7 +32,8 @@ public void lexerBenchmark() { } } - //@Test + @Ignore + @Test public void executeBenchmark() throws RunnerException { Options opt = new OptionsBuilder() .include(LexerBenchmarkTest.class.getSimpleName()) diff --git a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java index fe2528f7..c1cba073 100644 --- a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; +import org.junit.Ignore; import org.junit.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; @@ -33,7 +34,8 @@ public void parserBenchmark(Blackhole bh) { } } - //@Test + @Ignore + @Test public void executeBenchmark() throws RunnerException { Options opt = new OptionsBuilder() .include(ParserBenchmarkTest.class.getSimpleName()) From 795d7660f99c579aa4568f28466965eb7d3b0c22 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Oct 2018 21:30:11 +0300 Subject: [PATCH 222/448] =?UTF-8?q?=D0=9D=D0=B5=D0=B7=D0=BD=D0=B0=D1=87?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=B8=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index 3bb3c78d..9518e7c0 100644 --- a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -430,7 +430,7 @@ private void init() { set("getBigDecimal", getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); set("getBoolean", getBooleanResult(rs::getBoolean, rs::getBoolean)); set("getByte", getNumberResult(rs::getByte, rs::getByte)); - set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, ArrayValue::of)); + set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); set("getDate", getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); set("getDouble", getNumberResult(rs::getDouble, rs::getDouble)); set("getFloat", getNumberResult(rs::getFloat, rs::getFloat)); From eb7430f507c9809ff2f6fc73cf43578bec66f0d4 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 7 Dec 2018 22:19:23 +0200 Subject: [PATCH 223/448] =?UTF-8?q?=D0=9D=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=D0=B0=20=D0=B8=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/annimon/ownlang/Console.java | 2 ++ src/main/java/com/annimon/ownlang/Main.java | 32 ++++++++----------- .../annimon/ownlang/parser/ParserTest.java | 3 +- .../annimon/ownlang/parser/ProgramsTest.java | 8 +++-- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index 6bf4a3e5..d6216cd8 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -8,6 +8,8 @@ public class Console { + private Console() { } + private static final String FILE_PREFIX = "tmp/"; private static boolean filePrefixEnabled; diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index d9998340..11f3ef09 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -40,8 +40,7 @@ public static void main(String[] args) throws IOException { return; } - final Options options = new Options(); - boolean beautifyMode = false; + final RunOptions options = new RunOptions(); String input = null; for (int i = 0; i < args.length; i++) { switch (args[i]) { @@ -52,19 +51,19 @@ public static void main(String[] args) throws IOException { case "-b": case "--beautify": - beautifyMode = true; + options.beautifyMode = true; break; - + case "-t": case "--showtokens": options.showTokens = true; break; - + case "-m": case "--showtime": options.showMeasurements = true; break; - + case "-o": case "--optimize": if (i + 1 < args.length) { @@ -88,7 +87,7 @@ public static void main(String[] args) throws IOException { case "--lint": options.lintMode = true; return; - + case "-f": case "--file": if (i + 1 < args.length) { @@ -104,7 +103,7 @@ public static void main(String[] args) throws IOException { System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length); Sandbox.main(newArgs); return; - + default: if (input == null) { input = args[i]; @@ -116,7 +115,7 @@ public static void main(String[] args) throws IOException { if (input == null) { throw new IllegalArgumentException("Empty input"); } - if (beautifyMode) { + if (options.beautifyMode) { System.out.println(Beautifier.beautify(input)); return; } @@ -124,12 +123,7 @@ public static void main(String[] args) throws IOException { } private static void runDefault() throws IOException { - final Options options = new Options(); - options.showAst = false; - options.showTokens = false; - options.showMeasurements = false; - options.lintMode = false; - options.optimizationLevel = 0; + final RunOptions options = new RunOptions(); run(SourceLoader.readSource("program.own"), options); } @@ -153,7 +147,7 @@ private static void createOwnLangArgs(String[] javaArgs, int index) { System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); } - private static void run(String input, Options options) { + private static void run(String input, RunOptions options) { options.validate(); final TimeMeasurement measurement = new TimeMeasurement(); measurement.start("Tokenize time"); @@ -209,16 +203,18 @@ private static void run(String input, Options options) { } } - private static class Options { + private static class RunOptions { boolean showTokens, showAst, showMeasurements; boolean lintMode; + boolean beautifyMode; int optimizationLevel; - Options() { + RunOptions() { showTokens = false; showAst = false; showMeasurements = false; lintMode = false; + beautifyMode = false; optimizationLevel = 0; } diff --git a/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/src/test/java/com/annimon/ownlang/parser/ParserTest.java index dfe031ec..598a798b 100644 --- a/src/test/java/com/annimon/ownlang/parser/ParserTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ParserTest.java @@ -50,7 +50,8 @@ private static BlockStatement assertProgram(String input, BlockStatement actual) } private static void assertStatements(BlockStatement expected, BlockStatement actual) { - for (int i = 0; i < expected.statements.size(); i++) { + final int size = expected.statements.size(); + for (int i = 0; i < size; i++) { assertEquals(expected.statements.get(i).getClass(), actual.statements.get(i).getClass()); } } diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index c8ed605e..a59c539f 100644 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -36,12 +36,16 @@ public ProgramsTest(String programPath) { public static Collection data() { final File resDir = new File(RES_DIR); return scanDirectory(resDir) - .map(f -> f.getPath()) + .map(File::getPath) .collect(Collectors.toList()); } private static Stream scanDirectory(File dir) { - return Arrays.stream(dir.listFiles()) + final File[] files = dir.listFiles(); + if (files == null || files.length == 0) { + return Stream.empty(); + } + return Arrays.stream(files) .flatMap(file -> { if (file.isDirectory()) { return scanDirectory(file); From 52d712175183daf8294994e73d25e883d9e078f0 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 7 Dec 2018 23:33:58 +0200 Subject: [PATCH 224/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=B2=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=81=D0=BE=D0=BB=D0=B8=20=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=B0=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=B2=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/annimon/ownlang/Console.java | 43 +++++------ .../outputsettings/ConsoleOutputSettings.java | 56 +++++++++++++++ .../outputsettings/OutputSettings.java | 27 +++++++ .../outputsettings/StringOutputSettings.java | 71 +++++++++++++++++++ .../com/annimon/ownlang/utils/Sandbox.java | 9 ++- .../annimon/ownlang/parser/ProgramsTest.java | 19 +++++ 6 files changed, 203 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java create mode 100644 src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java create mode 100644 src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index d6216cd8..57487ac9 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -1,6 +1,8 @@ package com.annimon.ownlang; import com.annimon.ownlang.lib.CallStack; +import com.annimon.ownlang.outputsettings.ConsoleOutputSettings; +import com.annimon.ownlang.outputsettings.OutputSettings; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; @@ -9,44 +11,51 @@ public class Console { private Console() { } + + private static OutputSettings outputSettings = new ConsoleOutputSettings(); - private static final String FILE_PREFIX = "tmp/"; - private static boolean filePrefixEnabled; + public static void useSettings(OutputSettings outputSettings) { + Console.outputSettings = outputSettings; + } - public static void enableFilePrefix() { - Console.filePrefixEnabled = true; + public static OutputSettings getSettings() { + return outputSettings; } public static String newline() { - return System.lineSeparator(); + return outputSettings.newline(); } public static void print(String value) { - System.out.print(value); + outputSettings.print(value); } public static void print(Object value) { - print(value.toString()); + outputSettings.print(value); } public static void println() { - System.out.println(); + outputSettings.println(); } public static void println(String value) { - System.out.println(value); + outputSettings.println(value); } public static void println(Object value) { - println(value.toString()); + outputSettings.println(value); + } + + public static String text() { + return outputSettings.getText(); } public static void error(Throwable throwable) { - error(throwable.toString()); + outputSettings.error(throwable); } public static void error(CharSequence value) { - System.err.println(value); + outputSettings.error(value); } public static void handleException(Thread thread, Throwable throwable) { @@ -68,14 +77,6 @@ public static void handleException(Thread thread, Throwable throwable) { } public static File fileInstance(String path) { - final String filepath; - if (filePrefixEnabled) { - filepath = FILE_PREFIX.concat(path); - } else { - filepath = path; - } - return new File(filepath); + return outputSettings.fileInstance(path); } - - } diff --git a/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java b/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java new file mode 100644 index 00000000..87c409df --- /dev/null +++ b/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.outputsettings; + +import java.io.File; + +public class ConsoleOutputSettings implements OutputSettings { + + @Override + public String newline() { + return System.lineSeparator(); + } + + @Override + public void print(String value) { + System.out.print(value); + } + + @Override + public void print(Object value) { + print(value.toString()); + } + + @Override + public void println() { + System.out.println(); + } + + @Override + public void println(String value) { + System.out.println(value); + } + + @Override + public void println(Object value) { + println(value.toString()); + } + + @Override + public String getText() { + return ""; + } + + @Override + public void error(Throwable throwable) { + error(throwable.toString()); + } + + @Override + public void error(CharSequence value) { + System.err.println(value); + } + + @Override + public File fileInstance(String path) { + return new File(path); + } +} diff --git a/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java b/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java new file mode 100644 index 00000000..30427080 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.outputsettings; + +import java.io.File; + +public interface OutputSettings { + + String newline(); + + void print(String value); + + void print(Object value); + + void println(); + + void println(String value); + + void println(Object value); + + String getText(); + + void error(Throwable throwable); + + void error(CharSequence value); + + File fileInstance(String path); + +} diff --git a/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java b/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java new file mode 100644 index 00000000..3ffc8f11 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java @@ -0,0 +1,71 @@ +package com.annimon.ownlang.outputsettings; + +import java.io.File; + +public class StringOutputSettings implements OutputSettings { + + private final StringBuffer out, err; + + public StringOutputSettings() { + this(new StringBuffer()); + } + + public StringOutputSettings(StringBuffer out) { + this(out, out); + } + + public StringOutputSettings(StringBuffer out, StringBuffer err) { + this.out = out; + this.err = err; + } + + @Override + public String newline() { + return System.lineSeparator(); + } + + @Override + public void print(String value) { + out.append(value); + } + + @Override + public void print(Object value) { + out.append(value.toString()); + } + + @Override + public void println() { + out.append(newline()); + } + + @Override + public void println(String value) { + out.append(value).append(newline()); + } + + @Override + public void println(Object value) { + println(value.toString()); + } + + @Override + public String getText() { + return out.toString(); + } + + @Override + public void error(Throwable throwable) { + error(throwable.toString()); + } + + @Override + public void error(CharSequence value) { + err.append(value).append(newline()); + } + + @Override + public File fileInstance(String path) { + return new File(path); + } +} diff --git a/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/src/main/java/com/annimon/ownlang/utils/Sandbox.java index ee09be80..7d23ed69 100644 --- a/src/main/java/com/annimon/ownlang/utils/Sandbox.java +++ b/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -3,12 +3,14 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.lib.CallStack; +import com.annimon.ownlang.outputsettings.ConsoleOutputSettings; import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.SourceLoader; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.FunctionAdder; +import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -18,7 +20,12 @@ public final class Sandbox { public static void main(String[] args) throws IOException { - Console.enableFilePrefix(); + Console.useSettings(new ConsoleOutputSettings() { + @Override + public File fileInstance(String path) { + return new File("tmp/" + path); + } + }); final String input = SourceLoader.readAndCloseStream(System.in); dumpInputArguments(input, args); diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index a59c539f..d1421228 100644 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -1,8 +1,11 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.outputsettings.OutputSettings; +import com.annimon.ownlang.outputsettings.StringOutputSettings; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; @@ -94,6 +97,22 @@ public void testProgram() throws IOException { } } + @Test + public void testOutput() throws IOException { + OutputSettings oldSettings = Console.getSettings(); + Console.useSettings(new StringOutputSettings()); + String source = "for i = 0, i <= 5, i++\n print i"; + final Statement s = Parser.parse(Lexer.tokenize(source)); + try { + s.execute(); + assertEquals("012345", Console.text()); + } catch (Exception oae) { + Assert.fail(oae.toString()); + } finally { + Console.useSettings(oldSettings); + } + } + private static Visitor testFunctionsExecutor = new AbstractVisitor() { @Override public void visit(FunctionDefineStatement s) { From 89651745891528d31fa8543fa63d02cab3d8ac7d Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 12 Dec 2018 21:26:15 +0200 Subject: [PATCH 225/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D0=B5=D0=B9=20use=20["std",=20"types",=20"files"]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/parser/ast/UseStatement.java | 41 +++++++++++++++++-- .../UseWithNonStringValueValidator.java | 24 +++++++++-- src/test/resources/modules/base64/base64.own | 4 +- src/test/resources/modules/files/files.own | 3 +- .../resources/modules/functional/stream.own | 4 +- src/test/resources/modules/java/classes.own | 3 +- src/test/resources/modules/std/range.own | 4 +- .../resources/modules/yaml/yamldecode.own | 12 +++--- .../resources/modules/yaml/yamlencode.own | 4 +- 9 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 7a40d23a..568b63b6 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -1,5 +1,9 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; import java.lang.reflect.Method; @@ -21,9 +25,24 @@ public UseStatement(Expression expression) { @Override public void execute() { super.interruptionCheck(); + final Value value = expression.eval(); + switch (value.type()) { + case Types.ARRAY: + for (Value module : ((ArrayValue) value)) { + loadModule(module.asString()); + } + break; + case Types.STRING: + loadModule(value.asString()); + break; + default: + throw new TypeException("Array or string required"); + } + } + + private void loadModule(String name) { try { - final String moduleName = expression.eval().asString(); - final Module module = (Module) Class.forName(String.format(PACKAGE, moduleName, moduleName)).newInstance(); + final Module module = (Module) Class.forName(String.format(PACKAGE, name, name)).newInstance(); module.init(); } catch (Exception ex) { throw new RuntimeException(ex); @@ -31,14 +50,30 @@ public void execute() { } public void loadConstants() { + final Value value = expression.eval(); + switch (value.type()) { + case Types.ARRAY: + for (Value module : ((ArrayValue) value)) { + loadConstants(module.asString()); + } + break; + case Types.STRING: + loadConstants(value.asString()); + break; + default: + throw new TypeException("Array or string required"); + } + } + + private void loadConstants(String moduleName) { try { - final String moduleName = expression.eval().asString(); final Class moduleClass = Class.forName(String.format(PACKAGE, moduleName, moduleName)); final Method method = moduleClass.getMethod(INIT_CONSTANTS_METHOD); if (method != null) { method.invoke(this); } } catch (Exception ex) { + // ignore } } diff --git a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java index fa4745c8..de1e51b9 100644 --- a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java +++ b/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.ast.*; @@ -23,9 +24,26 @@ public void visit(UseStatement st) { } final Value value = ((ValueExpression) st.expression).value; - if (value.type() != Types.STRING) { - Console.error(String.format( - "Warning: `use` with %s - %s, not string", Types.typeToString(value.type()), value.asString())); + switch (value.type()) { + case Types.STRING: + // ok + break; + case Types.ARRAY: + // ok, need additional check + for (Value module : ((ArrayValue) value)) { + if (module.type() != Types.STRING) { + warnWrongType(module); + } + } + break; + default: + warnWrongType(value); } } + + private void warnWrongType(Value value) { + Console.error(String.format( + "Warning: `use` with %s - %s, not string", + Types.typeToString(value.type()), value.asString())); + } } diff --git a/src/test/resources/modules/base64/base64.own b/src/test/resources/modules/base64/base64.own index 841dfddd..b9e11db5 100644 --- a/src/test/resources/modules/base64/base64.own +++ b/src/test/resources/modules/base64/base64.own @@ -1,6 +1,4 @@ -use "base64" -use "functional" -use "types" +use ["base64", "functional", "types"] base64Example = [0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x20, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65] base64Example_enc = [0x51, 0x6D, 0x46, 0x7A, 0x5A, 0x54, 0x59, 0x30, diff --git a/src/test/resources/modules/files/files.own b/src/test/resources/modules/files/files.own index f4591b86..4f9c7e35 100644 --- a/src/test/resources/modules/files/files.own +++ b/src/test/resources/modules/files/files.own @@ -1,5 +1,4 @@ -use "files" -use "types" +use ["files", "types"] def testFiles() { // writeLong diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own index c3c5ae37..62681437 100644 --- a/src/test/resources/modules/functional/stream.own +++ b/src/test/resources/modules/functional/stream.own @@ -1,6 +1,4 @@ -use "std" -use "functional" -use "math" +use ["std", "functional", "math"] def testStream() { data = [1,2,3,4,5,6,7] diff --git a/src/test/resources/modules/java/classes.own b/src/test/resources/modules/java/classes.own index 04a8dd2a..2ccb6adb 100644 --- a/src/test/resources/modules/java/classes.own +++ b/src/test/resources/modules/java/classes.own @@ -1,5 +1,4 @@ -use "std" -use "java" +use ["std", "java"] def testCheckNull() { assertTrue(isNull(null)) diff --git a/src/test/resources/modules/std/range.own b/src/test/resources/modules/std/range.own index d1e13fe4..ae616dbd 100644 --- a/src/test/resources/modules/std/range.own +++ b/src/test/resources/modules/std/range.own @@ -1,6 +1,4 @@ -use "std" -use "types" -use "functional" +use ["std", "types", "functional"] def testRangeParams() { x = range(10) diff --git a/src/test/resources/modules/yaml/yamldecode.own b/src/test/resources/modules/yaml/yamldecode.own index 1cc25655..fc335af7 100644 --- a/src/test/resources/modules/yaml/yamldecode.own +++ b/src/test/resources/modules/yaml/yamldecode.own @@ -1,24 +1,22 @@ -use "std" -use "yaml" -use "ounit" +use ["std", "yaml", "ounit"] x = yamldecode(" name: \"std\" scope: \"both\" desc: \"Contains common functions\" - desc_ru: \" \" + desc_ru: \"Содержит вспомогательные функции общего назначения\" constants: [] functions: - name: \"arrayCombine\" args: \"keys, values\" desc: \"creates map by combining two arrays\" - desc_ru: \" \" + desc_ru: \"создаёт объект на основе двух массивов\" - name: \"typeof\" args: \"value\" desc: \"returns the type of value\" - desc_ru: \" \" + desc_ru: \"возвращает тип переданного значения\" example: |- print typeof(1) // 1 (NUMBER) print typeof(\"text\") // 2 (STRING) @@ -30,4 +28,4 @@ assertEquals("both", x.scope) assertEquals(0, length(x.constants)) assertEquals(2, length(x.functions)) assertEquals("arrayCombine", x.functions[0].name) -assertEquals(" ", x.functions[1].desc_ru) \ No newline at end of file +assertEquals("возвращает тип переданного значения", x.functions[1].desc_ru) \ No newline at end of file diff --git a/src/test/resources/modules/yaml/yamlencode.own b/src/test/resources/modules/yaml/yamlencode.own index a54e33d2..cf2e465d 100644 --- a/src/test/resources/modules/yaml/yamlencode.own +++ b/src/test/resources/modules/yaml/yamlencode.own @@ -1,6 +1,4 @@ -use "std" -use "yaml" -use "ounit" +use ["std", "yaml", "ounit"] yml = yamlencode({ "name": "Yaml Example", From a30ca2fd4d5f3c9be88af635cc2b3f08891ed9cf Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 13 Dec 2018 20:08:02 +0200 Subject: [PATCH 226/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20regex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/modules/regex/MatcherValue.java | 171 ++++++++++++++++++ .../ownlang/modules/regex/PatternValue.java | 62 +++++++ .../annimon/ownlang/modules/regex/regex.java | 61 +++++++ src/test/resources/modules/regex/match.own | 15 ++ .../modules/regex/replaceCallback.own | 8 + 5 files changed, 317 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/regex/regex.java create mode 100644 src/test/resources/modules/regex/match.own create mode 100644 src/test/resources/modules/regex/replaceCallback.own diff --git a/src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java b/src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java new file mode 100644 index 00000000..c8dd9252 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java @@ -0,0 +1,171 @@ +package com.annimon.ownlang.modules.regex; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.regex.Matcher; + +public class MatcherValue extends MapValue { + + private final Matcher value; + + public MatcherValue(Matcher value) { + super(22); + this.value = value; + init(); + } + + private void init() { + set("start", this::start); + set("end", this::end); + set("find", this::find); + set("group", this::group); + set("pattern", this::pattern); + set("region", this::region); + set("replaceFirst", this::replaceFirst); + set("replaceAll", this::replaceAll); + set("replaceCallback", this::replaceCallback); + set("reset", this::reset); + set("usePattern", this::usePattern); + set("useAnchoringBounds", this::useAnchoringBounds); + set("hasAnchoringBounds", Converters.voidToBoolean(value::hasAnchoringBounds)); + set("useTransparentBounds", this::useTransparentBounds); + set("hasTransparentBounds", Converters.voidToBoolean(value::hasTransparentBounds)); + set("hitEnd", Converters.voidToBoolean(value::hitEnd)); + set("lookingAt", Converters.voidToBoolean(value::lookingAt)); + set("matches", Converters.voidToBoolean(value::matches)); + set("groupCount", Converters.voidToInt(value::groupCount)); + set("regionStart", Converters.voidToInt(value::regionStart)); + set("regionEnd", Converters.voidToInt(value::regionEnd)); + } + + private Value start(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + final int result; + if (args.length == 0) { + result = value.start(); + } else { + final Value arg = args[0]; + if (arg.type() == Types.NUMBER) { + result = value.start(arg.asInt()); + } else { + result = value.start(arg.asString()); + } + } + return NumberValue.of(result); + } + + private Value end(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + final int result; + if (args.length == 0) { + result = value.end(); + } else { + final Value arg = args[0]; + if (arg.type() == Types.NUMBER) { + result = value.end(arg.asInt()); + } else { + result = value.end(arg.asString()); + } + } + return NumberValue.of(result); + } + + private Value find(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + final boolean result; + if (args.length == 0) { + result = value.find(); + } else { + result = value.find(args[0].asInt()); + } + return NumberValue.fromBoolean(result); + } + + private Value group(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + final String result; + if (args.length == 0) { + result = value.group(); + } else { + final Value arg = args[0]; + if (arg.type() == Types.NUMBER) { + result = value.group(arg.asInt()); + } else { + result = value.group(arg.asString()); + } + } + return new StringValue(result); + } + + private Value pattern(Value[] args) { + return new PatternValue(value.pattern()); + } + + private Value region(Value[] args) { + Arguments.check(2, args.length); + value.region(args[0].asInt(), args[1].asInt()); + return this; + } + + private Value replaceFirst(Value[] args) { + Arguments.check(1, args.length); + return new StringValue(value.replaceFirst(args[0].asString())); + } + + private Value replaceAll(Value[] args) { + Arguments.check(1, args.length); + return new StringValue(value.replaceAll(args[0].asString())); + } + + static StringValue replaceCallback(MatcherValue matcherValue, Function callback) { + final Matcher matcher = matcherValue.value; + final StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String replacement = callback.execute(matcherValue).asString(); + matcher.appendReplacement(sb, replacement); + } + matcher.appendTail(sb); + return new StringValue(sb.toString()); + } + + private Value replaceCallback(Value[] args) { + Arguments.check(1, args.length); + if (args[0].type() != Types.FUNCTION) { + throw new TypeException(args[0].toString() + " is not a function"); + } + return replaceCallback(this, ((FunctionValue) args[0]).getValue()); + } + + private Value reset(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + value.reset(); + } else { + value.reset(args[0].asString()); + } + return this; + } + + private Value useAnchoringBounds(Value[] args) { + Arguments.check(1, args.length); + value.useAnchoringBounds(args[0].asInt() != 0); + return this; + } + + private Value useTransparentBounds(Value[] args) { + Arguments.check(1, args.length); + value.useTransparentBounds(args[0].asInt() != 0); + return this; + } + + private Value usePattern(Value[] args) { + Arguments.check(1, args.length); + final Value arg = args[0]; + if (arg.type() == Types.MAP && (arg instanceof PatternValue)) { + value.usePattern(((PatternValue) arg).getValue()); + } else { + throw new TypeException("Pattern value expected"); + } + return this; + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java b/src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java new file mode 100644 index 00000000..e39598ce --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.modules.regex; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.util.regex.Pattern; + +public class PatternValue extends MapValue { + + private final Pattern value; + + public PatternValue(Pattern value) { + super(8); + this.value = value; + init(); + } + + private void init() { + set("flags", Converters.voidToInt(value::flags)); + set("pattern", Converters.voidToString(value::pattern)); + set("matcher", this::matcher); + set("matches", this::matches); + set("split", this::split); + set("replaceCallback", this::replaceCallback); + } + + public Pattern getValue() { + return value; + } + + private Value split(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final int limit = (args.length == 2) ? args[1].asInt() : 0; + return ArrayValue.of(value.split(args[0].asString(), limit)); + } + + private Value matcher(Value[] args) { + Arguments.check(1, args.length); + return new MatcherValue(value.matcher(args[0].asString())); + } + + private Value matches(Value[] args) { + Arguments.check(1, args.length); + return NumberValue.fromBoolean(value.matcher(args[0].asString()).matches()); + } + + private Value replaceCallback(Value[] args) { + Arguments.check(2, args.length); + if (args[1].type() != Types.FUNCTION) { + throw new TypeException(args[1].toString() + " is not a function"); + } + return MatcherValue.replaceCallback( + new MatcherValue(value.matcher(args[0].asString())), + ((FunctionValue) args[1]).getValue()); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/regex/regex.java b/src/main/java/com/annimon/ownlang/modules/regex/regex.java new file mode 100644 index 00000000..a7c01937 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/regex/regex.java @@ -0,0 +1,61 @@ +package com.annimon.ownlang.modules.regex; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.modules.Module; +import java.util.regex.Pattern; + +public final class regex implements Module { + + public static void initConstants() { + MapValue map = new MapValue(20); + map.set("UNIX_LINES", NumberValue.of(Pattern.UNIX_LINES)); + map.set("I", NumberValue.of(Pattern.CASE_INSENSITIVE)); + map.set("CASE_INSENSITIVE", NumberValue.of(Pattern.CASE_INSENSITIVE)); + map.set("COMMENTS", NumberValue.of(Pattern.COMMENTS)); + map.set("M", NumberValue.of(Pattern.MULTILINE)); + map.set("MULTILINE", NumberValue.of(Pattern.MULTILINE)); + map.set("LITERAL", NumberValue.of(Pattern.LITERAL)); + map.set("S", NumberValue.of(Pattern.DOTALL)); + map.set("DOTALL", NumberValue.of(Pattern.DOTALL)); + map.set("UNICODE_CASE", NumberValue.of(Pattern.UNICODE_CASE)); + map.set("CANON_EQ", NumberValue.of(Pattern.CANON_EQ)); + map.set("U", NumberValue.of(Pattern.UNICODE_CHARACTER_CLASS)); + map.set("UNICODE_CHARACTER_CLASS", NumberValue.of(Pattern.UNICODE_CHARACTER_CLASS)); + + map.set("quote", args -> { + Arguments.check(1, args.length); + return new StringValue(Pattern.quote(args[0].asString())); + }); + map.set("matches", args -> { + Arguments.check(2, args.length); + return NumberValue.fromBoolean(Pattern.matches(args[0].asString(), args[1].asString())); + }); + map.set("split", args -> { + Arguments.checkOrOr(2, 3, args.length); + final Pattern pattern = Pattern.compile(args[0].asString()); + final int limit = (args.length == 3) ? args[2].asInt() : 0; + return ArrayValue.of(pattern.split(args[1].asString(), limit)); + }); + map.set("compile", regex::compile); + Variables.define("Pattern", map); + } + + @Override + public void init() { + initConstants(); + Functions.set("regex", regex::compile); + } + + private static Value compile(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final int flags = (args.length == 2) ? args[1].asInt() : 0; + return new PatternValue(Pattern.compile(args[0].asString(), flags)); + } +} diff --git a/src/test/resources/modules/regex/match.own b/src/test/resources/modules/regex/match.own new file mode 100644 index 00000000..24954fe5 --- /dev/null +++ b/src/test/resources/modules/regex/match.own @@ -0,0 +1,15 @@ +use ["regex", "types"] + +def testMatchGitUrl() { + pattern = Pattern.compile("https?://((git(hu|la)b\.com)|bitbucket\.org)/?") + assertTrue(pattern.matches("http://github.com")) + assertTrue(pattern.matches("http://github.com/")) + assertTrue(pattern.matches("https://gitlab.com/")) + assertTrue(pattern.matches("https://bitbucket.org/")) + + assertFalse(pattern.matches("http://github.org")) + assertFalse(pattern.matches("https://bithub.com/")) + assertFalse(pattern.matches("http://gitlab.org")) + assertFalse(pattern.matches("ftp://github.com/")) + assertFalse(pattern.matches("http://gitbucket.org/")) +} \ No newline at end of file diff --git a/src/test/resources/modules/regex/replaceCallback.own b/src/test/resources/modules/regex/replaceCallback.own new file mode 100644 index 00000000..cdfb5255 --- /dev/null +++ b/src/test/resources/modules/regex/replaceCallback.own @@ -0,0 +1,8 @@ +use ["regex", "types"] + +def testReplaceCallback() { + in = "[1-2-3-4]" + pattern = regex("(\d)") + out = pattern.replaceCallback(in, def(m) = m.group() * int(m.group())) + assertEquals("[1-22-333-4444]", out) +} \ No newline at end of file From 9540834d2f66d2e6d0c8fd91e021953159959743 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 2 Jan 2019 18:22:04 +0200 Subject: [PATCH 227/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20std::default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/modules/std/std.java | 1 + .../ownlang/modules/std/std_default.java | 38 +++++++++++++++++++ src/test/resources/modules/std/default.own | 19 ++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/modules/std/std_default.java create mode 100644 src/test/resources/modules/std/default.own diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index 470be66f..eb21870f 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -26,6 +26,7 @@ public void init() { Functions.set("thread", new std_thread()); Functions.set("sync", new std_sync()); Functions.set("try", new std_try()); + Functions.set("default", new std_default()); // Numbers Functions.set("toHexString", NumberFunctions::toHexString); diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_default.java b/src/main/java/com/annimon/ownlang/modules/std/std_default.java new file mode 100644 index 00000000..68819bf8 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/std/std_default.java @@ -0,0 +1,38 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; + +public final class std_default implements Function { + + @Override + public Value execute(Value... args) { + Arguments.check(2, args.length); + if (isEmpty(args[0])) { + return args[1]; + } + return args[0]; + } + + private boolean isEmpty(Value value) { + if (value == null || value.raw() == null) { + return true; + } + switch (value.type()) { + case Types.NUMBER: + return (value.asInt() == 0); + case Types.STRING: + return (value.asString().isEmpty()); + case Types.ARRAY: + return ((ArrayValue) value).size() == 0; + case Types.MAP: + return ((MapValue) value).size() == 0; + default: + return false; + } + } +} \ No newline at end of file diff --git a/src/test/resources/modules/std/default.own b/src/test/resources/modules/std/default.own new file mode 100644 index 00000000..375aa333 --- /dev/null +++ b/src/test/resources/modules/std/default.own @@ -0,0 +1,19 @@ +use "std" + +def testDefaultNumber() { + assertEquals(123, default(0, 123)) +} + +def testDefaultString() { + assertEquals("123", default("", "123")) +} + +def testDefaultNull() { + use "java" + assertEquals("not null", default(null, "not null")) +} + +def testOperatorOverloading() { + def `?:`(a, b) = default(a, b) + assertEquals("not null", "" ?: "not null") +} From dbdc56f323b0feba28bccb0aaa1a1bdc5c6b3cd7 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 2 Jan 2019 18:57:15 +0200 Subject: [PATCH 228/448] =?UTF-8?q?=D0=9A=D0=BE=D0=BD=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=20std::OwnLang=20=D1=81=20=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=81=D0=B8=D0=B5=D0=B9=20=D0=B8=20=D0=BF=D0=BB=D0=B0=D1=82?= =?UTF-8?q?=D1=84=D0=BE=D1=80=D0=BC=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/Main.java | 9 +++++++-- src/main/java/com/annimon/ownlang/modules/std/std.java | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index 11f3ef09..66b74d9d 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -21,8 +21,13 @@ * @author aNNiMON */ public final class Main { - - public static final String VERSION = "1.3.1_" + Gen.BUILD_DATE; + + public static int VERSION_MAJOR = 1; + public static int VERSION_MINOR = 4; + public static int VERSION_PATCH = 0; + public static final String VERSION = VERSION_MAJOR + "." + + VERSION_MINOR + "." + VERSION_PATCH + + "_" + Gen.BUILD_DATE; private static String[] ownlangArgs = new String[0]; diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index eb21870f..29bf1574 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -11,6 +11,13 @@ public final class std implements Module { public static void initConstants() { + MapValue ownlang = new MapValue(5); + ownlang.set("PLATFORM", new StringValue("desktop")); + ownlang.set("VERSION", new StringValue(Main.VERSION)); + ownlang.set("VERSION_MAJOR", NumberValue.of(Main.VERSION_MAJOR)); + ownlang.set("VERSION_MINOR", NumberValue.of(Main.VERSION_MINOR)); + ownlang.set("VERSION_PATCH", NumberValue.of(Main.VERSION_PATCH)); + Variables.define("OwnLang", ownlang); } @Override From 0bbbeccda63746d01ea04ba9651a8e0a31489adc Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Jan 2019 21:59:37 +0200 Subject: [PATCH 229/448] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=B2=D0=BD=D1=83=D1=82=D1=80=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D0=B8=D1=85=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9=20=D0=B8=20?= =?UTF-8?q?=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9=20=D1=83=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exceptions/UnknownPropertyException.java | 15 +++++++++++++++ .../com/annimon/ownlang/lib/StringValue.java | 15 ++++++++++++++- .../com/annimon/ownlang/parser/Parser.java | 18 +++++++++++++++++- .../parser/ast/ContainerAccessExpression.java | 3 +++ src/test/resources/other/stringFunctions.own | 10 ++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java create mode 100644 src/test/resources/other/stringFunctions.own diff --git a/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java b/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java new file mode 100644 index 00000000..4156c59e --- /dev/null +++ b/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.exceptions; + +public final class UnknownPropertyException extends RuntimeException { + + private final String propertyName; + + public UnknownPropertyException(String name) { + super("Unknown property " + name); + this.propertyName = name; + } + + public String getPropertyName() { + return propertyName; + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/StringValue.java b/src/main/java/com/annimon/ownlang/lib/StringValue.java index 8a1ac225..f665d044 100644 --- a/src/main/java/com/annimon/ownlang/lib/StringValue.java +++ b/src/main/java/com/annimon/ownlang/lib/StringValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.UnknownPropertyException; import java.util.Objects; /** @@ -15,6 +16,19 @@ public final class StringValue implements Value { public StringValue(String value) { this.value = value; } + + public Value access(Value property) { + switch (property.asString()) { + // Properties + case "length": + return NumberValue.of(length()); + + // Functions + case "trim": + return new FunctionValue(args -> new StringValue(value.trim())); + } + throw new UnknownPropertyException(property.asString()); + } public int length() { return value.length(); @@ -82,5 +96,4 @@ public int compareTo(Value o) { public String toString() { return asString(); } - } diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 2f5a270c..58b35aec 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -782,7 +782,23 @@ private Expression value() { return new ValueExpression(createNumber(current.getText(), 16)); } if (match(TokenType.TEXT)) { - return new ValueExpression(current.getText()); + final ValueExpression strExpr = new ValueExpression(current.getText()); + // "text".property || "text".func() + if (lookMatch(0, TokenType.DOT)) { + if (lookMatch(1, TokenType.WORD) && lookMatch(2, TokenType.LPAREN)) { + match(TokenType.DOT); + return functionChain(new ContainerAccessExpression( + strExpr, Collections.singletonList( + new ValueExpression(consume(TokenType.WORD).getText()) + ))); + } + final List indices = variableSuffix(); + if (indices.isEmpty()) { + return strExpr; + } + return new ContainerAccessExpression(strExpr, indices); + } + return strExpr; } throw new ParseException("Unknown expression: " + current); } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 794b8429..27bb6763 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -48,6 +48,9 @@ public Value get() { case Types.MAP: return ((MapValue) container).get(lastIndex); + + case Types.STRING: + return ((StringValue) container).access(lastIndex); default: throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); diff --git a/src/test/resources/other/stringFunctions.own b/src/test/resources/other/stringFunctions.own new file mode 100644 index 00000000..154f7688 --- /dev/null +++ b/src/test/resources/other/stringFunctions.own @@ -0,0 +1,10 @@ +def testLength() { + assertEquals(3, "123".length) + s = "test" + assertEquals(4, s.length) +} + +def testTrim() { + s = " test " + assertEquals("test", s.trim()) +} \ No newline at end of file From 0e2b92bfacd88a65d9530e3cc46708f835dc6916 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Jan 2019 22:23:28 +0200 Subject: [PATCH 230/448] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9?= =?UTF-8?q?-=D1=80=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5=D0=BD=D0=B8=D0=B9?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=81=D1=82=D1=80=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/StringValue.java | 19 ++++++++++++++++--- src/test/resources/other/stringFunctions.own | 11 +++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/StringValue.java b/src/main/java/com/annimon/ownlang/lib/StringValue.java index f665d044..2f52da80 100644 --- a/src/main/java/com/annimon/ownlang/lib/StringValue.java +++ b/src/main/java/com/annimon/ownlang/lib/StringValue.java @@ -17,8 +17,9 @@ public StringValue(String value) { this.value = value; } - public Value access(Value property) { - switch (property.asString()) { + public Value access(Value propertyValue) { + final String prop = propertyValue.asString(); + switch (prop) { // Properties case "length": return NumberValue.of(length()); @@ -26,8 +27,20 @@ public Value access(Value property) { // Functions case "trim": return new FunctionValue(args -> new StringValue(value.trim())); + + default: + if (Functions.isExists(prop)) { + final Function f = Functions.get(prop); + return new FunctionValue(args -> { + final Value[] newArgs = new Value[args.length + 1]; + newArgs[0] = this; + System.arraycopy(args, 0, newArgs, 1, args.length); + return f.execute(newArgs); + }); + } + break; } - throw new UnknownPropertyException(property.asString()); + throw new UnknownPropertyException(prop); } public int length() { diff --git a/src/test/resources/other/stringFunctions.own b/src/test/resources/other/stringFunctions.own index 154f7688..3e2386e7 100644 --- a/src/test/resources/other/stringFunctions.own +++ b/src/test/resources/other/stringFunctions.own @@ -7,4 +7,15 @@ def testLength() { def testTrim() { s = " test " assertEquals("test", s.trim()) +} + +def testExtensionFunction() { + use "std" + s = "1es1" + assertEquals("test", s.replace("1", "t")) +} + +def testExtensionCustomFunction() { + def repeat(str, num) = str * num + assertEquals("****", "*".repeat(4)) } \ No newline at end of file From 748c7ee45e427a97cdf98265f862aa38ead3fef4 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Jan 2019 22:57:07 +0200 Subject: [PATCH 231/448] =?UTF-8?q?std::try=20=D1=82=D0=B5=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D1=8C=20=D0=BC=D0=BE=D0=B6=D0=B5=D1=82=20=D1=81=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D1=83=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0=D1=89?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B8=D0=B7=20catch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit try(def() = parseInt("oops"), 42) --- .../annimon/ownlang/modules/std/std_try.java | 17 +++++++++++------ src/test/resources/modules/std/try.own | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/modules/std/try.own 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 c748b8f2..4ae987bd 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 @@ -14,12 +14,17 @@ public Value execute(Value... args) { try { return ((FunctionValue) args[0]).getValue().execute(); } catch (Exception ex) { - if (args.length == 2 && args[1].type() == Types.FUNCTION) { - final String message = ex.getMessage(); - final Function catchFunction = ((FunctionValue) args[1]).getValue(); - return catchFunction.execute( - new StringValue(ex.getClass().getName()), - new StringValue(message == null ? "" : message)); + if (args.length == 2) { + switch (args[1].type()) { + case Types.FUNCTION: + final String message = ex.getMessage(); + final Function catchFunction = ((FunctionValue) args[1]).getValue(); + return catchFunction.execute( + new StringValue(ex.getClass().getName()), + new StringValue(message == null ? "" : message)); + default: + return args[1]; + } } return NumberValue.MINUS_ONE; } diff --git a/src/test/resources/modules/std/try.own b/src/test/resources/modules/std/try.own new file mode 100644 index 00000000..8a5ba12b --- /dev/null +++ b/src/test/resources/modules/std/try.own @@ -0,0 +1,16 @@ +use "std" + +def testTryOnly() { + assertEquals(1, try(def() = 1)) + assertEquals(-1, try(def() = parseInt("oops"))) +} + +def testCatchFunction() { + actual = try(def() = parseInt("oops"), def(class, cause) = class) + assertEquals("java.lang.NumberFormatException", actual) +} + +def testCatchValue() { + actual = try(def() = parseInt("oops"), 42) + assertEquals(42, actual) +} \ No newline at end of file From 4eabeca2aca772fc566565920c4df2f2fab1ea07 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Jan 2019 23:40:22 +0200 Subject: [PATCH 232/448] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=BE=D1=88=D0=B8?= =?UTF-8?q?=D0=B1=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/lib/UserDefinedFunction.java | 6 ++++-- .../ownlang/parser/ast/FunctionalExpression.java | 16 ++++++++++++---- .../annimon/ownlang/parser/ast/UseStatement.java | 11 ++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index ab954c3d..965be46f 100644 --- a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -34,11 +34,13 @@ public Value execute(Value... values) { final int size = values.length; final int requiredArgsCount = arguments.getRequiredArgumentsCount(); if (size < requiredArgsCount) { - throw new ArgumentsMismatchException(String.format("Arguments count mismatch. %d < %d", size, requiredArgsCount)); + throw new ArgumentsMismatchException(String.format( + "Arguments count mismatch. Required %d, got %d", requiredArgsCount, size)); } final int totalArgsCount = getArgsCount(); if (size > totalArgsCount) { - throw new ArgumentsMismatchException(String.format("Arguments count mismatch. %d > %d", size, totalArgsCount)); + throw new ArgumentsMismatchException(String.format( + "Arguments count mismatch. Total %d, got %d", totalArgsCount, size)); } try { diff --git a/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 672f3f10..103ca473 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; import com.annimon.ownlang.exceptions.UnknownFunctionException; import com.annimon.ownlang.lib.*; @@ -40,9 +42,13 @@ public Value eval() { } final Function f = consumeFunction(functionExpr); CallStack.enter(functionExpr.toString(), f); - final Value result = f.execute(values); - CallStack.exit(); - return result; + try { + return f.execute(values); + } catch (ArgumentsMismatchException | TypeException | VariableDoesNotExistsException ex) { + throw new RuntimeException(ex.getMessage() + " in function " + functionExpr, ex); + } finally { + CallStack.exit(); + } } private Function consumeFunction(Expression expr) { @@ -58,7 +64,9 @@ private Function consumeFunction(Expression expr) { } private Function getFunction(String key) { - if (Functions.isExists(key)) return Functions.get(key); + if (Functions.isExists(key)) { + return Functions.get(key); + } if (Variables.isExists(key)) { final Value variable = Variables.get(key); if (variable.type() == Types.FUNCTION) { diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 568b63b6..087f03ea 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -36,7 +36,7 @@ public void execute() { loadModule(value.asString()); break; default: - throw new TypeException("Array or string required"); + throw typeException(value); } } @@ -45,7 +45,7 @@ private void loadModule(String name) { final Module module = (Module) Class.forName(String.format(PACKAGE, name, name)).newInstance(); module.init(); } catch (Exception ex) { - throw new RuntimeException(ex); + throw new RuntimeException("Unable to load module " + name, ex); } } @@ -61,10 +61,15 @@ public void loadConstants() { loadConstants(value.asString()); break; default: - throw new TypeException("Array or string required"); + throw typeException(value); } } + private TypeException typeException(Value value) { + return new TypeException("Array or string required in 'use' statement, " + + "got " + Types.typeToString(value.type()) + " " + value); + } + private void loadConstants(String moduleName) { try { final Class moduleClass = Class.forName(String.format(PACKAGE, moduleName, moduleName)); From 5cada737b506600eca8923e097d1ba9134968976 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 5 Jan 2019 20:37:15 +0200 Subject: [PATCH 233/448] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Parser.java | 32 ++++++++++++++----- .../parser/ast/FunctionalExpression.java | 6 ++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 58b35aec..23b2a218 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -219,12 +219,15 @@ private Statement doWhileStatement() { private Statement forStatement() { int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0; - if (lookMatch(foreachIndex, TokenType.WORD) && lookMatch(foreachIndex + 1, TokenType.COLON)) { + if (lookMatch(foreachIndex, TokenType.WORD) + && lookMatch(foreachIndex + 1, TokenType.COLON)) { // for v : arr || for (v : arr) return foreachArrayStatement(); } - if (lookMatch(foreachIndex, TokenType.WORD) && lookMatch(foreachIndex + 1, TokenType.COMMA) - && lookMatch(foreachIndex + 2, TokenType.WORD) && lookMatch(foreachIndex + 3, TokenType.COLON)) { + if (lookMatch(foreachIndex, TokenType.WORD) + && lookMatch(foreachIndex + 1, TokenType.COMMA) + && lookMatch(foreachIndex + 2, TokenType.WORD) + && lookMatch(foreachIndex + 3, TokenType.COLON)) { // for key, value : arr || for (key, value : arr) return foreachMapStatement(); } @@ -242,23 +245,29 @@ && lookMatch(foreachIndex + 2, TokenType.WORD) && lookMatch(foreachIndex + 3, To } private ForeachArrayStatement foreachArrayStatement() { + // for x : arr boolean optParentheses = match(TokenType.LPAREN); final String variable = consume(TokenType.WORD).getText(); consume(TokenType.COLON); final Expression container = expression(); - if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses + if (optParentheses) { + consume(TokenType.RPAREN); // close opt parentheses + } final Statement statement = statementOrBlock(); return new ForeachArrayStatement(variable, container, statement); } private ForeachMapStatement foreachMapStatement() { + // for k, v : map boolean optParentheses = match(TokenType.LPAREN); final String key = consume(TokenType.WORD).getText(); consume(TokenType.COMMA); final String value = consume(TokenType.WORD).getText(); consume(TokenType.COLON); final Expression container = expression(); - if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses + if (optParentheses) { + consume(TokenType.RPAREN); // close opt parentheses + } final Statement statement = statementOrBlock(); return new ForeachMapStatement(key, value, container, statement); } @@ -440,13 +449,14 @@ private Expression assignment() { } private Expression assignmentStrict() { + // x[0].prop += ... final int position = pos; final Expression targetExpr = qualifiedName(); if (!(targetExpr instanceof Accessible)) { pos = position; return null; } - + final TokenType currentType = get(0).getType(); if (!ASSIGN_OPERATORS.containsKey(currentType)) { pos = position; @@ -696,6 +706,7 @@ private Expression primary() { } if (match(TokenType.COLONCOLON)) { + // ::method reference final String functionName = consume(TokenType.WORD).getText(); return new FunctionReferenceExpression(functionName); } @@ -703,6 +714,7 @@ private Expression primary() { return match(); } if (match(TokenType.DEF)) { + // anonymous function def(args) ... final Arguments arguments = arguments(); final Statement statement = statementBody(); return new ValueExpression(new UserDefinedFunction(arguments, statement)); @@ -818,14 +830,18 @@ private Number createNumber(String text, int radix) { private Token consume(TokenType type) { final Token current = get(0); - if (type != current.getType()) throw new ParseException("Token " + current + " doesn't match " + type); + if (type != current.getType()) { + throw new ParseException("Token " + current + " doesn't match " + type); + } pos++; return current; } private boolean match(TokenType type) { final Token current = get(0); - if (type != current.getType()) return false; + if (type != current.getType()) { + return false; + } pos++; return true; } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 103ca473..eb4c5c43 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -43,11 +43,11 @@ public Value eval() { final Function f = consumeFunction(functionExpr); CallStack.enter(functionExpr.toString(), f); try { - return f.execute(values); + final Value result = f.execute(values); + CallStack.exit(); + return result; } catch (ArgumentsMismatchException | TypeException | VariableDoesNotExistsException ex) { throw new RuntimeException(ex.getMessage() + " in function " + functionExpr, ex); - } finally { - CallStack.exit(); } } From 62525e08e9799378efec2afcd49347974b06e478 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 5 Jan 2019 23:09:35 +0200 Subject: [PATCH 234/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20?= =?UTF-8?q?=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Converters.java | 8 +++++ .../com/annimon/ownlang/lib/StringValue.java | 32 +++++++++++++++-- src/test/resources/other/stringFunctions.own | 35 +++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java index fbef1d57..51afbc9d 100644 --- a/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import java.util.function.Predicate; import static com.annimon.ownlang.lib.ValueUtils.getFloatNumber; /** @@ -192,4 +193,11 @@ public static FunctionValue stringToVoid(StringToVoidFunction f) { return NumberValue.ZERO; }); } + + public static FunctionValue stringToBoolean(Predicate f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + return NumberValue.fromBoolean(f.test(args[0].asString())); + }); + } } diff --git a/src/main/java/com/annimon/ownlang/lib/StringValue.java b/src/main/java/com/annimon/ownlang/lib/StringValue.java index 2f52da80..91ca4905 100644 --- a/src/main/java/com/annimon/ownlang/lib/StringValue.java +++ b/src/main/java/com/annimon/ownlang/lib/StringValue.java @@ -23,10 +23,38 @@ public Value access(Value propertyValue) { // Properties case "length": return NumberValue.of(length()); + case "lower": + return new StringValue(value.toLowerCase()); + case "upper": + return new StringValue(value.toUpperCase()); + case "chars": { + final Value[] chars = new Value[length()]; + int i = 0; + for (char ch : value.toCharArray()) { + chars[i++] = NumberValue.of((int) ch); + } + return new ArrayValue(chars); + } // Functions case "trim": - return new FunctionValue(args -> new StringValue(value.trim())); + return Converters.voidToString(value::trim); + case "startsWith": + return new FunctionValue(args -> { + Arguments.checkOrOr(1, 2, args.length); + int offset = (args.length == 2) ? args[1].asInt() : 0; + return NumberValue.fromBoolean(value.startsWith(args[0].asString(), offset)); + }); + case "endsWith": + return Converters.stringToBoolean(value::endsWith); + case "matches": + return Converters.stringToBoolean(value::matches); + case "contains": + return Converters.stringToBoolean(value::contains); + case "equalsIgnoreCase": + return Converters.stringToBoolean(value::equalsIgnoreCase); + case "isEmpty": + return Converters.voidToBoolean(value::isEmpty); default: if (Functions.isExists(prop)) { @@ -42,7 +70,7 @@ public Value access(Value propertyValue) { } throw new UnknownPropertyException(prop); } - + public int length() { return value.length(); } diff --git a/src/test/resources/other/stringFunctions.own b/src/test/resources/other/stringFunctions.own index 3e2386e7..7e0e60a3 100644 --- a/src/test/resources/other/stringFunctions.own +++ b/src/test/resources/other/stringFunctions.own @@ -9,6 +9,41 @@ def testTrim() { assertEquals("test", s.trim()) } +def testLower() { + s = "TeST" + assertEquals("test", s.lower) +} + +def testUpper() { + s = "Test" + assertEquals("TEST", s.upper) +} + +def testChars() { + assertEquals([49, 50, 51, 52], "1234".chars) +} + +def testStartsWith() { + s = "test" + assertTrue(s.startsWith("tes")) + assertTrue(s.startsWith("t")) + assertFalse(s.startsWith("s")) +} + +def testEndsWith() { + s = "test" + assertTrue(s.endsWith("st")) + assertTrue(s.endsWith("t")) + assertFalse(s.endsWith("s")) +} + +def testIsEmpty() { + assertFalse(" ".isEmpty()) + assertTrue(" ".trim().isEmpty()) + assertFalse("1234".isEmpty()) + assertTrue("".isEmpty()) +} + def testExtensionFunction() { use "std" s = "1es1" From 2a94fc1759047ac8ce141d6660b528afc390aa28 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 6 Jan 2019 16:46:38 +0200 Subject: [PATCH 235/448] =?UTF-8?q?=D0=A0=D0=B5=D0=BB=D0=B8=D0=B7=201.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ec9d0100..3ed127c9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,20 @@ # OwnLang [![Build Status](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial.svg?branch=latest)](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial) -[![SonarCloud Status](https://sonarcloud.io/api/project_badges/measure?project=aNNiMON_Own-Programming-Language-Tutorial&metric=alert_status)](https://sonarcloud.io/dashboard?id=aNNiMON_Own-Programming-Language-Tutorial) -[![SonarCloud Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=aNNiMON_Own-Programming-Language-Tutorial&metric=sqale_rating)](https://sonarcloud.io/dashboard/?id=aNNiMON_Own-Programming-Language-Tutorial) + +OwnLang - dynamic functional programming language inspired by Scala and Python. Available for PC, Android and Java ME devices. + +## Installing | Free | Pro | Desktop | | :--: | :-: | :-----: | -| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.3.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.3.0) +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.4.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.4.0) -OwnLang - dynamic functional programming language inspired by Scala and Python. Available for PC, Android and Java ME devices. +Also available as AUR package: + +``` +yay -S ownlang +``` ## Key features @@ -61,8 +67,7 @@ def fizzbuzz(limit = 100) { Operate data in functional style. ```scala -use "std" -use "functional" +use ["std", "functional"] nums = [1,2,3,4,5,6,7,8,9,10] nums = filter(nums, def(x) = x % 2 == 0) @@ -72,6 +77,11 @@ foreach(squares, ::echo) // Sum of squares sum = reduce(squares, 0, def(x, y) = x + y) println "Sum: " + sum +// Same using stream +println "Sum: " + stream(range(1, 11)) + .filter(def(x) = x % 2 == 0) + .map(def(x) = x * x) + .reduce(0, def(x, y) = x + y) ``` #### Operator overloading @@ -79,11 +89,9 @@ println "Sum: " + sum Why not? ```scala -use "std" -use "types" -use "math" +use ["std", "types", "math"] -def `..`(a, b) = range(a, b - 1) +def `..`(a, b) = range(a, b) def `**`(a, b) = int(pow(a, b)) for y : 1 .. 10 { println sprintf("2 ^ %d = %d", y, 2 ** y) @@ -130,8 +138,6 @@ def patch_callback(v) { Build using Gradle `./gradlew dist` -or Ant `ant clean pack` - or take a look to [latest release](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/latest) for binaries. From 8a000cd6c25503a4e9bd287c532d9171a3c476ec Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 6 Jan 2019 18:36:53 +0200 Subject: [PATCH 236/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/basics/extended_identifier.own | 4 ++-- examples/basics/operator_overloading.own | 6 ++---- examples/console/colors.own | 20 ++++++-------------- examples/forms/complicatedForm.own | 2 +- examples/forms/samobot_chat.own | 6 ++---- examples/functions/calculator.own | 10 +++++----- examples/functions/stream.own | 5 ++--- examples/network/github_timeline.own | 14 +++++++------- 8 files changed, 27 insertions(+), 40 deletions(-) diff --git a/examples/basics/extended_identifier.own b/examples/basics/extended_identifier.own index d4e3897e..7e6cfa32 100644 --- a/examples/basics/extended_identifier.own +++ b/examples/basics/extended_identifier.own @@ -1,5 +1,5 @@ -`extended indetifier variable` = 9 -println `extended indetifier variable` +`extended identifier variable` = 9 +println `extended identifier variable` `(。◕‿◕。)` = 20 `ʕ•ᴥ•ʔ` = 30 diff --git a/examples/basics/operator_overloading.own b/examples/basics/operator_overloading.own index 9d3f0e16..2ea1ac00 100644 --- a/examples/basics/operator_overloading.own +++ b/examples/basics/operator_overloading.own @@ -1,6 +1,4 @@ -use "std" -use "types" -use "math" +use ["std", "types", "math"] println "Operator overloading" def `::`(v1, v2) = string(v1) + string(v2) @@ -11,7 +9,7 @@ def `^`(v1, v2) = pow(v1[0], v2[0]) print "[2] ^ [7] = " println [2] ^ [7] -def `..`(a, b) = range(a, b - 1) +def `..`(a, b) = range(a, b) def `**`(a, b) = int(pow(a, b)) for y : 1 .. 10 { println sprintf("2 ^ %d = %d", y, 2 ** y) diff --git a/examples/console/colors.own b/examples/console/colors.own index f6a832a9..b530ad89 100644 --- a/examples/console/colors.own +++ b/examples/console/colors.own @@ -1,8 +1,12 @@ use "std" -for b : range(8) + +// header +print " " * 4 +for b : range(9) print sprintf(" 4%dm ", b) println "" -for f : range(30, 38) { + +for f : range(30, 39) { for s : ["", "1;"] { print sprintf("%4sm", s+f) print sprintf(" \u001B[%sm%s\u001B[0m", s+f, "gYw ") @@ -12,15 +16,3 @@ for f : range(30, 38) { } } -/*use "functional" -stream(range(30, 38)) - .flatMap(def(f) = [[f, ""], [f, "1;"]]) - .forEach(def(a) { - extract(f, s) = a - print sprintf("%4sm", s+f) - print sprintf(" \u001B[%sm%s\u001B[0m", s+f, "gYw ") - for b : range(8) - print sprintf(" \u001B[4%s;%sm%s\u001B[0m", b, s+f, " gYw ") - println "" - }) -*/ diff --git a/examples/forms/complicatedForm.own b/examples/forms/complicatedForm.own index 7796b588..fce462b4 100644 --- a/examples/forms/complicatedForm.own +++ b/examples/forms/complicatedForm.own @@ -13,7 +13,7 @@ enterTextLabel = newLabel("Enter a text", SwingConstants.CENTER) textField = newTextField() textField.addKeyListener(def(type, event) { - lengthLabel.setText(length(textField.getText())) + lengthLabel.setText(textField.getText().length) }) statusPanel = newPanel() diff --git a/examples/forms/samobot_chat.own b/examples/forms/samobot_chat.own index 5f37748f..2e4515a6 100644 --- a/examples/forms/samobot_chat.own +++ b/examples/forms/samobot_chat.own @@ -1,6 +1,4 @@ -use "std" -use "http" -use "forms" +use ["std", "http", "forms"] chatHistory = newLabel("Чат с самоботом
") messageField = newTextField() @@ -13,7 +11,7 @@ def onSend() { if (length(text) == 0) return 0 messageField.setText("") chatHistory.setText(chatHistory.getText() + "
вы > " + text) - thread(::http, "http://annimon.com/json/bot.php", "POST", {"text": text}, def(answer) { + thread(::http, "https://annimon.com/json/bot.php", "POST", {"text": text}, def(answer) { chatHistory.setText(chatHistory.getText() + "
бот > " + answer) }) } diff --git a/examples/functions/calculator.own b/examples/functions/calculator.own index 571cbe88..9e6355d2 100644 --- a/examples/functions/calculator.own +++ b/examples/functions/calculator.own @@ -20,18 +20,18 @@ def calculate(expression) { def parseNumber() { buffer = "" - while (pos < len && isDigit(charAt(expression, pos))) { - buffer += toChar(charAt(expression, pos)) + while (pos < len && isDigit(expression.charAt(pos))) { + buffer += toChar(expression.charAt(pos)) pos++ } return number(buffer) } def parseOperation() { - while (pos < len && !arrayKeyExists(toChar(charAt(expression, pos)), operations)) { + while (pos < len && !arrayKeyExists(toChar(expression.charAt(pos)), operations)) { pos++ } - return operations[toChar(charAt(expression, pos++))] + return operations[toChar(expression.charAt(pos++))] } num1 = parseNumber() @@ -43,4 +43,4 @@ def calculate(expression) { println calculate("2+2") println calculate("400*16") println calculate("400/160") -println calculate("3>4") \ No newline at end of file +println calculate("3>4") diff --git a/examples/functions/stream.own b/examples/functions/stream.own index 2deb5cc6..d654635e 100644 --- a/examples/functions/stream.own +++ b/examples/functions/stream.own @@ -1,5 +1,4 @@ -use "std" -use "functional" +use ["std", "functional"] println "x, square(x), cube(x) for even numbers" data = [1,2,3,4,5,6,7,8,9] @@ -23,4 +22,4 @@ def reverse(container) { result[size - i - 1] = container[i] } return result -} \ No newline at end of file +} diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own index 79efbebe..d6d78e2a 100644 --- a/examples/network/github_timeline.own +++ b/examples/network/github_timeline.own @@ -1,12 +1,9 @@ -use "std" -use "http" -use "json" -use "functional" +use ["std", "http", "json", "functional", "date"] header = "* Prints current GitHub timeline *" -println "*" * length(header) +println "*" * header.length println header -println "*" * length(header) +println "*" * header.length // Executes in main thread //http("https://api.github.com/events", def(r) { @@ -19,7 +16,7 @@ thread(::http, "https://api.github.com/events", def(r) { }) def show_github_events(event) { - println event.created_at + println event.created_at.formatTzDate() println "User: https://github.com/" + event.actor.login println github_event_type(event) println "-" * 50 @@ -41,3 +38,6 @@ def github_event_type(event) { case type : type + " on " + repo } } + +def formatTzDate(str) = formatDate(parseTzDate(str), newFormat("yyyy-MM-dd HH:mm:ss")) +def parseTzDate(str) = parseDate(str, newFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")) From 3a21089d4e27793b4eb0d72b4cb9cdb20798d6a9 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 13 Jan 2019 21:52:07 +0200 Subject: [PATCH 237/448] =?UTF-8?q?=D0=A1=D0=BE=D0=B2=D0=BC=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8C=20=D1=81=20Android-?= =?UTF-8?q?=D0=B2=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 51afbc9d..3c144352 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 interface VoidToIntFunction { int apply(); } + public interface VoidToLongFunction { + long apply(); + } + public interface VoidToFloatFunction { float apply(); } @@ -28,6 +32,10 @@ public interface VoidToDoubleFunction { double apply(); } + public interface VoidToCharSequenceFunction { + CharSequence apply(); + } + public interface VoidToStringFunction { String apply(); } @@ -44,6 +52,14 @@ public interface IntToVoidFunction { 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 interface Double4ToVoidFunction { 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 static FunctionValue voidToInt(VoidToIntFunction f) { 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 static FunctionValue voidToFloat(VoidToFloatFunction f) { 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 static FunctionValue intToVoid(IntToVoidFunction f) { }); } + 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 static FunctionValue double4ToVoid(Double4ToVoidFunction f) { }); } + 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 5d051bc7..de5803de 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 static Function consumeFunction(Value value, int argumentNumber) { } 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 fce2bc8e..51ce0317 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 Value execute(Value... args) { 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 5f4dc900..254720fa 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 Value execute(Value... args) { 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 c4749ce2..6752e805 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 functional_filter(boolean takeWhile) { @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 @@ private Value filterMap(MapValue map, Function predicate, boolean takeWhile) { } 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 9016e69d..50979548 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.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 Value execute(Value... args) { 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 a128066a..dbf429ab 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.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 5e87598f..7ce988b2 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 f600bb9d..05d5948b 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 @@ private Value sorted(Value... args) { 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 @@ private Value sorted(Value... args) { 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 b1677a87..0cbef55c 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 Value execute(Value... args) { } 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 Value execute(Value... args) { } } -} \ 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 4ae987bd..fb3a4530 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 Value execute(Value... args) { } } -} \ 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 2a644771..1884f920 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 List tokenize() { else if (current == '"') tokenizeText(); else if (current == '#') { next(); - tokenizeHexNumber(); + tokenizeHexNumber(1); } else if (OPERATOR_CHARS.indexOf(current) != -1) { tokenizeOperator(); @@ -155,7 +155,7 @@ private void tokenizeNumber() { if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { next(); next(); - tokenizeHexNumber(); + tokenizeHexNumber(2); return; } while (true) { @@ -170,7 +170,7 @@ private void tokenizeNumber() { 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 @@ private void tokenizeHexNumber() { } 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 23b2a218..ce16bfaa 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 @@ private Expression functionChain(Expression qualifiedNameExpr) { } 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 @@ private Expression assignmentStrict() { // 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 @@ private Expression qualifiedName() { 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 @@ private Expression qualifiedName() { 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 @@ private Expression value() { ))); } final List indices = variableSuffix(); - if (indices.isEmpty()) { + if (indices == null || indices.isEmpty()) { return strExpr; } return new ContainerAccessExpression(strExpr, indices); From 2d0b49bf84c3409d01508b203cb58dd3ded3a818 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 13 Jan 2019 21:52:26 +0200 Subject: [PATCH 238/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/database/hsqldb.own | 17 +++ examples/database/sqlite.own | 19 +++ examples/game/pipes_online.own | 176 +++++++++++++++++++++++++++ examples/network/github_timeline.own | 8 +- 4 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 examples/database/hsqldb.own create mode 100644 examples/database/sqlite.own create mode 100644 examples/game/pipes_online.own diff --git a/examples/database/hsqldb.own b/examples/database/hsqldb.own new file mode 100644 index 00000000..47fb10aa --- /dev/null +++ b/examples/database/hsqldb.own @@ -0,0 +1,17 @@ +use ["std", "jdbc"] + +connection = getConnection("jdbc:hsqldb:file:hsql.db", "", "", "org.hsqldb.jdbcDriver") +statement = connection.createStatement() +statement.executeUpdate("drop table if exists cities") +statement.executeUpdate("CREATE TABLE cities (id IDENTITY, name VARCHAR(32))") +statement.executeUpdate("INSERT INTO cities (name) VALUES('Киев')") +statement.executeUpdate("INSERT INTO cities (name) VALUES('Минск')") +statement.executeUpdate("INSERT INTO cities (name) VALUES('Москва')") + +rs = statement.executeQuery("SELECT id, name FROM cities") +while(rs.next()) { + // read the result set + println "name = " + rs.getString("name") + println "id = " + rs.getInt("id") +} +statement.execute("SHUTDOWN") diff --git a/examples/database/sqlite.own b/examples/database/sqlite.own new file mode 100644 index 00000000..670d818e --- /dev/null +++ b/examples/database/sqlite.own @@ -0,0 +1,19 @@ +use ["std", "jdbc"] + +// Example from https://github.com/xerial/sqlite-jdbc + +connection = getConnection("jdbc:sqlite:sample.db") +statement = connection.createStatement() +statement.setQueryTimeout(30) // set timeout to 30 sec. + +statement.executeUpdate("drop table if exists person") +statement.executeUpdate("create table person (id integer, name string)") +statement.executeUpdate("insert into person values(1, 'leo')") +statement.executeUpdate("insert into person values(2, 'yui')") + +rs = statement.executeQuery("select * from person") +while(rs.next()) { + // read the result set + println "name = " + rs.getString("name") + println "id = " + rs.getInt("id") +} diff --git a/examples/game/pipes_online.own b/examples/game/pipes_online.own new file mode 100644 index 00000000..396744e6 --- /dev/null +++ b/examples/game/pipes_online.own @@ -0,0 +1,176 @@ +use "std" +use "canvas" +use "socket" + +/// --- PIPES CELL --- +CELL_START = 0 +HORIZONTAL = 0 +VERTICAL = 1 +LEFT_TO_DOWN = 2 +LEFT_TO_UP = 3 +RIGHT_TO_UP = 4 +RIGHT_TO_DOWN = 5 +CROSS = 6 +CELL_LAST = 6 + +Cells = [ + {"index": HORIZONTAL, "next": VERTICAL}, + {"index": VERTICAL, "next": HORIZONTAL}, + {"index": LEFT_TO_DOWN, "next": LEFT_TO_UP}, + {"index": LEFT_TO_UP, "next": RIGHT_TO_UP}, + {"index": RIGHT_TO_UP, "next": RIGHT_TO_DOWN}, + {"index": RIGHT_TO_DOWN, "next": LEFT_TO_DOWN}, + {"index": CROSS, "next": CROSS} +]; + + +def draw(v, cellSize) { + c2 = cellSize / 2 + match v { + case HORIZONTAL : fillRect(0, c2 - 2, cellSize, 4) + case VERTICAL : fillRect(c2 - 2, 0, 4, cellSize) + case LEFT_TO_DOWN : { + fillRect(0, c2 - 2, c2, 4) + fillRect(c2 - 2, c2 - 2, 4, c2 + 2) + } + case LEFT_TO_UP : { + fillRect(0, c2 - 2, c2, 4) + fillRect(c2 - 2, 0, 4, c2 + 2) + } + case RIGHT_TO_UP : { + fillRect(c2 - 2, c2 - 2, c2 + 2, 4) + fillRect(c2 - 2, 0, 4, c2 + 2) + } + case RIGHT_TO_DOWN : { + fillRect(c2 - 2, c2 - 2, c2 + 2, 4) + fillRect(c2 - 2, c2 - 2, 4, c2 + 2) + } + case CROSS : { + fillRect(c2 - 2, 0, 4, cellSize) + fillRect(0, c2 - 2, cellSize, 4) + } + } +} + + +/// --- PIPES BOARD --- +SIZE = 10 + +// Creating game board +board = newarray(SIZE, SIZE) +boardGhost = newarray(SIZE, SIZE) + +def switchCell(x, y) { + board[x][y] = Cells[board[x][y]].next +} +def setGhostCell(x, y) { + boardGhost[x][y] = Cells[boardGhost[x][y]].next +} + + +/// --- PIPES MAIN --- +translateX = 0 translateY = 0 +isGameFinished = false +isWin = false + +/* frect with translate ability */ +def fillRect(x,y,w,h) { + frect(translateX+x, translateY+y, w, h) +} + +WIDTH = 320 HEIGHT = 320 +WINDOW_WIDTH = WIDTH * 2 +window("Pipes Online", WINDOW_WIDTH, HEIGHT) +cellSize = WIDTH / SIZE + +// cursor +curX = 0 curY = 0 +curGhostX = 0 curGhostY = 0 + +// Initialize client +socket = newSocket("http://localhost:6469") +socket.on("gameStart", def(data) { + data = data[0] + for i=0, i 0) { + curX-- + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_RIGHT && curX < SIZE - 1) { + curX++ + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_UP && curY > 0) { + curY-- + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_DOWN && curY < SIZE - 1) { + curY++ + socket.emit("updateCursor", {"x": curX, "y": curY}) + } else if (key == VK_FIRE) { + switchCell(curX, curY) + socket.emit("switchCell", {"x": curX, "y": curY}) + } + else if (key == 48) run = 0 + } + + // background + color(isGameFinished ? (isWin ? #66FF66 : #FF6666) : #FFFFFF) + frect(0, 0, WIDTH, HEIGHT) + color(isGameFinished ? (!isWin ? #66FF66 : #FF6666) : #DDDDDD) + frect(WIDTH, 0, WIDTH, HEIGHT) + // cursor + color(#4444FF) + frect(curX*cellSize, curY*cellSize, cellSize, cellSize) + color(#4040DD) + frect(WIDTH + curGhostX*cellSize, curGhostY*cellSize, cellSize, cellSize) + for (i=0, i Date: Sun, 13 Jan 2019 21:58:07 +0200 Subject: [PATCH 239/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B4=D0=B5=D1=82=D0=B5=D0=BA=D1=82=D0=BE=D1=80?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=D0=B9=20=D0=BE=D1=82=20?= =?UTF-8?q?Android-=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/visitors/ModuleDetector.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java new file mode 100644 index 00000000..5d62f802 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -0,0 +1,39 @@ +package com.annimon.ownlang.parser.visitors; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.UseStatement; +import com.annimon.ownlang.parser.ast.ValueExpression; +import java.util.HashSet; +import java.util.Set; + +public class ModuleDetector extends AbstractVisitor { + + private Set modules; + + public ModuleDetector() { + modules = new HashSet<>(); + } + + public Set detect(Statement s) { + s.accept(this); + return modules; + } + + @Override + public void visit(UseStatement st) { + if (st.expression instanceof ValueExpression) { + ValueExpression ve = (ValueExpression) st.expression; + if (ve.value.type() == Types.ARRAY) { + for (Value module : ((ArrayValue) ve.value)) { + modules.add(module.asString()); + } + } else { + modules.add(ve.value.asString()); + } + } + super.visit(st); + } +} From e0f52cc3e3b6c099aa401cc0102790e38f55a01e Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Fri, 18 Jan 2019 01:28:15 +0200 Subject: [PATCH 240/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ed127c9..c6f4929b 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ def patch_callback(v) { ## Language specification -[Available on GitBook (English and Russian)](https://www.gitbook.com/book/annimon/ownlang/details) +[English](https://annimon.com/docs/ownlang/en/) and [Russian](https://annimon.com/docs/ownlang/ru/) [Examples](examples/) From 616afbc8c01df7ca1aa7fb0757b0436375309e1c Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 27 Mar 2019 18:24:01 +0200 Subject: [PATCH 241/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=20mysql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index 9518e7c0..e72cbdca 100644 --- a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -66,7 +66,7 @@ public void init() { initConstants(); Functions.set("getConnection", getConnectionFunction()); Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); - Functions.set("mysql", getConnectionFunction("jdbc:", () -> Class.forName("com.mysql.jdbc.Driver"))); + Functions.set("mysql", getConnectionFunction("jdbc:")); } private static com.annimon.ownlang.lib.Function getConnectionFunction() { From c73844fd51067a95a7f38fbdea2819db5ba4e989 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 4 Apr 2019 12:46:39 +0300 Subject: [PATCH 242/448] =?UTF-8?q?=D0=A1=D1=82=D1=80=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=B5=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=81=D1=82=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=B2=20=D1=87=D0=B8=D1=81=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/annimon/ownlang/lib/StringValue.java | 12 ++---------- .../java/com/annimon/ownlang/parser/ParserTest.java | 7 +++++-- .../ownlang/parser/ast/VariableExpressionTest.java | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/StringValue.java b/src/main/java/com/annimon/ownlang/lib/StringValue.java index 91ca4905..d58f59f1 100644 --- a/src/main/java/com/annimon/ownlang/lib/StringValue.java +++ b/src/main/java/com/annimon/ownlang/lib/StringValue.java @@ -87,20 +87,12 @@ public Object raw() { @Override public int asInt() { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return 0; - } + return Integer.parseInt(value); } @Override public double asNumber() { - try { - return Double.parseDouble(value); - } catch (NumberFormatException e) { - return 0; - } + return Double.parseDouble(value); } @Override diff --git a/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/src/test/java/com/annimon/ownlang/parser/ParserTest.java index 598a798b..9a473d50 100644 --- a/src/test/java/com/annimon/ownlang/parser/ParserTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ParserTest.java @@ -35,8 +35,11 @@ private static void assertEval(Value expectedValue, String input, Expression exp BlockStatement program = assertExpression(input, expected); program.execute(); final Value actual = Variables.get("a"); - assertEquals(expectedValue.asNumber(), actual.asNumber(), 0.001); - assertEquals(expectedValue.asString(), actual.asString()); + try { + assertEquals(expectedValue.asNumber(), actual.asNumber(), 0.001); + } catch (NumberFormatException nfe) { + assertEquals(expectedValue.asString(), actual.asString()); + } } private static BlockStatement assertExpression(String input, Expression expected) { diff --git a/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java index 14783684..36ccbde8 100644 --- a/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.ast; import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; import org.junit.Test; /** @@ -26,7 +27,7 @@ public void testVariableReplace() { assertValue(number(8), var("a").eval()); } - @Test(expected = RuntimeException.class) + @Test(expected = VariableDoesNotExistsException.class) public void testUnknownVariable() { var("a").eval(); } From 3f129a14b81052faf91f797f02affa34e49ade38 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 4 Apr 2019 12:48:43 +0300 Subject: [PATCH 243/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=BA=D0=B8=D1=85=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9=20=D1=81=D1=80=D0=B0=D0=B7=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/parser/ast/UseStatement.java | 21 ++++----- .../UseWithNonStringValueValidator.java | 43 +++++++++++-------- .../parser/optimization/VariablesGrabber.java | 32 +++++++------- .../parser/visitors/ModuleDetector.java | 19 ++++---- 4 files changed, 58 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 087f03ea..79f4e401 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -50,18 +50,15 @@ private void loadModule(String name) { } public void loadConstants() { - final Value value = expression.eval(); - switch (value.type()) { - case Types.ARRAY: - for (Value module : ((ArrayValue) value)) { - loadConstants(module.asString()); - } - break; - case Types.STRING: - loadConstants(value.asString()); - break; - default: - throw typeException(value); + if (expression instanceof ArrayExpression) { + ArrayExpression ae = (ArrayExpression) expression; + for (Expression expr : ae.elements) { + loadConstants(expr.eval().asString()); + } + } + if (expression instanceof ValueExpression) { + ValueExpression ve = (ValueExpression) expression; + loadConstants(ve.value.asString()); } } diff --git a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java index de1e51b9..451be324 100644 --- a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java +++ b/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.ast.*; @@ -17,28 +16,34 @@ public void visit(IncludeStatement st) { @Override public void visit(UseStatement st) { super.visit(st); - if (!(st.expression instanceof ValueExpression)) { + + if (st.expression instanceof ArrayExpression) { + ArrayExpression ae = (ArrayExpression) st.expression; + for (Expression expr : ae.elements) { + if (!checkExpression(expr)) { + return; + } + } + } else { + if (!checkExpression(st.expression)) { + return; + } + } + } + + private boolean checkExpression(Expression expr) { + if (!(expr instanceof ValueExpression)) { Console.error(String.format( - "Warning: `use` with %s, not ValueExpression", st.expression.getClass().getSimpleName())); - return; + "Warning: `use` with %s, not ValueExpression", expr.getClass().getSimpleName())); + return false; } - final Value value = ((ValueExpression) st.expression).value; - switch (value.type()) { - case Types.STRING: - // ok - break; - case Types.ARRAY: - // ok, need additional check - for (Value module : ((ArrayValue) value)) { - if (module.type() != Types.STRING) { - warnWrongType(module); - } - } - break; - default: - warnWrongType(value); + final Value value = ((ValueExpression) expr).value; + if (value.type() != Types.STRING) { + warnWrongType(value); + return false; } + return true; } private void warnWrongType(Value value) { diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 7ea484eb..1ee16362 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -2,20 +2,7 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; -import com.annimon.ownlang.parser.ast.Accessible; -import com.annimon.ownlang.parser.ast.Argument; -import com.annimon.ownlang.parser.ast.Arguments; -import com.annimon.ownlang.parser.ast.AssignmentExpression; -import com.annimon.ownlang.parser.ast.ContainerAccessExpression; -import com.annimon.ownlang.parser.ast.DestructuringAssignmentStatement; -import com.annimon.ownlang.parser.ast.ForeachArrayStatement; -import com.annimon.ownlang.parser.ast.ForeachMapStatement; -import com.annimon.ownlang.parser.ast.MatchExpression; -import com.annimon.ownlang.parser.ast.Node; -import com.annimon.ownlang.parser.ast.UnaryExpression; -import com.annimon.ownlang.parser.ast.UseStatement; -import com.annimon.ownlang.parser.ast.ValueExpression; -import com.annimon.ownlang.parser.ast.VariableExpression; +import com.annimon.ownlang.parser.ast.*; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isVariable; import java.util.HashMap; @@ -116,7 +103,7 @@ public Node visit(UseStatement s, Map t) { // To get module variables we need to store current variables, clear all, then load module. final Map currentVariables = new HashMap<>(Variables.variables()); Variables.variables().clear(); - if (isValue(s.expression)) { + if (canLoadConstants(s.expression)) { s.loadConstants(); } // Grab module variables @@ -131,6 +118,19 @@ public Node visit(UseStatement s, Map t) { return super.visit(s, t); } + private boolean canLoadConstants(Expression expression) { + if (expression instanceof ArrayExpression) { + ArrayExpression ae = (ArrayExpression) expression; + for (Expression expr : ae.elements) { + if (!isValue(expr)) { + return false; + } + } + return true; + } + return isValue(expression); + } + @Override protected boolean visit(Arguments in, Arguments out, Map t) { for (Argument argument : in) { @@ -159,4 +159,4 @@ private VariableInfo variableInfo(Map t, final String vari } return var; } -} \ No newline at end of file +} diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java index 5d62f802..98f291cb 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -1,8 +1,7 @@ package com.annimon.ownlang.parser.visitors; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.parser.ast.ArrayExpression; +import com.annimon.ownlang.parser.ast.Expression; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.UseStatement; import com.annimon.ownlang.parser.ast.ValueExpression; @@ -24,15 +23,15 @@ public Set detect(Statement s) { @Override public void visit(UseStatement st) { + if (st.expression instanceof ArrayExpression) { + ArrayExpression ae = (ArrayExpression) st.expression; + for (Expression expr : ae.elements) { + modules.add(expr.eval().asString()); + } + } if (st.expression instanceof ValueExpression) { ValueExpression ve = (ValueExpression) st.expression; - if (ve.value.type() == Types.ARRAY) { - for (Value module : ((ArrayValue) ve.value)) { - modules.add(module.asString()); - } - } else { - modules.add(ve.value.asString()); - } + modules.add(ve.value.asString()); } super.visit(st); } From 2d93c8b9a78d246f1d84d6e9f5446732f34131d0 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 4 Apr 2019 13:24:57 +0300 Subject: [PATCH 244/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=81=D0=B2=D0=BE=D0=B9=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8?= =?UTF-8?q?=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/ArrayValue.java | 31 ++++++++++++++----- .../parser/ast/ContainerAccessExpression.java | 3 +- .../annimon/ownlang/parser/ProgramsTest.java | 10 ++++++ src/test/resources/other/arrayFunctions.own | 26 ++++++++++++++++ 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/other/arrayFunctions.own diff --git a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java index 81a34486..cdd3b688 100644 --- a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.lib; import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.exceptions.UnknownPropertyException; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -78,29 +79,45 @@ public Value[] getCopyElements() { public int type() { return Types.ARRAY; } - + public int size() { return elements.length; } - + public Value get(int index) { return elements[index]; } - + + public Value get(Value index) { + final String prop = index.asString(); + switch (prop) { + // Properties + case "length": + return NumberValue.of(size()); + + // Functions + case "isEmpty": + return NumberValue.fromBoolean(size() == 0); + + default: + return get(index.asInt()); + } + } + public void set(int index, Value value) { elements[index] = value; } - + @Override public Object raw() { return elements; } - + @Override public int asInt() { throw new TypeException("Cannot cast array to integer"); } - + @Override public double asNumber() { throw new TypeException("Cannot cast array to number"); @@ -132,7 +149,7 @@ public boolean equals(Object obj) { final ArrayValue other = (ArrayValue) obj; return Arrays.deepEquals(this.elements, other.elements); } - + @Override public int compareTo(Value o) { if (o.type() == Types.ARRAY) { diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 27bb6763..1d5e915b 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -43,8 +43,7 @@ public Value get() { final Value lastIndex = lastIndex(); switch (container.type()) { case Types.ARRAY: - final int arrayIndex = lastIndex.asInt(); - return ((ArrayValue) container).get(arrayIndex); + return ((ArrayValue) container).get(lastIndex); case Types.MAP: return ((MapValue) container).get(lastIndex); diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index d1421228..0fcab099 100644 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Variables; @@ -83,6 +84,15 @@ public void initialize() { assertFalse(args[0].asInt() != 0); return NumberValue.ONE; }); + Functions.set("assertFail", (args) -> { + try { + ((FunctionValue) args[0]).getValue().execute(); + fail("Function should fail"); + } catch (Throwable thr) { + + } + return NumberValue.ONE; + }); } @Test diff --git a/src/test/resources/other/arrayFunctions.own b/src/test/resources/other/arrayFunctions.own new file mode 100644 index 00000000..da25e6a8 --- /dev/null +++ b/src/test/resources/other/arrayFunctions.own @@ -0,0 +1,26 @@ +def testSetUnknownKey() = assertFail(def() { + arr = [1, 2, 3] + arr.one = 1 + }) + +def testSetLengthProperty() = assertFail(def() { + arr = [1, 2, 3] + arr.length = 10 + }) + +def testGetLength() { + arr = [1, 2, 3] + assertEquals(3, arr.length) +} + +def testGetLengthInnerArray() { + arr = [[1, 2, 3], [1, 2, [3, 4, 5, 6]]] + assertEquals(2, arr.length) + assertEquals(3, arr[0].length) + assertEquals(4, arr[1][2].length) +} + +def testIsEmpty() { + arr = [1, 2, 3] + assertFalse(arr.isEmpty) +} \ No newline at end of file From 589fdbf0d5b254205c10702d0ee4bd47fa4f4a69 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 4 Apr 2019 17:53:59 +0300 Subject: [PATCH 245/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B8=D1=82=D0=B5=D1=80=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BA,=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D0=BE?= =?UTF-8?q?=D0=B2=20=D1=81=20=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=D0=BE?= =?UTF-8?q?=D0=BC=20=D0=B2=20for,=20=D0=B8=20functional::foreach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/Variables.java | 4 +- .../functional/functional_foreach.java | 66 +++++++++++----- .../parser/ast/ForeachArrayStatement.java | 64 ++++++++++++++-- .../parser/ast/ForeachMapStatement.java | 75 ++++++++++++++++--- .../resources/expressions/foreachKeyValue.own | 30 ++++++++ .../resources/expressions/foreachValue.own | 41 ++++++++++ .../resources/modules/functional/foreach.own | 36 +++++++++ 7 files changed, 275 insertions(+), 41 deletions(-) create mode 100644 src/test/resources/expressions/foreachKeyValue.own create mode 100644 src/test/resources/expressions/foreachValue.own create mode 100644 src/test/resources/modules/functional/foreach.own diff --git a/src/main/java/com/annimon/ownlang/lib/Variables.java b/src/main/java/com/annimon/ownlang/lib/Variables.java index 4c6f9514..4033fa33 100644 --- a/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -48,13 +48,13 @@ public static void clear() { scope.variables.put("false", NumberValue.ZERO); } - static void push() { + public static void push() { synchronized (lock) { scope = new Scope(scope); } } - static void pop() { + public static void pop() { synchronized (lock) { if (scope.parent != null) { scope = scope.parent; diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java index fc5117e1..fc743839 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java @@ -1,36 +1,62 @@ package com.annimon.ownlang.modules.functional; 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.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.ValueUtils; +import com.annimon.ownlang.lib.*; import java.util.Map; public final class functional_foreach implements Function { + private static final int UNKNOWN = -1; + @Override public Value execute(Value... args) { Arguments.check(2, args.length); final Value container = args[0]; final Function consumer = ValueUtils.consumeFunction(args[1], 1); - if (container.type() == Types.ARRAY) { - final ArrayValue array = (ArrayValue) container; - for (Value element : array) { - consumer.execute(element); - } - return array; + final int argsCount; + if (consumer instanceof UserDefinedFunction) { + argsCount = ((UserDefinedFunction) consumer).getArgsCount(); + } else { + argsCount = UNKNOWN; } - if (container.type() == Types.MAP) { - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - consumer.execute(element.getKey(), element.getValue()); - } - return map; + + switch (container.type()) { + case Types.STRING: + final StringValue string = (StringValue) container; + if (argsCount == 2) { + for (char ch : string.asString().toCharArray()) { + consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); + } + } else { + for (char ch : string.asString().toCharArray()) { + consumer.execute(new StringValue(String.valueOf(ch))); + } + } + return string; + + case Types.ARRAY: + final ArrayValue array = (ArrayValue) container; + if (argsCount == 2) { + int index = 0; + for (Value element : array) { + consumer.execute(element, NumberValue.of(index++)); + } + } else { + for (Value element : array) { + consumer.execute(element); + } + } + return array; + + case Types.MAP: + final MapValue map = (MapValue) container; + for (Map.Entry element : map) { + consumer.execute(element.getKey(), element.getValue()); + } + return map; + + default: + throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); } - throw new TypeException("Invalid first argument. Array or map expected"); } } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java index 640a7903..fbc5aa6c 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -1,7 +1,8 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.Map; /** * @@ -23,8 +24,45 @@ public ForeachArrayStatement(String variable, Expression container, Statement bo public void execute() { super.interruptionCheck(); final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; - final Iterable iterator = (Iterable) container.eval(); - for (Value value : iterator) { + + final Value containerValue = container.eval(); + switch (containerValue.type()) { + case Types.STRING: + iterateString(containerValue.asString()); + break; + case Types.ARRAY: + iterateArray((ArrayValue) containerValue); + break; + case Types.MAP: + iterateMap((MapValue) containerValue); + break; + default: + throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); + } + + // Restore variables + if (previousVariableValue != null) { + Variables.set(variable, previousVariableValue); + } else { + Variables.remove(variable); + } + } + + private void iterateString(String str) { + for (char ch : str.toCharArray()) { + Variables.set(variable, new StringValue(String.valueOf(ch))); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } + } + } + + private void iterateArray(ArrayValue containerValue) { + for (Value value : containerValue) { Variables.set(variable, value); try { body.execute(); @@ -34,9 +72,21 @@ public void execute() { // continue; } } - // Восстанавливаем переменную - if (previousVariableValue != null) { - Variables.set(variable, previousVariableValue); + } + + private void iterateMap(MapValue containerValue) { + for (Map.Entry entry : containerValue) { + Variables.set(variable, new ArrayValue(new Value[] { + entry.getKey(), + entry.getValue() + })); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } } } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java index f2805616..2a24393f 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; import java.util.Map; /** @@ -26,10 +26,39 @@ public void execute() { super.interruptionCheck(); final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; - final Iterable> iterator = (Iterable>) container.eval(); - for (Map.Entry entry : iterator) { - Variables.set(key, entry.getKey()); - Variables.set(value, entry.getValue()); + + final Value containerValue = container.eval(); + switch (containerValue.type()) { + case Types.STRING: + iterateString(containerValue.asString()); + break; + case Types.ARRAY: + iterateArray((ArrayValue) containerValue); + break; + case Types.MAP: + iterateMap((MapValue) containerValue); + break; + default: + throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); + } + + // Restore variables + if (previousVariableValue1 != null) { + Variables.set(key, previousVariableValue1); + } else { + Variables.remove(key); + } + if (previousVariableValue2 != null) { + Variables.set(value, previousVariableValue2); + } else { + Variables.remove(value); + } + } + + private void iterateString(String str) { + for (char ch : str.toCharArray()) { + Variables.set(key, new StringValue(String.valueOf(ch))); + Variables.set(value, NumberValue.of(ch)); try { body.execute(); } catch (BreakStatement bs) { @@ -38,15 +67,37 @@ public void execute() { // continue; } } - // Восстанавливаем переменные - if (previousVariableValue1 != null) { - Variables.set(key, previousVariableValue1); + } + + private void iterateArray(ArrayValue containerValue) { + int index = 0; + for (Value v : containerValue) { + Variables.set(key, v); + Variables.set(value, NumberValue.of(index++)); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } } - if (previousVariableValue2 != null) { - Variables.set(value, previousVariableValue2); + } + + private void iterateMap(MapValue containerValue) { + for (Map.Entry entry : containerValue) { + Variables.set(key, entry.getKey()); + Variables.set(value, entry.getValue()); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } } } - + @Override public void accept(Visitor visitor) { visitor.visit(this); diff --git a/src/test/resources/expressions/foreachKeyValue.own b/src/test/resources/expressions/foreachKeyValue.own new file mode 100644 index 00000000..d0256c8a --- /dev/null +++ b/src/test/resources/expressions/foreachKeyValue.own @@ -0,0 +1,30 @@ +def testArrayIterate() { + sum = 0 + for v, i : [1, 2, 3] { + sum += v * i + } + assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) +} + +def testMapIterate() { + map = {12: 1, 13: 2, 14: 3} + sumKey = 0 + sumValue = 0 + for key, value : map { + sumKey += key + sumValue += value + } + assertEquals(39, sumKey) + assertEquals(6, sumValue) +} + +def testStringIterate() { + str = "" + sum = 0 + for s, code : "abcd" { + str += s.upper + sum += code + } + assertEquals("ABCD", str) + assertEquals(394/*97 + 98 + 99 + 100*/, sum) +} diff --git a/src/test/resources/expressions/foreachValue.own b/src/test/resources/expressions/foreachValue.own new file mode 100644 index 00000000..36942a06 --- /dev/null +++ b/src/test/resources/expressions/foreachValue.own @@ -0,0 +1,41 @@ +use "std" + +def testArrayIterate() { + sum = 0 + for v : [1, 2, 3] { + sum += v + } + assertEquals(6, sum) +} + +def testMapIterate() { + map = {12: 1, 13: 2, 14: 3} + sumKey = 0 + sumValue = 0 + for pair : map { + extract(key, value) = pair + sumKey += key + sumValue += value + } + assertEquals(39, sumKey) + assertEquals(6, sumValue) +} + +def testStringIterate() { + sum = 0 + for s : "abcd" { + sum += s.charAt(0) + } + assertEquals(394/*97 + 98 + 99 + 100*/, sum) +} + +def testScope() { + v = 45 + sum = 0 + for v : [1, 2, 3] { + sum += v + } + assertEquals(6, sum) + assertEquals(45, v) +} + diff --git a/src/test/resources/modules/functional/foreach.own b/src/test/resources/modules/functional/foreach.own new file mode 100644 index 00000000..6d452f4c --- /dev/null +++ b/src/test/resources/modules/functional/foreach.own @@ -0,0 +1,36 @@ +use ["std", "functional"] + +def testArrayForeach1Arg() { + sum = 0 + foreach([1, 2, 3], def(v) { + sum += v + }) + assertEquals(6, sum) +} + +def testArrayForeach2Args() { + sum = 0 + foreach([1, 2, 3], def(v, index) { + sum += v * index + }) + assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) +} + +def testStringForeach1Arg() { + sum = 0 + foreach("abcd", def(s) { + sum += s.charAt(0) + }) + assertEquals(394/*97 + 98 + 99 + 100*/, sum) +} + +def testStringForeach2Args() { + str = "" + sum = 0 + foreach("abcd", def(s, code) { + str += s.upper + sum += code + }) + assertEquals("ABCD", str) + assertEquals(97 + 98 + 99 + 100, sum) +} From 82030496c15f6ffb44eeb8a0c3fab7d0e60bdd14 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 11 Apr 2019 14:30:28 +0300 Subject: [PATCH 246/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20zip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + build.gradle | 1 + .../com/annimon/ownlang/modules/zip/zip.java | 261 ++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 src/main/java/com/annimon/ownlang/modules/zip/zip.java diff --git a/.gitignore b/.gitignore index bf174c2e..d2384ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.nb-gradle/ /build/ /dist/ +/out/ /store/ /optimizations/ /nbproject/private/ diff --git a/build.gradle b/build.gradle index 53a1e753..d32874a3 100644 --- a/build.gradle +++ b/build.gradle @@ -106,6 +106,7 @@ task sandbox(dependsOn: proguard, type: Jar) { "**/modules/socket/**", "io/**", "**/modules/aimp/**", "aimpremote/**", "**/modules/downloader/**", + "**/modules/zip/**", "jline/**", "org/fusesource/**", "META-INF/native/**" manifest { diff --git a/src/main/java/com/annimon/ownlang/modules/zip/zip.java b/src/main/java/com/annimon/ownlang/modules/zip/zip.java new file mode 100644 index 00000000..34ebba51 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/zip/zip.java @@ -0,0 +1,261 @@ +package com.annimon.ownlang.modules.zip; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class zip implements Module { + + @Override + public void init() { + Functions.set("zip", this::zipWithMapper); + Functions.set("zipFiles", this::zipFiles); + Functions.set("unzip", this::unzip); + Functions.set("unzipFiles", this::unzipFiles); + Functions.set("listZipEntries", this::listZipEntries); + } + + private Value zipWithMapper(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + final String inputPath = args[0].asString(); + final File input = new File(inputPath); + if (!input.exists()) { + return NumberValue.MINUS_ONE; + } + final File output = new File(args[1].asString()); + if (output.isDirectory()) { + return NumberValue.MINUS_ONE; + } + final Function mapper = getMapperOrNull(args, 2); + + final Map mappings = new HashMap<>(); + final String rootPath = (input.isFile() ? input.getParent() : input.getAbsolutePath()); + generateFileList(mappings, rootPath, input, mapper); + return zipFileList(mappings, output); + } + + private Value zipFiles(Value[] args) { + Arguments.check(2, args.length); + + final Map mappings = new HashMap<>(); + switch (args[0].type()) { + case Types.STRING: { + final File file = new File(args[0].asString()); + mappings.put(file, file.getName()); + } break; + case Types.ARRAY: + for (Value value : ((ArrayValue) args[0])) { + final File file = new File(value.asString()); + mappings.put(file, file.getName()); + } + break; + case Types.MAP: + for (Map.Entry entry : ((MapValue) args[0])) { + final File file = new File(entry.getKey().asString()); + mappings.put(file, entry.getValue().asString()); + } + break; + default: + throw new TypeException("Single file path, file paths array or file mappings expected at first argument"); + } + + final File output = new File(args[1].asString()); + if (output.isDirectory()) { + return NumberValue.MINUS_ONE; + } + return zipFileList(mappings, output); + } + + private Value zipFileList(Map mappings, File output) { + int count = 0; + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(output))) { + for (Map.Entry entry : mappings.entrySet()) { + final File node = entry.getKey(); + final String entryName = entry.getValue(); + if (node.isDirectory()) { + zos.putNextEntry(new ZipEntry(entryName + (entryName.endsWith("/") ? "" : "/"))); + } else { + zipFile(zos, entry.getKey(), entry.getValue()); + count++; + } + } + } catch (IOException ex) { + throw new RuntimeException("zip files", ex); + } + return NumberValue.of(count); + } + + private void zipFile(ZipOutputStream zos, File file, String entryPath) throws IOException { + final ZipEntry entry = new ZipEntry(entryPath); + entry.setTime(file.lastModified()); + zos.putNextEntry(entry); + try (FileInputStream fis = new FileInputStream(file)) { + copy(fis, zos); + } + } + + private Value unzip(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + final File input = new File(args[0].asString()); + if (!input.exists() || !input.canRead() || input.isDirectory()) { + return NumberValue.MINUS_ONE; + } + final File output = new File(args[1].asString()); + if (!output.exists()) { + output.mkdirs(); + } + final Function mapper = getMapperOrNull(args, 2); + + final Map mappings = new HashMap<>(); + for (String entryName : listEntries(input)) { + String fileName = entryName; + if (mapper != null) { + fileName = mapper.execute(new StringValue(entryName)).asString(); + } + if (!fileName.isEmpty()) { + mappings.put(entryName, new File(output, fileName)); + } + } + return unzipFileList(input, mappings); + } + + private Value unzipFiles(Value[] args) { + Arguments.check(2, args.length); + + final File input = new File(args[0].asString()); + if (!input.exists() || !input.canRead() || input.isDirectory()) { + return NumberValue.MINUS_ONE; + } + + final Map mappings = new HashMap<>(); + switch (args[1].type()) { + case Types.STRING: { + final String entryPath = args[1].asString(); + mappings.put(entryPath, new File(entryPath)); + } break; + case Types.ARRAY: + for (Value value : ((ArrayValue) args[1])) { + final String entryPath = value.asString(); + mappings.put(entryPath, new File(entryPath)); + } + break; + case Types.MAP: + for (Map.Entry entry : ((MapValue) args[1])) { + final File file = new File(entry.getValue().asString()); + mappings.put(entry.getKey().asString(), file); + } + break; + default: + throw new TypeException("Single entry path, entry paths array or entry mappings expected at second argument"); + } + return unzipFileList(input, mappings); + } + + private Value unzipFileList(File input, Map mappings) { + int count = 0; + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(input))) { + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + final String entryName = ze.getName(); + final File file = mappings.get(entryName); + if (file == null) continue; + + if (entryName.endsWith("/")) { + safeMkdirs(file); + } else { + safeMkdirs(file.getParentFile()); + try (FileOutputStream fos = new FileOutputStream(file)) { + copy(zis, fos); + } + if (ze.getTime() > 0) { + file.setLastModified(ze.getTime()); + } + count++; + } + } + zis.closeEntry(); + } catch (IOException ex) { + throw new RuntimeException("unzip files", ex); + } + return NumberValue.of(count); + } + + private Value listZipEntries(Value[] args) { + Arguments.check(1, args.length); + final File input = new File(args[0].asString()); + if (!input.exists() || !input.canRead() || input.isDirectory()) { + return new ArrayValue(0); + } + return ArrayValue.of(listEntries(input)); + } + + private Function getMapperOrNull(Value[] args, int index) { + Function mapper; + if (args.length >= (index + 1)) { + mapper = ValueUtils.consumeFunction(args[index], index); + } else { + mapper = null; + } + return mapper; + } + + private void copy(InputStream is, OutputStream os) throws IOException { + final byte[] buffer = new byte[8192]; + int read; + while ((read = is.read(buffer)) != -1) { + os.write(buffer, 0, read); + } + } + + private void generateFileList(Map mappings, String rootPath, File node, Function mapper) { + if (!rootPath.equals(node.getAbsolutePath())) { + String entryPath = node.getAbsolutePath().substring(rootPath.length() + 1); + if (mapper != null) { + entryPath = mapper.execute(new StringValue(entryPath)).asString(); + if (entryPath.isEmpty()) { + return; + } + } + mappings.put(node, entryPath); + } + + if (node.isDirectory()) { + for (File file : node.listFiles()) { + generateFileList(mappings, rootPath, file, mapper); + } + } + } + + private String[] listEntries(File input) { + final List entries = new ArrayList<>(); + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(input))) { + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + entries.add(ze.getName()); + } + zis.closeEntry(); + } catch (IOException ex) { + throw new RuntimeException("list zip entries", ex); + } + return entries.toArray(new String[0]); + } + + private void safeMkdirs(File file) { + if (file != null) { + file.mkdirs(); + } + } +} From ccd06c98a6486901d9b3cf3c904cf6e8fbfb8428 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 11 Apr 2019 20:44:13 +0300 Subject: [PATCH 247/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20gzip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../annimon/ownlang/modules/gzip/gzip.java | 114 ++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/gzip/gzip.java diff --git a/build.gradle b/build.gradle index d32874a3..62f8729e 100644 --- a/build.gradle +++ b/build.gradle @@ -106,7 +106,7 @@ task sandbox(dependsOn: proguard, type: Jar) { "**/modules/socket/**", "io/**", "**/modules/aimp/**", "aimpremote/**", "**/modules/downloader/**", - "**/modules/zip/**", + "**/modules/zip/**", "**/modules/gzip/**", "jline/**", "org/fusesource/**", "META-INF/native/**" manifest { diff --git a/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java b/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java new file mode 100644 index 00000000..86523f68 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java @@ -0,0 +1,114 @@ +package com.annimon.ownlang.modules.gzip; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class gzip implements Module { + + @Override + public void init() { + Functions.set("gzip", this::gzipFile); + Functions.set("gzipBytes", this::gzipBytes); + Functions.set("ungzip", this::ungzipFile); + Functions.set("ungzipBytes", this::ungzipBytes); + } + + private Value gzipFile(Value[] args) { + Arguments.check(2, args.length); + + final File input = new File(args[0].asString()); + if (!input.exists() || !input.canRead() || input.isDirectory()) { + return NumberValue.MINUS_ONE; + } + final File output = new File(args[1].asString()); + if (output.isDirectory()) { + return NumberValue.MINUS_ONE; + } + + try (InputStream is = new FileInputStream(input); + OutputStream os = new FileOutputStream(output); + GZIPOutputStream gzos = new GZIPOutputStream(os)) { + copy(is, gzos); + gzos.finish(); + return NumberValue.ONE; + } catch (IOException ex) { + throw new RuntimeException("gzipFile", ex); + } + } + + private Value gzipBytes(Value[] args) { + Arguments.check(1, args.length); + + if (args[0].type() != Types.ARRAY) { + throw new TypeException("Byte array expected at first argument"); + } + final byte[] input = ValueUtils.toByteArray(((ArrayValue) args[0])); + try (InputStream is = new ByteArrayInputStream(input); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzos = new GZIPOutputStream(baos)) { + copy(is, gzos); + gzos.finish(); + return ArrayValue.of(baos.toByteArray()); + } catch (IOException ex) { + throw new RuntimeException("gzipBytes", ex); + } + } + + private Value ungzipFile(Value[] args) { + Arguments.check(2, args.length); + + final File input = new File(args[0].asString()); + if (!input.exists() || !input.canRead() || input.isDirectory()) { + return NumberValue.MINUS_ONE; + } + final File output = new File(args[1].asString()); + if (output.isDirectory()) { + return NumberValue.MINUS_ONE; + } + + try (InputStream is = new FileInputStream(input); + GZIPInputStream gzis = new GZIPInputStream(is); + OutputStream os = new FileOutputStream(output)) { + copy(gzis, os); + return NumberValue.ONE; + } catch (IOException ex) { + throw new RuntimeException("ungzipFile", ex); + } + } + + private Value ungzipBytes(Value[] args) { + Arguments.check(1, args.length); + + if (args[0].type() != Types.ARRAY) { + throw new TypeException("Byte array expected at first argument"); + } + final byte[] input = ValueUtils.toByteArray(((ArrayValue) args[0])); + try (InputStream is = new ByteArrayInputStream(input); + GZIPInputStream gzis = new GZIPInputStream(is); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + copy(gzis, baos); + return ArrayValue.of(baos.toByteArray()); + } catch (IOException ex) { + throw new RuntimeException("ungzipBytes", ex); + } + } + + private void copy(InputStream is, OutputStream os) throws IOException { + final byte[] buffer = new byte[8192]; + int read; + while ((read = is.read(buffer)) != -1) { + os.write(buffer, 0, read); + } + } +} From ed164085c639579f7dc6b264eedca8ce944a5958 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 12 Apr 2019 19:12:35 +0300 Subject: [PATCH 248/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20std::getBytes=20=D0=B8=20std::stringFromBy?= =?UTF-8?q?tes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/ArrayValue.java | 1 - .../ownlang/modules/std/ArrayFunctions.java | 29 +++++++++++++++++++ .../ownlang/modules/std/StringFunctions.java | 16 ++++++++-- .../com/annimon/ownlang/modules/std/std.java | 2 ++ src/test/resources/modules/gzip/gzipBytes.own | 15 ++++++++++ src/test/resources/modules/std/getBytes.own | 9 ++++++ .../resources/modules/std/stringFromBytes.own | 9 ++++++ 7 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java create mode 100644 src/test/resources/modules/gzip/gzipBytes.own create mode 100644 src/test/resources/modules/std/getBytes.own create mode 100644 src/test/resources/modules/std/stringFromBytes.own diff --git a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java index cdd3b688..d8935a11 100644 --- a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.lib; import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.exceptions.UnknownPropertyException; import java.util.Arrays; import java.util.Iterator; import java.util.List; diff --git a/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java new file mode 100644 index 00000000..b5692890 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import java.io.UnsupportedEncodingException; + +public final class ArrayFunctions { + + private ArrayFunctions() { } + + static StringValue stringFromBytes(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + if (args[0].type() != Types.ARRAY) { + throw new TypeException("Array expected at first argument"); + } + final byte[] bytes = ValueUtils.toByteArray((ArrayValue) args[0]); + final String charset = (args.length == 2) ? args[1].asString() : "UTF-8"; + try { + return new StringValue(new String(bytes, charset)); + } catch (UnsupportedEncodingException uee) { + throw new RuntimeException(uee); + } + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java index d850d6de..21d37bb9 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java +++ b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java @@ -1,20 +1,32 @@ package com.annimon.ownlang.modules.std; import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; +import java.io.UnsupportedEncodingException; public final class StringFunctions { private StringFunctions() { } - static Value parseInt(Value... args) { + static ArrayValue getBytes(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final String charset = (args.length == 2) ? args[1].asString() : "UTF-8"; + try { + return ArrayValue.of(args[0].asString().getBytes(charset)); + } catch (UnsupportedEncodingException uee) { + throw new RuntimeException(uee); + } + } + + static Value parseInt(Value[] args) { Arguments.checkOrOr(1, 2, args.length); final int radix = (args.length == 2) ? args[1].asInt() : 10; return NumberValue.of(Integer.parseInt(args[0].asString(), radix)); } - static Value parseLong(Value... args) { + static Value parseLong(Value[] args) { Arguments.checkOrOr(1, 2, args.length); final int radix = (args.length == 2) ? args[1].asInt() : 10; return NumberValue.of(Long.parseLong(args[0].asString(), radix)); diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index 29bf1574..023498a8 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -39,6 +39,7 @@ public void init() { Functions.set("toHexString", NumberFunctions::toHexString); // String + Functions.set("getBytes", StringFunctions::getBytes); Functions.set("sprintf", new std_sprintf()); Functions.set("split", new std_split()); Functions.set("indexOf", new std_indexof()); @@ -65,5 +66,6 @@ public void init() { Functions.set("arrayValues", new std_arrayValues()); Functions.set("arraySplice", new std_arraySplice()); Functions.set("range", new std_range()); + Functions.set("stringFromBytes", ArrayFunctions::stringFromBytes); } } diff --git a/src/test/resources/modules/gzip/gzipBytes.own b/src/test/resources/modules/gzip/gzipBytes.own new file mode 100644 index 00000000..37e57a03 --- /dev/null +++ b/src/test/resources/modules/gzip/gzipBytes.own @@ -0,0 +1,15 @@ +use ["std", "gzip"] + +def testGzipText() { + text = trim(" +Lorem ipsum dolor sit amet, consectetur adipiscing elit. In leo dui, venenatis eu eleifend ut, volutpat vitae risus. Vivamus sed massa consectetur, fermentum est ac, semper ligula. Donec vel facilisis urna. Cras scelerisque libero a pulvinar mollis. Maecenas elementum, lectus vitae ullamcorper viverra, odio justo interdum lacus, a dictum mauris lacus id neque. Donec ante nibh, ornare ac lacus at, rutrum vulputate lacus. Quisque aliquet sem sit amet nisl semper faucibus. Aenean finibus sodales est, eget efficitur nibh. Donec ut tortor ut ex auctor fringilla id sed neque. Aenean id placerat ipsum. +Etiam enim ligula, vulputate ac velit nec, accumsan blandit velit. Aenean tortor neque, ornare eu quam vel, viverra condimentum erat. In vitae mattis augue. Sed nec auctor est. Aenean in auctor lorem. Etiam non accumsan arcu. Vivamus purus massa, finibus at ultrices feugiat, congue vitae quam. +Vestibulum porttitor finibus nulla, vel mollis elit luctus vel. Phasellus ut erat ante. Praesent consectetur vulputate sem eget bibendum. Etiam porttitor magna et egestas viverra. Praesent urna eros, porttitor vitae lorem sodales, luctus sagittis velit. Donec sed aliquet nulla, ac gravida urna. Mauris at quam eros. Nam dolor lacus, laoreet vel consequat non, semper sed quam. Nunc semper interdum arcu eget iaculis. Vivamus at turpis et urna pellentesque suscipit in nec tellus. Nam sed sem ut ipsum semper imperdiet in et est. Praesent vestibulum augue a dolor consequat feugiat. Nam massa tortor, feugiat quis orci ut, blandit varius tellus. Quisque et blandit erat. +Fusce ultricies, odio scelerisque venenatis pellentesque, nisl mi vehicula mi, eu ultrices dui nisi ac elit. Nullam laoreet et lacus ac fringilla. Morbi pretium, eros non facilisis bibendum, nisl ex ultricies ipsum, sed sodales est felis vel eros. Suspendisse lobortis lectus scelerisque turpis dapibus, et tristique neque faucibus. Donec sed tellus id augue molestie tempus ac in urna. Morbi accumsan velit in erat ultricies, vitae ultricies nulla viverra. Pellentesque non euismod purus. Pellentesque vitae consequat orci. Pellentesque in nulla pulvinar, blandit leo sed, vehicula quam. Cras finibus libero ut diam aliquam efficitur. Vestibulum rutrum tincidunt posuere. Integer ultricies rutrum libero vitae pellentesque. Nulla blandit ipsum sit amet aliquet venenatis. Maecenas cursus massa quis massa posuere eleifend. Morbi vel lorem luctus, efficitur nibh id, sagittis tortor. Maecenas tristique suscipit dui vel congue. + ") + bytes1 = getBytes(text) + bytesGzip = gzipBytes(bytes1) + bytes2 = ungzipBytes(bytesGzip) + assertEquals(bytes1.length, bytes2.length) + assertEquals(text, stringFromBytes(bytes2)) +} \ No newline at end of file diff --git a/src/test/resources/modules/std/getBytes.own b/src/test/resources/modules/std/getBytes.own new file mode 100644 index 00000000..48ba8fd8 --- /dev/null +++ b/src/test/resources/modules/std/getBytes.own @@ -0,0 +1,9 @@ +use "std" + +def testGetBytes() { + assertEquals([111, 119, 110, 108, 97, 110, 103], getBytes("ownlang")) +} + +def testGetBytesEmptyString() { + assertEquals([], getBytes("")) +} diff --git a/src/test/resources/modules/std/stringFromBytes.own b/src/test/resources/modules/std/stringFromBytes.own new file mode 100644 index 00000000..6964c31b --- /dev/null +++ b/src/test/resources/modules/std/stringFromBytes.own @@ -0,0 +1,9 @@ +use "std" + +def testStringFromBytes() { + assertEquals("ownlang", stringFromBytes([111, 119, 110, 108, 97, 110, 103])) +} + +def testStringFromEmptyString() { + assertEquals("", stringFromBytes([])) +} From c6a2f71457d97279cbe91fd3baf9be5e0808bcfb Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 12 Apr 2019 23:09:34 +0300 Subject: [PATCH 249/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20std::stripMargin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 +++ .../ownlang/modules/std/StringFunctions.java | 51 ++++++++++++++++++ .../com/annimon/ownlang/modules/std/std.java | 1 + .../resources/modules/std/stripMargin.own | 54 +++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 src/test/resources/modules/std/stripMargin.own diff --git a/build.gradle b/build.gradle index 62f8729e..a18c3f04 100644 --- a/build.gradle +++ b/build.gradle @@ -133,4 +133,11 @@ sonarqube { property "sonar.projectKey", "aNNiMON_Own-Programming-Language-Tutorial" property "sonar.host.url", "https://sonarcloud.io" } +} + +test { + testLogging { + events "passed", "skipped", "failed" + exceptionFormat "full" + } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java index 21d37bb9..774bd60b 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java +++ b/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; import java.io.UnsupportedEncodingException; @@ -31,4 +32,54 @@ static Value parseLong(Value[] args) { final int radix = (args.length == 2) ? args[1].asInt() : 10; return NumberValue.of(Long.parseLong(args[0].asString(), radix)); } + + static Value stripMargin(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final String input = args[0].asString(); + final String marginPrefix = (args.length == 2) ? args[1].asString() : "|"; + if (!input.contains(marginPrefix)) { + return args[0]; + } + final String[] lines = input.split("\\r?\\n"); + + // First blank line is omitted + final StringBuilder sb = new StringBuilder(); + final int firstLineIndex = (isBlank(lines[0])) ? 1 : 0; + final int lastLineIndex = lines.length - 1; + int index = firstLineIndex; + while (true) { + sb.append(strip(lines[index], marginPrefix)); + if (++index >= lastLineIndex) break; + sb.append('\n'); + } + // Process last line + if (lastLineIndex >= (firstLineIndex + 1) && !isBlank(lines[lastLineIndex])) { + sb.append('\n').append(strip(lines[lastLineIndex], marginPrefix)); + } + return new StringValue(sb.toString()); + } + + private static String strip(String str, String marginPrefix) { + final int nonBlankIndex = firstNonBlankIndex(str); + if (str.startsWith(marginPrefix, nonBlankIndex)) { + return str.substring(nonBlankIndex + marginPrefix.length()); + } else { + return str; + } + } + + private static boolean isBlank(String str) { + return firstNonBlankIndex(str) == str.length(); + } + + private static int firstNonBlankIndex(String str) { + final int length = str.length(); + for (int index = 0; index < length; index++) { + final char ch = str.charAt(index); + if (ch != ' ' && ch != '\t' && !Character.isWhitespace(ch)) { + return index; + } + } + return length; + } } diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java index 023498a8..eca4d2bf 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -55,6 +55,7 @@ public void init() { Functions.set("replaceFirst", new std_replacefirst()); Functions.set("parseInt", StringFunctions::parseInt); Functions.set("parseLong", StringFunctions::parseLong); + Functions.set("stripMargin", StringFunctions::stripMargin); // Arrays and maps Functions.set("newarray", new std_newarray()); diff --git a/src/test/resources/modules/std/stripMargin.own b/src/test/resources/modules/std/stripMargin.own new file mode 100644 index 00000000..e5ba625c --- /dev/null +++ b/src/test/resources/modules/std/stripMargin.own @@ -0,0 +1,54 @@ +use "std" + +def testStripMargin() { + testCases = [ + "|abc".stripMargin(), + " |abc".stripMargin(), + " + |abc".stripMargin(), + "|abc + ".stripMargin(), + " + |abc + ".stripMargin(), + "abc".stripMargin(""), + "abc".stripMargin(), + "#abc".stripMargin("#"), + "| abc".stripMargin("| "), + " + | abc + ".stripMargin("| "), + "xxxabc".stripMargin("xxx"), + " + xxxabc".stripMargin("xxx"), + " + xxxabc + ".stripMargin("xxx") + ] + for actual : testCases { + assertEquals("abc", actual) + } +} + +def testStripMargin2() { + assertEquals("123\n456\n789", "123 + |456 + |789".stripMargin()) + assertEquals("123\n456\n789", "|123 + |456 + |789".stripMargin()) + assertEquals("123\n456\n789", " + |123 + |456 + |789".stripMargin()) + assertEquals("123\n456\n789", " + |123 + |456 + |789 + ".stripMargin()) + assertEquals("123\n456\n789", " + //123 + //456 + //789 + ".stripMargin("//")) +} \ No newline at end of file From d72fad6de3d0ae4dc80b3cd9ded0fc451e80de36 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 24 Apr 2019 19:23:33 +0300 Subject: [PATCH 250/448] =?UTF-8?q?=D0=A1=D1=82=D1=80=D0=BE=D0=B3=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D0=B2=D0=B0=D1=8E=D1=89=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=BA=D1=80=D1=83=D0=B3=D0=BB=D0=BE=D0=B9=20=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=B1=D0=BA=D0=B8.=20Fix=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index ce16bfaa..0af2d426 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -703,7 +703,7 @@ private Expression unary() { private Expression primary() { if (match(TokenType.LPAREN)) { Expression result = expression(); - match(TokenType.RPAREN); + consume(TokenType.RPAREN); return result; } From 23ed9e6d045a55f303c1ff304b65000a6516e945 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 22 May 2019 19:10:48 +0300 Subject: [PATCH 251/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20okhttp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 285 +++++++++--------- examples/network/okhttp_imgur_upload.own | 17 ++ .../network/okhttp_telegram_sendvoice.own | 20 ++ examples/network/okhttp_websocket.own | 20 ++ .../com/annimon/ownlang/lib/ValueUtils.java | 262 ++++++++-------- .../ownlang/modules/okhttp/CallValue.java | 62 ++++ .../modules/okhttp/HttpClientValue.java | 122 ++++++++ .../okhttp/MultipartBodyBuilderValue.java | 70 +++++ .../modules/okhttp/MultipartBodyValue.java | 24 ++ .../modules/okhttp/RequestBodyValue.java | 55 ++++ .../modules/okhttp/RequestBuilderValue.java | 109 +++++++ .../modules/okhttp/ResponseBodyValue.java | 56 ++++ .../ownlang/modules/okhttp/ResponseValue.java | 52 ++++ .../ownlang/modules/okhttp/Values.java | 47 +++ .../modules/okhttp/WebSocketValue.java | 46 +++ .../ownlang/modules/okhttp/okhttp.java | 81 +++++ 16 files changed, 1057 insertions(+), 271 deletions(-) create mode 100644 examples/network/okhttp_imgur_upload.own create mode 100644 examples/network/okhttp_telegram_sendvoice.own create mode 100644 examples/network/okhttp_websocket.own create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/Values.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java diff --git a/build.gradle b/build.gradle index a18c3f04..c67023d0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,143 +1,144 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'net.sf.proguard:proguard-gradle:6.0.1' - } -} - -plugins { - id "java" - id "com.github.johnrengelman.shadow" version "2.0.2" - id "org.sonarqube" version "2.6.2" -} - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import proguard.gradle.ProGuardTask - - -sourceCompatibility = '1.8' -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' - -if (!hasProperty('mainClass')) { - ext.mainClass = 'com.annimon.ownlang.Main' -} - -ext.generatedJavaDir = "${rootProject.projectDir}/src/main/generatedJava" -sourceSets.main.java.srcDirs += project.generatedJavaDir - -repositories { - jcenter() -} - -task generateJavaSources() { - doLast { - def source = """ - package com.annimon.ownlang; - class Gen { - private Gen() {} - public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; - } - """ - def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") - genFile.getParentFile().mkdirs() - genFile.write(source) - } -} -compileJava.dependsOn(generateJavaSources) - -task run(dependsOn: classes, type: JavaExec) { - main = project.mainClass - classpath = sourceSets.main.runtimeClasspath - standardInput = System.in - ignoreExitValue = true -} - -task runOptimizing(dependsOn: classes, type: JavaExec) { - main = project.mainClass - classpath = sourceSets.main.runtimeClasspath - ignoreExitValue = true - // args '-o 9 -m -a -f examples/game/minesweeper.own'.split(' ') - args '-o 9 -m -a -f program.own'.split(' ') -} - -task dist(type: ShadowJar) { - from sourceSets.main.output - configurations = [project.configurations.runtime] - destinationDir file("$rootProject.projectDir/dist") - - exclude 'META-INF/*.DSA' - exclude 'META-INF/*.RSA' - exclude 'META-INF/maven/**' - exclude 'LICENSE*' - - manifest.attributes( - 'Main-Class': project.mainClass, - 'Build-Date': new Date().format('YYMMdd') - ) -} - -task proguard(dependsOn: dist, type: ProGuardTask) { - configuration "$rootProject.projectDir/proguard.properties" - injars "$rootProject.projectDir/dist/OwnLang.jar" - outjars "$rootProject.projectDir/store/OwnLang.jar" - - // Automatically handle the Java version of this build. - if (System.getProperty('java.version').startsWith('1.')) { - // Before Java 9, the runtime classes were packaged in a single jar file. - libraryjars "${System.getProperty('java.home')}/lib/rt.jar" - } else { - // As of Java 9, the runtime classes are packaged in modular jmod files. - def jmods = files { file("${System.getProperty('java.home')}/jmods").listFiles() } - jmods.each { - libraryjars it, jarfilter: '!**.jar', filter: '!module-info.class' - } - } -} - -task sandbox(dependsOn: proguard, type: Jar) { - from zipTree("$rootProject.projectDir/store/OwnLang.jar") - libsDirName = "$rootProject.projectDir/store" - appendix = "Sandbox" - - exclude "**/modules/canvas/**", "**/modules/canvasfx/**", "**/modules/forms/**", - "**/modules/java/**", "**/modules/jdbc/**", "**/modules/robot/**", - "**/modules/socket/**", "io/**", - "**/modules/aimp/**", "aimpremote/**", - "**/modules/downloader/**", - "**/modules/zip/**", "**/modules/gzip/**", - "jline/**", "org/fusesource/**", "META-INF/native/**" - - manifest { - attributes 'Main-Class': project.mainClass - } -} - -dependencies { - compile ('io.socket:socket.io-client:1.0.0') { - exclude group: 'org.json', module: 'json' - } - compile 'org.json:json:20180130' - compile 'org.yaml:snakeyaml:1.20' - compile 'jline:jline:2.14.5' - - testImplementation 'junit:junit:4.12' - testImplementation 'org.openjdk.jmh:jmh-core:1.13' - testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.13' -} - -sonarqube { - properties { - property "sonar.projectName", "Own-Programming-Language-Tutorial" - property "sonar.projectKey", "aNNiMON_Own-Programming-Language-Tutorial" - property "sonar.host.url", "https://sonarcloud.io" - } -} - -test { - testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" - } +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'net.sf.proguard:proguard-gradle:6.0.1' + } +} + +plugins { + id "java" + id "com.github.johnrengelman.shadow" version "2.0.2" + id "org.sonarqube" version "2.6.2" +} + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import proguard.gradle.ProGuardTask + + +sourceCompatibility = '1.8' +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' + +if (!hasProperty('mainClass')) { + ext.mainClass = 'com.annimon.ownlang.Main' +} + +ext.generatedJavaDir = "${rootProject.projectDir}/src/main/generatedJava" +sourceSets.main.java.srcDirs += project.generatedJavaDir + +repositories { + jcenter() +} + +task generateJavaSources() { + doLast { + def source = """ + package com.annimon.ownlang; + class Gen { + private Gen() {} + public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; + } + """ + def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") + genFile.getParentFile().mkdirs() + genFile.write(source) + } +} +compileJava.dependsOn(generateJavaSources) + +task run(dependsOn: classes, type: JavaExec) { + main = project.mainClass + classpath = sourceSets.main.runtimeClasspath + standardInput = System.in + ignoreExitValue = true +} + +task runOptimizing(dependsOn: classes, type: JavaExec) { + main = project.mainClass + classpath = sourceSets.main.runtimeClasspath + ignoreExitValue = true + // args '-o 9 -m -a -f examples/game/minesweeper.own'.split(' ') + args '-o 9 -m -a -f program.own'.split(' ') +} + +task dist(type: ShadowJar) { + from sourceSets.main.output + configurations = [project.configurations.runtime] + destinationDir file("$rootProject.projectDir/dist") + + exclude 'META-INF/*.DSA' + exclude 'META-INF/*.RSA' + exclude 'META-INF/maven/**' + exclude 'LICENSE*' + + manifest.attributes( + 'Main-Class': project.mainClass, + 'Build-Date': new Date().format('YYMMdd') + ) +} + +task proguard(dependsOn: dist, type: ProGuardTask) { + configuration "$rootProject.projectDir/proguard.properties" + injars "$rootProject.projectDir/dist/OwnLang.jar" + outjars "$rootProject.projectDir/store/OwnLang.jar" + + // Automatically handle the Java version of this build. + if (System.getProperty('java.version').startsWith('1.')) { + // Before Java 9, the runtime classes were packaged in a single jar file. + libraryjars "${System.getProperty('java.home')}/lib/rt.jar" + } else { + // As of Java 9, the runtime classes are packaged in modular jmod files. + def jmods = files { file("${System.getProperty('java.home')}/jmods").listFiles() } + jmods.each { + libraryjars it, jarfilter: '!**.jar', filter: '!module-info.class' + } + } +} + +task sandbox(dependsOn: proguard, type: Jar) { + from zipTree("$rootProject.projectDir/store/OwnLang.jar") + libsDirName = "$rootProject.projectDir/store" + appendix = "Sandbox" + + exclude "**/modules/canvas/**", "**/modules/canvasfx/**", "**/modules/forms/**", + "**/modules/java/**", "**/modules/jdbc/**", "**/modules/robot/**", + "**/modules/okhttp/**", + "**/modules/socket/**", "io/**", + "**/modules/aimp/**", "aimpremote/**", + "**/modules/downloader/**", + "**/modules/zip/**", "**/modules/gzip/**", + "jline/**", "org/fusesource/**", "META-INF/native/**" + + manifest { + attributes 'Main-Class': project.mainClass + } +} + +dependencies { + compile ('io.socket:socket.io-client:1.0.0') { + exclude group: 'org.json', module: 'json' + } + compile 'org.json:json:20180130' + compile 'org.yaml:snakeyaml:1.20' + compile 'jline:jline:2.14.5' + + testImplementation 'junit:junit:4.12' + testImplementation 'org.openjdk.jmh:jmh-core:1.13' + testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.13' +} + +sonarqube { + properties { + property "sonar.projectName", "Own-Programming-Language-Tutorial" + property "sonar.projectKey", "aNNiMON_Own-Programming-Language-Tutorial" + property "sonar.host.url", "https://sonarcloud.io" + } +} + +test { + testLogging { + events "passed", "skipped", "failed" + exceptionFormat "full" + } } \ No newline at end of file diff --git a/examples/network/okhttp_imgur_upload.own b/examples/network/okhttp_imgur_upload.own new file mode 100644 index 00000000..d9ea0a4a --- /dev/null +++ b/examples/network/okhttp_imgur_upload.own @@ -0,0 +1,17 @@ +use ["std", "okhttp"] + +// https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostMultipart.java + +println okhttp.request() + .header("Authorization", "Client-ID 9199fdef135c122") + .url("https://api.imgur.com/3/image") + .post(MultipartBody.builder() + .setType(MultipartBody.FORM) + .addFormDataPart("title", "Sample image") + .addFormDataPart("image", "image.png", RequestBody.file("image/png", "image.png")) + .build() + ) + .newCall(okhttp.client) + .execute() + .body() + .string() \ No newline at end of file diff --git a/examples/network/okhttp_telegram_sendvoice.own b/examples/network/okhttp_telegram_sendvoice.own new file mode 100644 index 00000000..1070ab76 --- /dev/null +++ b/examples/network/okhttp_telegram_sendvoice.own @@ -0,0 +1,20 @@ +use ["std", "okhttp"] + +TOKEN = "your bot token" + +println okhttp.request() + .url("https://api.telegram.org/bot" + TOKEN + "/sendVoice") + .post(MultipartBody.builder() + .setType(MultipartBody.FORM) + .addFormData({ + "chat_id": "-1001100000123", + "caption": "sample text" + }) + .addFormDataPart("voice", "file.ogg", + RequestBody.file("audio/ogg", "voice.ogg")) + .build() + ) + .newCall(okhttp.client) + .execute() + .body() + .string() \ No newline at end of file diff --git a/examples/network/okhttp_websocket.own b/examples/network/okhttp_websocket.own new file mode 100644 index 00000000..645260f0 --- /dev/null +++ b/examples/network/okhttp_websocket.own @@ -0,0 +1,20 @@ +use ["std", "okhttp"] + +// https://github.com/square/okhttp/blob/b21ed68c08c2a5c1eb0bbe93a6f720d1aa2820da/samples/guide/src/main/java/okhttp3/recipes/WebSocketEcho.java + +okhttp.client.newWebSocket( + okhttp.request().url("ws://echo.websocket.org"), + { + "onOpen": def(ws, resp) { + ws.send("Hello...") + ws.send("...World!") + ws.close(1000, "Goodbye, World!") + }, + "onTextMessage": def(ws, text) = echo(text), + "onBytesMessage": def(ws, bytes) = echo(bytes), + "onClosing": def(ws, code, reason) { + ws.close(1000) + echo("CLOSE:", code, reason) + } + } +) \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index de5803de..016315c8 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -1,129 +1,133 @@ -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; -import org.json.JSONObject; - -public final class ValueUtils { - - private ValueUtils() { } - - public static Object toObject(Value val) { - switch (val.type()) { - case Types.ARRAY: - return toObject((ArrayValue) val); - case Types.MAP: - return toObject((MapValue) val); - case Types.NUMBER: - return val.raw(); - case Types.STRING: - return val.asString(); - default: - return JSONObject.NULL; - } - } - - public static Object toObject(MapValue map) { - final JSONObject result = new JSONObject(); - for (Map.Entry entry : map) { - final String key = entry.getKey().asString(); - final Object value = toObject(entry.getValue()); - result.put(key, value); - } - return result; - } - - public static Object toObject(ArrayValue array) { - final JSONArray result = new JSONArray(); - for (Value value : array) { - result.put(toObject(value)); - } - return result; - } - - public static Value toValue(Object obj) { - if (obj instanceof JSONObject) { - return toValue((JSONObject) obj); - } - if (obj instanceof JSONArray) { - return toValue((JSONArray) obj); - } - if (obj instanceof String) { - return new StringValue((String) obj); - } - if (obj instanceof Number) { - return NumberValue.of(((Number) obj)); - } - if (obj instanceof Boolean) { - return NumberValue.fromBoolean((Boolean) obj); - } - // NULL or other - return NumberValue.ZERO; - } - - public static MapValue toValue(JSONObject json) { - final MapValue result = new MapValue(json.length()); - final Iterator it = json.keys(); - while(it.hasNext()) { - final String key = it.next(); - final Value value = toValue(json.get(key)); - result.set(new StringValue(key), value); - } - return result; - } - - public static ArrayValue toValue(JSONArray json) { - final int length = json.length(); - final ArrayValue result = new ArrayValue(length); - for (int i = 0; i < length; i++) { - final Value value = toValue(json.get(i)); - result.set(i, value); - } - return result; - } - - public static Number getNumber(Value value) { - if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); - return value.asInt(); - } - - public static float getFloatNumber(Value value) { - if (value.type() == Types.NUMBER) return ((NumberValue) value).raw().floatValue(); - return (float) value.asNumber(); - } - - public static byte[] toByteArray(ArrayValue array) { - final int size = array.size(); - final byte[] result = new byte[size]; - for (int i = 0; i < size; i++) { - result[i] = (byte) array.get(i).asInt(); - } - return result; - } - - public static Function consumeFunction(Value value, int argumentNumber) { - final int type = value.type(); - if (type != Types.FUNCTION) { - throw new TypeException("Function expected at argument " + (argumentNumber + 1) - + ", but found " + Types.typeToString(type)); - } - 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; - } -} +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; +import org.json.JSONObject; + +public final class ValueUtils { + + private ValueUtils() { } + + public static Object toObject(Value val) { + switch (val.type()) { + case Types.ARRAY: + return toObject((ArrayValue) val); + case Types.MAP: + return toObject((MapValue) val); + case Types.NUMBER: + return val.raw(); + case Types.STRING: + return val.asString(); + default: + return JSONObject.NULL; + } + } + + public static Object toObject(MapValue map) { + final JSONObject result = new JSONObject(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = toObject(entry.getValue()); + result.put(key, value); + } + return result; + } + + public static Object toObject(ArrayValue array) { + final JSONArray result = new JSONArray(); + for (Value value : array) { + result.put(toObject(value)); + } + return result; + } + + public static Value toValue(Object obj) { + if (obj instanceof JSONObject) { + return toValue((JSONObject) obj); + } + if (obj instanceof JSONArray) { + return toValue((JSONArray) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return NumberValue.of(((Number) obj)); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + public static MapValue toValue(JSONObject json) { + final MapValue result = new MapValue(json.length()); + final Iterator it = json.keys(); + while(it.hasNext()) { + final String key = it.next(); + final Value value = toValue(json.get(key)); + result.set(new StringValue(key), value); + } + return result; + } + + public static ArrayValue toValue(JSONArray json) { + final int length = json.length(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = toValue(json.get(i)); + result.set(i, value); + } + return result; + } + + public static Number getNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); + return value.asInt(); + } + + public static float getFloatNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw().floatValue(); + return (float) value.asNumber(); + } + + public static byte[] toByteArray(ArrayValue array) { + final int size = array.size(); + final byte[] result = new byte[size]; + for (int i = 0; i < size; i++) { + result[i] = (byte) array.get(i).asInt(); + } + return result; + } + + public static Function consumeFunction(Value value, int argumentNumber) { + return consumeFunction(value, " at argument " + (argumentNumber + 1)); + } + + public static Function consumeFunction(Value value, String errorMessage) { + final int type = value.type(); + if (type != Types.FUNCTION) { + throw new TypeException("Function expected" + errorMessage + + ", but found " + Types.typeToString(type)); + } + 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/okhttp/CallValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java new file mode 100644 index 00000000..41db95aa --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import java.io.IOException; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +public class CallValue extends MapValue { + + private final Call call; + + public CallValue(Call call) { + super(6); + this.call = call; + init(); + } + + private void init() { + set("cancel", Converters.voidToVoid(call::cancel)); + set("enqueue", this::enqueue); + set("execute", this::execute); + set("isCanceled", Converters.voidToBoolean(call::isCanceled)); + set("isExecuted", Converters.voidToBoolean(call::isExecuted)); + } + + private Value enqueue(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final Function onResponse = ValueUtils.consumeFunction(args[0], 0); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + onResponse.execute(new CallValue(call), new ResponseValue(response)); + } + + @Override + public void onFailure(Call call, IOException e) { + if (args.length == 2) { + ValueUtils.consumeFunction(args[1], 1) + .execute(new CallValue(call), new StringValue(e.getMessage())); + } + } + }); + return NumberValue.ZERO; + } + + private Value execute(Value[] args) { + Arguments.check(0, args.length); + try { + return new ResponseValue(call.execute()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java new file mode 100644 index 00000000..b222339e --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java @@ -0,0 +1,122 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import okio.ByteString; + +public class HttpClientValue extends MapValue { + + private final OkHttpClient client; + + public HttpClientValue(OkHttpClient client) { + super(10); + this.client = client; + init(); + } + + public OkHttpClient getClient() { + return client; + } + + private void init() { + set("connectTimeoutMillis", Converters.voidToInt(client::connectTimeoutMillis)); + set("followRedirects", Converters.voidToBoolean(client::followRedirects)); + set("followSslRedirects", Converters.voidToBoolean(client::followSslRedirects)); + set("newCall", args -> { + Arguments.check(1, args.length); + final Request request = Values.getRequest(args[0], " at first argument"); + return new CallValue(client.newCall(request)); + }); + set("newWebSocket", this::newWebSocket); + set("pingIntervalMillis", Converters.voidToInt(client::pingIntervalMillis)); + set("readTimeoutMillis", Converters.voidToInt(client::readTimeoutMillis)); + set("retryOnConnectionFailure", Converters.voidToBoolean(client::retryOnConnectionFailure)); + set("writeTimeoutMillis", Converters.voidToInt(client::writeTimeoutMillis)); + } + + private static final StringValue onOpen = new StringValue("onOpen"); + private static final StringValue onTextMessage = new StringValue("onTextMessage"); + private static final StringValue onBytesMessage = new StringValue("onBytesMessage"); + private static final StringValue onClosing = new StringValue("onClosing"); + private static final StringValue onClosed = new StringValue("onClosed"); + private static final StringValue onFailure = new StringValue("onFailure"); + + private Value newWebSocket(Value[] args) { + Arguments.check(2, args.length); + final Request request = Values.getRequest(args[0], " at first argument"); + if (args[1].type() != Types.MAP) { + throw new TypeException("Map expected at second argument"); + } + final MapValue callbacks = (MapValue) args[1]; + final WebSocket ws = client.newWebSocket(request, new WebSocketListener() { + @Override + public void onOpen(WebSocket webSocket, Response response) { + final Value func = callbacks.get(onOpen); + if (func != null) { + ValueUtils.consumeFunction(func, " at onOpen").execute( + new WebSocketValue(webSocket), + new ResponseValue(response)); + } + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + final Value func = callbacks.get(onTextMessage); + if (func != null) { + ValueUtils.consumeFunction(func, "at onTextMessage").execute( + new WebSocketValue(webSocket), + new StringValue(text)); + } + } + + @Override + public void onMessage(WebSocket webSocket, ByteString bytes) { + final Value func = callbacks.get(onBytesMessage); + if (func != null) { + ValueUtils.consumeFunction(func, "at onBytesMessage").execute( + new WebSocketValue(webSocket), + ArrayValue.of(bytes.toByteArray())); + } + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + final Value func = callbacks.get(onClosing); + if (func != null) { + ValueUtils.consumeFunction(func, "at onClosing").execute( + new WebSocketValue(webSocket), + NumberValue.of(code), + new StringValue(reason)); + } + } + + @Override + public void onClosed(WebSocket webSocket, int code, String reason) { + final Value func = callbacks.get(onClosed); + if (func != null) { + ValueUtils.consumeFunction(func, "at onClosed").execute( + new WebSocketValue(webSocket), + NumberValue.of(code), + new StringValue(reason)); + } + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + final Value func = callbacks.get(onFailure); + if (func != null) { + ValueUtils.consumeFunction(func, "at onFailure").execute( + new WebSocketValue(webSocket), + new StringValue(t.getMessage()), + new ResponseValue(response)); + } + } + }); + return new CallValue(client.newCall(request)); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java new file mode 100644 index 00000000..b7d891ec --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java @@ -0,0 +1,70 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.util.Map; +import okhttp3.MediaType; +import okhttp3.MultipartBody; + +public class MultipartBodyBuilderValue extends MapValue { + + private final MultipartBody.Builder builder; + + public MultipartBodyBuilderValue() { + super(5); + this.builder = new MultipartBody.Builder(); + init(); + } + + private void init() { + set("addFormData", this::addFormData); + set("addFormDataPart", this::addFormDataPart); + set("addPart", this::addPart); + set("build", args -> new MultipartBodyValue(builder.build())); + set("setType", args -> { + Arguments.check(1, args.length); + builder.setType(MediaType.parse(args[0].asString())); + return this; + }); + } + + private Value addFormDataPart(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + if (args.length == 2) { + builder.addFormDataPart(args[0].asString(), args[1].asString()); + } else { + builder.addFormDataPart( + args[0].asString(), + args[1].asString(), + Values.getRequestBody(args[2], " at third argument")); + } + return this; + } + + private Value addFormData(Value[] args) { + Arguments.check(1, args.length); + if (args[0].type() != Types.MAP) { + throw new TypeException("Map expected at first argument"); + } + for (Map.Entry entry : ((MapValue) args[0])) { + builder.addFormDataPart(entry.getKey().asString(), entry.getValue().asString()); + } + return this; + } + + private Value addPart(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + if (args.length == 1) { + builder.addPart( + Values.getRequestBody(args[0], " at first argument")); + } else { + builder.addPart( + Values.getHeaders(args[0], " at first argument"), + Values.getRequestBody(args[1], " at second argument")); + } + return this; + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java new file mode 100644 index 00000000..41db1584 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java @@ -0,0 +1,24 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Converters; +import okhttp3.MultipartBody; + +public class MultipartBodyValue extends RequestBodyValue { + + private final MultipartBody multipartBody; + + public MultipartBodyValue(MultipartBody multipartBody) { + super(multipartBody, 5); + this.multipartBody = multipartBody; + init(); + } + + public MultipartBody getMultipartBody() { + return multipartBody; + } + + private void init() { + set("boundary", Converters.voidToString(multipartBody::boundary)); + set("size", Converters.voidToInt(multipartBody::size)); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java new file mode 100644 index 00000000..b9ea5903 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.StringValue; +import java.io.IOException; +import java.nio.charset.Charset; +import okhttp3.MediaType; +import okhttp3.RequestBody; + +public class RequestBodyValue extends MapValue { + + private final RequestBody requestBody; + private final MediaType mediaType; + + public RequestBodyValue(RequestBody requestBody) { + this(requestBody, 0); + } + + protected RequestBodyValue(RequestBody requestBody, int methodsCount) { + super(4 + methodsCount); + this.requestBody = requestBody; + this.mediaType = requestBody.contentType(); + init(); + } + + public RequestBody getRequestBody() { + return requestBody; + } + + public MediaType getMediaType() { + return mediaType; + } + + private void init() { + set("getContentLength", Converters.voidToLong(() -> { + try { + return requestBody.contentLength(); + } catch (IOException ex) { + return -1; + } + })); + set("getType", Converters.voidToString(mediaType::type)); + set("getSubtype", Converters.voidToString(mediaType::subtype)); + set("getCharset", args -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + return new StringValue(mediaType.charset().name()); + } else { + return new StringValue(mediaType.charset(Charset.forName(args[0].asString())).name()); + } + }); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java new file mode 100644 index 00000000..51d4edf4 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java @@ -0,0 +1,109 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Converters.VoidToVoidFunction; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +public class RequestBuilderValue extends MapValue { + + private final Request.Builder requestBuilder; + + public RequestBuilderValue() { + super(15); + requestBuilder = new Request.Builder(); + init(); + } + + public Request getRequest() { + return requestBuilder.build(); + } + + private void init() { + set("addHeader", args -> { + Arguments.check(2, args.length); + requestBuilder.addHeader(args[0].asString(), args[1].asString()); + return this; + }); + set("cacheControl", args -> { + Arguments.check(1, args.length); + // TODO + return this; + }); + set("delete", httpMethod(requestBuilder::delete, requestBuilder::delete)); + set("get", args -> { + requestBuilder.get(); + return this; + }); + set("head", args -> { + requestBuilder.head(); + return this; + }); + set("header", args -> { + Arguments.check(2, args.length); + requestBuilder.header(args[0].asString(), args[1].asString()); + return this; + }); + set("headers", args -> { + Arguments.check(1, args.length); + requestBuilder.headers(Values.getHeaders(args[0], " at first argument")); + return this; + }); + set("method", args -> { + Arguments.checkOrOr(1, 2, args.length); + final RequestBody body; + if (args.length == 1) { + body = null; + } else { + body = Values.getRequestBody(args[1], " at second argument"); + } + requestBuilder.method(args[0].asString(), body); + return this; + }); + set("newCall", args -> { + Arguments.check(1, args.length); + final OkHttpClient client = Values.getHttpClient(args[0], " at first argument"); + return new CallValue(client.newCall(getRequest())); + }); + set("patch", httpMethod(requestBuilder::patch)); + set("post", httpMethod(requestBuilder::post)); + set("put", httpMethod(requestBuilder::put)); + set("removeHeader", args -> { + Arguments.check(1, args.length); + requestBuilder.removeHeader(args[0].asString()); + return this; + }); + set("url", args -> { + Arguments.check(1, args.length); + requestBuilder.url(args[0].asString()); + return this; + }); + } + + private Function httpMethod(VoidToVoidFunction voidFunc, RequestBodyToVoidFunction bodyFunc) { + return (args) -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + voidFunc.apply(); + } else { + bodyFunc.apply(Values.getRequestBody(args[0], " at first argument")); + } + return this; + }; + } + + private Function httpMethod(RequestBodyToVoidFunction bodyFunc) { + return (args) -> { + Arguments.check(1, args.length); + bodyFunc.apply(Values.getRequestBody(args[0], " at first argument")); + return this; + }; + } + + private interface RequestBodyToVoidFunction { + void apply(RequestBody value); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java new file mode 100644 index 00000000..faa08451 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import java.io.IOException; +import okhttp3.ResponseBody; +import okio.BufferedSink; +import okio.Okio; + +public class ResponseBodyValue extends MapValue { + + private final ResponseBody responseBody; + + public ResponseBodyValue(ResponseBody response) { + super(8); + this.responseBody = response; + init(); + } + + private void init() { + set("bytes", args -> { + try { + return ArrayValue.of(responseBody.bytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + set("close", Converters.voidToVoid(responseBody::close)); + set("contentLength", Converters.voidToLong(responseBody::contentLength)); + set("contentType", args -> new StringValue(responseBody.contentType().toString())); + set("string", args -> { + try { + return new StringValue(responseBody.string()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + set("file", args -> { + Arguments.check(1, args.length); + try { + BufferedSink sink = Okio.buffer(Okio.sink(Console.fileInstance(args[0].asString()))); + sink.writeAll(responseBody.source()); + sink.close(); + return NumberValue.ONE; + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java new file mode 100644 index 00000000..aee83fbb --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.StringValue; +import java.util.List; +import java.util.Map; +import okhttp3.Response; + +public class ResponseValue extends MapValue { + + private final Response response; + + public ResponseValue(Response response) { + super(15); + this.response = response; + init(); + } + + private void init() { + set("body", args -> new ResponseBodyValue(response.body())); + set("cacheResponse", args -> new ResponseValue(response.cacheResponse())); + set("code", Converters.voidToInt(response::code)); + set("close", Converters.voidToVoid(response::close)); + set("header", args -> { + Arguments.checkOrOr(1, 2, args.length); + final String defaultValue = (args.length == 1) ? null : args[1].asString(); + return new StringValue(response.header(args[0].asString(), defaultValue)); + }); + set("headers", args -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + final Map> headers = response.headers().toMultimap(); + final MapValue result = new MapValue(headers.size()); + for (Map.Entry> entry : headers.entrySet()) { + result.set(entry.getKey(), ArrayValue.of(entry.getValue().toArray(new String[0]))); + } + return result; + } else { + return ArrayValue.of(response.headers(args[0].asString()).toArray(new String[0])); + } + }); + set("message", Converters.voidToString(response::message)); + set("networkResponse", args -> new ResponseValue(response.networkResponse())); + set("priorResponse", args -> new ResponseValue(response.priorResponse())); + set("receivedResponseAtMillis", Converters.voidToLong(response::receivedResponseAtMillis)); + set("sentRequestAtMillis", Converters.voidToLong(response::sentRequestAtMillis)); + } + +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/Values.java b/src/main/java/com/annimon/ownlang/modules/okhttp/Values.java new file mode 100644 index 00000000..eb83ec0e --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/Values.java @@ -0,0 +1,47 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.util.LinkedHashMap; +import java.util.Map; +import okhttp3.Headers; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +public class Values { + + public static RequestBody getRequestBody(Value arg, String msg) { + if (arg.type() == Types.MAP && (arg instanceof RequestBodyValue)) { + return ((RequestBodyValue) arg).getRequestBody(); + } + throw new TypeException("RequestBody value expected" + msg); + } + + public static Request getRequest(Value arg, String msg) { + if (arg.type() == Types.MAP && (arg instanceof RequestBuilderValue)) { + return ((RequestBuilderValue) arg).getRequest(); + } + throw new TypeException("Request value expected" + msg); + } + + public static OkHttpClient getHttpClient(Value arg, String msg) { + if (arg.type() == Types.MAP && (arg instanceof HttpClientValue)) { + return ((HttpClientValue) arg).getClient(); + } + throw new TypeException("HttpClient value expected" + msg); + } + + public static Headers getHeaders(Value arg, String msg) { + if (arg.type() != Types.MAP) { + throw new TypeException("Map expected" + msg); + } + final Map headers = new LinkedHashMap<>(); + for (Map.Entry entry : ((MapValue) arg)) { + headers.put(entry.getKey().asString(), entry.getValue().asString()); + } + return Headers.of(headers); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java b/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java new file mode 100644 index 00000000..9af00d06 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java @@ -0,0 +1,46 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.ValueUtils; +import okhttp3.WebSocket; +import okio.ByteString; + +public class WebSocketValue extends MapValue { + + private final WebSocket ws; + + protected WebSocketValue(WebSocket ws) { + super(4); + this.ws = ws; + init(); + } + + public WebSocket getWebSocket() { + return ws; + } + + private void init() { + set("cancel", Converters.voidToVoid(ws::cancel)); + set("close", args -> { + Arguments.checkOrOr(1, 2, args.length); + final String reason = (args.length == 2) ? args[1].asString() : null; + return NumberValue.fromBoolean(ws.close(args[0].asInt(), reason)); + }); + set("queueSize", Converters.voidToLong(ws::queueSize)); + set("send", args -> { + Arguments.check(1, args.length); + final boolean result; + if (args[0].type() == Types.ARRAY) { + result = ws.send(ByteString.of( ValueUtils.toByteArray(((ArrayValue) args[0])) )); + } else { + result = ws.send(args[0].asString()); + } + return NumberValue.fromBoolean(result); + }); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java b/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java new file mode 100644 index 00000000..cf65b181 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java @@ -0,0 +1,81 @@ +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.ValueUtils; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.modules.Module; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; + +public final class okhttp implements Module { + + private static final HttpClientValue defaultClient = new HttpClientValue(new OkHttpClient()); + + public static void initConstants() { + MapValue requestBody = new MapValue(5); + requestBody.set("bytes", args -> { + Arguments.checkOrOr(2, 4, args.length); + if (args[1].type() != Types.ARRAY) { + throw new TypeException("Array of bytes expected at second argument"); + } + final byte[] bytes = ValueUtils.toByteArray((ArrayValue) args[1]); + final int offset; + final int bytesCount; + if (args.length == 2) { + offset = 0; + bytesCount = bytes.length; + } else { + offset = args[2].asInt(); + bytesCount = args[3].asInt(); + } + return new RequestBodyValue(RequestBody.create( + MediaType.parse(args[0].asString()), + bytes, offset, bytesCount + )); + }); + requestBody.set("file", args -> { + Arguments.check(2, args.length); + return new RequestBodyValue(RequestBody.create( + MediaType.parse(args[0].asString()), + Console.fileInstance(args[1].asString()) + )); + }); + requestBody.set("string", args -> { + Arguments.check(2, args.length); + return new RequestBodyValue(RequestBody.create( + MediaType.parse(args[0].asString()), + args[1].asString() + )); + }); + Variables.define("RequestBody", requestBody); + + + MapValue multipartBody = new MapValue(10); + multipartBody.set("ALTERNATIVE", new StringValue(MultipartBody.ALTERNATIVE.toString())); + multipartBody.set("DIGEST", new StringValue(MultipartBody.DIGEST.toString())); + multipartBody.set("FORM", new StringValue(MultipartBody.FORM.toString())); + multipartBody.set("MIXED", new StringValue(MultipartBody.MIXED.toString())); + multipartBody.set("PARALLEL", new StringValue(MultipartBody.PARALLEL.toString())); + multipartBody.set("builder", args -> new MultipartBodyBuilderValue()); + Variables.define("MultipartBody", multipartBody); + + + MapValue okhttp = new MapValue(5); + okhttp.set("client", defaultClient); + okhttp.set("request", args -> new RequestBuilderValue()); + Variables.define("okhttp", okhttp); + } + + @Override + public void init() { + initConstants(); + } +} From d3b5d9f0595b0eb0c08d222efff957ce14fc7ea5 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 29 May 2019 19:11:53 +0300 Subject: [PATCH 252/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BE=D1=82=D1=81=D1=82=D1=83=D0=BF=20=D0=B2=20jsonen?= =?UTF-8?q?code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/ValueUtils.java | 266 +++++++++--------- .../ownlang/modules/json/json_encode.java | 40 ++- 2 files changed, 169 insertions(+), 137 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 016315c8..ef379a69 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -1,133 +1,133 @@ -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; -import org.json.JSONObject; - -public final class ValueUtils { - - private ValueUtils() { } - - public static Object toObject(Value val) { - switch (val.type()) { - case Types.ARRAY: - return toObject((ArrayValue) val); - case Types.MAP: - return toObject((MapValue) val); - case Types.NUMBER: - return val.raw(); - case Types.STRING: - return val.asString(); - default: - return JSONObject.NULL; - } - } - - public static Object toObject(MapValue map) { - final JSONObject result = new JSONObject(); - for (Map.Entry entry : map) { - final String key = entry.getKey().asString(); - final Object value = toObject(entry.getValue()); - result.put(key, value); - } - return result; - } - - public static Object toObject(ArrayValue array) { - final JSONArray result = new JSONArray(); - for (Value value : array) { - result.put(toObject(value)); - } - return result; - } - - public static Value toValue(Object obj) { - if (obj instanceof JSONObject) { - return toValue((JSONObject) obj); - } - if (obj instanceof JSONArray) { - return toValue((JSONArray) obj); - } - if (obj instanceof String) { - return new StringValue((String) obj); - } - if (obj instanceof Number) { - return NumberValue.of(((Number) obj)); - } - if (obj instanceof Boolean) { - return NumberValue.fromBoolean((Boolean) obj); - } - // NULL or other - return NumberValue.ZERO; - } - - public static MapValue toValue(JSONObject json) { - final MapValue result = new MapValue(json.length()); - final Iterator it = json.keys(); - while(it.hasNext()) { - final String key = it.next(); - final Value value = toValue(json.get(key)); - result.set(new StringValue(key), value); - } - return result; - } - - public static ArrayValue toValue(JSONArray json) { - final int length = json.length(); - final ArrayValue result = new ArrayValue(length); - for (int i = 0; i < length; i++) { - final Value value = toValue(json.get(i)); - result.set(i, value); - } - return result; - } - - public static Number getNumber(Value value) { - if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); - return value.asInt(); - } - - public static float getFloatNumber(Value value) { - if (value.type() == Types.NUMBER) return ((NumberValue) value).raw().floatValue(); - return (float) value.asNumber(); - } - - public static byte[] toByteArray(ArrayValue array) { - final int size = array.size(); - final byte[] result = new byte[size]; - for (int i = 0; i < size; i++) { - result[i] = (byte) array.get(i).asInt(); - } - return result; - } - - public static Function consumeFunction(Value value, int argumentNumber) { - return consumeFunction(value, " at argument " + (argumentNumber + 1)); - } - - public static Function consumeFunction(Value value, String errorMessage) { - final int type = value.type(); - if (type != Types.FUNCTION) { - throw new TypeException("Function expected" + errorMessage - + ", but found " + Types.typeToString(type)); - } - 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; - } -} +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; +import org.json.JSONObject; + +public final class ValueUtils { + + private ValueUtils() { } + + public static Object toObject(Value val) { + switch (val.type()) { + case Types.ARRAY: + return toObject((ArrayValue) val); + case Types.MAP: + return toObject((MapValue) val); + case Types.NUMBER: + return val.raw(); + case Types.STRING: + return val.asString(); + default: + return JSONObject.NULL; + } + } + + public static JSONObject toObject(MapValue map) { + final JSONObject result = new JSONObject(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = toObject(entry.getValue()); + result.put(key, value); + } + return result; + } + + public static JSONArray toObject(ArrayValue array) { + final JSONArray result = new JSONArray(); + for (Value value : array) { + result.put(toObject(value)); + } + return result; + } + + public static Value toValue(Object obj) { + if (obj instanceof JSONObject) { + return toValue((JSONObject) obj); + } + if (obj instanceof JSONArray) { + return toValue((JSONArray) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return NumberValue.of(((Number) obj)); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + public static MapValue toValue(JSONObject json) { + final MapValue result = new MapValue(json.length()); + final Iterator it = json.keys(); + while(it.hasNext()) { + final String key = it.next(); + final Value value = toValue(json.get(key)); + result.set(new StringValue(key), value); + } + return result; + } + + public static ArrayValue toValue(JSONArray json) { + final int length = json.length(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = toValue(json.get(i)); + result.set(i, value); + } + return result; + } + + public static Number getNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); + return value.asInt(); + } + + public static float getFloatNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw().floatValue(); + return (float) value.asNumber(); + } + + public static byte[] toByteArray(ArrayValue array) { + final int size = array.size(); + final byte[] result = new byte[size]; + for (int i = 0; i < size; i++) { + result[i] = (byte) array.get(i).asInt(); + } + return result; + } + + public static Function consumeFunction(Value value, int argumentNumber) { + return consumeFunction(value, " at argument " + (argumentNumber + 1)); + } + + public static Function consumeFunction(Value value, String errorMessage) { + final int type = value.type(); + if (type != Types.FUNCTION) { + throw new TypeException("Function expected" + errorMessage + + ", but found " + Types.typeToString(type)); + } + 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/json/json_encode.java b/src/main/java/com/annimon/ownlang/modules/json/json_encode.java index 7222124d..a7b4e649 100644 --- a/src/main/java/com/annimon/ownlang/modules/json/json_encode.java +++ b/src/main/java/com/annimon/ownlang/modules/json/json_encode.java @@ -1,25 +1,57 @@ package com.annimon.ownlang.modules.json; import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.ValueUtils; import org.json.JSONException; import org.json.JSONObject; +import org.json.JSONWriter; public final class json_encode implements Function { @Override - public Value execute(Value... args) { - Arguments.check(1, args.length); + public Value execute(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); try { - final Object root = ValueUtils.toObject(args[0]); - final String jsonRaw = JSONObject.valueToString(root); + final int indent; + if (args.length == 2) { + indent = args[1].asInt(); + } else { + indent = 0; + } + + final String jsonRaw; + if (indent > 0) { + jsonRaw = format(args[0], indent); + } else { + final Object root = ValueUtils.toObject(args[0]); + jsonRaw = JSONWriter.valueToString(root); + } return new StringValue(jsonRaw); + } catch (JSONException ex) { throw new RuntimeException("Error while creating json", ex); } } + + private String format(Value val, int indent) { + switch (val.type()) { + case Types.ARRAY: + return ValueUtils.toObject((ArrayValue) val).toString(indent); + case Types.MAP: + return ValueUtils.toObject((MapValue) val).toString(indent); + case Types.NUMBER: + return JSONWriter.valueToString(val.raw()); + case Types.STRING: + return JSONWriter.valueToString(val.asString()); + default: + return JSONWriter.valueToString(JSONObject.NULL); + } + } } \ No newline at end of file From 8fd71406295c52397613bb02c05454f3c36ae8b6 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 29 May 2019 22:30:14 +0300 Subject: [PATCH 253/448] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=BF=D0=BE=D1=80=D1=8F=D0=B4=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=B9=20=D1=83=20=D0=BE=D0=B1?= =?UTF-8?q?=D1=8A=D0=B5=D0=BA=D1=82=D0=BE=D0=B2=20=D0=B2=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D1=83=D0=BB=D0=B5=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/ValueUtils.java | 5 +++-- .../java/com/annimon/ownlang/modules/yaml/yaml_encode.java | 4 ++-- .../java/com/annimon/ownlang/utils/ModulesInfoCreator.java | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index ef379a69..c8a2eab5 100644 --- a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -4,6 +4,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; @@ -28,7 +29,7 @@ public static Object toObject(Value val) { } public static JSONObject toObject(MapValue map) { - final JSONObject result = new JSONObject(); + final JSONObject result = new JSONObject(new LinkedHashMap()); for (Map.Entry entry : map) { final String key = entry.getKey().asString(); final Object value = toObject(entry.getValue()); @@ -66,7 +67,7 @@ public static Value toValue(Object obj) { } public static MapValue toValue(JSONObject json) { - final MapValue result = new MapValue(json.length()); + final MapValue result = new MapValue(new LinkedHashMap<>(json.length())); final Iterator it = json.keys(); while(it.hasNext()) { final String key = it.next(); diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java index 9c00be4c..2620c9be 100644 --- a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.lib.*; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.yaml.snakeyaml.Yaml; @@ -37,7 +37,7 @@ private Object process(Value val) { } private Object process(MapValue map) { - final Map result = new HashMap<>(); + final Map result = new LinkedHashMap<>(); for (Map.Entry entry : map) { final String key = entry.getKey().asString(); final Object value = process(entry.getValue()); diff --git a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index b9ec788e..7cc70689 100644 --- a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -131,7 +131,7 @@ public List> constants() { constant.put("type", value.type()); constant.put("typeName", Types.typeToString(value.type())); if (value.type() == Types.MAP) { - String text = ((Map) value.raw()).entrySet().stream() + String text = ((MapValue) value).getMap().entrySet().stream() .sorted(Comparator.comparing( e -> ((MapValue)value).size() > 16 ? e.getKey() : e.getValue())) .map(Object::toString) From d8fde244b0cfe62bc49114c1655ed725c7a01111 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 29 May 2019 23:29:53 +0300 Subject: [PATCH 254/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=BD=D0=B0=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B8=D1=82=D1=8C=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D1=8B=20=D0=BF=D0=B0=D1=80=D1=81=D0=B5=D1=80=D0=B0?= =?UTF-8?q?/=D0=B4=D0=B0=D0=BC=D0=BF=D0=B5=D1=80=D0=B0=20yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/modules/yaml/yaml_decode.java | 17 +++++++-- .../ownlang/modules/yaml/yaml_encode.java | 38 ++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java index 07e3f198..472b7430 100644 --- a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -1,23 +1,34 @@ package com.annimon.ownlang.modules.yaml; import com.annimon.ownlang.lib.*; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; public final class yaml_decode implements Function { @Override public Value execute(Value... args) { - Arguments.check(1, args.length); + Arguments.checkOrOr(1, 2, args.length); try { final String yamlRaw = args[0].asString(); - final Object root = new Yaml().load(yamlRaw); + final LoaderOptions options = new LoaderOptions(); + if (args.length == 2 && args[1].type() == Types.MAP) { + configure(options, ((MapValue) args[1])); + } + final Object root = new Yaml(options).load(yamlRaw); return process(root); } catch (Exception ex) { throw new RuntimeException("Error while parsing yaml", ex); } } + + private void configure(LoaderOptions options, MapValue map) { + map.ifPresent("allowDuplicateKeys", value -> + options.setAllowDuplicateKeys(value.asInt() != 0)); + } private Value process(Object obj) { if (obj instanceof Map) { @@ -40,7 +51,7 @@ private Value process(Object obj) { } private MapValue process(Map map) { - final MapValue result = new MapValue(map.size()); + final MapValue result = new MapValue(new LinkedHashMap<>(map.size())); for (Map.Entry entry : map.entrySet()) { final String key = entry.getKey().toString(); final Value value = process(entry.getValue()); diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java index 2620c9be..5b30a22e 100644 --- a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java @@ -5,21 +5,55 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; public final class yaml_encode implements Function { @Override public Value execute(Value... args) { - Arguments.check(1, args.length); + Arguments.checkOrOr(1, 2, args.length); try { final Object root = process(args[0]); - final String yamlRaw = new Yaml().dump(root); + final DumperOptions options = new DumperOptions(); + if (args.length == 2 && args[1].type() == Types.MAP) { + configure(options, ((MapValue) args[1])); + } + final String yamlRaw = new Yaml(options).dump(root); return new StringValue(yamlRaw); } catch (Exception ex) { throw new RuntimeException("Error while creating yaml", ex); } } + + private void configure(DumperOptions options, MapValue map) { + map.ifPresent("allowReadOnlyProperties", value -> + options.setAllowReadOnlyProperties(value.asInt() != 0)); + map.ifPresent("allowUnicode", value -> + options.setAllowUnicode(value.asInt() != 0)); + map.ifPresent("canonical", value -> + options.setCanonical(value.asInt() != 0)); + map.ifPresent("defaultFlowStyle", value -> + options.setDefaultFlowStyle(DumperOptions.FlowStyle.valueOf(value.asString()))); + map.ifPresent("defaultScalarStyle", value -> + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.valueOf(value.asString()))); + map.ifPresent("explicitEnd", value -> + options.setExplicitEnd(value.asInt() != 0)); + map.ifPresent("explicitStart", value -> + options.setExplicitStart(value.asInt() != 0)); + map.ifPresent("indent", value -> + options.setIndent(value.asInt())); + map.ifPresent("indicatorIndent", value -> + options.setIndicatorIndent(value.asInt())); + map.ifPresent("lineBreak", value -> + options.setLineBreak(DumperOptions.LineBreak.valueOf(value.asString()))); + map.ifPresent("prettyFlow", value -> + options.setPrettyFlow(value.asInt() != 0)); + map.ifPresent("splitLines", value -> + options.setSplitLines(value.asInt() != 0)); + map.ifPresent("width", value -> + options.setWidth(value.asInt())); + } private Object process(Value val) { switch (val.type()) { From 2e439d73b5bd67b3b8fefc32b29b97b7723f8cc8 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 4 Jul 2019 22:09:21 +0300 Subject: [PATCH 255/448] =?UTF-8?q?MapValue=20=D0=BF=D0=BE=20=D1=83=D0=BC?= =?UTF-8?q?=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8=D1=8E=20=D1=81=D0=BE=D1=85?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D1=8F=D0=B5=D1=82=20=D0=BF=D0=BE=D1=80=D1=8F?= =?UTF-8?q?=D0=B4=D0=BE=D0=BA=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/MapValue.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java index 5d3d33e6..1d8c7d1e 100644 --- a/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -1,8 +1,8 @@ package com.annimon.ownlang.lib; import com.annimon.ownlang.exceptions.TypeException; -import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.function.Consumer; @@ -25,7 +25,7 @@ public static MapValue merge(MapValue map1, MapValue map2) { private final Map map; public MapValue(int size) { - this.map = new HashMap<>(size); + this.map = new LinkedHashMap<>(size); } public MapValue(Map map) { @@ -84,6 +84,10 @@ public void set(String key, Function function) { public void set(Value key, Value value) { map.put(key, value); } + + public Map getMap() { + return map; + } @Override public Object raw() { From 0bd2aa10d51691dea4afb2509644047fab2b3dde Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 6 Jul 2019 20:32:31 +0300 Subject: [PATCH 256/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=B4=D0=B8=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=81=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Lexer.java | 1 + .../com/annimon/ownlang/parser/Parser.java | 180 ++++++++++-------- .../com/annimon/ownlang/parser/TokenType.java | 1 + .../parser/ast/ConditionalExpression.java | 80 +++++--- .../resources/expressions/nullCoalesce.own | 27 +++ 5 files changed, 180 insertions(+), 109 deletions(-) create mode 100644 src/test/resources/expressions/nullCoalesce.own diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java index 1884f920..3bc71f54 100644 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -83,6 +83,7 @@ public static List tokenize(String input) { OPERATORS.put("**", TokenType.STARSTAR); OPERATORS.put("^^", TokenType.CARETCARET); OPERATORS.put("?:", TokenType.QUESTIONCOLON); + OPERATORS.put("??", TokenType.QUESTIONQUESTION); } private static final Map KEYWORDS; diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 0af2d426..545300f7 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -17,7 +17,7 @@ * @author aNNiMON */ public final class Parser { - + public static Statement parse(List tokens) { final Parser parser = new Parser(tokens); final Statement program = parser.parse(); @@ -26,7 +26,7 @@ public static Statement parse(List tokens) { } return program; } - + private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); private static final EnumMap ASSIGN_OPERATORS; @@ -52,7 +52,7 @@ public static Statement parse(List tokens) { private final int size; private final ParseErrors parseErrors; private Statement parsedStatement; - + private int pos; public Parser(List tokens) { @@ -60,15 +60,15 @@ public Parser(List tokens) { size = tokens.size(); parseErrors = new ParseErrors(); } - + public Statement getParsedStatement() { return parsedStatement; } - + public ParseErrors getParseErrors() { return parseErrors; } - + public Statement parse() { parseErrors.clear(); final BlockStatement result = new BlockStatement(); @@ -83,13 +83,13 @@ public Statement parse() { parsedStatement = result; return result; } - + private int getErrorLine() { if (size == 0) return 0; if (pos >= size) return tokens.get(size - 1).getRow(); return tokens.get(pos).getRow(); } - + private void recover() { int preRecoverPosition = pos; for (int i = preRecoverPosition; i <= size; i++) { @@ -104,7 +104,7 @@ private void recover() { } } } - + private Statement block() { final BlockStatement block = new BlockStatement(); consume(TokenType.LBRACE); @@ -113,12 +113,12 @@ private Statement block() { } return block; } - + private Statement statementOrBlock() { if (lookMatch(0, TokenType.LBRACE)) return block(); return statement(); } - + private Statement statement() { if (match(TokenType.PRINT)) { return new PrintStatement(expression()); @@ -164,7 +164,7 @@ private Statement statement() { } return assignmentStatement(); } - + private Statement assignmentStatement() { if (match(TokenType.EXTRACT)) { return destructuringAssignment(); @@ -175,7 +175,7 @@ private Statement assignmentStatement() { } throw new ParseException("Unknown statement: " + get(0)); } - + private DestructuringAssignmentStatement destructuringAssignment() { // extract(var1, var2, ...) = ... consume(TokenType.LPAREN); @@ -191,7 +191,7 @@ private DestructuringAssignmentStatement destructuringAssignment() { consume(TokenType.EQ); return new DestructuringAssignmentStatement(variables, expression()); } - + private Statement ifElse() { final Expression condition = expression(); final Statement ifStatement = statementOrBlock(); @@ -203,20 +203,20 @@ private Statement ifElse() { } return new IfStatement(condition, ifStatement, elseStatement); } - + private Statement whileStatement() { final Expression condition = expression(); final Statement statement = statementOrBlock(); return new WhileStatement(condition, statement); } - + private Statement doWhileStatement() { final Statement statement = statementOrBlock(); consume(TokenType.WHILE); final Expression condition = expression(); return new DoWhileStatement(condition, statement); } - + private Statement forStatement() { int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0; if (lookMatch(foreachIndex, TokenType.WORD) @@ -243,7 +243,7 @@ && lookMatch(foreachIndex + 3, TokenType.COLON)) { final Statement statement = statementOrBlock(); return new ForStatement(initialization, termination, increment, statement); } - + private ForeachArrayStatement foreachArrayStatement() { // for x : arr boolean optParentheses = match(TokenType.LPAREN); @@ -256,7 +256,7 @@ private ForeachArrayStatement foreachArrayStatement() { final Statement statement = statementOrBlock(); return new ForeachArrayStatement(variable, container, statement); } - + private ForeachMapStatement foreachMapStatement() { // for k, v : map boolean optParentheses = match(TokenType.LPAREN); @@ -271,7 +271,7 @@ private ForeachMapStatement foreachMapStatement() { final Statement statement = statementOrBlock(); return new ForeachMapStatement(key, value, container, statement); } - + private FunctionDefineStatement functionDefine() { // def name(arg1, arg2 = value) { ... } || def name(args) = expr final String name = consume(TokenType.WORD).getText(); @@ -279,7 +279,7 @@ private FunctionDefineStatement functionDefine() { final Statement body = statementBody(); return new FunctionDefineStatement(name, arguments, body); } - + private Arguments arguments() { // (arg1, arg2, arg3 = expr1, arg4 = expr2) final Arguments arguments = new Arguments(); @@ -299,14 +299,14 @@ private Arguments arguments() { } return arguments; } - + private Statement statementBody() { if (match(TokenType.EQ)) { return new ReturnStatement(expression()); } return statementOrBlock(); } - + private Expression functionChain(Expression qualifiedNameExpr) { // f1()()() || f1().f2().f3() || f1().key final Expression expr = function(qualifiedNameExpr); @@ -339,7 +339,7 @@ private FunctionalExpression function(Expression qualifiedNameExpr) { } return function; } - + private Expression array() { // [value1, value2, ...] consume(TokenType.LBRACKET); @@ -350,7 +350,7 @@ private Expression array() { } return new ArrayExpression(elements); } - + private Expression map() { // {key1 : value1, key2 : value2, ...} consume(TokenType.LBRACE); @@ -364,7 +364,7 @@ private Expression map() { } return new MapExpression(elements); } - + private MatchExpression match() { // match expression { // case pattern1: result1 @@ -378,12 +378,12 @@ private MatchExpression match() { MatchExpression.Pattern pattern = null; final Token current = get(0); if (match(TokenType.NUMBER)) { - // case 0.5: + // case 0.5: pattern = new MatchExpression.ConstantPattern( NumberValue.of(createNumber(current.getText(), 10)) ); } else if (match(TokenType.HEX_NUMBER)) { - // case #FF: + // case #FF: pattern = new MatchExpression.ConstantPattern( NumberValue.of(createNumber(current.getText(), 16)) ); @@ -393,7 +393,7 @@ private MatchExpression match() { new StringValue(current.getText()) ); } else if (match(TokenType.WORD)) { - // case value: + // case value: pattern = new MatchExpression.VariablePattern(current.getText()); } else if (match(TokenType.LBRACKET)) { // case [x :: xs]: @@ -417,7 +417,7 @@ private MatchExpression match() { } pattern = tuplePattern; } - + if (pattern == null) { throw new ParseException("Wrong pattern in match expression: " + current); } @@ -425,7 +425,7 @@ private MatchExpression match() { // case e if e > 0: pattern.optCondition = expression(); } - + consume(TokenType.COLON); if (lookMatch(0, TokenType.LBRACE)) { pattern.result = block(); @@ -434,14 +434,14 @@ private MatchExpression match() { } patterns.add(pattern); } while (!match(TokenType.RBRACE)); - + return new MatchExpression(expression, patterns); } - + private Expression expression() { return assignment(); } - + private Expression assignment() { final Expression assignment = assignmentStrict(); if (assignment != null) { @@ -449,7 +449,7 @@ private Expression assignment() { } return ternary(); } - + private Expression assignmentStrict() { // x[0].prop += ... final int position = pos; @@ -465,16 +465,16 @@ private Expression assignmentStrict() { return null; } match(currentType); - + final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType); final Expression expression = expression(); - + return new AssignmentExpression(op, (Accessible) targetExpr, expression); } - + private Expression ternary() { - Expression result = logicalOr(); - + Expression result = nullCoalesce(); + if (match(TokenType.QUESTION)) { final Expression trueExpr = expression(); consume(TokenType.COLON); @@ -486,10 +486,24 @@ private Expression ternary() { } return result; } - + + private Expression nullCoalesce() { + Expression result = logicalOr(); + + while (true) { + if (match(TokenType.QUESTIONQUESTION)) { + result = new ConditionalExpression(ConditionalExpression.Operator.NULL_COALESCE, result, expression()); + continue; + } + break; + } + + return result; + } + private Expression logicalOr() { Expression result = logicalAnd(); - + while (true) { if (match(TokenType.BARBAR)) { result = new ConditionalExpression(ConditionalExpression.Operator.OR, result, logicalAnd()); @@ -497,13 +511,13 @@ private Expression logicalOr() { } break; } - + return result; } - + private Expression logicalAnd() { Expression result = bitwiseOr(); - + while (true) { if (match(TokenType.AMPAMP)) { result = new ConditionalExpression(ConditionalExpression.Operator.AND, result, bitwiseOr()); @@ -511,13 +525,13 @@ private Expression logicalAnd() { } break; } - + return result; } - + private Expression bitwiseOr() { Expression expression = bitwiseXor(); - + while (true) { if (match(TokenType.BAR)) { expression = new BinaryExpression(BinaryExpression.Operator.OR, expression, bitwiseXor()); @@ -525,13 +539,13 @@ private Expression bitwiseOr() { } break; } - + return expression; } - + private Expression bitwiseXor() { Expression expression = bitwiseAnd(); - + while (true) { if (match(TokenType.CARET)) { expression = new BinaryExpression(BinaryExpression.Operator.XOR, expression, bitwiseAnd()); @@ -539,13 +553,13 @@ private Expression bitwiseXor() { } break; } - + return expression; } - + private Expression bitwiseAnd() { Expression expression = equality(); - + while (true) { if (match(TokenType.AMP)) { expression = new BinaryExpression(BinaryExpression.Operator.AND, expression, equality()); @@ -553,26 +567,26 @@ private Expression bitwiseAnd() { } break; } - + return expression; } - + private Expression equality() { Expression result = conditional(); - + if (match(TokenType.EQEQ)) { return new ConditionalExpression(ConditionalExpression.Operator.EQUALS, result, conditional()); } if (match(TokenType.EXCLEQ)) { return new ConditionalExpression(ConditionalExpression.Operator.NOT_EQUALS, result, conditional()); } - + return result; } - + private Expression conditional() { Expression result = shift(); - + while (true) { if (match(TokenType.LT)) { result = new ConditionalExpression(ConditionalExpression.Operator.LT, result, shift()); @@ -592,13 +606,13 @@ private Expression conditional() { } break; } - + return result; } - + private Expression shift() { Expression expression = additive(); - + while (true) { if (match(TokenType.LTLT)) { expression = new BinaryExpression(BinaryExpression.Operator.LSHIFT, expression, additive()); @@ -618,13 +632,13 @@ private Expression shift() { } break; } - + return expression; } - + private Expression additive() { Expression result = multiplicative(); - + while (true) { if (match(TokenType.PLUS)) { result = new BinaryExpression(BinaryExpression.Operator.ADD, result, multiplicative()); @@ -648,13 +662,13 @@ private Expression additive() { } break; } - + return result; } - + private Expression multiplicative() { Expression result = unary(); - + while (true) { if (match(TokenType.STAR)) { result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary()); @@ -674,10 +688,10 @@ private Expression multiplicative() { } break; } - + return result; } - + private Expression unary() { if (match(TokenType.PLUSPLUS)) { return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary()); @@ -699,14 +713,14 @@ private Expression unary() { } return primary(); } - + private Expression primary() { if (match(TokenType.LPAREN)) { Expression result = expression(); consume(TokenType.RPAREN); return result; } - + if (match(TokenType.COLONCOLON)) { // ::method reference final String functionName = consume(TokenType.WORD).getText(); @@ -729,7 +743,7 @@ private Expression variable() { if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return functionChain(new ValueExpression(consume(TokenType.WORD).getText())); } - + final Expression qualifiedNameExpr = qualifiedName(); if (qualifiedNameExpr != null) { // variable(args) || arr["key"](args) || obj.key(args) @@ -745,7 +759,7 @@ private Expression variable() { } return qualifiedNameExpr; } - + if (lookMatch(0, TokenType.LBRACKET)) { return array(); } @@ -754,12 +768,12 @@ private Expression variable() { } return value(); } - + private Expression qualifiedName() { // var || var.key[index].key2 final Token current = get(0); if (!match(TokenType.WORD)) return null; - + final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { return new VariableExpression(current.getText()); @@ -786,7 +800,7 @@ private List variableSuffix() { } return indices; } - + private Expression value() { final Token current = get(0); if (match(TokenType.NUMBER)) { @@ -816,7 +830,7 @@ private Expression value() { } throw new ParseException("Unknown expression: " + current); } - + private Number createNumber(String text, int radix) { // Double if (text.contains(".")) { @@ -829,7 +843,7 @@ private Number createNumber(String text, int radix) { return Long.parseLong(text, radix); } } - + private Token consume(TokenType type) { final Token current = get(0); if (type != current.getType()) { @@ -838,7 +852,7 @@ private Token consume(TokenType type) { pos++; return current; } - + private boolean match(TokenType type) { final Token current = get(0); if (type != current.getType()) { @@ -847,11 +861,11 @@ private boolean match(TokenType type) { pos++; return true; } - + private boolean lookMatch(int pos, TokenType type) { return get(pos).getType() == type; } - + private Token get(int relativePosition) { final int position = pos + relativePosition; if (position >= size) return EOF; diff --git a/src/main/java/com/annimon/ownlang/parser/TokenType.java b/src/main/java/com/annimon/ownlang/parser/TokenType.java index d30bbd29..04187654 100644 --- a/src/main/java/com/annimon/ownlang/parser/TokenType.java +++ b/src/main/java/com/annimon/ownlang/parser/TokenType.java @@ -69,6 +69,7 @@ public enum TokenType { DOTDOT, // .. STARSTAR, // ** QUESTIONCOLON, // ?: + QUESTIONQUESTION, // ?? TILDE, // ~ CARET, // ^ diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java index b39d7964..01630014 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -10,19 +10,21 @@ * @author aNNiMON */ public final class ConditionalExpression implements Expression { - + public enum Operator { EQUALS("=="), NOT_EQUALS("!="), - + LT("<"), LTEQ("<="), GT(">"), GTEQ(">="), - + AND("&&"), - OR("||"); - + OR("||"), + + NULL_COALESCE("??"); + private final String name; private Operator(String name) { @@ -33,7 +35,7 @@ public String getName() { return name; } } - + public final Expression expr1, expr2; public final Operator operation; @@ -45,17 +47,24 @@ public ConditionalExpression(Operator operation, Expression expr1, Expression ex @Override public Value eval() { - final Value value1 = expr1.eval(); switch (operation) { - case AND: return NumberValue.fromBoolean( - (value1.asInt() != 0) && (expr2.eval().asInt() != 0) ); - case OR: return NumberValue.fromBoolean( - (value1.asInt() != 0) || (expr2.eval().asInt() != 0) ); + case AND: + return NumberValue.fromBoolean((expr1AsInt() != 0) && (expr2AsInt() != 0)); + case OR: + return NumberValue.fromBoolean((expr1AsInt() != 0) || (expr2AsInt() != 0)); + + case NULL_COALESCE: + return nullCoalesce(); + + default: + return NumberValue.fromBoolean(evalAndCompare()); } - - + } + + private boolean evalAndCompare() { + final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); - + double number1, number2; if (value1.type() == Types.NUMBER) { number1 = value1.asNumber(); @@ -64,23 +73,42 @@ public Value eval() { number1 = value1.compareTo(value2); number2 = 0; } - - boolean result; + switch (operation) { - case EQUALS: result = number1 == number2; break; - case NOT_EQUALS: result = number1 != number2; break; - - case LT: result = number1 < number2; break; - case LTEQ: result = number1 <= number2; break; - case GT: result = number1 > number2; break; - case GTEQ: result = number1 >= number2; break; - + case EQUALS: return number1 == number2; + case NOT_EQUALS: return number1 != number2; + + case LT: return number1 < number2; + case LTEQ: return number1 <= number2; + case GT: return number1 > number2; + case GTEQ: return number1 >= number2; + default: throw new OperationIsNotSupportedException(operation); } - return NumberValue.fromBoolean(result); } - + + private Value nullCoalesce() { + Value value1; + try { + value1 = expr1.eval(); + } catch (NullPointerException npe) { + value1 = null; + } + if (value1 == null) { + return expr2.eval(); + } + return value1; + } + + private int expr1AsInt() { + return expr1.eval().asInt(); + } + + private int expr2AsInt() { + return expr2.eval().asInt(); + } + @Override public void accept(Visitor visitor) { visitor.visit(this); diff --git a/src/test/resources/expressions/nullCoalesce.own b/src/test/resources/expressions/nullCoalesce.own new file mode 100644 index 00000000..d0e1f2c8 --- /dev/null +++ b/src/test/resources/expressions/nullCoalesce.own @@ -0,0 +1,27 @@ +def testZero() { + assertEquals(0, 0 ?? 1) + x = 0 + assertEquals(0, x ?? 2) +} + +def testObject() { + obj = {"a": 12} + assertEquals(12, obj.a ?? 10) +} + +def testObjectMissingKey() { + obj = {"a": 12} + assertEquals(10, obj.test ?? 10) +} + +def testNestedObjects() { + obj = {"a": {"b": 12}} + assertEquals(12, obj.a.b ?? 10) +} + +def testNestedObjectsMissingKey() { + obj = {"a": {"b": 12}} + assertEquals(1, obj.test ?? 1) + assertEquals(2, obj.a.test ?? 2) + assertEquals(3, obj.test1.test2 ?? 3) +} From 6774dc10041d53be6893ea8c4c0c4345a892b8bb Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 7 Aug 2019 13:35:13 +0300 Subject: [PATCH 257/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/basics/classes.own | 27 +++++ .../exceptions/UnknownClassException.java | 15 +++ .../ownlang/lib/ClassDeclarations.java | 37 +++++++ .../ownlang/lib/ClassInstanceValue.java | 98 +++++++++++++++++++ .../com/annimon/ownlang/lib/ClassMethod.java | 26 +++++ .../java/com/annimon/ownlang/lib/Classes.java | 36 +++++++ .../java/com/annimon/ownlang/lib/Types.java | 9 +- .../ownlang/lib/UserDefinedFunction.java | 2 +- .../com/annimon/ownlang/parser/Lexer.java | 2 + .../com/annimon/ownlang/parser/Parser.java | 54 ++++++++-- .../com/annimon/ownlang/parser/TokenType.java | 2 + .../ownlang/parser/ast/BinaryExpression.java | 2 +- .../parser/ast/ClassDeclarationStatement.java | 46 +++++++++ .../parser/ast/ContainerAccessExpression.java | 3 + .../parser/ast/ObjectCreationExpression.java | 67 +++++++++++++ .../ownlang/parser/ast/ResultVisitor.java | 2 + .../annimon/ownlang/parser/ast/Visitor.java | 2 + .../optimization/OptimizationVisitor.java | 23 +++++ .../parser/visitors/AbstractVisitor.java | 12 +++ .../ownlang/parser/visitors/PrintVisitor.java | 45 +++++++-- 20 files changed, 491 insertions(+), 19 deletions(-) create mode 100644 examples/basics/classes.own create mode 100644 src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java create mode 100644 src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java create mode 100644 src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java create mode 100644 src/main/java/com/annimon/ownlang/lib/ClassMethod.java create mode 100644 src/main/java/com/annimon/ownlang/lib/Classes.java create mode 100644 src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java create mode 100644 src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java diff --git a/examples/basics/classes.own b/examples/basics/classes.own new file mode 100644 index 00000000..b46c12f2 --- /dev/null +++ b/examples/basics/classes.own @@ -0,0 +1,27 @@ +use ["std"] + +class Point { + def Point(x = 0, y = 0) { + this.x = x + this.y = y + } + + def moveBy(p) { + this.move(p.x, p.y) + } + + def move(dx, dy) { + this.x += dx + this.y += dy + } + + def toString() = "(" + this.x + ", " + this.y + ")" +} + +p = new Point(20, 30) +p.move(10, -5) +println p.toString() + +p2 = new Point(1, 1) +p2.moveBy(p) +println p2.toString() diff --git a/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java b/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java new file mode 100644 index 00000000..40c29b9d --- /dev/null +++ b/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.exceptions; + +public final class UnknownClassException extends RuntimeException { + + private final String className; + + public UnknownClassException(String name) { + super("Unknown class " + name); + this.className = name; + } + + public String getClassName() { + return className; + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java b/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java new file mode 100644 index 00000000..1335b1b2 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java @@ -0,0 +1,37 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.exceptions.UnknownFunctionException; +import com.annimon.ownlang.parser.ast.ClassDeclarationStatement; +import java.util.HashMap; +import java.util.Map; + +public final class ClassDeclarations { + + private static final Map declarations; + static { + declarations = new HashMap<>(); + } + + private ClassDeclarations() { } + + public static void clear() { + declarations.clear(); + } + + public static Map getAll() { + return declarations; + } + + public static boolean isExists(String key) { + return declarations.containsKey(key); + } + + public static ClassDeclarationStatement get(String key) { + if (!isExists(key)) throw new UnknownFunctionException(key); + return declarations.get(key); + } + + public static void set(String key, ClassDeclarationStatement classDef) { + declarations.put(key, classDef); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java new file mode 100644 index 00000000..febcfa96 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java @@ -0,0 +1,98 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.exceptions.TypeException; +import java.util.Objects; + +public class ClassInstanceValue implements Value { + + private final String className; + private final MapValue thisMap; + private ClassMethod constructor; + + public ClassInstanceValue(String name) { + this.className = name; + thisMap = new MapValue(10); + } + + public MapValue getThisMap() { + return thisMap; + } + + public String getClassName() { + return className; + } + + public void addField(String name, Value value) { + thisMap.set(name, value); + } + + public void addMethod(String name, ClassMethod method) { + thisMap.set(name, method); + if (name.equals(className)) { + constructor = method; + } + } + + public void callConstructor(Value[] args) { + if (constructor != null) { + constructor.execute(args); + } + } + + public Value access(Value value) { + return thisMap.get(value); + } + + @Override + public Object raw() { + return null; + } + + @Override + public int asInt() { + throw new TypeException("Cannot cast class to integer"); + } + + @Override + public double asNumber() { + throw new TypeException("Cannot cast class to integer"); + } + + @Override + public String asString() { + return "class " + className + "@" + thisMap; + } + + @Override + public int type() { + return Types.CLASS; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 37 * hash + Objects.hash(className, thisMap); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) + return false; + final ClassInstanceValue other = (ClassInstanceValue) obj; + return Objects.equals(this.className, other.className) + && Objects.equals(this.thisMap, other.thisMap); + } + + @Override + public int compareTo(Value o) { + return asString().compareTo(o.asString()); + } + + @Override + public String toString() { + return asString(); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/src/main/java/com/annimon/ownlang/lib/ClassMethod.java new file mode 100644 index 00000000..a5950398 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/ClassMethod.java @@ -0,0 +1,26 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.parser.ast.Arguments; +import com.annimon.ownlang.parser.ast.Statement; + +public class ClassMethod extends UserDefinedFunction { + + public final ClassInstanceValue classInstance; + + public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance) { + super(arguments, body); + this.classInstance = classInstance; + } + + @Override + public Value execute(Value[] values) { + Variables.push(); + Variables.define("this", classInstance.getThisMap()); + + try { + return super.execute(values); + } finally { + Variables.pop(); + } + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/Classes.java b/src/main/java/com/annimon/ownlang/lib/Classes.java new file mode 100644 index 00000000..488d5466 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/Classes.java @@ -0,0 +1,36 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.exceptions.UnknownClassException; +import java.util.HashMap; +import java.util.Map; + +public final class Classes { + + private static final Map classes; + static { + classes = new HashMap<>(); + } + + private Classes() { } + + public static void clear() { + classes.clear(); + } + + public static Map getFunctions() { + return classes; + } + + public static boolean isExists(String key) { + return classes.containsKey(key); + } + + public static ClassInstanceValue get(String key) { + if (!isExists(key)) throw new UnknownClassException(key); + return classes.get(key); + } + + public static void set(String key, ClassInstanceValue classDef) { + classes.put(key, classDef); + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/Types.java b/src/main/java/com/annimon/ownlang/lib/Types.java index 8c0b2160..ab533687 100644 --- a/src/main/java/com/annimon/ownlang/lib/Types.java +++ b/src/main/java/com/annimon/ownlang/lib/Types.java @@ -8,11 +8,14 @@ public final class Types { STRING = 2, ARRAY = 3, MAP = 4, - FUNCTION = 5; + FUNCTION = 5, + CLASS = 6; private static final int FIRST = OBJECT; - private static final int LAST = FUNCTION; - private static final String[] NAMES = {"object", "number", "string", "array", "map", "function"}; + private static final int LAST = CLASS; + private static final String[] NAMES = { + "object", "number", "string", "array", "map", "function", "class" + }; public static String typeToString(int type) { if (FIRST <= type && type <= LAST) { diff --git a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index 965be46f..6b0ba780 100644 --- a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -10,7 +10,7 @@ * * @author aNNiMON */ -public final class UserDefinedFunction implements Function { +public class UserDefinedFunction implements Function { public final Arguments arguments; public final Statement body; diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java index 3bc71f54..4bed5181 100644 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -105,6 +105,8 @@ public static List tokenize(String input) { KEYWORDS.put("case", TokenType.CASE); KEYWORDS.put("extract", TokenType.EXTRACT); KEYWORDS.put("include", TokenType.INCLUDE); + KEYWORDS.put("class", TokenType.CLASS); + KEYWORDS.put("new", TokenType.NEW); } public static Set getKeywords() { diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 545300f7..b23851b4 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -159,6 +159,9 @@ private Statement statement() { if (match(TokenType.MATCH)) { return match(); } + if (match(TokenType.CLASS)) { + return classDeclaration(); + } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return new ExprStatement(functionChain(qualifiedName())); } @@ -437,6 +440,30 @@ private MatchExpression match() { return new MatchExpression(expression, patterns); } + + private Statement classDeclaration() { + // class Name { + // x = 123 + // str = "" + // def method() = str + // } + final String name = consume(TokenType.WORD).getText(); + final ClassDeclarationStatement classDeclaration = new ClassDeclarationStatement(name); + consume(TokenType.LBRACE); + do { + if (match(TokenType.DEF)) { + classDeclaration.addMethod(functionDefine()); + } else { + final AssignmentExpression fieldDeclaration = assignmentStrict(); + if (fieldDeclaration != null) { + classDeclaration.addField(fieldDeclaration); + } else { + throw new ParseException("Class can contain only assignments and function declarations"); + } + } + } while (!match(TokenType.RBRACE)); + return classDeclaration; + } private Expression expression() { return assignment(); @@ -450,7 +477,7 @@ private Expression assignment() { return ternary(); } - private Expression assignmentStrict() { + private AssignmentExpression assignmentStrict() { // x[0].prop += ... final int position = pos; final Expression targetExpr = qualifiedName(); @@ -667,23 +694,23 @@ private Expression additive() { } private Expression multiplicative() { - Expression result = unary(); + Expression result = objectCreation(); while (true) { if (match(TokenType.STAR)) { - result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary()); + result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, expression()); continue; } if (match(TokenType.SLASH)) { - result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, unary()); + result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, expression()); continue; } if (match(TokenType.PERCENT)) { - result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, unary()); + result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, expression()); continue; } if (match(TokenType.STARSTAR)) { - result = new BinaryExpression(BinaryExpression.Operator.POWER, result, unary()); + result = new BinaryExpression(BinaryExpression.Operator.POWER, result, expression()); continue; } break; @@ -691,6 +718,21 @@ private Expression multiplicative() { return result; } + + private Expression objectCreation() { + if (match(TokenType.NEW)) { + final String className = consume(TokenType.WORD).getText(); + final List args = new ArrayList<>(); + consume(TokenType.LPAREN); + while (!match(TokenType.RPAREN)) { + args.add(expression()); + match(TokenType.COMMA); + } + return new ObjectCreationExpression(className, args); + } + + return unary(); + } private Expression unary() { if (match(TokenType.PLUSPLUS)) { diff --git a/src/main/java/com/annimon/ownlang/parser/TokenType.java b/src/main/java/com/annimon/ownlang/parser/TokenType.java index 04187654..87d15eb5 100644 --- a/src/main/java/com/annimon/ownlang/parser/TokenType.java +++ b/src/main/java/com/annimon/ownlang/parser/TokenType.java @@ -28,6 +28,8 @@ public enum TokenType { CASE, EXTRACT, INCLUDE, + CLASS, + NEW, PLUS, // + MINUS, // - diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java index 999e94e8..b6cdab0b 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -308,7 +308,7 @@ private Value push(Value value1, Value value2) { "for " + Types.typeToString(value1.type())); } } - + private Value and(Value value1, Value value2) { switch (value1.type()) { case Types.NUMBER: return and((NumberValue) value1, value2); diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java new file mode 100644 index 00000000..92a272c4 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java @@ -0,0 +1,46 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.ClassDeclarations; +import java.util.ArrayList; +import java.util.List; + +public final class ClassDeclarationStatement implements Statement { + + public final String name; + public final List methods; + public final List fields; + + public ClassDeclarationStatement(String name) { + this.name = name; + methods = new ArrayList<>(); + fields = new ArrayList<>(); + } + + public void addField(AssignmentExpression expr) { + fields.add(expr); + } + + public void addMethod(FunctionDefineStatement statement) { + methods.add(statement); + } + + @Override + public void execute() { + ClassDeclarations.set(name, this); + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public R accept(ResultVisitor visitor, T t) { + return visitor.visit(this, t); + } + + @Override + public String toString() { + return String.format("class %s {\n %s %s}", name, fields, methods); + } +} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 1d5e915b..d0137171 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -51,6 +51,9 @@ public Value get() { case Types.STRING: return ((StringValue) container).access(lastIndex); + case Types.CLASS: + return ((ClassInstanceValue) container).access(lastIndex); + default: throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java new file mode 100644 index 00000000..e5edc279 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -0,0 +1,67 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.*; +import java.util.Iterator; +import java.util.List; + +public final class ObjectCreationExpression implements Expression { + + public final String className; + public final List constructorArguments; + + public ObjectCreationExpression(String className, List constructorArguments) { + this.className = className; + this.constructorArguments = constructorArguments; + } + + @Override + public Value eval() { + final ClassDeclarationStatement cd = ClassDeclarations.get(className); + + // Create an instance and put evaluated fields with method declarations + final ClassInstanceValue instance = new ClassInstanceValue(className); + for (AssignmentExpression f : cd.fields) { + // TODO check only variable assignments + final String fieldName = ((VariableExpression) f.target).name; + instance.addField(fieldName, f.eval()); + } + for (FunctionDefineStatement m : cd.methods) { + instance.addMethod(m.name, new ClassMethod(m.arguments, m.body, instance)); + } + + // Call a constructor + final int argsSize = constructorArguments.size(); + final Value[] ctorArgs = new Value[argsSize]; + for (int i = 0; i < argsSize; i++) { + ctorArgs[i] = constructorArguments.get(i).eval(); + } + instance.callConstructor(ctorArgs); + + return instance; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public R accept(ResultVisitor visitor, T t) { + return visitor.visit(this, t); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("new ").append(className).append(' '); + final Iterator it = constructorArguments.iterator(); + if (it.hasNext()) { + sb.append(it.next()); + while (it.hasNext()) { + sb.append(", ").append(it.next()); + } + } + sb.append(')'); + return sb.toString(); + } +} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java b/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java index a98e7abf..b5127501 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java @@ -13,6 +13,7 @@ public interface ResultVisitor { R visit(BinaryExpression s, T t); R visit(BlockStatement s, T t); R visit(BreakStatement s, T t); + R visit(ClassDeclarationStatement s, T t); R visit(ConditionalExpression s, T t); R visit(ContainerAccessExpression s, T t); R visit(ContinueStatement s, T t); @@ -29,6 +30,7 @@ public interface ResultVisitor { R visit(IncludeStatement s, T t); R visit(MapExpression s, T t); R visit(MatchExpression s, T t); + R visit(ObjectCreationExpression s, T t); R visit(PrintStatement s, T t); R visit(PrintlnStatement s, T t); R visit(ReturnStatement s, T t); diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java b/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java index 31ad18c4..adee1999 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java @@ -11,6 +11,7 @@ public interface Visitor { void visit(BinaryExpression s); void visit(BlockStatement s); void visit(BreakStatement s); + void visit(ClassDeclarationStatement s); void visit(ConditionalExpression s); void visit(ContainerAccessExpression s); void visit(ContinueStatement s); @@ -27,6 +28,7 @@ public interface Visitor { void visit(IncludeStatement s); void visit(MapExpression s); void visit(MatchExpression s); + void visit(ObjectCreationExpression s); void visit(PrintStatement s); void visit(PrintlnStatement s); void visit(ReturnStatement s); diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 8db0e84e..4311b410 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -75,6 +75,11 @@ public Node visit(BreakStatement s, T t) { return s; } + @Override + public Node visit(ClassDeclarationStatement s, T t) { + return s; + } + @Override public Node visit(ConditionalExpression s, T t) { final Node expr1 = s.expr1.accept(this, t); @@ -314,6 +319,24 @@ public Node visit(MatchExpression s, T t) { } return s; } + + @Override + public Node visit(ObjectCreationExpression s, T t) { + final List args = new ArrayList<>(); + boolean changed = false; + for (Expression argument : s.constructorArguments) { + final Node expr = argument.accept(this, t); + if (expr != argument) { + changed = true; + } + args.add((Expression) expr); + } + + if (changed) { + return new ObjectCreationExpression(s.className, args); + } + return s; + } @Override public Node visit(PrintStatement s, T t) { diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 3dbc9c36..6455abfa 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -39,6 +39,11 @@ public void visit(BlockStatement s) { public void visit(BreakStatement s) { } + @Override + public void visit(ClassDeclarationStatement s) { + + } + @Override public void visit(ConditionalExpression s) { s.expr1.accept(this); @@ -136,6 +141,13 @@ public void visit(MapExpression s) { public void visit(MatchExpression s) { s.expression.accept(this); } + + @Override + public void visit(ObjectCreationExpression s) { + for (Expression argument : s.constructorArguments) { + argument.accept(this); + } + } @Override public void visit(PrintStatement s) { diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 2e0f9ca4..7d3ef2e6 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -7,6 +7,7 @@ import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.Iterator; +import java.util.List; import java.util.Map; public class PrintVisitor implements ResultVisitor { @@ -79,6 +80,23 @@ public StringBuilder visit(BreakStatement s, StringBuilder t) { return t; } + @Override + public StringBuilder visit(ClassDeclarationStatement s, StringBuilder t) { + t.append("class ").append(s.name).append(" {"); + newLine(t); + + increaseIndent(); + for (AssignmentExpression field : s.fields) { + field.accept(this, t); + } + for (FunctionDefineStatement method : s.methods) { + method.accept(this, t); + } + decreaseIndent(); + t.append("}"); + return t; + } + @Override public StringBuilder visit(ConditionalExpression s, StringBuilder t) { s.expr1.accept(this, t); @@ -209,14 +227,7 @@ public StringBuilder visit(FunctionalExpression s, StringBuilder t) { } else { s.functionExpr.accept(this, t); } - t.append("("); - boolean firstElement = true; - for (Expression expr : s.arguments) { - if (firstElement) firstElement = false; - else t.append(", "); - expr.accept(this, t); - } - t.append(")"); + printArgs(t, s.arguments); return t; } @@ -290,6 +301,13 @@ public StringBuilder visit(MatchExpression s, StringBuilder t) { t.append("}"); return t; } + + @Override + public StringBuilder visit(ObjectCreationExpression s, StringBuilder t) { + t.append("new ").append(s.className); + printArgs(t, s.constructorArguments); + return t; + } @Override public StringBuilder visit(PrintStatement s, StringBuilder t) { @@ -422,6 +440,17 @@ private StringBuilder visitFunctionBody(Statement s, StringBuilder t) { } return t; } + + private void printArgs(StringBuilder t, List args) { + t.append("("); + boolean firstElement = true; + for (Expression expr : args) { + if (firstElement) firstElement = false; + else t.append(", "); + expr.accept(this, t); + } + t.append(")"); + } private void newLine(StringBuilder t) { t.append(Console.newline()); From 26cf77f1bd3356b23e622376a9165c0662f6d54a Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Wed, 7 Aug 2019 13:39:43 +0300 Subject: [PATCH 258/448] =?UTF-8?q?JDK11=20=D0=B2=20.travis.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4c78dead..ace2159c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false install: true jdk: - - oraclejdk8 + - oraclejdk11 addons: sonarcloud: From 4f4a416af998bf25ddc796663dba0c43dc08596e Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Mon, 23 Sep 2019 18:03:59 +0300 Subject: [PATCH 259/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=84=D0=B0=D0=B9=D0=BB=20=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/modules.yml | 6537 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6537 insertions(+) create mode 100644 docs/modules.yml diff --git a/docs/modules.yml b/docs/modules.yml new file mode 100644 index 00000000..4f82eece --- /dev/null +++ b/docs/modules.yml @@ -0,0 +1,6537 @@ +--- + - name: std + scope: "both" + desc: "Contains common functions" + desc_ru: "Содержит вспомогательные функции общего назначения" + constants: + - name: "ARGS" + typeName: string + scope: "desktop" + type: 2 + value: "command-line arguments" + - name: OwnLang + typeName: map + type: 4 + value: "{PLATFORM=android/desktop, VERSION=1.4.0_000000, VERSION_MAJOR=1, VERSION_MINOR=4, VERSION_PATCH=0}" + since: 1.4.0 + functions: + - name: arrayCombine + args: "keys, values" + desc: "creates map by combining two arrays" + desc_ru: "создаёт объект на основе двух массивов" + - name: arrayKeyExists + args: "key, map" + desc: "checks existing key in map. 1 - exists, 0 - no" + desc_ru: "проверяет, содержится ли ключ key в объекте map. 1 - содержится, 0 - нет" + - name: arrayKeys + args: "map" + desc: "returns array of map keys" + desc_ru: "возвращает массив ключей карты" + - name: arraySplice + args: "array, start, deleteCount = length(array) - start, additions = []" + desc: "returns new array with removed `deleteCount` elements starting from `start` and/or added new elements from `start` index" + desc_ru: "возвращает новый массив с удалёнными `deleteCount` элементами, начиная с позиции `start` и/или добавляет новые элементы с позиции `start`" + - name: arrayValues + args: "map" + desc: "returns array of map values" + desc_ru: "возвращает массив значений карты" + - name: charAt + args: "input, index" + desc: returns char code in position `index` of string `input` + desc_ru: возвращает код символа в позиции `index` строки `input` + - name: clearConsole + scope: "android" + args: "" + desc: "clears console" + desc_ru: "очищает консоль" + - name: default + args: a, b + desc: returns value `a` if it it non empty, returns `b` otherwise + desc_ru: возвращает значение `a`, если оно не пустое, иначе возвращается значение `b` + since: 1.4.0 + example: |- + name = default(user.name, "Unknown") + example_ru: |- + name = default(user.name, "Неизвестно") + - name: echo + args: "arg..." + desc: "prints values to console, separate them by space and puts newline at the end. Takes variable number of arguments" + desc_ru: "выводит значения в консоль, разделяя их пробелом, а потом ставит перенос строки. Может принимать переменное значение аргументов" + example: |- + echo(1, "abc") // prints "1 abc" to console + echo(1, 2, 3, 4, 5, "a", "b") // prints "1 2 3 4 5 a b" + example_ru: |- + echo(1, "abc") // выведет строку "1 abc" в консоль + echo(1, 2, 3, 4, 5, "a", "b") // выведет строку "1 2 3 4 5 a b" в консоль" + - name: getBytes + args: input, charset = "UTF-8" + desc: returns byte array of the string in the given charset + desc_ru: возвращает массив байт строки в заданной кодировке + since: 1.5.0 + - name: indexOf + args: "input, what, index = 0" + desc: "finds first occurrence of `what` in string `input`, starting at position `index`" + desc_ru: "поиск первого вхождения подстроки `what` в строке `input`, начиная с позиции `index`" + - name: join + args: "array, delimiter = \"\", prefix = \"\", suffix = \"\"" + desc: "join array to string with `delimiter`, `prefix` and `suffix`" + desc_ru: "объединяет массив в строку с разделителем `delimiter`, префиксом `prefix` и суффиксом `suffix`" + - name: lastIndexOf + args: "input, what, index = 0" + desc: "finds last occurrence of `what` in string `input`, starting at position `index`" + desc_ru: "поиск последнего вхождения подстроки `what` в строке `input`, начиная с позиции `index`" + - name: length + args: "x" + desc: "returns length of string, array/map size or number of function arguments" + desc_ru: "возвращает длину строки, размер массива/объекта или количество аргументов функции в зависимости от типа аргумента x" + - name: newarray + args: "size..." + desc: "creates array with `size`.\n`newarray(x)` - creates 1D array, `newarray(x,y)` - creates 2D array" + desc_ru: "оздаёт массив с размером size. Если указать 1 аргумент - создаётся одномерный массив, если 2 аргумента - двухмерный и т.д" + example: |- + newarray(4) // [0, 0, 0, 0] + newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] + - name: parseInt + args: "str, radix" + desc: parses string into integer in the `radix` + desc_ru: парсит строку в целое число с указанным основанием + - name: parseLong + args: "str, radix" + desc: parses string into long in the `radix` + desc_ru: парсит строку в длинное целое число с указанным основанием + - name: rand + args: "from = 0, to = .." + desc: |- + returns pseudo-random number. + `rand()` - returns float number from 0 to 1 + `rand(max)` - returns random number from 0 to max + `rand(from, to)` - return random number from `from` to `to` + desc_ru: "возвращает псевдослучайное число. Если вызвать функцию без аргументов, возвращается вещественное число от 0 до 1. Если указан один аргумент - возвращается целое число в диапазоне [0...значение). Если указаны два аргумента - возвращается псевдослучайное число в промежутке [from...to)" + - name: range + args: "from = 0, to, step = 1" + desc: |- + creates lazy array by number range. + `range(to)` - creates range from 0 to `to` (exclusive) with step 1 + `range(from, to)` - creates range from `from` to `to` (exclusive) with step 1 + `range(from, to, step)` - creates range from `from` to `to` (exclusive) with step `step` + desc_ru: |- + создаёт массив с элементами числового промежутка. Элементы вычисляются по мере их использования, так что в цикле foreach можно использовать любые промежутки. + `range(to)` - создаёт промежуток от 0 до `to` (не включительно) с шагом 1 + `range(from, to)` - создаёт промежуток от `from` до `to` (не включительно) с шагом 1 + `range(from, to, step)` - создаёт промежуток от `from` до `to` (не включительно) с шагом `step` + example: |- + print range(3) // [0, 1, 2] + r = range(-5, 0) // [-5, -4, -3, -2, -1] + print r[0] // -5 + print r[2] // -3 + for x : range(20, 9, -5) { + println x + } // 20 15 10 + - name: readln + scope: "desktop" + args: "x" + desc: "reads a line from console" + desc_ru: "чтение строки из консоли" + - name: replace + args: "str, target, replacement" + desc: "replaces all occurrences of string `target` with string `replacement`" + desc_ru: "заменяет все вхождения подстроки `target` на строку `replacement`" + - name: replaceAll + args: "str, regex, replacement" + desc: "replaces all occurrences of regular expression `regex` with string `replacement`" + desc_ru: "заменяет все вхождения регулярного выражения `regex` на строку `replacement`" + - name: replaceFirst + args: "str, regex, replacement" + desc: "replaces first occurrence of regular expression `regex` with string `replacement`" + desc_ru: "заменяет первое вхождение регулярного выражения `regex` на строку `replacement`" + - name: sleep + args: "time" + desc: "causes current thread to sleep for `time` milliseconds" + desc_ru: "приостановка текущего потока на time миллисекунд" + - name: sort + args: "array, comparator = .." + desc: "sorts array by natural order or by `comparator` function" + desc_ru: "сортирует массив. Если задана функция `comparator`, то сортировка будет производится на основе результата функции сравнения" + - name: split + args: "str, regex, limit = 0" + desc: "splits string `str` with regular expression `regex` into array. `limit` parameter affects the length of resulting array" + desc_ru: "разделяет строку `str` по шаблону `regex` и помещает элементы в массив. Если указан параметр `limit`, то будет произведено не более limit разбиений, соответственно размер результирующего массива будет ограничен этим значением limit" + example: |- + split("a5b5c5d5e", "5") // ["a", "b", "c", "d", "e"] + split("a5b5c5d5e", "5", 3) // ["a", "b", "c5d5e"] + - name: sprintf + args: "format, args..." + desc: "formats string by arguments" + desc_ru: "форматирует строку" + - name: stringFromBytes + args: input, charset = "UTF-8" + desc: returns a string from byte array in the given charset + desc_ru: возвращает строку из массива байт в заданной кодировке + - name: stripMargin + args: input, marginPrefix = "|" + desc: trims leading whitespaces followed by `marginPrefix` on each line and removes the first and the last lines if they are blank + desc_ru: обрезает начальные пробелы, сопровождаемые `marginPrefix` в каждой строке, и удаляет первую и последнюю строки, если они пустые + since: 1.5.0 + example: |- + use "std" + println " + |123 + |456 + ".stripMargin() // "123\n456" + - name: substring + args: "str, startIndex, endIndex = .." + desc: "returns string from `startIndex` to `endIndex` or to end of string if `endIndex` is not set" + desc_ru: "обрезает строку `str`, начиная от символа после позиции `startIndex` и по `endIndex`. Если `endIndex` не указан, обрезается до конца строки" + example: |- + substring("abcde", 1) // bcde + substring("abcde", 2, 4) // cd + - name: sync + args: "callback" + desc: calls an asynchronous function synchronously + desc_ru: делает асинхронный вызов синхронным + example: |- + result = sync(def(ret) { + http(url, def(t) = ret(t)) + }) + - name: thread + args: "func, args..." + desc: "creates new thread with parameters if passed" + desc_ru: |- + создаёт новый поток и передаёт параметры, если есть. + + `func` - функция, ссылка на функцию (`::function`) или имя функции (`"function"`) + + `args` - 0 или более аргументов, которые необходимо передать в функцию func + example: |- + thread(def() { + print "New Thread" + }) + + thread(::newthread, 10) + thread("newthread", 20) + + def newthread(x) { + print "New Thread. x = " + x + } + - name: time + args: "" + desc: "returns current time in milliseconds from 01.01.1970" + desc_ru: "возвращает текущее время в миллисекундах начиная с 1970 года" + - name: toChar + args: "code" + desc: "converts char code to string" + desc_ru: "переводит код символа в строку" + example: |- + toChar(48) // "0" + - name: toHexString + args: 'number' + desc: 'converts number into hex string' + desc_ru: 'конвертирует число в строку с шестнадцатиричным представлением' + - name: toLowerCase + args: "str" + desc: "converts all symbols to lower case" + desc_ru: "переводит все символы строки в нижний регистр" + - name: toUpperCase + args: "str" + desc: "converts all symbols to upper case" + desc_ru: "переводит все символы строки в верхний регистр" + - name: trim + args: "str" + desc: "removes any leading and trailing whitespaces in string" + desc_ru: "обрезает пробельные невидимые символы по обоим концам строки" + - name: try + args: "unsafeFunction, catchFunction = def(type, message) = -1" + desc: suppress any error in `unsafeFunction` and returns the result of the `catchFunction` if any error occurs + desc_ru: подавляет любые ошибки в `unsafeFunction` и возрвщает результат функции `catchFunction`, если была ошибка + example: |- + try(def() = "success") // success + try(def() = try + 2) // -1 + try(def() = try(), def(type, message) = sprintf("Error handled:\ntype: %s\nmessage: %s", type, message)) + try(def() = try(), 42) // 42 + example_ru: |- + try(def() = "успех") // успех + try(def() = try + 2) // -1 + try(def() = try(), def(type, message) = sprintf("Обработана ошибка:\nтип: %s\nсообщение: %s", type, message)) + try(def() = try(), 42) // 42 + - name: types + scope: "both" + desc: "Contains functions for type checking and conversion" + desc_ru: "Содержит функции для проверки и преобразования типов" + constants: + - + name: "OBJECT" + typeName: number + type: 1 + value: "0" + - + name: "NUMBER" + typeName: number + type: 1 + value: "1" + - + name: "STRING" + typeName: number + type: 1 + value: "2" + - + name: "ARRAY" + typeName: number + type: 1 + value: "3" + - + name: "MAP" + typeName: number + type: 1 + value: "4" + - + name: "FUNCTION" + typeName: number + type: 1 + value: "5" + functions: + - + name: "byte" + args: "value" + desc: "converts value to byte" + desc_ru: "преобразует значение к типу byte" + - + name: "double" + args: "value" + desc: "converts value to double" + desc_ru: "преобразует значение к типу double" + - + name: "float" + args: "value" + desc: "converts value to float" + desc_ru: "преобразует значение к типу float" + - + name: "int" + args: "value" + desc: "converts value to int" + desc_ru: "преобразует значение к типу int" + - + name: "long" + args: "value" + desc: "converts value to long" + desc_ru: "преобразует значение к типу long" + - + name: "number" + args: "value" + desc: "converts value to number if possible" + desc_ru: "преобразует значение к числу, если это возможно" + example: |- + print typeof(number("2.3")) // 1 (NUMBER) + - + name: "short" + args: "value" + desc: "converts value to short" + desc_ru: "преобразует значение к типу short" + - + name: "string" + args: "value" + desc: "converts value to string" + desc_ru: "преобразует значение в строку" + example: |- + print typeof(string(1)) // 2 (STRING) + - + name: "typeof" + args: "value" + desc: "returns the type of value" + desc_ru: "возвращает тип переданного значения" + example: |- + print typeof(1) // 1 (NUMBER) + print typeof("text") // 2 (STRING) + print typeof([]) // 3 (ARRAY) + - name: math + scope: "both" + desc: "Contains math functions and constants" + desc_ru: "Содержит математические функции и константы" + constants: + - + name: "E" + typeName: number + type: 1 + value: "2.718281828459045" + - + name: "PI" + typeName: number + type: 1 + value: "3.141592653589793" + functions: + - + name: "abs" + args: "x" + desc: "absolute value of `x`" + desc_ru: "модуль числа `x`" + - + name: "acos" + args: "x" + desc: "arc cosine" + desc_ru: "арккосинус" + - + name: "asin" + args: "x" + desc: "arc sine" + desc_ru: "арксинус" + - + name: "atan" + args: "x" + desc: "arc tangent" + desc_ru: "арктангенс" + - + name: "atan2" + args: "y, x" + desc: "returns angle θ whose tangent is the ratio of two numbers" + desc_ru: "угол θ, тангенс которого равен отношению двух указанных чисел" + - + name: "cbrt" + args: "x" + desc: "cube root" + desc_ru: "кубический корень числа x" + - + name: "ceil" + args: "x" + desc: "returns the ceiling of `x`" + desc_ru: "округляет вещественное число в большую сторону" + example: |- + ceil(6.4) // 7 + - + name: "copySign" + args: "magnitude, sign" + desc: "" + desc_ru: "" + - + name: "cos" + args: "x" + desc: "trigonometric cosine" + desc_ru: "косинус" + - + name: "cosh" + args: "x" + desc: "hyperbolic cosine" + desc_ru: "гиперболический косинус" + - + name: "exp" + args: "x" + desc: "ex" + desc_ru: "ex" + - + name: "expm1" + args: "x" + desc: "ex-1" + desc_ru: "ex-1" + - + name: "floor" + args: "x" + desc: "returns floor of `x`" + desc_ru: "округляет вещественное число в меньшую сторону" + example: |- + floor(3.8) // 3 + - + name: "getExponent" + args: "x" + desc: "" + desc_ru: "" + - + name: "hypot" + args: "x, y" + desc: "" + desc_ru: "расчёт гипотенузы sqrt(x2 + y2) без переполнения" + - + name: "IEEEremainder" + args: "x, y" + desc: "" + desc_ru: "" + - + name: "log" + args: "x" + desc: "" + desc_ru: "логарифм" + - + name: "log1p" + args: "x" + desc: "" + desc_ru: "натуральный логарифм от x + 1 (`ln(x + 1)`)" + - + name: "log10" + args: "x" + desc: "" + desc_ru: "десятичный логарифм" + - + name: "max" + args: "x, y" + desc: "" + desc_ru: "максимальное из двух чисел" + - + name: "min" + args: "x, y" + desc: "" + desc_ru: "минимальное из двух чисел" + - + name: "nextAfter" + args: "x, y" + desc: "" + desc_ru: "" + - + name: "nextUp" + args: "x" + desc: "" + desc_ru: "" + - + name: "pow" + args: "x, y" + desc: "" + desc_ru: "возведение x в степень y" + - + name: "rint" + args: "x" + desc: "" + desc_ru: "" + - + name: "round" + args: "x" + desc: "" + desc_ru: "округляет вещественное число до ближайшего целого" + - + name: "signum" + args: "x" + desc: "" + desc_ru: "" + - + name: "sin" + args: "x" + desc: "" + desc_ru: "синус" + - + name: "sinh" + args: "x" + desc: "" + desc_ru: "гиперболический синус" + - + name: "sqrt" + args: "x" + desc: "" + desc_ru: "квадратный корень" + - + name: "tan" + args: "x" + desc: "" + desc_ru: "тангенс" + - + name: "tanh" + args: "x" + desc: "" + desc_ru: "гиперболический тангенс" + - + name: "toDegrees" + args: "x" + desc: "" + desc_ru: "перевод радиан в градусы" + - + name: "toRadians" + args: "x" + desc: "" + desc_ru: "перевод градусов в радианы" + - + name: "ulp" + args: "x" + desc: "" + desc_ru: "" + - name: date + scope: "both" + desc: "Contains functions for working with date and time" + desc_ru: "Содержит функции для работы с датой и временем" + constants: + - + name: "STYLE_FULL" + typeName: number + type: 1 + value: "0" + - + name: "STYLE_LONG" + typeName: number + type: 1 + value: "1" + - + name: "STYLE_MEDIUM" + typeName: number + type: 1 + value: "2" + - + name: "STYLE_SHORT" + typeName: number + type: 1 + value: "3" + functions: + - + name: "newDate" + args: "..." + desc: |- + `newDate()` - returns current date. + + `newDate(timestamp)` - returns date by given timestamp. + + `newDate(dateString)` - parses and returns date by given string. + + `newDate(pattern, dateString)` - parses and returns date by given string in `pattern` format. + + `newDate(year, month, day)` - returns date by year, month and day. + + `newDate(year, month, day, hour, minute)` - returns date by year, month, day, hour and minute. + + `newDate(year, month, day, hour, minute, second)` - returns date by year, month, day, hour, minute and second. + + Returns DateValue. + desc_ru: |- + `newDate()` - возвращает текущую дату. + + `newDate(timestamp)` - возвращает дату для указанной метки времени. + + `newDate(dateString)` - парсит и возвращает дату, записанную в виде строки. + + `newDate(pattern, dateString)` - парсит и возвращает дату, записанную в виде строки в формате `pattern`. + + `newDate(year, month, day)` - возвращает дату для указанных года, месяца и дня. + + `newDate(year, month, day, hour, minute)` - возвращает дату для указанных года, месяца, дня, часа и минуты. + + `newDate(year, month, day, hour, minute, second)` - возвращает дату для указанных года, месяца, дня, часа, минуты и секунды. + + Возвращает DateValue. + - + name: "newFormat" + args: "..." + desc: |- + `newFormat()` - returns default date format. + + `newFormat(pattern)` - returns date format by given pattern. + + `newFormat(type)` - returns format: 0 - default, 1 - date, 2 - time, 3 - date and time. + + `newFormat(pattern, locale)` - returns date format by given pattern and locale. + + `newFormat(type, style)` - returns format: 0 - default, 1 - date, 2 - time, 3 - date and time. `style`: 0 - full, 1 - long, 2 - medium, 3 - short. + + Returns DateFormatValue. + desc_ru: |- + `newFormat()` - возвращает формат даты по умолчанию. + + `newFormat(pattern)` - возвращает формат с указанным шаблоном. + + `newFormat(type)` - возвращает формат: 0 - по умолчанию, 1 - для даты, 2 - для времени, 3 - для времени и даты. + + `newFormat(pattern, locale)` - возвращает формат для указанного шаблона в заданной локализации. + + `newFormat(type, style)` - возвращает формат: 0 - по умолчанию, 1 - для даты, 2 - для времени, 3 - для времени и даты. `style`: 0 - полный, 1 - длинный, 2 - средний, 3 - короткий. + + Возвращает DateFormatValue. + - + name: "formatDate" + args: "date, format = default" + desc: formats date by given format and returns string + desc_ru: форматирует дату в указанном формате и возвращает строку + example: |- + d = date(2016, 4, 8) + println formatDate(d, newFormat("yyyy/MM/dd")) // "2016/05/08" + - + name: "parseDate" + args: "dateString, format = default" + desc: parses date from string by given pattern. Returns DateValue + desc_ru: парсит дату из строки в указанном шаблоне. Возвращает DateValue + example: |- + println parseDate("2016/05/08", newFormat("yyyy/MM/dd")) + - + name: "toTimestamp" + args: "date" + desc: returns timestamp in milliseconds + desc_ru: возвращает время в миллисекундах + types: + - + name: "DateValue" + value: "year, month, day, hour, minute, second, millisecond" + - + name: "DateFormatValue" + - name: files + scope: "both" + desc: "Contains functions for working with files" + desc_ru: "Содержит функции для работы с файлами" + constants: + - + name: "FILES_COMPARATOR" + typeName: "function" + scope: "both" + type: 5 + value: "def(f1, f2) = compare(f1, f2)" + desc: "function which compares two file descriptors" + desc_ru: "функция, которая сравнивает два файловых дескриптора" + - + name: "SDCARD" + typeName: string + scope: "android" + type: 2 + value: "path to SDCARD" + desc: "path to SDCARD" + desc_ru: "путь к внешнему хранилищу" + functions: + - + name: "canExecute" + args: "f" + desc: "checks execute permission of the descriptor `f`" + desc_ru: "проверяет права на выполнение дескриптора `f`" + - + name: "canRead" + args: "f" + desc: "checks read permission of the descriptor `f`" + desc_ru: "проверяет права на чтение дескриптора `f`" + - + name: "canWrite" + args: "f" + desc: "checks write permission of the descriptor `f`" + desc_ru: "проверяет права на запись дескриптора `f`" + - name: copy + args: 'src, dst' + desc: 'copies file src to dst location' + desc_ru: 'копирует файл src в dst' + - + name: "delete" + args: "f" + desc: "removes file or directory. Returns 1 if delete was successfull, 0 otherwise" + desc_ru: "удаляет файл или папку. Возвращает 1, если удаление прошло успешно, иначе - 0" + - + name: "exists" + args: "f" + desc: "checks file or directory existing. Returns 1 if exists, 0 otherwise" + desc_ru: "проверяет, существует ли файл или папка. Возвращает 1, если существует, иначе - 0" + - + name: "fclose" + args: "f" + desc: "closes file" + desc_ru: "закрывает файл" + - + name: "fileSize" + args: "f" + desc: "returns file size in bytes" + desc_ru: "возвращает размер файла в байтах" + - + name: "flush" + args: "f" + desc: "flushes write buffer into file" + desc_ru: "сбрасывает буфер записи в файл" + - + name: "fopen" + args: "path, mode = \"r\"" + desc: |- + opens file файл with `path` in given `mode`: + + - "" - opens file or directory for getting info; + - "r" - opens file for read in text mode; + - "rb" - opens file for read in binary mode; + - "w" - opens file for write in text mode; + - "w+" - opens file for append in text mode; + - "wb" - opens file for write in binary mode; + - "wb+" - opens file for append in binary mode. + + Returns a file descriptor for using in other functions. + desc_ru: |- + открывает файл по пути `path` в заданном режиме `mode`: + + - "" - открывает файл или папку для получения информации; + - "r" - открывает файл для чтения в текстовом режиме; + - "rb" - открывает файл для чтения в бинарном режиме; + - "w" - открывает файл для записи в текстовом режиме; + - "w+" - открывает файл для дозаписи в текстовом режиме; + - "wb" - открывает файл для записи в бинарном режиме; + - "wb+" - открывает файл для дозаписи в бинарном режиме. + + Возвращает дескриптор файла, который необходим для остальных функций. + example: |- + f1 = fopen("text.txt") // opens file text.txt for read in text mode + f2 = fopen("E:/1.dat", "rbwb") // opens file 1.dat on drive E for binary read and write" + example_ru: |- + f1 = fopen("text.txt") // открывает файл text.txt для текстового чтения + f2 = fopen("E:/1.dat", "rbwb") // открывает файл 1.dat на диске E для бинарного чтения и записи" + - + name: "getParent" + args: "f" + desc: "returns parent path of the given descriptor `f`" + desc_ru: "возвращает родительский путь для заданного дескриптора `f`" + - + name: "isDirectory" + args: "f" + desc: "checks if descriptor `f` is directory" + desc_ru: "проверяет, является ли дескриптор `f` папкой. 1 - является, 0 - нет" + - + name: "isFile" + args: "f" + desc: "checks if descriptor `f` is file" + desc_ru: "проверяет, является ли дескриптор f файлом. 1 - является, 0 - нет" + - + name: "isHidden" + args: "f" + desc: "checks if descriptor `f` is hidden" + desc_ru: "проверяет, скрыт ли дескриптор f. 1 - скрыт, 0 - нет" + - + name: "lastModified" + args: "f" + desc: "returns last modification time" + desc_ru: "возвращает время последнего изменения" + - + name: "listFiles" + args: "f" + desc: "returns array with filenames in given directory.\n\n f - directory descriptor" + desc_ru: "возвращает массив с именами файлов в указанной директории.\n\n f - дескриптор папки" + example: |- + f1 = fopen("E:/examples", "") // opens directory examples for getting information + list = listFiles(f1) // gets array with filenames in directory + example_ru: |- + f1 = fopen("E:/examples", "") // открыть папку examples для получения информации + list = listFiles(f1) // получить массив с именами файлов в этой папке + - + name: "mkdir" + args: "f" + desc: "creates the directory. Returns 1 if operation was successfull, 0 otherwise" + desc_ru: "создаёт папку. Возвращает 1, если создание прошло успешно, иначе - 0" + - + name: "mkdirs" + args: "f" + desc: "creates the directories. Returns 1 if operation was successfull, 0 otherwise" + desc_ru: "создаёт папки. Возвращает 1, если создание прошло успешно, иначе - 0" + - + name: "readAllBytes" + args: "f" + desc: "reads all bytes from file. Returns array with bytes" + desc_ru: "чтение всех байт файла. Возвращает массив байт файла" + example: |- + f1 = fopen("file.bin", "rb") + array = readAllBytes(f1) + - + name: "readBoolean" + args: "f" + desc: "reads boolean (1 byte). Returns 0 if byte was 0, 1 otherwise" + desc_ru: "чтение boolean-значения (1 байт). Возвращает 0, если байт имеет значение 0, 1 - если значение не равно 0" + - + name: "readByte" + args: "f" + desc: "reads one byte" + desc_ru: "чтение одного байта" + - + name: "readBytes" + args: "f, array, offset = 0, length = length(array)" + desc: "reads `length` bytes of file `f` to `array` starting from `offset`. Returns number of readed bytes" + desc_ru: "чтение заданного количества байт в массив `array`. Возвращает число прочитанных байт. \nЕсли offset и length не указаны, то читается количество байт равное длине массива. \nЕсли offset и length указаны, то пропускается offset байт и читается length байт в массив array" + example: |- + f1 = fopen("file.bin", "rb") + array = newarray(2048) + readedCount = readBytes(f1, array) // reads 2048 bytes + readedCount = readBytes(f1, array, 10) // reads 2048 bytes starting from 11 byte + readedCount = readBytes(f1, array, 20, 10) // reads 10 bytes, starting from 21 byte + example_ru: |- + f1 = fopen("file.bin", "rb") + array = newarray(2048) + readedCount = readBytes(f1, array) // читает 2048 байт из файла + readedCount = readBytes(f1, array, 10) // читает 2048 байт, начиная с 11 байта + readedCount = readBytes(f1, array, 20, 10) // читает 10 байт, начиная с 21 байта + - + name: "readChar" + args: "f" + desc: "reads one char (2 bytes). Returns number char's code" + desc_ru: "чтение одного символа (2 байта). Возвращает число - код символа" + - + name: "readDouble" + args: "f" + desc: "reads 8 bytes double number" + desc_ru: "чтение 8 байт (вещественное число двойной точности)" + - + name: "readFloat" + args: "f" + desc: "reads 4 bytes float number" + desc_ru: "чтение 4 байт (вещественное число)" + - + name: "readInt" + args: "f" + desc: "reads 4 bytes integer number" + desc_ru: "чтение 4 байт (целое число)" + - + name: "readLine" + args: "f" + desc: "reads line from file opened in text mode" + desc_ru: "чтение строки в текстовом режиме" + - + name: "readLong" + args: "f" + desc: "reads 8 bytes long number" + desc_ru: "чтение 8 байт (длинное целое число)" + - + name: "readShort" + args: "f" + desc: "reads 2 bytes short number" + desc_ru: "чтение 2 байт (короткое целое число)" + - + name: "readText" + args: "f" + desc: "reads all file's content as string" + desc_ru: "чтение всего файла в текстовом режиме в строку" + - + name: "readUTF" + args: "f" + desc: "reads string in binary mode" + desc_ru: "чтение строки в бинарном режиме" + - + name: "rename" + args: "from, to" + desc: "renames (or moves) file" + desc_ru: "переименование (или перемещение) файла" + example: |- + f1 = fopen("C:/file1", "i") + f2 = fopen("E:/file2", "i") + rename(f1, f2) + fclose(f1) + fclose(f2) + - + name: "setLastModified" + args: "f, time" + desc: "sets last modified time" + desc_ru: "устанавливает время изменения" + - + name: "setReadOnly" + args: "f" + desc: "marks descriptor read only" + desc_ru: "помечает дескриптор только для чтения" + - + name: "setExecutable" + args: "f, executable, ownerOnly = true" + desc: "sets execute permission" + desc_ru: "устанавливает права на выполнение" + - + name: "setReadable" + args: "f, readable, ownerOnly = true" + desc: "sets read permission" + desc_ru: "устанавливает права на чтение" + - + name: "setWritable" + args: "f, writable, ownerOnly = true" + desc: "sets write permission" + desc_ru: "устанавливает права на запись" + - + name: "writeBoolean" + args: "f, v" + desc: "writes boolean (0 or 1) to file" + desc_ru: "запись одного байта boolean (0 или 1) в файл" + - + name: "writeByte" + args: "f, v" + desc: "writes one byte to file" + desc_ru: "запись одного байта в файл" + - + name: "writeBytes" + args: "f, array, offset = 0, length = length(array)" + desc: "writes `length` bytes to file `f` from byte `array` starting from `offset`" + desc_ru: "запись заданного количества байт в файл `f` из массива байт `array`. \nЕсли offset и length не указаны, то записывается количество байт равное длине массива. \nЕсли offset и length указаны, то пропускается offset байт и записывается length байт" + - + name: "writeChar" + args: "f, v" + desc: "writes one char (2 bytes) to file. `v` can be number - writes number, or string - writes code of first symbol" + desc_ru: "запись одного символа (2 байта) в файл. `v` может быть как числом (пишется это число), так и строкой (пишется код первого символа)" + - + name: "writeDouble" + args: "f, v" + desc: "writes 8 bytes double number to file" + desc_ru: "запись 8 байт (вещественное число двойной точности)" + - + name: "writeFloat" + args: "f, v" + desc: "writes 4 bytes float number to file" + desc_ru: "запись 4 байт (вещественное число)" + - + name: "writeInt" + args: "f, v" + desc: "writes 4 bytes integer number to file" + desc_ru: "запись 4 байт (целое число)" + - + name: "writeLine" + args: "f, v" + desc: "writes string to file in text mode **adds line break at the end of the string**" + desc_ru: "запись строки в текстовом режиме **Добавляет в конец символ переноса строки**" + - + name: "writeLong" + args: "f, v" + desc: "writes 8 bytes long number to file" + desc_ru: "запись 8 байт (длинное целое число)" + - + name: "writeShort" + args: "f, v" + desc: "writes 2 bytes short number to file" + desc_ru: "запись двух байт (короткое целое число)" + - + name: "writeText" + args: "f, v" + desc: "writes string to file in text mode. Unlike `writeLine` does not add line break" + desc_ru: "запись всего текста в текстовом режиме. В отличие от `writeLine`, не добавляет символ переноса строки" + - + name: "writeUTF" + args: "f, v" + desc: "writes string to file in binary mode" + desc_ru: "запись строки в бинарном режиме" + - name: http + scope: "both" + desc: "Contains network functions" + desc_ru: "Содержит функции для взаимодействия с сетью" + constants: [] + functions: + - + name: "http" + args: "url" + desc: |- + performs GET-request to `url`. + + `http(url, method)` - performs request with `method` (GET, POST, PUT, DELETE, PATCH, OPTIONS) to `url`. + + `http(url, callback)` - performs GET-request to `url`, response will be send to function `callback`. + + `http(url, method, params)` - performs request with given `method` and object `params` to `url`. + + `http(url, method, callback)` - performs request with given `method` to `url`, response will be send to function `callback`. + + `http(url, method, params, callback)` - performs request with given `method` and object `params` to `url`, response will be send to function `callback`. + + `http(url, method, params, options, callback)` - performs request with given `method`, object `params` and connection `options` to `url`, response will be send to function `callback`. + + Connection options is a object (map): + + - `header` - sets http-header (string or array). + - `encoded` - is `params` object already urlencoded. + - `content_type` - sets Content-Type. + - `extended_result` - marks that response should be extended and should contains: + - `text` - server response text + - `message` - server response message + - `code` - server response code + - `headers` - response http-header + - `content_length` - Content-Length + - `content_type` - Content-Type + desc_ru: |- + `http(url)` - выполняет GET-запрос на указанный адрес `url`. + + `http(url, method)` - выполняет запрос на указанный адрес `url` методом method (GET, POST, PUT, DELETE, PATCH, OPTIONS). + + `http(url, callback)` - выполняет GET-запрос на указанный адрес `url`, ответ сервера передаёт в функцию `callback`. + + `http(url, method, params)` - выполняет запрос на указанный адрес `url`, методом `method` c данными `params` (объект). + + `http(url, method, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, ответ сервера передаёт в функцию `callback`. + + `http(url, method, params, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, с данными `params`, ответ сервера передаёт в функцию `callback`. + + `http(url, method, params, options, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, с данными `params`, параметрами подключения `options`, ответ сервера передаёт в функцию `callback`. + + Параметрами подключения выступает объект. Допустимы следующие параметры + + - `header` - задаёт http-заголовок, если передана строка или несколько заголовков, если массив. + - `encoded` - указывает, что данные `params` уже закодированы в URL-формате. + - `content_type` - указывает Content-Type. + - `extended_result` - указывает, что ответ сервера нужно вернуть в расширенном виде, а именно объектом с данными: + - `text` - текст ответа сервера + - `message` - сообщение сервера + - `code` - код ответа сервера + - `headers` - http-заголовки ответа + - `content_length` - Content-Length + - `content_type` - Content-Type + example: |- + use "http" + http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { + println "Added: " + v + }) + - name: "download" + args: "url" + desc: "downloads content by url as bytes array" + desc_ru: "получает содержимое по указанному адресу в виде массива байт" + example: |- + use ["http", "files"] + bytes = download("http://url") + f = fopen("file", "wb") + writeBytes(f, bytes) + flush(f) + fclose(f) + - name: "urlencode" + args: "str" + desc: "converts string to URL-format" + desc_ru: "преобразует строку в URL-формат" + - name: socket + scope: both + constants: + - name: EVENT_CONNECT + type: 2 + typeName: string + value: connect + - name: EVENT_CONNECTING + type: 2 + typeName: string + value: connecting + - name: EVENT_CONNECT_ERROR + type: 2 + typeName: string + value: connect_error + - name: EVENT_CONNECT_TIMEOUT + type: 2 + typeName: string + value: connect_timeout + - name: EVENT_DISCONNECT + type: 2 + typeName: string + value: disconnect + - name: EVENT_ERROR + type: 2 + typeName: string + value: error + - name: EVENT_MESSAGE + type: 2 + typeName: string + value: message + - name: EVENT_PING + type: 2 + typeName: string + value: ping + - name: EVENT_PONG + type: 2 + typeName: string + value: pong + - name: EVENT_RECONNECT + type: 2 + typeName: string + value: reconnect + - name: EVENT_RECONNECTING + type: 2 + typeName: string + value: reconnecting + - name: EVENT_RECONNECT_ATTEMPT + type: 2 + typeName: string + value: reconnect_attempt + - name: EVENT_RECONNECT_ERROR + type: 2 + typeName: string + value: reconnect_error + - name: EVENT_RECONNECT_FAILED + type: 2 + typeName: string + value: reconnect_failed + functions: + - name: newSocket + args: 'url, options = {}' + desc: |- + creates new SocketValue + + options (map with keys): + - forceNew (boolean) + - multiplex (boolean) + - reconnection (boolean) + - rememberUpgrade (boolean) + - secure (boolean) + - timestampRequests (boolean) + - upgrade (boolean) + - policyPort (integer) + - port (integer) + - reconnectionAttempts (integer) + - reconnectionDelay (timestamp - long) + - reconnectionDelayMax (timestamp - long) + - timeout (timestamp - long) - set -1 to disable + - randomizationFactor (double) + - host (string) + - hostname (string) + - path (string) + - query (string) + - timestampParam (string) + - transports (array of strings) + desc_ru: |- + создаёт новый SocketValue + + options (map с ключами): + - forceNew (boolean) + - multiplex (boolean) + - reconnection (boolean) + - rememberUpgrade (boolean) + - secure (boolean) + - timestampRequests (boolean) + - upgrade (boolean) + - policyPort (integer) + - port (integer) + - reconnectionAttempts (integer) + - reconnectionDelay (timestamp - long) + - reconnectionDelayMax (timestamp - long) + - timeout (timestamp - long) - -1 для отключения + - randomizationFactor (double) + - host (string) + - hostname (string) + - path (string) + - query (string) + - timestampParam (string) + - transports (array of strings) + types: + - name: SocketValue + functions: + - name: "close" + args: "" + desc: "disconnects the socket" + desc_ru: "закрывает соединение сокета" + - name: "connect" + args: "" + desc: "connects the socket" + desc_ru: "подключает сокет" + - name: "connected" + args: "" + desc: "returns connected status (1 - connected, 0 - no)" + desc_ru: "возвращает состояние подключения (1 - подключен, 0 - нет)" + - name: "disconnect" + args: "" + desc: "disconnects the socket" + desc_ru: "закрывает соединение сокета" + - name: "emit" + args: "event, data" + desc: "emits an event" + desc_ru: "посылает событие" + - name: "hasListeners" + args: "event" + desc: "returns true if there is listeners for specified event" + desc_ru: "возвращает true, если для указанного события есть обработчики" + - name: "id" + args: "" + desc: "returns socket id" + desc_ru: "возвращает id сокета" + - name: "off" + args: "event = .." + desc: "removes specified event handler, or removes all if no arguments were passed" + desc_ru: "удаляет обработчик указанного события или удаляет все обработчики, если не было передано ни одного аргумента" + - name: "on" + args: "event, listener" + desc: "adds event listener" + desc_ru: "добавляет обработчик указанного события" + - name: "once" + args: "event, listener" + desc: "adds one time event listener" + desc_ru: "добавляет одноразовый обработчик указанного события" + - name: "open" + args: "" + desc: "connects the socket" + desc_ru: "подключает сокет" + - name: "send" + args: "data" + desc: "send messages" + desc_ru: "отправляет сообщения" + - name: downloader + scope: both + desc: "Contains functions for downloading large files" + desc_ru: "Содержит функции для скачивания больших файлов" + functions: + - name: getContentLength + args: 'url' + desc: 'gets content length by sending HEAD request to the given url' + desc_ru: 'получает значение заголовка Content-Length путём отправки HEAD-запроса на указанный url' + - name: downloader + args: 'downloadUrl, filePath, progressCallback = def() {}, bufferSize = 16384' + desc: 'downloads file from `downloadUrl` to `filePath`' + desc_ru: 'скачивает файл по адресу `downloadUrl` и сохраняет в `filePath`' + example: |- + use "downloader" + downloader(url, file, def(progress, bytesDownloaded, bytesMax) { + bar = "#" * (progress / 2) + print sprintf("%-50s %d%% %.2f / %.2f\r", bar, progress, cur / 1048576.0, max / 1048576.0) + }) + - name: base64 + scope: both + desc: "Contains base64 encoding and decoding functions" + desc_ru: "Содержит функции кодирования данных в base64 и наоборот" + constants: + - name: BASE64_URL_SAFE + type: 1 + typeName: number + value: '8' + desc: 'Url safe encoding output' + desc_ru: 'Вывод данных в безопасном для ссылок формате' + functions: + - name: base64decode + args: 'data, type = 0' + desc: 'decodes base64-encoded byte array or string into byte array' + desc_ru: 'декодирует массив байт или строку, закодированную в base64, в массив байт' + - name: base64encode + args: 'data, type = 0' + desc: 'encodes byte array or string into base64-encoded byte array' + desc_ru: 'кодирует массив байт или строку в закодированный base64 массив байт' + - name: base64encodeToString + args: 'data, type = 0' + desc: 'encodes byte array or string into base64-encoded string' + desc_ru: 'кодирует массив байт или строку в закодированную base64 строку' + - name: json + scope: "both" + desc: "Contains functions for working with the json format" + desc_ru: "Содержит функции преобразования данных в формат json и наоборот" + constants: [] + functions: + - + name: "jsondecode" + args: "data" + desc: "converts data to json string" + desc_ru: "преобразует переданные данные в строку в формате json" + example: |- + use "json" + print jsondecode("{\"key1\":1,\"key2\":[1,2,3],\"key3\":\"text\"}") // {key2=[1, 2, 3], key3=text, key1=1} + - + name: "jsonencode" + args: "jsonString" + desc: "converts string to data" + desc_ru: "преобразует строку в формате json в данные" + example: |- + use "json" + data = { + "key1": 1, + "key2": [1, 2, 3], + "key3": "text" + } + print jsonencode(data) // {"key1":1,"key2":[1,2,3],"key3":"text"} + - name: yaml + scope: desktop + desc: "Contains functions for working with the yaml format" + desc_ru: "Содержит функции преобразования данных в формат yaml и наоборот" + constants: [] + functions: + - name: yamldecode + args: "data" + desc: "converts data to yaml string" + desc_ru: "преобразует переданные данные в строку в формате yaml" + - name: yamlencode + args: "yamlString" + desc: "converts yaml string to data" + desc_ru: "преобразует строку в формате yaml в данные" + - name: zip + since: 1.5.0 + scope: both + desc: "Contains functions for working with zip archives" + desc_ru: "Содержит функции для работы с zip архивами" + constants: [] + functions: + - + name: zip + args: "inputPath, outputFile, mapper = def(entryPath) = entryPath" + desc: |- + creates a zip archive with the contents of `inputPath` and saves to `outputFile`. + `mapper` is used to set the name of the final file inside the archive and for filtering. If the mapper returns an empty string, the file will be skipped. + Returns the number of archived files, or -1 if the archive could not be created. + desc_ru: |- + создаёт zip архив с содержимым `inputPath` и сохраняет в `outputFile`. + `mapper` используется для задания имени конечного файла внутри архива, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. + Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. + example: |- + use "zip" + // Zip all files in directory + zip("/tmp/dir", "/tmp/1.zip") + // Zip .txt files + zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") + example_ru: |- + use "zip" + // Архивировать все файлы в директории + zip("/tmp/dir", "/tmp/1.zip") + // Архивировать .txt файлы + zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") + - + name: zipFiles + args: "input, outputFile" + desc: |- + creates a zip archive with the contents of `inputPath` and saves to `outputFile`. + If `input` is a string, then a single file or the contents of a folder is archived. + If `input` is an array, then the files and folders listed in it are archived. + If `input` is an associative array, then the files and folders listed in the keys are archived and the names inside the archive will be the values of an array. + Returns the number of archived files, or -1 if the archive could not be created. + desc_ru: |- + создаёт zip архив с содержимым `input` и сохраняет в `outputFile`. + Если `input` — строка, то архивируется один файл или содержимое папки. + Если `input` — массив, то архивируются файлы и папки, перечисленные в нём. + Если `input` — ассоциативный массив, то архивируются файлы и папки перечисленные в ключах, а именами внутри архива будут служить значения. + Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. + example: |- + use "zip" + zipFiles("/tmp/dir/file.txt", "/tmp/1.zip") + zipFiles(["/tmp/dir/file.txt", "/tmp/dir/readme.md"], "/tmp/2.zip") + zipFiles({"/tmp/dir/file.txt" : "docs/1.md", "/tmp/dir/readme.md" : "docs/2.md"}, "/tmp/3.zip") + - + name: unzip + args: "input, output, mapper = def(entryName) = entryPath" + desc: |- + unpacks a zip archive to `output` directory. + `mapper` is used to set the name of the final file and for filtering. If the mapper returns an empty string, the file will be skipped. + Returns the number of unzipped files, or -1 if unzipping the archive was failed. + desc_ru: |- + распаковывает zip архив `input` в папку `output`. + `mapper` используется для задания имени конечного файла, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. + Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. + example: |- + use "zip" + // Unzip all files in directory + unzip("/tmp/1.zip", "/tmp/dir") + // Unzip .txt files + unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") + example_ru: |- + use "zip" + // Распаковать все файлы в директории + unzip("/tmp/1.zip", "/tmp/dir") + // Распаковать .txt файлы + unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") + - + name: unzipFiles + args: "input, output" + desc: |- + unpacks a `output` files from zip archive . + If `output` is a string, then a single file is unzipped. + If `output` is an array, then the files listed in it are unzipped. + If `output` is an associative array, the files listed in the keys are unzipped and the values will be file names. + Returns the number of unzipped files, or -1 if unzipping the archive was failed. + desc_ru: |- + распаковывает `output` файлы из zip архива. + Если `output` — строка, то разархивируется один файл. + Если `output` — массив, то разархивируются файлы, перечисленные в нём. + Если `output` — ассоциативный массив, то разархивируются файлы перечисленные в ключах, а именами файлов будут служить значения. + Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. + example: |- + use "zip" + unzipFiles("/tmp/1.zip", "file.txt") + unzipFiles("/tmp/2.zip", ["file.txt", "readme.md"]) + unzipFiles("/tmp/3.zip", {"docs/1.md" : "/tmp/dir/file.txt", "docs/2.md" : "/tmp/dir/readme.md"}) + - + name: listZipEntries + args: "input" + desc: returns an array of zip archive filenames + desc_ru: возвращает массив с именами файлов zip архива + - name: gzip + since: 1.5.0 + scope: both + desc: "Contains functions for working with gzip compression" + desc_ru: "Содержит функции для работы с gzip компрессией" + constants: [] + functions: + - + name: gzip + args: "inputFile, outputFile" + desc: |- + creates a gzip archive with the `inputFile` file and saves to `outputFile`. + Returns 1 if compression was successfull, -1 otherwise. + desc_ru: |- + создаёт gzip архив с файлом `inputFile` и сохраняет в `outputFile`. + Возвращает 1 если компрессия завершилась успешно, и -1 в противном случае. + example: |- + use "gzip" + gzip("/tmp/readme.md", "/tmp/readme.md.gz") + - + name: gzipBytes + args: "bytes" + desc: returns gzip-compressed input bytes. + desc_ru: возвращает сжатый в gzip массив байт. + example: |- + use "gzip" + bytes = gzipBytes([0, 119, 87, 80/* ... */]) + - + name: ungzip + args: "inputFile, outputFile" + desc: |- + unpacks a gzip archive to `outputFile` file. + Returns 1 if operation was successfull, -1 otherwise. + desc_ru: |- + распаковывает gzip архив в файл `outputFile`. + Возвращает 1 если операция завершилась успешно, и -1 в противном случае. + example: |- + use "gzip" + gzip("/tmp/readme.md.gz", "/tmp/readme.md") + - + name: ungzipBytes + args: "bytes" + desc: returns uncompressed bytes. + desc_ru: возвращает распакованный gzip массив байт. + example: |- + use "gzip" + bytes = ungzipBytes([0, 119, 87, 80/* ... */]) + - name: functional + scope: "both" + desc: "Contains functions for operating data in functional style" + desc_ru: "Содержит функции для работы с данными в функциональном стиле" + constants: + - + name: "IDENTITY" + typeName: "function" + type: 5 + value: "def(x) = x" + desc: "function which returns passed argument" + desc_ru: "функция, которая возвращает переданный в неё аргумент" + functions: + - name: "chain" + args: "data, functions..." + desc: "" + desc_ru: "" + - name: "combine" + args: "functions..." + desc: "combines functions" + desc_ru: "комбинирует функции (композиция)" + example: |- + f = combine(::f1, ::f2, ::f3) + // same as + f = def(f1, f2, f3) = f3(f2(f1)) + example_ru: |- + f = combine(::f1, ::f2, ::f3) + // равносильно + f = def(f1, f2, f3) = f3(f2(f1)) + - name: dropwhile + args: 'data, predicate' + desc: 'skips elements while predicate function returns true' + desc_ru: 'пропускает элементы пока функция-предикат возвращает true' + - name: "filter" + args: "data, predicate" + desc: "filters array or object.\n\n`predicate` is a function which takes one argument for arrays or two arguments for objects" + desc_ru: "фильтрует массив или объект и возвращает массив только с теми элементами, которые удовлетворяют предикату `predicate`.\n\n`predicate` - функция которая принимает один (для массивов) и два (для объектов) аргумента" + example: |- + nums = [1,2,3,4,5] + print filter(nums, def(x) = x % 2 == 0) // [2, 4] + - name: "flatmap" + args: "array, mapper" + desc: "converts each element of an array to other array" + desc_ru: "преобразует каждый элемент массива в массив элементов" + example: |- + nums = [1,2,3,4] + print flatmap(nums, def(x) { + arr = newarray(x) + for i = 0, i < x, i++ + arr[i] = x + return arr + }) // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] + - name: "foreach" + args: "data, consumer" + desc: "invokes function `consumer` for each element of array or map `data`\n\nIf `data` - массив, то в функции consumer необходим один параметр, если объект - два (ключ и значение)." + desc_ru: "для каждого элемента в массиве или объекте `data` вызывает функцию `consumer`\n\nЕсли `data` - массив, то в функции `consumer` необходим один параметр, если объект - два (ключ и значение)." + example: |- + foreach([1, 2, 3], def(v) { print v }) + foreach({"key": 1, "key2": "text"}, def(key, value) { + print key + ": " + value + }) + - name: "map" + args: "data, mapper..." + desc: "converts elements of array or map. If `data` is array - `mapper` converts his elements, if `data` is object - you need to pass `keyMapper` - converts keys and `valueMapper` - converts values" + desc_ru: "преобразует элементы массива или объекта.\n\nЕсли `data` - массив, то функция `mapper` преобразует значения, если объект - необходимо передать две функции: `keyMapper` - преобразует ключи и `valueMapper` - преобразует значения" + example: |- + nums = [3,4,5] + print map(nums, def(x) = x * x) // [9, 16, 25] + - name: "reduce" + args: "data, identity, accumulator" + desc: "converts elements of an array or a map to one value, e.g. sum of elements or concatenation string. `accumulator` takes one argument for array and two arguments for object (key and value)." + desc_ru: "преобразует элементы массива или объекта в одно значение, например сумма элементов или объединение в строку.\n\nЕсли `data` - массив, то в функции `accumulator` необходим один параметр, если объект - два (ключ и значение)" + example: |- + nums = [1,2,3,4,5] + print reduce(nums, 0, def(x, y) = x + x) // 15 + - name: "sortby" + args: "array, function" + desc: "sorts elements of an array or an object by `function` result" + desc_ru: "сортирует элементы массива по данным в функции `function`" + example: |- + data = [ + {"k1": 2, "k2": "x"}, + {"k1": 7, "k2": "d"}, + {"k1": 4, "k2": "z"}, + {"k1": 5, "k2": "p"}, + ] + print sortby(data, def(v) = v.k1) // [{k1=2, k2=x}, {k1=4, k2=z}, {k1=5, k2=p}, {k1=7, k2=d}] + print sortby(data, def(v) = v.k2) // [{k1=7, k2=d}, {k1=5, k2=p}, {k1=2, k2=x}, {k1=4, k2=z}] + - name: "stream" + args: "data" + desc: |- + creates stream from data and returns StreamValue + + StreamValue functions: + - `filter(func)` - filters elements + - `map(func)` - converts each element + - `flatMap(func)` - converts each element to array + - `sorted(func)` - sorts elements with comparator function + - `sortBy(func)` - applies function, then sorts elements + - `takeWhile(func)` - takes elements while predicate function returns true + - `dropWhile(func)` - skips elements while predicate function returns true + - `peek(func)` - executes function for each element and returns stream + - `skip(count)` - skips count elements + - `limit(count)` - limits elements size + - `custom(func)` - performs custom operation + - `reduce(func)` - converts elements to one value + - `forEach(func)` - executes function for each element + - `toArray()` - returns array of elements + - `count()` - returns count of elements + desc_ru: |- + создаёт stream из данных и возвращает StreamValue + + Функции StreamValue: + - `filter(func)` - фильтрует элементы + - `map(func)` - преобразует каждый элемент + - `flatMap(func)` - преобразует каждый элемент в массив + - `sorted(func)` - сортирует элементы в соответствии с функцией-компаратором + - `sortBy(func)` - применяет функцию, затем сортирует элементы + - `takeWhile(func)` - собирает элементы пока функция-предикат возвращает true + - `dropWhile(func)` - пропускает элементы пока функция-предикат возвращает true + - `peek(func)` - вызывает функцию для каждого элемента и возвращает stream + - `skip(count)` - пропускает указанное количество элементов + - `limit(count)` - ограничивает количество элементов + - `custom(func)` - выполняет пользовательскую операцию над данными + - `reduce(func)` - преобразует элементы в одно значение + - `forEach(func)` - вызывает функцию для каждого элемента + - `toArray()` - возвращает массив элементов + - `count()` - возвращает количество элементов + - name: takewhile + args: 'data, predicate' + desc: 'takes elements while predicate function returns true' + desc_ru: 'собирает элементы пока функция-предикат возвращает true' + - name: robot + scope: "both" + desc: "Contains functions for working with clipboard, processes, automation" + desc_ru: "Содержит функции для работы с буфером обмена, процессами, автоматизацией" + constants: + - + name: "BUTTON1" + typeName: number + type: 1 + value: "16" + desc: "left mouse button code" + desc_ru: "код левой кнопки мыши" + - + name: "BUTTON2" + typeName: number + type: 1 + value: "8" + desc: "middle mouse button code" + desc_ru: "код средней кнопки мыши" + - + name: "BUTTON3" + typeName: number + type: 1 + value: "4" + desc: "right mouse button code" + desc_ru: "код правой кнопки мыши" + - + name: "VK_DOWN" + typeName: number + type: 1 + value: "40" + desc: "key down code" + desc_ru: "код клавиши вниз" + - + name: "VK_ESCAPE" + typeName: number + type: 1 + value: "27" + desc: "Escape key code" + desc_ru: "код клавиши Escape" + - + name: "VK_FIRE" + typeName: number + type: 1 + value: "10" + desc: "Enter key code" + desc_ru: "код клавиши Enter" + - + name: "VK_LEFT" + typeName: number + type: 1 + value: "37" + desc: "key left code" + desc_ru: "код клавиши влево" + - + name: "VK_RIGHT" + typeName: number + type: 1 + value: "39" + desc: "key right code" + desc_ru: "код клавиши вправо" + functions: + - + name: "click" + args: "buttons" + scope: "desktop" + desc: "performs click with given mouse buttons" + desc_ru: "осуществляет клик мышью с заданными клавишами" + example: |- + click(BUTTON3) // right mouse button click + example_ru: |- + click(BUTTON3) // клик правой кнопкой мыши + - + name: "delay" + args: "ms" + scope: "desktop" + desc: "delay by given milliseconds" + desc_ru: "задержка на заданной количество миллисекунд" + - + name: "execProcess" + args: "args..." + desc: "executes the process with parameters" + desc_ru: "запускает процесс с параметрами\n\n Если функции переданы несколько аргументов, то они все передаются как параметры.\n Если функции передан только один параметр - массив, то его элементы передаются как параметры.\n Если функции передан только один параметр, то он служит единственным параметром." + example: |- + execProcess("mkdir", "Test") + execProcess("mkdir Test") + execProcess(["mkdir", "Test"]) + - + name: "execProcessAndWait" + args: "args..." + desc: "same as `execProcess`, but waits until process completes, returns it's exit code" + desc_ru: "аналогичен функции `execProcess`, но ожидает завершение порождаемого процесса и возвращает его статус" + - + name: "fromClipboard" + args: "" + desc: "gets text from clipboard" + desc_ru: "получает строку из буфера обмена" + - + name: "keyPress" + args: "key" + scope: "desktop" + desc: "performs pressing key" + desc_ru: "осуществляет зажатие клавиши с кодом key" + - + name: "keyRelease" + args: "key" + scope: "desktop" + desc: "performs releasing key" + desc_ru: "осуществляет отпускание клавиши с кодом key" + - + name: "mouseMove" + args: "x, y" + scope: "desktop" + desc: "moves mouse pointer to given point" + desc_ru: "перемещает указатель мыши в заданную координату" + - + name: "mousePress" + args: "buttons" + scope: "desktop" + desc: "performs pressing the given mouse button" + desc_ru: "осуществляет зажатие заданной кнопки мыши" + - + name: "mouseRelease" + args: "buttons" + scope: "desktop" + desc: "performs releasing the given mouse button" + desc_ru: "осуществляет отпускание заданной кнопки мыши" + - + name: "mouseWheel" + args: "value" + scope: "desktop" + desc: "performs scrolling (< 0 - up, > 0 - down)" + desc_ru: "осуществляет прокрутку колеса мыши (отрицательное значение - вверх, положительное - вниз)" + - + name: "setAutoDelay" + args: "ms" + scope: "desktop" + desc: "sets delay after each automation event" + desc_ru: "установка длительности автоматической задержки после каждого события автоматизации" + - + name: "toClipboard" + args: "text" + desc: "adds text to clipboards" + desc_ru: "копирует строку в буфер обмена" + - + name: "typeText" + args: "text" + scope: "desktop" + desc: "performs typing text by pressing keys for each character" + desc_ru: "осуществляет последовательное нажатие клавиш для набора заданного текста" + - + name: "sudo" + args: "args..." + scope: "android" + desc: "same as `execProcess`, but executes command as root (requires rooted device)" + desc_ru: "аналогичен функции `execProcess`, но выполняет команду от имени администратора (нужен Root)" + - name: ounit + scope: "both" + desc: "Contains functions for testing. Invokes all functions with prefix `test` and checks expected and actual values, counts execution time" + desc_ru: "Содержит функции для тестирования. Поочерёдно вызывает все функции программы, которые имеют приставку `test` и подсчитывает время выполнение и расхождения с ожидаемыми значениями" + constants: [] + functions: + - + name: "assertEquals" + args: "expected, actual" + desc: "checks that two values are equal" + desc_ru: "проверяет, равны ли два значения" + - + name: "assertFalse" + args: "actual" + desc: "checks that value is false (equals 0)" + desc_ru: "проверяет, является ли значение ложным (равным нулю)" + - + name: "assertNotEquals" + args: "expected, actual" + desc: "checks that two values are not equal" + desc_ru: "проверяет, отличаются ли два значения" + - + name: "assertSameType" + args: "expected, actual" + desc: "checks that types of two values are equal" + desc_ru: "проверяет, одинаковы ли типы у двух значений" + - + name: "assertTrue" + args: "actual" + desc: "checks that value is true (not equals 0)" + desc_ru: "проверяет, является ли значение истинным (не равным нулю)" + - + name: "runTests" + args: "" + desc: "executes tests and returns information about it's results" + desc_ru: "запускает тесты и возвращает информацию о них по завершению работы в виде строки" + example: |- + use "ounit" + + def testAdditionOnNumbers() { + assertEquals(6, 0 + 1 + 2 + 3) + } + + def testTypes() { + assertSameType(0, 0.0) + } + + def testFail() { + assertTrue(false) + } + + println runTests() + + /* + testTypes [passed] + Elapsed: 0,0189 sec + + testAdditionOnNumbers [passed] + Elapsed: 0,0008 sec + + testFail [FAILED] + Expected true, but found false. + Elapsed: 0,0001 sec + + Tests run: 3, Failures: 1, Time elapsed: 0,0198 sec + */ + - name: canvas + scope: "desktop" + desc: "Contains functions for working with graphics" + desc_ru: "Содержит функции для работы с графикой" + constants: + - + name: "VK_DOWN" + typeName: number + type: 1 + value: "40" + - + name: "VK_ESCAPE" + typeName: number + type: 1 + value: "27" + - + name: "VK_FIRE" + typeName: number + type: 1 + value: "10" + - + name: "VK_LEFT" + typeName: number + type: 1 + value: "37" + - + name: "VK_RIGHT" + typeName: number + type: 1 + value: "39" + - + name: "VK_UP" + typeName: number + type: 1 + value: "38" + functions: + - + name: "clip" + args: "" + desc: "" + desc_ru: "" + - + name: "color" + args: "" + desc: "" + desc_ru: "" + - + name: "drawstring" + args: "" + desc: "" + desc_ru: "" + - + name: "foval" + args: "" + desc: "" + desc_ru: "" + - + name: "frect" + args: "" + desc: "" + desc_ru: "" + - + name: "keypressed" + args: "" + desc: "" + desc_ru: "" + - + name: "line" + args: "" + desc: "" + desc_ru: "" + - + name: "mousehover" + args: "" + desc: "" + desc_ru: "" + - + name: "oval" + args: "" + desc: "" + desc_ru: "" + - + name: "prompt" + args: "" + desc: "" + desc_ru: "" + - + name: "rect" + args: "" + desc: "" + desc_ru: "" + - + name: "repaint" + args: "" + desc: "" + desc_ru: "" + - + name: "window" + args: "" + desc: "" + desc_ru: "" + - name: canvasfx + scope: "desktop" + desc: "Contains functions for working with Java FX graphics" + desc_ru: "Содержит функции для работы с графикой Java FX" + constants: + - + name: "ArcType" + typeName: map + type: 4 + value: "{OPEN=0, CHORD=1, ROUND=2}" + - + name: "BlendMode" + typeName: map + type: 4 + value: "{SRC_OVER=0, SRC_ATOP=1, ADD=2, MULTIPLY=3, SCREEN=4, OVERLAY=5, DARKEN=6, LIGHTEN=7, COLOR_DODGE=8, COLOR_BURN=9, HARD_LIGHT=10, SOFT_LIGHT=11, DIFFERENCE=12, EXCLUSION=13, RED=14, GREEN=15, BLUE=16}" + - + name: "Color" + typeName: map + type: 4 + value: "{hsb=def(hue,saturation,brightness,opacity=1.0), new=def(rgb) def(r,g,b,opacity=1.0), rgb=def(r,g,b,opacity=1.0), web=def(name,opacity=1.0, ALICEBLUE=ColorValue 0xf0f8ffff, ANTIQUEWHITE=ColorValue 0xfaebd7ff, AQUA=ColorValue 0x00ffffff, AQUAMARINE=ColorValue 0x7fffd4ff, AZURE=ColorValue 0xf0ffffff, BEIGE=ColorValue 0xf5f5dcff, BISQUE=ColorValue 0xffe4c4ff, BLACK=ColorValue 0x000000ff, BLANCHEDALMOND=ColorValue 0xffebcdff, BLUE=ColorValue 0x0000ffff, BLUEVIOLET=ColorValue 0x8a2be2ff, BROWN=ColorValue 0xa52a2aff, BURLYWOOD=ColorValue 0xdeb887ff, CADETBLUE=ColorValue 0x5f9ea0ff, CHARTREUSE=ColorValue 0x7fff00ff, CHOCOLATE=ColorValue 0xd2691eff, CORAL=ColorValue 0xff7f50ff, CORNFLOWERBLUE=ColorValue 0x6495edff, CORNSILK=ColorValue 0xfff8dcff, CRIMSON=ColorValue 0xdc143cff, CYAN=ColorValue 0x00ffffff, DARKBLUE=ColorValue 0x00008bff, DARKCYAN=ColorValue 0x008b8bff, DARKGOLDENROD=ColorValue 0xb8860bff, DARKGRAY=ColorValue 0xa9a9a9ff, DARKGREEN=ColorValue 0x006400ff, DARKGREY=ColorValue 0xa9a9a9ff, DARKKHAKI=ColorValue 0xbdb76bff, DARKMAGENTA=ColorValue 0x8b008bff, DARKOLIVEGREEN=ColorValue 0x556b2fff, DARKORANGE=ColorValue 0xff8c00ff, DARKORCHID=ColorValue 0x9932ccff, DARKRED=ColorValue 0x8b0000ff, DARKSALMON=ColorValue 0xe9967aff, DARKSEAGREEN=ColorValue 0x8fbc8fff, DARKSLATEBLUE=ColorValue 0x483d8bff, DARKSLATEGRAY=ColorValue 0x2f4f4fff, DARKSLATEGREY=ColorValue 0x2f4f4fff, DARKTURQUOISE=ColorValue 0x00ced1ff, DARKVIOLET=ColorValue 0x9400d3ff, DEEPPINK=ColorValue 0xff1493ff, DEEPSKYBLUE=ColorValue 0x00bfffff, DIMGRAY=ColorValue 0x696969ff, DIMGREY=ColorValue 0x696969ff, DODGERBLUE=ColorValue 0x1e90ffff, FIREBRICK=ColorValue 0xb22222ff, FLORALWHITE=ColorValue 0xfffaf0ff, FORESTGREEN=ColorValue 0x228b22ff, FUCHSIA=ColorValue 0xff00ffff, GAINSBORO=ColorValue 0xdcdcdcff, GHOSTWHITE=ColorValue 0xf8f8ffff, GOLD=ColorValue 0xffd700ff, GOLDENROD=ColorValue 0xdaa520ff, GRAY=ColorValue 0x808080ff, GREEN=ColorValue 0x008000ff, GREENYELLOW=ColorValue 0xadff2fff, GREY=ColorValue 0x808080ff, HONEYDEW=ColorValue 0xf0fff0ff, HOTPINK=ColorValue 0xff69b4ff, INDIANRED=ColorValue 0xcd5c5cff, INDIGO=ColorValue 0x4b0082ff, IVORY=ColorValue 0xfffff0ff, KHAKI=ColorValue 0xf0e68cff, LAVENDER=ColorValue 0xe6e6faff, LAVENDERBLUSH=ColorValue 0xfff0f5ff, LAWNGREEN=ColorValue 0x7cfc00ff, LEMONCHIFFON=ColorValue 0xfffacdff, LIGHTBLUE=ColorValue 0xadd8e6ff, LIGHTCORAL=ColorValue 0xf08080ff, LIGHTCYAN=ColorValue 0xe0ffffff, LIGHTGOLDENRODYELLOW=ColorValue 0xfafad2ff, LIGHTGRAY=ColorValue 0xd3d3d3ff, LIGHTGREEN=ColorValue 0x90ee90ff, LIGHTGREY=ColorValue 0xd3d3d3ff, LIGHTPINK=ColorValue 0xffb6c1ff, LIGHTSALMON=ColorValue 0xffa07aff, LIGHTSEAGREEN=ColorValue 0x20b2aaff, LIGHTSKYBLUE=ColorValue 0x87cefaff, LIGHTSLATEGRAY=ColorValue 0x778899ff, LIGHTSLATEGREY=ColorValue 0x778899ff, LIGHTSTEELBLUE=ColorValue 0xb0c4deff, LIGHTYELLOW=ColorValue 0xffffe0ff, LIME=ColorValue 0x00ff00ff, LIMEGREEN=ColorValue 0x32cd32ff, LINEN=ColorValue 0xfaf0e6ff, MAGENTA=ColorValue 0xff00ffff, MAROON=ColorValue 0x800000ff, MEDIUMAQUAMARINE=ColorValue 0x66cdaaff, MEDIUMBLUE=ColorValue 0x0000cdff, MEDIUMORCHID=ColorValue 0xba55d3ff, MEDIUMPURPLE=ColorValue 0x9370dbff, MEDIUMSEAGREEN=ColorValue 0x3cb371ff, MEDIUMSLATEBLUE=ColorValue 0x7b68eeff, MEDIUMSPRINGGREEN=ColorValue 0x00fa9aff, MEDIUMTURQUOISE=ColorValue 0x48d1ccff, MEDIUMVIOLETRED=ColorValue 0xc71585ff, MIDNIGHTBLUE=ColorValue 0x191970ff, MINTCREAM=ColorValue 0xf5fffaff, MISTYROSE=ColorValue 0xffe4e1ff, MOCCASIN=ColorValue 0xffe4b5ff, NAVAJOWHITE=ColorValue 0xffdeadff, NAVY=ColorValue 0x000080ff, OLDLACE=ColorValue 0xfdf5e6ff, OLIVE=ColorValue 0x808000ff, OLIVEDRAB=ColorValue 0x6b8e23ff, ORANGE=ColorValue 0xffa500ff, ORANGERED=ColorValue 0xff4500ff, ORCHID=ColorValue 0xda70d6ff, PALEGOLDENROD=ColorValue 0xeee8aaff, PALEGREEN=ColorValue 0x98fb98ff, PALETURQUOISE=ColorValue 0xafeeeeff, PALEVIOLETRED=ColorValue 0xdb7093ff, PAPAYAWHIP=ColorValue 0xffefd5ff, PEACHPUFF=ColorValue 0xffdab9ff, PERU=ColorValue 0xcd853fff, PINK=ColorValue 0xffc0cbff, PLUM=ColorValue 0xdda0ddff, POWDERBLUE=ColorValue 0xb0e0e6ff, PURPLE=ColorValue 0x800080ff, RED=ColorValue 0xff0000ff, ROSYBROWN=ColorValue 0xbc8f8fff, ROYALBLUE=ColorValue 0x4169e1ff, SADDLEBROWN=ColorValue 0x8b4513ff, SALMON=ColorValue 0xfa8072ff, SANDYBROWN=ColorValue 0xf4a460ff, SEAGREEN=ColorValue 0x2e8b57ff, SEASHELL=ColorValue 0xfff5eeff, SIENNA=ColorValue 0xa0522dff, SILVER=ColorValue 0xc0c0c0ff, SKYBLUE=ColorValue 0x87ceebff, SLATEBLUE=ColorValue 0x6a5acdff, SLATEGRAY=ColorValue 0x708090ff, SLATEGREY=ColorValue 0x708090ff, SNOW=ColorValue 0xfffafaff, SPRINGGREEN=ColorValue 0x00ff7fff, STEELBLUE=ColorValue 0x4682b4ff, TAN=ColorValue 0xd2b48cff, TEAL=ColorValue 0x008080ff, THISTLE=ColorValue 0xd8bfd8ff, TOMATO=ColorValue 0xff6347ff, TRANSPARENT=ColorValue 0x00000000, TURQUOISE=ColorValue 0x40e0d0ff, VIOLET=ColorValue 0xee82eeff, WHEAT=ColorValue 0xf5deb3ff, WHITE=ColorValue 0xffffffff, WHITESMOKE=ColorValue 0xf5f5f5ff, YELLOW=ColorValue 0xffff00ff, YELLOWGREEN=ColorValue 0x9acd32ff}" + - + name: "Events" + typeName: map + type: 4 + value: "{DRAG_DETECTED=0, MOUSE_CLICKED=1, MOUSE_DRAGGED=2, MOUSE_ENTERED=3, MOUSE_ENTERED_TARGET=4, MOUSE_EXITED=5, MOUSE_EXITED_TARGET=6, MOUSE_MOVED=7, MOUSE_PRESSED=8, MOUSE_RELEASED=9, KEY_PRESSED=10, KEY_RELEASED=11, KEY_TYPED=12, SWIPE_DOWN=13, SWIPE_LEFT=14, SWIPE_RIGHT=15, SWIPE_UP=16}" + - + name: "FillRule" + typeName: map + type: 4 + value: "{EVEN_ODD=0, NON_ZERO=1}" + - + name: "KeyCode" + typeName: map + type: 4 + value: "{A=36, ACCEPT=158, ADD=76, AGAIN=180, ALL_CANDIDATES=168, ALPHANUMERIC=162, ALT=7, ALT_GRAPH=185, AMPERSAND=134, ASTERISK=135, AT=141, B=37, BACK_QUOTE=112, BACK_SLASH=63, BACK_SPACE=1, BEGIN=186, BRACELEFT=139, BRACERIGHT=140, C=38, CANCEL=3, CAPS=9, CHANNEL_DOWN=218, CHANNEL_UP=217, CIRCUMFLEX=143, CLEAR=4, CLOSE_BRACKET=64, CODE_INPUT=170, COLON=142, COLORED_KEY_0=206, COLORED_KEY_1=207, COLORED_KEY_2=208, COLORED_KEY_3=209, COMMA=20, COMMAND=222, COMPOSE=184, CONTEXT_MENU=154, CONTROL=6, CONVERT=156, COPY=177, CUT=176, D=39, DEAD_ABOVEDOT=124, DEAD_ABOVERING=126, DEAD_ACUTE=119, DEAD_BREVE=123, DEAD_CARON=128, DEAD_CEDILLA=129, DEAD_CIRCUMFLEX=120, DEAD_DIAERESIS=125, DEAD_DOUBLEACUTE=127, DEAD_GRAVE=118, DEAD_IOTA=131, DEAD_MACRON=122, DEAD_OGONEK=130, DEAD_SEMIVOICED_SOUND=133, DEAD_TILDE=121, DEAD_VOICED_SOUND=132, DECIMAL=79, DELETE=81, DIGIT0=24, DIGIT1=25, DIGIT2=26, DIGIT3=27, DIGIT4=28, DIGIT5=29, DIGIT6=30, DIGIT7=31, DIGIT8=32, DIGIT9=33, DIVIDE=80, DOLLAR=144, DOWN=19, E=40, EJECT_TOGGLE=210, END=14, ENTER=0, EQUALS=35, ESCAPE=10, EURO_SIGN=145, EXCLAMATION_MARK=146, F=41, F1=84, F10=93, F11=94, F12=95, F13=96, F14=97, F15=98, F16=99, F17=100, F18=101, F19=102, F2=85, F20=103, F21=104, F22=105, F23=106, F24=107, F3=86, F4=87, F5=88, F6=89, F7=90, F8=91, F9=92, FAST_FWD=213, FINAL=155, FIND=181, FULL_WIDTH=165, G=42, GAME_A=198, GAME_B=199, GAME_C=200, GAME_D=201, GREATER=138, H=43, HALF_WIDTH=166, HELP=110, HIRAGANA=164, HOME=15, I=44, INFO=205, INPUT_METHOD_ON_OFF=175, INSERT=109, INVERTED_EXCLAMATION_MARK=147, J=45, JAPANESE_HIRAGANA=172, JAPANESE_KATAKANA=171, JAPANESE_ROMAN=173, K=46, KANA=160, KANA_LOCK=174, KANJI=161, KATAKANA=163, KP_DOWN=115, KP_LEFT=116, KP_RIGHT=117, KP_UP=114, L=47, LEFT=16, LEFT_PARENTHESIS=148, LESS=137, M=48, META=111, MINUS=21, MODECHANGE=159, MULTIPLY=75, MUTE=221, N=49, NONCONVERT=157, NUMBER_SIGN=149, NUMPAD0=65, NUMPAD1=66, NUMPAD2=67, NUMPAD3=68, NUMPAD4=69, NUMPAD5=70, NUMPAD6=71, NUMPAD7=72, NUMPAD8=73, NUMPAD9=74, NUM_LOCK=82, O=50, OPEN_BRACKET=62, P=51, PAGE_DOWN=13, PAGE_UP=12, PASTE=178, PAUSE=8, PERIOD=22, PLAY=211, PLUS=150, POUND=203, POWER=204, PREVIOUS_CANDIDATE=169, PRINTSCREEN=108, PROPS=182, Q=52, QUOTE=113, QUOTEDBL=136, R=53, RECORD=212, REWIND=214, RIGHT=18, RIGHT_PARENTHESIS=151, ROMAN_CHARACTERS=167, S=54, SCROLL_LOCK=83, SEMICOLON=34, SEPARATOR=77, SHIFT=5, SHORTCUT=223, SLASH=23, SOFTKEY_0=188, SOFTKEY_1=189, SOFTKEY_2=190, SOFTKEY_3=191, SOFTKEY_4=192, SOFTKEY_5=193, SOFTKEY_6=194, SOFTKEY_7=195, SOFTKEY_8=196, SOFTKEY_9=197, SPACE=11, STAR=202, STOP=183, SUBTRACT=78, T=55, TAB=2, TRACK_NEXT=216, TRACK_PREV=215, U=56, UNDEFINED=187, UNDERSCORE=152, UNDO=179, UP=17, V=57, VOLUME_DOWN=220, VOLUME_UP=219, W=58, WINDOWS=153, X=59, Y=60, Z=61}" + - + name: "MouseButton" + typeName: map + type: 4 + value: "{NONE=0, PRIMARY=1, MIDDLE=2, SECONDARY=3}" + - + name: "StrokeLineCap" + typeName: map + type: 4 + value: "{SQUARE=0, BUTT=1, ROUND=2}" + - + name: "StrokeLineJoin" + typeName: map + type: 4 + value: "{MITER=0, BEVEL=1, ROUND=2}" + - + name: "TextAlignment" + typeName: map + type: 4 + value: "{LEFT=0, CENTER=1, RIGHT=2, JUSTIFY=3}" + - + name: "VPos" + typeName: map + type: 4 + value: "{TOP=0, CENTER=1, BASELINE=2, BOTTOM=3}" + functions: + - + name: "BlendEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "BloomEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "BoxBlurEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "ColorAdjustEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "ColorInputEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "DropShadowEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "GaussianBlurEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "GlowEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "InnerShadowEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "LightingEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "MotionBlurEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "PerspectiveTransformEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "ReflectionEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "SepiaToneEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "ShadowEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "addEventFilter" + args: "" + desc: "" + desc_ru: "" + - + name: "addEventHandler" + args: "" + desc: "" + desc_ru: "" + - + name: "createImage" + args: "..." + desc: |- + `createImage(url)` - creates an image from url. + + `createImage(w, h)` - creates an image with the given size. + + `createBitmap(w, h, pixels)` - creates an image from pixels array. + + Returns ImageFXValue. + desc_ru: |- + `createImage(url)` - создаёт изображение из пути к ресурсу. + + `createImage(w, h)` - создаёт новое изображение с заданным размером. + + `createBitmap(w, h, pixels)` - создаёт изображение из массива пикселей. + + Возвращает ImageFXValue. + example: |- + use "canvasfx" + g = showcanvas() + bitmap = createImage("http://lorempixel.com/image_output/nature-q-c-640-480-10.jpg") + g.drawBitmap(bitmap, 0, 0) + bitmap = createImage("file:image.png") + g.drawBitmap(bitmap, 200, 0) + - + name: "repaint" + args: "" + desc: "" + desc_ru: "" + - + name: "window" + args: "" + desc: "" + desc_ru: "" + types: + - + name: "ColorValue" + - + name: "EffectValue" + - + name: "GraphicsFXValue" + functions: + - + name: "appendSVGPath" + args: "" + desc: "" + desc_ru: "" + - + name: "applyEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "arc" + args: "" + desc: "" + desc_ru: "" + - + name: "arcTo" + args: "" + desc: "" + desc_ru: "" + - + name: "beginPath" + args: "" + desc: "" + desc_ru: "" + - + name: "bezierCurveTo" + args: "" + desc: "" + desc_ru: "" + - + name: "clearRect" + args: "" + desc: "" + desc_ru: "" + - + name: "clip" + args: "" + desc: "" + desc_ru: "" + - + name: "closePath" + args: "" + desc: "" + desc_ru: "" + - + name: "fill" + args: "" + desc: "" + desc_ru: "" + - + name: "fillArc" + args: "" + desc: "" + desc_ru: "" + - + name: "fillOval" + args: "" + desc: "" + desc_ru: "" + - + name: "fillPolygon" + args: "" + desc: "" + desc_ru: "" + - + name: "fillRect" + args: "" + desc: "" + desc_ru: "" + - + name: "fillRoundRect" + args: "" + desc: "" + desc_ru: "" + - + name: "fillText" + args: "" + desc: "" + desc_ru: "" + - + name: "getFill" + args: "" + desc: "" + desc_ru: "" + - + name: "getFillRule" + args: "" + desc: "" + desc_ru: "" + - + name: "getGlobalAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "getGlobalBlendMode" + args: "" + desc: "" + desc_ru: "" + - + name: "getLineCap" + args: "" + desc: "" + desc_ru: "" + - + name: "getLineJoin" + args: "" + desc: "" + desc_ru: "" + - + name: "getLineWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "getMiterLimit" + args: "" + desc: "" + desc_ru: "" + - + name: "getStroke" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextAlign" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextBaseline" + args: "" + desc: "" + desc_ru: "" + - + name: "isPointInPath" + args: "" + desc: "" + desc_ru: "" + - + name: "lineTo" + args: "" + desc: "" + desc_ru: "" + - + name: "moveTo" + args: "" + desc: "" + desc_ru: "" + - + name: "quadraticCurveTo" + args: "" + desc: "" + desc_ru: "" + - + name: "rect" + args: "" + desc: "" + desc_ru: "" + - + name: "restore" + args: "" + desc: "" + desc_ru: "" + - + name: "rotate" + args: "" + desc: "" + desc_ru: "" + - + name: "save" + args: "" + desc: "" + desc_ru: "" + - + name: "scale" + args: "" + desc: "" + desc_ru: "" + - + name: "setEffect" + args: "" + desc: "" + desc_ru: "" + - + name: "setFill" + args: "" + desc: "" + desc_ru: "" + - + name: "setFillRule" + args: "" + desc: "" + desc_ru: "" + - + name: "setGlobalAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "setGlobalBlendMode" + args: "" + desc: "" + desc_ru: "" + - + name: "setLineCap" + args: "" + desc: "" + desc_ru: "" + - + name: "setLineJoin" + args: "" + desc: "" + desc_ru: "" + - + name: "setLineWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "setMiterLimit" + args: "" + desc: "" + desc_ru: "" + - + name: "setStroke" + args: "" + desc: "" + desc_ru: "" + - + name: "setTextAlign" + args: "" + desc: "" + desc_ru: "" + - + name: "setTextBaseline" + args: "" + desc: "" + desc_ru: "" + - + name: "stroke" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeArc" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeLine" + args: "x1, y1, x2, y2" + desc: "" + desc_ru: "" + - + name: "strokeOval" + args: "" + desc: "" + desc_ru: "" + - + name: "strokePolygon" + args: "" + desc: "" + desc_ru: "" + - + name: "strokePolyline" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeRect" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeRoundRect" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeText" + args: "" + desc: "" + desc_ru: "" + - + name: "transform" + args: "" + desc: "" + desc_ru: "" + - + name: "translate" + args: "" + desc: "" + desc_ru: "" + - + name: "ImageFXValue" + constants: + - + name: "width" + typeName: number + type: 1 + value: "/*width of the image*/" + - + name: "height" + typeName: number + type: 1 + value: "/*height of the image*/" + - + name: "preserveRatio" + typeName: number + type: 1 + value: "/*is preserve ratio*/" + - + name: "smooth" + typeName: number + type: 1 + value: "/*is smooth*/" + functions: + - + name: "getPixels" + args: "" + desc: "returns pixels array of the image" + desc_ru: "возвращает массив пикселей изображения" + - name: forms + scope: desktop + desc: "Contains functions for working with forms" + desc_ru: "Содержит функции для работы с формами" + constants: + - name: BorderLayout + type: 4 + typeName: map + value: '{AFTER_LINE_ENDS=After, LINE_END=After, LINE_START=Before, BEFORE_LINE_BEGINS=Before, CENTER=Center, EAST=East, BEFORE_FIRST_LINE=First, PAGE_START=First, AFTER_LAST_LINE=Last, PAGE_END=Last, NORTH=North, SOUTH=South, WEST=West}' + - name: BoxLayout + type: 4 + typeName: map + value: '{X_AXIS=0, Y_AXIS=1, LINE_AXIS=2, PAGE_AXIS=3}' + - name: DISPOSE_ON_CLOSE + type: 1 + typeName: number + value: '2' + - name: DO_NOTHING_ON_CLOSE + type: 1 + typeName: number + value: '0' + - name: EXIT_ON_CLOSE + type: 1 + typeName: number + value: '3' + - name: HIDE_ON_CLOSE + type: 1 + typeName: number + value: '1' + - name: SwingConstants + type: 4 + typeName: map + value: '{BOTTOM=3, CENTER=0, EAST=3, HORIZONTAL=0, LEADING=10, LEFT=2, NEXT=12, NORTH=1, NORTH_EAST=2, NORTH_WEST=8, PREVIOUS=13, RIGHT=4, SOUTH=5, SOUTH_EAST=4, SOUTH_WEST=6, TOP=1, TRAILING=11, VERTICAL=1, WEST=7}' + functions: + - name: borderLayout + args: 'hgap = 0, vgap = 0' + desc: 'creates BorderLayout' + desc_ru: 'создаёт BorderLayout' + - name: boxLayout + args: 'panel, axis = BoxLayout.PAGE_AXIS' + desc: 'creates BoxLayout' + desc_ru: 'создаёт BoxLayout' + - name: cardLayout + args: 'hgap = 0, vgap = 0' + desc: 'creates CardLayout' + desc_ru: 'создаёт CardLayout' + - name: flowLayout + args: 'align = FlowLayout.CENTER, hgap = 5, vgap = 5' + desc: 'creates FlowLayout' + desc_ru: 'создаёт FlowLayout' + - name: gridLayout + args: 'rows = 1, cols = 0, hgap = 0, vgap = 0' + desc: 'creates GridLayout' + desc_ru: 'создаёт GridLayout' + - name: newButton + args: 'text = ""' + desc: 'creates new button' + desc_ru: 'создаёт новую кнопку' + - name: newLabel + args: 'text = "", align = SwingConstants.LEADING' + desc: 'creates new label' + desc_ru: 'создаёт новую текстовую метку' + - name: newPanel + args: 'layoutManager = ...' + desc: 'creates new panel with optional layout manager' + desc_ru: 'создаёт новую панель с опциональным LayoutManager' + - name: newTextField + args: 'text = ""' + desc: 'creates new text field' + desc_ru: 'создаёт новое поле ввода' + - name: newWindow + args: 'title = ""' + desc: 'creates new window and returns JFrameValue' + desc_ru: 'создаёт новое окно и возвращает JFrameValue' + - name: java + scope: both + constants: + - name: Object.class + type: 4 + typeName: map + value: 'java.lang.Object' + - name: Object[].class + type: 4 + typeName: map + value: 'java.lang.Object[]' + - name: Object[][].class + type: 4 + typeName: map + value: 'java.lang.Object[][]' + - name: String.class + type: 4 + typeName: map + value: 'java.lang.String' + - name: String[].class + type: 4 + typeName: map + value: 'java.lang.String[]' + - name: String[][].class + type: 4 + typeName: map + value: 'java.lang.String[][]' + - name: boolean.class + type: 4 + typeName: map + value: 'boolean' + - name: boolean[].class + type: 4 + typeName: map + value: 'boolean[]' + - name: boolean[][].class + type: 4 + typeName: map + value: 'boolean[][]' + - name: byte.class + type: 4 + typeName: map + value: 'byte' + - name: byte[].class + type: 4 + typeName: map + value: 'byte[]' + - name: byte[][].class + type: 4 + typeName: map + value: 'byte[][]' + - name: char.class + type: 4 + typeName: map + value: 'char' + - name: char[].class + type: 4 + typeName: map + value: 'char[]' + - name: char[][].class + type: 4 + typeName: map + value: 'char[][]' + - name: double.class + type: 4 + typeName: map + value: 'double' + - name: double[].class + type: 4 + typeName: map + value: 'double[]' + - name: double[][].class + type: 4 + typeName: map + value: 'double[][]' + - name: float.class + type: 4 + typeName: map + value: 'float' + - name: float[].class + type: 4 + typeName: map + value: 'float[]' + - name: float[][].class + type: 4 + typeName: map + value: 'float[][]' + - name: int.class + type: 4 + typeName: map + value: 'int' + - name: int[].class + type: 4 + typeName: map + value: 'int[]' + - name: int[][].class + type: 4 + typeName: map + value: 'int[][]' + - name: long.class + type: 4 + typeName: map + value: 'long' + - name: long[].class + type: 4 + typeName: map + value: 'long[]' + - name: long[][].class + type: 4 + typeName: map + value: 'long[][]' + - name: 'null' + type: 482862660 + typeName: unknown (482862660) + value: 'null' + - name: short.class + type: 4 + typeName: map + value: 'short' + - name: short[].class + type: 4 + typeName: map + value: 'short[]' + - name: short[][].class + type: 4 + typeName: map + value: 'short[][]' + functions: + - name: isNull + args: 'values...' + desc: 'checks if one or more values are null' + desc_ru: 'проверяет, является ли одно или несколько значений null' + - name: newClass + args: 'name' + desc: 'creates ClassValue' + desc_ru: 'создаёт ClassValue' + - name: toObject + args: 'ownlangValue' + desc: 'converts ownlangValue to Java object' + desc_ru: 'конвертирует ownlangValue в Java объект' + - name: toValue + args: 'javaObject' + desc: 'converts javaObject to OwnLang value' + desc_ru: 'конвертирует javaObject в OwnLang значение' + types: + - name: ClassValue + functions: + - name: "asSubclass" + args: "" + desc: "" + desc_ru: "" + - name: "canonicalName" + args: "" + desc: "" + desc_ru: "" + - name: "cast" + args: "" + desc: "" + desc_ru: "" + - name: "genericString" + args: "" + desc: "" + desc_ru: "" + - name: "getComponentType" + args: "" + desc: "" + desc_ru: "" + - name: "getDeclaringClass" + args: "" + desc: "" + desc_ru: "" + - name: "getEnclosingClass" + args: "" + desc: "" + desc_ru: "" + - name: "getSuperclass" + args: "" + desc: "" + desc_ru: "" + - name: "getClasses" + args: "" + desc: "" + desc_ru: "" + - name: "getDeclaredClasses" + args: "" + desc: "" + desc_ru: "" + - name: "getInterfaces" + args: "" + desc: "" + desc_ru: "" + - name: "isAnnotation" + args: "" + desc: "" + desc_ru: "" + - name: "isAnonymousClass" + args: "" + desc: "" + desc_ru: "" + - name: "isArray" + args: "" + desc: "" + desc_ru: "" + - name: "isAssignableFrom" + args: "" + desc: "" + desc_ru: "" + - name: "isEnum" + args: "" + desc: "" + desc_ru: "" + - name: "isInterface" + args: "" + desc: "" + desc_ru: "" + - name: "isLocalClass" + args: "" + desc: "" + desc_ru: "" + - name: "isMemberClass" + args: "" + desc: "" + desc_ru: "" + - name: "isPrimitive" + args: "" + desc: "" + desc_ru: "" + - name: "isSynthetic" + args: "" + desc: "" + desc_ru: "" + - name: "modifiers" + args: "" + desc: "" + desc_ru: "" + - name: "name" + args: "" + desc: "" + desc_ru: "" + - name: "new" + args: "" + desc: "creates new instance of class" + desc_ru: "создаёт новый экземпляр класса" + - name: "simpleName" + args: "" + desc: "" + desc_ru: "" + - name: "typeName" + args: "" + desc: "" + desc_ru: "" + - name: NullValue + - name: ObjectValue + - name: jdbc + scope: desktop + constants: + - name: CLOSE_ALL_RESULTS + type: 1 + typeName: number + value: '3' + - name: CLOSE_CURRENT_RESULT + type: 1 + typeName: number + value: '1' + - name: CLOSE_CURSORS_AT_COMMIT + type: 1 + typeName: number + value: '2' + - name: CONCUR_READ_ONLY + type: 1 + typeName: number + value: '1007' + - name: CONCUR_UPDATABLE + type: 1 + typeName: number + value: '1008' + - name: EXECUTE_FAILED + type: 1 + typeName: number + value: '-3' + - name: FETCH_FORWARD + type: 1 + typeName: number + value: '1000' + - name: FETCH_REVERSE + type: 1 + typeName: number + value: '1001' + - name: FETCH_UNKNOWN + type: 1 + typeName: number + value: '1002' + - name: HOLD_CURSORS_OVER_COMMIT + type: 1 + typeName: number + value: '1' + - name: KEEP_CURRENT_RESULT + type: 1 + typeName: number + value: '2' + - name: NO_GENERATED_KEYS + type: 1 + typeName: number + value: '2' + - name: RETURN_GENERATED_KEYS + type: 1 + typeName: number + value: '1' + - name: SUCCESS_NO_INFO + type: 1 + typeName: number + value: '-2' + - name: TRANSACTION_NONE + type: 1 + typeName: number + value: '0' + - name: TRANSACTION_READ_COMMITTED + type: 1 + typeName: number + value: '2' + - name: TRANSACTION_READ_UNCOMMITTED + type: 1 + typeName: number + value: '1' + - name: TRANSACTION_REPEATABLE_READ + type: 1 + typeName: number + value: '4' + - name: TRANSACTION_SERIALIZABLE + type: 1 + typeName: number + value: '8' + - name: TYPE_FORWARD_ONLY + type: 1 + typeName: number + value: '1003' + - name: TYPE_SCROLL_INSENSITIVE + type: 1 + typeName: number + value: '1004' + - name: TYPE_SCROLL_SENSITIVE + type: 1 + typeName: number + value: '1005' + functions: + - name: getConnection + args: '...' + desc: |- + `getConnection(connectionUrl)` + + `getConnection(connectionUrl, driverClassName)` + + `getConnection(connectionUrl, user, password)` + + `getConnection(connectionUrl, user, password, driverClassName)` + + Creates connection and returns ConnectionValue. + desc_ru: |- + `getConnection(connectionUrl)` + + `getConnection(connectionUrl, driverClassName)` + + `getConnection(connectionUrl, user, password)` + + `getConnection(connectionUrl, user, password, driverClassName)` + + Создаёт подключение и возвращает ConnectionValue. + - name: mysql + args: 'connectionUrl' + desc: 'creates mysql connection' + desc_ru: 'создаёт mysql подключение' + - name: sqlite + args: 'connectionUrl' + desc: 'creates sqlite connection' + desc_ru: 'создаёт sqlite подключение' + types: + - name: ConnectionValue + functions: + - name: "clearWarnings" + args: "" + desc: "" + desc_ru: "" + - name: "close" + args: "" + desc: "" + desc_ru: "" + - name: "commit" + args: "" + desc: "" + desc_ru: "" + - name: "createStatement" + args: "" + desc: "" + desc_ru: "" + - name: "getAutoCommit" + args: "" + desc: "" + desc_ru: "" + - name: "getCatalog" + args: "" + desc: "" + desc_ru: "" + - name: "getHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "getNetworkTimeout" + args: "" + desc: "" + desc_ru: "" + - name: "getSchema" + args: "" + desc: "" + desc_ru: "" + - name: "getTransactionIsolation" + args: "" + desc: "" + desc_ru: "" + - name: "getUpdateCount" + args: "" + desc: "" + desc_ru: "" + - name: "isClosed" + args: "" + desc: "" + desc_ru: "" + - name: "isReadOnly" + args: "" + desc: "" + desc_ru: "" + - name: "prepareStatement" + args: "" + desc: "" + desc_ru: "" + - name: "rollback" + args: "" + desc: "" + desc_ru: "" + - name: "setHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "setTransactionIsolation" + args: "" + desc: "" + desc_ru: "" + - name: ResultSetValue + functions: + - name: "absolute" + args: "" + desc: "" + desc_ru: "" + - name: "afterLast" + args: "" + desc: "" + desc_ru: "" + - name: "beforeFirst" + args: "" + desc: "" + desc_ru: "" + - name: "cancelRowUpdates" + args: "" + desc: "" + desc_ru: "" + - name: "clearWarnings" + args: "" + desc: "" + desc_ru: "" + - name: "close" + args: "" + desc: "" + desc_ru: "" + - name: "deleteRow" + args: "" + desc: "" + desc_ru: "" + - name: "findColumn" + args: "" + desc: "" + desc_ru: "" + - name: "first" + args: "" + desc: "" + desc_ru: "" + - name: "getArray" + args: "" + desc: "" + desc_ru: "" + - name: "getBigDecimal" + args: "" + desc: "" + desc_ru: "" + - name: "getBoolean" + args: "" + desc: "" + desc_ru: "" + - name: "getByte" + args: "" + desc: "" + desc_ru: "" + - name: "getBytes" + args: "" + desc: "" + desc_ru: "" + - name: "getConcurrency" + args: "" + desc: "" + desc_ru: "" + - name: "getCursorName" + args: "" + desc: "" + desc_ru: "" + - name: "getDate" + args: "" + desc: "" + desc_ru: "" + - name: "getDouble" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "getFloat" + args: "" + desc: "" + desc_ru: "" + - name: "getHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "getInt" + args: "" + desc: "" + desc_ru: "" + - name: "getLong" + args: "" + desc: "" + desc_ru: "" + - name: "getNString" + args: "" + desc: "" + desc_ru: "" + - name: "getRow" + args: "" + desc: "" + desc_ru: "" + - name: "getRowId" + args: "" + desc: "" + desc_ru: "" + - name: "getShort" + args: "" + desc: "" + desc_ru: "" + - name: "getStatement" + args: "" + desc: "" + desc_ru: "" + - name: "getString" + args: "" + desc: "" + desc_ru: "" + - name: "getTime" + args: "" + desc: "" + desc_ru: "" + - name: "getTimestamp" + args: "" + desc: "" + desc_ru: "" + - name: "getType" + args: "" + desc: "" + desc_ru: "" + - name: "getURL" + args: "" + desc: "" + desc_ru: "" + - name: "insertRow" + args: "" + desc: "" + desc_ru: "" + - name: "isAfterLast" + args: "" + desc: "" + desc_ru: "" + - name: "isBeforeFirst" + args: "" + desc: "" + desc_ru: "" + - name: "isClosed" + args: "" + desc: "" + desc_ru: "" + - name: "isFirst" + args: "" + desc: "" + desc_ru: "" + - name: "isLast" + args: "" + desc: "" + desc_ru: "" + - name: "last" + args: "" + desc: "" + desc_ru: "" + - name: "moveToCurrentRow" + args: "" + desc: "" + desc_ru: "" + - name: "moveToInsertRow" + args: "" + desc: "" + desc_ru: "" + - name: "next" + args: "" + desc: "" + desc_ru: "" + - name: "previous" + args: "" + desc: "" + desc_ru: "" + - name: "refreshRow" + args: "" + desc: "" + desc_ru: "" + - name: "relative" + args: "" + desc: "" + desc_ru: "" + - name: "rowDeleted" + args: "" + desc: "" + desc_ru: "" + - name: "rowInserted" + args: "" + desc: "" + desc_ru: "" + - name: "rowUpdated" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "updateBigDecimal" + args: "" + desc: "" + desc_ru: "" + - name: "updateBoolean" + args: "" + desc: "" + desc_ru: "" + - name: "updateByte" + args: "" + desc: "" + desc_ru: "" + - name: "updateBytes" + args: "" + desc: "" + desc_ru: "" + - name: "updateDate" + args: "" + desc: "" + desc_ru: "" + - name: "updateDouble" + args: "" + desc: "" + desc_ru: "" + - name: "updateFloat" + args: "" + desc: "" + desc_ru: "" + - name: "updateInt" + args: "" + desc: "" + desc_ru: "" + - name: "updateLong" + args: "" + desc: "" + desc_ru: "" + - name: "updateNString" + args: "" + desc: "" + desc_ru: "" + - name: "updateNull" + args: "" + desc: "" + desc_ru: "" + - name: "updateRow" + args: "" + desc: "" + desc_ru: "" + - name: "updateShort" + args: "" + desc: "" + desc_ru: "" + - name: "updateString" + args: "" + desc: "" + desc_ru: "" + - name: "updateTime" + args: "" + desc: "" + desc_ru: "" + - name: "updateTimestamp" + args: "" + desc: "" + desc_ru: "" + - name: "wasNull" + args: "" + desc: "" + desc_ru: "" + - name: StatementValue + functions: + - name: "addBatch" + args: "" + desc: "" + desc_ru: "" + - name: "cancel" + args: "" + desc: "" + desc_ru: "" + - name: "clearBatch" + args: "" + desc: "" + desc_ru: "" + - name: "clearParameters" + args: "" + desc: "" + desc_ru: "" + - name: "clearWarnings" + args: "" + desc: "" + desc_ru: "" + - name: "close" + args: "" + desc: "" + desc_ru: "" + - name: "closeOnCompletion" + args: "" + desc: "" + desc_ru: "" + - name: "execute" + args: "" + desc: "" + desc_ru: "" + - name: "executeBatch" + args: "" + desc: "" + desc_ru: "" + - name: "executeLargeBatch" + args: "" + desc: "" + desc_ru: "" + - name: "executeLargeUpdate" + args: "" + desc: "" + desc_ru: "" + - name: "executeQuery" + args: "" + desc: "" + desc_ru: "" + - name: "executeUpdate" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "getGeneratedKeys" + args: "" + desc: "" + desc_ru: "" + - name: "getMaxFieldSize" + args: "" + desc: "" + desc_ru: "" + - name: "getMaxRows" + args: "" + desc: "" + desc_ru: "" + - name: "getMoreResults" + args: "" + desc: "" + desc_ru: "" + - name: "getQueryTimeout" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSet" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSetConcurrency" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSetHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSetType" + args: "" + desc: "" + desc_ru: "" + - name: "getUpdateCount" + args: "" + desc: "" + desc_ru: "" + - name: "isCloseOnCompletion" + args: "" + desc: "" + desc_ru: "" + - name: "isClosed" + args: "" + desc: "" + desc_ru: "" + - name: "isPoolable" + args: "" + desc: "" + desc_ru: "" + - name: "setBigDecimal" + args: "" + desc: "" + desc_ru: "" + - name: "setBoolean" + args: "" + desc: "" + desc_ru: "" + - name: "setByte" + args: "" + desc: "" + desc_ru: "" + - name: "setBytes" + args: "" + desc: "" + desc_ru: "" + - name: "setCursorName" + args: "" + desc: "" + desc_ru: "" + - name: "setDate" + args: "" + desc: "" + desc_ru: "" + - name: "setDouble" + args: "" + desc: "" + desc_ru: "" + - name: "setEscapeProcessing" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "setFloat" + args: "" + desc: "" + desc_ru: "" + - name: "setInt" + args: "" + desc: "" + desc_ru: "" + - name: "setLargeMaxRows" + args: "" + desc: "" + desc_ru: "" + - name: "setLong" + args: "" + desc: "" + desc_ru: "" + - name: "setMaxFieldSize" + args: "" + desc: "" + desc_ru: "" + - name: "setMaxRows" + args: "" + desc: "" + desc_ru: "" + - name: "setNString" + args: "" + desc: "" + desc_ru: "" + - name: "setNull" + args: "" + desc: "" + desc_ru: "" + - name: "setPoolable" + args: "" + desc: "" + desc_ru: "" + - name: "setQueryTimeout" + args: "" + desc: "" + desc_ru: "" + - name: "setShort" + args: "" + desc: "" + desc_ru: "" + - name: "setString" + args: "" + desc: "" + desc_ru: "" + - name: "setTime" + args: "" + desc: "" + desc_ru: "" + - name: "setTimestamp" + args: "" + desc: "" + desc_ru: "" + - name: "setURL" + args: "" + desc: "" + desc_ru: "" + - name: regex + since: 1.4.0 + constants: + - name: Pattern + type: 4 + typeName: map + value: "{UNIX_LINES=1, CASE_INSENSITIVE=2, I=2, COMMENTS=4, MULTILINE=8, M=8, LITERAL=16, S=32, DOTALL=32, UNICODE_CASE=64, CANON_EQ=128, UNICODE_CHARACTER_CLASS=256, U=256, quote=def(s) { return string }, matches=def(str,pattern) { return boolean }, split=def(str,pattern,limit = 0) { return array }, compile=def(pattern,flags = 0) { return PatternValue }}" + functions: + - name: regex + args: 'pattern, flags = 0' + desc: 'creates pattern and returns PatternValue' + desc_ru: 'создаёт шаблон регулярного выражения и возвращает PatternValue' + types: + - name: PatternValue + functions: + - name: "flags" + args: "" + desc: "returns pattern flags" + desc_ru: "возвращает флаги шаблона" + - name: "pattern" + args: "" + desc: "returns pattern as string" + desc_ru: "возвращает шаблон в виде строки" + - name: "matcher" + args: "input" + desc: "returns MatcherValue" + desc_ru: "возвращает MatcherValue" + - name: "matches" + args: "input" + desc: "checks if input matches the pattern" + desc_ru: "проверяет, соответствует ли входная строка шаблону" + - name: "split" + args: "input, limit = 0" + desc: "splits input around matches of this pattern" + desc_ru: "разбивает строку на основе совпадений шаблона" + - name: "replaceCallback" + args: "input, callback" + desc: "replaces input with the result of the given callback" + desc_ru: "заменяет строку результатом заданной функции" + example: |- + use "regex" + in = "dog cat" + pattern = regex("(\w+)\s(\w+)", Pattern.I) + println pattern.replaceCallback(in, def(m) = m.group(2) + "" + m.group(1)) + example_ru: |- + use "regex" + in = "пёс кот" + pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) + println pattern.replaceCallback(in, def(m) = m.group(2) + "о" + m.group(1)) + - name: MatcherValue + functions: + - name: "start" + args: "group = ..." + desc: "returns the start index of matched subsequence" + desc_ru: "возвращает начальную позицию найденного совпадения" + - name: "end" + args: "group = ..." + desc: "returns the offset after last character of matched subsequence" + desc_ru: "возвращает позицию, следующую за последним символов найденного совпадения" + - name: "find" + args: "start = 0" + desc: "resets this matcher and attempts to find the next matched subsequence" + desc_ru: "сбрасывает состояние матчера и пытается найти следующее совпадение" + - name: "group" + args: "group = 0" + desc: "returns matched group" + desc_ru: "возвращает найденную группу" + - name: "pattern" + args: "" + desc: "returns the pattern" + desc_ru: "возвращает шаблон" + - name: "region" + args: "start, end" + desc: "sets the limits of this matcher's region" + desc_ru: "задаёт ограничения для текущего региона" + - name: "replaceFirst" + args: "replacement" + desc: "replaces first matched subsequence with the given replacement string" + desc_ru: "заменяет первое совпадение на заданную строку" + - name: "replaceAll" + args: "replacement" + desc: "replaces all matched subsequences with the given replacement string" + desc_ru: "заменяет все совпадения на заданную строку" + - name: "replaceCallback" + args: "callback" + desc: "replaces input with the result of the given callback" + desc_ru: "заменяет строку результатом заданной функции" + example: |- + use "regex" + in = "dog cat" + pattern = regex("(\w+)\s(\w+)", Pattern.I) + matcher = pattern.matcher(in) + println matcher.replaceCallback(def(m) = m.group(2) + m.group(1)) + example_ru: |- + use "regex" + in = "пёс кот" + pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) + matcher = pattern.matcher(in) + println matcher.replaceCallback(def(m) = m.group(2) + "о" + m.group(1)) + - name: "reset" + args: input = "" + desc: "" + desc_ru: "" + - name: "usePattern" + args: "patternvalue" + desc: "" + desc_ru: "" + - name: "useAnchoringBounds" + args: "status" + desc: "" + desc_ru: "" + - name: "hasAnchoringBounds" + args: "" + desc: "" + desc_ru: "" + - name: "useTransparentBounds" + args: "status" + desc: "" + desc_ru: "" + - name: "hasTransparentBounds" + args: "" + desc: "" + desc_ru: "" + - name: "hitEnd" + args: "" + desc: "" + desc_ru: "" + - name: "lookingAt" + args: "" + desc: "" + desc_ru: "" + - name: "matches" + args: "" + desc: "" + desc_ru: "" + - name: "groupCount" + args: "" + desc: "" + desc_ru: "" + - name: "regionStart" + args: "" + desc: "" + desc_ru: "" + - name: "regionEnd" + args: "" + desc: "" + desc_ru: "" + - name: android + scope: "android" + desc: "Contains common Android functions" + desc_ru: "Содержит вспомогательные функции для Android" + constants: + - name: "Intent" + typeName: map + type: 4 + value: "{ACTION_BOOT_COMPLETED=android.intent.action.BOOT_COMPLETED, ACTION_DEFAULT=android.intent.action.VIEW, ACTION_DELETE=android.intent.action.DELETE, ACTION_EDIT=android.intent.action.EDIT, ACTION_INSTALL_PACKAGE=android.intent.action.INSTALL_PACKAGE, ACTION_LOCATION_SOURCE_SETTINGS=android.settings.LOCATION_SOURCE_SETTINGS, ACTION_MAIN=android.intent.action.MAIN, ACTION_MEDIA_MOUNTED=android.intent.action.MEDIA_MOUNTED, ACTION_REBOOT=android.intent.action.REBOOT, ACTION_RUN=android.intent.action.RUN, ACTION_SEARCH=android.intent.action.SEARCH, ACTION_SEND=android.intent.action.SEND, ACTION_VIEW=android.intent.action.VIEW, ACTION_WEB_SEARCH=android.intent.action.WEB_SEARCH}" + - name: R + type: 4 + typeName: map + value: '{array={}, attr={}, color={}, drawable={}, id={}, integer={}, layout={}, string={}}' + desc: "Resource constants from android.R.xxx class" + desc_ru: "Константы ресурсов класса android.R.xxx" + - name: SDK_INT + type: 1 + typeName: number + value: 'Android version SDK' + desc: "Android version SDK" + desc_ru: "Версия SDK Android" + - name: "Span" + typeName: map + type: 4 + value: "{COLOR=0, ABSOLUTE_SIZE=1, RELATIVE_SIZE=2, URL=3, STYLE=4, CLICKABLE=5, TYPEFACE=6, HTML=7}" + functions: + - + name: "assetBitmap" + args: "path" + desc: "loads bitmap from the file in apk's assets folder" + desc_ru: "загружает изображение из файла в папке assets внутри apk" + - + name: "assetBytes" + args: "path" + desc: "reads bytes of the file in apk's assets folder" + desc_ru: "читает массив байт из файла в папке assets внутри apk" + - + name: "assetText" + args: "path" + desc: "reads text content of the file in apk's assets folder" + desc_ru: "читает текст файла в папке assets внутри apk" + - + name: "chooser" + args: "" + desc: "" + desc_ru: "" + - + name: "listAssets" + args: "path" + desc: "returns list of files in apk's assets folder" + desc_ru: "возвращает список файлов в папке assets внутри apk" + - + name: "spannable" + args: "type, text, ..." + desc: "" + desc_ru: "" + - + name: "startActivity" + args: "intent, uri = \"\", bundle = []" + desc: "starts an activity" + desc_ru: "запускает Activity" + - + name: "toast" + args: "text, duration = 0" + desc: "shows toast notification" + desc_ru: "показывает всплывающее уведомление (toast)" + - + name: "uithread" + args: "function, ..." + desc: "runs function in main UI-thread" + desc_ru: "выполняет функцию в главном UI-потоке" + - name: canvas + scope: "android" + desc: "Contains functions for working with graphics on Android" + desc_ru: "Содержит функции для работы с графикой в Android" + constants: + - name: "VertexMode" + typeName: map + type: 4 + value: "{TRIANGLES=0, TRIANGLE_STRIP=1, TRIANGLE_FAN=2}" + - + name: "Action" + typeName: map + type: 4 + value: "{DOWN=0, UP=1, MOVE=2, MULTIPLE=2, CANCEL=3, OUTSIDE=4, POINTER_DOWN=5, POINTER_UP=6, POINTER_INDEX_SHIFT=8, MASK=255, POINTER_INDEX_MASK=65280}" + - + name: "BitmapCompressFormat" + typeName: map + type: 4 + value: "{JPEG=0, PNG=1, WEBP=2}" + - + name: "EdgeType" + typeName: map + type: 4 + value: "{BW=0, AA=1}" + - + name: "Cap" + typeName: map + type: 4 + value: "{BUTT=0, ROUND=1, SQUARE=2}" + - + name: "Style" + typeName: map + type: 4 + value: "{FILL=0, STROKE=1, FILL_AND_STROKE=2}" + - + name: "BitmapConfig" + typeName: map + type: 4 + value: "{ALPHA_8=0, RGB_565=1, ARGB_4444=2, ARGB_8888=3}" + - + name: "Join" + typeName: map + type: 4 + value: "{MITER=0, ROUND=1, BEVEL=2}" + - + name: "Align" + typeName: map + type: 4 + value: "{LEFT=0, CENTER=1, RIGHT=2}" + functions: + - name: "createBitmap" + args: "..." + desc: |- + `createBitmap(bitmap)` - creates a copy of the bitmap. + + `createBitmap(bytes)` - creates bitmap from byte array. + + `createBitmap(w, h)` - creates new bitmap with the given size. + + `createBitmap(w, h, config)` - creates new bitmap with the given size and config. + + `createBitmap(bytes, offset, length)` - creates bitmap from byte array starting from offset. + + `createBitmap(pixels, w, h, config)` - creates new bitmap from pixels array. + + `createBitmap(bitmap, x, y, w, h)` - creates new bitmap from the part of the `bitmap`. + + `createBitmap(pixels, offset, stride, w, h, config)` - creates new bitmap from pixels array starting from offset. + + Returns BitmapValue. + desc_ru: |- + `createBitmap(bitmap)` - создаёт копию изображения. + + `createBitmap(bytes)` - создаёт изображение из массива байт. + + `createBitmap(w, h)` - создаёт новое изображение с заданным размером. + + `createBitmap(w, h, config)` - создаёт новое изображение с заданным размером и конфигурацией. + + `createBitmap(bytes, offset, length)` - создаёт изображение из массива байт, начиная с offset. + + `createBitmap(pixels, w, h, config)` - создаёт изображение из массива пикселей. + + `createBitmap(bitmap, x, y, w, h)` - создаёт изображение из части другого изображения. + + `createBitmap(pixels, offset, stride, w, h, config)` - создаёт изображение из массива пикселей, начиная с offset. + + Возвращает BitmapValue. + example: |- + use ["http", "canvas"] + g = showcanvas() + imageBytes = download("http://lorempixel.com/image_output/nature-q-c-640-480-10.jpg") + bitmap = createBitmap(imageBytes) + g.drawBitmap(bitmap, 0, 0) + - + name: "createScaledBitmap" + args: "srcBitmap, width, height, filter" + desc: "scales bitmap to size and returns new BitmapValue" + desc_ru: "возвращает BitmapValue с изменённым размером заданного изображения" + - + name: "getScreenBitmap" + args: "" + desc: "returns current screen as bitmap" + desc_ru: "возвращает содержимое экрана в виде изображения" + - + name: "hidecanvas" + args: "" + desc: "closes canvas screen and releases resources" + desc_ru: "закрывает экран канваса и освобождает ресурсы" + - + name: "repaint" + args: "" + desc: "" + desc_ru: "" + - + name: "setOnKeyDownEvent" + args: "" + desc: "" + desc_ru: "" + - + name: "setOnKeyUpEvent" + args: "" + desc: "" + desc_ru: "" + - + name: "setOnLongPressEvent" + args: "" + desc: "" + desc_ru: "" + - + name: "setOnTouchEvent" + args: "" + desc: "" + desc_ru: "" + - + name: "showcanvas" + args: "" + desc: "shows canvas screen and returns GraphicsValue" + desc_ru: "показывает экран канваса и возвращает GraphicsValue" + example: |- + use "canvas" + g = showcanvas() + types: + - name: "BitmapValue" + functions: + - + name: "compress" + args: "" + desc: "" + desc_ru: "" + - + name: "copy" + args: "" + desc: "" + desc_ru: "" + - + name: "eraseColor" + args: "" + desc: "" + desc_ru: "" + - + name: "extractAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "getAllocationByteCount" + args: "" + desc: "" + desc_ru: "" + - + name: "getByteCount" + args: "" + desc: "" + desc_ru: "" + - + name: "getDensity" + args: "" + desc: "" + desc_ru: "" + - + name: "getGraphics" + args: "" + desc: "" + desc_ru: "" + - + name: "getWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "getHeight" + args: "" + desc: "" + desc_ru: "" + - + name: "getRowBytes" + args: "" + desc: "" + desc_ru: "" + - + name: "getPixel" + args: "" + desc: "" + desc_ru: "" + - + name: "getPixels" + args: "" + desc: "" + desc_ru: "" + - + name: "getScaledWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "getScaledHeight" + args: "" + desc: "" + desc_ru: "" + - + name: "hasAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "hasMipMap" + args: "" + desc: "" + desc_ru: "" + - + name: "isMutable" + args: "" + desc: "" + desc_ru: "" + - + name: "isPremultiplied" + args: "" + desc: "" + desc_ru: "" + - + name: "isRecycled" + args: "" + desc: "" + desc_ru: "" + - + name: "prepareToDraw" + args: "" + desc: "" + desc_ru: "" + - + name: "recycle" + args: "" + desc: "" + desc_ru: "" + - + name: "setPixel" + args: "" + desc: "" + desc_ru: "" + - + name: "setPixels" + args: "" + desc: "" + desc_ru: "" + - + name: "GraphicsValue" + functions: + - + name: "ascent" + args: "" + desc: "" + desc_ru: "" + - + name: "breakText" + args: "" + desc: "" + desc_ru: "" + - + name: "clearShadowLayer" + args: "" + desc: "" + desc_ru: "" + - + name: "clipRect" + args: "" + desc: "" + desc_ru: "" + - + name: "descent" + args: "" + desc: "" + desc_ru: "" + - + name: "drawARGB" + args: "" + desc: "" + desc_ru: "" + - + name: "drawArc" + args: "" + desc: "" + desc_ru: "" + - + name: "drawBitmap" + args: "" + desc: "" + desc_ru: "" + - + name: "drawCircle" + args: "" + desc: "" + desc_ru: "" + - + name: "drawColor" + args: "" + desc: "" + desc_ru: "" + - + name: "drawLine" + args: "" + desc: "" + desc_ru: "" + - + name: "drawOval" + args: "" + desc: "" + desc_ru: "" + - + name: "drawPoint" + args: "" + desc: "" + desc_ru: "" + - + name: "drawRGB" + args: "" + desc: "" + desc_ru: "" + - + name: "drawRect" + args: "" + desc: "" + desc_ru: "" + - + name: "drawRoundRect" + args: "" + desc: "" + desc_ru: "" + - + name: "drawText" + args: "" + desc: "" + desc_ru: "" + - + name: "fillCircle" + args: "" + desc: "" + desc_ru: "" + - + name: "fillOval" + args: "" + desc: "" + desc_ru: "" + - + name: "fillRect" + args: "" + desc: "" + desc_ru: "" + - + name: "fillRoundRect" + args: "" + desc: "" + desc_ru: "" + - + name: "getAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "getClipBounds" + args: "" + desc: "" + desc_ru: "" + - + name: "getColor" + args: "" + desc: "" + desc_ru: "" + - + name: "getDensity" + args: "" + desc: "" + desc_ru: "" + - + name: "getFlags" + args: "" + desc: "" + desc_ru: "" + - + name: "getFontSpacing" + args: "" + desc: "" + desc_ru: "" + - + name: "getHeight" + args: "" + desc: "" + desc_ru: "" + - + name: "getSaveCount" + args: "" + desc: "" + desc_ru: "" + - + name: "getStrokeCap" + args: "" + desc: "" + desc_ru: "" + - + name: "getStrokeJoin" + args: "" + desc: "" + desc_ru: "" + - + name: "getStrokeMiter" + args: "" + desc: "" + desc_ru: "" + - + name: "getStrokeWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "getStyle" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextAlign" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextBounds" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextScaleX" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextSize" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextSkewX" + args: "" + desc: "" + desc_ru: "" + - + name: "getTextWidths" + args: "" + desc: "" + desc_ru: "" + - + name: "getTypeface" + args: "" + desc: "" + desc_ru: "" + - + name: "getWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "isAntiAlias" + args: "" + desc: "" + desc_ru: "" + - + name: "isDither" + args: "" + desc: "" + desc_ru: "" + - + name: "isFakeBoldText" + args: "" + desc: "" + desc_ru: "" + - + name: "isFilterBitmap" + args: "" + desc: "" + desc_ru: "" + - + name: "isLinearText" + args: "" + desc: "" + desc_ru: "" + - + name: "isOpaque" + args: "" + desc: "" + desc_ru: "" + - + name: "isStrikeThruText" + args: "" + desc: "" + desc_ru: "" + - + name: "isSubpixelText" + args: "" + desc: "" + desc_ru: "" + - + name: "isUnderlineText" + args: "" + desc: "" + desc_ru: "" + - + name: "measureText" + args: "" + desc: "" + desc_ru: "" + - + name: "quickReject" + args: "" + desc: "" + desc_ru: "" + - + name: "reset" + args: "" + desc: "" + desc_ru: "" + - + name: "restore" + args: "" + desc: "" + desc_ru: "" + - + name: "restoreToCount" + args: "" + desc: "" + desc_ru: "" + - + name: "rotate" + args: "" + desc: "" + desc_ru: "" + - + name: "save" + args: "" + desc: "" + desc_ru: "" + - + name: "saveLayer" + args: "" + desc: "" + desc_ru: "" + - + name: "saveLayerAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "scale" + args: "" + desc: "" + desc_ru: "" + - + name: "setAlpha" + args: "" + desc: "" + desc_ru: "" + - + name: "setAntiAlias" + args: "" + desc: "" + desc_ru: "" + - + name: "setBitmap" + args: "" + desc: "" + desc_ru: "" + - + name: "setColor" + args: "" + desc: "" + desc_ru: "" + - + name: "setDensity" + args: "" + desc: "" + desc_ru: "" + - + name: "setDither" + args: "" + desc: "" + desc_ru: "" + - + name: "setFakeBoldText" + args: "" + desc: "" + desc_ru: "" + - + name: "setFilterBitmap" + args: "" + desc: "" + desc_ru: "" + - + name: "setFlags" + args: "" + desc: "" + desc_ru: "" + - + name: "setLinearText" + args: "" + desc: "" + desc_ru: "" + - + name: "setShadowLayer" + args: "" + desc: "" + desc_ru: "" + - + name: "setStrikeThruText" + args: "" + desc: "" + desc_ru: "" + - + name: "setStrokeCap" + args: "" + desc: "" + desc_ru: "" + - + name: "setStrokeJoin" + args: "" + desc: "" + desc_ru: "" + - + name: "setStrokeMiter" + args: "" + desc: "" + desc_ru: "" + - + name: "setStrokeWidth" + args: "" + desc: "" + desc_ru: "" + - + name: "setStyle" + args: "" + desc: "" + desc_ru: "" + - + name: "setSubpixelText" + args: "" + desc: "" + desc_ru: "" + - + name: "setTextAlign" + args: "" + desc: "" + desc_ru: "" + - + name: "setTextScaleX" + args: "" + desc: "" + desc_ru: "" + - + name: "setTextSize" + args: "" + desc: "" + desc_ru: "" + - + name: "setTextSkewX" + args: "" + desc: "" + desc_ru: "" + - + name: "setTypeface" + args: "" + desc: "" + desc_ru: "" + - + name: "setUnderlineText" + args: "" + desc: "" + desc_ru: "" + - + name: "skew" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeCircle" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeOval" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeRect" + args: "" + desc: "" + desc_ru: "" + - + name: "strokeRoundRect" + args: "" + desc: "" + desc_ru: "" + - + name: "translate" + args: "" + desc: "" + desc_ru: "" + - name: forms + scope: android + desc: "Contains functions for working with forms" + desc_ru: "Содержит функции для работы с формами" + constants: + - name: Gravity + type: 4 + typeName: map + value: '{NONE=0, NO_GRAVITY=0, CENTER_HORIZONTAL=1, LEFT=3, RIGHT=5, FILL_HORIZONTAL=7, CLIP_HORIZONTAL=8, CENTER_VERTICAL=16, CENTER=17, TOP=48, BOTTOM=80, FILL_VERTICAL=112, FILL=119, CLIP_VERTICAL=128}' + - name: InputType + type: 4 + typeName: map + value: '{TYPE_CLASS_DATETIME=4, TYPE_CLASS_NUMBER=2, TYPE_CLASS_PHONE=3, TYPE_CLASS_TEXT=1, TYPE_DATETIME_VARIATION_DATE=16, TYPE_DATETIME_VARIATION_NORMAL=0, TYPE_DATETIME_VARIATION_TIME=32, TYPE_MASK_CLASS=15, TYPE_MASK_FLAGS=16773120, TYPE_MASK_VARIATION=4080, TYPE_NULL=0, TYPE_NUMBER_FLAG_DECIMAL=8192, TYPE_NUMBER_FLAG_SIGNED=4096, TYPE_NUMBER_VARIATION_NORMAL=0, TYPE_NUMBER_VARIATION_PASSWORD=16, TYPE_TEXT_FLAG_AUTO_COMPLETE=65536, TYPE_TEXT_FLAG_AUTO_CORRECT=32768, TYPE_TEXT_FLAG_CAP_CHARACTERS=4096, TYPE_TEXT_FLAG_CAP_SENTENCES=16384, TYPE_TEXT_FLAG_CAP_WORDS=8192, TYPE_TEXT_FLAG_IME_MULTI_LINE=262144, TYPE_TEXT_FLAG_MULTI_LINE=131072, TYPE_TEXT_FLAG_NO_SUGGESTIONS=524288, TYPE_TEXT_VARIATION_EMAIL_ADDRESS=32, TYPE_TEXT_VARIATION_EMAIL_SUBJECT=48, TYPE_TEXT_VARIATION_FILTER=176, TYPE_TEXT_VARIATION_LONG_MESSAGE=80, TYPE_TEXT_VARIATION_NORMAL=0, TYPE_TEXT_VARIATION_PASSWORD=128, TYPE_TEXT_VARIATION_PERSON_NAME=96, TYPE_TEXT_VARIATION_PHONETIC=192, TYPE_TEXT_VARIATION_POSTAL_ADDRESS=112, TYPE_TEXT_VARIATION_SHORT_MESSAGE=64, TYPE_TEXT_VARIATION_URI=16, TYPE_TEXT_VARIATION_VISIBLE_PASSWORD=144, TYPE_TEXT_VARIATION_WEB_EDIT_TEXT=160, TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS=208, TYPE_TEXT_VARIATION_WEB_PASSWORD=224}' + - name: LinearLayout + type: 4 + typeName: map + value: '{HORIZONTAL=0, VERTICAL=1}' + - name: MATCH_PARENT + type: 1 + typeName: number + value: '-1' + - name: PorterDuff + type: 4 + typeName: map + value: '{ADD=16, CLEAR=0, DARKEN=12, DST=2, DST_ATOP=10, DST_IN=6, DST_OUT=8, DST_OVER=4, LIGHTEN=13, MULTIPLY=14, OVERLAY=17, SCREEN=15, SRC=1, SRC_ATOP=9, SRC_IN=5, SRC_OUT=7, SRC_OVER=3, XOR=11}' + - name: ScaleType + type: 4 + typeName: map + value: '{MATRIX=0, FIT_XY=1, FIT_START=2, FIT_CENTER=3, FIT_END=4, CENTER=5, CENTER_CROP=6, CENTER_INSIDE=7}' + - name: WRAP_CONTENT + type: 1 + typeName: number + value: '-2' + functions: + - name: showForm + args: 'view, layoutParams = {}' + desc: 'shows view' + desc_ru: 'показывает форму' + - name: inflate + args: 'resourceId, rootView = null, attachToRoot = false' + desc: 'Inflates view from resource xml' + desc_ru: 'Создаёт view из xml-ресурса' + - name: newArrayAdapter + args: 'resourceId = R.layout.simple_list_item_1, elements = []' + desc: 'Creates ArrayAdapter to use in ListView' + desc_ru: 'Создаёт ArrayAdapter для использования в ListView' + - name: newBaseAdapter + args: 'mapWithFunctions' + desc: '' + desc_ru: '' + example: |- + items = [ + {"img" : img1, "text" : "Item 1"}, + {"img" : img2, "text" : "Item 2"} + ] + adapter = newBaseAdapter({ + "getCount": def() = length(items) + "getItem": def(pos) = items[pos] + "getItemId": def(pos) = pos + "getView": def(pos, view, parent) { + if (view == 0) { + view = newLinearLayout() + view.setOrientation(LinearLayout.HORIZONTAL) + imageView = newImageView() + view.addView(imageView) + textView = newTextView() + view.addView(textView) + view.setTag([imageView, textView]) + } else { + extract(imageView, textView) = view.getTag() + } + + imageView.setImageBitmap(items[pos].img); + textView.setText(toHexString(items[pos].text)); + return view + } + }); + - name: newButton + args: 'text = ""' + desc: 'creates Button' + desc_ru: 'создаёт Button' + - name: newCheckBox + args: '' + desc: 'creates CheckBox' + desc_ru: 'создаёт CheckBox' + - name: newEditText + args: '' + desc: 'creates EditText' + desc_ru: 'создаёт EditText' + - name: newFrameLayout + args: '' + desc: 'creates FrameLayout container' + desc_ru: 'создаёт контейнер FrameLayout' + - name: newImageButton + args: '' + desc: 'creates ImageButton' + desc_ru: 'создаёт ImageButton' + - name: newImageView + args: '' + desc: 'creates ImageView' + desc_ru: 'создаёт ImageView' + - name: newLinearLayout + args: '' + desc: 'creates LinearLayout container' + desc_ru: 'создаёт контейнер LinearLayout' + - name: newListView + args: '' + desc: 'creates ListView' + desc_ru: 'создаёт ListView' + - name: newProgressBar + args: 'style = R.attr.progressBarStyle' + desc: 'creates ProgressBar' + desc_ru: 'создаёт ProgressBar' + example: |- + use ["android", "forms"] + pb1 = newProgressBar(R.attr.progressBarStyleHorizontal) + pb1.setMax(100) + pb1.setProgress(10) + pb2 = newProgressBar() + pb2.setIndeterminate(true) + - name: newRadioButton + args: '' + desc: 'creates RadioButton' + desc_ru: 'создаёт RadioButton' + - name: newRadioGroup + args: '' + desc: 'creates RadioGroup container' + desc_ru: 'создаёт контейнер RadioGroup' + - name: newRelativeLayout + args: '' + desc: 'creates RelativeLayout container' + desc_ru: 'создаёт контейнер RelativeLayout' + - name: newScrollView + args: '' + desc: 'creates ScrollView container' + desc_ru: 'создаёт контейнер ScrollView' + - name: newSeekBar + args: '' + desc: 'creates SeekBar' + desc_ru: 'создаёт SeekBar' + - name: newSwitch + args: '' + desc: 'creates Switch (available for SDK_INT >= 14)' + desc_ru: 'создаёт Switch (доступен для SDK_INT >= 14)' + - name: newTextView + args: 'text = ""' + desc: 'creates TextView' + desc_ru: 'создаёт TextView' + - name: newToggleButton + args: '' + desc: 'creates ToggleButton' + desc_ru: 'создаёт ToggleButton' + types: + - name: ViewValue + functions: + - name: bringToFront + args: '' + desc: '' + desc_ru: '' + - name: buildDrawingCache + args: '' + desc: '' + desc_ru: '' + - name: callOnClick + args: '' + desc: 'available for SDK_INT >= 15' + desc_ru: 'доступно для SDK_INT >= 15' + - name: cancelLongPress + args: '' + desc: '' + desc_ru: '' + - name: clearAnimation + args: '' + desc: '' + desc_ru: '' + - name: clearFocus + args: '' + desc: '' + desc_ru: '' + - name: computeScroll + args: '' + desc: '' + desc_ru: '' + - name: destroyDrawingCache + args: '' + desc: '' + desc_ru: '' + - name: dispatchDisplayHint + args: '' + desc: '' + desc_ru: '' + - name: findFocus + args: '' + desc: '' + desc_ru: '' + - name: findViewById + args: '' + desc: '' + desc_ru: '' + - name: focusSearch + args: '' + desc: '' + desc_ru: '' + - name: forceLayout + args: '' + desc: '' + desc_ru: '' + - name: getAlpha + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getBaseline + args: '' + desc: '' + desc_ru: '' + - name: getBottom + args: '' + desc: '' + desc_ru: '' + - name: getContentDescription + args: '' + desc: '' + desc_ru: '' + - name: getDrawingCacheBackgroundColor + args: '' + desc: '' + desc_ru: '' + - name: getDrawingCacheQuality + args: '' + desc: '' + desc_ru: '' + - name: getDrawingTime + args: '' + desc: '' + desc_ru: '' + - name: getHeight + args: '' + desc: '' + desc_ru: '' + - name: getHorizontalFadingEdgeLength + args: '' + desc: '' + desc_ru: '' + - name: getId + args: '' + desc: '' + desc_ru: '' + - name: getKeepScreenOn + args: '' + desc: '' + desc_ru: '' + - name: getLeft + args: '' + desc: '' + desc_ru: '' + - name: getMeasuredHeight + args: '' + desc: '' + desc_ru: '' + - name: getMeasuredHeightAndState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getMeasuredState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getMeasuredWidth + args: '' + desc: '' + desc_ru: '' + - name: getMeasuredWidthAndState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getNextFocusDownId + args: '' + desc: '' + desc_ru: '' + - name: getNextFocusForwardId + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getNextFocusLeftId + args: '' + desc: '' + desc_ru: '' + - name: getNextFocusRightId + args: '' + desc: '' + desc_ru: '' + - name: getNextFocusUpId + args: '' + desc: '' + desc_ru: '' + - name: getOverScrollMode + args: '' + desc: '' + desc_ru: '' + - name: getPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getPaddingEnd + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getPaddingLeft + args: '' + desc: '' + desc_ru: '' + - name: getPaddingRight + args: '' + desc: '' + desc_ru: '' + - name: getPaddingStart + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: getPivotX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getPivotY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getRight + args: '' + desc: '' + desc_ru: '' + - name: getRootView + args: '' + desc: '' + desc_ru: '' + - name: getRotation + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getRotationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getRotationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getScaleX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getScaleY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getScrollBarFadeDuration + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: getScrollBarSize + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: getScrollBarStyle + args: '' + desc: '' + desc_ru: '' + - name: getScrollX + args: '' + desc: '' + desc_ru: '' + - name: getScrollY + args: '' + desc: '' + desc_ru: '' + - name: getSolidColor + args: '' + desc: '' + desc_ru: '' + - name: getSystemUiVisibility + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getTag + args: '' + desc: '' + desc_ru: '' + - name: getTextAlignment + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getTextDirection + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getTop + args: '' + desc: '' + desc_ru: '' + - name: getTranslationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getTranslationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getTranslationZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: getVerticalFadingEdgeLength + args: '' + desc: '' + desc_ru: '' + - name: getVerticalScrollbarPosition + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getVerticalScrollbarWidth + args: '' + desc: '' + desc_ru: '' + - name: getVisibility + args: '' + desc: '' + desc_ru: '' + - name: getWidth + args: '' + desc: '' + desc_ru: '' + - name: getWindowSystemUiVisibility + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: getWindowVisibility + args: '' + desc: '' + desc_ru: '' + - name: getX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: hasFocus + args: '' + desc: '' + desc_ru: '' + - name: hasFocusable + args: '' + desc: '' + desc_ru: '' + - name: hasNestedScrollingParent + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: hasOnClickListeners + args: '' + desc: 'available for SDK_INT >= 15' + desc_ru: 'доступно для SDK_INT >= 15' + - name: hasOverlappingRendering + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: hasTransientState + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: hasWindowFocus + args: '' + desc: '' + desc_ru: '' + - name: invalidate + args: '' + desc: '' + desc_ru: '' + - name: invalidateDrawable + args: '' + desc: '' + desc_ru: '' + - name: invalidateOutline + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isAccessibilityFocused + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isActivated + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isAttachedToWindow + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isClickable + args: '' + desc: '' + desc_ru: '' + - name: isContextClickable + args: '' + desc: 'available for SDK_INT >= 23' + desc_ru: 'доступно для SDK_INT >= 23' + - name: isDirty + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isDrawingCacheEnabled + args: '' + desc: '' + desc_ru: '' + - name: isDuplicateParentStateEnabled + args: '' + desc: '' + desc_ru: '' + - name: isEnabled + args: '' + desc: '' + desc_ru: '' + - name: isFocusable + args: '' + desc: '' + desc_ru: '' + - name: isFocusableInTouchMode + args: '' + desc: '' + desc_ru: '' + - name: isFocused + args: '' + desc: '' + desc_ru: '' + - name: isHapticFeedbackEnabled + args: '' + desc: '' + desc_ru: '' + - name: isHardwareAccelerated + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isHorizontalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: isHorizontalScrollBarEnabled + args: '' + desc: '' + desc_ru: '' + - name: isHovered + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: isImportantForAccessibility + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isInEditMode + args: '' + desc: '' + desc_ru: '' + - name: isInLayout + args: '' + desc: 'available for SDK_INT >= 18' + desc_ru: 'доступно для SDK_INT >= 18' + - name: isInTouchMode + args: '' + desc: '' + desc_ru: '' + - name: isLaidOut + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isLayoutDirectionResolved + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isLayoutRequested + args: '' + desc: '' + desc_ru: '' + - name: isLongClickable + args: '' + desc: '' + desc_ru: '' + - name: isNestedScrollingEnabled + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isOpaque + args: '' + desc: '' + desc_ru: '' + - name: isPaddingRelative + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: isPressed + args: '' + desc: '' + desc_ru: '' + - name: isSaveEnabled + args: '' + desc: '' + desc_ru: '' + - name: isSaveFromParentEnabled + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isScrollContainer + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: isScrollbarFadingEnabled + args: '' + desc: '' + desc_ru: '' + - name: isSelected + args: '' + desc: '' + desc_ru: '' + - name: isShown + args: '' + desc: '' + desc_ru: '' + - name: isSoundEffectsEnabled + args: '' + desc: '' + desc_ru: '' + - name: isTextAlignmentResolved + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isTextDirectionResolved + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isVerticalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: isVerticalScrollBarEnabled + args: '' + desc: '' + desc_ru: '' + - name: jumpDrawablesToCurrentState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: offsetLeftAndRight + args: '' + desc: '' + desc_ru: '' + - name: offsetTopAndBottom + args: '' + desc: '' + desc_ru: '' + - name: onClick + args: '' + desc: '' + desc_ru: '' + - name: onFocusChange + args: '' + desc: '' + desc_ru: '' + - name: onKey + args: '' + desc: '' + desc_ru: '' + - name: onLongClick + args: '' + desc: '' + desc_ru: '' + - name: performClick + args: '' + desc: '' + desc_ru: '' + - name: performHapticFeedback + args: '' + desc: '' + desc_ru: '' + - name: performLongClick + args: '' + desc: '' + desc_ru: '' + - name: playSoundEffect + args: '' + desc: '' + desc_ru: '' + - name: post + args: '' + desc: '' + desc_ru: '' + - name: postDelayed + args: '' + desc: '' + desc_ru: '' + - name: postInvalidate + args: '' + desc: '' + desc_ru: '' + - name: refreshDrawableState + args: '' + desc: '' + desc_ru: '' + - name: requestFocus + args: '' + desc: '' + desc_ru: '' + - name: requestFocusFromTouch + args: '' + desc: '' + desc_ru: '' + - name: requestLayout + args: '' + desc: '' + desc_ru: '' + - name: scrollBy + args: '' + desc: '' + desc_ru: '' + - name: scrollTo + args: '' + desc: '' + desc_ru: '' + - name: sendAccessibilityEvent + args: '' + desc: '' + desc_ru: '' + - name: setActivated + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setAlpha + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setBackground + args: '' + desc: '' + desc_ru: '' + - name: setBackgroundColor + args: '' + desc: '' + desc_ru: '' + - name: setBackgroundDrawable + args: '' + desc: '' + desc_ru: '' + - name: setBackgroundResource + args: '' + desc: '' + desc_ru: '' + - name: setBottom + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setCameraDistance + args: '' + desc: 'available for SDK_INT >= 12' + desc_ru: 'доступно для SDK_INT >= 12' + - name: setClickable + args: '' + desc: '' + desc_ru: '' + - name: setClipToOutline + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: setContentDescription + args: '' + desc: '' + desc_ru: '' + - name: setContextClickable + args: '' + desc: 'available for SDK_INT >= 23' + desc_ru: 'доступно для SDK_INT >= 23' + - name: setDrawingCacheBackgroundColor + args: '' + desc: '' + desc_ru: '' + - name: setDrawingCacheEnabled + args: '' + desc: '' + desc_ru: '' + - name: setDrawingCacheQuality + args: '' + desc: '' + desc_ru: '' + - name: setDuplicateParentStateEnabled + args: '' + desc: '' + desc_ru: '' + - name: setEnabled + args: '' + desc: '' + desc_ru: '' + - name: setFadingEdgeLength + args: '' + desc: '' + desc_ru: '' + - name: setFilterTouchesWhenObscured + args: '' + desc: '' + desc_ru: '' + - name: setFitsSystemWindows + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setFocusable + args: '' + desc: '' + desc_ru: '' + - name: setFocusableInTouchMode + args: '' + desc: '' + desc_ru: '' + - name: setForeground + args: '' + desc: '' + desc_ru: '' + - name: setHapticFeedbackEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalScrollBarEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHovered + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setId + args: '' + desc: '' + desc_ru: '' + - name: setImportantForAccessibility + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setKeepScreenOn + args: '' + desc: '' + desc_ru: '' + - name: setLabelFor + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setLayoutDirection + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setLeft + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setLongClickable + args: '' + desc: '' + desc_ru: '' + - name: setMinimumHeight + args: '' + desc: '' + desc_ru: '' + - name: setMinimumWidth + args: '' + desc: '' + desc_ru: '' + - name: setNestedScrollingEnabled + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: setNextFocusDownId + args: '' + desc: '' + desc_ru: '' + - name: setNextFocusForwardId + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setNextFocusLeftId + args: '' + desc: '' + desc_ru: '' + - name: setNextFocusRightId + args: '' + desc: '' + desc_ru: '' + - name: setNextFocusUpId + args: '' + desc: '' + desc_ru: '' + - name: setOnClickListener + args: '' + desc: '' + desc_ru: '' + - name: setOnFocusChangeListener + args: '' + desc: '' + desc_ru: '' + - name: setOnKeyListener + args: '' + desc: '' + desc_ru: '' + - name: setOnLongClickListener + args: '' + desc: '' + desc_ru: '' + - name: setOverScrollMode + args: '' + desc: '' + desc_ru: '' + - name: setPadding + args: '' + desc: '' + desc_ru: '' + - name: setPaddingRelative + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setPivotX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setPivotY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setPressed + args: '' + desc: '' + desc_ru: '' + - name: setRight + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setRotation + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setRotationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setRotationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setSaveEnabled + args: '' + desc: '' + desc_ru: '' + - name: setSaveFromParentEnabled + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setScaleX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setScaleY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setScrollBarDefaultDelayBeforeFade + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setScrollBarFadeDuration + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setScrollBarSize + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setScrollBarStyle + args: '' + desc: '' + desc_ru: '' + - name: setScrollContainer + args: '' + desc: '' + desc_ru: '' + - name: setScrollX + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setScrollY + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setSelected + args: '' + desc: '' + desc_ru: '' + - name: setSoundEffectsEnabled + args: '' + desc: '' + desc_ru: '' + - name: setSystemUiVisibility + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTag + args: '' + desc: '' + desc_ru: '' + - name: setTextAlignment + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setTextDirection + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setTop + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTranslationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTranslationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTranslationZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: setVerticalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: setVerticalScrollbarPosition + args: '' + desc: '' + desc_ru: '' + - name: setVisibility + args: '' + desc: '' + desc_ru: '' + - name: setWillNotCacheDrawing + args: '' + desc: '' + desc_ru: '' + - name: setWillNotDraw + args: '' + desc: '' + desc_ru: '' + - name: setX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: showContextMenu + args: '' + desc: '' + desc_ru: '' + - name: willNotCacheDrawing + args: '' + desc: '' + desc_ru: '' + - name: willNotDraw + args: '' + desc: '' + desc_ru: '' + - name: TextViewValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: beginBatchEdit + args: '' + desc: '' + desc_ru: '' + - name: endBatchEdit + args: '' + desc: '' + desc_ru: '' + - name: getAutoLinkMask + args: '' + desc: '' + desc_ru: '' + - name: getCompoundDrawablePadding + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingLeft + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingRight + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: getCurrentHintTextColor + args: '' + desc: '' + desc_ru: '' + - name: getCurrentTextColor + args: '' + desc: '' + desc_ru: '' + - name: getEditableText + args: '' + desc: '' + desc_ru: '' + - name: getEllipsize + args: '' + desc: '' + desc_ru: '' + - name: getError + args: '' + desc: '' + desc_ru: '' + - name: getExtendedPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getExtendedPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: getFreezesText + args: '' + desc: '' + desc_ru: '' + - name: getGravity + args: '' + desc: '' + desc_ru: '' + - name: getHighlightColor + args: '' + desc: '' + desc_ru: '' + - name: getHint + args: '' + desc: '' + desc_ru: '' + - name: getImeActionId + args: '' + desc: '' + desc_ru: '' + - name: getImeActionLabel + args: '' + desc: '' + desc_ru: '' + - name: getImeOptions + args: '' + desc: '' + desc_ru: '' + - name: getInputType + args: '' + desc: '' + desc_ru: '' + - name: getLineCount + args: '' + desc: '' + desc_ru: '' + - name: getLineHeight + args: '' + desc: '' + desc_ru: '' + - name: getLinksClickable + args: '' + desc: '' + desc_ru: '' + - name: getSelectionEnd + args: '' + desc: '' + desc_ru: '' + - name: getSelectionStart + args: '' + desc: '' + desc_ru: '' + - name: getText + args: '' + desc: '' + desc_ru: '' + - name: getTextScaleX + args: '' + desc: '' + desc_ru: '' + - name: getTextSize + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingLeft + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingRight + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: hasSelection + args: '' + desc: '' + desc_ru: '' + - name: isCursorVisible + args: '' + desc: '' + desc_ru: '' + - name: isInputMethodTarget + args: '' + desc: '' + desc_ru: '' + - name: isSuggestionsEnabled + args: '' + desc: '' + desc_ru: '' + - name: isTextSelectable + args: '' + desc: '' + desc_ru: '' + - name: length + args: '' + desc: '' + desc_ru: '' + - name: moveCursorToVisibleOffset + args: '' + desc: '' + desc_ru: '' + - name: setAllCaps + args: '' + desc: '' + desc_ru: '' + - name: setAutoLinkMask + args: '' + desc: '' + desc_ru: '' + - name: setBreakStrategy + args: '' + desc: '' + desc_ru: '' + - name: setCompoundDrawablePadding + args: '' + desc: '' + desc_ru: '' + - name: setCompoundDrawables + args: '' + desc: '' + desc_ru: '' + - name: setCursorVisible + args: '' + desc: '' + desc_ru: '' + - name: setEllipsize + args: '' + desc: '' + desc_ru: '' + - name: setEms + args: '' + desc: '' + desc_ru: '' + - name: setError + args: '' + desc: '' + desc_ru: '' + - name: setFreezesText + args: '' + desc: '' + desc_ru: '' + - name: setGravity + args: '' + desc: '' + desc_ru: '' + - name: setHeight + args: '' + desc: '' + desc_ru: '' + - name: setHighlightColor + args: '' + desc: '' + desc_ru: '' + - name: setHint + args: '' + desc: '' + desc_ru: '' + - name: setHintTextColor + args: '' + desc: '' + desc_ru: '' + - name: setHorizontallyScrolling + args: '' + desc: '' + desc_ru: '' + - name: setImeOptions + args: '' + desc: '' + desc_ru: '' + - name: setInputType + args: '' + desc: '' + desc_ru: '' + - name: setLines + args: '' + desc: '' + desc_ru: '' + - name: setLinkTextColor + args: '' + desc: '' + desc_ru: '' + - name: setLinksClickable + args: '' + desc: '' + desc_ru: '' + - name: setMaxEms + args: '' + desc: '' + desc_ru: '' + - name: setMaxHeight + args: '' + desc: '' + desc_ru: '' + - name: setMaxLines + args: '' + desc: '' + desc_ru: '' + - name: setMaxWidth + args: '' + desc: '' + desc_ru: '' + - name: setMinEms + args: '' + desc: '' + desc_ru: '' + - name: setMinHeight + args: '' + desc: '' + desc_ru: '' + - name: setMinLines + args: '' + desc: '' + desc_ru: '' + - name: setMinWidth + args: '' + desc: '' + desc_ru: '' + - name: setPaintFlags + args: '' + desc: '' + desc_ru: '' + - name: setRawInputType + args: '' + desc: '' + desc_ru: '' + - name: setSelectAllOnFocus + args: '' + desc: '' + desc_ru: '' + - name: setSingleLine + args: '' + desc: '' + desc_ru: '' + - name: setText + args: '' + desc: '' + desc_ru: '' + - name: setTextColor + args: '' + desc: '' + desc_ru: '' + - name: setTextIsSelectable + args: '' + desc: '' + desc_ru: '' + - name: setTextScaleX + args: '' + desc: '' + desc_ru: '' + - name: setTextSize + args: '' + desc: '' + desc_ru: '' + - name: setWidth + args: '' + desc: '' + desc_ru: '' + - name: EditTextValue + desc: 'Inheritance hierarchy: TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: TextViewValue < ViewValue' + functions: + - name: extendSelection + args: '' + desc: '' + desc_ru: '' + - name: selectAll + args: '' + desc: '' + desc_ru: '' + - name: setSelection + args: '' + desc: '' + desc_ru: '' + - name: ButtonValue + desc: 'Inheritance hierarchy: TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: TextViewValue < ViewValue' + functions: [] + - name: CompoundButtonValue + desc: 'Inheritance hierarchy: ButtonValue < TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: ButtonValue < TextViewValue < ViewValue' + functions: + - name: isChecked + args: '' + desc: '' + desc_ru: '' + - name: onCheck + args: '' + desc: '' + desc_ru: '' + - name: setButtonDrawable + args: '' + desc: '' + desc_ru: '' + - name: setChecked + args: '' + desc: '' + desc_ru: '' + - name: toggle + args: '' + desc: '' + desc_ru: '' + - name: ToggleButtonValue + desc: 'Inheritance hierarchy: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + functions: + - name: getTextOff + args: '' + desc: '' + desc_ru: '' + - name: getTextOn + args: '' + desc: '' + desc_ru: '' + - name: setTextOff + args: '' + desc: '' + desc_ru: '' + - name: setTextOn + args: '' + desc: '' + desc_ru: '' + - name: SwitchValue + desc: 'Inheritance hierarchy: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + functions: + - name: getTextOff + args: '' + desc: '' + desc_ru: '' + - name: getTextOn + args: '' + desc: '' + desc_ru: '' + - name: setTextOff + args: '' + desc: '' + desc_ru: '' + - name: setTextOn + args: '' + desc: '' + desc_ru: '' + - name: ImageViewValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: clearColorFilter + args: '' + desc: '' + desc_ru: '' + - name: getScaleType + args: '' + desc: '' + desc_ru: '' + - name: setAdjustViewBounds + args: '' + desc: '' + desc_ru: '' + - name: setColorFilter + args: '' + desc: '' + desc_ru: '' + - name: setImageAlpha + args: '' + desc: '' + desc_ru: '' + - name: setImageBitmap + args: '' + desc: '' + desc_ru: '' + - name: setImageDrawable + args: '' + desc: '' + desc_ru: '' + - name: setImageLevel + args: '' + desc: '' + desc_ru: '' + - name: setImageResource + args: '' + desc: '' + desc_ru: '' + - name: setImageURI + args: '' + desc: '' + desc_ru: '' + - name: setMaxHeight + args: '' + desc: '' + desc_ru: '' + - name: setMaxWidth + args: '' + desc: '' + desc_ru: '' + - name: setScaleType + args: '' + desc: '' + desc_ru: '' + - name: ImageButtonValue + desc: 'Inheritance hierarchy: ImageViewValue < ViewValue' + desc_ru: 'Иерархия наследования: ImageViewValue < ViewValue' + functions: [] + - name: ViewGroupValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: addView + args: '' + desc: '' + desc_ru: '' + - name: bringChildToFront + args: '' + desc: '' + desc_ru: '' + - name: clearChildFocus + args: '' + desc: '' + desc_ru: '' + - name: getChildAt + args: '' + desc: '' + desc_ru: '' + - name: getChildCount + args: '' + desc: '' + desc_ru: '' + - name: indexOfChild + args: '' + desc: '' + desc_ru: '' + - name: recomputeViewAttributes + args: '' + desc: '' + desc_ru: '' + - name: removeAllViews + args: '' + desc: '' + desc_ru: '' + - name: removeAllViewsInLayout + args: '' + desc: '' + desc_ru: '' + - name: removeView + args: '' + desc: '' + desc_ru: '' + - name: removeViewAt + args: '' + desc: '' + desc_ru: '' + - name: removeViewInLayout + args: '' + desc: '' + desc_ru: '' + - name: LinearLayoutValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: + - name: getOrientation + args: '' + desc: '' + desc_ru: '' + - name: getWeightSum + args: '' + desc: '' + desc_ru: '' + - name: setGravity + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalGravity + args: '' + desc: '' + desc_ru: '' + - name: setOrientation + args: '' + desc: '' + desc_ru: '' + - name: setVerticalGravity + args: '' + desc: '' + desc_ru: '' + - name: setWeightSum + args: '' + desc: '' + desc_ru: '' + - name: RelativeLayoutValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: + - name: getGravity + args: '' + desc: '' + desc_ru: '' + - name: setGravity + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalGravity + args: '' + desc: '' + desc_ru: '' + - name: setIgnoreGravity + args: '' + desc: '' + desc_ru: '' + - name: setVerticalGravity + args: '' + desc: '' + desc_ru: '' + - name: FrameLayoutValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: [] + - name: ScrollViewValue + desc: 'Inheritance hierarchy: FrameLayoutValue < ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: FrameLayoutValue < ViewGroupValue < ViewValue' + functions: + - name: isFillViewport + args: '' + desc: '' + desc_ru: '' + - name: isSmoothScrollingEnabled + args: '' + desc: '' + desc_ru: '' + - name: setFillViewport + args: '' + desc: '' + desc_ru: '' + - name: setSmoothScrollingEnabled + args: '' + desc: '' + desc_ru: '' + - name: AdapterViewValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: + - name: getAdapter + args: '' + desc: '' + desc_ru: '' + - name: getCount + args: '' + desc: '' + desc_ru: '' + - name: getEmptyView + args: '' + desc: '' + desc_ru: '' + - name: getFirstVisiblePosition + args: '' + desc: '' + desc_ru: '' + - name: getItemAtPosition + args: '' + desc: '' + desc_ru: '' + - name: getItemIdAtPosition + args: '' + desc: '' + desc_ru: '' + - name: getLastVisiblePosition + args: '' + desc: '' + desc_ru: '' + - name: getPositionForView + args: '' + desc: '' + desc_ru: '' + - name: getSelectedItem + args: '' + desc: '' + desc_ru: '' + - name: getSelectedItemId + args: '' + desc: '' + desc_ru: '' + - name: getSelectedItemPosition + args: '' + desc: '' + desc_ru: '' + - name: getSelectedView + args: '' + desc: '' + desc_ru: '' + - name: onItemClick + args: '' + desc: '' + desc_ru: '' + - name: onItemLongClick + args: '' + desc: '' + desc_ru: '' + - name: onItemSelected + args: '' + desc: '' + desc_ru: '' + - name: performItemClick + args: '' + desc: '' + desc_ru: '' + - name: setAdapter + args: '' + desc: '' + desc_ru: '' + - name: setEmptyView + args: '' + desc: '' + desc_ru: '' + - name: ListViewValue + desc: 'Inheritance hierarchy: AdapterViewValue < ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: AdapterViewValue < ViewGroupValue < ViewValue' + functions: + - name: addFooterView + args: '' + desc: '' + desc_ru: '' + - name: addHeaderView + args: '' + desc: '' + desc_ru: '' + - name: getDividerHeight + args: '' + desc: '' + desc_ru: '' + - name: getFooterViewsCount + args: '' + desc: '' + desc_ru: '' + - name: getHeaderViewsCount + args: '' + desc: '' + desc_ru: '' + - name: getItemsCanFocus + args: '' + desc: '' + desc_ru: '' + - name: getMaxScrollAmount + args: '' + desc: '' + desc_ru: '' + - name: removeFooterView + args: '' + desc: '' + desc_ru: '' + - name: removeHeaderView + args: '' + desc: '' + desc_ru: '' + - name: setCacheColorHint + args: '' + desc: '' + desc_ru: '' + - name: setDividerHeight + args: '' + desc: '' + desc_ru: '' + - name: setFooterDividersEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHeaderDividersEnabled + args: '' + desc: '' + desc_ru: '' + - name: setItemsCanFocus + args: '' + desc: '' + desc_ru: '' + - name: setSelection + args: '' + desc: '' + desc_ru: '' + - name: setSelectionAfterHeaderView + args: '' + desc: '' + desc_ru: '' + - name: smoothScrollToPosition + args: '' + desc: '' + desc_ru: '' + - name: RadioGroupValue + desc: 'Inheritance hierarchy: LinearLayoutValue < ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: LinearLayoutValue < ViewGroupValue < ViewValue' + functions: + - name: check + args: '' + desc: '' + desc_ru: '' + - name: clearCheck + args: '' + desc: '' + desc_ru: '' + - name: getCheckedRadioButtonId + args: '' + desc: '' + desc_ru: '' + - name: onCheck + args: '' + desc: '' + desc_ru: '' + - name: setOnCheckedChangeListener + args: '' + desc: '' + desc_ru: '' + - name: ProgressBarValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: getMax + args: '' + desc: '' + desc_ru: '' + - name: getProgress + args: '' + desc: '' + desc_ru: '' + - name: getSecondaryProgress + args: '' + desc: '' + desc_ru: '' + - name: incrementProgressBy + args: '' + desc: '' + desc_ru: '' + - name: incrementSecondaryProgressBy + args: '' + desc: '' + desc_ru: '' + - name: setIndeterminate + args: '' + desc: '' + desc_ru: '' + - name: setIndeterminateDrawable + args: '' + desc: '' + desc_ru: '' + - name: setMax + args: '' + desc: '' + desc_ru: '' + - name: setProgress + args: '' + desc: '' + desc_ru: '' + - name: setProgressDrawable + args: '' + desc: '' + desc_ru: '' + - name: setSecondaryProgress + args: '' + desc: '' + desc_ru: '' + - name: SeekBarValue + desc: 'Inheritance hierarchy: ProgressBarValue < ViewValue' + desc_ru: 'Иерархия наследования: ProgressBarValue < ViewValue' + functions: + - name: getKeyProgressIncrement + args: '' + desc: '' + desc_ru: '' + - name: getThumbOffset + args: '' + desc: '' + desc_ru: '' + - name: onSeekBarChange + args: '' + desc: '' + desc_ru: '' + - name: setKeyProgressIncrement + args: '' + desc: '' + desc_ru: '' + - name: setOnSeekBarChangeListener + args: '' + desc: '' + desc_ru: '' + - name: setThumb + args: '' + desc: '' + desc_ru: '' + - name: setThumbOffset + args: '' + desc: '' + desc_ru: '' + - name: AdapterValue + functions: + - name: getCount + args: '' + desc: '' + desc_ru: '' + - name: getItem + args: '' + desc: '' + desc_ru: '' + - name: getItemId + args: '' + desc: '' + desc_ru: '' + - name: getItemViewType + args: '' + desc: '' + desc_ru: '' + - name: getView + args: '' + desc: '' + desc_ru: '' + - name: getViewTypeCount + args: '' + desc: '' + desc_ru: '' + - name: hasStableIds + args: '' + desc: '' + desc_ru: '' + - name: isEmpty + args: '' + desc: '' + desc_ru: '' + - name: ListAdapterValue + desc: 'Inheritance hierarchy: AdapterValue' + desc_ru: 'Иерархия наследования: AdapterValue' + functions: + - name: areAllItemsEnabled + args: '' + desc: '' + desc_ru: '' + - name: isEnabled + args: '' + desc: '' + desc_ru: '' + - name: imageprocessing + scope: "android" + desc: |- + Contains functions for image processing. + + You can apply effect in two ways: + + 1. Pass BitmapValue and parameters array. The result will be a BitmapValue. `bitmap = boxBlur(bitmap, [20, 40])` + 2. Pass width, height, pixels array and parameters array. The result will be an array [width, height, pixels]. `extract(width, height, pixels) = boxBlur(w, h, pixels, [20, 40])` + desc_ru: |- + Содержит функции для обработки изображений. + + Применить эффект можно двумя способами: + + 1. Передать BitmapValue и массив параметров. Результатом будет BitmapValue. `bitmap = boxBlur(bitmap, [20, 40])` + 2. Передать ширину, высоту, массив пикселей и массив параметров. Результатом будет массив [ширина, высота, пиксели]. `extract(width, height, pixels) = boxBlur(w, h, pixels, [20, 40])` + functions: + - + name: "boxBlur" + args: "horizontalBlur = 10 (min 1, max 100), verticalBlur = 10 (min 1, max 100)" + desc: "applies quick blur effect" + desc_ru: "применяет быстрый эффект размытия" + - + name: "contrast" + args: "level = 40 (min -100, max 100)" + desc: "changes contrast of the image" + desc_ru: "изменяет контрастность изображения" + - + name: "decolour" + args: "" + desc: "converts color image to grayscale" + desc_ru: "преобразует цветное изображение в оттенки серого" + - + name: "edgeDetection" + args: "operator = 1, mode = 0" + desc: |- + applies edge detection effect. + + `operator` 0 - Roberts, 1 - Prewitt, 2 - Sobel, 3 - Scharr + `mode` 0 - color edges, 1 - gray edges, 2 - subtract edges + desc_ru: |- + применяет эффект выделения границ. + + `operator` 0 - оператор Робертса, 1 - Прюитт, 2 - Собеля, 3 - Шарра + `mode` 0 - цветные грани, 1 - чёрно-белые грани, 2 - вычитание границ + - + name: "emboss" + args: "azimuth = 45 (min 0, max 360), elevation = 45 (min 0, max 90), edgeHeight = 140 (min 0, max 256), edgeThickness = 80 (min 2, max 100), emboss = 0 (min 0, max 1)" + desc: "applies emboss effect" + desc_ru: "применяет эффект выдавливания" + - + name: "extractChannel" + args: "channel = 0, monochrome = 0" + desc: |- + extracts given channel from image. + + `channel` 0 - red, 1 - green, 2 - blue + `monochrome` 0 - off, 1 - on + desc_ru: |- + извлекает заданный канал из изображения. + + `channel` 0 - красный, 1 - зелёный, 2 - синий + `monochrome` конвертировать полученную маску в чёрно-белый, 0 - нет, 1 - да + - + name: "gamma" + args: "level = 20 (min -50, max 50)" + desc: "changes gamma of the image" + desc_ru: "изменяет гамму изображения" + - + name: "hsbCorrection" + args: "hue = 45 (min 0, max 360), saturation = 0 (min -100, max 100), brightness = 0 (min -100, max 100), tone = 0 (min 0, max 1)" + desc: "changes hue, saturation and brightness of the image" + desc_ru: "изменяет оттенок, насыщенность и яркость изображения, тонирует при `tone` = 1" + - + name: "invert" + args: "invertAlpha = 0, invertRed = 1, invertGreen = 2, invertBlue = 3" + desc: "inverts channels of the image" + desc_ru: "инвертирует заданные каналы изображения" + - + name: "monochrome" + args: "level = 128 (min 0, max 255)" + desc: "converts color image to monochrome" + desc_ru: "преобразует цветное изображение в монохромное" + - + name: "mosaic" + args: "size = 4 (min 1, max 50)" + desc: "applies mosaic effect" + desc_ru: "применяет эффект мозайки" + - + name: "noiseGeneration" + args: "amount = 50 (min 0, max 255), monochrome = 0" + desc: "adds noise to images" + desc_ru: "добавляет шум к изображению" + - + name: "posterization" + args: "level = 64 (min 1, max 255)" + desc: "applies posterization effect" + desc_ru: "применяет эффект постеризации" + - + name: "rgbCorrection" + args: "alpha = 0 (min -255, max 255), red = 0 (min -255, max 255), green = 0 (min -255, max 255), blue = 0 (min -255, max 255)" + desc: "changes alpha, red, green and blue channels of the image" + desc_ru: "изменяет прозрачность, красный, зелёный, синий каналы изображения" + - + name: "rotate" + args: "angle = 45 (min 0, max 360)" + desc: "rotates image" + desc_ru: "поворачивает изображение" + - + name: "saturation" + args: "level = 64 (min -255, max 255)" + desc: "changes saturation of the image" + desc_ru: "изменяет насыщенность изображения" + - + name: "scatter" + args: "horizontalScatter = 10 (min 1, max 100), verticalScatter = 10 (min 1, max 100)" + desc: "applies pixel scatter effect" + desc_ru: "применяет эффект рассеивания пикселей" + - + name: "smooth" + args: "level = 3 (min 1, max 25)" + desc: "applies smooth effect" + desc_ru: "применяет эффект сглаживания" + - + name: "xor" + args: "level = 64 (min 0, max 255)" + desc: "applies xor operation for each pixel of the image" + desc_ru: "применяет операцию ИСКЛЮЧАЮЩЕЕ ИЛИ для каждого пикселя изображения" + - name: gps + scope: "android" + desc: |- + Contains functions for working with GPS. + desc_ru: |- + Содержит функции для работы с GPS. + constants: + - name: GPS_PROVIDER + type: 2 + typeName: string + value: gps + - name: NETWORK_PROVIDER + type: 2 + typeName: string + value: network + functions: + - + name: "isEnabled" + args: "provider" + desc: "checks if the given location service provider is enabled" + desc_ru: "проверяет доступность указанного провайдера местоположения" + - + name: "lastKnownLocation" + args: "provider" + desc: "gets last known location with the given location provider, or zero if it is unable to get location" + desc_ru: "получает последнее сохранённое местоположение для указанного провайдера, либо 0, если получить местоположение не удалось" + - + name: "getProviders" + args: "enabledOnly = false" + desc: "returns an array of location providers" + desc_ru: "возвращает массив провайдеров местоположения" + - + name: "requestUpdates" + args: "provider, minTime, minDistance, callback" + desc: |- + subscribes to the location listener + desc_ru: |- + подписывается на обработчик получения местоположения + example: |- + use ["std", "gps"] + // requestUpdates(provider, 0, 25, def(loc) = echo("location changed: ", loc)) + requestUpdates(provider, 10 * 1000, 25, { + "onLocationChanged" : def(loc) = echo("location changed: ", loc) + "onStatusChanged" : def(p, status) = echo("status changed: ", p, " is ", getStatus(status)) + "onProviderEnabled" : def(p) = echo("provider ", p, " is now enabled") + "onProviderDisabled" : def(p) = echo("provider ", p, " is now disabled") + }) From c1b3ec4c119fb6d95dd40bd4dfddad3224ff68a9 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 24 Sep 2019 21:55:12 +0300 Subject: [PATCH 260/448] =?UTF-8?q?=D0=92=D1=8B=D0=B2=D0=BE=D0=B4=20=D1=83?= =?UTF-8?q?=D0=BF=D0=B0=D0=B2=D1=88=D0=B5=D0=B9=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/annimon/ownlang/parser/ProgramsTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 0fcab099..163b95f6 100644 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -127,7 +127,11 @@ public void testOutput() throws IOException { @Override public void visit(FunctionDefineStatement s) { if (s.name.startsWith("test")) { - Functions.get(s.name).execute(); + try { + Functions.get(s.name).execute(); + } catch (AssertionError err) { + throw new AssertionError(s.name + ": " + err.getMessage(), err); + } } } }; From c82114d34a468c8bbf12e360a0cd2235cf1b8f79 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 24 Sep 2019 22:45:47 +0300 Subject: [PATCH 261/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B4=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=BE=D1=80=D1=8F=D0=B4=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B9=20?= =?UTF-8?q?=D1=83=D0=BC=D0=BD=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index b23851b4..8ec59e0a 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -698,19 +698,19 @@ private Expression multiplicative() { while (true) { if (match(TokenType.STAR)) { - result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, expression()); + result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, objectCreation()); continue; } if (match(TokenType.SLASH)) { - result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, expression()); + result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, objectCreation()); continue; } if (match(TokenType.PERCENT)) { - result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, expression()); + result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, objectCreation()); continue; } if (match(TokenType.STARSTAR)) { - result = new BinaryExpression(BinaryExpression.Operator.POWER, result, expression()); + result = new BinaryExpression(BinaryExpression.Operator.POWER, result, objectCreation()); continue; } break; From 7711170601457cb3f4f4ff9b1060fb98b4dfcf68 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 24 Sep 2019 23:07:51 +0300 Subject: [PATCH 262/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/modules/java/classes.own | 2 +- src/test/resources/modules/std/try.own | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/resources/modules/java/classes.own b/src/test/resources/modules/java/classes.own index 2ccb6adb..14ddbc6d 100644 --- a/src/test/resources/modules/java/classes.own +++ b/src/test/resources/modules/java/classes.own @@ -39,7 +39,7 @@ def testInvokeMethodSameName() { def createObject() { dataClass = newClass("interop.Data") - return dataClass.new() + return dataClass.`new`() } def assertObjectArray(obj) { diff --git a/src/test/resources/modules/std/try.own b/src/test/resources/modules/std/try.own index 8a5ba12b..bb9751f1 100644 --- a/src/test/resources/modules/std/try.own +++ b/src/test/resources/modules/std/try.own @@ -6,7 +6,7 @@ def testTryOnly() { } def testCatchFunction() { - actual = try(def() = parseInt("oops"), def(class, cause) = class) + actual = try(def() = parseInt("oops"), def(clazz, cause) = clazz) assertEquals("java.lang.NumberFormatException", actual) } From 00db00c2aaf067c3214558e4060fe8b32b9ad8f6 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 24 Sep 2019 23:08:57 +0300 Subject: [PATCH 263/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B8=D1=82=D0=B5=D1=80=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=20=D0=B2=20std::range=20=D0=B4=D0=BB=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B2=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83=D1=82=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2.=20Fix=20#3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/modules/std/std_range.java | 4 ++-- src/test/resources/modules/std/range.own | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_range.java b/src/main/java/com/annimon/ownlang/modules/std/std_range.java index 19e8c9c0..fdad6822 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_range.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_range.java @@ -134,7 +134,7 @@ public Iterator iterator() { @Override public boolean hasNext() { - return value < toInt; + return (stepInt > 0) ? (value < toInt) : (value > toInt); } @Override @@ -154,7 +154,7 @@ public void remove() { } @Override public boolean hasNext() { - return value < to; + return (step > 0) ? (value < to) : (value > to); } @Override diff --git a/src/test/resources/modules/std/range.own b/src/test/resources/modules/std/range.own index ae616dbd..85a60eda 100644 --- a/src/test/resources/modules/std/range.own +++ b/src/test/resources/modules/std/range.own @@ -28,6 +28,18 @@ def testRangeParamsReversed() { assertEquals(5, x[5]) } +def testRangeIterator() { + x = range(0, 9, 2) + arr = reduce(x, [], def(acc, e) = acc += e) + assertEquals([0, 2, 4, 6, 8], arr) +} + +def testRangeReversedIterator() { + x = range(20, 9, -2) + arr = reduce(x, [], def(acc, e) = acc += e) + assertEquals([20, 18, 16, 14, 12, 10], arr) +} + def testRangeLength() { assertEquals(10, length(range(0, 10, 1))) assertEquals(5, length(range(0, 10, 2))) From 34b8df236fa0d8bc701aed64e9a78e1a31320aa5 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 24 Sep 2019 23:44:44 +0300 Subject: [PATCH 264/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20files::readBytes=20c=20offset=20=D0=B8=20length.=20Fix?= =?UTF-8?q?=20#4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/modules/files/files.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/files/files.java b/src/main/java/com/annimon/ownlang/modules/files/files.java index 00947c4c..f65c9dd8 100644 --- a/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -301,11 +301,11 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { } final byte[] buffer = new byte[length]; - final int readed = fileInfo.dis.read(buffer, offset, length); - for (int i = 0; i < readed; i++) { - array.set(i, NumberValue.of(buffer[i])); + final int read = fileInfo.dis.read(buffer, 0, length); + for (int i = 0; i < read; i++) { + array.set(offset + i, NumberValue.of(buffer[i])); } - return NumberValue.of(readed); + return NumberValue.of(read); } } @@ -315,9 +315,9 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { final int bufferSize = 4096; final byte[] buffer = new byte[bufferSize]; final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int readed; - while ((readed = fileInfo.dis.read(buffer, 0, bufferSize)) != -1) { - baos.write(buffer, 0, readed); + int read; + while ((read = fileInfo.dis.read(buffer, 0, bufferSize)) != -1) { + baos.write(buffer, 0, read); } baos.flush(); baos.close(); @@ -389,9 +389,9 @@ private static class readText extends FileFunction { protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { final StringBuilder result = new StringBuilder(); final char[] buffer = new char[BUFFER_SIZE]; - int readed; - while ((readed = fileInfo.reader.read(buffer, 0, BUFFER_SIZE)) != -1) { - result.append(buffer, 0, readed); + int read; + while ((read = fileInfo.reader.read(buffer, 0, BUFFER_SIZE)) != -1) { + result.append(buffer, 0, read); } return new StringValue(result.toString()); } From a5c8842dad40c54406785cdc09c8224ec9d46912 Mon Sep 17 00:00:00 2001 From: Ortogo Genius <55155499+ortogo@users.noreply.github.com> Date: Fri, 27 Sep 2019 22:24:44 +0300 Subject: [PATCH 265/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=20=D0=B8=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +- docs/modules.yml | 345 +++++++++++++++++++++--------- examples.own | 167 +++++++++++++++ examples/game/minesweeper.own | 2 +- examples/network/twitch_tools.own | 4 +- 5 files changed, 427 insertions(+), 104 deletions(-) create mode 100644 examples.own diff --git a/README.md b/README.md index c6f4929b..935f8e00 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ OwnLang - dynamic functional programming language inspired by Scala and Python. | Free | Pro | Desktop | | :--: | :-: | :-----: | -| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.4.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.4.0) +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.4.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/1.4.0) Also available as AUR package: @@ -29,13 +29,14 @@ operations = { "*" : def(a,b) = a*b, "/" : ::division } + def division(v1, v2) { - if (v2 == 0) return "error" + if (v2 == 0) return "error: division by zero" return v1 / v2 } -for operation : operations { - println operation(2, 3) +for name, operation : operations { + println "2 " + name + " 3 = " + operation(2, 3) } ``` @@ -60,6 +61,9 @@ def fizzbuzz(limit = 100) { } } } + +// run +fizzbuzz() ``` #### Functional data operations @@ -111,6 +115,7 @@ use "functional" http("https://api.github.com/events", def(r) { use "json" events = jsondecode(r) + println events[0] }) // POST request diff --git a/docs/modules.yml b/docs/modules.yml index 4f82eece..43d77f55 100644 --- a/docs/modules.yml +++ b/docs/modules.yml @@ -50,17 +50,31 @@ desc_ru: возвращает значение `a`, если оно не пустое, иначе возвращается значение `b` since: 1.4.0 example: |- + use "std" + + user = {"name": "", "lastname": "Doe"} name = default(user.name, "Unknown") + lastname = default(user.lastname, "Unknown") + println name + " " + lastname // Unknown Doe example_ru: |- - name = default(user.name, "Неизвестно") + use "std" + + user = {"name": "", "lastname": "Иванов"} + name = default(user.name, "Имя неизвестно") + lastname = default(user.lastname, "фамилия неизвестна") + println name + " " + lastname // Имя неизвестно Иванов - name: echo args: "arg..." desc: "prints values to console, separate them by space and puts newline at the end. Takes variable number of arguments" desc_ru: "выводит значения в консоль, разделяя их пробелом, а потом ставит перенос строки. Может принимать переменное значение аргументов" example: |- + use "std" + echo(1, "abc") // prints "1 abc" to console echo(1, 2, 3, 4, 5, "a", "b") // prints "1 2 3 4 5 a b" example_ru: |- + use "std" + echo(1, "abc") // выведет строку "1 abc" в консоль echo(1, 2, 3, 4, 5, "a", "b") // выведет строку "1 2 3 4 5 a b" в консоль" - name: getBytes @@ -89,8 +103,10 @@ desc: "creates array with `size`.\n`newarray(x)` - creates 1D array, `newarray(x,y)` - creates 2D array" desc_ru: "оздаёт массив с размером size. Если указать 1 аргумент - создаётся одномерный массив, если 2 аргумента - двухмерный и т.д" example: |- - newarray(4) // [0, 0, 0, 0] - newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] + use "std" + + println newarray(4) // [0, 0, 0, 0] + println newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] - name: parseInt args: "str, radix" desc: parses string into integer in the `radix` @@ -120,10 +136,12 @@ `range(from, to)` - создаёт промежуток от `from` до `to` (не включительно) с шагом 1 `range(from, to, step)` - создаёт промежуток от `from` до `to` (не включительно) с шагом `step` example: |- - print range(3) // [0, 1, 2] + use "std" + + println range(3) // [0, 1, 2] r = range(-5, 0) // [-5, -4, -3, -2, -1] - print r[0] // -5 - print r[2] // -3 + println r[0] // -5 + println r[2] // -3 for x : range(20, 9, -5) { println x } // 20 15 10 @@ -157,8 +175,10 @@ desc: "splits string `str` with regular expression `regex` into array. `limit` parameter affects the length of resulting array" desc_ru: "разделяет строку `str` по шаблону `regex` и помещает элементы в массив. Если указан параметр `limit`, то будет произведено не более limit разбиений, соответственно размер результирующего массива будет ограничен этим значением limit" example: |- - split("a5b5c5d5e", "5") // ["a", "b", "c", "d", "e"] - split("a5b5c5d5e", "5", 3) // ["a", "b", "c5d5e"] + use "std" + + println split("a5b5c5d5e", "5") // ["a", "b", "c", "d", "e"] + println split("a5b5c5d5e", "5", 3) // ["a", "b", "c5d5e"] - name: sprintf args: "format, args..." desc: "formats string by arguments" @@ -174,6 +194,7 @@ since: 1.5.0 example: |- use "std" + println " |123 |456 @@ -183,16 +204,22 @@ desc: "returns string from `startIndex` to `endIndex` or to end of string if `endIndex` is not set" desc_ru: "обрезает строку `str`, начиная от символа после позиции `startIndex` и по `endIndex`. Если `endIndex` не указан, обрезается до конца строки" example: |- - substring("abcde", 1) // bcde - substring("abcde", 2, 4) // cd + use "std" + + println substring("abcde", 1) // bcde + println substring("abcde", 2, 4) // cd - name: sync args: "callback" desc: calls an asynchronous function synchronously desc_ru: делает асинхронный вызов синхронным example: |- + use ["std", "http"] + + url = "https://whatthecommit.com/index.txt" result = sync(def(ret) { http(url, def(t) = ret(t)) }) + println result - name: thread args: "func, args..." desc: "creates new thread with parameters if passed" @@ -203,15 +230,17 @@ `args` - 0 или более аргументов, которые необходимо передать в функцию func example: |- + use "std" + thread(def() { - print "New Thread" + println "New Thread" }) thread(::newthread, 10) thread("newthread", 20) def newthread(x) { - print "New Thread. x = " + x + println "New Thread. x = " + x } - name: time args: "" @@ -222,7 +251,9 @@ desc: "converts char code to string" desc_ru: "переводит код символа в строку" example: |- - toChar(48) // "0" + use "std" + + println toChar(48) // "0" - name: toHexString args: 'number' desc: 'converts number into hex string' @@ -244,15 +275,19 @@ desc: suppress any error in `unsafeFunction` and returns the result of the `catchFunction` if any error occurs desc_ru: подавляет любые ошибки в `unsafeFunction` и возрвщает результат функции `catchFunction`, если была ошибка example: |- - try(def() = "success") // success - try(def() = try + 2) // -1 - try(def() = try(), def(type, message) = sprintf("Error handled:\ntype: %s\nmessage: %s", type, message)) - try(def() = try(), 42) // 42 + use "std" + + println try(def() = "success") // success + println try(def() = try + 2) // -1 + println try(def() = try(), def(type, message) = sprintf("Error handled:\ntype: %s\nmessage: %s", type, message)) + println try(def() = try(), 42) // 42 example_ru: |- - try(def() = "успех") // успех - try(def() = try + 2) // -1 - try(def() = try(), def(type, message) = sprintf("Обработана ошибка:\nтип: %s\nсообщение: %s", type, message)) - try(def() = try(), 42) // 42 + use "std" + + println try(def() = "успех") // успех + println try(def() = try + 2) // -1 + println try(def() = try(), def(type, message) = sprintf("Обработана ошибка:\nтип: %s\nсообщение: %s", type, message)) + println try(def() = try(), 42) // 42 - name: types scope: "both" desc: "Contains functions for type checking and conversion" @@ -320,7 +355,9 @@ desc: "converts value to number if possible" desc_ru: "преобразует значение к числу, если это возможно" example: |- - print typeof(number("2.3")) // 1 (NUMBER) + use "types" + + println typeof(number("2.3")) // 1 (NUMBER) - name: "short" args: "value" @@ -332,16 +369,20 @@ desc: "converts value to string" desc_ru: "преобразует значение в строку" example: |- - print typeof(string(1)) // 2 (STRING) + use "types" + + println typeof(string(1)) // 2 (STRING) - name: "typeof" args: "value" desc: "returns the type of value" desc_ru: "возвращает тип переданного значения" example: |- - print typeof(1) // 1 (NUMBER) - print typeof("text") // 2 (STRING) - print typeof([]) // 3 (ARRAY) + use "types" + + println typeof(1) // 1 (NUMBER) + println typeof("text") // 2 (STRING) + println typeof([]) // 3 (ARRAY) - name: math scope: "both" desc: "Contains math functions and constants" @@ -394,12 +435,14 @@ desc: "returns the ceiling of `x`" desc_ru: "округляет вещественное число в большую сторону" example: |- + use "math" + ceil(6.4) // 7 - name: "copySign" args: "magnitude, sign" - desc: "" - desc_ru: "" + desc: "returns a value with the magnitude of x and the sign of y" + desc_ru: "возвращает значение с величиной x и знаком y" - name: "cos" args: "x" @@ -426,26 +469,28 @@ desc: "returns floor of `x`" desc_ru: "округляет вещественное число в меньшую сторону" example: |- + use "math" + floor(3.8) // 3 - name: "getExponent" args: "x" - desc: "" - desc_ru: "" + desc: "returns the unbiased exponent used in the representation of a double or float" + desc_ru: "возвращают несмещенное значение экспоненты числа" - name: "hypot" args: "x, y" - desc: "" + desc: "returns the square root of the sum of squares of its arguments" desc_ru: "расчёт гипотенузы sqrt(x2 + y2) без переполнения" - name: "IEEEremainder" args: "x, y" - desc: "" - desc_ru: "" + desc: "returns the remainder resulting from the division of a specified number by another specified number. This operation complies with the remainder operation defined in Section 5.1 of ANSI/IEEE Std 754-1985; IEEE Standard for Binary Floating-Point Arithmetic; Institute of Electrical and Electronics Engineers, Inc; 1985." + desc_ru: "возвращает остаток от деления x на y по стандарту ANSI/IEEE Std 754-1985, раздел 5.1" - name: "log" args: "x" - desc: "" + desc: "returns the logarithm of a specified number" desc_ru: "логарифм" - name: "log1p" @@ -455,17 +500,17 @@ - name: "log10" args: "x" - desc: "" + desc: "returns the base 10 logarithm of a specified number" desc_ru: "десятичный логарифм" - name: "max" args: "x, y" - desc: "" + desc: "returns the larger of two specified numbers" desc_ru: "максимальное из двух чисел" - name: "min" args: "x, y" - desc: "" + desc: "returns the smaller of two numbers" desc_ru: "минимальное из двух чисел" - name: "nextAfter" @@ -480,7 +525,7 @@ - name: "pow" args: "x, y" - desc: "" + desc: "returns a specified number raised to the specified power" desc_ru: "возведение x в степень y" - name: "rint" @@ -490,13 +535,13 @@ - name: "round" args: "x" - desc: "" + desc: "rounds a value to the nearest integer or to the specified number of fractional digits" desc_ru: "округляет вещественное число до ближайшего целого" - name: "signum" args: "x" - desc: "" - desc_ru: "" + desc: "returns an integer that indicates the sign of a number" + desc_ru: "возвращает целое число, указывающее знак числа" - name: "sin" args: "x" @@ -631,7 +676,9 @@ desc: formats date by given format and returns string desc_ru: форматирует дату в указанном формате и возвращает строку example: |- - d = date(2016, 4, 8) + use "date" + + d = newDate(2016, 4, 8) println formatDate(d, newFormat("yyyy/MM/dd")) // "2016/05/08" - name: "parseDate" @@ -639,6 +686,8 @@ desc: parses date from string by given pattern. Returns DateValue desc_ru: парсит дату из строки в указанном шаблоне. Возвращает DateValue example: |- + use "date" + println parseDate("2016/05/08", newFormat("yyyy/MM/dd")) - name: "toTimestamp" @@ -745,9 +794,13 @@ Возвращает дескриптор файла, который необходим для остальных функций. example: |- + use "files" + f1 = fopen("text.txt") // opens file text.txt for read in text mode f2 = fopen("E:/1.dat", "rbwb") // opens file 1.dat on drive E for binary read and write" example_ru: |- + use "files" + f1 = fopen("text.txt") // открывает файл text.txt для текстового чтения f2 = fopen("E:/1.dat", "rbwb") // открывает файл 1.dat на диске E для бинарного чтения и записи" - @@ -781,9 +834,13 @@ desc: "returns array with filenames in given directory.\n\n f - directory descriptor" desc_ru: "возвращает массив с именами файлов в указанной директории.\n\n f - дескриптор папки" example: |- + use "files" + f1 = fopen("E:/examples", "") // opens directory examples for getting information list = listFiles(f1) // gets array with filenames in directory example_ru: |- + use "files" + f1 = fopen("E:/examples", "") // открыть папку examples для получения информации list = listFiles(f1) // получить массив с именами файлов в этой папке - @@ -802,8 +859,11 @@ desc: "reads all bytes from file. Returns array with bytes" desc_ru: "чтение всех байт файла. Возвращает массив байт файла" example: |- + use ["std", "files"] + f1 = fopen("file.bin", "rb") array = readAllBytes(f1) + println length(array) - name: "readBoolean" args: "f" @@ -817,20 +877,24 @@ - name: "readBytes" args: "f, array, offset = 0, length = length(array)" - desc: "reads `length` bytes of file `f` to `array` starting from `offset`. Returns number of readed bytes" - desc_ru: "чтение заданного количества байт в массив `array`. Возвращает число прочитанных байт. \nЕсли offset и length не указаны, то читается количество байт равное длине массива. \nЕсли offset и length указаны, то пропускается offset байт и читается length байт в массив array" + desc: "reads `length` bytes of file `f` and stores to `array` starting from `offset+1` byte. Returns number of read bytes" + desc_ru: "чтение заданного количества байт в массив `array`. Возвращает число прочитанных байт. \nЕсли offset и length не указаны, то читается количество байт равное длине массива. \nЕсли offset и length указаны, то читается length байт в массив array, начиная с `offset+1` байта" example: |- - f1 = fopen("file.bin", "rb") + use "files" + + f1 = fopen("file.bin", "rb") // file.bin must contain more than 5000 bytes array = newarray(2048) - readedCount = readBytes(f1, array) // reads 2048 bytes - readedCount = readBytes(f1, array, 10) // reads 2048 bytes starting from 11 byte - readedCount = readBytes(f1, array, 20, 10) // reads 10 bytes, starting from 21 byte + readCount = readBytes(f1, array) // reads 2048 bytes + readCount = readBytes(f1, array, 10) // reads 2048 bytes starting from 11 byte + readCount = readBytes(f1, array, 20, 10) // reads 10 bytes, starting from 21 byte example_ru: |- - f1 = fopen("file.bin", "rb") + use "files" + + f1 = fopen("file.bin", "rb") // file.bin должен иметь больше 5000 байтов array = newarray(2048) - readedCount = readBytes(f1, array) // читает 2048 байт из файла - readedCount = readBytes(f1, array, 10) // читает 2048 байт, начиная с 11 байта - readedCount = readBytes(f1, array, 20, 10) // читает 10 байт, начиная с 21 байта + readCount = readBytes(f1, array) // читает 2048 байт из файла + readCount = readBytes(f1, array, 10) // читает 2048 байт, начиная с 11 байта + readCount = readBytes(f1, array, 20, 10) // читает 10 байт, начиная с 21 байта - name: "readChar" args: "f" @@ -882,6 +946,8 @@ desc: "renames (or moves) file" desc_ru: "переименование (или перемещение) файла" example: |- + use "files" + f1 = fopen("C:/file1", "i") f2 = fopen("E:/file2", "i") rename(f1, f2) @@ -1230,10 +1296,15 @@ desc: 'downloads file from `downloadUrl` to `filePath`' desc_ru: 'скачивает файл по адресу `downloadUrl` и сохраняет в `filePath`' example: |- - use "downloader" + use ["downloader", "std"] + + MBYTES = 1048576.0 // 1024*1024 + url = "http://www.ovh.net/files/10Mb.dat" + file = "10Mb.dat" + downloader(url, file, def(progress, bytesDownloaded, bytesMax) { bar = "#" * (progress / 2) - print sprintf("%-50s %d%% %.2f / %.2f\r", bar, progress, cur / 1048576.0, max / 1048576.0) + print sprintf("%-50s %d%% %.2f / %.2f MiB\r", bar, progress, bytesDownloaded / MBYTES, bytesMax / MBYTES) }) - name: base64 scope: both @@ -1467,13 +1538,29 @@ desc: "combines functions" desc_ru: "комбинирует функции (композиция)" example: |- + use "functional" + + def f1() = 2 + def f2(a) = a*2 + def f3(a) = a/4 + f = combine(::f1, ::f2, ::f3) + println f() // 1 // same as - f = def(f1, f2, f3) = f3(f2(f1)) + f = def() = f3(f2(f1())) + println f() // 1 example_ru: |- + use "functional" + + def f1() = 2 + def f2(a) = a*2 + def f3(a) = a/4 + f = combine(::f1, ::f2, ::f3) + println f() // 1 // равносильно - f = def(f1, f2, f3) = f3(f2(f1)) + f = def() = f3(f2(f1())) + println f() // 1 - name: dropwhile args: 'data, predicate' desc: 'skips elements while predicate function returns true' @@ -1483,6 +1570,8 @@ desc: "filters array or object.\n\n`predicate` is a function which takes one argument for arrays or two arguments for objects" desc_ru: "фильтрует массив или объект и возвращает массив только с теми элементами, которые удовлетворяют предикату `predicate`.\n\n`predicate` - функция которая принимает один (для массивов) и два (для объектов) аргумента" example: |- + use "functional" + nums = [1,2,3,4,5] print filter(nums, def(x) = x % 2 == 0) // [2, 4] - name: "flatmap" @@ -1490,6 +1579,8 @@ desc: "converts each element of an array to other array" desc_ru: "преобразует каждый элемент массива в массив элементов" example: |- + use "functional" + nums = [1,2,3,4] print flatmap(nums, def(x) { arr = newarray(x) @@ -1502,6 +1593,8 @@ desc: "invokes function `consumer` for each element of array or map `data`\n\nIf `data` - массив, то в функции consumer необходим один параметр, если объект - два (ключ и значение)." desc_ru: "для каждого элемента в массиве или объекте `data` вызывает функцию `consumer`\n\nЕсли `data` - массив, то в функции `consumer` необходим один параметр, если объект - два (ключ и значение)." example: |- + use "functional" + foreach([1, 2, 3], def(v) { print v }) foreach({"key": 1, "key2": "text"}, def(key, value) { print key + ": " + value @@ -1511,6 +1604,8 @@ desc: "converts elements of array or map. If `data` is array - `mapper` converts his elements, if `data` is object - you need to pass `keyMapper` - converts keys and `valueMapper` - converts values" desc_ru: "преобразует элементы массива или объекта.\n\nЕсли `data` - массив, то функция `mapper` преобразует значения, если объект - необходимо передать две функции: `keyMapper` - преобразует ключи и `valueMapper` - преобразует значения" example: |- + use "functional" + nums = [3,4,5] print map(nums, def(x) = x * x) // [9, 16, 25] - name: "reduce" @@ -1518,13 +1613,17 @@ desc: "converts elements of an array or a map to one value, e.g. sum of elements or concatenation string. `accumulator` takes one argument for array and two arguments for object (key and value)." desc_ru: "преобразует элементы массива или объекта в одно значение, например сумма элементов или объединение в строку.\n\nЕсли `data` - массив, то в функции `accumulator` необходим один параметр, если объект - два (ключ и значение)" example: |- + use "functional" + nums = [1,2,3,4,5] - print reduce(nums, 0, def(x, y) = x + x) // 15 + print reduce(nums, 0, def(x, y) = x + y) // 15 - name: "sortby" args: "array, function" desc: "sorts elements of an array or an object by `function` result" desc_ru: "сортирует элементы массива по данным в функции `function`" example: |- + use "functional" + data = [ {"k1": 2, "k2": "x"}, {"k1": 7, "k2": "d"}, @@ -1646,8 +1745,12 @@ desc: "performs click with given mouse buttons" desc_ru: "осуществляет клик мышью с заданными клавишами" example: |- + use "robot" + click(BUTTON3) // right mouse button click example_ru: |- + use "robot" + click(BUTTON3) // клик правой кнопкой мыши - name: "delay" @@ -1661,6 +1764,8 @@ desc: "executes the process with parameters" desc_ru: "запускает процесс с параметрами\n\n Если функции переданы несколько аргументов, то они все передаются как параметры.\n Если функции передан только один параметр - массив, то его элементы передаются как параметры.\n Если функции передан только один параметр, то он служит единственным параметром." example: |- + use "robot" + execProcess("mkdir", "Test") execProcess("mkdir Test") execProcess(["mkdir", "Test"]) @@ -1809,97 +1914,114 @@ typeName: number type: 1 value: "40" + desc: "arrow down key code" + desc_ru: "код клавиши стрелка вниз" - name: "VK_ESCAPE" typeName: number type: 1 value: "27" + desc: "Esc key code" + desc_ru: "код клавиши Esc" - name: "VK_FIRE" typeName: number type: 1 value: "10" + desc: "Enter key code" + desc_ru: "код клавиши Enter" - name: "VK_LEFT" typeName: number type: 1 value: "37" + desc: "arrow left key code" + desc_ru: "код клавиши стрелка влево" - name: "VK_RIGHT" typeName: number type: 1 value: "39" + desc: "arrow left key code" + desc_ru: "код клавиши стрелка вправо" - name: "VK_UP" typeName: number type: 1 value: "38" + desc: "arrow up key code" + desc_ru: "код клавиши стрелка вверх" functions: - name: "clip" - args: "" - desc: "" - desc_ru: "" + args: "x, y, w, h" + desc: "sets the current clip to the rectangle specified by the given coordinates" + desc_ru: "устанавливает текущий клип в прямоугольник, заданный данными координатами" - name: "color" - args: "" - desc: "" - desc_ru: "" + args: "rgb" + desc: "sets color drawing. `rgb` - color with the specified combined RGB value" + desc_ru: "устанвливает цвет рисования. `rgb` - целое, комбинация цветов RGB, например `#FFGGFF`" + - + name: "color" + args: "red, green, blue" + desc: "sets color with the specified red, green, and blue values in the range (0 - 255)" + desc_ru: "устанвливает цвет рисования c отдельными уровнями красного, зеленого и синего в диапазоне (0 - 255)" - name: "drawstring" - args: "" - desc: "" - desc_ru: "" + args: "text, x, y" + desc: "draws string `text` at position `x`, `y`" + desc_ru: "рисует строку `text` с координатами `x`, `y`" - name: "foval" - args: "" - desc: "" - desc_ru: "" + args: "x, y, w, h" + desc: "draws a filled oval at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует закрашенный овал на позиции `x`, `y`, размером `w`, `h`" - name: "frect" - args: "" - desc: "" - desc_ru: "" + args: "x, y, w, h" + desc: "draws a filled rectangle at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует закрашенный прямоугольник на позиции `x`, `y`, размером `w`, `h`" - name: "keypressed" args: "" - desc: "" - desc_ru: "" + desc: "returns the code of the pressed key (see the constant section)" + desc_ru: "возрвращает код нажатой клавиши (см. раздел константы)" - name: "line" - args: "" - desc: "" - desc_ru: "" + args: "x1, y1, x2, y2" + desc: "draws line from point (`x1`;y1`) to (`x2`;y2`)" + desc_ru: "рисует линию от позиции (`x1`;y1`) до (`x2`;y2`)" - name: "mousehover" args: "" - desc: "" - desc_ru: "" + desc: "returns array with current mouse pointer coordinates" + desc_ru: "возвращает массив с текущими координатами указателя мыши" - name: "oval" - args: "" - desc: "" - desc_ru: "" + args: "x, y, w, h" + desc: "draws a oval at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует овал на позиции `x`, `y`, размером `w`, `h`" - name: "prompt" - args: "" - desc: "" - desc_ru: "" + args: "message" + desc: "displays a dialog box that prompts the visitor for input" + desc_ru: "показывает диалог для ввода значения от пользователя" - name: "rect" - args: "" - desc: "" - desc_ru: "" + args: "x, y, w, h" + desc: "draws a rectangle at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует прямоугольник на позиции `x`, `y`, размером `w`, `h`" - name: "repaint" args: "" - desc: "" - desc_ru: "" + desc: "draws elements from graphics buffer on canvas" + desc_ru: "прорисовывает элементы из буфера на холсте" - name: "window" - args: "" - desc: "" - desc_ru: "" + args: "name, width, hight" + desc: "creates a new window with the specified `name` and size `width`x`height`" + desc_ru: "создает новое окно с именем `name` и размером `width`x`height`" - name: canvasfx scope: "desktop" desc: "Contains functions for working with Java FX graphics" @@ -2067,8 +2189,10 @@ Возвращает ImageFXValue. example: |- use "canvasfx" + g = showcanvas() - bitmap = createImage("http://lorempixel.com/image_output/nature-q-c-640-480-10.jpg") + url = "http://lorempixel.com/640/480/nature" + bitmap = createImage(url) g.drawBitmap(bitmap, 0, 0) bitmap = createImage("file:image.png") g.drawBitmap(bitmap, 200, 0) @@ -3734,10 +3858,13 @@ Возвращает BitmapValue. example: |- use ["http", "canvas"] + g = showcanvas() - imageBytes = download("http://lorempixel.com/image_output/nature-q-c-640-480-10.jpg") + url = "http://lorempixel.com/640/480/nature" + imageBytes = download(url) bitmap = createBitmap(imageBytes) g.drawBitmap(bitmap, 0, 0) + repaint() - name: "createScaledBitmap" args: "srcBitmap, width, height, filter" @@ -4418,6 +4545,11 @@ desc: '' desc_ru: '' example: |- + use ["std", "android", "forms"] + + img1 = assetBitmap("ownlang.png") + img2 = img1 + items = [ {"img" : img1, "text" : "Item 1"}, {"img" : img2, "text" : "Item 2"} @@ -4438,12 +4570,24 @@ } else { extract(imageView, textView) = view.getTag() } - + imageView.setImageBitmap(items[pos].img); - textView.setText(toHexString(items[pos].text)); + textView.setText(items[pos].text); return view } }); + + listView = newListView() + listView.setAdapter(adapter) + listView.onItemClick(def(v, pos, id) { + toast(adapter.getItem(pos).text + " selected") + }) + + panel = newLinearLayout() + panel.addView(newTextView("ListView with BaseAdapter demo")) + panel.addView(listView) + + showForm(panel) - name: newButton args: 'text = ""' desc: 'creates Button' @@ -4487,6 +4631,11 @@ pb1.setProgress(10) pb2 = newProgressBar() pb2.setIndeterminate(true) + + panel = newLinearLayout() + panel.addView(pb1) + panel.addView(pb2) + showForm(panel) - name: newRadioButton args: '' desc: 'creates RadioButton' @@ -6528,6 +6677,8 @@ подписывается на обработчик получения местоположения example: |- use ["std", "gps"] + + provider = "gps" // or passive, network if exists // requestUpdates(provider, 0, 25, def(loc) = echo("location changed: ", loc)) requestUpdates(provider, 10 * 1000, 25, { "onLocationChanged" : def(loc) = echo("location changed: ", loc) diff --git a/examples.own b/examples.own new file mode 100644 index 00000000..ee107ddc --- /dev/null +++ b/examples.own @@ -0,0 +1,167 @@ +/* + * + * Automatic run examples for testing. + * Have functions for special launch own scripts if need + * + */ + +use ["date", "files", "robot", "std"] + +DEBUG = true +EXAMPLES_DIR = "examples" +REPORT_PATH = "F:/report.txt" +EXEC_TEMPLATE = "cmd /U /C \"ownlang -f %s %s >> %s 2>&1\"" + +// Main list of examples. Contains predefined examples with custom executing params +listExamples = { + /* template + "program_name": { + "isRun": false, + "path": "" // relative path to program + "args": [], // additional args for run + "prelaunch": "", // pre-launch other application, e.g. start server + }, + */ + "fx_basic_shapes.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "fx_event_handlers.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "fx_global_alpha.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "fx_image_negate.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "fx_image.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "fx_koch_snowflake.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "fx_rotation.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "okhttp_telegram_sendvoice.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "telegram_api.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "okhttp_imgur_upload.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "okhttp_websocket.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, + "pipes_online.own": { + "isRun": false, + "path": "" + "args": [], + "prelaunch": "", + }, +} + +def debug(something) { + if !DEBUG return 0 + + println sprintf("[%s] %s", newDate(), something) +} + +// Algorithm: get list of examples, filter and add to main list of examples +def readExamples() { + examplesDir = fopen(EXAMPLES_DIR, "") + dirs = listFiles(examplesDir) + + for dir : dirs { + relativeDirPath = EXAMPLES_DIR + "/" + dir + subDir = fopen(relativeDirPath, "") + if (!isDirectory(subDir) || dir == "." || dir == "..") continue + files = listFiles(subDir) + for file : files { + if (indexOf(file, ".own") < 0 || dir == "." || dir == "..") { + debug(file + "not ownlang application or sub directory") + continue + } + if (arrayKeyExists(file, listExamples)) { + debug(file + " exists in main list") + continue + } + program = { + "isRun": true, + "path": relativeDirPath + "/" + file + "args": [], + "prelaunch": "", + } + listExamples[file] = program + } + } + return listExamples +} + +readExamples() + +// remove old report +if exists(REPORT_PATH) { + delete(REPORT_PATH) +} + +// main task +for name, program : listExamples { + if !program.isRun { + println "Skip: " + name + continue + } + + println "Executing: " + name + + reportBeforeExec = sprintf("cmd /U /C \"echo %s\n >> %s", program.path, REPORT_PATH) + execProcessAndWait(reportBeforeExec) + + if length(trim(program.prelaunch)) > 0 { + println "Pre-launch: " + program.pre-launch + execProcessAndWait(program.pre-launch) + } + + execString = sprintf(EXEC_TEMPLATE, program.path, join(program.args, " "), REPORT_PATH) + debug(execString) + exitCode = execProcessAndWait(execString) + println "Exit code: " + exitCode + + reportAfterExec = sprintf("cmd /U /C \"echo %s\n >> %s", "*"*19, REPORT_PATH) + execProcessAndWait(reportAfterExec) +} \ No newline at end of file diff --git a/examples/game/minesweeper.own b/examples/game/minesweeper.own index 2f76ee6b..1b99cf1b 100644 --- a/examples/game/minesweeper.own +++ b/examples/game/minesweeper.own @@ -59,7 +59,7 @@ def drawGameTable(showBombs = false) { case CELL_MINE if !showBombs: g.setFill(DEFAULT_CELL_COLOR) case _ : g.setFill(OPENED_CELL_COLOR) } - if (FLAGS[j][i] && (TABLE[j][i] == CELL_NONE || TABLE[j][i] == CELL_MINE) { + if FLAGS[j][i] && (TABLE[j][i] == CELL_NONE || TABLE[j][i] == CELL_MINE) { g.setFill(FLAG_COLOR) } g.fillRect(i * gridStepX + 1, j * gridStepY + 1, gridStepX - 2, gridStepY - 2) diff --git a/examples/network/twitch_tools.own b/examples/network/twitch_tools.own index f5308703..71b87b92 100644 --- a/examples/network/twitch_tools.own +++ b/examples/network/twitch_tools.own @@ -80,7 +80,7 @@ def listPastBroadcasts(channel) { for vod : pastVods { println "=" * 30 println vod._id - if (arrayKeyExists("title", vod) { + if arrayKeyExists("title", vod) { println vod.title } println "Date: " + formatTzDate(vod.recorded_at) @@ -89,7 +89,7 @@ def listPastBroadcasts(channel) { println "Views: " + vod.views println "Previews:" println vod.preview - if (arrayKeyExists("animated_preview", vod) { + if arrayKeyExists("animated_preview", vod) { println vod.animated_preview } for quality : ["chunked", "high", "medium", "low", "mobile"] { From 483c7c1242fc8f53f073239fac575c0c1ff1cb4b Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 3 Oct 2019 20:36:38 +0300 Subject: [PATCH 266/448] =?UTF-8?q?=D0=9F=D0=BE=D0=B8=D1=81=D0=BA=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D1=85=D0=BE=D0=B4=D1=8F=D1=89=D0=B5=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=20=D0=B2=20java::new?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/modules/java/java.java | 28 +++++++++++++------ src/test/resources/modules/java/classes.own | 6 ++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index 953ae2cb..c8f78229 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -156,17 +157,13 @@ private Value asSubclass(Value... args) { return new ClassValue(clazz.asSubclass( ((ClassValue)args[0]).clazz )); } - private Value isAssignableFrom(Value... args) { + private Value isAssignableFrom(Value[] args) { Arguments.check(1, args.length); return NumberValue.fromBoolean(clazz.isAssignableFrom( ((ClassValue)args[0]).clazz )); } - private Value newInstance(Value... args) { - try { - return new ObjectValue(clazz.newInstance()); - } catch (InstantiationException | IllegalAccessException ex) { - return NULL; - } + private Value newInstance(Value[] args) { + return findConstructorAndInstantiate(args, clazz.getConstructors()); } private Value cast(Value... args) { @@ -293,6 +290,21 @@ private static Value getValue(Class clazz, Object object, String key) { return NULL; } + + private static Value findConstructorAndInstantiate(Value[] args, Constructor[] ctors) { + for (Constructor ctor : ctors) { + if (ctor.getParameterCount() != args.length) continue; + if (!isMatch(args, ctor.getParameterTypes())) continue; + try { + final Object result = ctor.newInstance(valuesToObjects(args)); + return new ObjectValue(result); + } catch (InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException ex) { + // skip + } + } + return null; + } private static Function methodsToFunction(Object object, List methods) { return (args) -> { @@ -312,7 +324,7 @@ private static Function methodsToFunction(Object object, List methods) { return null; }; } - + private static boolean isMatch(Value[] args, Class[] types) { for (int i = 0; i < args.length; i++) { final Value arg = args[i]; diff --git a/src/test/resources/modules/java/classes.own b/src/test/resources/modules/java/classes.own index 14ddbc6d..bfd12ce3 100644 --- a/src/test/resources/modules/java/classes.own +++ b/src/test/resources/modules/java/classes.own @@ -36,6 +36,12 @@ def testInvokeMethodSameName() { assertEquals("text", data.getText()) } +def testNonDefaultConstructor() { + StringBuilder = newClass("java.lang.StringBuilder") + sb = StringBuilder.`new`("text") + assertEquals("text", sb.toString()) +} + def createObject() { dataClass = newClass("interop.Data") From 826891ac52bc254ba91b132ba321eed971a44caa Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 3 Oct 2019 21:04:17 +0300 Subject: [PATCH 267/448] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=B8=D0=BD=D1=81=D1=82=D0=B0=D0=BD=D1=86?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20java-=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81=D0=B0=20=D1=81=20=D0=BF=D0=BE=D0=BC?= =?UTF-8?q?=D0=BE=D1=89=D1=8C=D1=8E=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=B2?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=20new?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/lib/ClassDeclarations.java | 10 ++-------- .../com/annimon/ownlang/lib/Instantiable.java | 9 +++++++++ .../annimon/ownlang/modules/java/java.java | 5 +++-- .../parser/ast/ObjectCreationExpression.java | 20 ++++++++++++++++--- 4 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/Instantiable.java diff --git a/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java b/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java index 1335b1b2..b079ef3d 100644 --- a/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java +++ b/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java @@ -1,15 +1,14 @@ package com.annimon.ownlang.lib; -import com.annimon.ownlang.exceptions.UnknownFunctionException; import com.annimon.ownlang.parser.ast.ClassDeclarationStatement; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public final class ClassDeclarations { private static final Map declarations; static { - declarations = new HashMap<>(); + declarations = new ConcurrentHashMap<>(); } private ClassDeclarations() { } @@ -22,12 +21,7 @@ public static Map getAll() { return declarations; } - public static boolean isExists(String key) { - return declarations.containsKey(key); - } - public static ClassDeclarationStatement get(String key) { - if (!isExists(key)) throw new UnknownFunctionException(key); return declarations.get(key); } diff --git a/src/main/java/com/annimon/ownlang/lib/Instantiable.java b/src/main/java/com/annimon/ownlang/lib/Instantiable.java new file mode 100644 index 00000000..709b4c79 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/Instantiable.java @@ -0,0 +1,9 @@ +package com.annimon.ownlang.lib; + +/** + * Interface for values that supports creating instances with `new` keyword. + */ +public interface Instantiable { + + Value newInstance(Value[] args); +} diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index c8f78229..58ef5fcf 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -103,7 +103,7 @@ public String toString() { } } - private static class ClassValue extends MapValue { + private static class ClassValue extends MapValue implements Instantiable { public static Value classOrNull(Class clazz) { if (clazz == null) return NULL; @@ -162,7 +162,8 @@ private Value isAssignableFrom(Value[] args) { return NumberValue.fromBoolean(clazz.isAssignableFrom( ((ClassValue)args[0]).clazz )); } - private Value newInstance(Value[] args) { + @Override + public Value newInstance(Value[] args) { return findConstructorAndInstantiate(args, clazz.getConstructors()); } diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index e5edc279..6e15ad03 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.UnknownClassException; import com.annimon.ownlang.lib.*; import java.util.Iterator; import java.util.List; @@ -17,6 +18,16 @@ public ObjectCreationExpression(String className, List constructorAr @Override public Value eval() { final ClassDeclarationStatement cd = ClassDeclarations.get(className); + if (cd == null) { + // Is Instantiable? + if (Variables.isExists(className)) { + final Value variable = Variables.get(className); + if (variable instanceof Instantiable) { + return ((Instantiable) variable).newInstance(ctorArgs()); + } + } + throw new UnknownClassException(className); + } // Create an instance and put evaluated fields with method declarations final ClassInstanceValue instance = new ClassInstanceValue(className); @@ -30,14 +41,17 @@ public Value eval() { } // Call a constructor + instance.callConstructor(ctorArgs()); + return instance; + } + + private Value[] ctorArgs() { final int argsSize = constructorArguments.size(); final Value[] ctorArgs = new Value[argsSize]; for (int i = 0; i < argsSize; i++) { ctorArgs[i] = constructorArguments.get(i).eval(); } - instance.callConstructor(ctorArgs); - - return instance; + return ctorArgs; } @Override From f03cbb6fc741aa92fbec8d877d152270a4ba32c5 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Oct 2019 00:21:28 +0300 Subject: [PATCH 268/448] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=B0=D1=8F=20=D1=82=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B2=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=20java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/modules/java/java.java | 82 +++++++++++++++++-- src/test/resources/modules/java/classes.own | 2 +- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index 58ef5fcf..890f89b3 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -8,6 +8,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -167,7 +168,7 @@ public Value newInstance(Value[] args) { return findConstructorAndInstantiate(args, clazz.getConstructors()); } - private Value cast(Value... args) { + private Value cast(Value[] args) { Arguments.check(1, args.length); return objectToValue(clazz, clazz.cast(((ObjectValue)args[0]).object)); } @@ -337,7 +338,15 @@ private static boolean isMatch(Value[] args, Class[] types) { boolean assignable = unboxed != null; final Object object = valueToObject(arg); assignable = assignable && (object != null); - assignable = assignable && (unboxed.isAssignableFrom(object.getClass())); + if (assignable && unboxed.isArray() && object.getClass().isArray()) { + final Class uComponentType = unboxed.getComponentType(); + final Class oComponentType = object.getClass().getComponentType(); + assignable = assignable && (uComponentType != null); + assignable = assignable && (oComponentType != null); + assignable = assignable && (uComponentType.isAssignableFrom(oComponentType)); + } else { + assignable = assignable && (unboxed.isAssignableFrom(object.getClass())); + } if (assignable) continue; return false; @@ -460,7 +469,7 @@ private static Value arrayToValue(Class clazz, Object o) { } return result; } - + private static Object[] valuesToObjects(Value[] args) { Object[] result = new Object[args.length]; for (int i = 0; i < args.length; i++) { @@ -490,11 +499,72 @@ private static Object valueToObject(Value value) { private static Object arrayToObject(ArrayValue value) { final int size = value.size(); - final Object[] result = new Object[size]; + final Object[] array = new Object[size]; + if (size == 0) { + return array; + } + + Class elementsType = null; for (int i = 0; i < size; i++) { - result[i] = valueToObject(value.get(i)); + array[i] = valueToObject(value.get(i)); + if (i == 0) { + elementsType = array[0].getClass(); + } else { + elementsType = mostCommonType(elementsType, array[i].getClass()); + } + } + + if (elementsType.equals(Object[].class)) { + return array; + } + return typedArray(array, size, elementsType); + } + + private static T[] typedArray(U[] elements, int newLength, Class elementsType) { + @SuppressWarnings("unchecked") + T[] copy = (T[]) Array.newInstance(elementsType, newLength); + System.arraycopy(elements, 0, copy, 0, Math.min(elements.length, newLength)); + return copy; + } + + private static Class mostCommonType(Class c1, Class c2) { + if (c1.equals(c2)) { + return c1; + } else if (c1.isAssignableFrom(c2)) { + return c1; + } else if (c2.isAssignableFrom(c1)) { + return c2; + } + final Class s1 = c1.getSuperclass(); + final Class s2 = c2.getSuperclass(); + if (s1 == null && s2 == null) { + final List> upperTypes = Arrays.asList( + Object.class, void.class, boolean.class, char.class, + byte.class, short.class, int.class, long.class, + float.class, double.class); + for (Class type : upperTypes) { + if (c1.equals(type) && c2.equals(type)) { + return s1; + } + } + return Object.class; + } else if (s1 == null || s2 == null) { + if (c1.equals(c2)) { + return c1; + } + if (c1.isInterface() && c1.isAssignableFrom(c2)) { + return c1; + } + if (c2.isInterface() && c2.isAssignableFrom(c1)) { + return c2; + } + } + + if (s1 != null) { + return mostCommonType(s1, c2); + } else { + return mostCommonType(c1, s2); } - return result; } // } diff --git a/src/test/resources/modules/java/classes.own b/src/test/resources/modules/java/classes.own index bfd12ce3..0aaecbfa 100644 --- a/src/test/resources/modules/java/classes.own +++ b/src/test/resources/modules/java/classes.own @@ -38,7 +38,7 @@ def testInvokeMethodSameName() { def testNonDefaultConstructor() { StringBuilder = newClass("java.lang.StringBuilder") - sb = StringBuilder.`new`("text") + sb = new StringBuilder("text") assertEquals("text", sb.toString()) } From 1cfe654e926d49989b5a5bbeb1cf6b46df9491e4 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 4 Oct 2019 22:16:29 +0300 Subject: [PATCH 269/448] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D0=B5=D0=B5=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D0=B2=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5=20java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annimon/ownlang/modules/java/java.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index 890f89b3..398c90ed 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -153,7 +153,7 @@ private void init(Class clazz) { set("cast", new FunctionValue(this::cast)); } - private Value asSubclass(Value... args) { + private Value asSubclass(Value[] args) { Arguments.check(1, args.length); return new ClassValue(clazz.asSubclass( ((ClassValue)args[0]).clazz )); } @@ -208,7 +208,7 @@ public ObjectValue(Object object) { @Override public boolean containsKey(Value key) { - return getValue(object.getClass(), object, key.asString()) != null; + return get(key) != null; } @Override @@ -228,7 +228,7 @@ public String toString() { } // - private Value isNull(Value... args) { + private Value isNull(Value[] args) { Arguments.checkAtLeast(1, args.length); for (Value arg : args) { if (arg.raw() == null) return NumberValue.ONE; @@ -236,24 +236,24 @@ private Value isNull(Value... args) { return NumberValue.ZERO; } - private Value newClass(Value... args) { + private Value newClass(Value[] args) { Arguments.check(1, args.length); final String className = args[0].asString(); try { return new ClassValue(Class.forName(className)); } catch (ClassNotFoundException ce) { - return NULL; + throw new RuntimeException("Class " + className + " not found.", ce); } } - private Value toObject(Value... args) { + private Value toObject(Value[] args) { Arguments.check(1, args.length); if (args[0] == NULL) return NULL; return new ObjectValue(valueToObject(args[0])); } - private Value toValue(Value... args) { + private Value toValue(Value[] args) { Arguments.check(1, args.length); if (args[0] instanceof ObjectValue) { return objectToValue( ((ObjectValue) args[0]).object ); @@ -305,7 +305,8 @@ private static Value findConstructorAndInstantiate(Value[] args, Constructor[ // skip } } - return null; + throw new RuntimeException("Constructor for " + args.length + " arguments" + + " not found or non accessible"); } private static Function methodsToFunction(Object object, List methods) { @@ -323,7 +324,9 @@ private static Function methodsToFunction(Object object, List methods) { // skip } } - return null; + final String className = (object == null ? "null" : object.getClass().getName()); + throw new RuntimeException("Method for " + args.length + " arguments" + + " not found or non accessible in " + className); }; } From 7775b264cbaecfb7960cf91030c81c93bc39324e Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 6 Oct 2019 20:18:23 +0300 Subject: [PATCH 270/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20JButton=20=D0=B8=20JTextField?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/forms/AbstractButtonValue.java | 60 +++++++++++++++++++ .../ownlang/modules/forms/ComponentValue.java | 8 +-- .../ownlang/modules/forms/JButtonValue.java | 25 ++------ .../modules/forms/JComponentValue.java | 14 ++++- .../modules/forms/JTextComponentValue.java | 38 ++++++++++++ .../modules/forms/JTextFieldValue.java | 14 ++--- 6 files changed, 124 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java b/src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java new file mode 100644 index 00000000..64f4ed44 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java @@ -0,0 +1,60 @@ +package com.annimon.ownlang.modules.forms; + +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +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 javax.swing.AbstractButton; + +public class AbstractButtonValue extends JComponentValue { + + final AbstractButton abstractButton; + + public AbstractButtonValue(int functionsCount, AbstractButton abstractButton) { + super(functionsCount + 2, abstractButton); + this.abstractButton = abstractButton; + init(); + } + + private void init() { + set("onClick", new FunctionValue(this::addActionListener)); + set("addActionListener", new FunctionValue(this::addActionListener)); + set("onChange", new FunctionValue(this::addChangeListener)); + set("addChangeListener", new FunctionValue(this::addChangeListener)); + set("doClick", intOptToVoid(abstractButton::doClick, abstractButton::doClick)); + set("getActionCommand", voidToString(abstractButton::getActionCommand)); + set("getDisplayedMnemonicIndex", voidToInt(abstractButton::getDisplayedMnemonicIndex)); + set("getHideActionText", voidToBoolean(abstractButton::getHideActionText)); + set("getHorizontalAlignment", voidToInt(abstractButton::getHorizontalAlignment)); + set("getHorizontalTextPosition", voidToInt(abstractButton::getHorizontalTextPosition)); + set("getIconTextGap", voidToInt(abstractButton::getIconTextGap)); + set("getText", voidToString(abstractButton::getText)); + set("getVerticalAlignment", voidToInt(abstractButton::getVerticalAlignment)); + set("getVerticalTextPosition", voidToInt(abstractButton::getVerticalTextPosition)); + set("isSelected", voidToBoolean(abstractButton::isSelected)); + set("setActionCommand", stringToVoid(abstractButton::setActionCommand)); + set("setHorizontalAlignment", intToVoid(abstractButton::setHorizontalAlignment)); + set("setHorizontalTextPosition", intToVoid(abstractButton::setHorizontalTextPosition)); + set("setSelected", booleanToVoid(abstractButton::setSelected)); + set("setText", stringToVoid(abstractButton::setText)); + set("setVerticalAlignment", intToVoid(abstractButton::setVerticalAlignment)); + set("setVerticalTextPosition", intToVoid(abstractButton::setVerticalTextPosition)); + } + + private Value addActionListener(Value[] args) { + Arguments.check(1, args.length); + final Function action = ValueUtils.consumeFunction(args[0], 0); + abstractButton.addActionListener(e -> action.execute()); + return NumberValue.ZERO; + } + + private Value addChangeListener(Value[] args) { + Arguments.check(1, args.length); + final Function action = ValueUtils.consumeFunction(args[0], 0); + abstractButton.addChangeListener(e -> action.execute()); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java index 15a0de2d..04afe22a 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java @@ -77,7 +77,7 @@ private void init() { set("validate", voidToVoid(component::validate)); } - private Value addKeyListener(Value... args) { + private Value addKeyListener(Value[] args) { Arguments.check(1, args.length); final Function action = ValueUtils.consumeFunction(args[0], 0); component.addKeyListener(new KeyListener() { @@ -118,7 +118,7 @@ private void handleKeyEvent(String type, final KeyEvent e) { return NumberValue.ZERO; } - private Value getLocation(Value... args) { + private Value getLocation(Value[] args) { final Point location = component.getLocation(); final ArrayValue result = new ArrayValue(2); result.set(0, NumberValue.of(location.x)); @@ -126,7 +126,7 @@ private Value getLocation(Value... args) { return result; } - private Value getLocationOnScreen(Value... args) { + private Value getLocationOnScreen(Value[] args) { final Point location = component.getLocationOnScreen(); final ArrayValue result = new ArrayValue(2); result.set(0, NumberValue.of(location.x)); @@ -134,7 +134,7 @@ private Value getLocationOnScreen(Value... args) { return result; } - private Value setLocation(Value... args) { + private Value setLocation(Value[] args) { Arguments.check(2, args.length); component.setLocation(args[0].asInt(), args[1].asInt()); return NumberValue.ZERO; diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java index 87766fb5..24939b4c 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java @@ -1,32 +1,17 @@ package com.annimon.ownlang.modules.forms; -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 javax.swing.JButton; -public class JButtonValue extends JComponentValue { +public class JButtonValue extends AbstractButtonValue { - final JButton button; + final JButton jButton; - public JButtonValue(JButton button) { - super(2, button); - this.button = button; + public JButtonValue(JButton jButton) { + super(0, jButton); + this.jButton = jButton; init(); } private void init() { - set("onClick", new FunctionValue(this::addActionListener)); - set("addActionListener", new FunctionValue(this::addActionListener)); - } - - private Value addActionListener(Value... args) { - Arguments.check(1, args.length); - final Function action = ValueUtils.consumeFunction(args[0], 0); - button.addActionListener(e -> action.execute()); - return NumberValue.ZERO; } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java index 63b5829f..cd8561a7 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java @@ -8,12 +8,24 @@ public abstract class JComponentValue extends ContainerValue { final JComponent jComponent; public JComponentValue(int functionsCount, JComponent jComponent) { - super(functionsCount + 2, jComponent); + super(functionsCount + 14, jComponent); this.jComponent = jComponent; init(); } private void init() { + set("getAutoscrolls", voidToBoolean(jComponent::getAutoscrolls)); + set("setAutoscrolls", booleanToVoid(jComponent::setAutoscrolls)); + set("isDoubleBuffered", voidToBoolean(jComponent::isDoubleBuffered)); + set("setDoubleBuffered", booleanToVoid(jComponent::setDoubleBuffered)); + set("getInheritsPopupMenu", voidToBoolean(jComponent::getInheritsPopupMenu)); + set("setInheritsPopupMenu", booleanToVoid(jComponent::setInheritsPopupMenu)); + set("isOpaque", voidToBoolean(jComponent::isOpaque)); + set("setOpaque", booleanToVoid(jComponent::setOpaque)); + set("isRequestFocusEnabled", voidToBoolean(jComponent::isRequestFocusEnabled)); + set("setRequestFocusEnabled", booleanToVoid(jComponent::setRequestFocusEnabled)); + set("getVerifyInputWhenFocusTarget", voidToBoolean(jComponent::getVerifyInputWhenFocusTarget)); + set("setVerifyInputWhenFocusTarget", booleanToVoid(jComponent::setVerifyInputWhenFocusTarget)); set("getToolTipText", voidToString(jComponent::getToolTipText)); set("setToolTipText", stringToVoid(jComponent::setToolTipText)); } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java new file mode 100644 index 00000000..d314701e --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java @@ -0,0 +1,38 @@ +package com.annimon.ownlang.modules.forms; + +import static com.annimon.ownlang.lib.Converters.*; +import javax.swing.text.JTextComponent; + +public class JTextComponentValue extends JComponentValue { + + private final JTextComponent textComponent; + + public JTextComponentValue(int functionsCount, JTextComponent textComponent) { + super(functionsCount + 20, textComponent); + this.textComponent = textComponent; + init(); + } + + private void init() { + set("copy", voidToVoid(textComponent::copy)); + set("cut", voidToVoid(textComponent::cut)); + set("getCaretPosition", voidToInt(textComponent::getCaretPosition)); + set("getDragEnabled", voidToBoolean(textComponent::getDragEnabled)); + set("getSelectedText", voidToString(textComponent::getSelectedText)); + set("getSelectionStart", voidToInt(textComponent::getSelectionStart)); + set("getSelectionEnd", voidToInt(textComponent::getSelectionEnd)); + set("getText", voidToString(textComponent::getText)); + set("isEditable", voidToBoolean(textComponent::isEditable)); + set("moveCaretPosition", intToVoid(textComponent::moveCaretPosition)); + set("paste", voidToVoid(textComponent::paste)); + set("replaceSelection", stringToVoid(textComponent::replaceSelection)); + set("select", int2ToVoid(textComponent::select)); + set("selectAll", voidToVoid(textComponent::selectAll)); + set("setCaretPosition", intToVoid(textComponent::setCaretPosition)); + set("setDragEnabled", booleanToVoid(textComponent::setDragEnabled)); + set("setEditable", booleanToVoid(textComponent::setEditable)); + set("setSelectionStart", intToVoid(textComponent::setSelectionStart)); + set("setSelectionEnd", intToVoid(textComponent::setSelectionEnd)); + set("setText", stringToVoid(textComponent::setText)); + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java index 841b92a7..b43f65b9 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java @@ -9,12 +9,12 @@ import javax.swing.JTextField; import static com.annimon.ownlang.lib.ValueUtils.consumeFunction; -public class JTextFieldValue extends JComponentValue { +public class JTextFieldValue extends JTextComponentValue { private final JTextField textField; public JTextFieldValue(JTextField textField) { - super(16, textField); + super(10, textField); this.textField = textField; init(); } @@ -22,20 +22,14 @@ public JTextFieldValue(JTextField textField) { private void init() { set("onAction", new FunctionValue(this::addActionListener)); set("addActionListener", new FunctionValue(this::addActionListener)); - set("getCaretPosition", voidToInt(textField::getCaretPosition)); set("getColumns", voidToInt(textField::getColumns)); set("getHorizontalAlignment", voidToInt(textField::getHorizontalAlignment)); - set("getSelectionEnd", voidToInt(textField::getSelectionEnd)); - set("getSelectionStart", voidToInt(textField::getSelectionStart)); set("getScrollOffset", voidToInt(textField::getScrollOffset)); - set("getText", voidToString(textField::getText)); - set("setCaretPosition", intToVoid(textField::setCaretPosition)); + set("postActionEvent", voidToVoid(textField::postActionEvent)); + set("setActionCommand", stringToVoid(textField::setActionCommand)); set("setColumns", intToVoid(textField::setColumns)); set("setHorizontalAlignment", intToVoid(textField::setHorizontalAlignment)); set("setScrollOffset", intToVoid(textField::setScrollOffset)); - set("setSelectionEnd", intToVoid(textField::setSelectionEnd)); - set("setSelectionStart", intToVoid(textField::setSelectionStart)); - set("setText", stringToVoid(textField::setText)); } private Value addActionListener(Value... args) { From 090f3a5a704347cd986d2b221c3c46298b080828 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 6 Oct 2019 20:19:15 +0300 Subject: [PATCH 271/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20JProgressBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/forms/progressbar.own | 29 ++++++++++ .../com/annimon/ownlang/lib/Converters.java | 14 ++++- .../ownlang/modules/forms/Components.java | 53 ++++++++++++++++--- .../modules/forms/JProgressBarValue.java | 51 ++++++++++++++++++ .../ownlang/modules/forms/LayoutManagers.java | 10 ++-- .../annimon/ownlang/modules/forms/forms.java | 1 + 6 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 examples/forms/progressbar.own create mode 100644 src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java diff --git a/examples/forms/progressbar.own b/examples/forms/progressbar.own new file mode 100644 index 00000000..261587fd --- /dev/null +++ b/examples/forms/progressbar.own @@ -0,0 +1,29 @@ +use "forms" + +label = newLabel("Current value: 50") +progressBar = newProgressBar() +progressBar.setValue(50) +progressBar.onChange(def() { + label.setText("Current value: " + progressBar.getValue()) +}) +minusBtn = newButton("-1") +minusBtn.onClick(def() = changeProgress(-1)) +plusBtn = newButton("+1") +plusBtn.onClick(def() = changeProgress(1)) + +def changeProgress(delta) { + value = progressBar.getValue() + delta + if (value > 100) value = 100 + else if (value < 0) value = 0 + progressBar.setValue(value) +} + +window = newWindow("ProgressBar example") +window.add(minusBtn, BorderLayout.WEST) +window.add(progressBar, BorderLayout.CENTER) +window.add(plusBtn, BorderLayout.EAST) +window.add(label, BorderLayout.SOUTH) +window.pack() +window.setLocationByPlatform() +window.setResizable(false) +window.setVisible() \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/src/main/java/com/annimon/ownlang/lib/Converters.java index 3c144352..9415df00 100644 --- a/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -158,6 +158,18 @@ public static FunctionValue intToVoid(IntToVoidFunction f) { return NumberValue.ZERO; }); } + + public static FunctionValue intOptToVoid(VoidToVoidFunction f1, IntToVoidFunction f2) { + return new FunctionValue(args -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + f1.apply(); + } else { + f2.apply(args[0].asInt()); + } + return NumberValue.ZERO; + }); + } public static FunctionValue intToLong(IntToLongFunction f) { return new FunctionValue(args -> { @@ -168,7 +180,7 @@ public static FunctionValue intToLong(IntToLongFunction f) { public static FunctionValue int2ToVoid(Int2ToVoidFunction f) { return new FunctionValue(args -> { - Arguments.check(4, args.length); + Arguments.check(2, args.length); f.apply(args[0].asInt(), args[1].asInt()); return NumberValue.ZERO; diff --git a/src/main/java/com/annimon/ownlang/modules/forms/Components.java b/src/main/java/com/annimon/ownlang/modules/forms/Components.java index f283e5a4..9e2e7d83 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/Components.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/Components.java @@ -6,6 +6,8 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingConstants; @@ -16,7 +18,7 @@ public final class Components { private Components() { } - static Value newWindow(Value... args) { + static Value newWindow(Value[] args) { Arguments.checkOrOr(0, 1, args.length); String title = (args.length == 1) ? args[0].asString() : ""; final JFrame frame = new JFrame(title); @@ -24,7 +26,7 @@ static Value newWindow(Value... args) { return new JFrameValue(frame); } - static Value newPanel(Value... args) { + static Value newPanel(Value[] args) { Arguments.checkOrOr(0, 1, args.length); final JPanel panel = new JPanel(); if (args.length == 1) { @@ -33,22 +35,57 @@ static Value newPanel(Value... args) { return new JPanelValue(panel); } - static Value newButton(Value... args) { + static Value newButton(Value[] args) { Arguments.checkOrOr(0, 1, args.length); String text = (args.length == 1) ? args[0].asString() : ""; return new JButtonValue(new JButton(text)); } - static Value newLabel(Value... args) { + static Value newLabel(Value[] args) { Arguments.checkRange(0, 2, args.length); String text = (args.length >= 1) ? args[0].asString() : ""; int align = (args.length == 2) ? args[1].asInt() : SwingConstants.LEADING; return new JLabelValue(new JLabel(text, align)); } - static Value newTextField(Value... args) { - Arguments.checkOrOr(0, 1, args.length); - String text = (args.length == 1) ? args[0].asString() : ""; - return new JTextFieldValue(new JTextField(text)); + static Value newTextField(Value[] args) { + Arguments.checkRange(0, 2, args.length); + String text = ""; + int cols = 0; + switch (args.length) { + case 1: { + text = args[0].asString(); + } break; + case 2: { + text = args[0].asString(); + cols = args[1].asInt(); + } break; + } + return new JTextFieldValue(new JTextField(text, cols)); + } + + static Value newProgressBar(Value[] args) { + Arguments.checkRange(0, 3, args.length); + boolean isVertical = false; + int min = 0; + int max = 100; + switch (args.length) { + case 1: { + isVertical = args[0].asInt() != 0; + } break; + case 2: { + min = args[0].asInt(); + max = args[1].asInt(); + } break; + case 3: { + isVertical = args[0].asInt() != 0; + min = args[1].asInt(); + max = args[2].asInt(); + } break; + } + return new JProgressBarValue(new JProgressBar( + isVertical ? SwingConstants.VERTICAL : SwingConstants.HORIZONTAL, + min, max + )); } } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java new file mode 100644 index 00000000..5417e5eb --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java @@ -0,0 +1,51 @@ +package com.annimon.ownlang.modules.forms; + +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +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 javax.swing.JProgressBar; + +public class JProgressBarValue extends JComponentValue { + + final JProgressBar progressBar; + + public JProgressBarValue(JProgressBar progressBar) { + super(19, progressBar); + this.progressBar = progressBar; + init(); + } + + private void init() { + set("onChange", new FunctionValue(this::addChangeListener)); + set("addChangeListener", new FunctionValue(this::addChangeListener)); + set("getMinimum", voidToInt(progressBar::getMinimum)); + set("getMaximum", voidToInt(progressBar::getMaximum)); + set("getOrientation", voidToInt(progressBar::getOrientation)); + set("getValue", voidToInt(progressBar::getValue)); + set("isBorderPainted", voidToBoolean(progressBar::isBorderPainted)); + set("isIndeterminate", voidToBoolean(progressBar::isIndeterminate)); + set("isStringPainted", voidToBoolean(progressBar::isStringPainted)); + set("getString", voidToString(progressBar::getString)); + set("getPercentComplete", voidToDouble(progressBar::getPercentComplete)); + + set("setMinimum", intToVoid(progressBar::setMinimum)); + set("setMaximum", intToVoid(progressBar::setMaximum)); + set("setBorderPainted", booleanToVoid(progressBar::setBorderPainted)); + set("setIndeterminate", booleanToVoid(progressBar::setIndeterminate)); + set("setOrientation", intToVoid(progressBar::setOrientation)); + set("setStringPainted", booleanToVoid(progressBar::setStringPainted)); + set("setString", stringToVoid(progressBar::setString)); + set("setValue", intToVoid(progressBar::setValue)); + } + + private Value addChangeListener(Value[] args) { + Arguments.check(1, args.length); + final Function action = ValueUtils.consumeFunction(args[0], 0); + progressBar.addChangeListener(e -> action.execute()); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java index 45558f29..262e168c 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java @@ -15,7 +15,7 @@ public final class LayoutManagers { private LayoutManagers() { } - static Value borderLayout(Value... args) { + static Value borderLayout(Value[] args) { Arguments.checkOrOr(0, 2, args.length); int hgap = (args.length == 2) ? args[0].asInt() : 0; int vgap = (args.length == 2) ? args[1].asInt() : 0; @@ -24,7 +24,7 @@ static Value borderLayout(Value... args) { ); } - static Value boxLayout(Value... args) { + static Value boxLayout(Value[] args) { Arguments.checkOrOr(1, 2, args.length); int axis = (args.length == 2) ? args[1].asInt() : BoxLayout.PAGE_AXIS; return new LayoutManagerValue( @@ -32,7 +32,7 @@ static Value boxLayout(Value... args) { ); } - static Value cardLayout(Value... args) { + static Value cardLayout(Value[] args) { Arguments.checkOrOr(0, 2, args.length); int hgap = (args.length == 2) ? args[0].asInt() : 0; int vgap = (args.length == 2) ? args[1].asInt() : 0; @@ -41,7 +41,7 @@ static Value cardLayout(Value... args) { ); } - static Value gridLayout(Value... args) { + static Value gridLayout(Value[] args) { Arguments.checkRange(0, 4, args.length); int rows = 1, cols = 0, hgap = 0, vgap = 0; switch (args.length) { @@ -69,7 +69,7 @@ static Value gridLayout(Value... args) { ); } - static Value flowLayout(Value... args) { + static Value flowLayout(Value[] args) { Arguments.checkRange(0, 3, args.length); final int align, hgap, vgap; switch (args.length) { diff --git a/src/main/java/com/annimon/ownlang/modules/forms/forms.java b/src/main/java/com/annimon/ownlang/modules/forms/forms.java index 477f7ff2..af761ab4 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/forms.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/forms.java @@ -75,6 +75,7 @@ public void init() { Functions.set("newButton", Components::newButton); Functions.set("newLabel", Components::newLabel); Functions.set("newPanel", Components::newPanel); + Functions.set("newProgressBar", Components::newProgressBar); Functions.set("newTextField", Components::newTextField); Functions.set("newWindow", Components::newWindow); From f46ed638bfb60423a8e355328a1544cf4578005f Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 8 Oct 2019 21:55:44 +0300 Subject: [PATCH 272/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20JTextArea=20=D0=B8=20JScrollPane?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/forms/textarea.own | 25 ++++++++ .../ownlang/modules/forms/Components.java | 48 ++++++++++++++ .../modules/forms/JScrollPaneValue.java | 56 +++++++++++++++++ .../ownlang/modules/forms/JTextAreaValue.java | 62 +++++++++++++++++++ .../modules/forms/JTextComponentValue.java | 56 ++++++++++++++++- .../annimon/ownlang/modules/forms/forms.java | 28 +++++++++ 6 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 examples/forms/textarea.own create mode 100644 src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java create mode 100644 src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java diff --git a/examples/forms/textarea.own b/examples/forms/textarea.own new file mode 100644 index 00000000..1d70efb0 --- /dev/null +++ b/examples/forms/textarea.own @@ -0,0 +1,25 @@ +use ["std", "forms", "functional"] + +text = join(map(range(1, 16), def(x) = "line " + x), "\n") +label = newLabel() +textArea = newTextArea(text) +textArea.addCaretListener(def(event) = updateInfo()) +textArea.addDocumentListener(def(type, event) = updateInfo()) +updateInfo() + +def updateInfo() { + text = "Text length: " + textArea.getText().length + text += ", lines: " + textArea.getLineCount() + selectedText = default(textArea.getSelectedText(), "") + if (!selectedText.isEmpty()) { + text += ", selected: " + selectedText.length + } + label.setText(text) +} + +window = newWindow("JTextArea example") +window.add(newScrollPane(textArea), BorderLayout.CENTER) +window.add(label, BorderLayout.SOUTH) +window.setSize(300, 200) +window.setLocationByPlatform() +window.setVisible() \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/Components.java b/src/main/java/com/annimon/ownlang/modules/forms/Components.java index 9e2e7d83..4e075b95 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/Components.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/Components.java @@ -2,11 +2,13 @@ import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Value; +import java.awt.Component; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; +import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingConstants; @@ -64,6 +66,28 @@ static Value newTextField(Value[] args) { return new JTextFieldValue(new JTextField(text, cols)); } + static Value newTextArea(Value[] args) { + Arguments.checkRange(0, 3, args.length); + String text = ""; + int rows = 0; + int cols = 0; + switch (args.length) { + case 1: { + text = args[0].asString(); + } break; + case 2: { + rows = args[0].asInt(); + cols = args[1].asInt(); + } break; + case 3: { + text = args[0].asString(); + rows = args[1].asInt(); + cols = args[2].asInt(); + } break; + } + return new JTextAreaValue(new JTextArea(text, rows, cols)); + } + static Value newProgressBar(Value[] args) { Arguments.checkRange(0, 3, args.length); boolean isVertical = false; @@ -88,4 +112,28 @@ static Value newProgressBar(Value[] args) { min, max )); } + + static Value newScrollPane(Value[] args) { + Arguments.checkRange(0, 3, args.length); + Component view = null; + int vsbPolicy = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED; + int hsbPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED; + switch (args.length) { + case 1: { + view = ((ComponentValue) args[0]).component; + } break; + case 2: { + vsbPolicy = args[0].asInt(); + hsbPolicy = args[1].asInt(); + } break; + case 3: { + view = ((ComponentValue) args[0]).component; + vsbPolicy = args[1].asInt(); + hsbPolicy = args[2].asInt(); + } break; + } + return new JScrollPaneValue(new JScrollPane( + view, vsbPolicy, hsbPolicy + )); + } } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java new file mode 100644 index 00000000..13d4a00c --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.modules.forms; + +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; +import javax.swing.JScrollPane; + +public class JScrollPaneValue extends JComponentValue { + + final JScrollPane scrollPane; + + public JScrollPaneValue(JScrollPane scrollPane) { + super(10, scrollPane); + this.scrollPane = scrollPane; + init(); + } + + private void init() { + set("getHorizontalScrollBarPolicy", voidToInt(scrollPane::getHorizontalScrollBarPolicy)); + set("getVerticalScrollBarPolicy", voidToInt(scrollPane::getVerticalScrollBarPolicy)); + set("isWheelScrollingEnabled", voidToBoolean(scrollPane::isWheelScrollingEnabled)); + set("setColumnHeaderView", new FunctionValue(this::setColumnHeaderView)); + set("setCorner", new FunctionValue(this::setCorner)); + set("setHorizontalScrollBarPolicy", intToVoid(scrollPane::setHorizontalScrollBarPolicy)); + set("setRowHeaderView", new FunctionValue(this::setRowHeaderView)); + set("setVerticalScrollBarPolicy", intToVoid(scrollPane::setVerticalScrollBarPolicy)); + set("setViewportView", new FunctionValue(this::setViewportView)); + set("setWheelScrollingEnabled", booleanToVoid(scrollPane::setWheelScrollingEnabled)); + } + + private Value setViewportView(Value[] args) { + Arguments.check(1, args.length); + scrollPane.setViewportView(((ComponentValue) args[0]).component); + return NumberValue.ZERO; + } + + private Value setRowHeaderView(Value[] args) { + Arguments.check(1, args.length); + scrollPane.setRowHeaderView(((ComponentValue) args[0]).component); + return NumberValue.ZERO; + } + + private Value setColumnHeaderView(Value[] args) { + Arguments.check(1, args.length); + scrollPane.setColumnHeaderView(((ComponentValue) args[0]).component); + return NumberValue.ZERO; + } + + private Value setCorner(Value[] args) { + Arguments.check(2, args.length); + scrollPane.setCorner(args[0].asString(), ((ComponentValue) args[1]).component); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java new file mode 100644 index 00000000..083ac4b6 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.modules.forms; + +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; +import javax.swing.JTextArea; +import javax.swing.text.BadLocationException; + +public class JTextAreaValue extends JTextComponentValue { + + private final JTextArea textArea; + + public JTextAreaValue(JTextArea textArea) { + super(18, textArea); + this.textArea = textArea; + init(); + } + + private void init() { + set("append", stringToVoid(textArea::append)); + set("getColumns", voidToInt(textArea::getColumns)); + set("getLineCount", voidToInt(textArea::getLineCount)); + set("getLineStartOffset", offsetFunction(textArea::getLineStartOffset)); + set("getLineEndOffset", offsetFunction(textArea::getLineEndOffset)); + set("getLineOfOffset", offsetFunction(textArea::getLineOfOffset)); + set("getLineWrap", voidToBoolean(textArea::getLineWrap)); + set("getWrapStyleWord", voidToBoolean(textArea::getWrapStyleWord)); + set("getRows", voidToInt(textArea::getRows)); + set("getColumns", voidToInt(textArea::getColumns)); + set("getTabSize", voidToInt(textArea::getTabSize)); + set("insert", this::insert); + set("setRows", intToVoid(textArea::setRows)); + set("setColumns", intToVoid(textArea::setColumns)); + set("setTabSize", intToVoid(textArea::setTabSize)); + set("setLineWrap", booleanToVoid(textArea::setLineWrap)); + set("setWrapStyleWord", booleanToVoid(textArea::setWrapStyleWord)); + } + + private Value insert(Value[] args) { + Arguments.check(2, args.length); + textArea.insert(args[0].asString(), args[1].asInt()); + return NumberValue.ZERO; + } + + private interface OffsetFunction { + int accept(int line) throws BadLocationException; + } + + private FunctionValue offsetFunction(OffsetFunction f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + try { + int result = f.accept(args[0].asInt()); + return NumberValue.of(result); + } catch (BadLocationException ex) { + throw new RuntimeException(ex); + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java index d314701e..858fe548 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java @@ -1,6 +1,16 @@ package com.annimon.ownlang.modules.forms; +import com.annimon.ownlang.lib.Arguments; import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import javax.swing.event.CaretEvent; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.text.JTextComponent; public class JTextComponentValue extends JComponentValue { @@ -8,12 +18,14 @@ public class JTextComponentValue extends JComponentValue { private final JTextComponent textComponent; public JTextComponentValue(int functionsCount, JTextComponent textComponent) { - super(functionsCount + 20, textComponent); + super(functionsCount + 21, textComponent); this.textComponent = textComponent; init(); } private void init() { + set("addCaretListener", this::addCaretListener); + set("addDocumentListener", this::addDocumentListener); set("copy", voidToVoid(textComponent::copy)); set("cut", voidToVoid(textComponent::cut)); set("getCaretPosition", voidToInt(textComponent::getCaretPosition)); @@ -35,4 +47,46 @@ private void init() { set("setSelectionEnd", intToVoid(textComponent::setSelectionEnd)); set("setText", stringToVoid(textComponent::setText)); } + + private Value addCaretListener(Value[] args) { + Arguments.check(1, args.length); + final Function action = ValueUtils.consumeFunction(args[0], 0); + textComponent.addCaretListener((CaretEvent e) -> { + final MapValue map = new MapValue(2); + map.set("getDot", NumberValue.of(e.getDot())); + map.set("getMark", NumberValue.of(e.getMark())); + action.execute(map); + }); + return NumberValue.ZERO; + } + + private Value addDocumentListener(Value[] args) { + Arguments.check(1, args.length); + final Function action = ValueUtils.consumeFunction(args[0], 0); + textComponent.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + handleDocumentEvent(DocumentEvent.EventType.INSERT, e); + } + + @Override + public void removeUpdate(DocumentEvent e) { + handleDocumentEvent(DocumentEvent.EventType.REMOVE, e); + } + + @Override + public void changedUpdate(DocumentEvent e) { + handleDocumentEvent(DocumentEvent.EventType.CHANGE, e); + } + + private void handleDocumentEvent(DocumentEvent.EventType type, final DocumentEvent e) { + final MapValue map = new MapValue(3); + map.set("getLength", NumberValue.of(e.getLength())); + map.set("getOffset", NumberValue.of(e.getOffset())); + map.set("getType", new StringValue(e.getType().toString())); + action.execute(new StringValue(type.toString()), map); + } + }); + return NumberValue.ZERO; + } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/forms.java b/src/main/java/com/annimon/ownlang/modules/forms/forms.java index af761ab4..2f17298c 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/forms.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/forms.java @@ -5,6 +5,7 @@ import java.awt.BorderLayout; import javax.swing.BoxLayout; import javax.swing.JFrame; +import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; /** @@ -59,6 +60,31 @@ public static void initConstants() { border.set("SOUTH", new StringValue(BorderLayout.SOUTH)); border.set("WEST", new StringValue(BorderLayout.WEST)); Variables.define("BorderLayout", border); + + // ScrollPane constants + final MapValue scrollpane = new MapValue(13); + scrollpane.set("COLUMN_HEADER", new StringValue(ScrollPaneConstants.COLUMN_HEADER)); + scrollpane.set("HORIZONTAL_SCROLLBAR", new StringValue(ScrollPaneConstants.HORIZONTAL_SCROLLBAR)); + scrollpane.set("HORIZONTAL_SCROLLBAR_POLICY", new StringValue(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_POLICY)); + scrollpane.set("LOWER_LEADING_CORNER", new StringValue(ScrollPaneConstants.LOWER_LEADING_CORNER)); + scrollpane.set("LOWER_LEFT_CORNER", new StringValue(ScrollPaneConstants.LOWER_LEFT_CORNER)); + scrollpane.set("LOWER_RIGHT_CORNER", new StringValue(ScrollPaneConstants.LOWER_RIGHT_CORNER)); + scrollpane.set("LOWER_TRAILING_CORNER", new StringValue(ScrollPaneConstants.LOWER_TRAILING_CORNER)); + scrollpane.set("ROW_HEADER", new StringValue(ScrollPaneConstants.ROW_HEADER)); + scrollpane.set("UPPER_LEADING_CORNER", new StringValue(ScrollPaneConstants.UPPER_LEADING_CORNER)); + scrollpane.set("UPPER_LEFT_CORNER", new StringValue(ScrollPaneConstants.UPPER_LEFT_CORNER)); + scrollpane.set("UPPER_RIGHT_CORNER", new StringValue(ScrollPaneConstants.UPPER_RIGHT_CORNER)); + scrollpane.set("UPPER_TRAILING_CORNER", new StringValue(ScrollPaneConstants.UPPER_TRAILING_CORNER)); + scrollpane.set("VERTICAL_SCROLLBAR", new StringValue(ScrollPaneConstants.VERTICAL_SCROLLBAR)); + scrollpane.set("VERTICAL_SCROLLBAR_POLICY", new StringValue(ScrollPaneConstants.VERTICAL_SCROLLBAR_POLICY)); + scrollpane.set("VIEWPORT", new StringValue(ScrollPaneConstants.VIEWPORT)); + scrollpane.set("HORIZONTAL_SCROLLBAR_ALWAYS", NumberValue.of(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS)); + scrollpane.set("HORIZONTAL_SCROLLBAR_AS_NEEDED", NumberValue.of(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED)); + scrollpane.set("HORIZONTAL_SCROLLBAR_NEVER", NumberValue.of(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER)); + scrollpane.set("VERTICAL_SCROLLBAR_ALWAYS", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS)); + scrollpane.set("VERTICAL_SCROLLBAR_AS_NEEDED", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED)); + scrollpane.set("VERTICAL_SCROLLBAR_NEVER", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER)); + Variables.define("ScrollPaneConstants", scrollpane); final MapValue box = new MapValue(4); box.set("LINE_AXIS", NumberValue.of(BoxLayout.LINE_AXIS)); @@ -76,6 +102,8 @@ public void init() { Functions.set("newLabel", Components::newLabel); Functions.set("newPanel", Components::newPanel); Functions.set("newProgressBar", Components::newProgressBar); + Functions.set("newScrollPane", Components::newScrollPane); + Functions.set("newTextArea", Components::newTextArea); Functions.set("newTextField", Components::newTextField); Functions.set("newWindow", Components::newWindow); From 4185fd5baa463a29a63c1f37d68e7f4f2784b0cb Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 8 Oct 2019 22:24:31 +0300 Subject: [PATCH 273/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20joinToString=20=D0=B4=D0=BB=D1=8F=20=D0=BC=D0=B0=D1=81=D1=81?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=20=D0=B8=20joining=20=D0=B4=D0=BB=D1=8F=20st?= =?UTF-8?q?ream?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/forms/textarea.own | 2 +- .../com/annimon/ownlang/lib/ArrayValue.java | 32 ++++++++++++++++++- .../modules/functional/functional_stream.java | 13 ++++---- .../annimon/ownlang/modules/std/std_join.java | 20 +++--------- .../resources/modules/functional/stream.own | 7 ++++ src/test/resources/other/arrayFunctions.own | 8 ++++- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/examples/forms/textarea.own b/examples/forms/textarea.own index 1d70efb0..f659ff62 100644 --- a/examples/forms/textarea.own +++ b/examples/forms/textarea.own @@ -1,6 +1,6 @@ use ["std", "forms", "functional"] -text = join(map(range(1, 16), def(x) = "line " + x), "\n") +text = map(range(1, 16), def(x) = "line " + x).joinToString("\n") label = newLabel() textArea = newTextArea(text) textArea.addCaretListener(def(event) = updateInfo()) diff --git a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java index d8935a11..bc824fb1 100644 --- a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java +++ b/src/main/java/com/annimon/ownlang/lib/ArrayValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; import java.util.Arrays; import java.util.Iterator; @@ -48,6 +49,17 @@ public static ArrayValue merge(ArrayValue array1, ArrayValue array2) { return result; } + public static StringValue joinToString(ArrayValue array, String delimiter, String prefix, String suffix) { + final StringBuilder sb = new StringBuilder(); + for (Value value : array) { + if (sb.length() > 0) sb.append(delimiter); + else sb.append(prefix); + sb.append(value.asString()); + } + sb.append(suffix); + return new StringValue(sb.toString()); + } + private final Value[] elements; public ArrayValue(int size) { @@ -96,12 +108,30 @@ public Value get(Value index) { // Functions case "isEmpty": - return NumberValue.fromBoolean(size() == 0); + return Converters.voidToBoolean(() -> size() == 0); + case "joinToString": + return new FunctionValue(this::joinToString); default: return get(index.asInt()); } } + + public Value joinToString(Value[] args) { + Arguments.checkRange(0, 3, args.length); + switch (args.length) { + case 0: + return joinToString(this, "", "", ""); + case 1: + return joinToString(this, args[0].asString(), "", ""); + case 2: + return joinToString(this, args[0].asString(), args[1].asString(), args[1].asString()); + case 3: + return joinToString(this, args[0].asString(), args[1].asString(), args[2].asString()); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } public void set(int index, Value value) { elements[index] = value; 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 05d5948b..db6e8ca6 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 @@ -8,7 +8,7 @@ public final class functional_stream implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); if (args.length > 1) { @@ -52,10 +52,11 @@ private void init() { set("reduce", wrapTerminal(new functional_reduce())); set("forEach", wrapTerminal(new functional_foreach())); set("toArray", args -> container); + set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); } - private Value skip(Value... args) { + private Value skip(Value[] args) { Arguments.check(1, args.length); final int skipCount = args[0].asInt(); @@ -71,7 +72,7 @@ private Value skip(Value... args) { return new StreamValue(new ArrayValue(result)); } - private Value limit(Value... args) { + private Value limit(Value[] args) { Arguments.check(1, args.length); final int limitCount = args[0].asInt(); @@ -87,7 +88,7 @@ private Value limit(Value... args) { return new StreamValue(new ArrayValue(result)); } - private Value sorted(Value... args) { + private Value sorted(Value[] args) { Arguments.checkOrOr(0, 1, args.length); final Value[] elements = container.getCopyElements(); @@ -106,7 +107,7 @@ private Value sorted(Value... args) { return new StreamValue(new ArrayValue(elements)); } - private Value custom(Value... args) { + private Value custom(Value[] args) { Arguments.check(1, args.length); final Function f = ValueUtils.consumeFunction(args[0], 0); final Value result = f.execute(container); @@ -115,7 +116,7 @@ private Value custom(Value... args) { } return result; } - + private FunctionValue wrapIntermediate(Function f) { return wrap(f, true); } diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_join.java b/src/main/java/com/annimon/ownlang/modules/std/std_join.java index 6bf417e4..3696fb14 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_join.java +++ b/src/main/java/com/annimon/ownlang/modules/std/std_join.java @@ -5,7 +5,6 @@ import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; @@ -21,26 +20,15 @@ public Value execute(Value... args) { final ArrayValue array = (ArrayValue) args[0]; switch (args.length) { case 1: - return join(array, "", "", ""); + return ArrayValue.joinToString(array, "", "", ""); case 2: - return join(array, args[1].asString(), "", ""); + return ArrayValue.joinToString(array, args[1].asString(), "", ""); case 3: - return join(array, args[1].asString(), args[2].asString(), args[2].asString()); + return ArrayValue.joinToString(array, args[1].asString(), args[2].asString(), args[2].asString()); case 4: - return join(array, args[1].asString(), args[2].asString(), args[3].asString()); + return ArrayValue.joinToString(array, args[1].asString(), args[2].asString(), args[3].asString()); default: throw new ArgumentsMismatchException("Wrong number of arguments"); } } - - private static StringValue join(ArrayValue array, String delimiter, String prefix, String suffix) { - final StringBuilder sb = new StringBuilder(); - for (Value value : array) { - if (sb.length() > 0) sb.append(delimiter); - else sb.append(prefix); - sb.append(value.asString()); - } - sb.append(suffix); - return new StringValue(sb.toString()); - } } diff --git a/src/test/resources/modules/functional/stream.own b/src/test/resources/modules/functional/stream.own index 62681437..b9478b0f 100644 --- a/src/test/resources/modules/functional/stream.own +++ b/src/test/resources/modules/functional/stream.own @@ -43,6 +43,13 @@ def testCustom() { assertEquals([5,6,4,2], stream(data).custom(::reverse).toArray()) } +def testJoining() { + data = [1,2,3,4] + assertEquals("1234", stream(data).joining()) + assertEquals("1-2-3-4", stream(data).joining("-")) + assertEquals("<1-2-3-4>", stream(data).joining("-", "<", ">")) +} + def testPeek() { data = [2,3,4,5,6,7] expected = [2,4,6] diff --git a/src/test/resources/other/arrayFunctions.own b/src/test/resources/other/arrayFunctions.own index da25e6a8..c16c1edb 100644 --- a/src/test/resources/other/arrayFunctions.own +++ b/src/test/resources/other/arrayFunctions.own @@ -22,5 +22,11 @@ def testGetLengthInnerArray() { def testIsEmpty() { arr = [1, 2, 3] - assertFalse(arr.isEmpty) + assertFalse(arr.isEmpty()) +} + +def testJoinToString() { + arr = [1, 2, 3] + assertEquals("123", arr.joinToString()) + assertEquals("1 2 3", arr.joinToString(" ")) } \ No newline at end of file From 69e1a562fc13eaaa9d5d579f3ed98cbdbb536578 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 8 Oct 2019 23:20:43 +0300 Subject: [PATCH 274/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20WindowListener?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/modules/forms/ContainerValue.java | 1 + .../ownlang/modules/forms/JFrameValue.java | 7 +- .../ownlang/modules/forms/WindowValue.java | 98 +++++++++++++++++++ .../annimon/ownlang/modules/forms/forms.java | 16 +++ 4 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java b/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java index 0ccb0716..5f909737 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java @@ -29,6 +29,7 @@ private void init() { set("getComponentCount", voidToInt(container::getComponentCount)); set("isFocusCycleRoot", voidToBoolean(container::isFocusCycleRoot)); set("isValidateRoot", voidToBoolean(container::isValidateRoot)); + set("setFocusCycleRoot", booleanToVoid(container::setFocusCycleRoot)); set("setLayout", new FunctionValue(this::setLayout)); } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java b/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java index bb547d17..40f2cb34 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java @@ -3,7 +3,7 @@ import static com.annimon.ownlang.lib.Converters.*; import javax.swing.JFrame; -public class JFrameValue extends ContainerValue { +public class JFrameValue extends WindowValue { final JFrame frame; @@ -14,13 +14,10 @@ public JFrameValue(JFrame frame) { } private void init() { - set("dispose", voidToVoid(frame::dispose)); set("getTitle", voidToString(frame::getTitle)); + set("getResizable", voidToBoolean(frame::isResizable)); set("getDefaultCloseOperation", voidToInt(frame::getDefaultCloseOperation)); - set("pack", voidToVoid(frame::pack)); - set("setAlwaysOnTop", booleanOptToVoid(frame::setAlwaysOnTop)); set("setDefaultCloseOperation", intToVoid(frame::setDefaultCloseOperation)); - set("setLocationByPlatform", booleanOptToVoid(frame::setLocationByPlatform)); set("setResizable", booleanOptToVoid(frame::setResizable)); set("setTitle", stringToVoid(frame::setTitle)); } diff --git a/src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java b/src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java new file mode 100644 index 00000000..d0b018aa --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java @@ -0,0 +1,98 @@ +package com.annimon.ownlang.modules.forms; + +import com.annimon.ownlang.lib.Arguments; +import static com.annimon.ownlang.lib.Converters.*; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import java.awt.Window; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; + +public class WindowValue extends ContainerValue { + + private final Window window; + + public WindowValue(int functionsCount, Window window) { + super(functionsCount + 18, window); + this.window = window; + init(); + } + + private void init() { + set("addWindowListener", this::addWindowListener); + set("dispose", voidToVoid(window::dispose)); + set("isActive", voidToBoolean(window::isActive)); + set("isAlwaysOnTop", voidToBoolean(window::isAlwaysOnTop)); + set("isAlwaysOnTopSupported", voidToBoolean(window::isAlwaysOnTopSupported)); + set("isAutoRequestFocus", voidToBoolean(window::isAutoRequestFocus)); + set("isFocusableWindow", voidToBoolean(window::isFocusableWindow)); + set("isFocused", voidToBoolean(window::isFocused)); + set("isLocationByPlatform", voidToBoolean(window::isLocationByPlatform)); + set("isShowing", voidToBoolean(window::isShowing)); + set("getOpacity", voidToFloat(window::getOpacity)); + set("pack", voidToVoid(window::pack)); + set("setAlwaysOnTop", booleanOptToVoid(window::setAlwaysOnTop)); + set("setAutoRequestFocus", booleanToVoid(window::setAutoRequestFocus)); + set("setFocusableWindowState", booleanToVoid(window::setFocusableWindowState)); + set("setLocationByPlatform", booleanOptToVoid(window::setLocationByPlatform)); + set("setOpacity", floatToVoid(window::setOpacity)); + set("toBack", voidToVoid(window::toBack)); + set("toFront", voidToVoid(window::toFront)); + } + + + private Value addWindowListener(Value[] args) { + Arguments.check(1, args.length); + final Function action = ValueUtils.consumeFunction(args[0], 0); + window.addWindowListener(new WindowListener() { + @Override + public void windowOpened(WindowEvent e) { + handleWindowEvent("opened", e); + } + + @Override + public void windowClosing(WindowEvent e) { + handleWindowEvent("closing", e); + } + + @Override + public void windowClosed(WindowEvent e) { + handleWindowEvent("closed", e); + } + + @Override + public void windowIconified(WindowEvent e) { + handleWindowEvent("iconified", e); + } + + @Override + public void windowDeiconified(WindowEvent e) { + handleWindowEvent("deiconified", e); + } + + @Override + public void windowActivated(WindowEvent e) { + handleWindowEvent("activated", e); + } + + @Override + public void windowDeactivated(WindowEvent e) { + handleWindowEvent("deactivated", e); + } + + private void handleWindowEvent(String type, final WindowEvent e) { + final MapValue map = new MapValue(4); + map.set("id", NumberValue.of(e.getID())); + map.set("newState", NumberValue.of(e.getNewState())); + map.set("oldState", NumberValue.of(e.getOldState())); + map.set("paramString", new StringValue(e.paramString())); + action.execute(new StringValue(type), map); + } + }); + return NumberValue.ZERO; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/forms/forms.java b/src/main/java/com/annimon/ownlang/modules/forms/forms.java index 2f17298c..ca0c9b0e 100644 --- a/src/main/java/com/annimon/ownlang/modules/forms/forms.java +++ b/src/main/java/com/annimon/ownlang/modules/forms/forms.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.awt.BorderLayout; +import java.awt.event.WindowEvent; import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.ScrollPaneConstants; @@ -92,6 +93,21 @@ public static void initConstants() { box.set("X_AXIS", NumberValue.of(BoxLayout.X_AXIS)); box.set("Y_AXIS", NumberValue.of(BoxLayout.Y_AXIS)); Variables.define("BoxLayout", box); + + final MapValue windowEvent = new MapValue(4); + windowEvent.set("WINDOW_FIRST", NumberValue.of(WindowEvent.WINDOW_FIRST)); + windowEvent.set("WINDOW_OPENED", NumberValue.of(WindowEvent.WINDOW_OPENED)); + windowEvent.set("WINDOW_CLOSING", NumberValue.of(WindowEvent.WINDOW_CLOSING)); + windowEvent.set("WINDOW_CLOSED", NumberValue.of(WindowEvent.WINDOW_CLOSED)); + windowEvent.set("WINDOW_ICONIFIED", NumberValue.of(WindowEvent.WINDOW_ICONIFIED)); + windowEvent.set("WINDOW_DEICONIFIED", NumberValue.of(WindowEvent.WINDOW_DEICONIFIED)); + windowEvent.set("WINDOW_ACTIVATED", NumberValue.of(WindowEvent.WINDOW_ACTIVATED)); + windowEvent.set("WINDOW_DEACTIVATED", NumberValue.of(WindowEvent.WINDOW_DEACTIVATED)); + windowEvent.set("WINDOW_GAINED_FOCUS", NumberValue.of(WindowEvent.WINDOW_GAINED_FOCUS)); + windowEvent.set("WINDOW_LOST_FOCUS", NumberValue.of(WindowEvent.WINDOW_LOST_FOCUS)); + windowEvent.set("WINDOW_STATE_CHANGED", NumberValue.of(WindowEvent.WINDOW_STATE_CHANGED)); + windowEvent.set("WINDOW_LAST", NumberValue.of(WindowEvent.WINDOW_LAST)); + Variables.define("WindowEvent", windowEvent); } @Override From b82596cd8770a54f62251da09673c6666f4c8bac Mon Sep 17 00:00:00 2001 From: Nikolay Petrov <55155499+ortogo@users.noreply.github.com> Date: Sat, 12 Oct 2019 18:03:08 +0300 Subject: [PATCH 275/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=B7=D1=87=D0=B8=D0=BA=D0=B0=20=D1=80=D0=B5=D1=81=D1=83=D1=80?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлен путь к внутренним ресурсам --- src/main/java/com/annimon/ownlang/parser/SourceLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java index 67e49cf9..4621d138 100644 --- a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java +++ b/src/main/java/com/annimon/ownlang/parser/SourceLoader.java @@ -10,7 +10,7 @@ public final class SourceLoader { private SourceLoader() { } public static String readSource(String name) throws IOException { - InputStream is = SourceLoader.class.getResourceAsStream(name); + InputStream is = SourceLoader.class.getResourceAsStream("/" + name); if (is != null) return readAndCloseStream(is); is = new FileInputStream(name); From e217e3bb84139906d2e5658e5eecf476b9fb7faf Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 12 Oct 2019 19:38:23 +0300 Subject: [PATCH 276/448] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=B2=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B0=D1=85=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B7=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D1=83?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D1=8D=D0=BA=D0=B7=D0=B5=D0=BC=D0=BF=D0=BB?= =?UTF-8?q?=D1=8F=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/annimon/ownlang/lib/ClassInstanceValue.java | 9 +++++++++ .../ownlang/parser/ast/ContainerAccessExpression.java | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java index febcfa96..a0e2a4ac 100644 --- a/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java +++ b/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java @@ -43,6 +43,15 @@ public Value access(Value value) { return thisMap.get(value); } + public void set(Value key, Value value) { + final Value v = thisMap.get(key); + if (v == null) { + throw new RuntimeException("Unable to add new field " + + key.asString() + " to class " + className); + } + thisMap.set(key, value); + } + @Override public Object raw() { return null; diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index d0137171..6ee83055 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -73,6 +73,10 @@ public Value set(Value value) { ((MapValue) container).set(lastIndex, value); return value; + case Types.CLASS: + ((ClassInstanceValue) container).set(lastIndex, value); + return value; + default: throw new TypeException("Array or map expected. Got " + container.type()); } From 51b2ef93be5b64f6742010b49ce0c36fc2a3dbac Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 12 Oct 2019 23:15:38 +0300 Subject: [PATCH 277/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B2=D1=8B=D0=B4=D0=B0=D1=87=D0=B0?= =?UTF-8?q?=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B2=20include=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ownlang/parser/ast/IncludeStatement.java | 3 +- src/test/resources/expressions/include.own | 35 +++++++++++++++++++ .../expressions/includeClass.own.txt | 8 +++++ .../includeParseErrorSource.own.txt | 4 +++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/expressions/include.own create mode 100644 src/test/resources/expressions/includeClass.own.txt create mode 100644 src/test/resources/expressions/includeParseErrorSource.own.txt diff --git a/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java index 3bca5a09..0c131292 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.ParseException; import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.SourceLoader; @@ -40,7 +41,7 @@ public Statement loadProgram(String path) throws IOException { final Parser parser = new Parser(tokens); final Statement program = parser.parse(); if (parser.getParseErrors().hasErrors()) { - return null; + throw new ParseException(parser.getParseErrors().toString()); } return program; } diff --git a/src/test/resources/expressions/include.own b/src/test/resources/expressions/include.own new file mode 100644 index 00000000..41eeb1eb --- /dev/null +++ b/src/test/resources/expressions/include.own @@ -0,0 +1,35 @@ +use "std" + +def testIncludeClass() { + include "src/test/resources/expressions/includeClass.own.txt" + obj = new IncludeClass() + assertEquals("1", obj.field1) + assertEquals(2, obj.field2) + assertEquals(42, obj.test()) +} + +def testIncludeNotExistsSource() { + assertFail(def() { + include "src/test.own" + }) +} + +def testCatchingIncludeNotExistsSource() { + res = try(def() { + include "src/test.own" + }, def(classname, message) = "ok") + assertEquals("ok", res) +} + +def testIncludeParseErrorSource() { + assertFail(def() { + include "src/test/resources/expressions/includeParseErrorSource.own.txt" + }) +} + +def testCatchingIncludeParseErrorSource() { + res = try(def() { + include "src/test/resources/expressions/includeParseErrorSource.own.txt" + }, def(classname, message) = "ok") + assertEquals("ok", res) +} \ No newline at end of file diff --git a/src/test/resources/expressions/includeClass.own.txt b/src/test/resources/expressions/includeClass.own.txt new file mode 100644 index 00000000..4c6a7ce6 --- /dev/null +++ b/src/test/resources/expressions/includeClass.own.txt @@ -0,0 +1,8 @@ +class IncludeClass { + field1 = "1" + field2 = 2 + + def test() { + return 42 + } +} \ No newline at end of file diff --git a/src/test/resources/expressions/includeParseErrorSource.own.txt b/src/test/resources/expressions/includeParseErrorSource.own.txt new file mode 100644 index 00000000..a6395c97 --- /dev/null +++ b/src/test/resources/expressions/includeParseErrorSource.own.txt @@ -0,0 +1,4 @@ +def test() = return println match x { + case case 1 +} +value = "passed" \ No newline at end of file From 0cd9446b14b8309d7d12c5d0d59c89248f0a8836 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 13 Oct 2019 00:25:04 +0300 Subject: [PATCH 278/448] =?UTF-8?q?openjdk8=20=D0=B2=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ace2159c..ab238666 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false install: true jdk: - - oraclejdk11 + - openjdk8 addons: sonarcloud: From cb5cae184ddc02e16c5dd38e30746a34d68b98d8 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 13 Oct 2019 00:36:56 +0300 Subject: [PATCH 279/448] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20forms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/forms/look_and_feel.own | 33 +++++++++++++++++++++++++++++++ examples/forms/windowlistener.own | 24 ++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 examples/forms/look_and_feel.own create mode 100644 examples/forms/windowlistener.own diff --git a/examples/forms/look_and_feel.own b/examples/forms/look_and_feel.own new file mode 100644 index 00000000..d7435d65 --- /dev/null +++ b/examples/forms/look_and_feel.own @@ -0,0 +1,33 @@ +use ["java", "forms"] + +UIManager = newClass("javax.swing.UIManager") +// UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel") +UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) + +label = newLabel("Current value: 50") +progressBar = newProgressBar() +progressBar.setValue(50) +progressBar.onChange(def() { + label.setText("Current value: " + progressBar.getValue()) +}) +minusBtn = newButton("-1") +minusBtn.onClick(def() = changeProgress(-1)) +plusBtn = newButton("+1") +plusBtn.onClick(def() = changeProgress(1)) + +def changeProgress(delta) { + value = progressBar.getValue() + delta + if (value > 100) value = 100 + else if (value < 0) value = 0 + progressBar.setValue(value) +} + +window = newWindow("ProgressBar example") +window.add(minusBtn, BorderLayout.WEST) +window.add(progressBar, BorderLayout.CENTER) +window.add(plusBtn, BorderLayout.EAST) +window.add(label, BorderLayout.SOUTH) +window.pack() +window.setLocationByPlatform() +window.setResizable(false) +window.setVisible() diff --git a/examples/forms/windowlistener.own b/examples/forms/windowlistener.own new file mode 100644 index 00000000..ccb15f67 --- /dev/null +++ b/examples/forms/windowlistener.own @@ -0,0 +1,24 @@ +use "forms" + +textArea = newTextArea("Window logs:") + +window = newWindow("Window listener example") +window.addWindowListener(def(type, event) { + textArea.append("\n" + type + ", id: " + match event.id { + case WINDOW_OPENED: "WINDOW_OPENED" + case WINDOW_CLOSING: "WINDOW_CLOSING" + case WINDOW_CLOSED: "WINDOW_CLOSED" + case WINDOW_ICONIFIED: "WINDOW_ICONIFIED" + case WINDOW_DEICONIFIED: "WINDOW_DEICONIFIED" + case WINDOW_ACTIVATED: "WINDOW_ACTIVATED" + case WINDOW_DEACTIVATED: "WINDOW_DEACTIVATED" + case WINDOW_GAINED_FOCUS: "WINDOW_GAINED_FOCUS" + case WINDOW_LOST_FOCUS: "WINDOW_LOST_FOCUS" + case WINDOW_STATE_CHANGED: "WINDOW_STATE_CHANGED" + case _: "unknown type" + }) +}) +window.add(newScrollPane(textArea)) +window.setSize(300, 200) +window.setLocationByPlatform() +window.setVisible() \ No newline at end of file From 651bc3a3a3d256af011e8f5534c8b10debbc6a19 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 17 Oct 2019 11:05:14 +0300 Subject: [PATCH 280/448] =?UTF-8?q?=D0=A0=D0=B5=D0=BB=D0=B8=D0=B7=201.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/Main.java | 6 +- .../modules/collections/collections.java | 88 +++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/modules/collections/collections.java diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java index 66b74d9d..470422da 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/src/main/java/com/annimon/ownlang/Main.java @@ -22,9 +22,9 @@ */ public final class Main { - public static int VERSION_MAJOR = 1; - public static int VERSION_MINOR = 4; - public static int VERSION_PATCH = 0; + public static final int VERSION_MAJOR = 1; + public static final int VERSION_MINOR = 5; + public static final int VERSION_PATCH = 0; public static final String VERSION = VERSION_MAJOR + "." + VERSION_MINOR + "." + VERSION_PATCH + "_" + Gen.BUILD_DATE; diff --git a/src/main/java/com/annimon/ownlang/modules/collections/collections.java b/src/main/java/com/annimon/ownlang/modules/collections/collections.java new file mode 100644 index 00000000..3523b8d6 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/collections/collections.java @@ -0,0 +1,88 @@ +package com.annimon.ownlang.modules.collections; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Functions; +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 com.annimon.ownlang.modules.Module; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.function.Supplier; + +public class collections implements Module { + + public static void initConstants() { + } + + @Override + public void init() { + initConstants(); + Functions.set("hashMap", mapFunction(HashMap::new)); + Functions.set("linkedHashMap", mapFunction(LinkedHashMap::new)); + Functions.set("concurrentHashMap", mapFunction(ConcurrentHashMap::new)); + Functions.set("treeMap", sortedMapFunction(TreeMap::new, TreeMap::new)); + Functions.set("concurrentSkipListMap", sortedMapFunction(ConcurrentSkipListMap::new, ConcurrentSkipListMap::new)); + } + + private Function mapFunction(final Supplier> mapSupplier) { + return (args) -> { + Arguments.checkOrOr(0, 1, args.length); + final Map map = mapSupplier.get(); + if (args.length == 1) { + if (args[0].type() == Types.MAP) { + map.putAll(((MapValue) args[0]).getMap()); + } else { + throw new TypeException("Map expected in first argument"); + } + } + return new MapValue(map); + }; + } + + private Function sortedMapFunction(final Supplier> mapSupplier, + final java.util.function.Function< + Comparator, + SortedMap> comparatorToMapFunction) { + return (args) -> { + Arguments.checkRange(0, 2, args.length); + final SortedMap map; + switch (args.length) { + case 0: // treeMap() + map = mapSupplier.get(); + break; + case 1: // treeMap(map) || treeMap(comparator) + if (args[0].type() == Types.MAP) { + map = mapSupplier.get(); + map.putAll(((MapValue) args[0]).getMap()); + } else if (args[0].type() == Types.FUNCTION) { + final Function comparator = ValueUtils.consumeFunction(args[0], 0); + map = comparatorToMapFunction.apply((o1, o2) -> comparator.execute(o1, o2).asInt()); + } else { + throw new TypeException("Map or comparator function expected in first argument"); + } + break; + case 2: // treeMap(map, comparator) + if (args[0].type() != Types.MAP) { + throw new TypeException("Map expected in first argument"); + } + final Function comparator = ValueUtils.consumeFunction(args[1], 1); + map = comparatorToMapFunction.apply((o1, o2) -> comparator.execute(o1, o2).asInt()); + map.putAll(((MapValue) args[0]).getMap()); + break; + default: + throw new IllegalStateException(); + } + return new MapValue(map); + }; + } +} From 36ea7b25b5e0197e63702f97707a9c1ce02f8f51 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 17 Oct 2019 14:39:53 +0300 Subject: [PATCH 281/448] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- examples/formats/gzip.own | 21 +++++++++++++++++++++ examples/formats/json.own | 16 ++++++++++++++++ examples/formats/yaml.own | 11 +++++++++-- examples/formats/zip.own | 16 ++++++++++++++++ examples/game/minesweeper.own | 14 +++++++------- 6 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 examples/formats/gzip.own create mode 100644 examples/formats/json.own create mode 100644 examples/formats/zip.own diff --git a/README.md b/README.md index 935f8e00..dfa95af4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ OwnLang - dynamic functional programming language inspired by Scala and Python. | Free | Pro | Desktop | | :--: | :-: | :-----: | -| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.4.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/1.4.0) +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.5.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/1.5.0) Also available as AUR package: diff --git a/examples/formats/gzip.own b/examples/formats/gzip.own new file mode 100644 index 00000000..08b6c140 --- /dev/null +++ b/examples/formats/gzip.own @@ -0,0 +1,21 @@ +use ["std", "gzip"] + +// println "Gzip single file" +// gzip("absolute path to file", "example.gz") + +println "Gzip bytes" +text = trim(" +Lorem ipsum dolor sit amet, consectetur adipiscing elit. In leo dui, venenatis eu eleifend ut, volutpat vitae risus. Vivamus sed massa consectetur, fermentum est ac, semper ligula. Donec vel facilisis urna. Cras scelerisque libero a pulvinar mollis. Maecenas elementum, lectus vitae ullamcorper viverra, odio justo interdum lacus, a dictum mauris lacus id neque. Donec ante nibh, ornare ac lacus at, rutrum vulputate lacus. Quisque aliquet sem sit amet nisl semper faucibus. Aenean finibus sodales est, eget efficitur nibh. Donec ut tortor ut ex auctor fringilla id sed neque. Aenean id placerat ipsum. +Etiam enim ligula, vulputate ac velit nec, accumsan blandit velit. Aenean tortor neque, ornare eu quam vel, viverra condimentum erat. In vitae mattis augue. Sed nec auctor est. Aenean in auctor lorem. Etiam non accumsan arcu. Vivamus purus massa, finibus at ultrices feugiat, congue vitae quam. +Vestibulum porttitor finibus nulla, vel mollis elit luctus vel. Phasellus ut erat ante. Praesent consectetur vulputate sem eget bibendum. Etiam porttitor magna et egestas viverra. Praesent urna eros, porttitor vitae lorem sodales, luctus sagittis velit. Donec sed aliquet nulla, ac gravida urna. Mauris at quam eros. Nam dolor lacus, laoreet vel consequat non, semper sed quam. Nunc semper interdum arcu eget iaculis. Vivamus at turpis et urna pellentesque suscipit in nec tellus. Nam sed sem ut ipsum semper imperdiet in et est. Praesent vestibulum augue a dolor consequat feugiat. Nam massa tortor, feugiat quis orci ut, blandit varius tellus. Quisque et blandit erat. +Fusce ultricies, odio scelerisque venenatis pellentesque, nisl mi vehicula mi, eu ultrices dui nisi ac elit. Nullam laoreet et lacus ac fringilla. Morbi pretium, eros non facilisis bibendum, nisl ex ultricies ipsum, sed sodales est felis vel eros. Suspendisse lobortis lectus scelerisque turpis dapibus, et tristique neque faucibus. Donec sed tellus id augue molestie tempus ac in urna. Morbi accumsan velit in erat ultricies, vitae ultricies nulla viverra. Pellentesque non euismod purus. Pellentesque vitae consequat orci. Pellentesque in nulla pulvinar, blandit leo sed, vehicula quam. Cras finibus libero ut diam aliquam efficitur. Vestibulum rutrum tincidunt posuere. Integer ultricies rutrum libero vitae pellentesque. Nulla blandit ipsum sit amet aliquet venenatis. Maecenas cursus massa quis massa posuere eleifend. Morbi vel lorem luctus, efficitur nibh id, sagittis tortor. Maecenas tristique suscipit dui vel congue. +") +bytesOriginal = getBytes(text) +bytesGzipped = gzipBytes(bytesOriginal) + +println "Original bytes count: " + bytesOriginal.length +println "Gzipped bytes count: " + bytesGzipped.length + +println "Ungzip" +ungzippedBytes = ungzipBytes(bytesGzipped) +println stringFromBytes(ungzippedBytes) \ No newline at end of file diff --git a/examples/formats/json.own b/examples/formats/json.own new file mode 100644 index 00000000..49a76e48 --- /dev/null +++ b/examples/formats/json.own @@ -0,0 +1,16 @@ +use "json" + +data = { + "name": "Json Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } +} +println "Json encode" +println "Minified: " + jsonencode(data) +println "Pretty-print: " + jsonencode(data, 2) diff --git a/examples/formats/yaml.own b/examples/formats/yaml.own index cd2c08d5..0778ad7c 100644 --- a/examples/formats/yaml.own +++ b/examples/formats/yaml.own @@ -1,7 +1,6 @@ use "yaml" -println "Yaml encode" -println yamlencode({ +data = { "name": "Yaml Example", "version": 1, "arrayData": [ @@ -11,6 +10,14 @@ println yamlencode({ "key": "value", 10: "1000" } +} +println "Yaml encode" +println yamlencode(data) +println "Yaml encode with options" +println yamlencode(data, { + "indent": 6, + "prettyFlow": true, + "defaultFlowStyle": "BLOCK" }) println "\nYaml decode" diff --git a/examples/formats/zip.own b/examples/formats/zip.own new file mode 100644 index 00000000..b3e3db1a --- /dev/null +++ b/examples/formats/zip.own @@ -0,0 +1,16 @@ +use "zip" + +// println "Zip single file" +// zip("absolute path to file", "example.zip") + +println "Zip files" +zipFiles(["json.own", "yaml.own", "zip.own"], "example1.zip") + +println "Zip files" +zipFiles({ + "json.own": "json.txt", + "yaml.own": "yaml/yaml.own", + "zip.own": "readme.md"}, "example2.zip") + +println "List zip entries" +println listZipEntries("example2.zip").joinToString(", ") diff --git a/examples/game/minesweeper.own b/examples/game/minesweeper.own index 1b99cf1b..596701f4 100644 --- a/examples/game/minesweeper.own +++ b/examples/game/minesweeper.own @@ -7,11 +7,11 @@ use "canvasfx" CELL_NONE = -100 CELL_MINE = -200 // Colors -BACKGROUND_COLOR = Color.new(#FF283593) -OPENED_CELL_COLOR = Color.new(0xFF9FA8DA) -DEFAULT_CELL_COLOR = Color.new(#FF5C6BC0) -MINE_CELL_COLOR = Color.new(#FF1A237E) -FLAG_COLOR = Color.new(#FF7A231E) +BACKGROUND_COLOR = Color.`new`(#FF283593) +OPENED_CELL_COLOR = Color.`new`(0xFF9FA8DA) +DEFAULT_CELL_COLOR = Color.`new`(#FF5C6BC0) +MINE_CELL_COLOR = Color.`new`(#FF1A237E) +FLAG_COLOR = Color.`new`(#FF7A231E) // Parameters WIDTH = 400 HEIGHT = 400 @@ -73,7 +73,7 @@ def drawGameTable(showBombs = false) { def drawWin() { drawGameTable(true) - g.setFill(Color.new(#60FFFFFF)) + g.setFill(Color.`new`(#60FFFFFF)) g.fillRect(0, 0, WIDTH, HEIGHT) g.setFill(Color.DARKGREEN) g.fillText("YOU WIN", WIDTH / 2, HEIGHT / 2) @@ -81,7 +81,7 @@ def drawWin() { def drawGameOver() { drawGameTable(true) - g.setFill(Color.new(#60000000)) + g.setFill(Color.`new`(#60000000)) g.fillRect(0, 0, WIDTH, HEIGHT) g.setFill(Color.PINK) g.fillText("Game Over", WIDTH / 2, HEIGHT / 2) From fed61592ba343092487c1791421904fe8b907c0f Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 18 Dec 2019 19:33:41 +0200 Subject: [PATCH 282/448] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/modules.yml | 190 +++++++++++------- examples/versions/whatsnew_1.5.0.own | 110 ++++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- .../modules/functional/functional_chain.java | 2 +- .../modules/functional/functional_stream.java | 2 +- .../annimon/ownlang/modules/java/java.java | 2 +- .../com/annimon/ownlang/modules/zip/zip.java | 12 +- 7 files changed, 246 insertions(+), 74 deletions(-) create mode 100644 examples/versions/whatsnew_1.5.0.own diff --git a/docs/modules.yml b/docs/modules.yml index 43d77f55..d9fdbc77 100644 --- a/docs/modules.yml +++ b/docs/modules.yml @@ -12,7 +12,7 @@ - name: OwnLang typeName: map type: 4 - value: "{PLATFORM=android/desktop, VERSION=1.4.0_000000, VERSION_MAJOR=1, VERSION_MINOR=4, VERSION_PATCH=0}" + value: "{PLATFORM=android/desktop, VERSION=1.5.0_000000, VERSION_MAJOR=1, VERSION_MINOR=5, VERSION_PATCH=0}" since: 1.4.0 functions: - name: arrayCombine @@ -187,6 +187,7 @@ args: input, charset = "UTF-8" desc: returns a string from byte array in the given charset desc_ru: возвращает строку из массива байт в заданной кодировке + since: 1.5.0 - name: stripMargin args: input, marginPrefix = "|" desc: trims leading whitespaces followed by `marginPrefix` on each line and removes the first and the last lines if they are blank @@ -1304,7 +1305,7 @@ downloader(url, file, def(progress, bytesDownloaded, bytesMax) { bar = "#" * (progress / 2) - print sprintf("%-50s %d%% %.2f / %.2f MiB\r", bar, progress, bytesDownloaded / MBYTES, bytesMax / MBYTES) + print sprintf("%-50s %d%% %.2f / %.2f MiB\\r", bar, progress, bytesDownloaded / MBYTES, bytesMax / MBYTES) }) - name: base64 scope: both @@ -1346,7 +1347,7 @@ print jsondecode("{\"key1\":1,\"key2\":[1,2,3],\"key3\":\"text\"}") // {key2=[1, 2, 3], key3=text, key1=1} - name: "jsonencode" - args: "jsonString" + args: "jsonString, indent = 0" desc: "converts string to data" desc_ru: "преобразует строку в формате json в данные" example: |- @@ -1651,6 +1652,7 @@ - `custom(func)` - performs custom operation - `reduce(func)` - converts elements to one value - `forEach(func)` - executes function for each element + - `joining(delimiter = "", prefix = "", suffix = "")` - joins elements into a string - `toArray()` - returns array of elements - `count()` - returns count of elements desc_ru: |- @@ -1670,6 +1672,7 @@ - `custom(func)` - выполняет пользовательскую операцию над данными - `reduce(func)` - преобразует элементы в одно значение - `forEach(func)` - вызывает функцию для каждого элемента + - `joining(delimiter = "", prefix = "", suffix = "")` - склеивает элементы в строку - `toArray()` - возвращает массив элементов - `count()` - возвращает количество элементов - name: takewhile @@ -2604,8 +2607,23 @@ args: 'layoutManager = ...' desc: 'creates new panel with optional layout manager' desc_ru: 'создаёт новую панель с опциональным LayoutManager' - - name: newTextField + - name: newProgressBar + args: 'isVertical = false, min = 0, max = 100' + desc: 'creates new progress bar' + desc_ru: 'создаёт новый прогрессбар' + since: 1.5.0 + - name: newScrollPane + args: 'view, verticalPolicy = AS_NEEDED, horizontalPolicy = AS_NEEDED' + desc: 'creates new scroll pane' + desc_ru: 'создаёт новую область прокрутки' + since: 1.5.0 + - name: newTextArea args: 'text = ""' + desc: 'creates new text area' + desc_ru: 'создаёт новую область ввода' + since: 1.5.0 + - name: newTextField + args: 'text = "", rows = 0, cols = 0' desc: 'creates new text field' desc_ru: 'создаёт новое поле ввода' - name: newWindow @@ -3777,46 +3795,43 @@ typeName: map type: 4 value: "{TRIANGLES=0, TRIANGLE_STRIP=1, TRIANGLE_FAN=2}" - - - name: "Action" + - name: "Action" typeName: map type: 4 value: "{DOWN=0, UP=1, MOVE=2, MULTIPLE=2, CANCEL=3, OUTSIDE=4, POINTER_DOWN=5, POINTER_UP=6, POINTER_INDEX_SHIFT=8, MASK=255, POINTER_INDEX_MASK=65280}" - - - name: "BitmapCompressFormat" + - name: "BitmapCompressFormat" typeName: map type: 4 value: "{JPEG=0, PNG=1, WEBP=2}" - - - name: "EdgeType" + - name: "EdgeType" typeName: map type: 4 value: "{BW=0, AA=1}" - - - name: "Cap" + - name: "Cap" typeName: map type: 4 value: "{BUTT=0, ROUND=1, SQUARE=2}" - - - name: "Style" + - name: "Style" typeName: map type: 4 value: "{FILL=0, STROKE=1, FILL_AND_STROKE=2}" - - - name: "BitmapConfig" + - name: "BitmapConfig" typeName: map type: 4 value: "{ALPHA_8=0, RGB_565=1, ARGB_4444=2, ARGB_8888=3}" - - - name: "Join" + - name: "Join" typeName: map type: 4 value: "{MITER=0, ROUND=1, BEVEL=2}" - - - name: "Align" + - name: "Align" typeName: map type: 4 value: "{LEFT=0, CENTER=1, RIGHT=2}" + - name: "DisplayMetrics" + typeName: map + type: 4 + value: "{density=, densityDpi=, scaledDensity=, widthPixels=, heightPixels=, xdpi=, ydpi=}" + since: 1.5.1 functions: - name: "createBitmap" args: "..." @@ -3865,48 +3880,74 @@ bitmap = createBitmap(imageBytes) g.drawBitmap(bitmap, 0, 0) repaint() - - - name: "createScaledBitmap" + - name: "createScaledBitmap" args: "srcBitmap, width, height, filter" desc: "scales bitmap to size and returns new BitmapValue" desc_ru: "возвращает BitmapValue с изменённым размером заданного изображения" - - - name: "getScreenBitmap" + - name: "getScreenBitmap" args: "" desc: "returns current screen as bitmap" desc_ru: "возвращает содержимое экрана в виде изображения" - - - name: "hidecanvas" + - name: "hidecanvas" args: "" desc: "closes canvas screen and releases resources" desc_ru: "закрывает экран канваса и освобождает ресурсы" - - - name: "repaint" + - name: "newPath" + args: "path = null" + desc: "creates new PathValue" + desc_ru: "создаёт новый PathValue" + since: 1.5.1 + - name: "newLinearGradient" + args: "x0, y0, x1, y1, colors|color1, positions|color2, tileMode" + desc: "creates new shader" + desc_ru: "создаёт новый шейдер" + since: 1.5.1 + - name: "newRadialGradient" + args: "cx, cy, radius, colors|color1, positions|color2, tileMode" + desc: "creates new shader" + desc_ru: "создаёт новый шейдер" + since: 1.5.1 + - name: "newSweepGradient" + args: "cx, cy, colors|color1, positions|color2" + desc: "creates new shader" + desc_ru: "создаёт новый шейдер" + since: 1.5.1 + - name: "newBitmapShader" + args: "bitmap, modeX, modeY" + desc: "creates new bitmap shader" + desc_ru: "создаёт новый шейдер из картинки" + since: 1.5.1 + - name: "newComposeShader" + args: "shader1, shader2, mode = SRC_OVER" + desc: "creates new composition shader" + desc_ru: "создаёт новый композитный шейдер" + since: 1.5.1 + - name: "repaint" args: "" desc: "" desc_ru: "" - - - name: "setOnKeyDownEvent" + - name: "setOnKeyDownEvent" args: "" desc: "" desc_ru: "" - - - name: "setOnKeyUpEvent" + - name: "setOnKeyUpEvent" args: "" desc: "" desc_ru: "" - - - name: "setOnLongPressEvent" + - name: "setOnLongPressEvent" args: "" desc: "" desc_ru: "" - - - name: "setOnTouchEvent" + - name: "setOnTouchEvent" args: "" desc: "" desc_ru: "" - - - name: "showcanvas" + - name: "setGestureDetectorListener" + args: "listener" + desc: "sets gesture detector listener" + desc_ru: "устанавливает обработчик детектора жестов" + since: 1.5.1 + - name: "showcanvas" args: "" desc: "shows canvas screen and returns GraphicsValue" desc_ru: "показывает экран канваса и возвращает GraphicsValue" @@ -4054,9 +4095,13 @@ args: "" desc: "" desc_ru: "" - - - name: "clipRect" - args: "" + - name: "clipPath" + args: "path" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "clipRect" + args: "x, y, w, h, op = 0" desc: "" desc_ru: "" - @@ -4099,13 +4144,16 @@ args: "" desc: "" desc_ru: "" - - - name: "drawPoint" + - name: "drawPath" + args: "path" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "drawPoint" args: "" desc: "" desc_ru: "" - - - name: "drawRGB" + - name: "drawRGB" args: "" desc: "" desc_ru: "" @@ -4119,13 +4167,16 @@ args: "" desc: "" desc_ru: "" - - - name: "drawText" + - name: "drawText" args: "" desc: "" desc_ru: "" - - - name: "fillCircle" + - name: "drawTextOnPath" + args: "text, path, hOffset, vOffset" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "fillCircle" args: "" desc: "" desc_ru: "" @@ -4154,13 +4205,16 @@ args: "" desc: "" desc_ru: "" - - - name: "getColor" + - name: "getColor" args: "" desc: "" desc_ru: "" - - - name: "getDensity" + - name: "getFillPath" + args: "src, dst" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "getDensity" args: "" desc: "" desc_ru: "" @@ -4394,29 +4448,29 @@ args: "" desc: "" desc_ru: "" - - - name: "setShadowLayer" - args: "" + - name: "setShader" + args: "shader" desc: "" desc_ru: "" - - - name: "setStrikeThruText" - args: "" + since: 1.5.1 + - name: "setShadowLayer" + args: "radius, dx, dy, shadowColor" desc: "" desc_ru: "" - - - name: "setStrokeCap" - args: "" + - name: "setStrikeThruText" + args: "isEnabled" desc: "" desc_ru: "" - - - name: "setStrokeJoin" - args: "" + - name: "setStrokeCap" + args: "cap" desc: "" desc_ru: "" - - - name: "setStrokeMiter" - args: "" + - name: "setStrokeJoin" + args: "join" + desc: "" + desc_ru: "" + - name: "setStrokeMiter" + args: "miter" desc: "" desc_ru: "" - diff --git a/examples/versions/whatsnew_1.5.0.own b/examples/versions/whatsnew_1.5.0.own new file mode 100644 index 00000000..26762cbf --- /dev/null +++ b/examples/versions/whatsnew_1.5.0.own @@ -0,0 +1,110 @@ +use ["std", "functional", "gzip", "json", "java"] + +title("Added std::getBytes, std::stringFromBytes") +arr = [119, 111, 114, 108, 100] +println getBytes("Hello") +println stringFromBytes(arr) +println getBytes("👍", "UTF-8") + + +title("Added std::stripMargin") +println "123 + | 456 + | 789".stripMargin("|") +println " + > 123 + > 456 + > 789 + ".stripMargin("> ") + + +title("Added functional::stream::joining") +println stream(range(1, 10)) + .filter(def(x) = x % 2 == 0) + .joining(", ") +println stream(range(1, 4)) + .joining(" | ", "<", ">") + + +title("Added properties and functions for arrays") +println arr.length +if (!arr.isEmpty()) { + println arr.joinToString("-") +} + + +title("Added null coalesce operator") +obj = {"a": {"b": 12}} +println obj.b ?? 1 +println obj.a.b ?? 2 +println obj.test1.test2 ?? 3 + + +title("Added basic classes support") +class Point { + def Point(x, y) { + this.x = x + this.y = y + } + def move(dx, dy) { + this.x += dx + this.y += dy + } + + def info() = "[" + this.x + ", " + this.y + "]" +} +a = new Point(10, 12) +println a.info() +b = new Point(2, 4) +a.move(b.x, b.y) +println a.info() + + +title("Ability to iterate strings and arrays with index") +for ch : "test " + print ch +for ch, code : "test" + print "{" + ch + ", " + code + "} " + +println "" +for el : arr + print " " + el +println "" +for el, index : arr + print "{" + el + ", " + index + "} " + + +title("Ability to pretty-print json") +data = {"xyz": 123, "uvw": 456, "rst": 789} +println jsonencode(data) +println jsonencode(data, 2) + + +title("Added gzip module") +text = "Lorem ipsum dolor sit amet" * 80 +bytesOriginal = getBytes(text) +bytesGzipped = gzipBytes(bytesOriginal) +println "Original bytes count: " + bytesOriginal.length +println "Gzipped bytes count: " + bytesGzipped.length +println "Ungzip" +ungzippedBytes = ungzipBytes(bytesGzipped) +println stringFromBytes(ungzippedBytes) == text ? "match" : "not match" + + +title("Improved java interoperability") +Random = newClass("java.util.Random") +rnd = new Random() +println rnd.nextInt(100) + + + +title("Other") +println "Added zip, okhttp module" + +// helpers +def title(s) { + println "\n" + println "=" * s.length + println s + println "=" * s.length +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 95b89ea2..9863f8c3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Fri Mar 09 10:54:07 EET 2018 -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists 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 51ce0317..a4b685f8 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 @@ -18,5 +18,5 @@ public Value execute(Value... args) { } return result; } - + } 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 db6e8ca6..f0888aa5 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 @@ -116,7 +116,7 @@ private Value custom(Value[] args) { } return result; } - + private FunctionValue wrapIntermediate(Function f) { return wrap(f, true); } diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/src/main/java/com/annimon/ownlang/modules/java/java.java index 398c90ed..a64f19b1 100644 --- a/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -329,7 +329,7 @@ private static Function methodsToFunction(Object object, List methods) { + " not found or non accessible in " + className); }; } - + private static boolean isMatch(Value[] args, Class[] types) { for (int i = 0; i < args.length; i++) { final Value arg = args[i]; diff --git a/src/main/java/com/annimon/ownlang/modules/zip/zip.java b/src/main/java/com/annimon/ownlang/modules/zip/zip.java index 34ebba51..569979aa 100644 --- a/src/main/java/com/annimon/ownlang/modules/zip/zip.java +++ b/src/main/java/com/annimon/ownlang/modules/zip/zip.java @@ -221,7 +221,7 @@ private void copy(InputStream is, OutputStream os) throws IOException { } private void generateFileList(Map mappings, String rootPath, File node, Function mapper) { - if (!rootPath.equals(node.getAbsolutePath())) { + if (rootPath != null && !rootPath.equals(node.getAbsolutePath())) { String entryPath = node.getAbsolutePath().substring(rootPath.length() + 1); if (mapper != null) { entryPath = mapper.execute(new StringValue(entryPath)).asString(); @@ -233,11 +233,19 @@ private void generateFileList(Map mappings, String rootPath, File } if (node.isDirectory()) { - for (File file : node.listFiles()) { + for (File file : safeListFiles(node)) { generateFileList(mappings, rootPath, file, mapper); } } } + + private File[] safeListFiles(File node) { + final File[] files = node.listFiles(); + if (files != null) { + return files; + } + return new File[0]; + } private String[] listEntries(File input) { final List entries = new ArrayList<>(); From 981d796766e19b769d4577855d086d5d197ee124 Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Wed, 27 May 2020 16:55:52 +0300 Subject: [PATCH 283/448] Create .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..053c9066 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.own linguist-language=Scala From 493a3995a9426b3e84b2a53ff97021ddac3662bb Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Thu, 18 Jun 2020 23:33:03 +0300 Subject: [PATCH 284/448] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=20=D0=B2=2024-=D1=87=D0=B0=D1=81=D0=BE=D0=B2=D0=BE=D0=BC=20?= =?UTF-8?q?=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/modules/date/date.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/modules/date/date.java b/src/main/java/com/annimon/ownlang/modules/date/date.java index b83e24cb..7f5e10da 100644 --- a/src/main/java/com/annimon/ownlang/modules/date/date.java +++ b/src/main/java/com/annimon/ownlang/modules/date/date.java @@ -58,7 +58,7 @@ private static DateValue from(Calendar calendar) { value.set(YEAR, NumberValue.of(calendar.get(Calendar.YEAR))); value.set(MONTH, NumberValue.of(calendar.get(Calendar.MONTH))); value.set(DAY, NumberValue.of(calendar.get(Calendar.DAY_OF_MONTH))); - value.set(HOUR, NumberValue.of(calendar.get(Calendar.HOUR))); + value.set(HOUR, NumberValue.of(calendar.get(Calendar.HOUR_OF_DAY))); value.set(MINUTE, NumberValue.of(calendar.get(Calendar.MINUTE))); value.set(SECOND, NumberValue.of(calendar.get(Calendar.SECOND))); value.set(MILLISECOND, NumberValue.of(calendar.get(Calendar.MILLISECOND))); From d18a14b2acafa2db744f371258483415301f8d9a Mon Sep 17 00:00:00 2001 From: corgifist <86985149+corgifist@users.noreply.github.com> Date: Thu, 8 Jul 2021 17:58:11 +0300 Subject: [PATCH 285/448] =?UTF-8?q?=D0=90=D0=B2=D1=82=D0=BE=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=20toString()=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added auto toString() * Update ClassInstanceValue.java --- .../ownlang/lib/ClassInstanceValue.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java index a0e2a4ac..9a2ff767 100644 --- a/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java +++ b/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java @@ -8,6 +8,7 @@ public class ClassInstanceValue implements Value { private final String className; private final MapValue thisMap; private ClassMethod constructor; + private UserDefinedFunction toString; public ClassInstanceValue(String name) { this.className = name; @@ -17,28 +18,32 @@ public ClassInstanceValue(String name) { public MapValue getThisMap() { return thisMap; } - + public String getClassName() { return className; } - + public void addField(String name, Value value) { thisMap.set(name, value); } - + public void addMethod(String name, ClassMethod method) { + if (name.equals("toString")) { + toString = method; + } thisMap.set(name, method); if (name.equals(className)) { constructor = method; } } - + + public void callConstructor(Value[] args) { if (constructor != null) { constructor.execute(args); } } - + public Value access(Value value) { return thisMap.get(value); } @@ -46,7 +51,7 @@ public Value access(Value value) { public void set(Value key, Value value) { final Value v = thisMap.get(key); if (v == null) { - throw new RuntimeException("Unable to add new field " + throw new RuntimeException("Unable to add new field " + key.asString() + " to class " + className); } thisMap.set(key, value); @@ -69,14 +74,17 @@ public double asNumber() { @Override public String asString() { - return "class " + className + "@" + thisMap; + if (toString != null) { + return toString.execute(new Value[] {}).asString(); + } + return className + "@" + thisMap; } @Override public int type() { return Types.CLASS; } - + @Override public int hashCode() { int hash = 5; @@ -99,7 +107,7 @@ public boolean equals(Object obj) { public int compareTo(Value o) { return asString().compareTo(o.asString()); } - + @Override public String toString() { return asString(); From 30f18ab053d5ada0223bc1f7e502dd918bdbd344 Mon Sep 17 00:00:00 2001 From: Victor Melnik Date: Sun, 30 Jan 2022 12:41:53 +0200 Subject: [PATCH 286/448] Fix release link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfa95af4..2b6669c3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ OwnLang - dynamic functional programming language inspired by Scala and Python. | Free | Pro | Desktop | | :--: | :-: | :-----: | -| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.5.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/1.5.0) +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.5.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.5.0) Also available as AUR package: From 66140ace3fb46bf27c30357789c246c6f3eee2d1 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 12 Aug 2023 23:58:11 +0300 Subject: [PATCH 287/448] Gradle 8, Java 17+ --- .travis.yml | 23 -- build.gradle | 126 ++------ gradle/wrapper/gradle-wrapper.jar | Bin 52818 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 6 +- gradlew | 287 +++++++++++------- gradlew.bat | 43 +-- program.own | 4 +- .../java/com/annimon/ownlang/Console.java | 8 +- .../ownlang/modules/canvasfx/canvasfx.java | 10 +- .../ownlang/parser/LexerBenchmarkTest.java | 7 +- .../com/annimon/ownlang/parser/LexerTest.java | 36 ++- .../ownlang/parser/ParserBenchmarkTest.java | 7 +- .../annimon/ownlang/parser/ParserTest.java | 5 +- .../annimon/ownlang/parser/ProgramsTest.java | 49 ++- .../annimon/ownlang/parser/ast/ASTHelper.java | 3 +- .../parser/ast/OperatorExpressionTest.java | 17 +- .../parser/ast/ValueExpressionTest.java | 3 +- .../parser/ast/VariableExpressionTest.java | 11 +- 18 files changed, 308 insertions(+), 337 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ab238666..00000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: java -sudo: false -install: true - -jdk: - - openjdk8 - -addons: - sonarcloud: - organization: "annimon-github" - -cache: - directories: - - $HOME/.m2 - - $HOME/.gradle - - $HOME/.sonar/cache - -before_install: - - chmod +x gradlew - -after_success: - - ./gradlew proguard sonarqube - - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "latest" && curl -F "file=@store/OwnLang.jar" http://projects.annimon.com/samples/php/travis/upload.php?mode=ownlang diff --git a/build.gradle b/build.gradle index c67023d0..fadfc0d7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,25 +1,11 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'net.sf.proguard:proguard-gradle:6.0.1' - } -} - plugins { - id "java" - id "com.github.johnrengelman.shadow" version "2.0.2" - id "org.sonarqube" version "2.6.2" + id 'java' } -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import proguard.gradle.ProGuardTask - +group = 'com.annimon' +version = '2.0-SNAPSHOT' -sourceCompatibility = '1.8' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' - if (!hasProperty('mainClass')) { ext.mainClass = 'com.annimon.ownlang.Main' } @@ -28,10 +14,10 @@ ext.generatedJavaDir = "${rootProject.projectDir}/src/main/generatedJava" sourceSets.main.java.srcDirs += project.generatedJavaDir repositories { - jcenter() + mavenCentral() } -task generateJavaSources() { +tasks.register('generateJavaSources') { doLast { def source = """ package com.annimon.ownlang; @@ -39,7 +25,7 @@ task generateJavaSources() { private Gen() {} public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; } - """ + """.stripIndent() def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") genFile.getParentFile().mkdirs() genFile.write(source) @@ -47,98 +33,38 @@ task generateJavaSources() { } compileJava.dependsOn(generateJavaSources) -task run(dependsOn: classes, type: JavaExec) { - main = project.mainClass +tasks.register('run', JavaExec) { + dependsOn classes + mainClass = project.mainClass classpath = sourceSets.main.runtimeClasspath standardInput = System.in - ignoreExitValue = true + ignoreExitValue true } -task runOptimizing(dependsOn: classes, type: JavaExec) { - main = project.mainClass +tasks.register('runOptimizing', JavaExec) { + dependsOn classes + mainClass = project.mainClass classpath = sourceSets.main.runtimeClasspath - ignoreExitValue = true - // args '-o 9 -m -a -f examples/game/minesweeper.own'.split(' ') + ignoreExitValue true args '-o 9 -m -a -f program.own'.split(' ') } -task dist(type: ShadowJar) { - from sourceSets.main.output - configurations = [project.configurations.runtime] - destinationDir file("$rootProject.projectDir/dist") - - exclude 'META-INF/*.DSA' - exclude 'META-INF/*.RSA' - exclude 'META-INF/maven/**' - exclude 'LICENSE*' - - manifest.attributes( - 'Main-Class': project.mainClass, - 'Build-Date': new Date().format('YYMMdd') - ) -} - -task proguard(dependsOn: dist, type: ProGuardTask) { - configuration "$rootProject.projectDir/proguard.properties" - injars "$rootProject.projectDir/dist/OwnLang.jar" - outjars "$rootProject.projectDir/store/OwnLang.jar" - - // Automatically handle the Java version of this build. - if (System.getProperty('java.version').startsWith('1.')) { - // Before Java 9, the runtime classes were packaged in a single jar file. - libraryjars "${System.getProperty('java.home')}/lib/rt.jar" - } else { - // As of Java 9, the runtime classes are packaged in modular jmod files. - def jmods = files { file("${System.getProperty('java.home')}/jmods").listFiles() } - jmods.each { - libraryjars it, jarfilter: '!**.jar', filter: '!module-info.class' - } - } -} - -task sandbox(dependsOn: proguard, type: Jar) { - from zipTree("$rootProject.projectDir/store/OwnLang.jar") - libsDirName = "$rootProject.projectDir/store" - appendix = "Sandbox" - - exclude "**/modules/canvas/**", "**/modules/canvasfx/**", "**/modules/forms/**", - "**/modules/java/**", "**/modules/jdbc/**", "**/modules/robot/**", - "**/modules/okhttp/**", - "**/modules/socket/**", "io/**", - "**/modules/aimp/**", "aimpremote/**", - "**/modules/downloader/**", - "**/modules/zip/**", "**/modules/gzip/**", - "jline/**", "org/fusesource/**", "META-INF/native/**" - - manifest { - attributes 'Main-Class': project.mainClass - } -} dependencies { - compile ('io.socket:socket.io-client:1.0.0') { - exclude group: 'org.json', module: 'json' - } - compile 'org.json:json:20180130' - compile 'org.yaml:snakeyaml:1.20' - compile 'jline:jline:2.14.5' - - testImplementation 'junit:junit:4.12' - testImplementation 'org.openjdk.jmh:jmh-core:1.13' - testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.13' -} - -sonarqube { - properties { - property "sonar.projectName", "Own-Programming-Language-Tutorial" - property "sonar.projectKey", "aNNiMON_Own-Programming-Language-Tutorial" - property "sonar.host.url", "https://sonarcloud.io" + implementation ('io.socket:socket.io-client:1.0.2') { + exclude group: 'org.json', module: 'json' } + implementation 'org.json:json:20230227' + implementation 'org.yaml:snakeyaml:1.20' + implementation 'jline:jline:2.14.5' + + testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.openjdk.jmh:jmh-core:1.37' + testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.37' } test { - testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" - } + useJUnitPlatform() } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index deedc7fa5e6310eac3148a7dd0b1f069b07364cb..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch literal 60756 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≺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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9863f8c3..42dae806 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Mar 09 10:54:07 EET 2018 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip +#Sat Aug 12 23:03:44 EEST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 9aa616c2..1b6c7873 100644 --- a/gradlew +++ b/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -89,81 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then - cd "$(dirname "$0")" fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f9553162..107acd32 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/program.own b/program.own index 83bacfe6..239b8394 100644 --- a/program.own +++ b/program.own @@ -173,14 +173,14 @@ def fact2(n) = match n { println fact1(6) println fact2(6) -class = { +functions = { "add": def(a, b) = a + b, "sub": def(a, b) = a - b, "mul": def(a, b) = a * b, "div": def(a, b) = a / b } -println class.add(2, class.mul(2, 2)) +println functions.add(2, functions.mul(2, 2)) println split("1/2/3/4/5/6", "/") println join(nums, ", ") diff --git a/src/main/java/com/annimon/ownlang/Console.java b/src/main/java/com/annimon/ownlang/Console.java index 57487ac9..888d9f75 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/src/main/java/com/annimon/ownlang/Console.java @@ -6,7 +6,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; public class Console { @@ -69,11 +69,7 @@ public static void handleException(Thread thread, Throwable throwable) { throwable.printStackTrace(ps); ps.flush(); } - try { - error(baos.toString("UTF-8")); - } catch (UnsupportedEncodingException ex) { - error(baos.toString()); - } + error(baos.toString(StandardCharsets.UTF_8)); } public static File fileInstance(String path) { diff --git a/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java b/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java index e1568025..6c570416 100644 --- a/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java +++ b/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.modules.canvasfx; -import com.annimon.ownlang.exceptions.TypeException; +/*import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import static com.annimon.ownlang.lib.Converters.*; @@ -37,15 +37,15 @@ import javafx.scene.shape.StrokeLineCap; import javafx.scene.shape.StrokeLineJoin; import javafx.scene.text.TextAlignment; -import javax.swing.JFrame; +import javax.swing.JFrame;*/ /** * * @author aNNiMON */ -public final class canvasfx implements Module { +public final class canvasfx /*implements Module*/ { - private static final int FX_EFFECT_TYPE = 5301; + /*private static final int FX_EFFECT_TYPE = 5301; private static final int FX_COLOR_TYPE = 5302; private static JFrame frame; @@ -1112,6 +1112,6 @@ private static void handleDragEvent(final DragEvent e, final Function handler) { map.set("isConsumed", NumberValue.fromBoolean(e.isConsumed())); map.set("isDropCompleted", NumberValue.fromBoolean(e.isDropCompleted())); handler.execute(map); - } + }*/ } diff --git a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java index fed985b6..b654fff2 100644 --- a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java +++ b/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java @@ -2,8 +2,9 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; -import org.junit.Ignore; -import org.junit.Test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; @@ -32,7 +33,7 @@ public void lexerBenchmark() { } } - @Ignore + @Disabled @Test public void executeBenchmark() throws RunnerException { Options opt = new OptionsBuilder() diff --git a/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/src/test/java/com/annimon/ownlang/parser/LexerTest.java index 6e9b5eb4..a4d94006 100644 --- a/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -1,11 +1,13 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.exceptions.LexerException; -import static com.annimon.ownlang.parser.TokenType.*; +import org.junit.jupiter.api.Test; + import java.util.ArrayList; import java.util.List; -import org.junit.Test; -import static org.junit.Assert.*; + +import static com.annimon.ownlang.parser.TokenType.*; +import static org.junit.jupiter.api.Assertions.*; /** * @@ -25,10 +27,10 @@ public void testNumbers() { assertEquals("f7d6c5", result.get(3).getText()); } - @Test(expected = LexerException.class) + @Test public void testNumbersError() { - String input = "3.14.15 0Xf7_p6_s5"; - Lexer.tokenize(input); + final String input = "3.14.15 0Xf7_p6_s5"; + assertThrows(LexerException.class, () -> Lexer.tokenize(input)); } @Test @@ -74,13 +76,15 @@ public void testEmptyString() { assertEquals("", result.get(0).getText()); } - @Test(expected = LexerException.class) + @Test public void testStringError() { String input = "\"1\"\""; List expList = list(TEXT); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("1", result.get(0).getText()); + assertThrows(LexerException.class, () -> { + List result = Lexer.tokenize(input); + assertTokens(expList, result); + assertEquals("1", result.get(0).getText()); + }); } @Test @@ -117,16 +121,16 @@ public void testComments2() { assertTokens(expList, result); } - @Test(expected = LexerException.class) + @Test public void testCommentsError() { - String input = "/* 1234 \n"; - Lexer.tokenize(input); + final String input = "/* 1234 \n"; + assertThrows(LexerException.class, () -> Lexer.tokenize(input)); } - @Test(expected = LexerException.class) + @Test public void testExtendedWordError() { - String input = "` 1234"; - Lexer.tokenize(input); + final String input = "` 1234"; + assertThrows(LexerException.class, () -> Lexer.tokenize(input)); } @Test diff --git a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java index c1cba073..68e3b93d 100644 --- a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java @@ -3,8 +3,9 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; -import org.junit.Ignore; -import org.junit.Test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.runner.Runner; @@ -34,7 +35,7 @@ public void parserBenchmark(Blackhole bh) { } } - @Ignore + @Disabled @Test public void executeBenchmark() throws RunnerException { Options opt = new OptionsBuilder() diff --git a/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/src/test/java/com/annimon/ownlang/parser/ParserTest.java index 9a473d50..21e11bf9 100644 --- a/src/test/java/com/annimon/ownlang/parser/ParserTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ParserTest.java @@ -3,9 +3,10 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.*; +import org.junit.jupiter.api.Test; + import static com.annimon.ownlang.parser.ast.ASTHelper.*; -import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author aNNiMON diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 163b95f6..caa2708e 100644 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -11,37 +11,25 @@ import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.visitors.AbstractVisitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.Collection; -import java.util.stream.Collectors; import java.util.stream.Stream; -import org.junit.Assert; -import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -@RunWith(value = Parameterized.class) -public class ProgramsTest { +import static org.junit.jupiter.api.Assertions.*; +public class ProgramsTest { private static final String RES_DIR = "src/test/resources"; - private final String programPath; - - public ProgramsTest(String programPath) { - this.programPath = programPath; - } - - @Parameters(name = "{index}: {0}") - public static Collection data() { + public static Stream data() { final File resDir = new File(RES_DIR); return scanDirectory(resDir) - .map(File::getPath) - .collect(Collectors.toList()); + .map(File::getPath); } private static Stream scanDirectory(File dir) { @@ -59,7 +47,7 @@ private static Stream scanDirectory(File dir) { .filter(f -> f.getName().endsWith(".own")); } - @Before + @BeforeEach public void initialize() { Variables.clear(); Functions.clear(); @@ -85,25 +73,22 @@ public void initialize() { return NumberValue.ONE; }); Functions.set("assertFail", (args) -> { - try { - ((FunctionValue) args[0]).getValue().execute(); - fail("Function should fail"); - } catch (Throwable thr) { - - } + assertThrows(Throwable.class, + () -> ((FunctionValue) args[0]).getValue().execute()); return NumberValue.ONE; }); } - @Test - public void testProgram() throws IOException { + @ParameterizedTest + @MethodSource("data") + public void testProgram(String programPath) throws IOException { final String source = SourceLoader.readSource(programPath); final Statement s = Parser.parse(Lexer.tokenize(source)); try { s.execute(); s.accept(testFunctionsExecutor); } catch (Exception oae) { - Assert.fail(oae.toString()); + fail(oae.toString()); } } @@ -117,7 +102,7 @@ public void testOutput() throws IOException { s.execute(); assertEquals("012345", Console.text()); } catch (Exception oae) { - Assert.fail(oae.toString()); + fail(oae.toString()); } finally { Console.useSettings(oldSettings); } diff --git a/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java b/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java index 45b5b7be..aaf4d3c4 100644 --- a/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java +++ b/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java @@ -4,7 +4,8 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -import static org.junit.Assert.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Helper for build and test AST nodes. diff --git a/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java index 239756c2..a501ab18 100644 --- a/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java @@ -1,8 +1,10 @@ package com.annimon.ownlang.parser.ast; +import org.junit.jupiter.api.Test; + import static com.annimon.ownlang.parser.ast.ASTHelper.*; import static com.annimon.ownlang.parser.ast.BinaryExpression.Operator.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @author aNNiMON @@ -36,14 +38,16 @@ public void testDivision() { assertValue(number(30), operator(DIVIDE, value(-900), operator(DIVIDE, value(60), value(-2))).eval()); } - @Test() + @Test public void testDivisionZero() { assertValue(number(Double.POSITIVE_INFINITY), operator(DIVIDE, value(2.0), value(0.0)).eval()); } - @Test(expected = RuntimeException.class) + @Test public void testDivisionZeroOnIntegers() { - operator(DIVIDE, value(2), value(0)).eval(); + assertThrows(RuntimeException.class, + () -> operator(DIVIDE, value(2), value(0)).eval()); + } @Test @@ -57,9 +61,10 @@ public void testRemainderZero() { assertValue(number(Double.NaN), operator(REMAINDER, value(2.0), value(0.0)).eval()); } - @Test(expected = RuntimeException.class) + @Test public void testRemainderZeroOnIntegers() { - operator(REMAINDER, value(2), value(0)).eval(); + assertThrows(RuntimeException.class, + () -> operator(REMAINDER, value(2), value(0)).eval()); } @Test diff --git a/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java index 50d41e89..4d2f4932 100644 --- a/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java @@ -1,7 +1,8 @@ package com.annimon.ownlang.parser.ast; +import org.junit.jupiter.api.Test; + import static com.annimon.ownlang.parser.ast.ASTHelper.*; -import org.junit.Test; /** * diff --git a/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java index 36ccbde8..87d9b00a 100644 --- a/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java +++ b/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java @@ -1,8 +1,10 @@ package com.annimon.ownlang.parser.ast; -import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import org.junit.jupiter.api.Test; import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; -import org.junit.Test; + +import static com.annimon.ownlang.parser.ast.ASTHelper.*; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @@ -27,8 +29,9 @@ public void testVariableReplace() { assertValue(number(8), var("a").eval()); } - @Test(expected = VariableDoesNotExistsException.class) + @Test public void testUnknownVariable() { - var("a").eval(); + assertThrows(VariableDoesNotExistsException.class, + () -> var("a").eval()); } } From 3aedda0e9390b1a94ac13ab7735fbc5883d7336a Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 26 Aug 2023 15:59:36 +0300 Subject: [PATCH 288/448] Add multi-project structure --- .gitignore | 7 +--- build.gradle | 74 ++++-------------------------------- modules/main/build.gradle | 25 ++++++++++++ ownlang-core/build.gradle | 36 ++++++++++++++++++ ownlang-desktop/build.gradle | 41 ++++++++++++++++++++ ownlang-parser/build.gradle | 20 ++++++++++ ownlang-utils/build.gradle | 20 ++++++++++ settings.gradle | 8 ++++ 8 files changed, 160 insertions(+), 71 deletions(-) create mode 100644 modules/main/build.gradle create mode 100644 ownlang-core/build.gradle create mode 100644 ownlang-desktop/build.gradle create mode 100644 ownlang-parser/build.gradle create mode 100644 ownlang-utils/build.gradle diff --git a/.gitignore b/.gitignore index d2384ba5..8fda7643 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,9 @@ /.gradle/ /.idea/ /.nb-gradle/ -/build/ +**/build/ /dist/ /out/ /store/ /optimizations/ -/nbproject/private/ -/src/main/generatedJava/ -OwnLang.iml -.nb-gradle-properties +OwnLang.iml \ No newline at end of file diff --git a/build.gradle b/build.gradle index fadfc0d7..50fdc929 100644 --- a/build.gradle +++ b/build.gradle @@ -1,70 +1,12 @@ -plugins { - id 'java' -} - -group = 'com.annimon' -version = '2.0-SNAPSHOT' - -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -if (!hasProperty('mainClass')) { - ext.mainClass = 'com.annimon.ownlang.Main' -} - -ext.generatedJavaDir = "${rootProject.projectDir}/src/main/generatedJava" -sourceSets.main.java.srcDirs += project.generatedJavaDir - -repositories { - mavenCentral() -} - -tasks.register('generateJavaSources') { - doLast { - def source = """ - package com.annimon.ownlang; - class Gen { - private Gen() {} - public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; - } - """.stripIndent() - def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") - genFile.getParentFile().mkdirs() - genFile.write(source) +allprojects { + repositories { + mavenCentral() } -} -compileJava.dependsOn(generateJavaSources) - -tasks.register('run', JavaExec) { - dependsOn classes - mainClass = project.mainClass - classpath = sourceSets.main.runtimeClasspath - standardInput = System.in - ignoreExitValue true -} - -tasks.register('runOptimizing', JavaExec) { - dependsOn classes - mainClass = project.mainClass - classpath = sourceSets.main.runtimeClasspath - ignoreExitValue true - args '-o 9 -m -a -f program.own'.split(' ') -} - -dependencies { - implementation ('io.socket:socket.io-client:1.0.2') { - exclude group: 'org.json', module: 'json' + gradle.projectsEvaluated { + tasks.withType(JavaCompile) { + [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } } - implementation 'org.json:json:20230227' - implementation 'org.yaml:snakeyaml:1.20' - implementation 'jline:jline:2.14.5' - - testImplementation platform('org.junit:junit-bom:5.9.2') - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' - testImplementation 'org.junit.jupiter:junit-jupiter' - testImplementation 'org.openjdk.jmh:jmh-core:1.37' - testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.37' } - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/modules/main/build.gradle b/modules/main/build.gradle new file mode 100644 index 00000000..f4e3e044 --- /dev/null +++ b/modules/main/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon.module' +version = '2.0-SNAPSHOT' + + +dependencies { + api project(":ownlang-core") + + implementation 'com.squareup.okhttp3:okhttp:3.8.1' + implementation ('io.socket:socket.io-client:1.0.2') { + exclude group: 'org.json', module: 'json' + } + implementation 'org.json:json:20230227' + implementation 'org.yaml:snakeyaml:1.20' + + testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/ownlang-core/build.gradle b/ownlang-core/build.gradle new file mode 100644 index 00000000..af686091 --- /dev/null +++ b/ownlang-core/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon' +version = '2.0-SNAPSHOT' + +dependencies { + implementation 'org.json:json:20230227' + + testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} + +ext.generatedJavaDir = "${project.buildDir}/gen/src/main/java" +sourceSets.main.java.srcDirs += project.generatedJavaDir + +tasks.register('generateJavaSources') { + doLast { + def source = """ + package com.annimon.ownlang; + class Gen { + private Gen() {} + public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; + } + """.stripIndent() + def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") + genFile.getParentFile().mkdirs() + genFile.write(source) + } +} +compileJava.dependsOn(generateJavaSources) \ No newline at end of file diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle new file mode 100644 index 00000000..93ff3a0d --- /dev/null +++ b/ownlang-desktop/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'java' + id 'application' +} + +group = 'com.annimon' +version = '2.0-SNAPSHOT' + +application { + mainClass ='com.annimon.ownlang.Main' +} + +dependencies { + implementation project(":ownlang-core") + implementation project(":ownlang-parser") + implementation project(":ownlang-utils") + implementation project(":modules:main") + + testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} + +/*tasks.register('run', JavaExec) { + dependsOn classes + mainClass = project.mainClass + classpath = sourceSets.main.runtimeClasspath + standardInput = System.in + ignoreExitValue true +} + +tasks.register('runOptimizing', JavaExec) { + dependsOn classes + mainClass = project.mainClass + classpath = sourceSets.main.runtimeClasspath + ignoreExitValue true + args '-o 9 -m -a -f program.own'.split(' ') +}*/ \ No newline at end of file diff --git a/ownlang-parser/build.gradle b/ownlang-parser/build.gradle new file mode 100644 index 00000000..b380dd7b --- /dev/null +++ b/ownlang-parser/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon' +version = '2.0-SNAPSHOT' + +dependencies { + api project(":ownlang-core") + + testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' + testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.openjdk.jmh:jmh-core:1.37' + testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.37' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/ownlang-utils/build.gradle b/ownlang-utils/build.gradle new file mode 100644 index 00000000..389808e9 --- /dev/null +++ b/ownlang-utils/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon' +version = '2.0-SNAPSHOT' + +dependencies { + api project(":ownlang-parser") + implementation 'org.json:json:20230227' + implementation 'org.yaml:snakeyaml:1.20' + implementation 'jline:jline:2.14.5' + + testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} diff --git a/settings.gradle b/settings.gradle index b699976e..bd2b452d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,9 @@ rootProject.name = 'OwnLang' + +include 'ownlang-core' +include 'ownlang-parser' +include 'ownlang-desktop' +include 'ownlang-utils' + +include 'modules:main' +findProject(':modules:main')?.name = 'main' \ No newline at end of file From 219d654fe5f8e31ae3ece3b6c73d32164fab243b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 26 Aug 2023 17:19:03 +0300 Subject: [PATCH 289/448] Move classes to separate modules --- .../ownlang/modules/base64/base64.java | 0 .../ownlang/modules/canvas/canvas.java | 0 .../ownlang/modules/canvasfx/canvasfx.java | 0 .../modules/collections/collections.java | 0 .../annimon/ownlang/modules/date/date.java | 0 .../modules/downloader/downloader.java | 0 .../annimon/ownlang/modules/files/files.java | 0 .../modules/forms/AbstractButtonValue.java | 0 .../ownlang/modules/forms/ComponentValue.java | 0 .../ownlang/modules/forms/Components.java | 0 .../ownlang/modules/forms/ContainerValue.java | 0 .../ownlang/modules/forms/JButtonValue.java | 0 .../modules/forms/JComponentValue.java | 0 .../ownlang/modules/forms/JFrameValue.java | 0 .../ownlang/modules/forms/JLabelValue.java | 0 .../ownlang/modules/forms/JPanelValue.java | 0 .../modules/forms/JProgressBarValue.java | 0 .../modules/forms/JScrollPaneValue.java | 0 .../ownlang/modules/forms/JTextAreaValue.java | 0 .../modules/forms/JTextComponentValue.java | 0 .../modules/forms/JTextFieldValue.java | 0 .../modules/forms/LayoutManagerValue.java | 0 .../ownlang/modules/forms/LayoutManagers.java | 0 .../ownlang/modules/forms/WindowValue.java | 0 .../annimon/ownlang/modules/forms/forms.java | 0 .../modules/functional/functional.java | 0 .../modules/functional/functional_chain.java | 0 .../functional/functional_combine.java | 0 .../functional/functional_dropwhile.java | 0 .../modules/functional/functional_filter.java | 0 .../functional/functional_flatmap.java | 0 .../functional/functional_foreach.java | 9 +- .../modules/functional/functional_map.java | 0 .../modules/functional/functional_reduce.java | 0 .../modules/functional/functional_sortby.java | 0 .../modules/functional/functional_stream.java | 0 .../annimon/ownlang/modules/gzip/gzip.java | 0 .../annimon/ownlang/modules/http/http.java | 0 .../ownlang/modules/http/http_download.java | 0 .../ownlang/modules/http/http_http.java | 0 .../ownlang/modules/http/http_urlencode.java | 0 .../annimon/ownlang/modules/java/java.java | 0 .../annimon/ownlang/modules/jdbc/jdbc.java | 0 .../annimon/ownlang/modules/json/json.java | 0 .../ownlang/modules/json/json_decode.java | 0 .../ownlang/modules/json/json_encode.java | 0 .../annimon/ownlang/modules/math/math.java | 0 .../ownlang/modules/okhttp/CallValue.java | 124 ++++----- .../modules/okhttp/HttpClientValue.java | 244 +++++++++--------- .../okhttp/MultipartBodyBuilderValue.java | 140 +++++----- .../modules/okhttp/MultipartBodyValue.java | 48 ++-- .../modules/okhttp/RequestBodyValue.java | 110 ++++---- .../modules/okhttp/RequestBuilderValue.java | 218 ++++++++-------- .../modules/okhttp/ResponseBodyValue.java | 112 ++++---- .../ownlang/modules/okhttp/ResponseValue.java | 104 ++++---- .../ownlang/modules/okhttp/Values.java | 0 .../modules/okhttp/WebSocketValue.java | 92 +++---- .../ownlang/modules/okhttp/okhttp.java | 162 ++++++------ .../annimon/ownlang/modules/ounit/ounit.java | 0 .../ownlang/modules/regex/MatcherValue.java | 0 .../ownlang/modules/regex/PatternValue.java | 0 .../annimon/ownlang/modules/regex/regex.java | 0 .../annimon/ownlang/modules/robot/robot.java | 0 .../ownlang/modules/robot/robot_exec.java | 0 .../modules/robot/robot_fromclipboard.java | 0 .../modules/robot/robot_toclipboard.java | 0 .../ownlang/modules/socket/socket.java | 0 .../ownlang/modules/std/ArrayFunctions.java | 0 .../ownlang/modules/std/NumberFunctions.java | 0 .../ownlang/modules/std/StringFunctions.java | 0 .../com/annimon/ownlang/modules/std/std.java | 13 +- .../ownlang/modules/std/std_arrayCombine.java | 0 .../modules/std/std_arrayKeyExists.java | 0 .../ownlang/modules/std/std_arrayKeys.java | 0 .../ownlang/modules/std/std_arraySplice.java | 0 .../ownlang/modules/std/std_arrayValues.java | 0 .../ownlang/modules/std/std_charat.java | 0 .../ownlang/modules/std/std_default.java | 0 .../annimon/ownlang/modules/std/std_echo.java | 0 .../ownlang/modules/std/std_indexof.java | 0 .../annimon/ownlang/modules/std/std_join.java | 0 .../ownlang/modules/std/std_lastindexof.java | 0 .../ownlang/modules/std/std_length.java | 6 +- .../ownlang/modules/std/std_newarray.java | 0 .../annimon/ownlang/modules/std/std_rand.java | 0 .../ownlang/modules/std/std_range.java | 0 .../ownlang/modules/std/std_readln.java | 0 .../ownlang/modules/std/std_replace.java | 0 .../ownlang/modules/std/std_replaceall.java | 0 .../ownlang/modules/std/std_replacefirst.java | 0 .../ownlang/modules/std/std_sleep.java | 0 .../annimon/ownlang/modules/std/std_sort.java | 0 .../ownlang/modules/std/std_split.java | 0 .../ownlang/modules/std/std_sprintf.java | 0 .../ownlang/modules/std/std_substring.java | 0 .../annimon/ownlang/modules/std/std_sync.java | 0 .../ownlang/modules/std/std_thread.java | 0 .../annimon/ownlang/modules/std/std_time.java | 0 .../ownlang/modules/std/std_tochar.java | 0 .../ownlang/modules/std/std_tolowercase.java | 0 .../ownlang/modules/std/std_touppercase.java | 0 .../annimon/ownlang/modules/std/std_trim.java | 0 .../annimon/ownlang/modules/std/std_try.java | 0 .../annimon/ownlang/modules/types/types.java | 0 .../annimon/ownlang/modules/yaml/yaml.java | 0 .../ownlang/modules/yaml/yaml_decode.java | 0 .../ownlang/modules/yaml/yaml_encode.java | 0 .../com/annimon/ownlang/modules/zip/zip.java | 0 .../java/com/annimon/ownlang/Console.java | 0 .../main/java/com/annimon/ownlang/Shared.java | 13 + .../java/com/annimon/ownlang/Version.java | 11 + .../ArgumentsMismatchException.java | 0 .../ownlang/exceptions/TypeException.java | 0 .../exceptions/UnknownClassException.java | 0 .../exceptions/UnknownFunctionException.java | 0 .../exceptions/UnknownPropertyException.java | 0 .../com/annimon/ownlang/lib/Arguments.java | 0 .../com/annimon/ownlang/lib/ArrayValue.java | 0 .../com/annimon/ownlang/lib/CallStack.java | 0 .../com/annimon/ownlang/lib/Converters.java | 0 .../com/annimon/ownlang/lib/Function.java | 4 + .../annimon/ownlang/lib/FunctionValue.java | 0 .../com/annimon/ownlang/lib/Functions.java | 0 .../com/annimon/ownlang/lib/Instantiable.java | 0 .../com/annimon/ownlang/lib/MapValue.java | 0 .../com/annimon/ownlang/lib/NumberValue.java | 0 .../com/annimon/ownlang/lib/StringValue.java | 0 .../java/com/annimon/ownlang/lib/Types.java | 0 .../java/com/annimon/ownlang/lib/Value.java | 0 .../com/annimon/ownlang/lib/ValueUtils.java | 0 .../com/annimon/ownlang/lib/Variables.java | 0 .../com/annimon/ownlang/modules/Module.java | 0 .../outputsettings/ConsoleOutputSettings.java | 0 .../outputsettings/OutputSettings.java | 0 .../outputsettings/StringOutputSettings.java | 0 .../main/java/com/annimon/ownlang/Main.java | 19 +- .../ownlang/exceptions/LexerException.java | 0 .../OperationIsNotSupportedException.java | 0 .../ownlang/exceptions/ParseException.java | 0 .../exceptions/PatternMatchingException.java | 0 .../ownlang/exceptions/StoppedException.java | 0 .../VariableDoesNotExistsException.java | 0 .../ownlang/lib/ClassDeclarations.java | 0 .../ownlang/lib/ClassInstanceValue.java | 0 .../com/annimon/ownlang/lib/ClassMethod.java | 0 .../java/com/annimon/ownlang/lib/Classes.java | 0 .../ownlang/lib/UserDefinedFunction.java | 3 +- .../annimon/ownlang/parser/Beautifier.java | 0 .../com/annimon/ownlang/parser/Lexer.java | 0 .../com/annimon/ownlang/parser/Linter.java | 0 .../com/annimon/ownlang/parser/Optimizer.java | 0 .../annimon/ownlang/parser/ParseError.java | 0 .../annimon/ownlang/parser/ParseErrors.java | 0 .../com/annimon/ownlang/parser/Parser.java | 0 .../annimon/ownlang/parser/SourceLoader.java | 0 .../com/annimon/ownlang/parser/Token.java | 0 .../com/annimon/ownlang/parser/TokenType.java | 0 .../ownlang/parser/ast/Accessible.java | 0 .../annimon/ownlang/parser/ast/Argument.java | 0 .../annimon/ownlang/parser/ast/Arguments.java | 0 .../ownlang/parser/ast/ArrayExpression.java | 0 .../parser/ast/AssignmentExpression.java | 0 .../ownlang/parser/ast/BinaryExpression.java | 0 .../ownlang/parser/ast/BlockStatement.java | 0 .../ownlang/parser/ast/BreakStatement.java | 0 .../parser/ast/ClassDeclarationStatement.java | 0 .../parser/ast/ConditionalExpression.java | 0 .../parser/ast/ContainerAccessExpression.java | 0 .../ownlang/parser/ast/ContinueStatement.java | 0 .../ast/DestructuringAssignmentStatement.java | 0 .../ownlang/parser/ast/DoWhileStatement.java | 0 .../ownlang/parser/ast/ExprStatement.java | 0 .../ownlang/parser/ast/Expression.java | 0 .../ownlang/parser/ast/ForStatement.java | 0 .../parser/ast/ForeachArrayStatement.java | 0 .../parser/ast/ForeachMapStatement.java | 0 .../parser/ast/FunctionDefineStatement.java | 0 .../ast/FunctionReferenceExpression.java | 0 .../parser/ast/FunctionalExpression.java | 0 .../ownlang/parser/ast/IfStatement.java | 0 .../ownlang/parser/ast/IncludeStatement.java | 0 .../ownlang/parser/ast/InterruptableNode.java | 0 .../ownlang/parser/ast/MapExpression.java | 0 .../ownlang/parser/ast/MatchExpression.java | 0 .../com/annimon/ownlang/parser/ast/Node.java | 0 .../parser/ast/ObjectCreationExpression.java | 0 .../ownlang/parser/ast/PrintStatement.java | 0 .../ownlang/parser/ast/PrintlnStatement.java | 0 .../ownlang/parser/ast/ResultVisitor.java | 0 .../ownlang/parser/ast/ReturnStatement.java | 0 .../annimon/ownlang/parser/ast/Statement.java | 0 .../ownlang/parser/ast/TernaryExpression.java | 0 .../ownlang/parser/ast/UnaryExpression.java | 0 .../ownlang/parser/ast/UseStatement.java | 0 .../ownlang/parser/ast/ValueExpression.java | 0 .../parser/ast/VariableExpression.java | 0 .../annimon/ownlang/parser/ast/Visitor.java | 0 .../ownlang/parser/ast/WhileStatement.java | 0 .../parser/linters/AssignValidator.java | 0 .../DefaultFunctionsOverrideValidator.java | 0 .../ownlang/parser/linters/LintVisitor.java | 0 .../UseWithNonStringValueValidator.java | 0 .../parser/optimization/ConstantFolding.java | 0 .../optimization/ConstantPropagation.java | 0 .../optimization/DeadCodeElimination.java | 0 .../ExpressionSimplification.java | 0 .../optimization/InstructionCombining.java | 0 .../parser/optimization/Optimizable.java | 0 .../optimization/OptimizationVisitor.java | 0 .../optimization/SummaryOptimization.java | 0 .../parser/optimization/VariableInfo.java | 0 .../parser/optimization/VariablesGrabber.java | 0 .../parser/visitors/AbstractVisitor.java | 0 .../parser/visitors/FunctionAdder.java | 0 .../parser/visitors/ModuleDetector.java | 0 .../ownlang/parser/visitors/PrintVisitor.java | 0 .../parser/visitors/VariablePrinter.java | 0 .../ownlang/parser/visitors/VisitorUtils.java | 0 .../ownlang/parser/LexerBenchmarkTest.java | 0 .../com/annimon/ownlang/parser/LexerTest.java | 0 .../ownlang/parser/ParserBenchmarkTest.java | 0 .../annimon/ownlang/parser/ParserTest.java | 0 .../annimon/ownlang/parser/ProgramsTest.java | 0 .../annimon/ownlang/parser/ast/ASTHelper.java | 0 .../parser/ast/OperatorExpressionTest.java | 0 .../parser/ast/ValueExpressionTest.java | 0 .../parser/ast/VariableExpressionTest.java | 0 .../src}/test/java/interop/Data.java | 0 .../expressions/assignmentExpression.own | 0 .../expressions/binaryExpressionOnNumbers.own | 0 .../expressions/binaryExpressionOnStrings.own | 0 .../resources/expressions/binaryUnaryExpr.own | 0 .../resources/expressions/foreachKeyValue.own | 0 .../resources/expressions/foreachValue.own | 0 .../expressions/functionReference.own | 0 .../test/resources/expressions/include.own | 0 .../expressions/includeClass.own.txt | 0 .../includeParseErrorSource.own.txt | 0 .../resources/expressions/nullCoalesce.own | 0 .../expressions/unaryExpressionOnStrings.own | 0 .../resources/expressions/varFuncSameName.own | 0 .../test/resources/modules/base64/base64.own | 0 .../resources/modules/date/compareDates.own | 0 .../resources/modules/date/dateFormat.own | 0 .../test/resources/modules/date/dateParse.own | 0 .../test/resources/modules/date/newDate.own | 0 .../test/resources/modules/files/files.own | 0 .../resources/modules/functional/chain.own | 0 .../resources/modules/functional/foreach.own | 0 .../resources/modules/functional/stream.own | 0 .../test/resources/modules/gzip/gzipBytes.own | 0 .../test/resources/modules/java/classes.own | 0 .../test/resources/modules/regex/match.own | 0 .../modules/regex/replaceCallback.own | 0 .../resources/modules/std/arraySplice.own | 0 .../test/resources/modules/std/default.own | 0 .../test/resources/modules/std/getBytes.own | 0 .../test/resources/modules/std/indexOf.own | 0 .../resources/modules/std/lastIndexOf.own | 0 .../test/resources/modules/std/parseInt.own | 0 .../test/resources/modules/std/parseLong.own | 0 .../src}/test/resources/modules/std/range.own | 0 .../resources/modules/std/stringFromBytes.own | 0 .../resources/modules/std/stripMargin.own | 0 .../resources/modules/std/toHexString.own | 0 .../src}/test/resources/modules/std/try.own | 0 .../resources/modules/yaml/yamldecode.own | 0 .../resources/modules/yaml/yamlencode.own | 0 .../test/resources/other/arrayFunctions.own | 0 .../test/resources/other/functionChain.own | 0 .../src}/test/resources/other/recursion.own | 0 .../src}/test/resources/other/scope.own | 0 .../test/resources/other/stringFunctions.own | 0 .../src}/test/resources/other/types.own | 0 .../ownlang/utils/ModulesInfoCreator.java | 0 .../ownlang/utils/OptimizationDumper.java | 0 .../java/com/annimon/ownlang/utils/Repl.java | 4 +- .../com/annimon/ownlang/utils/Sandbox.java | 0 .../ownlang/utils/TimeMeasurement.java | 0 .../ownlang/utils/repl/JLineConsole.java | 0 .../ownlang/utils/repl/OwnLangCompleter.java | 0 .../ownlang/utils/repl/ReplConsole.java | 0 .../ownlang/utils/repl/SystemConsole.java | 0 283 files changed, 722 insertions(+), 714 deletions(-) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/base64/base64.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/canvas/canvas.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/collections/collections.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/date/date.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/downloader/downloader.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/files/files.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/Components.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/WindowValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/forms/forms.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_chain.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_combine.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_filter.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java (88%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_map.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/functional/functional_stream.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/gzip/gzip.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/http/http.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/http/http_download.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/http/http_http.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/http/http_urlencode.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/java/java.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/json/json.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/json/json_decode.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/json/json_encode.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/math/math.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java (96%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java (97%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java (97%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java (96%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java (96%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java (97%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java (97%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java (97%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/Values.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java (96%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java (97%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/ounit/ounit.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/regex/PatternValue.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/regex/regex.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/robot/robot.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/robot/robot_exec.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/socket/socket.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/StringFunctions.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std.java (85%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_charat.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_default.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_echo.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_indexof.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_join.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_length.java (80%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_newarray.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_rand.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_range.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_readln.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_replace.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_replaceall.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_sleep.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_sort.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_split.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_sprintf.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_substring.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_sync.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_thread.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_time.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_tochar.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_touppercase.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_trim.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/std/std_try.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/types/types.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/yaml/yaml.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java (100%) rename {src => modules/main/src}/main/java/com/annimon/ownlang/modules/zip/zip.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/Console.java (100%) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/Shared.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/Version.java rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/exceptions/TypeException.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Arguments.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/ArrayValue.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/CallStack.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Converters.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Function.java (68%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/FunctionValue.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Functions.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Instantiable.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/MapValue.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/NumberValue.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/StringValue.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Types.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Value.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/ValueUtils.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/lib/Variables.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/modules/Module.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java (100%) rename {src => ownlang-core/src}/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java (100%) rename {src => ownlang-desktop/src}/main/java/com/annimon/ownlang/Main.java (93%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/exceptions/LexerException.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/exceptions/ParseException.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/exceptions/StoppedException.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/lib/ClassDeclarations.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/lib/ClassMethod.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/lib/Classes.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java (99%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/Beautifier.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/Lexer.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/Linter.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/Optimizer.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ParseError.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ParseErrors.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/Parser.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/SourceLoader.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/Token.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/TokenType.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Accessible.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Argument.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Arguments.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Expression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ForStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/IfStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/MapExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Node.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Statement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/UseStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/Visitor.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java (100%) rename {src => ownlang-parser/src}/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/LexerTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ParserTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ProgramsTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java (100%) rename {src => ownlang-parser/src}/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java (100%) rename {src => ownlang-parser/src}/test/java/interop/Data.java (100%) rename {src => ownlang-parser/src}/test/resources/expressions/assignmentExpression.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/binaryExpressionOnNumbers.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/binaryExpressionOnStrings.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/binaryUnaryExpr.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/foreachKeyValue.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/foreachValue.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/functionReference.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/include.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/includeClass.own.txt (100%) rename {src => ownlang-parser/src}/test/resources/expressions/includeParseErrorSource.own.txt (100%) rename {src => ownlang-parser/src}/test/resources/expressions/nullCoalesce.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/unaryExpressionOnStrings.own (100%) rename {src => ownlang-parser/src}/test/resources/expressions/varFuncSameName.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/base64/base64.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/date/compareDates.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/date/dateFormat.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/date/dateParse.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/date/newDate.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/files/files.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/functional/chain.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/functional/foreach.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/functional/stream.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/gzip/gzipBytes.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/java/classes.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/regex/match.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/regex/replaceCallback.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/arraySplice.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/default.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/getBytes.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/indexOf.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/lastIndexOf.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/parseInt.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/parseLong.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/range.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/stringFromBytes.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/stripMargin.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/toHexString.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/std/try.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/yaml/yamldecode.own (100%) rename {src => ownlang-parser/src}/test/resources/modules/yaml/yamlencode.own (100%) rename {src => ownlang-parser/src}/test/resources/other/arrayFunctions.own (100%) rename {src => ownlang-parser/src}/test/resources/other/functionChain.own (100%) rename {src => ownlang-parser/src}/test/resources/other/recursion.own (100%) rename {src => ownlang-parser/src}/test/resources/other/scope.own (100%) rename {src => ownlang-parser/src}/test/resources/other/stringFunctions.own (100%) rename {src => ownlang-parser/src}/test/resources/other/types.own (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/OptimizationDumper.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/Repl.java (98%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/Sandbox.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/TimeMeasurement.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java (100%) rename {src => ownlang-utils/src}/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java (100%) diff --git a/src/main/java/com/annimon/ownlang/modules/base64/base64.java b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/base64/base64.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java diff --git a/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java b/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/canvas/canvas.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java diff --git a/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java b/modules/main/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java diff --git a/src/main/java/com/annimon/ownlang/modules/collections/collections.java b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/collections/collections.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java diff --git a/src/main/java/com/annimon/ownlang/modules/date/date.java b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/date/date.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java diff --git a/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java b/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/downloader/downloader.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java diff --git a/src/main/java/com/annimon/ownlang/modules/files/files.java b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/files/files.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/AbstractButtonValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/ComponentValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/Components.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/Components.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/Components.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/Components.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JComponentValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JLabelValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JProgressBarValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JScrollPaneValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextComponentValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagers.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/WindowValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/forms/forms.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/forms/forms.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java similarity index 88% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java index fc743839..b13c6566 100644 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java @@ -6,19 +6,12 @@ public final class functional_foreach implements Function { - private static final int UNKNOWN = -1; - @Override public Value execute(Value... args) { Arguments.check(2, args.length); final Value container = args[0]; final Function consumer = ValueUtils.consumeFunction(args[1], 1); - final int argsCount; - if (consumer instanceof UserDefinedFunction) { - argsCount = ((UserDefinedFunction) consumer).getArgsCount(); - } else { - argsCount = UNKNOWN; - } + final int argsCount = consumer.getArgsCount(); switch (container.type()) { case Types.STRING: diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_map.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java diff --git a/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/gzip/gzip.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java diff --git a/src/main/java/com/annimon/ownlang/modules/http/http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/http/http.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java diff --git a/src/main/java/com/annimon/ownlang/modules/http/http_download.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/http/http_download.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java diff --git a/src/main/java/com/annimon/ownlang/modules/http/http_http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/http/http_http.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java diff --git a/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java diff --git a/src/main/java/com/annimon/ownlang/modules/java/java.java b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/java/java.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java diff --git a/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java diff --git a/src/main/java/com/annimon/ownlang/modules/json/json.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/json/json.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java diff --git a/src/main/java/com/annimon/ownlang/modules/json/json_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/json/json_decode.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java diff --git a/src/main/java/com/annimon/ownlang/modules/json/json_encode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_encode.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/json/json_encode.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/json/json_encode.java diff --git a/src/main/java/com/annimon/ownlang/modules/math/math.java b/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/math/math.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java similarity index 96% rename from src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java index 41db95aa..3f967220 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java @@ -1,62 +1,62 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Converters; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.ValueUtils; -import java.io.IOException; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.Response; - -public class CallValue extends MapValue { - - private final Call call; - - public CallValue(Call call) { - super(6); - this.call = call; - init(); - } - - private void init() { - set("cancel", Converters.voidToVoid(call::cancel)); - set("enqueue", this::enqueue); - set("execute", this::execute); - set("isCanceled", Converters.voidToBoolean(call::isCanceled)); - set("isExecuted", Converters.voidToBoolean(call::isExecuted)); - } - - private Value enqueue(Value[] args) { - Arguments.checkOrOr(1, 2, args.length); - final Function onResponse = ValueUtils.consumeFunction(args[0], 0); - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) throws IOException { - onResponse.execute(new CallValue(call), new ResponseValue(response)); - } - - @Override - public void onFailure(Call call, IOException e) { - if (args.length == 2) { - ValueUtils.consumeFunction(args[1], 1) - .execute(new CallValue(call), new StringValue(e.getMessage())); - } - } - }); - return NumberValue.ZERO; - } - - private Value execute(Value[] args) { - Arguments.check(0, args.length); - try { - return new ResponseValue(call.execute()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import java.io.IOException; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + +public class CallValue extends MapValue { + + private final Call call; + + public CallValue(Call call) { + super(6); + this.call = call; + init(); + } + + private void init() { + set("cancel", Converters.voidToVoid(call::cancel)); + set("enqueue", this::enqueue); + set("execute", this::execute); + set("isCanceled", Converters.voidToBoolean(call::isCanceled)); + set("isExecuted", Converters.voidToBoolean(call::isExecuted)); + } + + private Value enqueue(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final Function onResponse = ValueUtils.consumeFunction(args[0], 0); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) throws IOException { + onResponse.execute(new CallValue(call), new ResponseValue(response)); + } + + @Override + public void onFailure(Call call, IOException e) { + if (args.length == 2) { + ValueUtils.consumeFunction(args[1], 1) + .execute(new CallValue(call), new StringValue(e.getMessage())); + } + } + }); + return NumberValue.ZERO; + } + + private Value execute(Value[] args) { + Arguments.check(0, args.length); + try { + return new ResponseValue(call.execute()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java index b222339e..baea7c98 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/HttpClientValue.java @@ -1,122 +1,122 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okio.ByteString; - -public class HttpClientValue extends MapValue { - - private final OkHttpClient client; - - public HttpClientValue(OkHttpClient client) { - super(10); - this.client = client; - init(); - } - - public OkHttpClient getClient() { - return client; - } - - private void init() { - set("connectTimeoutMillis", Converters.voidToInt(client::connectTimeoutMillis)); - set("followRedirects", Converters.voidToBoolean(client::followRedirects)); - set("followSslRedirects", Converters.voidToBoolean(client::followSslRedirects)); - set("newCall", args -> { - Arguments.check(1, args.length); - final Request request = Values.getRequest(args[0], " at first argument"); - return new CallValue(client.newCall(request)); - }); - set("newWebSocket", this::newWebSocket); - set("pingIntervalMillis", Converters.voidToInt(client::pingIntervalMillis)); - set("readTimeoutMillis", Converters.voidToInt(client::readTimeoutMillis)); - set("retryOnConnectionFailure", Converters.voidToBoolean(client::retryOnConnectionFailure)); - set("writeTimeoutMillis", Converters.voidToInt(client::writeTimeoutMillis)); - } - - private static final StringValue onOpen = new StringValue("onOpen"); - private static final StringValue onTextMessage = new StringValue("onTextMessage"); - private static final StringValue onBytesMessage = new StringValue("onBytesMessage"); - private static final StringValue onClosing = new StringValue("onClosing"); - private static final StringValue onClosed = new StringValue("onClosed"); - private static final StringValue onFailure = new StringValue("onFailure"); - - private Value newWebSocket(Value[] args) { - Arguments.check(2, args.length); - final Request request = Values.getRequest(args[0], " at first argument"); - if (args[1].type() != Types.MAP) { - throw new TypeException("Map expected at second argument"); - } - final MapValue callbacks = (MapValue) args[1]; - final WebSocket ws = client.newWebSocket(request, new WebSocketListener() { - @Override - public void onOpen(WebSocket webSocket, Response response) { - final Value func = callbacks.get(onOpen); - if (func != null) { - ValueUtils.consumeFunction(func, " at onOpen").execute( - new WebSocketValue(webSocket), - new ResponseValue(response)); - } - } - - @Override - public void onMessage(WebSocket webSocket, String text) { - final Value func = callbacks.get(onTextMessage); - if (func != null) { - ValueUtils.consumeFunction(func, "at onTextMessage").execute( - new WebSocketValue(webSocket), - new StringValue(text)); - } - } - - @Override - public void onMessage(WebSocket webSocket, ByteString bytes) { - final Value func = callbacks.get(onBytesMessage); - if (func != null) { - ValueUtils.consumeFunction(func, "at onBytesMessage").execute( - new WebSocketValue(webSocket), - ArrayValue.of(bytes.toByteArray())); - } - } - - @Override - public void onClosing(WebSocket webSocket, int code, String reason) { - final Value func = callbacks.get(onClosing); - if (func != null) { - ValueUtils.consumeFunction(func, "at onClosing").execute( - new WebSocketValue(webSocket), - NumberValue.of(code), - new StringValue(reason)); - } - } - - @Override - public void onClosed(WebSocket webSocket, int code, String reason) { - final Value func = callbacks.get(onClosed); - if (func != null) { - ValueUtils.consumeFunction(func, "at onClosed").execute( - new WebSocketValue(webSocket), - NumberValue.of(code), - new StringValue(reason)); - } - } - - @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { - final Value func = callbacks.get(onFailure); - if (func != null) { - ValueUtils.consumeFunction(func, "at onFailure").execute( - new WebSocketValue(webSocket), - new StringValue(t.getMessage()), - new ResponseValue(response)); - } - } - }); - return new CallValue(client.newCall(request)); - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import okio.ByteString; + +public class HttpClientValue extends MapValue { + + private final OkHttpClient client; + + public HttpClientValue(OkHttpClient client) { + super(10); + this.client = client; + init(); + } + + public OkHttpClient getClient() { + return client; + } + + private void init() { + set("connectTimeoutMillis", Converters.voidToInt(client::connectTimeoutMillis)); + set("followRedirects", Converters.voidToBoolean(client::followRedirects)); + set("followSslRedirects", Converters.voidToBoolean(client::followSslRedirects)); + set("newCall", args -> { + Arguments.check(1, args.length); + final Request request = Values.getRequest(args[0], " at first argument"); + return new CallValue(client.newCall(request)); + }); + set("newWebSocket", this::newWebSocket); + set("pingIntervalMillis", Converters.voidToInt(client::pingIntervalMillis)); + set("readTimeoutMillis", Converters.voidToInt(client::readTimeoutMillis)); + set("retryOnConnectionFailure", Converters.voidToBoolean(client::retryOnConnectionFailure)); + set("writeTimeoutMillis", Converters.voidToInt(client::writeTimeoutMillis)); + } + + private static final StringValue onOpen = new StringValue("onOpen"); + private static final StringValue onTextMessage = new StringValue("onTextMessage"); + private static final StringValue onBytesMessage = new StringValue("onBytesMessage"); + private static final StringValue onClosing = new StringValue("onClosing"); + private static final StringValue onClosed = new StringValue("onClosed"); + private static final StringValue onFailure = new StringValue("onFailure"); + + private Value newWebSocket(Value[] args) { + Arguments.check(2, args.length); + final Request request = Values.getRequest(args[0], " at first argument"); + if (args[1].type() != Types.MAP) { + throw new TypeException("Map expected at second argument"); + } + final MapValue callbacks = (MapValue) args[1]; + final WebSocket ws = client.newWebSocket(request, new WebSocketListener() { + @Override + public void onOpen(WebSocket webSocket, Response response) { + final Value func = callbacks.get(onOpen); + if (func != null) { + ValueUtils.consumeFunction(func, " at onOpen").execute( + new WebSocketValue(webSocket), + new ResponseValue(response)); + } + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + final Value func = callbacks.get(onTextMessage); + if (func != null) { + ValueUtils.consumeFunction(func, "at onTextMessage").execute( + new WebSocketValue(webSocket), + new StringValue(text)); + } + } + + @Override + public void onMessage(WebSocket webSocket, ByteString bytes) { + final Value func = callbacks.get(onBytesMessage); + if (func != null) { + ValueUtils.consumeFunction(func, "at onBytesMessage").execute( + new WebSocketValue(webSocket), + ArrayValue.of(bytes.toByteArray())); + } + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + final Value func = callbacks.get(onClosing); + if (func != null) { + ValueUtils.consumeFunction(func, "at onClosing").execute( + new WebSocketValue(webSocket), + NumberValue.of(code), + new StringValue(reason)); + } + } + + @Override + public void onClosed(WebSocket webSocket, int code, String reason) { + final Value func = callbacks.get(onClosed); + if (func != null) { + ValueUtils.consumeFunction(func, "at onClosed").execute( + new WebSocketValue(webSocket), + NumberValue.of(code), + new StringValue(reason)); + } + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + final Value func = callbacks.get(onFailure); + if (func != null) { + ValueUtils.consumeFunction(func, "at onFailure").execute( + new WebSocketValue(webSocket), + new StringValue(t.getMessage()), + new ResponseValue(response)); + } + } + }); + return new CallValue(client.newCall(request)); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java index b7d891ec..b1ee7bc5 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java @@ -1,70 +1,70 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import java.util.Map; -import okhttp3.MediaType; -import okhttp3.MultipartBody; - -public class MultipartBodyBuilderValue extends MapValue { - - private final MultipartBody.Builder builder; - - public MultipartBodyBuilderValue() { - super(5); - this.builder = new MultipartBody.Builder(); - init(); - } - - private void init() { - set("addFormData", this::addFormData); - set("addFormDataPart", this::addFormDataPart); - set("addPart", this::addPart); - set("build", args -> new MultipartBodyValue(builder.build())); - set("setType", args -> { - Arguments.check(1, args.length); - builder.setType(MediaType.parse(args[0].asString())); - return this; - }); - } - - private Value addFormDataPart(Value[] args) { - Arguments.checkOrOr(2, 3, args.length); - if (args.length == 2) { - builder.addFormDataPart(args[0].asString(), args[1].asString()); - } else { - builder.addFormDataPart( - args[0].asString(), - args[1].asString(), - Values.getRequestBody(args[2], " at third argument")); - } - return this; - } - - private Value addFormData(Value[] args) { - Arguments.check(1, args.length); - if (args[0].type() != Types.MAP) { - throw new TypeException("Map expected at first argument"); - } - for (Map.Entry entry : ((MapValue) args[0])) { - builder.addFormDataPart(entry.getKey().asString(), entry.getValue().asString()); - } - return this; - } - - private Value addPart(Value[] args) { - Arguments.checkOrOr(2, 3, args.length); - if (args.length == 1) { - builder.addPart( - Values.getRequestBody(args[0], " at first argument")); - } else { - builder.addPart( - Values.getHeaders(args[0], " at first argument"), - Values.getRequestBody(args[1], " at second argument")); - } - return this; - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.util.Map; +import okhttp3.MediaType; +import okhttp3.MultipartBody; + +public class MultipartBodyBuilderValue extends MapValue { + + private final MultipartBody.Builder builder; + + public MultipartBodyBuilderValue() { + super(5); + this.builder = new MultipartBody.Builder(); + init(); + } + + private void init() { + set("addFormData", this::addFormData); + set("addFormDataPart", this::addFormDataPart); + set("addPart", this::addPart); + set("build", args -> new MultipartBodyValue(builder.build())); + set("setType", args -> { + Arguments.check(1, args.length); + builder.setType(MediaType.parse(args[0].asString())); + return this; + }); + } + + private Value addFormDataPart(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + if (args.length == 2) { + builder.addFormDataPart(args[0].asString(), args[1].asString()); + } else { + builder.addFormDataPart( + args[0].asString(), + args[1].asString(), + Values.getRequestBody(args[2], " at third argument")); + } + return this; + } + + private Value addFormData(Value[] args) { + Arguments.check(1, args.length); + if (args[0].type() != Types.MAP) { + throw new TypeException("Map expected at first argument"); + } + for (Map.Entry entry : ((MapValue) args[0])) { + builder.addFormDataPart(entry.getKey().asString(), entry.getValue().asString()); + } + return this; + } + + private Value addPart(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + if (args.length == 1) { + builder.addPart( + Values.getRequestBody(args[0], " at first argument")); + } else { + builder.addPart( + Values.getHeaders(args[0], " at first argument"), + Values.getRequestBody(args[1], " at second argument")); + } + return this; + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java similarity index 96% rename from src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java index 41db1584..c47b7e83 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java @@ -1,24 +1,24 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.lib.Converters; -import okhttp3.MultipartBody; - -public class MultipartBodyValue extends RequestBodyValue { - - private final MultipartBody multipartBody; - - public MultipartBodyValue(MultipartBody multipartBody) { - super(multipartBody, 5); - this.multipartBody = multipartBody; - init(); - } - - public MultipartBody getMultipartBody() { - return multipartBody; - } - - private void init() { - set("boundary", Converters.voidToString(multipartBody::boundary)); - set("size", Converters.voidToInt(multipartBody::size)); - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Converters; +import okhttp3.MultipartBody; + +public class MultipartBodyValue extends RequestBodyValue { + + private final MultipartBody multipartBody; + + public MultipartBodyValue(MultipartBody multipartBody) { + super(multipartBody, 5); + this.multipartBody = multipartBody; + init(); + } + + public MultipartBody getMultipartBody() { + return multipartBody; + } + + private void init() { + set("boundary", Converters.voidToString(multipartBody::boundary)); + set("size", Converters.voidToInt(multipartBody::size)); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java similarity index 96% rename from src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java index b9ea5903..2d11bc4e 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBodyValue.java @@ -1,55 +1,55 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Converters; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.StringValue; -import java.io.IOException; -import java.nio.charset.Charset; -import okhttp3.MediaType; -import okhttp3.RequestBody; - -public class RequestBodyValue extends MapValue { - - private final RequestBody requestBody; - private final MediaType mediaType; - - public RequestBodyValue(RequestBody requestBody) { - this(requestBody, 0); - } - - protected RequestBodyValue(RequestBody requestBody, int methodsCount) { - super(4 + methodsCount); - this.requestBody = requestBody; - this.mediaType = requestBody.contentType(); - init(); - } - - public RequestBody getRequestBody() { - return requestBody; - } - - public MediaType getMediaType() { - return mediaType; - } - - private void init() { - set("getContentLength", Converters.voidToLong(() -> { - try { - return requestBody.contentLength(); - } catch (IOException ex) { - return -1; - } - })); - set("getType", Converters.voidToString(mediaType::type)); - set("getSubtype", Converters.voidToString(mediaType::subtype)); - set("getCharset", args -> { - Arguments.checkOrOr(0, 1, args.length); - if (args.length == 0) { - return new StringValue(mediaType.charset().name()); - } else { - return new StringValue(mediaType.charset(Charset.forName(args[0].asString())).name()); - } - }); - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.StringValue; +import java.io.IOException; +import java.nio.charset.Charset; +import okhttp3.MediaType; +import okhttp3.RequestBody; + +public class RequestBodyValue extends MapValue { + + private final RequestBody requestBody; + private final MediaType mediaType; + + public RequestBodyValue(RequestBody requestBody) { + this(requestBody, 0); + } + + protected RequestBodyValue(RequestBody requestBody, int methodsCount) { + super(4 + methodsCount); + this.requestBody = requestBody; + this.mediaType = requestBody.contentType(); + init(); + } + + public RequestBody getRequestBody() { + return requestBody; + } + + public MediaType getMediaType() { + return mediaType; + } + + private void init() { + set("getContentLength", Converters.voidToLong(() -> { + try { + return requestBody.contentLength(); + } catch (IOException ex) { + return -1; + } + })); + set("getType", Converters.voidToString(mediaType::type)); + set("getSubtype", Converters.voidToString(mediaType::subtype)); + set("getCharset", args -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + return new StringValue(mediaType.charset().name()); + } else { + return new StringValue(mediaType.charset(Charset.forName(args[0].asString())).name()); + } + }); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java index 51d4edf4..68aeeb45 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/RequestBuilderValue.java @@ -1,109 +1,109 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Converters.VoidToVoidFunction; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.MapValue; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; - -public class RequestBuilderValue extends MapValue { - - private final Request.Builder requestBuilder; - - public RequestBuilderValue() { - super(15); - requestBuilder = new Request.Builder(); - init(); - } - - public Request getRequest() { - return requestBuilder.build(); - } - - private void init() { - set("addHeader", args -> { - Arguments.check(2, args.length); - requestBuilder.addHeader(args[0].asString(), args[1].asString()); - return this; - }); - set("cacheControl", args -> { - Arguments.check(1, args.length); - // TODO - return this; - }); - set("delete", httpMethod(requestBuilder::delete, requestBuilder::delete)); - set("get", args -> { - requestBuilder.get(); - return this; - }); - set("head", args -> { - requestBuilder.head(); - return this; - }); - set("header", args -> { - Arguments.check(2, args.length); - requestBuilder.header(args[0].asString(), args[1].asString()); - return this; - }); - set("headers", args -> { - Arguments.check(1, args.length); - requestBuilder.headers(Values.getHeaders(args[0], " at first argument")); - return this; - }); - set("method", args -> { - Arguments.checkOrOr(1, 2, args.length); - final RequestBody body; - if (args.length == 1) { - body = null; - } else { - body = Values.getRequestBody(args[1], " at second argument"); - } - requestBuilder.method(args[0].asString(), body); - return this; - }); - set("newCall", args -> { - Arguments.check(1, args.length); - final OkHttpClient client = Values.getHttpClient(args[0], " at first argument"); - return new CallValue(client.newCall(getRequest())); - }); - set("patch", httpMethod(requestBuilder::patch)); - set("post", httpMethod(requestBuilder::post)); - set("put", httpMethod(requestBuilder::put)); - set("removeHeader", args -> { - Arguments.check(1, args.length); - requestBuilder.removeHeader(args[0].asString()); - return this; - }); - set("url", args -> { - Arguments.check(1, args.length); - requestBuilder.url(args[0].asString()); - return this; - }); - } - - private Function httpMethod(VoidToVoidFunction voidFunc, RequestBodyToVoidFunction bodyFunc) { - return (args) -> { - Arguments.checkOrOr(0, 1, args.length); - if (args.length == 0) { - voidFunc.apply(); - } else { - bodyFunc.apply(Values.getRequestBody(args[0], " at first argument")); - } - return this; - }; - } - - private Function httpMethod(RequestBodyToVoidFunction bodyFunc) { - return (args) -> { - Arguments.check(1, args.length); - bodyFunc.apply(Values.getRequestBody(args[0], " at first argument")); - return this; - }; - } - - private interface RequestBodyToVoidFunction { - void apply(RequestBody value); - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Converters.VoidToVoidFunction; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; + +public class RequestBuilderValue extends MapValue { + + private final Request.Builder requestBuilder; + + public RequestBuilderValue() { + super(15); + requestBuilder = new Request.Builder(); + init(); + } + + public Request getRequest() { + return requestBuilder.build(); + } + + private void init() { + set("addHeader", args -> { + Arguments.check(2, args.length); + requestBuilder.addHeader(args[0].asString(), args[1].asString()); + return this; + }); + set("cacheControl", args -> { + Arguments.check(1, args.length); + // TODO + return this; + }); + set("delete", httpMethod(requestBuilder::delete, requestBuilder::delete)); + set("get", args -> { + requestBuilder.get(); + return this; + }); + set("head", args -> { + requestBuilder.head(); + return this; + }); + set("header", args -> { + Arguments.check(2, args.length); + requestBuilder.header(args[0].asString(), args[1].asString()); + return this; + }); + set("headers", args -> { + Arguments.check(1, args.length); + requestBuilder.headers(Values.getHeaders(args[0], " at first argument")); + return this; + }); + set("method", args -> { + Arguments.checkOrOr(1, 2, args.length); + final RequestBody body; + if (args.length == 1) { + body = null; + } else { + body = Values.getRequestBody(args[1], " at second argument"); + } + requestBuilder.method(args[0].asString(), body); + return this; + }); + set("newCall", args -> { + Arguments.check(1, args.length); + final OkHttpClient client = Values.getHttpClient(args[0], " at first argument"); + return new CallValue(client.newCall(getRequest())); + }); + set("patch", httpMethod(requestBuilder::patch)); + set("post", httpMethod(requestBuilder::post)); + set("put", httpMethod(requestBuilder::put)); + set("removeHeader", args -> { + Arguments.check(1, args.length); + requestBuilder.removeHeader(args[0].asString()); + return this; + }); + set("url", args -> { + Arguments.check(1, args.length); + requestBuilder.url(args[0].asString()); + return this; + }); + } + + private Function httpMethod(VoidToVoidFunction voidFunc, RequestBodyToVoidFunction bodyFunc) { + return (args) -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + voidFunc.apply(); + } else { + bodyFunc.apply(Values.getRequestBody(args[0], " at first argument")); + } + return this; + }; + } + + private Function httpMethod(RequestBodyToVoidFunction bodyFunc) { + return (args) -> { + Arguments.check(1, args.length); + bodyFunc.apply(Values.getRequestBody(args[0], " at first argument")); + return this; + }; + } + + private interface RequestBodyToVoidFunction { + void apply(RequestBody value); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java index faa08451..79dc95e5 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java @@ -1,56 +1,56 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Converters; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; -import java.io.IOException; -import okhttp3.ResponseBody; -import okio.BufferedSink; -import okio.Okio; - -public class ResponseBodyValue extends MapValue { - - private final ResponseBody responseBody; - - public ResponseBodyValue(ResponseBody response) { - super(8); - this.responseBody = response; - init(); - } - - private void init() { - set("bytes", args -> { - try { - return ArrayValue.of(responseBody.bytes()); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - set("close", Converters.voidToVoid(responseBody::close)); - set("contentLength", Converters.voidToLong(responseBody::contentLength)); - set("contentType", args -> new StringValue(responseBody.contentType().toString())); - set("string", args -> { - try { - return new StringValue(responseBody.string()); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - set("file", args -> { - Arguments.check(1, args.length); - try { - BufferedSink sink = Okio.buffer(Okio.sink(Console.fileInstance(args[0].asString()))); - sink.writeAll(responseBody.source()); - sink.close(); - return NumberValue.ONE; - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import java.io.IOException; +import okhttp3.ResponseBody; +import okio.BufferedSink; +import okio.Okio; + +public class ResponseBodyValue extends MapValue { + + private final ResponseBody responseBody; + + public ResponseBodyValue(ResponseBody response) { + super(8); + this.responseBody = response; + init(); + } + + private void init() { + set("bytes", args -> { + try { + return ArrayValue.of(responseBody.bytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + set("close", Converters.voidToVoid(responseBody::close)); + set("contentLength", Converters.voidToLong(responseBody::contentLength)); + set("contentType", args -> new StringValue(responseBody.contentType().toString())); + set("string", args -> { + try { + return new StringValue(responseBody.string()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + set("file", args -> { + Arguments.check(1, args.length); + try { + BufferedSink sink = Okio.buffer(Okio.sink(Console.fileInstance(args[0].asString()))); + sink.writeAll(responseBody.source()); + sink.close(); + return NumberValue.ONE; + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java similarity index 97% rename from src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java index aee83fbb..de57784a 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseValue.java @@ -1,52 +1,52 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Converters; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.StringValue; -import java.util.List; -import java.util.Map; -import okhttp3.Response; - -public class ResponseValue extends MapValue { - - private final Response response; - - public ResponseValue(Response response) { - super(15); - this.response = response; - init(); - } - - private void init() { - set("body", args -> new ResponseBodyValue(response.body())); - set("cacheResponse", args -> new ResponseValue(response.cacheResponse())); - set("code", Converters.voidToInt(response::code)); - set("close", Converters.voidToVoid(response::close)); - set("header", args -> { - Arguments.checkOrOr(1, 2, args.length); - final String defaultValue = (args.length == 1) ? null : args[1].asString(); - return new StringValue(response.header(args[0].asString(), defaultValue)); - }); - set("headers", args -> { - Arguments.checkOrOr(0, 1, args.length); - if (args.length == 0) { - final Map> headers = response.headers().toMultimap(); - final MapValue result = new MapValue(headers.size()); - for (Map.Entry> entry : headers.entrySet()) { - result.set(entry.getKey(), ArrayValue.of(entry.getValue().toArray(new String[0]))); - } - return result; - } else { - return ArrayValue.of(response.headers(args[0].asString()).toArray(new String[0])); - } - }); - set("message", Converters.voidToString(response::message)); - set("networkResponse", args -> new ResponseValue(response.networkResponse())); - set("priorResponse", args -> new ResponseValue(response.priorResponse())); - set("receivedResponseAtMillis", Converters.voidToLong(response::receivedResponseAtMillis)); - set("sentRequestAtMillis", Converters.voidToLong(response::sentRequestAtMillis)); - } - -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.StringValue; +import java.util.List; +import java.util.Map; +import okhttp3.Response; + +public class ResponseValue extends MapValue { + + private final Response response; + + public ResponseValue(Response response) { + super(15); + this.response = response; + init(); + } + + private void init() { + set("body", args -> new ResponseBodyValue(response.body())); + set("cacheResponse", args -> new ResponseValue(response.cacheResponse())); + set("code", Converters.voidToInt(response::code)); + set("close", Converters.voidToVoid(response::close)); + set("header", args -> { + Arguments.checkOrOr(1, 2, args.length); + final String defaultValue = (args.length == 1) ? null : args[1].asString(); + return new StringValue(response.header(args[0].asString(), defaultValue)); + }); + set("headers", args -> { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + final Map> headers = response.headers().toMultimap(); + final MapValue result = new MapValue(headers.size()); + for (Map.Entry> entry : headers.entrySet()) { + result.set(entry.getKey(), ArrayValue.of(entry.getValue().toArray(new String[0]))); + } + return result; + } else { + return ArrayValue.of(response.headers(args[0].asString()).toArray(new String[0])); + } + }); + set("message", Converters.voidToString(response::message)); + set("networkResponse", args -> new ResponseValue(response.networkResponse())); + set("priorResponse", args -> new ResponseValue(response.priorResponse())); + set("receivedResponseAtMillis", Converters.voidToLong(response::receivedResponseAtMillis)); + set("sentRequestAtMillis", Converters.voidToLong(response::sentRequestAtMillis)); + } + +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/Values.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/Values.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/okhttp/Values.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/Values.java diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java similarity index 96% rename from src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java index 9af00d06..b937af4c 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/WebSocketValue.java @@ -1,46 +1,46 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Converters; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.ValueUtils; -import okhttp3.WebSocket; -import okio.ByteString; - -public class WebSocketValue extends MapValue { - - private final WebSocket ws; - - protected WebSocketValue(WebSocket ws) { - super(4); - this.ws = ws; - init(); - } - - public WebSocket getWebSocket() { - return ws; - } - - private void init() { - set("cancel", Converters.voidToVoid(ws::cancel)); - set("close", args -> { - Arguments.checkOrOr(1, 2, args.length); - final String reason = (args.length == 2) ? args[1].asString() : null; - return NumberValue.fromBoolean(ws.close(args[0].asInt(), reason)); - }); - set("queueSize", Converters.voidToLong(ws::queueSize)); - set("send", args -> { - Arguments.check(1, args.length); - final boolean result; - if (args[0].type() == Types.ARRAY) { - result = ws.send(ByteString.of( ValueUtils.toByteArray(((ArrayValue) args[0])) )); - } else { - result = ws.send(args[0].asString()); - } - return NumberValue.fromBoolean(result); - }); - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Converters; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.ValueUtils; +import okhttp3.WebSocket; +import okio.ByteString; + +public class WebSocketValue extends MapValue { + + private final WebSocket ws; + + protected WebSocketValue(WebSocket ws) { + super(4); + this.ws = ws; + init(); + } + + public WebSocket getWebSocket() { + return ws; + } + + private void init() { + set("cancel", Converters.voidToVoid(ws::cancel)); + set("close", args -> { + Arguments.checkOrOr(1, 2, args.length); + final String reason = (args.length == 2) ? args[1].asString() : null; + return NumberValue.fromBoolean(ws.close(args[0].asInt(), reason)); + }); + set("queueSize", Converters.voidToLong(ws::queueSize)); + set("send", args -> { + Arguments.check(1, args.length); + final boolean result; + if (args[0].type() == Types.ARRAY) { + result = ws.send(ByteString.of( ValueUtils.toByteArray(((ArrayValue) args[0])) )); + } else { + result = ws.send(args[0].asString()); + } + return NumberValue.fromBoolean(result); + }); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java similarity index 97% rename from src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java index cf65b181..d4066dad 100644 --- a/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java @@ -1,81 +1,81 @@ -package com.annimon.ownlang.modules.okhttp; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.ValueUtils; -import com.annimon.ownlang.lib.Variables; -import com.annimon.ownlang.modules.Module; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.OkHttpClient; -import okhttp3.RequestBody; - -public final class okhttp implements Module { - - private static final HttpClientValue defaultClient = new HttpClientValue(new OkHttpClient()); - - public static void initConstants() { - MapValue requestBody = new MapValue(5); - requestBody.set("bytes", args -> { - Arguments.checkOrOr(2, 4, args.length); - if (args[1].type() != Types.ARRAY) { - throw new TypeException("Array of bytes expected at second argument"); - } - final byte[] bytes = ValueUtils.toByteArray((ArrayValue) args[1]); - final int offset; - final int bytesCount; - if (args.length == 2) { - offset = 0; - bytesCount = bytes.length; - } else { - offset = args[2].asInt(); - bytesCount = args[3].asInt(); - } - return new RequestBodyValue(RequestBody.create( - MediaType.parse(args[0].asString()), - bytes, offset, bytesCount - )); - }); - requestBody.set("file", args -> { - Arguments.check(2, args.length); - return new RequestBodyValue(RequestBody.create( - MediaType.parse(args[0].asString()), - Console.fileInstance(args[1].asString()) - )); - }); - requestBody.set("string", args -> { - Arguments.check(2, args.length); - return new RequestBodyValue(RequestBody.create( - MediaType.parse(args[0].asString()), - args[1].asString() - )); - }); - Variables.define("RequestBody", requestBody); - - - MapValue multipartBody = new MapValue(10); - multipartBody.set("ALTERNATIVE", new StringValue(MultipartBody.ALTERNATIVE.toString())); - multipartBody.set("DIGEST", new StringValue(MultipartBody.DIGEST.toString())); - multipartBody.set("FORM", new StringValue(MultipartBody.FORM.toString())); - multipartBody.set("MIXED", new StringValue(MultipartBody.MIXED.toString())); - multipartBody.set("PARALLEL", new StringValue(MultipartBody.PARALLEL.toString())); - multipartBody.set("builder", args -> new MultipartBodyBuilderValue()); - Variables.define("MultipartBody", multipartBody); - - - MapValue okhttp = new MapValue(5); - okhttp.set("client", defaultClient); - okhttp.set("request", args -> new RequestBuilderValue()); - Variables.define("okhttp", okhttp); - } - - @Override - public void init() { - initConstants(); - } -} +package com.annimon.ownlang.modules.okhttp; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.ValueUtils; +import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.modules.Module; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; + +public final class okhttp implements Module { + + private static final HttpClientValue defaultClient = new HttpClientValue(new OkHttpClient()); + + public static void initConstants() { + MapValue requestBody = new MapValue(5); + requestBody.set("bytes", args -> { + Arguments.checkOrOr(2, 4, args.length); + if (args[1].type() != Types.ARRAY) { + throw new TypeException("Array of bytes expected at second argument"); + } + final byte[] bytes = ValueUtils.toByteArray((ArrayValue) args[1]); + final int offset; + final int bytesCount; + if (args.length == 2) { + offset = 0; + bytesCount = bytes.length; + } else { + offset = args[2].asInt(); + bytesCount = args[3].asInt(); + } + return new RequestBodyValue(RequestBody.create( + MediaType.parse(args[0].asString()), + bytes, offset, bytesCount + )); + }); + requestBody.set("file", args -> { + Arguments.check(2, args.length); + return new RequestBodyValue(RequestBody.create( + MediaType.parse(args[0].asString()), + Console.fileInstance(args[1].asString()) + )); + }); + requestBody.set("string", args -> { + Arguments.check(2, args.length); + return new RequestBodyValue(RequestBody.create( + MediaType.parse(args[0].asString()), + args[1].asString() + )); + }); + Variables.define("RequestBody", requestBody); + + + MapValue multipartBody = new MapValue(10); + multipartBody.set("ALTERNATIVE", new StringValue(MultipartBody.ALTERNATIVE.toString())); + multipartBody.set("DIGEST", new StringValue(MultipartBody.DIGEST.toString())); + multipartBody.set("FORM", new StringValue(MultipartBody.FORM.toString())); + multipartBody.set("MIXED", new StringValue(MultipartBody.MIXED.toString())); + multipartBody.set("PARALLEL", new StringValue(MultipartBody.PARALLEL.toString())); + multipartBody.set("builder", args -> new MultipartBodyBuilderValue()); + Variables.define("MultipartBody", multipartBody); + + + MapValue okhttp = new MapValue(5); + okhttp.set("client", defaultClient); + okhttp.set("request", args -> new RequestBuilderValue()); + Variables.define("okhttp", okhttp); + } + + @Override + public void init() { + initConstants(); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/ounit/ounit.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java diff --git a/src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/regex/MatcherValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/regex/PatternValue.java diff --git a/src/main/java/com/annimon/ownlang/modules/regex/regex.java b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/regex/regex.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/robot/robot.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java diff --git a/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java diff --git a/src/main/java/com/annimon/ownlang/modules/socket/socket.java b/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/socket/socket.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java similarity index 85% rename from src/main/java/com/annimon/ownlang/modules/std/std.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java index eca4d2bf..272cdc44 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.modules.std; -import com.annimon.ownlang.Main; +import com.annimon.ownlang.Shared; +import com.annimon.ownlang.Version; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; @@ -13,17 +14,17 @@ public final class std implements Module { public static void initConstants() { MapValue ownlang = new MapValue(5); ownlang.set("PLATFORM", new StringValue("desktop")); - ownlang.set("VERSION", new StringValue(Main.VERSION)); - ownlang.set("VERSION_MAJOR", NumberValue.of(Main.VERSION_MAJOR)); - ownlang.set("VERSION_MINOR", NumberValue.of(Main.VERSION_MINOR)); - ownlang.set("VERSION_PATCH", NumberValue.of(Main.VERSION_PATCH)); + ownlang.set("VERSION", new StringValue(Version.VERSION)); + ownlang.set("VERSION_MAJOR", NumberValue.of(Version.VERSION_MAJOR)); + ownlang.set("VERSION_MINOR", NumberValue.of(Version.VERSION_MINOR)); + ownlang.set("VERSION_PATCH", NumberValue.of(Version.VERSION_PATCH)); Variables.define("OwnLang", ownlang); } @Override public void init() { initConstants(); - Variables.define("ARGS", ArrayValue.of(Main.getOwnlangArgs())); // is not constant + Variables.define("ARGS", ArrayValue.of(Shared.getOwnlangArgs())); // is not constant Functions.set("echo", new std_echo()); Functions.set("readln", new std_readln()); Functions.set("length", new std_length()); diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_charat.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_charat.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_default.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_default.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_echo.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_echo.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_indexof.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_join.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_join.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_length.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java similarity index 80% rename from src/main/java/com/annimon/ownlang/modules/std/std_length.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java index bdd7a210..4a7d0b75 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/std_length.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java @@ -22,11 +22,7 @@ public Value execute(Value... args) { break; case Types.FUNCTION: final Function func = ((FunctionValue) val).getValue(); - if (func instanceof UserDefinedFunction) { - length = ((UserDefinedFunction) func).getArgsCount(); - } else { - length = 0; - } + length = func.getArgsCount(); break; default: length = 0; diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_newarray.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_rand.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_rand.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_range.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_range.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_readln.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_readln.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_replace.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_replace.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_sleep.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_sort.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_sort.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_split.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_split.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_substring.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_substring.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_sync.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_sync.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_thread.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_thread.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_time.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_time.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_tochar.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_trim.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_trim.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_try.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/std/std_try.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java diff --git a/src/main/java/com/annimon/ownlang/modules/types/types.java b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/types/types.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/yaml/yaml.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java diff --git a/src/main/java/com/annimon/ownlang/modules/zip/zip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/zip/zip.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java diff --git a/src/main/java/com/annimon/ownlang/Console.java b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java similarity index 100% rename from src/main/java/com/annimon/ownlang/Console.java rename to ownlang-core/src/main/java/com/annimon/ownlang/Console.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/Shared.java b/ownlang-core/src/main/java/com/annimon/ownlang/Shared.java new file mode 100644 index 00000000..d4f270f9 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/Shared.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang; + +public final class Shared { + private static String[] ownlangArgs = new String[0]; + + public static String[] getOwnlangArgs() { + return ownlangArgs; + } + + public static void setOwnlangArgs(String[] args) { + ownlangArgs = args; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/Version.java b/ownlang-core/src/main/java/com/annimon/ownlang/Version.java new file mode 100644 index 00000000..aa476e84 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/Version.java @@ -0,0 +1,11 @@ +package com.annimon.ownlang; + +public final class Version { + public static final int VERSION_MAJOR = 2; + public static final int VERSION_MINOR = 0; + public static final int VERSION_PATCH = 0; + + public static final String VERSION = VERSION_MAJOR + "." + + VERSION_MINOR + "." + VERSION_PATCH + + "_" + Gen.BUILD_DATE; +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/TypeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/TypeException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java diff --git a/src/main/java/com/annimon/ownlang/lib/Arguments.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Arguments.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Arguments.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Arguments.java diff --git a/src/main/java/com/annimon/ownlang/lib/ArrayValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/ArrayValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java diff --git a/src/main/java/com/annimon/ownlang/lib/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/CallStack.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java diff --git a/src/main/java/com/annimon/ownlang/lib/Converters.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Converters.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java diff --git a/src/main/java/com/annimon/ownlang/lib/Function.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Function.java similarity index 68% rename from src/main/java/com/annimon/ownlang/lib/Function.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Function.java index 07cb0d97..78c46ab3 100644 --- a/src/main/java/com/annimon/ownlang/lib/Function.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Function.java @@ -7,4 +7,8 @@ public interface Function { Value execute(Value... args); + + default int getArgsCount() { + return 0; + } } diff --git a/src/main/java/com/annimon/ownlang/lib/FunctionValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/FunctionValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/FunctionValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/FunctionValue.java diff --git a/src/main/java/com/annimon/ownlang/lib/Functions.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Functions.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java diff --git a/src/main/java/com/annimon/ownlang/lib/Instantiable.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Instantiable.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Instantiable.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Instantiable.java diff --git a/src/main/java/com/annimon/ownlang/lib/MapValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/MapValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java diff --git a/src/main/java/com/annimon/ownlang/lib/NumberValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/NumberValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java diff --git a/src/main/java/com/annimon/ownlang/lib/StringValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/StringValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java diff --git a/src/main/java/com/annimon/ownlang/lib/Types.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Types.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Types.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Types.java diff --git a/src/main/java/com/annimon/ownlang/lib/Value.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Value.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/ValueUtils.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java diff --git a/src/main/java/com/annimon/ownlang/lib/Variables.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Variables.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java diff --git a/src/main/java/com/annimon/ownlang/modules/Module.java b/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java similarity index 100% rename from src/main/java/com/annimon/ownlang/modules/Module.java rename to ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java diff --git a/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java similarity index 100% rename from src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java rename to ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java diff --git a/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java similarity index 100% rename from src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java rename to ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java diff --git a/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java similarity index 100% rename from src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java rename to ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java diff --git a/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java similarity index 93% rename from src/main/java/com/annimon/ownlang/Main.java rename to ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 470422da..499edc72 100644 --- a/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -22,19 +22,6 @@ */ public final class Main { - public static final int VERSION_MAJOR = 1; - public static final int VERSION_MINOR = 5; - public static final int VERSION_PATCH = 0; - public static final String VERSION = VERSION_MAJOR + "." - + VERSION_MINOR + "." + VERSION_PATCH - + "_" + Gen.BUILD_DATE; - - private static String[] ownlangArgs = new String[0]; - - public static String[] getOwnlangArgs() { - return ownlangArgs; - } - public static void main(String[] args) throws IOException { if (args.length == 0) { try { @@ -104,6 +91,7 @@ public static void main(String[] args) throws IOException { case "--sandbox": createOwnLangArgs(args, i + 1); + final String[] ownlangArgs = Shared.getOwnlangArgs(); String[] newArgs = new String[ownlangArgs.length]; System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length); Sandbox.main(newArgs); @@ -133,7 +121,7 @@ private static void runDefault() throws IOException { } private static void printUsage() { - System.out.println("OwnLang version " + VERSION + "\n\n" + + System.out.println("OwnLang version " + Version.VERSION + "\n\n" + "Usage: ownlang [options]\n" + " options:\n" + " -f, --file [input] Run program file. Required.\n" + @@ -148,8 +136,9 @@ private static void printUsage() { private static void createOwnLangArgs(String[] javaArgs, int index) { if (index >= javaArgs.length) return; - ownlangArgs = new String[javaArgs.length - index]; + final String[] ownlangArgs = new String[javaArgs.length - index]; System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); + Shared.setOwnlangArgs(ownlangArgs); } private static void run(String input, RunOptions options) { diff --git a/src/main/java/com/annimon/ownlang/exceptions/LexerException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/LexerException.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/ParseException.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/StoppedException.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java diff --git a/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java similarity index 100% rename from src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java diff --git a/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java diff --git a/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java diff --git a/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/ClassMethod.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java diff --git a/src/main/java/com/annimon/ownlang/lib/Classes.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/Classes.java similarity index 100% rename from src/main/java/com/annimon/ownlang/lib/Classes.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/lib/Classes.java diff --git a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java similarity index 99% rename from src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index 6b0ba780..26be0845 100644 --- a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -20,10 +20,11 @@ public UserDefinedFunction(Arguments arguments, Statement body) { this.body = body; } + @Override public int getArgsCount() { return arguments.size(); } - + public String getArgsName(int index) { if (index < 0 || index >= getArgsCount()) return ""; return arguments.get(index).getName(); diff --git a/src/main/java/com/annimon/ownlang/parser/Beautifier.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/Beautifier.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/Lexer.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java diff --git a/src/main/java/com/annimon/ownlang/parser/Linter.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/Linter.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java diff --git a/src/main/java/com/annimon/ownlang/parser/Optimizer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Optimizer.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/Optimizer.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Optimizer.java diff --git a/src/main/java/com/annimon/ownlang/parser/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ParseError.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java diff --git a/src/main/java/com/annimon/ownlang/parser/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ParseErrors.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/Parser.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java diff --git a/src/main/java/com/annimon/ownlang/parser/SourceLoader.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/SourceLoader.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java diff --git a/src/main/java/com/annimon/ownlang/parser/Token.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/Token.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java diff --git a/src/main/java/com/annimon/ownlang/parser/TokenType.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/TokenType.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Accessible.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Accessible.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Accessible.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Accessible.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Argument.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Argument.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Arguments.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Expression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Expression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Expression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Expression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Node.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Node.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ResultVisitor.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Statement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Statement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/Visitor.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Visitor.java diff --git a/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java diff --git a/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java diff --git a/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java diff --git a/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java diff --git a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ExpressionSimplification.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/SummaryOptimization.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java similarity index 100% rename from src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java diff --git a/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/LexerTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ParserTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ProgramsTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java diff --git a/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/OperatorExpressionTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java diff --git a/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java similarity index 100% rename from src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java diff --git a/src/test/java/interop/Data.java b/ownlang-parser/src/test/java/interop/Data.java similarity index 100% rename from src/test/java/interop/Data.java rename to ownlang-parser/src/test/java/interop/Data.java diff --git a/src/test/resources/expressions/assignmentExpression.own b/ownlang-parser/src/test/resources/expressions/assignmentExpression.own similarity index 100% rename from src/test/resources/expressions/assignmentExpression.own rename to ownlang-parser/src/test/resources/expressions/assignmentExpression.own diff --git a/src/test/resources/expressions/binaryExpressionOnNumbers.own b/ownlang-parser/src/test/resources/expressions/binaryExpressionOnNumbers.own similarity index 100% rename from src/test/resources/expressions/binaryExpressionOnNumbers.own rename to ownlang-parser/src/test/resources/expressions/binaryExpressionOnNumbers.own diff --git a/src/test/resources/expressions/binaryExpressionOnStrings.own b/ownlang-parser/src/test/resources/expressions/binaryExpressionOnStrings.own similarity index 100% rename from src/test/resources/expressions/binaryExpressionOnStrings.own rename to ownlang-parser/src/test/resources/expressions/binaryExpressionOnStrings.own diff --git a/src/test/resources/expressions/binaryUnaryExpr.own b/ownlang-parser/src/test/resources/expressions/binaryUnaryExpr.own similarity index 100% rename from src/test/resources/expressions/binaryUnaryExpr.own rename to ownlang-parser/src/test/resources/expressions/binaryUnaryExpr.own diff --git a/src/test/resources/expressions/foreachKeyValue.own b/ownlang-parser/src/test/resources/expressions/foreachKeyValue.own similarity index 100% rename from src/test/resources/expressions/foreachKeyValue.own rename to ownlang-parser/src/test/resources/expressions/foreachKeyValue.own diff --git a/src/test/resources/expressions/foreachValue.own b/ownlang-parser/src/test/resources/expressions/foreachValue.own similarity index 100% rename from src/test/resources/expressions/foreachValue.own rename to ownlang-parser/src/test/resources/expressions/foreachValue.own diff --git a/src/test/resources/expressions/functionReference.own b/ownlang-parser/src/test/resources/expressions/functionReference.own similarity index 100% rename from src/test/resources/expressions/functionReference.own rename to ownlang-parser/src/test/resources/expressions/functionReference.own diff --git a/src/test/resources/expressions/include.own b/ownlang-parser/src/test/resources/expressions/include.own similarity index 100% rename from src/test/resources/expressions/include.own rename to ownlang-parser/src/test/resources/expressions/include.own diff --git a/src/test/resources/expressions/includeClass.own.txt b/ownlang-parser/src/test/resources/expressions/includeClass.own.txt similarity index 100% rename from src/test/resources/expressions/includeClass.own.txt rename to ownlang-parser/src/test/resources/expressions/includeClass.own.txt diff --git a/src/test/resources/expressions/includeParseErrorSource.own.txt b/ownlang-parser/src/test/resources/expressions/includeParseErrorSource.own.txt similarity index 100% rename from src/test/resources/expressions/includeParseErrorSource.own.txt rename to ownlang-parser/src/test/resources/expressions/includeParseErrorSource.own.txt diff --git a/src/test/resources/expressions/nullCoalesce.own b/ownlang-parser/src/test/resources/expressions/nullCoalesce.own similarity index 100% rename from src/test/resources/expressions/nullCoalesce.own rename to ownlang-parser/src/test/resources/expressions/nullCoalesce.own diff --git a/src/test/resources/expressions/unaryExpressionOnStrings.own b/ownlang-parser/src/test/resources/expressions/unaryExpressionOnStrings.own similarity index 100% rename from src/test/resources/expressions/unaryExpressionOnStrings.own rename to ownlang-parser/src/test/resources/expressions/unaryExpressionOnStrings.own diff --git a/src/test/resources/expressions/varFuncSameName.own b/ownlang-parser/src/test/resources/expressions/varFuncSameName.own similarity index 100% rename from src/test/resources/expressions/varFuncSameName.own rename to ownlang-parser/src/test/resources/expressions/varFuncSameName.own diff --git a/src/test/resources/modules/base64/base64.own b/ownlang-parser/src/test/resources/modules/base64/base64.own similarity index 100% rename from src/test/resources/modules/base64/base64.own rename to ownlang-parser/src/test/resources/modules/base64/base64.own diff --git a/src/test/resources/modules/date/compareDates.own b/ownlang-parser/src/test/resources/modules/date/compareDates.own similarity index 100% rename from src/test/resources/modules/date/compareDates.own rename to ownlang-parser/src/test/resources/modules/date/compareDates.own diff --git a/src/test/resources/modules/date/dateFormat.own b/ownlang-parser/src/test/resources/modules/date/dateFormat.own similarity index 100% rename from src/test/resources/modules/date/dateFormat.own rename to ownlang-parser/src/test/resources/modules/date/dateFormat.own diff --git a/src/test/resources/modules/date/dateParse.own b/ownlang-parser/src/test/resources/modules/date/dateParse.own similarity index 100% rename from src/test/resources/modules/date/dateParse.own rename to ownlang-parser/src/test/resources/modules/date/dateParse.own diff --git a/src/test/resources/modules/date/newDate.own b/ownlang-parser/src/test/resources/modules/date/newDate.own similarity index 100% rename from src/test/resources/modules/date/newDate.own rename to ownlang-parser/src/test/resources/modules/date/newDate.own diff --git a/src/test/resources/modules/files/files.own b/ownlang-parser/src/test/resources/modules/files/files.own similarity index 100% rename from src/test/resources/modules/files/files.own rename to ownlang-parser/src/test/resources/modules/files/files.own diff --git a/src/test/resources/modules/functional/chain.own b/ownlang-parser/src/test/resources/modules/functional/chain.own similarity index 100% rename from src/test/resources/modules/functional/chain.own rename to ownlang-parser/src/test/resources/modules/functional/chain.own diff --git a/src/test/resources/modules/functional/foreach.own b/ownlang-parser/src/test/resources/modules/functional/foreach.own similarity index 100% rename from src/test/resources/modules/functional/foreach.own rename to ownlang-parser/src/test/resources/modules/functional/foreach.own diff --git a/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own similarity index 100% rename from src/test/resources/modules/functional/stream.own rename to ownlang-parser/src/test/resources/modules/functional/stream.own diff --git a/src/test/resources/modules/gzip/gzipBytes.own b/ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own similarity index 100% rename from src/test/resources/modules/gzip/gzipBytes.own rename to ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own diff --git a/src/test/resources/modules/java/classes.own b/ownlang-parser/src/test/resources/modules/java/classes.own similarity index 100% rename from src/test/resources/modules/java/classes.own rename to ownlang-parser/src/test/resources/modules/java/classes.own diff --git a/src/test/resources/modules/regex/match.own b/ownlang-parser/src/test/resources/modules/regex/match.own similarity index 100% rename from src/test/resources/modules/regex/match.own rename to ownlang-parser/src/test/resources/modules/regex/match.own diff --git a/src/test/resources/modules/regex/replaceCallback.own b/ownlang-parser/src/test/resources/modules/regex/replaceCallback.own similarity index 100% rename from src/test/resources/modules/regex/replaceCallback.own rename to ownlang-parser/src/test/resources/modules/regex/replaceCallback.own diff --git a/src/test/resources/modules/std/arraySplice.own b/ownlang-parser/src/test/resources/modules/std/arraySplice.own similarity index 100% rename from src/test/resources/modules/std/arraySplice.own rename to ownlang-parser/src/test/resources/modules/std/arraySplice.own diff --git a/src/test/resources/modules/std/default.own b/ownlang-parser/src/test/resources/modules/std/default.own similarity index 100% rename from src/test/resources/modules/std/default.own rename to ownlang-parser/src/test/resources/modules/std/default.own diff --git a/src/test/resources/modules/std/getBytes.own b/ownlang-parser/src/test/resources/modules/std/getBytes.own similarity index 100% rename from src/test/resources/modules/std/getBytes.own rename to ownlang-parser/src/test/resources/modules/std/getBytes.own diff --git a/src/test/resources/modules/std/indexOf.own b/ownlang-parser/src/test/resources/modules/std/indexOf.own similarity index 100% rename from src/test/resources/modules/std/indexOf.own rename to ownlang-parser/src/test/resources/modules/std/indexOf.own diff --git a/src/test/resources/modules/std/lastIndexOf.own b/ownlang-parser/src/test/resources/modules/std/lastIndexOf.own similarity index 100% rename from src/test/resources/modules/std/lastIndexOf.own rename to ownlang-parser/src/test/resources/modules/std/lastIndexOf.own diff --git a/src/test/resources/modules/std/parseInt.own b/ownlang-parser/src/test/resources/modules/std/parseInt.own similarity index 100% rename from src/test/resources/modules/std/parseInt.own rename to ownlang-parser/src/test/resources/modules/std/parseInt.own diff --git a/src/test/resources/modules/std/parseLong.own b/ownlang-parser/src/test/resources/modules/std/parseLong.own similarity index 100% rename from src/test/resources/modules/std/parseLong.own rename to ownlang-parser/src/test/resources/modules/std/parseLong.own diff --git a/src/test/resources/modules/std/range.own b/ownlang-parser/src/test/resources/modules/std/range.own similarity index 100% rename from src/test/resources/modules/std/range.own rename to ownlang-parser/src/test/resources/modules/std/range.own diff --git a/src/test/resources/modules/std/stringFromBytes.own b/ownlang-parser/src/test/resources/modules/std/stringFromBytes.own similarity index 100% rename from src/test/resources/modules/std/stringFromBytes.own rename to ownlang-parser/src/test/resources/modules/std/stringFromBytes.own diff --git a/src/test/resources/modules/std/stripMargin.own b/ownlang-parser/src/test/resources/modules/std/stripMargin.own similarity index 100% rename from src/test/resources/modules/std/stripMargin.own rename to ownlang-parser/src/test/resources/modules/std/stripMargin.own diff --git a/src/test/resources/modules/std/toHexString.own b/ownlang-parser/src/test/resources/modules/std/toHexString.own similarity index 100% rename from src/test/resources/modules/std/toHexString.own rename to ownlang-parser/src/test/resources/modules/std/toHexString.own diff --git a/src/test/resources/modules/std/try.own b/ownlang-parser/src/test/resources/modules/std/try.own similarity index 100% rename from src/test/resources/modules/std/try.own rename to ownlang-parser/src/test/resources/modules/std/try.own diff --git a/src/test/resources/modules/yaml/yamldecode.own b/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own similarity index 100% rename from src/test/resources/modules/yaml/yamldecode.own rename to ownlang-parser/src/test/resources/modules/yaml/yamldecode.own diff --git a/src/test/resources/modules/yaml/yamlencode.own b/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own similarity index 100% rename from src/test/resources/modules/yaml/yamlencode.own rename to ownlang-parser/src/test/resources/modules/yaml/yamlencode.own diff --git a/src/test/resources/other/arrayFunctions.own b/ownlang-parser/src/test/resources/other/arrayFunctions.own similarity index 100% rename from src/test/resources/other/arrayFunctions.own rename to ownlang-parser/src/test/resources/other/arrayFunctions.own diff --git a/src/test/resources/other/functionChain.own b/ownlang-parser/src/test/resources/other/functionChain.own similarity index 100% rename from src/test/resources/other/functionChain.own rename to ownlang-parser/src/test/resources/other/functionChain.own diff --git a/src/test/resources/other/recursion.own b/ownlang-parser/src/test/resources/other/recursion.own similarity index 100% rename from src/test/resources/other/recursion.own rename to ownlang-parser/src/test/resources/other/recursion.own diff --git a/src/test/resources/other/scope.own b/ownlang-parser/src/test/resources/other/scope.own similarity index 100% rename from src/test/resources/other/scope.own rename to ownlang-parser/src/test/resources/other/scope.own diff --git a/src/test/resources/other/stringFunctions.own b/ownlang-parser/src/test/resources/other/stringFunctions.own similarity index 100% rename from src/test/resources/other/stringFunctions.own rename to ownlang-parser/src/test/resources/other/stringFunctions.own diff --git a/src/test/resources/other/types.own b/ownlang-parser/src/test/resources/other/types.own similarity index 100% rename from src/test/resources/other/types.own rename to ownlang-parser/src/test/resources/other/types.own diff --git a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java diff --git a/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java diff --git a/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java similarity index 98% rename from src/main/java/com/annimon/ownlang/utils/Repl.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 2c06f94e..89652968 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.utils; import com.annimon.ownlang.Console; -import com.annimon.ownlang.Main; +import com.annimon.ownlang.Version; import com.annimon.ownlang.exceptions.LexerException; import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.lib.Functions; @@ -39,7 +39,7 @@ public final class Repl { private static final Token PRINTLN_TOKEN = new Token(TokenType.PRINTLN, "", 0, 0); public static void main(String[] args) { - System.out.println("Welcome to OwnLang " + Main.VERSION + " REPL"); + System.out.println("Welcome to OwnLang " + Version.VERSION + " REPL"); printHelp(false); final BlockStatement history = new BlockStatement(); diff --git a/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/Sandbox.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java diff --git a/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java diff --git a/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java diff --git a/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java diff --git a/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java diff --git a/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java similarity index 100% rename from src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java From 0cdbbc1e8cb4646e815cc8834ddb0e8e8f3a386e Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 27 Aug 2023 14:59:38 +0300 Subject: [PATCH 290/448] Use separate dependency versions declaration --- build.gradle | 21 ++++++++++++++++++--- modules/main/build.gradle | 10 +++++----- ownlang-core/build.gradle | 4 ++-- ownlang-desktop/build.gradle | 3 ++- ownlang-parser/build.gradle | 11 ++++++----- ownlang-utils/build.gradle | 8 ++++---- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 50fdc929..1c08a8a6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,27 @@ +ext { + versions = [ + json: '20230227', // org.json:json + snakeyaml: '1.20', // org.yaml:snakeyaml + okhttp: '3.8.1', // com.squareup.okhttp3:okhttp + socket: '1.0.2', // io.socket:socket.io-client + jline: '2.14.5', // jline:jline + + junit: '5.9.2', // org.junit:junit-bom + jmh: '1.37' // org.openjdk.jmh:jmh-core + ] +} + allprojects { repositories { mavenCentral() } gradle.projectsEvaluated { - tasks.withType(JavaCompile) { - [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + tasks.withType(JavaCompile).tap { + configureEach { + [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } } } } diff --git a/modules/main/build.gradle b/modules/main/build.gradle index f4e3e044..ac3fdd02 100644 --- a/modules/main/build.gradle +++ b/modules/main/build.gradle @@ -9,14 +9,14 @@ version = '2.0-SNAPSHOT' dependencies { api project(":ownlang-core") - implementation 'com.squareup.okhttp3:okhttp:3.8.1' - implementation ('io.socket:socket.io-client:1.0.2') { + implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}" + implementation ("io.socket:socket.io-client:${versions.socket}") { exclude group: 'org.json', module: 'json' } - implementation 'org.json:json:20230227' - implementation 'org.yaml:snakeyaml:1.20' + implementation "org.json:json:${versions.json}" + implementation "org.yaml:snakeyaml:${versions.snakeyaml}" - testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' } diff --git a/ownlang-core/build.gradle b/ownlang-core/build.gradle index af686091..96e8bfbe 100644 --- a/ownlang-core/build.gradle +++ b/ownlang-core/build.gradle @@ -6,9 +6,9 @@ group = 'com.annimon' version = '2.0-SNAPSHOT' dependencies { - implementation 'org.json:json:20230227' + implementation "org.json:json:${versions.json}" - testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' } diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index 93ff3a0d..87751b5c 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'com.github.johnrengelman.shadow' version '8.1.1' id 'java' id 'application' } @@ -16,7 +17,7 @@ dependencies { implementation project(":ownlang-utils") implementation project(":modules:main") - testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' } diff --git a/ownlang-parser/build.gradle b/ownlang-parser/build.gradle index b380dd7b..5e06268b 100644 --- a/ownlang-parser/build.gradle +++ b/ownlang-parser/build.gradle @@ -6,13 +6,14 @@ group = 'com.annimon' version = '2.0-SNAPSHOT' dependencies { - api project(":ownlang-core") + api project(':ownlang-core') - testImplementation platform('org.junit:junit-bom:5.9.2') - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' + testImplementation project(':modules:main') + testImplementation platform("org.junit:junit-bom:${versions.junit}") + testImplementation "org.junit.jupiter:junit-jupiter-params:${versions.junit}" testImplementation 'org.junit.jupiter:junit-jupiter' - testImplementation 'org.openjdk.jmh:jmh-core:1.37' - testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.37' + testImplementation "org.openjdk.jmh:jmh-core:${versions.jmh}" + testImplementation "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" } test { diff --git a/ownlang-utils/build.gradle b/ownlang-utils/build.gradle index 389808e9..8a12b43a 100644 --- a/ownlang-utils/build.gradle +++ b/ownlang-utils/build.gradle @@ -7,11 +7,11 @@ version = '2.0-SNAPSHOT' dependencies { api project(":ownlang-parser") - implementation 'org.json:json:20230227' - implementation 'org.yaml:snakeyaml:1.20' - implementation 'jline:jline:2.14.5' + implementation "org.json:json:${versions.json}" + implementation "org.yaml:snakeyaml:${versions.snakeyaml}" + implementation "jline:jline:${versions.jline}" - testImplementation platform('org.junit:junit-bom:5.9.2') + testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' } From b88207175e04aa38a8a4413e9a75a2ed21fa7cb9 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 27 Aug 2023 17:32:13 +0300 Subject: [PATCH 291/448] Configure run tasks --- ownlang-desktop/build.gradle | 18 ++++++++++++------ program.own | 3 --- visitor.own | 5 ----- 3 files changed, 12 insertions(+), 14 deletions(-) delete mode 100644 visitor.own diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index 87751b5c..ae54add2 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -7,8 +7,9 @@ plugins { group = 'com.annimon' version = '2.0-SNAPSHOT' +ext.mainClassName = 'com.annimon.ownlang.Main' application { - mainClass ='com.annimon.ownlang.Main' + mainClass = project.mainClassName } dependencies { @@ -25,18 +26,23 @@ test { useJUnitPlatform() } -/*tasks.register('run', JavaExec) { +tasks.register('runProgram', JavaExec) { + group = "application" + description = "Run sample program" dependsOn classes - mainClass = project.mainClass + mainClass = project.mainClassName classpath = sourceSets.main.runtimeClasspath standardInput = System.in ignoreExitValue true + args '-f ../program.own'.split(' ') } tasks.register('runOptimizing', JavaExec) { + group = "application" + description = "Run sample program with optimizations and measurements" dependsOn classes - mainClass = project.mainClass + mainClass = project.mainClassName classpath = sourceSets.main.runtimeClasspath ignoreExitValue true - args '-o 9 -m -a -f program.own'.split(' ') -}*/ \ No newline at end of file + args '-o 9 -m -a -f ../program.own'.split(' ') +} \ No newline at end of file diff --git a/program.own b/program.own index 239b8394..fb6e712e 100644 --- a/program.own +++ b/program.own @@ -242,9 +242,6 @@ println 1 :: 2 :: 3 println "\u042a" -include "visitor.own" - - use "date" d = newDate(); println d diff --git a/visitor.own b/visitor.own deleted file mode 100644 index e9548e65..00000000 --- a/visitor.own +++ /dev/null @@ -1,5 +0,0 @@ -function() -def function() print "function\n" - -a = 2 + 3 * 4 -print a \ No newline at end of file From 8ed89c8a9dbb2b085ec171d34990dd1745864d1e Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 28 Aug 2023 15:55:04 +0300 Subject: [PATCH 292/448] Fix canvasfx module with Java FX 17 --- modules/canvasfx/build.gradle | 23 +++++++++++++++++++ .../ownlang/modules/canvasfx/canvasfx.java | 10 ++++---- ownlang-desktop/build.gradle | 1 + settings.gradle | 4 +++- 4 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 modules/canvasfx/build.gradle rename modules/{main => canvasfx}/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java (99%) diff --git a/modules/canvasfx/build.gradle b/modules/canvasfx/build.gradle new file mode 100644 index 00000000..827791a0 --- /dev/null +++ b/modules/canvasfx/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java-library' + id 'org.openjfx.javafxplugin' version '0.0.14' +} + +group = 'com.annimon.module' +version = '2.0-SNAPSHOT' + +javafx { + version = "17" + modules = [ 'javafx.controls', 'javafx.swing' ] +} + +dependencies { + api project(":ownlang-core") + + testImplementation platform("org.junit:junit-bom:${versions.junit}") + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java b/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java similarity index 99% rename from modules/main/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java rename to modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java index 6c570416..e1568025 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java +++ b/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.modules.canvasfx; -/*import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import static com.annimon.ownlang.lib.Converters.*; @@ -37,15 +37,15 @@ import javafx.scene.shape.StrokeLineCap; import javafx.scene.shape.StrokeLineJoin; import javafx.scene.text.TextAlignment; -import javax.swing.JFrame;*/ +import javax.swing.JFrame; /** * * @author aNNiMON */ -public final class canvasfx /*implements Module*/ { +public final class canvasfx implements Module { - /*private static final int FX_EFFECT_TYPE = 5301; + private static final int FX_EFFECT_TYPE = 5301; private static final int FX_COLOR_TYPE = 5302; private static JFrame frame; @@ -1112,6 +1112,6 @@ private static void handleDragEvent(final DragEvent e, final Function handler) { map.set("isConsumed", NumberValue.fromBoolean(e.isConsumed())); map.set("isDropCompleted", NumberValue.fromBoolean(e.isDropCompleted())); handler.execute(map); - }*/ + } } diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index ae54add2..d13ebda5 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation project(":ownlang-parser") implementation project(":ownlang-utils") implementation project(":modules:main") + implementation project(":modules:canvasfx") testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/settings.gradle b/settings.gradle index bd2b452d..28d12867 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,4 +6,6 @@ include 'ownlang-desktop' include 'ownlang-utils' include 'modules:main' -findProject(':modules:main')?.name = 'main' \ No newline at end of file +findProject(':modules:main')?.name = 'main' +include 'modules:canvasfx' +findProject(':modules:canvasfx')?.name = 'canvasfx' \ No newline at end of file From a2549c3afe8544e3d37d9807d152e1baca8a0561 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 28 Aug 2023 16:52:29 +0300 Subject: [PATCH 293/448] Switch to Java11+ features where possible --- .../com/annimon/ownlang/lib/ArrayValue.java | 39 +++++------- .../com/annimon/ownlang/lib/CallStack.java | 10 +--- .../com/annimon/ownlang/lib/StringValue.java | 48 ++++++--------- .../com/annimon/ownlang/lib/ValueUtils.java | 40 ++++++------- .../outputsettings/ConsoleOutputSettings.java | 2 +- .../outputsettings/OutputSettings.java | 2 +- .../outputsettings/StringOutputSettings.java | 2 +- .../ownlang/lib/UserDefinedFunction.java | 8 +-- .../com/annimon/ownlang/parser/Parser.java | 60 +++++++++---------- .../com/annimon/ownlang/parser/Token.java | 30 +--------- .../annimon/ownlang/parser/ast/Argument.java | 18 +----- .../parser/ast/ObjectCreationExpression.java | 4 +- .../parser/linters/AssignValidator.java | 4 +- .../optimization/ConstantPropagation.java | 2 +- .../optimization/DeadCodeElimination.java | 11 ++-- .../optimization/OptimizationVisitor.java | 13 ++-- .../parser/optimization/VariablesGrabber.java | 18 +++--- .../com/annimon/ownlang/parser/LexerTest.java | 20 +++---- 18 files changed, 125 insertions(+), 206 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java index bc824fb1..94b7a764 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java @@ -52,7 +52,7 @@ public static ArrayValue merge(ArrayValue array1, ArrayValue array2) { public static StringValue joinToString(ArrayValue array, String delimiter, String prefix, String suffix) { final StringBuilder sb = new StringBuilder(); for (Value value : array) { - if (sb.length() > 0) sb.append(delimiter); + if (!sb.isEmpty()) sb.append(delimiter); else sb.append(prefix); sb.append(value.asString()); } @@ -100,37 +100,26 @@ public Value get(int index) { } public Value get(Value index) { - final String prop = index.asString(); - switch (prop) { + return switch (index.asString()) { // Properties - case "length": - return NumberValue.of(size()); + case "length" -> NumberValue.of(size()); // Functions - case "isEmpty": - return Converters.voidToBoolean(() -> size() == 0); - case "joinToString": - return new FunctionValue(this::joinToString); - - default: - return get(index.asInt()); - } + case "isEmpty" -> Converters.voidToBoolean(() -> size() == 0); + case "joinToString" -> new FunctionValue(this::joinToString); + default -> get(index.asInt()); + }; } public Value joinToString(Value[] args) { Arguments.checkRange(0, 3, args.length); - switch (args.length) { - case 0: - return joinToString(this, "", "", ""); - case 1: - return joinToString(this, args[0].asString(), "", ""); - case 2: - return joinToString(this, args[0].asString(), args[1].asString(), args[1].asString()); - case 3: - return joinToString(this, args[0].asString(), args[1].asString(), args[2].asString()); - default: - throw new ArgumentsMismatchException("Wrong number of arguments"); - } + return switch (args.length) { + case 0 -> joinToString(this, "", "", ""); + case 1 -> joinToString(this, args[0].asString(), "", ""); + case 2 -> joinToString(this, args[0].asString(), args[1].asString(), args[1].asString()); + case 3 -> joinToString(this, args[0].asString(), args[1].asString(), args[2].asString()); + default -> throw new ArgumentsMismatchException("Wrong number of arguments"); + }; } public void set(int index, Value value) { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java index c7517bd3..eec2060a 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -25,15 +25,7 @@ public static synchronized Deque getCalls() { return calls; } - public static class CallInfo { - String name; - Function function; - - public CallInfo(String name, Function function) { - this.name = name; - this.function = function; - } - + public record CallInfo(String name, Function function) { @Override public String toString() { return String.format("%s: %s", name, function.toString().trim()); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java index d58f59f1..db975b8c 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java @@ -19,56 +19,46 @@ public StringValue(String value) { public Value access(Value propertyValue) { final String prop = propertyValue.asString(); - switch (prop) { + return switch (prop) { // Properties - case "length": - return NumberValue.of(length()); - case "lower": - return new StringValue(value.toLowerCase()); - case "upper": - return new StringValue(value.toUpperCase()); - case "chars": { + case "length" -> NumberValue.of(length()); + case "lower" -> new StringValue(value.toLowerCase()); + case "upper" -> new StringValue(value.toUpperCase()); + case "chars" -> { final Value[] chars = new Value[length()]; int i = 0; for (char ch : value.toCharArray()) { - chars[i++] = NumberValue.of((int) ch); + chars[i++] = NumberValue.of(ch); } - return new ArrayValue(chars); + yield new ArrayValue(chars); } // Functions - case "trim": - return Converters.voidToString(value::trim); - case "startsWith": - return new FunctionValue(args -> { + case "trim" -> Converters.voidToString(value::trim); + case "startsWith" -> new FunctionValue(args -> { Arguments.checkOrOr(1, 2, args.length); int offset = (args.length == 2) ? args[1].asInt() : 0; return NumberValue.fromBoolean(value.startsWith(args[0].asString(), offset)); }); - case "endsWith": - return Converters.stringToBoolean(value::endsWith); - case "matches": - return Converters.stringToBoolean(value::matches); - case "contains": - return Converters.stringToBoolean(value::contains); - case "equalsIgnoreCase": - return Converters.stringToBoolean(value::equalsIgnoreCase); - case "isEmpty": - return Converters.voidToBoolean(value::isEmpty); + case "endsWith" -> Converters.stringToBoolean(value::endsWith); + case "matches" -> Converters.stringToBoolean(value::matches); + case "contains" -> Converters.stringToBoolean(value::contains); + case "equalsIgnoreCase" -> Converters.stringToBoolean(value::equalsIgnoreCase); + case "isEmpty" -> Converters.voidToBoolean(value::isEmpty); - default: + default -> { if (Functions.isExists(prop)) { final Function f = Functions.get(prop); - return new FunctionValue(args -> { + yield new FunctionValue(args -> { final Value[] newArgs = new Value[args.length + 1]; newArgs[0] = this; System.arraycopy(args, 0, newArgs, 1, args.length); return f.execute(newArgs); }); } - break; - } - throw new UnknownPropertyException(prop); + throw new UnknownPropertyException(prop); + } + }; } public int length() { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index c8a2eab5..46c3dd94 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -14,18 +14,13 @@ public final class ValueUtils { private ValueUtils() { } public static Object toObject(Value val) { - switch (val.type()) { - case Types.ARRAY: - return toObject((ArrayValue) val); - case Types.MAP: - return toObject((MapValue) val); - case Types.NUMBER: - return val.raw(); - case Types.STRING: - return val.asString(); - default: - return JSONObject.NULL; - } + return switch (val.type()) { + case Types.ARRAY -> toObject((ArrayValue) val); + case Types.MAP -> toObject((MapValue) val); + case Types.NUMBER -> val.raw(); + case Types.STRING -> val.asString(); + default -> JSONObject.NULL; + }; } public static JSONObject toObject(MapValue map) { @@ -47,20 +42,20 @@ public static JSONArray toObject(ArrayValue array) { } public static Value toValue(Object obj) { - if (obj instanceof JSONObject) { - return toValue((JSONObject) obj); + if (obj instanceof JSONObject jsonObj) { + return toValue(jsonObj); } - if (obj instanceof JSONArray) { - return toValue((JSONArray) obj); + if (obj instanceof JSONArray jsonArr) { + return toValue(jsonArr); } - if (obj instanceof String) { - return new StringValue((String) obj); + if (obj instanceof String str) { + return new StringValue(str); } - if (obj instanceof Number) { - return NumberValue.of(((Number) obj)); + if (obj instanceof Number num) { + return NumberValue.of(num); } - if (obj instanceof Boolean) { - return NumberValue.fromBoolean((Boolean) obj); + if (obj instanceof Boolean flag) { + return NumberValue.fromBoolean(flag); } // NULL or other return NumberValue.ZERO; @@ -119,6 +114,7 @@ public static Function consumeFunction(Value value, String errorMessage) { return ((FunctionValue) value).getValue(); } + @SuppressWarnings("unchecked") public static MapValue collectNumberConstants(Class clazz, Class type) { MapValue result = new MapValue(20); for (Field field : clazz.getDeclaredFields()) { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java index 87c409df..49227218 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java @@ -2,7 +2,7 @@ import java.io.File; -public class ConsoleOutputSettings implements OutputSettings { +public non-sealed class ConsoleOutputSettings implements OutputSettings { @Override public String newline() { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java index 30427080..264714ad 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java @@ -2,7 +2,7 @@ import java.io.File; -public interface OutputSettings { +public sealed interface OutputSettings permits ConsoleOutputSettings, StringOutputSettings { String newline(); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java index 3ffc8f11..1c89163e 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java @@ -2,7 +2,7 @@ import java.io.File; -public class StringOutputSettings implements OutputSettings { +public non-sealed class StringOutputSettings implements OutputSettings { private final StringBuffer out, err; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index 26be0845..352008ca 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -27,7 +27,7 @@ public int getArgsCount() { public String getArgsName(int index) { if (index < 0 || index >= getArgsCount()) return ""; - return arguments.get(index).getName(); + return arguments.get(index).name(); } @Override @@ -52,7 +52,7 @@ public Value execute(Value... values) { // Optional args if exists for (int i = size; i < totalArgsCount; i++) { final Argument arg = arguments.get(i); - Variables.define(arg.getName(), arg.getValueExpr().eval()); + Variables.define(arg.name(), arg.valueExpr().eval()); } body.execute(); return NumberValue.ZERO; @@ -65,8 +65,8 @@ public Value execute(Value... values) { @Override public String toString() { - if (body instanceof ReturnStatement) { - return String.format("def%s = %s", arguments, ((ReturnStatement)body).expression); + if (body instanceof ReturnStatement returnStmt) { + return String.format("def%s = %s", arguments, returnStmt.expression); } return String.format("def%s %s", arguments, body); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 8ec59e0a..ea6333af 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -86,8 +86,8 @@ public Statement parse() { private int getErrorLine() { if (size == 0) return 0; - if (pos >= size) return tokens.get(size - 1).getRow(); - return tokens.get(pos).getRow(); + if (pos >= size) return tokens.get(size - 1).row(); + return tokens.get(pos).row(); } private void recover() { @@ -185,7 +185,7 @@ private DestructuringAssignmentStatement destructuringAssignment() { final List variables = new ArrayList<>(); while (!match(TokenType.RPAREN)) { if (lookMatch(0, TokenType.WORD)) { - variables.add(consume(TokenType.WORD).getText()); + variables.add(consume(TokenType.WORD).text()); } else { variables.add(null); } @@ -250,7 +250,7 @@ && lookMatch(foreachIndex + 3, TokenType.COLON)) { private ForeachArrayStatement foreachArrayStatement() { // for x : arr boolean optParentheses = match(TokenType.LPAREN); - final String variable = consume(TokenType.WORD).getText(); + final String variable = consume(TokenType.WORD).text(); consume(TokenType.COLON); final Expression container = expression(); if (optParentheses) { @@ -263,9 +263,9 @@ private ForeachArrayStatement foreachArrayStatement() { private ForeachMapStatement foreachMapStatement() { // for k, v : map boolean optParentheses = match(TokenType.LPAREN); - final String key = consume(TokenType.WORD).getText(); + final String key = consume(TokenType.WORD).text(); consume(TokenType.COMMA); - final String value = consume(TokenType.WORD).getText(); + final String value = consume(TokenType.WORD).text(); consume(TokenType.COLON); final Expression container = expression(); if (optParentheses) { @@ -277,7 +277,7 @@ private ForeachMapStatement foreachMapStatement() { private FunctionDefineStatement functionDefine() { // def name(arg1, arg2 = value) { ... } || def name(args) = expr - final String name = consume(TokenType.WORD).getText(); + final String name = consume(TokenType.WORD).text(); final Arguments arguments = arguments(); final Statement body = statementBody(); return new FunctionDefineStatement(name, arguments, body); @@ -289,7 +289,7 @@ private Arguments arguments() { boolean startsOptionalArgs = false; consume(TokenType.LPAREN); while (!match(TokenType.RPAREN)) { - final String name = consume(TokenType.WORD).getText(); + final String name = consume(TokenType.WORD).text(); if (match(TokenType.EQ)) { startsOptionalArgs = true; arguments.addOptional(name, variable()); @@ -383,26 +383,26 @@ private MatchExpression match() { if (match(TokenType.NUMBER)) { // case 0.5: pattern = new MatchExpression.ConstantPattern( - NumberValue.of(createNumber(current.getText(), 10)) + NumberValue.of(createNumber(current.text(), 10)) ); } else if (match(TokenType.HEX_NUMBER)) { // case #FF: pattern = new MatchExpression.ConstantPattern( - NumberValue.of(createNumber(current.getText(), 16)) + NumberValue.of(createNumber(current.text(), 16)) ); } else if (match(TokenType.TEXT)) { // case "text": pattern = new MatchExpression.ConstantPattern( - new StringValue(current.getText()) + new StringValue(current.text()) ); } else if (match(TokenType.WORD)) { // case value: - pattern = new MatchExpression.VariablePattern(current.getText()); + pattern = new MatchExpression.VariablePattern(current.text()); } else if (match(TokenType.LBRACKET)) { // case [x :: xs]: final MatchExpression.ListPattern listPattern = new MatchExpression.ListPattern(); while (!match(TokenType.RBRACKET)) { - listPattern.add(consume(TokenType.WORD).getText()); + listPattern.add(consume(TokenType.WORD).text()); match(TokenType.COLONCOLON); } pattern = listPattern; @@ -410,7 +410,7 @@ private MatchExpression match() { // case (1, 2): final MatchExpression.TuplePattern tuplePattern = new MatchExpression.TuplePattern(); while (!match(TokenType.RPAREN)) { - if ("_".equals(get(0).getText())) { + if ("_".equals(get(0).text())) { tuplePattern.addAny(); consume(TokenType.WORD); } else { @@ -447,7 +447,7 @@ private Statement classDeclaration() { // str = "" // def method() = str // } - final String name = consume(TokenType.WORD).getText(); + final String name = consume(TokenType.WORD).text(); final ClassDeclarationStatement classDeclaration = new ClassDeclarationStatement(name); consume(TokenType.LBRACE); do { @@ -481,12 +481,12 @@ private AssignmentExpression assignmentStrict() { // x[0].prop += ... final int position = pos; final Expression targetExpr = qualifiedName(); - if ((targetExpr == null) || !(targetExpr instanceof Accessible)) { + if (!(targetExpr instanceof Accessible)) { pos = position; return null; } - final TokenType currentType = get(0).getType(); + final TokenType currentType = get(0).type(); if (!ASSIGN_OPERATORS.containsKey(currentType)) { pos = position; return null; @@ -721,7 +721,7 @@ private Expression multiplicative() { private Expression objectCreation() { if (match(TokenType.NEW)) { - final String className = consume(TokenType.WORD).getText(); + final String className = consume(TokenType.WORD).text(); final List args = new ArrayList<>(); consume(TokenType.LPAREN); while (!match(TokenType.RPAREN)) { @@ -765,7 +765,7 @@ private Expression primary() { if (match(TokenType.COLONCOLON)) { // ::method reference - final String functionName = consume(TokenType.WORD).getText(); + final String functionName = consume(TokenType.WORD).text(); return new FunctionReferenceExpression(functionName); } if (match(TokenType.MATCH)) { @@ -783,7 +783,7 @@ private Expression primary() { private Expression variable() { // function(... if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return functionChain(new ValueExpression(consume(TokenType.WORD).getText())); + return functionChain(new ValueExpression(consume(TokenType.WORD).text())); } final Expression qualifiedNameExpr = qualifiedName(); @@ -818,9 +818,9 @@ private Expression qualifiedName() { final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { - return new VariableExpression(current.getText()); + return new VariableExpression(current.text()); } - return new ContainerAccessExpression(current.getText(), indices); + return new ContainerAccessExpression(current.text(), indices); } private List variableSuffix() { @@ -831,7 +831,7 @@ private List variableSuffix() { final List indices = new ArrayList<>(); while (lookMatch(0, TokenType.DOT) || lookMatch(0, TokenType.LBRACKET)) { if (match(TokenType.DOT)) { - final String fieldName = consume(TokenType.WORD).getText(); + final String fieldName = consume(TokenType.WORD).text(); final Expression key = new ValueExpression(fieldName); indices.add(key); } @@ -846,20 +846,20 @@ private List variableSuffix() { private Expression value() { final Token current = get(0); if (match(TokenType.NUMBER)) { - return new ValueExpression(createNumber(current.getText(), 10)); + return new ValueExpression(createNumber(current.text(), 10)); } if (match(TokenType.HEX_NUMBER)) { - return new ValueExpression(createNumber(current.getText(), 16)); + return new ValueExpression(createNumber(current.text(), 16)); } if (match(TokenType.TEXT)) { - final ValueExpression strExpr = new ValueExpression(current.getText()); + final ValueExpression strExpr = new ValueExpression(current.text()); // "text".property || "text".func() if (lookMatch(0, TokenType.DOT)) { if (lookMatch(1, TokenType.WORD) && lookMatch(2, TokenType.LPAREN)) { match(TokenType.DOT); return functionChain(new ContainerAccessExpression( strExpr, Collections.singletonList( - new ValueExpression(consume(TokenType.WORD).getText()) + new ValueExpression(consume(TokenType.WORD).text()) ))); } final List indices = variableSuffix(); @@ -888,7 +888,7 @@ private Number createNumber(String text, int radix) { private Token consume(TokenType type) { final Token current = get(0); - if (type != current.getType()) { + if (type != current.type()) { throw new ParseException("Token " + current + " doesn't match " + type); } pos++; @@ -897,7 +897,7 @@ private Token consume(TokenType type) { private boolean match(TokenType type) { final Token current = get(0); - if (type != current.getType()) { + if (type != current.type()) { return false; } pos++; @@ -905,7 +905,7 @@ private boolean match(TokenType type) { } private boolean lookMatch(int pos, TokenType type) { - return get(pos).getType() == type; + return get(pos).type() == type; } private Token get(int relativePosition) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java index 3d4f4e7d..c9704cb8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java @@ -1,38 +1,10 @@ package com.annimon.ownlang.parser; /** - * * @author aNNiMON */ -public final class Token { +public record Token(TokenType type, String text, int row, int col) { - private final TokenType type; - private final String text; - private final int row, col; - - public Token(TokenType type, String text, int row, int col) { - this.type = type; - this.text = text; - this.row = row; - this.col = col; - } - - public TokenType getType() { - return type; - } - - public String getText() { - return text; - } - - public int getRow() { - return row; - } - - public int getCol() { - return col; - } - public String position() { return "[" + row + " " + col + "]"; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java index d900fa12..34cea902 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java @@ -1,27 +1,11 @@ package com.annimon.ownlang.parser.ast; -public final class Argument { - - private final String name; - private final Expression valueExpr; +public record Argument(String name, Expression valueExpr) { public Argument(String name) { this(name, null); } - public Argument(String name, Expression valueExpr) { - this.name = name; - this.valueExpr = valueExpr; - } - - public String getName() { - return name; - } - - public Expression getValueExpr() { - return valueExpr; - } - @Override public String toString() { return name + (valueExpr == null ? "" : " = " + valueExpr); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index 6e15ad03..bd56c597 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -22,8 +22,8 @@ public Value eval() { // Is Instantiable? if (Variables.isExists(className)) { final Value variable = Variables.get(className); - if (variable instanceof Instantiable) { - return ((Instantiable) variable).newInstance(ctorArgs()); + if (variable instanceof Instantiable instantiable) { + return instantiable.newInstance(ctorArgs()); } } throw new UnknownClassException(className); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 0b65f2bc..7b680bf5 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -13,8 +13,8 @@ public final class AssignValidator extends LintVisitor { @Override public void visit(AssignmentExpression s) { super.visit(s); - if (s.target instanceof VariableExpression) { - final String variable = ((VariableExpression) s.target).name; + if (s.target instanceof VariableExpression varExpr) { + final String variable = varExpr.name; if (Variables.isExists(variable)) { Console.error(String.format( "Warning: variable \"%s\" overrides constant", variable)); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java index 7a2b00c6..c69ad0b1 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantPropagation.java @@ -50,7 +50,7 @@ public int optimizationsCount() { public String summaryInfo() { if (optimizationsCount() == 0) return ""; final StringBuilder sb = new StringBuilder(); - if (propagatedVariables.size() > 0) { + if (!propagatedVariables.isEmpty()) { sb.append("\nConstant propagations: ").append(propagatedVariables.size()); for (Map.Entry e : propagatedVariables.entrySet()) { sb.append("\n ").append(e.getKey()).append(": ").append(e.getValue()); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java index 86c9e9ea..d5ba7cc3 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java @@ -128,16 +128,15 @@ public Node visit(BlockStatement s, Map t) { if (node != statement) { changed = true; } - if (node instanceof ExprStatement - && isConstantValue( ((ExprStatement) node).expr )) { + if (node instanceof ExprStatement expr && isConstantValue(expr.expr)) { changed = true; continue; } - if (node instanceof Statement) { - result.add((Statement) node); - } else if (node instanceof Expression) { - result.add(new ExprStatement((Expression) node)); + if (node instanceof Statement stmt) { + result.add(stmt); + } else if (node instanceof Expression expr) { + result.add(new ExprStatement(expr)); } } if (changed) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 4311b410..aedc7475 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -261,8 +261,8 @@ public Node visit(MatchExpression s, T t) { boolean changed = expression != s.expression; final List patterns = new ArrayList<>(s.patterns.size()); for (MatchExpression.Pattern pattern : s.patterns) { - if (pattern instanceof MatchExpression.VariablePattern) { - final String variable = ((MatchExpression.VariablePattern) pattern).variable; + if (pattern instanceof MatchExpression.VariablePattern varPattern) { + final String variable = varPattern.variable; final VariableExpression expr = new VariableExpression(variable); final Node node = expr.accept(this, t); if ((node != expr) && isValue(node)) { @@ -276,8 +276,7 @@ public Node visit(MatchExpression s, T t) { } } - if (pattern instanceof MatchExpression.TuplePattern) { - final MatchExpression.TuplePattern tuple = (MatchExpression.TuplePattern) pattern; + if (pattern instanceof MatchExpression.TuplePattern tuple) { final List newValues = new ArrayList<>(tuple.values.size()); boolean valuesChanged = false; for (Expression value : tuple.values) { @@ -437,15 +436,15 @@ public UserDefinedFunction visit(UserDefinedFunction s, T t) { protected boolean visit(final Arguments in, final Arguments out, T t) { boolean changed = false; for (Argument argument : in) { - final Expression valueExpr = argument.getValueExpr(); + final Expression valueExpr = argument.valueExpr(); if (valueExpr == null) { - out.addRequired(argument.getName()); + out.addRequired(argument.name()); } else { final Node expr = valueExpr.accept(this, t); if (expr != valueExpr) { changed = true; } - out.addOptional(argument.getName(), (Expression) expr); + out.addOptional(argument.name(), (Expression) expr); } } return changed; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 1ee16362..7e753a5b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -32,7 +32,7 @@ public VariablesGrabber(boolean grabModuleConstants) { @Override public Node visit(AssignmentExpression s, Map t) { - if (!isVariable((Node)s.target)) { + if (!isVariable(s.target)) { return super.visit(s, t); } @@ -71,8 +71,8 @@ public Node visit(ForeachMapStatement s, Map t) { @Override public Node visit(MatchExpression s, Map t) { for (MatchExpression.Pattern pattern : s.patterns) { - if (pattern instanceof MatchExpression.VariablePattern) { - final String variableName = ((MatchExpression.VariablePattern) pattern).variable; + if (pattern instanceof MatchExpression.VariablePattern varPattern) { + final String variableName = varPattern.variable; t.put(variableName, variableInfo(t, variableName)); } } @@ -82,12 +82,11 @@ public Node visit(MatchExpression s, Map t) { @Override public Node visit(UnaryExpression s, Map t) { if (s.expr1 instanceof Accessible) { - if (s.expr1 instanceof VariableExpression) { - final String variableName = ((VariableExpression) s.expr1).name; + if (s.expr1 instanceof VariableExpression varExpr) { + final String variableName = varExpr.name; t.put(variableName, variableInfo(t, variableName)); } - if (s.expr1 instanceof ContainerAccessExpression) { - ContainerAccessExpression conExpr = (ContainerAccessExpression) s.expr1; + if (s.expr1 instanceof ContainerAccessExpression conExpr) { if (conExpr.rootIsVariable()) { final String variableName = ((VariableExpression) conExpr.root).name; t.put(variableName, variableInfo(t, variableName)); @@ -119,8 +118,7 @@ public Node visit(UseStatement s, Map t) { } private boolean canLoadConstants(Expression expression) { - if (expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) expression; + if (expression instanceof ArrayExpression ae) { for (Expression expr : ae.elements) { if (!isValue(expr)) { return false; @@ -134,7 +132,7 @@ private boolean canLoadConstants(Expression expression) { @Override protected boolean visit(Arguments in, Arguments out, Map t) { for (Argument argument : in) { - final String variableName = argument.getName(); + final String variableName = argument.name(); final VariableInfo var = variableInfo(t, variableName); /* No need to add value - it is optional arguments final Expression expr = argument.getValueExpr(); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index a4d94006..e88f26ae 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -21,10 +21,10 @@ public void testNumbers() { List expList = list(NUMBER, NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); List result = Lexer.tokenize(input); assertTokens(expList, result); - assertEquals("0", result.get(0).getText()); - assertEquals("3.1415", result.get(1).getText()); - assertEquals("CAFEBABE", result.get(2).getText()); - assertEquals("f7d6c5", result.get(3).getText()); + assertEquals("0", result.get(0).text()); + assertEquals("3.1415", result.get(1).text()); + assertEquals("CAFEBABE", result.get(2).text()); + assertEquals("f7d6c5", result.get(3).text()); } @Test @@ -39,7 +39,7 @@ public void testArithmetic() { List expList = list(WORD, EQ, MINUS, NUMBER, PLUS, NUMBER, STAR, NUMBER, PERCENT, NUMBER, SLASH, NUMBER); List result = Lexer.tokenize(input); assertTokens(expList, result); - assertEquals("x", result.get(0).getText()); + assertEquals("x", result.get(0).text()); } @Test @@ -64,7 +64,7 @@ public void testString() { List expList = list(TEXT); List result = Lexer.tokenize(input); assertTokens(expList, result); - assertEquals("1\"2", result.get(0).getText()); + assertEquals("1\"2", result.get(0).text()); } @Test @@ -73,7 +73,7 @@ public void testEmptyString() { List expList = list(TEXT); List result = Lexer.tokenize(input); assertTokens(expList, result); - assertEquals("", result.get(0).getText()); + assertEquals("", result.get(0).text()); } @Test @@ -83,7 +83,7 @@ public void testStringError() { assertThrows(LexerException.class, () -> { List result = Lexer.tokenize(input); assertTokens(expList, result); - assertEquals("1", result.get(0).getText()); + assertEquals("1", result.get(0).text()); }); } @@ -110,7 +110,7 @@ public void testComments() { List expList = list(NUMBER); List result = Lexer.tokenize(input); assertTokens(expList, result); - assertEquals("123", result.get(0).getText()); + assertEquals("123", result.get(0).text()); } @Test @@ -159,7 +159,7 @@ private static void assertTokens(List expList, List result) { final int length = expList.size(); assertEquals(length, result.size()); for (int i = 0; i < length; i++) { - assertEquals(expList.get(i).getType(), result.get(i).getType()); + assertEquals(expList.get(i).type(), result.get(i).type()); } } From fc1d8b4c75f9003bb5bad2045b4e7cae072bf170 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 30 Aug 2023 17:08:08 +0300 Subject: [PATCH 294/448] Mark core as compileOnlyApi in module dependencies --- modules/canvasfx/build.gradle | 3 ++- modules/main/build.gradle | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/canvasfx/build.gradle b/modules/canvasfx/build.gradle index 827791a0..aab78fa0 100644 --- a/modules/canvasfx/build.gradle +++ b/modules/canvasfx/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java-library' id 'org.openjfx.javafxplugin' version '0.0.14' + id 'com.github.johnrengelman.shadow' version '8.1.1' } group = 'com.annimon.module' @@ -12,7 +13,7 @@ javafx { } dependencies { - api project(":ownlang-core") + compileOnlyApi project(":ownlang-core") testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/modules/main/build.gradle b/modules/main/build.gradle index ac3fdd02..1e8e777d 100644 --- a/modules/main/build.gradle +++ b/modules/main/build.gradle @@ -7,7 +7,7 @@ version = '2.0-SNAPSHOT' dependencies { - api project(":ownlang-core") + compileOnlyApi project(":ownlang-core") implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}" implementation ("io.socket:socket.io-client:${versions.socket}") { From 16de63f72024b6c5b1af3314481f1d0af4488164 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 30 Aug 2023 18:21:58 +0300 Subject: [PATCH 295/448] Add JMH benchmark for programs --- ownlang-parser/build.gradle | 1 + .../ownlang/parser/LexerBenchmarkTest.java | 7 +- .../ownlang/parser/ParserBenchmarkTest.java | 2 +- .../ownlang/parser/ProgramsBenchmarkTest.java | 64 +++++++++++++++++++ .../annimon/ownlang/parser/ProgramsTest.java | 20 +----- .../annimon/ownlang/parser/TestDataUtil.java | 22 +++++++ .../resources/benchmarks/useStatement.own | 5 ++ 7 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java create mode 100644 ownlang-parser/src/test/java/com/annimon/ownlang/parser/TestDataUtil.java create mode 100644 ownlang-parser/src/test/resources/benchmarks/useStatement.own diff --git a/ownlang-parser/build.gradle b/ownlang-parser/build.gradle index 5e06268b..0354e96c 100644 --- a/ownlang-parser/build.gradle +++ b/ownlang-parser/build.gradle @@ -14,6 +14,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation "org.openjdk.jmh:jmh-core:${versions.jmh}" testImplementation "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" + testAnnotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" } test { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java index b654fff2..8fa8421a 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; @@ -23,13 +24,13 @@ public class LexerBenchmarkTest { @Setup(Level.Trial) public void initializeTrial() throws IOException { - input = SourceLoader.readSource("program.own"); + input = SourceLoader.readSource("../program.own"); } @Benchmark - public void lexerBenchmark() { + public void lexerBenchmark(Blackhole bh) { for (int i = 0; i < iterations; i++) { - Lexer.tokenize(input); + bh.consume(Lexer.tokenize(input)); } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java index 68e3b93d..05c867e3 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java @@ -25,7 +25,7 @@ public class ParserBenchmarkTest { @Setup(Level.Trial) public void initializeTrial() throws IOException { - input = Lexer.tokenize(SourceLoader.readSource("program.own")); + input = Lexer.tokenize(SourceLoader.readSource("../program.own")); } @Benchmark diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java new file mode 100644 index 00000000..cf898312 --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java @@ -0,0 +1,64 @@ +package com.annimon.ownlang.parser; + +import com.annimon.ownlang.parser.ast.Statement; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import static com.annimon.ownlang.parser.TestDataUtil.scanDirectory; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +public class ProgramsBenchmarkTest { + private static final String RES_DIR = "src/test/resources/benchmarks"; + + @Param({"10"}) + private int iterations; + @Param({"-"}) + private String path; + + private Statement program; + + @Setup(Level.Trial) + public void initializeTrial() throws IOException { + if (!Files.exists(Path.of(path))) return; + final var source = SourceLoader.readSource(path); + final var tokens = Lexer.tokenize(source); + program = Parser.parse(tokens); + } + + @Benchmark + public void programBenchmark() { + for (int i = 0; i < iterations; i++) { + program.execute(); + } + } + + @Disabled + @Test + public void executeBenchmark() throws RunnerException { + Options opt = new OptionsBuilder() + .include(ProgramsBenchmarkTest.class.getSimpleName()) + .warmupIterations(3) + .measurementIterations(5) + .param("path", scanDirectory(RES_DIR) + .map(File::getPath) + .toArray(String[]::new)) + .threads(1) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index caa2708e..e9bb36f0 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -18,35 +18,19 @@ import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.stream.Stream; +import static com.annimon.ownlang.parser.TestDataUtil.scanDirectory; import static org.junit.jupiter.api.Assertions.*; public class ProgramsTest { private static final String RES_DIR = "src/test/resources"; public static Stream data() { - final File resDir = new File(RES_DIR); - return scanDirectory(resDir) + return scanDirectory(RES_DIR) .map(File::getPath); } - private static Stream scanDirectory(File dir) { - final File[] files = dir.listFiles(); - if (files == null || files.length == 0) { - return Stream.empty(); - } - return Arrays.stream(files) - .flatMap(file -> { - if (file.isDirectory()) { - return scanDirectory(file); - } - return Stream.of(file); - }) - .filter(f -> f.getName().endsWith(".own")); - } - @BeforeEach public void initialize() { Variables.clear(); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/TestDataUtil.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/TestDataUtil.java new file mode 100644 index 00000000..fa239bf5 --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/TestDataUtil.java @@ -0,0 +1,22 @@ +package com.annimon.ownlang.parser; + +import java.io.File; +import java.util.Arrays; +import java.util.stream.Stream; + +public class TestDataUtil { + + static Stream scanDirectory(String dirPath) { + return scanDirectory(new File(dirPath)); + } + + static Stream scanDirectory(File dir) { + final File[] files = dir.listFiles(); + if (files == null || files.length == 0) { + return Stream.empty(); + } + return Arrays.stream(files) + .flatMap(f -> f.isDirectory() ? scanDirectory(f) : Stream.of(f)) + .filter(f -> f.getName().endsWith(".own")); + } +} diff --git a/ownlang-parser/src/test/resources/benchmarks/useStatement.own b/ownlang-parser/src/test/resources/benchmarks/useStatement.own new file mode 100644 index 00000000..0e72fece --- /dev/null +++ b/ownlang-parser/src/test/resources/benchmarks/useStatement.own @@ -0,0 +1,5 @@ +for i = 0, i < 50, i++ { + use "std" + use "files" + use ["math", "functional"] +} \ No newline at end of file From fe39c1ac00952f0b804dbd28bc15eeb7b2eec00d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 3 Sep 2023 21:42:23 +0300 Subject: [PATCH 296/448] Define functions and constants in root scope --- .../com/annimon/ownlang/lib/Functions.java | 25 +--- .../com/annimon/ownlang/lib/RootScope.java | 68 +++++++++ .../java/com/annimon/ownlang/lib/Scope.java | 64 +++++++++ .../com/annimon/ownlang/lib/ScopeHandler.java | 129 ++++++++++++++++++ .../com/annimon/ownlang/lib/Variables.java | 106 ++------------ .../com/annimon/ownlang/lib/ClassMethod.java | 6 +- .../ownlang/lib/UserDefinedFunction.java | 8 +- .../com/annimon/ownlang/parser/Linter.java | 12 +- .../ast/DestructuringAssignmentStatement.java | 11 +- .../parser/ast/ForeachArrayStatement.java | 13 +- .../parser/ast/ForeachMapStatement.java | 25 ++-- .../parser/ast/FunctionalExpression.java | 8 +- .../ownlang/parser/ast/MatchExpression.java | 34 +++-- .../parser/ast/VariableExpression.java | 3 +- .../annimon/ownlang/parser/ProgramsTest.java | 8 +- 15 files changed, 338 insertions(+), 182 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java index f43d0297..ed40469e 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java @@ -1,7 +1,5 @@ package com.annimon.ownlang.lib; -import com.annimon.ownlang.exceptions.UnknownFunctionException; -import java.util.HashMap; import java.util.Map; /** @@ -9,32 +7,21 @@ * @author aNNiMON */ public final class Functions { - - private static final Map functions; - static { - functions = new HashMap<>(); - } - private Functions() { } - public static void clear() { - functions.clear(); - } - public static Map getFunctions() { - return functions; + return ScopeHandler.functions(); } - public static boolean isExists(String key) { - return functions.containsKey(key); + public static boolean isExists(String name) { + return ScopeHandler.isFunctionExists(name); } - public static Function get(String key) { - if (!isExists(key)) throw new UnknownFunctionException(key); - return functions.get(key); + public static Function get(String name) { + return ScopeHandler.getFunction(name); } public static void set(String key, Function function) { - functions.put(key, function); + ScopeHandler.setFunction(key, function); } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java new file mode 100644 index 00000000..82ded80f --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java @@ -0,0 +1,68 @@ +package com.annimon.ownlang.lib; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +final class RootScope extends Scope { + private final Map constants; + private final Map functions; + + RootScope() { + functions = new ConcurrentHashMap<>(); + constants = new ConcurrentHashMap<>(); + constants.put("true", NumberValue.ONE); + constants.put("false", NumberValue.ZERO); + } + + @Override + public boolean isRoot() { + return true; + } + + @Override + public boolean contains(String name) { + return super.containsVariable(name) + || containsConstant(name); + } + + public boolean containsConstant(String name) { + return constants.containsKey(name); + } + + @Override + public Value get(String name) { + if (containsConstant(name)) { + return getConstant(name); + } + return super.get(name); + } + + public Value getConstant(String name) { + return constants.get(name); + } + + public void setConstant(String name, Value value) { + constants.put(name, value); + } + + public Map getConstants() { + return constants; + } + + + public boolean containsFunction(String name) { + return functions.containsKey(name); + } + + public Function getFunction(String name) { + return functions.get(name); + } + + public void setFunction(String name, Function function) { + functions.put(name, function); + } + + public Map getFunctions() { + return functions; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java new file mode 100644 index 00000000..a92d6046 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java @@ -0,0 +1,64 @@ +package com.annimon.ownlang.lib; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +sealed class Scope permits RootScope { + final Scope parent; + private final Map variables; + + Scope() { + this(null); + } + + Scope(Scope parent) { + this.parent = parent; + variables = new ConcurrentHashMap<>(); + } + + public boolean isRoot() { + return !hasParent(); + } + + public boolean hasParent() { + return parent != null; + } + + public boolean contains(String name) { + return containsVariable(name); + } + + public final boolean containsVariable(String name) { + return variables.containsKey(name); + } + + public Value get(String name) { + return getVariable(name); + } + + public final Value getVariable(String name) { + return variables.get(name); + } + + public final void setVariable(String name, Value value) { + variables.put(name, value); + } + + public final void removeVariable(String name) { + variables.remove(name); + } + + public Map getVariables() { + return variables; + } + + static class ScopeFindData { + final boolean isFound; + final Scope scope; + + ScopeFindData(boolean isFound, Scope scope) { + this.isFound = isFound; + this.scope = scope; + } + } +} \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java new file mode 100644 index 00000000..179c4013 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -0,0 +1,129 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.exceptions.UnknownFunctionException; +import com.annimon.ownlang.lib.Scope.ScopeFindData; + +import java.util.Map; + +public final class ScopeHandler { + + private static final Object lock = new Object(); + + private static volatile RootScope rootScope; + private static volatile Scope scope; + + static { + ScopeHandler.resetScope(); + } + + public static Map variables() { + return scope.getVariables(); + } + + public static Map constants() { + return rootScope.getConstants(); + } + + public static Map functions() { + return rootScope.getFunctions(); + } + + /** + * Resets a scope for new program execution + */ + public static void resetScope() { + rootScope = new RootScope(); + scope = rootScope; + } + + public static void push() { + synchronized (lock) { + scope = new Scope(scope); + } + } + + public static void pop() { + synchronized (lock) { + if (!scope.isRoot()) { + scope = scope.parent; + } + } + } + + + + public static boolean isFunctionExists(String name) { + return rootScope.containsFunction(name); + } + + public static Function getFunction(String name) { + final var function = rootScope.getFunction(name); + if (function == null) throw new UnknownFunctionException(name); + return function; + } + + public static void setFunction(String name, Function function) { + rootScope.setFunction(name, function); + } + + + public static boolean isVariableOrConstantExists(String name) { + synchronized (lock) { + return findScope(name).isFound; + } + } + + public static Value getVariableOrConstant(String name) { + synchronized (lock) { + final ScopeFindData scopeData = findScope(name); + if (scopeData.isFound) { + return scopeData.scope.get(name); + } + } + return NumberValue.ZERO; + } + + public static Value getVariable(String name) { + synchronized (lock) { + final ScopeFindData scopeData = findScope(name); + if (scopeData.isFound) { + return scopeData.scope.getVariable(name); + } + } + // TODO should be strict + return NumberValue.ZERO; + } + + public static void setVariable(String name, Value value) { + synchronized (lock) { + findScope(name).scope.setVariable(name, value); + } + } + + public static void setConstant(String name, Value value) { + rootScope.setConstant(name, value); + } + + public static void defineVariableInCurrentScope(String name, Value value) { + synchronized (lock) { + scope.setVariable(name, value); + } + } + + public static void removeVariable(String name) { + synchronized (lock) { + findScope(name).scope.removeVariable(name); + } + } + + private static ScopeFindData findScope(String name) { + Scope current = scope; + do { + if (current.contains(name)) { + return new ScopeFindData(true, current); + } + } while ((current = current.parent) != null); + + return new ScopeFindData(false, scope); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java index 4033fa33..a3b41216 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -1,118 +1,34 @@ package com.annimon.ownlang.lib; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * * @author aNNiMON */ public final class Variables { - - private static final Object lock = new Object(); - - private static class Scope { - final Scope parent; - final Map variables; - - Scope() { - this(null); - } - - Scope(Scope parent) { - this.parent = parent; - variables = new ConcurrentHashMap<>(); - } - } - - private static class ScopeFindData { - boolean isFound; - Scope scope; - } - - private static volatile Scope scope; - static { - Variables.clear(); - } - private Variables() { } public static Map variables() { - return scope.variables; - } - - public static void clear() { - scope = new Scope(); - scope.variables.clear(); - scope.variables.put("true", NumberValue.ONE); - scope.variables.put("false", NumberValue.ZERO); - } - - public static void push() { - synchronized (lock) { - scope = new Scope(scope); - } - } - - public static void pop() { - synchronized (lock) { - if (scope.parent != null) { - scope = scope.parent; - } - } + return ScopeHandler.variables(); } - public static boolean isExists(String key) { - synchronized (lock) { - return findScope(key).isFound; - } + public static boolean isExists(String name) { + return ScopeHandler.isVariableOrConstantExists(name); } - public static Value get(String key) { - synchronized (lock) { - final ScopeFindData scopeData = findScope(key); - if (scopeData.isFound) { - return scopeData.scope.variables.get(key); - } - } - return NumberValue.ZERO; + public static Value get(String name) { + return ScopeHandler.getVariableOrConstant(name); } - public static void set(String key, Value value) { - synchronized (lock) { - findScope(key).scope.variables.put(key, value); - } - } - - public static void define(String key, Value value) { - synchronized (lock) { - scope.variables.put(key, value); - } + public static void set(String name, Value value) { + ScopeHandler.setVariable(name, value); } - public static void remove(String key) { - synchronized (lock) { - findScope(key).scope.variables.remove(key); - } - } - - /* - * Find scope where variable exists. + /** + * For compatibility with other modules */ - private static ScopeFindData findScope(String variable) { - final ScopeFindData result = new ScopeFindData(); - - Scope current = scope; - do { - if (current.variables.containsKey(variable)) { - result.isFound = true; - result.scope = current; - return result; - } - } while ((current = current.parent) != null); - - result.isFound = false; - result.scope = scope; - return result; + public static void define(String name, Value value) { + ScopeHandler.setConstant(name, value); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java index a5950398..6155a64a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java @@ -14,13 +14,13 @@ public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue class @Override public Value execute(Value[] values) { - Variables.push(); - Variables.define("this", classInstance.getThisMap()); + ScopeHandler.push(); + ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap()); try { return super.execute(values); } finally { - Variables.pop(); + ScopeHandler.pop(); } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index 352008ca..b0a92be7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -45,21 +45,21 @@ public Value execute(Value... values) { } try { - Variables.push(); + ScopeHandler.push(); for (int i = 0; i < size; i++) { - Variables.define(getArgsName(i), values[i]); + ScopeHandler.defineVariableInCurrentScope(getArgsName(i), values[i]); } // Optional args if exists for (int i = size; i < totalArgsCount; i++) { final Argument arg = arguments.get(i); - Variables.define(arg.name(), arg.valueExpr().eval()); + ScopeHandler.defineVariableInCurrentScope(arg.name(), arg.valueExpr().eval()); } body.execute(); return NumberValue.ZERO; } catch (ReturnStatement rt) { return rt.getResult(); } finally { - Variables.pop(); + ScopeHandler.pop(); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java index 999236a4..ba2dee1b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java @@ -1,13 +1,12 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.Console; -import com.annimon.ownlang.parser.linters.AssignValidator; -import com.annimon.ownlang.parser.linters.UseWithNonStringValueValidator; -import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; +import com.annimon.ownlang.parser.linters.AssignValidator; +import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; +import com.annimon.ownlang.parser.linters.UseWithNonStringValueValidator; public final class Linter { @@ -36,7 +35,6 @@ public void execute() { } private void resetState() { - Variables.clear(); - Functions.getFunctions().clear(); + ScopeHandler.resetScope(); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java index 5848b35f..edf08859 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java @@ -1,10 +1,7 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; + import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,7 +39,7 @@ private void execute(ArrayValue array) { for (int i = 0; i < size; i++) { final String variable = variables.get(i); if (variable != null) { - Variables.define(variable, array.get(i)); + ScopeHandler.defineVariableInCurrentScope(variable, array.get(i)); } } } @@ -51,7 +48,7 @@ private void execute(MapValue map) { for (Map.Entry entry : map) { final String variable = variables.get(i); if (variable != null) { - Variables.define(variable, new ArrayValue( + ScopeHandler.defineVariableInCurrentScope(variable, new ArrayValue( new Value[] { entry.getKey(), entry.getValue() } )); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java index fbc5aa6c..921eda9a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -23,7 +23,8 @@ public ForeachArrayStatement(String variable, Expression container, Statement bo @Override public void execute() { super.interruptionCheck(); - final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; + // TODO removing without checking shadowing is dangerous + final Value previousVariableValue = ScopeHandler.getVariable(variable); final Value containerValue = container.eval(); switch (containerValue.type()) { @@ -42,15 +43,15 @@ public void execute() { // Restore variables if (previousVariableValue != null) { - Variables.set(variable, previousVariableValue); + ScopeHandler.setVariable(variable, previousVariableValue); } else { - Variables.remove(variable); + ScopeHandler.removeVariable(variable); } } private void iterateString(String str) { for (char ch : str.toCharArray()) { - Variables.set(variable, new StringValue(String.valueOf(ch))); + ScopeHandler.setVariable(variable, new StringValue(String.valueOf(ch))); try { body.execute(); } catch (BreakStatement bs) { @@ -63,7 +64,7 @@ private void iterateString(String str) { private void iterateArray(ArrayValue containerValue) { for (Value value : containerValue) { - Variables.set(variable, value); + ScopeHandler.setVariable(variable, value); try { body.execute(); } catch (BreakStatement bs) { @@ -76,7 +77,7 @@ private void iterateArray(ArrayValue containerValue) { private void iterateMap(MapValue containerValue) { for (Map.Entry entry : containerValue) { - Variables.set(variable, new ArrayValue(new Value[] { + ScopeHandler.setVariable(variable, new ArrayValue(new Value[] { entry.getKey(), entry.getValue() })); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java index 2a24393f..32bfd494 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -24,8 +24,9 @@ public ForeachMapStatement(String key, String value, Expression container, State @Override public void execute() { super.interruptionCheck(); - final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; - final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; + // TODO removing without checking shadowing is dangerous + final Value previousVariableValue1 = ScopeHandler.getVariable(key); + final Value previousVariableValue2 = ScopeHandler.getVariable(value); final Value containerValue = container.eval(); switch (containerValue.type()) { @@ -44,21 +45,21 @@ public void execute() { // Restore variables if (previousVariableValue1 != null) { - Variables.set(key, previousVariableValue1); + ScopeHandler.setVariable(key, previousVariableValue1); } else { - Variables.remove(key); + ScopeHandler.removeVariable(key); } if (previousVariableValue2 != null) { - Variables.set(value, previousVariableValue2); + ScopeHandler.setVariable(value, previousVariableValue2); } else { - Variables.remove(value); + ScopeHandler.removeVariable(value); } } private void iterateString(String str) { for (char ch : str.toCharArray()) { - Variables.set(key, new StringValue(String.valueOf(ch))); - Variables.set(value, NumberValue.of(ch)); + ScopeHandler.setVariable(key, new StringValue(String.valueOf(ch))); + ScopeHandler.setVariable(value, NumberValue.of(ch)); try { body.execute(); } catch (BreakStatement bs) { @@ -72,8 +73,8 @@ private void iterateString(String str) { private void iterateArray(ArrayValue containerValue) { int index = 0; for (Value v : containerValue) { - Variables.set(key, v); - Variables.set(value, NumberValue.of(index++)); + ScopeHandler.setVariable(key, v); + ScopeHandler.setVariable(value, NumberValue.of(index++)); try { body.execute(); } catch (BreakStatement bs) { @@ -86,8 +87,8 @@ private void iterateArray(ArrayValue containerValue) { private void iterateMap(MapValue containerValue) { for (Map.Entry entry : containerValue) { - Variables.set(key, entry.getKey()); - Variables.set(value, entry.getValue()); + ScopeHandler.setVariable(key, entry.getKey()); + ScopeHandler.setVariable(value, entry.getValue()); try { body.execute(); } catch (BreakStatement bs) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index eb4c5c43..84c41448 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -64,11 +64,11 @@ private Function consumeFunction(Expression expr) { } private Function getFunction(String key) { - if (Functions.isExists(key)) { - return Functions.get(key); + if (ScopeHandler.isFunctionExists(key)) { + return ScopeHandler.getFunction(key); } - if (Variables.isExists(key)) { - final Value variable = Variables.get(key); + if (ScopeHandler.isVariableOrConstantExists(key)) { + final Value variable = ScopeHandler.getVariableOrConstant(key); if (variable.type() == Types.FUNCTION) { return ((FunctionValue)variable).getValue(); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 4a334404..8151bf7a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -1,11 +1,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.PatternMatchingException; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -44,18 +41,18 @@ public Value eval() { final VariablePattern pattern = (VariablePattern) p; if (pattern.variable.equals("_")) return evalResult(p.result); - if (Variables.isExists(pattern.variable)) { - if (match(value, Variables.get(pattern.variable)) && optMatches(p)) { + if (ScopeHandler.isVariableOrConstantExists(pattern.variable)) { + if (match(value, ScopeHandler.getVariableOrConstant(pattern.variable)) && optMatches(p)) { return evalResult(p.result); } } else { - Variables.define(pattern.variable, value); + ScopeHandler.defineVariableInCurrentScope(pattern.variable, value); if (optMatches(p)) { final Value result = evalResult(p.result); - Variables.remove(pattern.variable); + ScopeHandler.removeVariable(pattern.variable); return result; } - Variables.remove(pattern.variable); + ScopeHandler.removeVariable(pattern.variable); } } if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) { @@ -64,7 +61,7 @@ public Value eval() { // Clean up variables if matched final Value result = evalResult(p.result); for (String var : pattern.parts) { - Variables.remove(var); + ScopeHandler.removeVariable(var); } return result; } @@ -105,11 +102,11 @@ private boolean matchListPattern(ArrayValue array, ListPattern p) { case 1: // match arr { case [x]: x = arr ... } final String variable = parts.get(0); - Variables.define(variable, array); + ScopeHandler.defineVariableInCurrentScope(variable, array); if (optMatches(p)) { return true; } - Variables.remove(variable); + ScopeHandler.removeVariable(variable); return false; default: { // match arr { case [...]: .. } @@ -128,7 +125,7 @@ private boolean matchListPattern(ArrayValue array, ListPattern p) { private boolean matchListPatternEqualsSize(ListPattern p, List parts, int partsSize, ArrayValue array) { // Set variables for (int i = 0; i < partsSize; i++) { - Variables.define(parts.get(i), array.get(i)); + ScopeHandler.defineVariableInCurrentScope(parts.get(i), array.get(i)); } if (optMatches(p)) { // Clean up will be provided after evaluate result @@ -136,7 +133,8 @@ private boolean matchListPatternEqualsSize(ListPattern p, List parts, in } // Clean up variables if no match for (String var : parts) { - Variables.remove(var); + // TODO removing without checking shadowing is dangerous + ScopeHandler.removeVariable(var); } return false; } @@ -145,14 +143,14 @@ private boolean matchListPatternWithTail(ListPattern p, List parts, int // Set element variables final int lastPart = partsSize - 1; for (int i = 0; i < lastPart; i++) { - Variables.define(parts.get(i), array.get(i)); + ScopeHandler.defineVariableInCurrentScope(parts.get(i), array.get(i)); } // Set tail variable final ArrayValue tail = new ArrayValue(arraySize - partsSize + 1); for (int i = lastPart; i < arraySize; i++) { tail.set(i - lastPart, array.get(i)); } - Variables.define(parts.get(lastPart), tail); + ScopeHandler.defineVariableInCurrentScope(parts.get(lastPart), tail); // Check optional condition if (optMatches(p)) { // Clean up will be provided after evaluate result @@ -160,7 +158,7 @@ private boolean matchListPatternWithTail(ListPattern p, List parts, int } // Clean up variables for (String var : parts) { - Variables.remove(var); + ScopeHandler.removeVariable(var); } return false; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java index 35653a14..4d4bac02 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; @@ -30,7 +31,7 @@ public Value get() { @Override public Value set(Value value) { - Variables.set(name, value); + ScopeHandler.setVariable(name, value); return value; } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index e9bb36f0..a860c473 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -1,10 +1,7 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.outputsettings.OutputSettings; import com.annimon.ownlang.outputsettings.StringOutputSettings; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; @@ -33,8 +30,7 @@ public static Stream data() { @BeforeEach public void initialize() { - Variables.clear(); - Functions.clear(); + ScopeHandler.resetScope(); // Let's mock junit methods as ounit functions Functions.set("assertEquals", (args) -> { assertEquals(args[0], args[1]); From b726a226b93d57ac6bdd65d849d3be9affb81ce6 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 3 Sep 2023 21:52:57 +0300 Subject: [PATCH 297/448] Use new switch and pattern matching, fix deprecations --- .../ownlang/modules/base64/base64.java | 12 +--- .../annimon/ownlang/modules/files/files.java | 5 +- .../functional/functional_combine.java | 2 +- .../modules/functional/functional_sortby.java | 3 +- .../annimon/ownlang/modules/jdbc/jdbc.java | 2 +- .../ownlang/modules/okhttp/CallValue.java | 2 +- .../annimon/ownlang/modules/ounit/ounit.java | 11 ++-- .../ownlang/modules/std/std_range.java | 13 ++-- .../ownlang/modules/yaml/yaml_decode.java | 26 ++++---- .../main/java/com/annimon/ownlang/Main.java | 2 +- .../annimon/ownlang/parser/Beautifier.java | 6 +- .../annimon/ownlang/parser/SourceLoader.java | 3 +- .../ownlang/parser/ast/BinaryExpression.java | 37 +++++------ .../parser/ast/ContainerAccessExpression.java | 48 +++++--------- .../ast/DestructuringAssignmentStatement.java | 8 +-- .../parser/ast/ForeachArrayStatement.java | 15 ++--- .../parser/ast/ForeachMapStatement.java | 15 ++--- .../parser/ast/FunctionalExpression.java | 4 +- .../ownlang/parser/ast/MatchExpression.java | 33 +++++----- .../ownlang/parser/ast/UseStatement.java | 25 +++---- .../UseWithNonStringValueValidator.java | 19 +++--- .../optimization/OptimizationVisitor.java | 3 +- .../parser/visitors/ModuleDetector.java | 8 +-- .../ownlang/parser/visitors/PrintVisitor.java | 7 +- .../com/annimon/ownlang/parser/LexerTest.java | 2 +- .../annimon/ownlang/parser/ProgramsTest.java | 2 +- .../ownlang/utils/ModulesInfoCreator.java | 66 +++++++++---------- .../ownlang/utils/OptimizationDumper.java | 3 +- .../java/com/annimon/ownlang/utils/Repl.java | 2 +- 29 files changed, 161 insertions(+), 223 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java index 328ffa93..ff16c1f5 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.Base64; public class base64 implements Module { @@ -45,17 +45,11 @@ private Value base64decode(Value... args) { private byte[] getInputToEncode(Value[] args) { - byte[] input; if (args[0].type() == Types.ARRAY) { - input = ValueUtils.toByteArray((ArrayValue) args[0]); + return ValueUtils.toByteArray((ArrayValue) args[0]); } else { - try { - input = args[0].asString().getBytes("UTF-8"); - } catch (UnsupportedEncodingException ex) { - input = args[0].asString().getBytes(); - } + return args[0].asString().getBytes(StandardCharsets.UTF_8); } - return input; } private Base64.Encoder getEncoder(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java index f65c9dd8..74d04cc5 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -16,6 +16,7 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -138,7 +139,7 @@ private Value process(File file, String mode) throws IOException { if (mode.contains("rb")) { dis = new DataInputStream(new FileInputStream(file)); } else if (mode.contains("r")) { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)); } DataOutputStream dos = null; @@ -147,7 +148,7 @@ private Value process(File file, String mode) throws IOException { if (mode.contains("wb")) { dos = new DataOutputStream(new FileOutputStream(file, append)); } else if (mode.contains("w")) { - writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), "UTF-8")); + writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), StandardCharsets.UTF_8)); } final int key = files.size(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java index 33458de1..08516bb8 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java @@ -15,7 +15,7 @@ public Value execute(Value... args) { Function result = null; for (Value arg : args) { if (arg.type() != Types.FUNCTION) { - throw new TypeException(arg.toString() + " is not a function"); + throw new TypeException(arg + " is not a function"); } final Function current = result; final Function next = ((FunctionValue) arg).getValue(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java index 7ce988b2..71f38203 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java @@ -8,6 +8,7 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.ValueUtils; import java.util.Arrays; +import java.util.Comparator; public final class functional_sortby implements Function { @@ -20,7 +21,7 @@ public Value execute(Value... args) { 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))); + Arrays.sort(elements, Comparator.comparing(function::execute)); return new ArrayValue(elements); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index e72cbdca..176342ad 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -430,7 +430,7 @@ private void init() { set("getBigDecimal", getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); set("getBoolean", getBooleanResult(rs::getBoolean, rs::getBoolean)); set("getByte", getNumberResult(rs::getByte, rs::getByte)); - set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); + set("getBytes", getObjectResult(rs::getBytes, rs::getBytes, ArrayValue::of)); set("getDate", getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); set("getDouble", getNumberResult(rs::getDouble, rs::getDouble)); set("getFloat", getNumberResult(rs::getFloat, rs::getFloat)); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java index 3f967220..c9452bac 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java @@ -36,7 +36,7 @@ private Value enqueue(Value[] args) { final Function onResponse = ValueUtils.consumeFunction(args[0], 0); call.enqueue(new Callback() { @Override - public void onResponse(Call call, Response response) throws IOException { + public void onResponse(Call call, Response response) { onResponse.execute(new CallValue(call), new ResponseValue(response)); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java index 1a129032..e912fb98 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -11,7 +11,6 @@ import com.annimon.ownlang.modules.Module; import java.text.DecimalFormat; import java.util.List; -import java.util.stream.Collectors; /** * @@ -92,7 +91,7 @@ public Value execute(Value... args) { List tests = Functions.getFunctions().entrySet().stream() .filter(e -> e.getKey().toLowerCase().startsWith("test")) .map(e -> runTest(e.getKey(), e.getValue())) - .collect(Collectors.toList()); + .toList(); int failures = 0; long summaryTime = 0; @@ -135,10 +134,10 @@ public OUnitAssertionException(String message) { } private static class TestInfo { - String name; - boolean isPassed; - String failureDescription; - long elapsedTimeInMicros; + final String name; + final boolean isPassed; + final String failureDescription; + final long elapsedTimeInMicros; public TestInfo(String name, boolean isPassed, String failureDescription, long elapsedTimeInMicros) { this.name = name; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java index fdad6822..820ece96 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java @@ -128,7 +128,7 @@ public Iterator iterator() { if (isIntegerRange()) { final int toInt = (int) to; final int stepInt = (int) step; - return new Iterator() { + return new Iterator<>() { int value = (int) from; @@ -145,10 +145,11 @@ public Value next() { } @Override - public void remove() { } + public void remove() { + } }; } - return new Iterator() { + return new Iterator<>() { long value = from; @@ -165,7 +166,8 @@ public Value next() { } @Override - public void remove() { } + public void remove() { + } }; } @@ -197,8 +199,7 @@ public int compareTo(Value o) { final int lengthCompare = Integer.compare(size(), ((ArrayValue) o).size()); if (lengthCompare != 0) return lengthCompare; - if (o instanceof RangeValue) { - final RangeValue o2 = ((RangeValue) o); + if (o instanceof RangeValue o2) { int compareResult; compareResult = Long.compare(this.from, o2.from); if (compareResult != 0) return compareResult; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java index 472b7430..a8a0db4a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -31,28 +31,28 @@ private void configure(LoaderOptions options, MapValue map) { } private Value process(Object obj) { - if (obj instanceof Map) { - return process((Map) obj); + if (obj instanceof Map map) { + return process(map); } - if (obj instanceof List) { - return process((List) obj); + if (obj instanceof List list) { + return process(list); } - if (obj instanceof String) { - return new StringValue((String) obj); + if (obj instanceof String str) { + return new StringValue(str); } - if (obj instanceof Number) { - return NumberValue.of(((Number) obj)); + if (obj instanceof Number number) { + return NumberValue.of(number); } - if (obj instanceof Boolean) { - return NumberValue.fromBoolean((Boolean) obj); + if (obj instanceof Boolean bool) { + return NumberValue.fromBoolean(bool); } // NULL or other return NumberValue.ZERO; } - private MapValue process(Map map) { + private MapValue process(Map map) { final MapValue result = new MapValue(new LinkedHashMap<>(map.size())); - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { final String key = entry.getKey().toString(); final Value value = process(entry.getValue()); result.set(new StringValue(key), value); @@ -60,7 +60,7 @@ private MapValue process(Map map) { return result; } - private ArrayValue process(List list) { + private ArrayValue process(List list) { final int length = list.size(); final ArrayValue result = new ArrayValue(length); for (int i = 0; i < length; i++) { diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 499edc72..d957c7c8 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -159,7 +159,7 @@ private static void run(String input, RunOptions options) { final Statement parsedProgram = parser.parse(); measurement.stop("Parse time"); if (options.showAst) { - System.out.println(parsedProgram.toString()); + System.out.println(parsedProgram); } if (parser.getParseErrors().hasErrors()) { System.out.println(parser.getParseErrors()); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java index 064506eb..ae96ced2 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java @@ -246,9 +246,7 @@ private void indent() { } private void indent(int count) { - for (int i = 0; i < count; i++) { - beautifiedCode.append(' '); - } + beautifiedCode.append(" ".repeat(Math.max(0, count))); } private void skipTo(String text) { @@ -262,7 +260,7 @@ private void skipTo(String text) { } private void skipTo(int position) { - beautifiedCode.append(input.substring(pos, position)); + beautifiedCode.append(input, pos, position); pos += (position - pos); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java index 4621d138..ac8c5406 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java @@ -4,6 +4,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; public final class SourceLoader { @@ -26,6 +27,6 @@ public static String readAndCloseStream(InputStream is) throws IOException { result.write(buffer, 0, read); } is.close(); - return result.toString("UTF-8"); + return result.toString(StandardCharsets.UTF_8); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java index b6cdab0b..d8e49d3a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -74,22 +74,21 @@ public Value eval() { } private Value eval(Value value1, Value value2) { - 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: - throw new OperationIsNotSupportedException(operation); - } + return switch (operation) { + case ADD -> add(value1, value2); + case SUBTRACT -> subtract(value1, value2); + case MULTIPLY -> multiply(value1, value2); + case DIVIDE -> divide(value1, value2); + case REMAINDER -> remainder(value1, value2); + case PUSH -> push(value1, value2); + case AND -> and(value1, value2); + case OR -> or(value1, value2); + case XOR -> xor(value1, value2); + case LSHIFT -> lshift(value1, value2); + case RSHIFT -> rshift(value1, value2); + case URSHIFT -> urshift(value1, value2); + default -> throw new OperationIsNotSupportedException(operation); + }; } private Value add(Value value1, Value value2) { @@ -217,11 +216,7 @@ private Value multiply(NumberValue value1, Value value2) { private Value multiply(StringValue value1, Value value2) { 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()); + return new StringValue(String.valueOf(string1).repeat(Math.max(0, iterations))); } private Value divide(Value value1, Value value2) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 6ee83055..e6af0506 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -41,22 +41,13 @@ public Value eval() { public Value get() { final Value container = getContainer(); final Value lastIndex = lastIndex(); - switch (container.type()) { - case Types.ARRAY: - return ((ArrayValue) container).get(lastIndex); - - case Types.MAP: - return ((MapValue) container).get(lastIndex); - - case Types.STRING: - return ((StringValue) container).access(lastIndex); - - case Types.CLASS: - return ((ClassInstanceValue) container).access(lastIndex); - - default: - throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); - } + return switch (container.type()) { + case Types.ARRAY -> ((ArrayValue) container).get(lastIndex); + case Types.MAP -> ((MapValue) container).get(lastIndex); + case Types.STRING -> ((StringValue) container).access(lastIndex); + case Types.CLASS -> ((ClassInstanceValue) container).access(lastIndex); + default -> throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); + }; } @Override @@ -65,18 +56,17 @@ public Value set(Value value) { final Value lastIndex = lastIndex(); switch (container.type()) { case Types.ARRAY: - final int arrayIndex = lastIndex.asInt(); - ((ArrayValue) container).set(arrayIndex, value); + ((ArrayValue) container).set(lastIndex.asInt(), value); return value; case Types.MAP: ((MapValue) container).set(lastIndex, value); return value; - + case Types.CLASS: ((ClassInstanceValue) container).set(lastIndex, value); return value; - + default: throw new TypeException("Array or map expected. Got " + container.type()); } @@ -87,19 +77,11 @@ public Value getContainer() { final int last = indices.size() - 1; for (int i = 0; i < last; i++) { final Value index = index(i); - switch (container.type()) { - case Types.ARRAY: - final int arrayIndex = index.asInt(); - container = ((ArrayValue) container).get(arrayIndex); - break; - - case Types.MAP: - container = ((MapValue) container).get(index); - break; - - default: - throw new TypeException("Array or map expected"); - } + container = switch (container.type()) { + case Types.ARRAY -> ((ArrayValue) container).get(index.asInt()); + case Types.MAP -> ((MapValue) container).get(index); + default -> throw new TypeException("Array or map expected"); + }; } return container; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java index edf08859..9e7a7c0b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java @@ -25,12 +25,8 @@ public void execute() { super.interruptionCheck(); final Value container = containerExpression.eval(); switch (container.type()) { - case Types.ARRAY: - execute((ArrayValue) container); - break; - case Types.MAP: - execute((MapValue) container); - break; + case Types.ARRAY -> execute((ArrayValue) container); + case Types.MAP -> execute((MapValue) container); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java index 921eda9a..30b4b992 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -28,17 +28,10 @@ public void execute() { final Value containerValue = container.eval(); switch (containerValue.type()) { - case Types.STRING: - iterateString(containerValue.asString()); - break; - case Types.ARRAY: - iterateArray((ArrayValue) containerValue); - break; - case Types.MAP: - iterateMap((MapValue) containerValue); - break; - default: - throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); + case Types.STRING -> iterateString(containerValue.asString()); + case Types.ARRAY -> iterateArray((ArrayValue) containerValue); + case Types.MAP -> iterateMap((MapValue) containerValue); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); } // Restore variables diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java index 32bfd494..b0588ae6 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -30,17 +30,10 @@ public void execute() { final Value containerValue = container.eval(); switch (containerValue.type()) { - case Types.STRING: - iterateString(containerValue.asString()); - break; - case Types.ARRAY: - iterateArray((ArrayValue) containerValue); - break; - case Types.MAP: - iterateMap((MapValue) containerValue); - break; - default: - throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); + case Types.STRING -> iterateString(containerValue.asString()); + case Types.ARRAY -> iterateArray((ArrayValue) containerValue); + case Types.MAP -> iterateMap((MapValue) containerValue); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); } // Restore variables diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 84c41448..0ffc8338 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -89,8 +89,8 @@ public R accept(ResultVisitor visitor, T t) { @Override public String toString() { final StringBuilder sb = new StringBuilder(); - if (functionExpr instanceof ValueExpression && ((ValueExpression)functionExpr).value.type() == Types.STRING) { - sb.append(((ValueExpression)functionExpr).value.asString()).append('('); + if (functionExpr instanceof ValueExpression valueExpr && (valueExpr.value.type() == Types.STRING)) { + sb.append(valueExpr.value.asString()).append('('); } else { sb.append(functionExpr).append('('); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 8151bf7a..e1a34045 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -31,14 +31,12 @@ public Value eval() { super.interruptionCheck(); final Value value = expression.eval(); for (Pattern p : patterns) { - if (p instanceof ConstantPattern) { - final ConstantPattern pattern = (ConstantPattern) p; + if (p instanceof ConstantPattern pattern) { if (match(value, pattern.constant) && optMatches(p)) { return evalResult(p.result); } } - if (p instanceof VariablePattern) { - final VariablePattern pattern = (VariablePattern) p; + if (p instanceof VariablePattern pattern) { if (pattern.variable.equals("_")) return evalResult(p.result); if (ScopeHandler.isVariableOrConstantExists(pattern.variable)) { @@ -55,8 +53,7 @@ public Value eval() { ScopeHandler.removeVariable(pattern.variable); } } - if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) { - final ListPattern pattern = (ListPattern) p; + if ((value.type() == Types.ARRAY) && (p instanceof ListPattern pattern)) { if (matchListPattern((ArrayValue) value, pattern)) { // Clean up variables if matched final Value result = evalResult(p.result); @@ -66,8 +63,7 @@ public Value eval() { return result; } } - if ((value.type() == Types.ARRAY) && (p instanceof TuplePattern)) { - final TuplePattern pattern = (TuplePattern) p; + if ((value.type() == Types.ARRAY) && (p instanceof TuplePattern pattern)) { if (matchTuplePattern((ArrayValue) value, pattern) && optMatches(p)) { return evalResult(p.result); } @@ -106,6 +102,7 @@ private boolean matchListPattern(ArrayValue array, ListPattern p) { if (optMatches(p)) { return true; } + // TODO remove is dangerous ScopeHandler.removeVariable(variable); return false; @@ -203,7 +200,7 @@ public String toString() { return sb.toString(); } - public abstract static class Pattern { + public abstract static sealed class Pattern { public Statement result; public Expression optCondition; @@ -218,8 +215,8 @@ public String toString() { } } - public static class ConstantPattern extends Pattern { - Value constant; + public static final class ConstantPattern extends Pattern { + final Value constant; public ConstantPattern(Value pattern) { this.constant = pattern; @@ -231,8 +228,8 @@ public String toString() { } } - public static class VariablePattern extends Pattern { - public String variable; + public static final class VariablePattern extends Pattern { + public final String variable; public VariablePattern(String pattern) { this.variable = pattern; @@ -244,8 +241,8 @@ public String toString() { } } - public static class ListPattern extends Pattern { - List parts; + public static final class ListPattern extends Pattern { + final List parts; public ListPattern() { this(new ArrayList<>()); @@ -275,11 +272,11 @@ public String toString() { } } - public static class TuplePattern extends Pattern { - public List values; + public static final class TuplePattern extends Pattern { + public final List values; public TuplePattern() { - this(new ArrayList()); + this(new ArrayList<>()); } public TuplePattern(List parts) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 79f4e401..d381dc1b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -27,22 +27,21 @@ public void execute() { super.interruptionCheck(); final Value value = expression.eval(); switch (value.type()) { - case Types.ARRAY: + case Types.ARRAY -> { for (Value module : ((ArrayValue) value)) { loadModule(module.asString()); } - break; - case Types.STRING: - loadModule(value.asString()); - break; - default: - throw typeException(value); + } + case Types.STRING -> loadModule(value.asString()); + default -> throw typeException(value); } } private void loadModule(String name) { try { - final Module module = (Module) Class.forName(String.format(PACKAGE, name, name)).newInstance(); + final Module module = (Module) Class.forName(String.format(PACKAGE, name, name)) + .getDeclaredConstructor() + .newInstance(); module.init(); } catch (Exception ex) { throw new RuntimeException("Unable to load module " + name, ex); @@ -50,14 +49,12 @@ private void loadModule(String name) { } public void loadConstants() { - if (expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) expression; + if (expression instanceof ArrayExpression ae) { for (Expression expr : ae.elements) { loadConstants(expr.eval().asString()); } } - if (expression instanceof ValueExpression) { - ValueExpression ve = (ValueExpression) expression; + if (expression instanceof ValueExpression ve) { loadConstants(ve.value.asString()); } } @@ -71,9 +68,7 @@ private void loadConstants(String moduleName) { try { final Class moduleClass = Class.forName(String.format(PACKAGE, moduleName, moduleName)); final Method method = moduleClass.getMethod(INIT_CONSTANTS_METHOD); - if (method != null) { - method.invoke(this); - } + method.invoke(this); } catch (Exception ex) { // ignore } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java index 451be324..a8b24417 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java @@ -17,8 +17,7 @@ public void visit(IncludeStatement st) { public void visit(UseStatement st) { super.visit(st); - if (st.expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) st.expression; + if (st.expression instanceof ArrayExpression ae) { for (Expression expr : ae.elements) { if (!checkExpression(expr)) { return; @@ -32,18 +31,18 @@ public void visit(UseStatement st) { } private boolean checkExpression(Expression expr) { - if (!(expr instanceof ValueExpression)) { + if (expr instanceof ValueExpression valueExpr) { + final Value value = valueExpr.value; + if (value.type() != Types.STRING) { + warnWrongType(value); + return false; + } + return true; + } else { Console.error(String.format( "Warning: `use` with %s, not ValueExpression", expr.getClass().getSimpleName())); return false; } - - final Value value = ((ValueExpression) expr).value; - if (value.type() != Types.STRING) { - warnWrongType(value); - return false; - } - return true; } private void warnWrongType(Value value) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index aedc7475..6fb81e31 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -386,8 +386,7 @@ public Node visit(UnaryExpression s, T t) { @Override public Node visit(ValueExpression s, T t) { - if ( (s.value.type() == Types.FUNCTION) && (s.value.raw() instanceof UserDefinedFunction) ) { - final UserDefinedFunction function = (UserDefinedFunction) s.value.raw(); + if ( (s.value.type() == Types.FUNCTION) && (s.value.raw() instanceof UserDefinedFunction function) ) { final UserDefinedFunction accepted = visit(function, t); if (accepted != function) { return new ValueExpression(accepted); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java index 98f291cb..3bfce125 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -10,7 +10,7 @@ public class ModuleDetector extends AbstractVisitor { - private Set modules; + private final Set modules; public ModuleDetector() { modules = new HashSet<>(); @@ -23,14 +23,12 @@ public Set detect(Statement s) { @Override public void visit(UseStatement st) { - if (st.expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) st.expression; + if (st.expression instanceof ArrayExpression ae) { for (Expression expr : ae.elements) { modules.add(expr.eval().asString()); } } - if (st.expression instanceof ValueExpression) { - ValueExpression ve = (ValueExpression) st.expression; + if (st.expression instanceof ValueExpression ve) { modules.add(ve.value.asString()); } super.visit(st); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 7d3ef2e6..1da2dcce 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -373,8 +373,7 @@ public StringBuilder visit(ValueExpression s, StringBuilder t) { break; case Types.FUNCTION: { final Function function = ((FunctionValue) s.value).getValue(); - if (function instanceof UserDefinedFunction) { - UserDefinedFunction f = (UserDefinedFunction) function; + if (function instanceof UserDefinedFunction f) { t.append("def"); t.append(f.arguments); return visitFunctionBody(f.body, t); @@ -457,9 +456,7 @@ private void newLine(StringBuilder t) { } private void printIndent(StringBuilder sb) { - for (int i = 0; i < indent; i++) { - sb.append(' '); - } + sb.append(" ".repeat(Math.max(0, indent))); } private void increaseIndent() { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index e88f26ae..0f05acb1 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -164,7 +164,7 @@ private static void assertTokens(List expList, List result) { } private static List list(TokenType... types) { - final List list = new ArrayList(); + final List list = new ArrayList<>(); for (TokenType t : types) { list.add(token(t)); } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index a860c473..efd62701 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -88,7 +88,7 @@ public void testOutput() throws IOException { } } - private static Visitor testFunctionsExecutor = new AbstractVisitor() { + private static final Visitor testFunctionsExecutor = new AbstractVisitor() { @Override public void visit(FunctionDefineStatement s) { if (s.name.startsWith("test")) { diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index 7cc70689..333f4bf0 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -1,10 +1,6 @@ package com.annimon.ownlang.utils; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.io.File; import java.util.*; @@ -20,14 +16,14 @@ public final class ModulesInfoCreator { private static final String MODULES_PATH = "src/main/java/com/annimon/ownlang/modules"; public static void main(String[] args) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { + throws ReflectiveOperationException { final Class clazz = Module.class; // get classloader for package final List moduleInfos = new ArrayList<>(); String[] moduleNames = Optional.ofNullable(new File(MODULES_PATH).listFiles()) - .map(Arrays::stream) - .orElse(Stream.empty()) + .stream() + .flatMap(Arrays::stream) .filter(File::isDirectory) .map(File::getName) .toArray(String[]::new); @@ -36,7 +32,7 @@ public static void main(String[] args) Class moduleClass = Class.forName(moduleClassPath); Functions.getFunctions().clear(); Variables.variables().clear(); - final Module module = (Module) moduleClass.newInstance(); + final Module module = (Module) moduleClass.getDeclaredConstructor().newInstance(); module.init(); final ModuleInfo moduleInfo = new ModuleInfo(moduleName); @@ -80,24 +76,26 @@ private static void printAsYaml(List moduleInfos) { System.out.println(new Yaml(options).dump(infos)); } - private static List listValues(Class moduleClass) { + private static List listValues(Class moduleClass) { return Arrays.stream(moduleClass.getDeclaredClasses()) .filter(clazz -> getAllInterfaces(clazz).stream().anyMatch(i -> i.equals(Value.class))) .map(Class::getSimpleName) .collect(Collectors.toList()); } - private static Set getAllInterfaces(Class clazz) { - if (clazz.getSuperclass() == null) return Collections.emptySet(); + private static Set> getAllInterfaces(Class clazz) { + if (clazz.getSuperclass() == null) { + return Collections.emptySet(); + } return Stream.concat(Arrays.stream(clazz.getInterfaces()), getAllInterfaces(clazz.getSuperclass()).stream()) .collect(Collectors.toSet()); } static class ModuleInfo { private final String name; - List functions; - Map constants; - List types; + final List functions; + final Map constants; + final List types; public ModuleInfo(String name) { this.name = name; @@ -122,26 +120,26 @@ public List> functions() { public List> constants() { final List> result = new ArrayList<>(); constants.entrySet().stream() - .sorted(Comparator.comparing(Map.Entry::getKey)) + .sorted(Map.Entry.comparingByKey()) .forEach(entry -> { - final Value value = entry.getValue(); - - final Map constant = new LinkedHashMap<>(); - constant.put("name", entry.getKey()); - constant.put("type", value.type()); - constant.put("typeName", Types.typeToString(value.type())); - if (value.type() == Types.MAP) { - String text = ((MapValue) value).getMap().entrySet().stream() - .sorted(Comparator.comparing( - e -> ((MapValue)value).size() > 16 ? e.getKey() : e.getValue())) - .map(Object::toString) - .collect(Collectors.joining(", ", "{", "}")); - constant.put("value", text); - } else { - constant.put("value", value.asString()); - } - result.add(constant); - }); + final Value value = entry.getValue(); + + final Map constant = new LinkedHashMap<>(); + constant.put("name", entry.getKey()); + constant.put("type", value.type()); + constant.put("typeName", Types.typeToString(value.type())); + if (value.type() == Types.MAP) { + String text = ((MapValue) value).getMap().entrySet().stream() + .sorted(Comparator.comparing( + e -> ((MapValue)value).size() > 16 ? e.getKey() : e.getValue())) + .map(Object::toString) + .collect(Collectors.joining(", ", "{", "}")); + constant.put("value", text); + } else { + constant.put("value", value.asString()); + } + result.add(constant); + }); return result; } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java index c4d7797c..d30001eb 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java @@ -17,6 +17,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; @@ -103,7 +104,7 @@ private static void writeSummary(final Map optimizationSteps) th private static void writeContent(File file, ThrowableConsumer consumer) throws IOException { try (OutputStream out = new FileOutputStream(file); - OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8")) { + OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) { consumer.accept(writer); } } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 89652968..fd4c5032 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -46,7 +46,7 @@ public static void main(String[] args) { final StringBuilder buffer = new StringBuilder(); final ReplConsole console = initReplConsole(); while (true) { - console.setPrompt((buffer.length() == 0) ? "\n> " : " "); + console.setPrompt((buffer.isEmpty()) ? "\n> " : " "); final String line = console.readLine(); if (line == null || EXIT.equalsIgnoreCase(line)) break; From 2db88523bcbfd76c475687fc6f83211fb4ae2e8b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 4 Sep 2023 21:10:16 +0300 Subject: [PATCH 298/448] Switch to ScopeHandler in modules --- .../ownlang/modules/canvasfx/canvasfx.java | 296 ++++++++---------- .../ownlang/modules/base64/base64.java | 14 +- .../ownlang/modules/canvas/canvas.java | 59 ++-- .../modules/collections/collections.java | 18 +- .../annimon/ownlang/modules/date/date.java | 28 +- .../modules/downloader/downloader.java | 16 +- .../annimon/ownlang/modules/files/files.java | 112 +++---- .../ownlang/modules/forms/ContainerValue.java | 6 +- .../modules/forms/JTextFieldValue.java | 2 +- .../annimon/ownlang/modules/forms/forms.java | 44 +-- .../modules/functional/functional.java | 27 +- .../modules/functional/functional_chain.java | 2 +- .../functional/functional_combine.java | 2 +- .../functional/functional_dropwhile.java | 2 +- .../modules/functional/functional_filter.java | 2 +- .../functional/functional_flatmap.java | 2 +- .../functional/functional_foreach.java | 2 +- .../modules/functional/functional_map.java | 2 +- .../modules/functional/functional_reduce.java | 2 +- .../modules/functional/functional_sortby.java | 2 +- .../annimon/ownlang/modules/gzip/gzip.java | 8 +- .../annimon/ownlang/modules/http/http.java | 8 +- .../ownlang/modules/http/http_download.java | 2 +- .../ownlang/modules/http/http_http.java | 2 +- .../ownlang/modules/http/http_urlencode.java | 2 +- .../annimon/ownlang/modules/java/java.java | 72 ++--- .../annimon/ownlang/modules/jdbc/jdbc.java | 85 +++-- .../annimon/ownlang/modules/json/json.java | 6 +- .../ownlang/modules/json/json_decode.java | 2 +- .../annimon/ownlang/modules/math/math.java | 90 +++--- .../ownlang/modules/okhttp/okhttp.java | 14 +- .../annimon/ownlang/modules/ounit/ounit.java | 32 +- .../annimon/ownlang/modules/regex/regex.java | 13 +- .../annimon/ownlang/modules/robot/robot.java | 44 +-- .../ownlang/modules/robot/robot_exec.java | 2 +- .../modules/robot/robot_fromclipboard.java | 2 +- .../modules/robot/robot_toclipboard.java | 2 +- .../ownlang/modules/socket/socket.java | 56 ++-- .../ownlang/modules/std/NumberFunctions.java | 2 +- .../com/annimon/ownlang/modules/std/std.java | 80 ++--- .../ownlang/modules/std/std_arrayCombine.java | 2 +- .../modules/std/std_arrayKeyExists.java | 2 +- .../ownlang/modules/std/std_arrayKeys.java | 2 +- .../ownlang/modules/std/std_arraySplice.java | 2 +- .../ownlang/modules/std/std_arrayValues.java | 2 +- .../ownlang/modules/std/std_charat.java | 2 +- .../ownlang/modules/std/std_default.java | 2 +- .../annimon/ownlang/modules/std/std_echo.java | 2 +- .../ownlang/modules/std/std_indexof.java | 2 +- .../annimon/ownlang/modules/std/std_join.java | 2 +- .../ownlang/modules/std/std_lastindexof.java | 2 +- .../ownlang/modules/std/std_length.java | 2 +- .../ownlang/modules/std/std_newarray.java | 2 +- .../annimon/ownlang/modules/std/std_rand.java | 2 +- .../ownlang/modules/std/std_range.java | 2 +- .../ownlang/modules/std/std_readln.java | 2 +- .../ownlang/modules/std/std_replace.java | 2 +- .../ownlang/modules/std/std_replaceall.java | 2 +- .../ownlang/modules/std/std_replacefirst.java | 2 +- .../ownlang/modules/std/std_sleep.java | 2 +- .../annimon/ownlang/modules/std/std_sort.java | 2 +- .../ownlang/modules/std/std_split.java | 2 +- .../ownlang/modules/std/std_sprintf.java | 2 +- .../ownlang/modules/std/std_substring.java | 2 +- .../annimon/ownlang/modules/std/std_sync.java | 2 +- .../ownlang/modules/std/std_thread.java | 2 +- .../annimon/ownlang/modules/std/std_time.java | 2 +- .../ownlang/modules/std/std_tochar.java | 2 +- .../ownlang/modules/std/std_tolowercase.java | 2 +- .../ownlang/modules/std/std_touppercase.java | 2 +- .../annimon/ownlang/modules/std/std_trim.java | 2 +- .../annimon/ownlang/modules/std/std_try.java | 2 +- .../annimon/ownlang/modules/types/types.java | 30 +- .../annimon/ownlang/modules/yaml/yaml.java | 4 +- .../ownlang/modules/yaml/yaml_decode.java | 2 +- .../ownlang/modules/yaml/yaml_encode.java | 2 +- .../com/annimon/ownlang/modules/zip/zip.java | 10 +- 77 files changed, 599 insertions(+), 677 deletions(-) diff --git a/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java b/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java index e1568025..1247d655 100644 --- a/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java +++ b/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java @@ -47,10 +47,8 @@ public final class canvasfx implements Module { private static final int FX_EFFECT_TYPE = 5301; private static final int FX_COLOR_TYPE = 5302; - - private static JFrame frame; + private static JFXPanel panel; - private static GraphicsContext graphics; private static Canvas canvas; private enum Events { @@ -76,7 +74,7 @@ private enum Events { private final EventType handler; - private Events(EventType handler) { + Events(EventType handler) { this.handler = handler; } @@ -100,94 +98,94 @@ public static void initConstants() { colors.put(new StringValue("rgb"), new FunctionValue(new rgbColor())); colors.put(new StringValue("hsb"), new FunctionValue(new hsbColor())); colors.put(new StringValue("web"), new FunctionValue(new webColor())); - Variables.define("Color", new MapValue(colors)); + ScopeHandler.setConstant("Color", new MapValue(colors)); final MapValue arcType = new MapValue(ArcType.values().length); for (ArcType value : ArcType.values()) { arcType.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("ArcType", arcType); + ScopeHandler.setConstant("ArcType", arcType); final MapValue fillRule = new MapValue(FillRule.values().length); for (FillRule value : FillRule.values()) { fillRule.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("FillRule", fillRule); + ScopeHandler.setConstant("FillRule", fillRule); final MapValue blendMode = new MapValue(BlendMode.values().length); for (BlendMode value : BlendMode.values()) { blendMode.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("BlendMode", blendMode); + ScopeHandler.setConstant("BlendMode", blendMode); final MapValue lineCap = new MapValue(StrokeLineCap.values().length); for (StrokeLineCap value : StrokeLineCap.values()) { lineCap.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("StrokeLineCap", lineCap); + ScopeHandler.setConstant("StrokeLineCap", lineCap); final MapValue lineJoin = new MapValue(StrokeLineJoin.values().length); for (StrokeLineJoin value : StrokeLineJoin.values()) { lineJoin.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("StrokeLineJoin", lineJoin); + ScopeHandler.setConstant("StrokeLineJoin", lineJoin); final MapValue textAlignment = new MapValue(TextAlignment.values().length); for (TextAlignment value : TextAlignment.values()) { textAlignment.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("TextAlignment", textAlignment); + ScopeHandler.setConstant("TextAlignment", textAlignment); final MapValue vPos = new MapValue(VPos.values().length); for (VPos value : VPos.values()) { vPos.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("VPos", vPos); + ScopeHandler.setConstant("VPos", vPos); final MapValue events = new MapValue(Events.values().length); for (Events value : Events.values()) { events.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("Events", events); + ScopeHandler.setConstant("Events", events); final MapValue mouseButton = new MapValue(MouseButton.values().length); for (MouseButton value : MouseButton.values()) { mouseButton.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("MouseButton", mouseButton); + ScopeHandler.setConstant("MouseButton", mouseButton); final MapValue keyCodes = new MapValue(KeyCode.values().length); for (KeyCode value : KeyCode.values()) { keyCodes.set(value.name(), NumberValue.of(value.ordinal())); } - Variables.define("KeyCode", keyCodes); + ScopeHandler.setConstant("KeyCode", keyCodes); } @Override public void init() { initConstants(); - Functions.set("window", new CreateWindow()); - Functions.set("repaint", new Repaint()); + ScopeHandler.setFunction("window", new CreateWindow()); + ScopeHandler.setFunction("repaint", new Repaint()); - Functions.set("BlendEffect", new BlendEffect()); - Functions.set("BloomEffect", new BloomEffect()); - Functions.set("BoxBlurEffect", new BoxBlurEffect()); - Functions.set("ColorAdjustEffect", new ColorAdjustEffect()); - Functions.set("ColorInputEffect", new ColorInputEffect()); - Functions.set("DropShadowEffect", new DropShadowEffect()); - Functions.set("GaussianBlurEffect", new GaussianBlurEffect()); - Functions.set("GlowEffect", new GlowEffect()); - Functions.set("InnerShadowEffect", new InnerShadowEffect()); - Functions.set("LightingEffect", new LightingEffect()); - Functions.set("MotionBlurEffect", new MotionBlurEffect()); - Functions.set("PerspectiveTransformEffect", new PerspectiveTransformEffect()); - Functions.set("ReflectionEffect", new ReflectionEffect()); - Functions.set("SepiaToneEffect", new SepiaToneEffect()); - Functions.set("ShadowEffect", new ShadowEffect()); + ScopeHandler.setFunction("BlendEffect", new BlendEffect()); + ScopeHandler.setFunction("BloomEffect", new BloomEffect()); + ScopeHandler.setFunction("BoxBlurEffect", new BoxBlurEffect()); + ScopeHandler.setFunction("ColorAdjustEffect", new ColorAdjustEffect()); + ScopeHandler.setFunction("ColorInputEffect", new ColorInputEffect()); + ScopeHandler.setFunction("DropShadowEffect", new DropShadowEffect()); + ScopeHandler.setFunction("GaussianBlurEffect", new GaussianBlurEffect()); + ScopeHandler.setFunction("GlowEffect", new GlowEffect()); + ScopeHandler.setFunction("InnerShadowEffect", new InnerShadowEffect()); + ScopeHandler.setFunction("LightingEffect", new LightingEffect()); + ScopeHandler.setFunction("MotionBlurEffect", new MotionBlurEffect()); + ScopeHandler.setFunction("PerspectiveTransformEffect", new PerspectiveTransformEffect()); + ScopeHandler.setFunction("ReflectionEffect", new ReflectionEffect()); + ScopeHandler.setFunction("SepiaToneEffect", new SepiaToneEffect()); + ScopeHandler.setFunction("ShadowEffect", new ShadowEffect()); - Functions.set("addEventFilter", new addEventFilter()); - Functions.set("addEventHandler", new addEventHandler()); - Functions.set("createImage", new createImage()); + ScopeHandler.setFunction("addEventFilter", new addEventFilter()); + ScopeHandler.setFunction("addEventHandler", new addEventHandler()); + ScopeHandler.setFunction("createImage", new createImage()); } private static class ColorValue implements Value { @@ -242,7 +240,7 @@ public String toString() { private static class newColor implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { double r, g, b, opacity; if (args.length == 1) { final int color = args[0].asInt(); @@ -263,7 +261,7 @@ public Value execute(Value... args) { private static class rgbColor implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { int r = args[0].asInt(); int g = args[1].asInt(); int b = args[2].asInt(); @@ -275,7 +273,7 @@ public Value execute(Value... args) { private static class hsbColor implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { double h = args[0].asNumber(); double s = args[1].asNumber(); double b = args[2].asNumber(); @@ -287,7 +285,7 @@ public Value execute(Value... args) { private static class webColor implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return new ColorValue(Color.web(args[0].asString(), (args.length >= 2) ? args[1].asNumber() : 1d )); } @@ -341,7 +339,7 @@ public String toString() { private static class BlendEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Blend effect = new Blend(); if (args.length >= 1) { effect.setMode(BlendMode.values()[args[0].asInt()]); @@ -359,7 +357,7 @@ public Value execute(Value... args) { private static class BloomEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Bloom effect = new Bloom(); if (args.length >= 1) { effect.setThreshold(args[0].asNumber()); @@ -373,7 +371,7 @@ public Value execute(Value... args) { private static class BoxBlurEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { BoxBlur effect = new BoxBlur(); if (args.length >= 3) { effect.setWidth(args[0].asNumber()); @@ -389,7 +387,7 @@ public Value execute(Value... args) { private static class ColorAdjustEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return new EffectValue(new ColorAdjust( args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber())); } @@ -397,7 +395,7 @@ public Value execute(Value... args) { private static class ColorInputEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return new EffectValue(new ColorInput( args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), (Color) args[4].raw())); } @@ -405,31 +403,22 @@ public Value execute(Value... args) { private static class DropShadowEffect implements Function { @Override - public Value execute(Value... args) { - DropShadow effect; - switch (args.length) { - case 2: - effect = new DropShadow(args[0].asNumber(), (Color) args[1].raw()); - break; - case 4: - effect = new DropShadow(args[0].asNumber(), - args[1].asInt(), args[2].asInt(), - (Color) args[3].raw()); - break; - case 6: - effect = new DropShadow(BlurType.values()[args[0].asInt()], (Color) args[1].raw(), - args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); - break; - default: - effect = new DropShadow(); - } + public Value execute(Value[] args) { + DropShadow effect = switch (args.length) { + case 2 -> new DropShadow(args[0].asNumber(), (Color) args[1].raw()); + case 4 -> new DropShadow(args[0].asNumber(), args[1].asInt(), args[2].asInt(), + (Color) args[3].raw()); + case 6 -> new DropShadow(BlurType.values()[args[0].asInt()], (Color) args[1].raw(), + args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); + default -> new DropShadow(); + }; return new EffectValue(effect); } } private static class GaussianBlurEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { GaussianBlur effect = new GaussianBlur(); if (args.length >= 1) { effect.setRadius(args[0].asNumber()); @@ -443,7 +432,7 @@ public Value execute(Value... args) { private static class GlowEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Glow effect = new Glow(); if (args.length >= 1) { effect.setLevel(args[0].asNumber()); @@ -457,46 +446,33 @@ public Value execute(Value... args) { private static class InnerShadowEffect implements Function { @Override - public Value execute(Value... args) { - InnerShadow effect; - switch (args.length) { - case 2: - effect = new InnerShadow(args[0].asNumber(), (Color) args[1].raw()); - break; - case 4: - effect = new InnerShadow(args[0].asNumber(), - args[1].asInt(), args[2].asInt(), - (Color) args[3].raw()); - break; - case 6: - effect = new InnerShadow(BlurType.values()[args[0].asInt()], (Color) args[1].raw(), - args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); - break; - default: - effect = new InnerShadow(); - } + public Value execute(Value[] args) { + InnerShadow effect = switch (args.length) { + case 2 -> new InnerShadow(args[0].asNumber(), (Color) args[1].raw()); + case 4 -> new InnerShadow(args[0].asNumber(), args[1].asInt(), args[2].asInt(), + (Color) args[3].raw()); + case 6 -> new InnerShadow(BlurType.values()[args[0].asInt()], (Color) args[1].raw(), + args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); + default -> new InnerShadow(); + }; return new EffectValue(effect); } } private static class LightingEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Light light; final ArrayValue l = (ArrayValue) args[0]; - switch (l.size()) { - case 3: - light = new Light.Distant(l.get(0).asNumber(), l.get(1).asNumber(), (Color) l.get(2).raw()); - break; - case 4: - light = new Light.Point(l.get(0).asNumber(), l.get(1).asNumber(), l.get(2).asNumber(), (Color) l.get(3).raw()); - break; - case 5: - light = new Light.Spot(l.get(0).asNumber(), l.get(1).asNumber(), l.get(2).asNumber(), l.get(3).asNumber(), (Color) l.get(4).raw()); - break; - default: - light = null; - } + light = switch (l.size()) { + case 3 -> new Light.Distant(l.get(0).asNumber(), l.get(1).asNumber(), + (Color) l.get(2).raw()); + case 4 -> new Light.Point(l.get(0).asNumber(), l.get(1).asNumber(), l.get(2).asNumber(), + (Color) l.get(3).raw()); + case 5 -> new Light.Spot(l.get(0).asNumber(), l.get(1).asNumber(), l.get(2).asNumber(), + l.get(3).asNumber(), (Color) l.get(4).raw()); + default -> null; + }; Lighting effect = new Lighting(light); if (args.length >= 2) { effect.setSurfaceScale(args[1].asNumber()); @@ -518,7 +494,7 @@ public Value execute(Value... args) { private static class MotionBlurEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { MotionBlur effect = new MotionBlur(); if (args.length >= 2) { effect.setAngle(args[0].asNumber()); @@ -533,7 +509,7 @@ public Value execute(Value... args) { private static class PerspectiveTransformEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return new EffectValue(new PerspectiveTransform( args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber(), args[6].asNumber(), args[7].asNumber() )); @@ -542,7 +518,7 @@ public Value execute(Value... args) { private static class ReflectionEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return new EffectValue(new Reflection( args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber())); } @@ -550,7 +526,7 @@ public Value execute(Value... args) { private static class SepiaToneEffect implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { SepiaTone effect = new SepiaTone(); if (args.length >= 1) { effect.setLevel(args[0].asNumber()); @@ -564,19 +540,13 @@ public Value execute(Value... args) { private static class ShadowEffect implements Function { @Override - public Value execute(Value... args) { - Shadow effect; - switch (args.length) { - case 2: - effect = new Shadow(args[0].asNumber(), (Color) args[1].raw()); - break; - case 3: - effect = new Shadow(BlurType.values()[args[0].asInt()], (Color) args[1].raw(), - args[2].asNumber()); - break; - default: - effect = new Shadow(); - } + public Value execute(Value[] args) { + Shadow effect = switch (args.length) { + case 2 -> new Shadow(args[0].asNumber(), (Color) args[1].raw()); + case 3 -> new Shadow(BlurType.values()[args[0].asInt()], (Color) args[1].raw(), + args[2].asNumber()); + default -> new Shadow(); + }; return new EffectValue(effect); } } @@ -600,7 +570,7 @@ private void init() { set("getPixels", this::getPixels); } - private Value getPixels(Value... args) { + private Value getPixels(Value[] args) { final int w = (int) image.getWidth(); final int h = (int) image.getHeight(); final int size = w * h; @@ -625,21 +595,15 @@ public String toString() { private static class createImage implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); - final Image result; - switch (args.length) { - case 1: - // createImage(url) - result = new Image(args[0].asString()); - break; - case 2: - default: - // createImage(width, height) - result = new WritableImage(args[0].asInt(), args[1].asInt()); - break; - case 3: - // createImage(w, h, pixels) + final Image result = switch (args.length) { + // createImage(url) + case 1 -> new Image(args[0].asString()); + // createImage(width, height) + default -> new WritableImage(args[0].asInt(), args[1].asInt()); + // createImage(w, h, pixels) + case 3 -> { final int w = args[0].asInt(); final int h = args[1].asInt(); final int size = w * h; @@ -652,9 +616,9 @@ public Value execute(Value... args) { buffer[i] = array.get(i).asInt(); } pw.setPixels(0, 0, w, h, format, buffer, 0, w); - result = writableImage; - - } + yield writableImage; + } + }; return new ImageFXValue(result); } } @@ -734,7 +698,7 @@ private void init() { set("translate", double2ToVoid(graphics::translate)); } - private Value applyEffect(Value... args) { + private Value applyEffect(Value[] args) { if (args[0].type() != FX_EFFECT_TYPE) { throw new TypeException("Effect expected, found " + Types.typeToString(args[0].type())); } @@ -742,33 +706,33 @@ private Value applyEffect(Value... args) { return NumberValue.ZERO; } - private Value arc(Value... args) { + private Value arc(Value[] args) { graphics.arc(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); return NumberValue.ZERO; } - private Value appendSVGPath(Value... args) { + private Value appendSVGPath(Value[] args) { graphics.appendSVGPath(args[0].asString()); return NumberValue.ZERO; } - private Value arcTo(Value... args) { + private Value arcTo(Value[] args) { graphics.arcTo(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber()); return NumberValue.ZERO; } - private Value bezierCurveTo(Value... args) { + private Value bezierCurveTo(Value[] args) { graphics.bezierCurveTo(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); return NumberValue.ZERO; } - private Value drawImage(Value... args) { + private Value drawImage(Value[] args) { Arguments.checkAtLeast(3, args.length); if (!(args[0] instanceof ImageFXValue)) { throw new TypeException("ImageFX expected"); @@ -798,7 +762,7 @@ private Value drawImage(Value... args) { return NumberValue.ZERO; } - private Value fillArc(Value... args) { + private Value fillArc(Value[] args) { graphics.fillArc(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber(), @@ -806,7 +770,7 @@ private Value fillArc(Value... args) { return NumberValue.ZERO; } - private Value fillPolygon(Value... args) { + private Value fillPolygon(Value[] args) { final ArrayValue xarr = (ArrayValue) args[0]; final ArrayValue yarr = (ArrayValue) args[1]; @@ -822,14 +786,14 @@ private Value fillPolygon(Value... args) { return NumberValue.ZERO; } - private Value fillRoundRect(Value... args) { + private Value fillRoundRect(Value[] args) { graphics.fillRoundRect(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber() ); return NumberValue.ZERO; } - private Value fillText(Value... args) { + private Value fillText(Value[] args) { if (args.length < 4) { // str x y graphics.fillText(args[0].asString(), args[1].asNumber(), @@ -841,19 +805,19 @@ private Value fillText(Value... args) { return NumberValue.ZERO; } - private Value getFill(Value... args) { + private Value getFill(Value[] args) { return new ColorValue((Color)graphics.getFill()); } - private Value getStroke(Value... args) { + private Value getStroke(Value[] args) { return new ColorValue((Color)graphics.getStroke()); } - private Value isPointInPath(Value... args) { + private Value isPointInPath(Value[] args) { return NumberValue.fromBoolean(graphics.isPointInPath(args[0].asNumber(), args[1].asNumber())); } - private Value setEffect(Value... args) { + private Value setEffect(Value[] args) { if (args[0].type() != FX_EFFECT_TYPE) { throw new TypeException("Effect expected, found " + Types.typeToString(args[0].type())); } @@ -861,47 +825,47 @@ private Value setEffect(Value... args) { return NumberValue.ZERO; } - private Value setFill(Value... args) { + private Value setFill(Value[] args) { graphics.setFill((Color) args[0].raw()); return NumberValue.ZERO; } - private Value setFillRule(Value... args) { + private Value setFillRule(Value[] args) { graphics.setFillRule(FillRule.values()[args[0].asInt()]); return NumberValue.ZERO; } - private Value setGlobalBlendMode(Value... args) { + private Value setGlobalBlendMode(Value[] args) { graphics.setGlobalBlendMode(BlendMode.values()[args[0].asInt()]); return NumberValue.ZERO; } - private Value setLineCap(Value... args) { + private Value setLineCap(Value[] args) { graphics.setLineCap(StrokeLineCap.values()[args[0].asInt()]); return NumberValue.ZERO; } - private Value setLineJoin(Value... args) { + private Value setLineJoin(Value[] args) { graphics.setLineJoin(StrokeLineJoin.values()[args[0].asInt()]); return NumberValue.ZERO; } - private Value setStroke(Value... args) { + private Value setStroke(Value[] args) { graphics.setStroke((Color) args[0].raw()); return NumberValue.ZERO; } - private Value setTextAlign(Value... args) { + private Value setTextAlign(Value[] args) { graphics.setTextAlign(TextAlignment.values()[args[0].asInt()]); return NumberValue.ZERO; } - private Value setTextBaseline(Value... args) { + private Value setTextBaseline(Value[] args) { graphics.setTextBaseline(VPos.values()[args[0].asInt()]); return NumberValue.ZERO; } - private Value strokeArc(Value... args) { + private Value strokeArc(Value[] args) { graphics.strokeArc(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber(), @@ -909,7 +873,7 @@ private Value strokeArc(Value... args) { return NumberValue.ZERO; } - private Value strokePolygon(Value... args) { + private Value strokePolygon(Value[] args) { final ArrayValue xarr = (ArrayValue) args[0]; final ArrayValue yarr = (ArrayValue) args[1]; @@ -925,7 +889,7 @@ private Value strokePolygon(Value... args) { return NumberValue.ZERO; } - private Value strokePolyline(Value... args) { + private Value strokePolyline(Value[] args) { final ArrayValue xarr = (ArrayValue) args[0]; final ArrayValue yarr = (ArrayValue) args[1]; @@ -941,14 +905,14 @@ private Value strokePolyline(Value... args) { return NumberValue.ZERO; } - private Value strokeRoundRect(Value... args) { + private Value strokeRoundRect(Value[] args) { graphics.strokeRoundRect(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber() ); return NumberValue.ZERO; } - private Value strokeText(Value... args) { + private Value strokeText(Value[] args) { if (args.length < 4) { // str x y graphics.strokeText(args[0].asString(), args[1].asNumber(), @@ -960,7 +924,7 @@ private Value strokeText(Value... args) { return NumberValue.ZERO; } - private Value transform(Value... args) { + private Value transform(Value[] args) { graphics.transform(args[0].asNumber(), args[1].asNumber(), args[2].asNumber(), args[3].asNumber(), args[4].asNumber(), args[5].asNumber()); @@ -976,7 +940,7 @@ public String toString() { private static class CreateWindow implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { String title = ""; int width = 640; int height = 480; @@ -1000,9 +964,9 @@ public Value execute(Value... args) { canvas = new Canvas(width, height); canvas.setFocusTraversable(true); canvas.requestFocus(); - graphics = canvas.getGraphicsContext2D(); - - frame = new JFrame(title); + GraphicsContext graphics = canvas.getGraphicsContext2D(); + + JFrame frame = new JFrame(title); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(panel); frame.pack(); @@ -1021,7 +985,7 @@ public Value execute(Value... args) { private static class Repaint implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { panel.invalidate(); panel.repaint(); return NumberValue.ZERO; @@ -1030,7 +994,7 @@ public Value execute(Value... args) { private static class addEventFilter implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { final Function handler = ((FunctionValue) args[1]).getValue(); final Events event = Events.values()[args[0].asInt()]; canvas.addEventFilter(event.getHandler(), e -> handleEvent(e, handler)); @@ -1040,7 +1004,7 @@ public Value execute(Value... args) { private static class addEventHandler implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { final Function handler = ((FunctionValue) args[1]).getValue(); final Events event = Events.values()[args[0].asInt()]; canvas.addEventHandler(event.getHandler(), e -> handleEvent(e, handler)); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java index ff16c1f5..1796403c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java @@ -10,28 +10,28 @@ public class base64 implements Module { private static final int TYPE_URL_SAFE = 8; public static void initConstants() { - Variables.define("BASE64_URL_SAFE", NumberValue.of(TYPE_URL_SAFE)); + ScopeHandler.setConstant("BASE64_URL_SAFE", NumberValue.of(TYPE_URL_SAFE)); } @Override public void init() { initConstants(); - Functions.set("base64encode", this::base64encode); - Functions.set("base64decode", this::base64decode); - Functions.set("base64encodeToString", this::base64encodeToString); + ScopeHandler.setFunction("base64encode", this::base64encode); + ScopeHandler.setFunction("base64decode", this::base64decode); + ScopeHandler.setFunction("base64encodeToString", this::base64encodeToString); } - private Value base64encode(Value... args) { + private Value base64encode(Value[] args) { Arguments.checkOrOr(1, 2, args.length); return ArrayValue.of(getEncoder(args).encode(getInputToEncode(args))); } - private Value base64encodeToString(Value... args) { + private Value base64encodeToString(Value[] args) { Arguments.checkOrOr(1, 2, args.length); return new StringValue(getEncoder(args).encodeToString(getInputToEncode(args))); } - private Value base64decode(Value... args) { + private Value base64decode(Value[] args) { Arguments.checkOrOr(1, 2, args.length); final Base64.Decoder decoder = getDecoder(args); final byte[] result; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java b/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java index a6f0bc54..6172798d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java @@ -21,8 +21,7 @@ * @author aNNiMON */ public final class canvas implements Module { - - private static JFrame frame; + private static CanvasPanel panel; private static Graphics2D graphics; private static BufferedImage img; @@ -31,30 +30,30 @@ public final class canvas implements Module { private static ArrayValue mouseHover; public static void initConstants() { - Variables.define("VK_UP", NumberValue.of(KeyEvent.VK_UP)); - Variables.define("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.define("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.define("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.define("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.define("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + ScopeHandler.setConstant("VK_UP", NumberValue.of(KeyEvent.VK_UP)); + ScopeHandler.setConstant("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); + ScopeHandler.setConstant("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); + ScopeHandler.setConstant("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); + ScopeHandler.setConstant("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); + ScopeHandler.setConstant("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); } @Override public void init() { initConstants(); - Functions.set("window", new CreateWindow()); - Functions.set("prompt", new Prompt()); - Functions.set("keypressed", new KeyPressed()); - Functions.set("mousehover", new MouseHover()); - Functions.set("line", intConsumer4Convert(canvas::line)); - Functions.set("oval", intConsumer4Convert(canvas::oval)); - Functions.set("foval", intConsumer4Convert(canvas::foval)); - Functions.set("rect", intConsumer4Convert(canvas::rect)); - Functions.set("frect", intConsumer4Convert(canvas::frect)); - Functions.set("clip", intConsumer4Convert(canvas::clip)); - Functions.set("drawstring", new DrawString()); - Functions.set("color", new SetColor()); - Functions.set("repaint", new Repaint()); + ScopeHandler.setFunction("window", new CreateWindow()); + ScopeHandler.setFunction("prompt", new Prompt()); + ScopeHandler.setFunction("keypressed", new KeyPressed()); + ScopeHandler.setFunction("mousehover", new MouseHover()); + ScopeHandler.setFunction("line", intConsumer4Convert(canvas::line)); + ScopeHandler.setFunction("oval", intConsumer4Convert(canvas::oval)); + ScopeHandler.setFunction("foval", intConsumer4Convert(canvas::foval)); + ScopeHandler.setFunction("rect", intConsumer4Convert(canvas::rect)); + ScopeHandler.setFunction("frect", intConsumer4Convert(canvas::frect)); + ScopeHandler.setFunction("clip", intConsumer4Convert(canvas::clip)); + ScopeHandler.setFunction("drawstring", new DrawString()); + ScopeHandler.setFunction("color", new SetColor()); + ScopeHandler.setFunction("repaint", new Repaint()); lastKey = NumberValue.MINUS_ONE; mouseHover = new ArrayValue(new Value[] { NumberValue.ZERO, NumberValue.ZERO }); @@ -135,7 +134,7 @@ protected void paintComponent(Graphics g) { private static class CreateWindow implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { String title = ""; int width = 640; int height = 480; @@ -154,8 +153,8 @@ public Value execute(Value... args) { break; } panel = new CanvasPanel(width, height); - - frame = new JFrame(title); + + JFrame frame = new JFrame(title); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(panel); frame.pack(); @@ -167,7 +166,7 @@ public Value execute(Value... args) { private static class KeyPressed implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return lastKey; } } @@ -175,7 +174,7 @@ public Value execute(Value... args) { private static class MouseHover implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return mouseHover; } } @@ -183,7 +182,7 @@ public Value execute(Value... args) { private static class DrawString implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(3, args.length); int x = args[1].asInt(); int y = args[2].asInt(); @@ -195,7 +194,7 @@ public Value execute(Value... args) { private static class Prompt implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { final String v = JOptionPane.showInputDialog(args[0].asString()); return new StringValue(v == null ? "0" : v); } @@ -204,7 +203,7 @@ public Value execute(Value... args) { private static class Repaint implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { panel.invalidate(); panel.repaint(); return NumberValue.ZERO; @@ -214,7 +213,7 @@ public Value execute(Value... args) { private static class SetColor implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { if (args.length == 1) { graphics.setColor(new Color(args[0].asInt())); return NumberValue.ZERO; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java index 3523b8d6..4a26bb03 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java @@ -1,13 +1,7 @@ package com.annimon.ownlang.modules.collections; import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.Functions; -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 com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.util.Comparator; import java.util.HashMap; @@ -27,11 +21,11 @@ public static void initConstants() { @Override public void init() { initConstants(); - Functions.set("hashMap", mapFunction(HashMap::new)); - Functions.set("linkedHashMap", mapFunction(LinkedHashMap::new)); - Functions.set("concurrentHashMap", mapFunction(ConcurrentHashMap::new)); - Functions.set("treeMap", sortedMapFunction(TreeMap::new, TreeMap::new)); - Functions.set("concurrentSkipListMap", sortedMapFunction(ConcurrentSkipListMap::new, ConcurrentSkipListMap::new)); + ScopeHandler.setFunction("hashMap", mapFunction(HashMap::new)); + ScopeHandler.setFunction("linkedHashMap", mapFunction(LinkedHashMap::new)); + ScopeHandler.setFunction("concurrentHashMap", mapFunction(ConcurrentHashMap::new)); + ScopeHandler.setFunction("treeMap", sortedMapFunction(TreeMap::new, TreeMap::new)); + ScopeHandler.setFunction("concurrentSkipListMap", sortedMapFunction(ConcurrentSkipListMap::new, ConcurrentSkipListMap::new)); } private Function mapFunction(final Supplier> mapSupplier) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java index 7f5e10da..f0185bdb 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java @@ -28,20 +28,20 @@ public final class date implements Module { MILLISECOND = new StringValue("millisecond"); public static void initConstants() { - Variables.define("STYLE_FULL", NumberValue.of(DateFormat.FULL)); - Variables.define("STYLE_LONG", NumberValue.of(DateFormat.LONG)); - Variables.define("STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM)); - Variables.define("STYLE_SHORT", NumberValue.of(DateFormat.SHORT)); + ScopeHandler.setConstant("STYLE_FULL", NumberValue.of(DateFormat.FULL)); + ScopeHandler.setConstant("STYLE_LONG", NumberValue.of(DateFormat.LONG)); + ScopeHandler.setConstant("STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM)); + ScopeHandler.setConstant("STYLE_SHORT", NumberValue.of(DateFormat.SHORT)); } @Override public void init() { initConstants(); - Functions.set("newDate", new date_newDate()); - Functions.set("newFormat", new date_newFormat()); - Functions.set("formatDate", new date_format()); - Functions.set("parseDate", new date_parse()); - Functions.set("toTimestamp", new date_toTimestamp()); + ScopeHandler.setFunction("newDate", new date_newDate()); + ScopeHandler.setFunction("newFormat", new date_newFormat()); + ScopeHandler.setFunction("formatDate", new date_format()); + ScopeHandler.setFunction("parseDate", new date_parse()); + ScopeHandler.setFunction("toTimestamp", new date_toTimestamp()); } // @@ -152,7 +152,7 @@ public String toString() { private static class date_newDate implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { final Calendar calendar = Calendar.getInstance(); calendar.clear(); switch (args.length) { @@ -213,7 +213,7 @@ private static void date(Calendar calendar, Value arg1, Value arg2) { private static class date_newFormat implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { if (args.length == 0) { return new DateFormatValue(new SimpleDateFormat()); } @@ -256,7 +256,7 @@ public Value execute(Value... args) { private static class date_parse implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(1, 2, args.length); final DateFormat format; @@ -280,7 +280,7 @@ public Value execute(Value... args) { private static class date_format implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(1, 2, args.length); final DateFormat format; @@ -300,7 +300,7 @@ public Value execute(Value... args) { private static class date_toTimestamp implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return NumberValue.of(((Calendar) args[0].raw()).getTimeInMillis() ); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java b/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java index 60110322..acfcc042 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java @@ -1,13 +1,7 @@ package com.annimon.ownlang.modules.downloader; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.io.FileOutputStream; import java.io.IOException; @@ -20,16 +14,16 @@ public final class downloader implements Module { @Override public void init() { - Functions.set("getContentLength", this::getContentLength); - Functions.set("downloader", this::downloader); + ScopeHandler.setFunction("getContentLength", this::getContentLength); + ScopeHandler.setFunction("downloader", this::downloader); } - private Value getContentLength(Value... args) { + private Value getContentLength(Value[] args) { Arguments.check(1, args.length); return NumberValue.of(getContentLength(args[0].asString())); } - private Value downloader(Value... args) { + private Value downloader(Value[] args) { Arguments.checkRange(2, 4, args.length); final String downloadUrl = args[0].asString(); final String filePath = args[1].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java index 74d04cc5..2335e38d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -29,7 +29,7 @@ public final class files implements Module { private static Map files; public static void initConstants() { - Variables.define("FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction())); + ScopeHandler.setConstant("FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction())); } @Override @@ -37,68 +37,68 @@ public void init() { files = new HashMap<>(); initConstants(); - Functions.set("fopen", new fopen()); - Functions.set("flush", new flush()); - Functions.set("fclose", new fclose()); + ScopeHandler.setFunction("fopen", new fopen()); + ScopeHandler.setFunction("flush", new flush()); + ScopeHandler.setFunction("fclose", new fclose()); // Operations - Functions.set("copy", new copy()); - Functions.set("delete", fileToBoolean(File::delete)); - Functions.set("listFiles", new listFiles()); - Functions.set("mkdir", fileToBoolean(File::mkdir)); - Functions.set("mkdirs", fileToBoolean(File::mkdirs)); - Functions.set("rename", new rename()); + ScopeHandler.setFunction("copy", new copy()); + ScopeHandler.setFunction("delete", fileToBoolean(File::delete)); + ScopeHandler.setFunction("listFiles", new listFiles()); + ScopeHandler.setFunction("mkdir", fileToBoolean(File::mkdir)); + ScopeHandler.setFunction("mkdirs", fileToBoolean(File::mkdirs)); + ScopeHandler.setFunction("rename", new rename()); // Permissions and statuses - Functions.set("canExecute", fileToBoolean(File::canExecute)); - Functions.set("canRead", fileToBoolean(File::canRead)); - Functions.set("canWrite", fileToBoolean(File::canWrite)); - Functions.set("isDirectory", fileToBoolean(File::isDirectory)); - Functions.set("isFile", fileToBoolean(File::isFile)); - Functions.set("isHidden", fileToBoolean(File::isHidden)); - Functions.set("setExecutable", new setExecutable()); - Functions.set("setReadable", new setReadable()); - Functions.set("setReadOnly", new setReadOnly()); - Functions.set("setWritable", new setWritable()); - - Functions.set("exists", fileToBoolean(File::exists)); - Functions.set("fileSize", new fileSize()); - Functions.set("getParent", new getParent()); - Functions.set("lastModified", new lastModified()); - Functions.set("setLastModified", new setLastModified()); + ScopeHandler.setFunction("canExecute", fileToBoolean(File::canExecute)); + ScopeHandler.setFunction("canRead", fileToBoolean(File::canRead)); + ScopeHandler.setFunction("canWrite", fileToBoolean(File::canWrite)); + ScopeHandler.setFunction("isDirectory", fileToBoolean(File::isDirectory)); + ScopeHandler.setFunction("isFile", fileToBoolean(File::isFile)); + ScopeHandler.setFunction("isHidden", fileToBoolean(File::isHidden)); + ScopeHandler.setFunction("setExecutable", new setExecutable()); + ScopeHandler.setFunction("setReadable", new setReadable()); + ScopeHandler.setFunction("setReadOnly", new setReadOnly()); + ScopeHandler.setFunction("setWritable", new setWritable()); + + ScopeHandler.setFunction("exists", fileToBoolean(File::exists)); + ScopeHandler.setFunction("fileSize", new fileSize()); + ScopeHandler.setFunction("getParent", new getParent()); + ScopeHandler.setFunction("lastModified", new lastModified()); + ScopeHandler.setFunction("setLastModified", new setLastModified()); // IO - Functions.set("readBoolean", new readBoolean()); - Functions.set("readByte", new readByte()); - Functions.set("readBytes", new readBytes()); - Functions.set("readAllBytes", new readAllBytes()); - Functions.set("readChar", new readChar()); - Functions.set("readShort", new readShort()); - Functions.set("readInt", new readInt()); - Functions.set("readLong", new readLong()); - Functions.set("readFloat", new readFloat()); - Functions.set("readDouble", new readDouble()); - Functions.set("readUTF", new readUTF()); - Functions.set("readLine", new readLine()); - Functions.set("readText", new readText()); - Functions.set("writeBoolean", new writeBoolean()); - Functions.set("writeByte", new writeByte()); - Functions.set("writeBytes", new writeBytes()); - Functions.set("writeChar", new writeChar()); - Functions.set("writeShort", new writeShort()); - Functions.set("writeInt", new writeInt()); - Functions.set("writeLong", new writeLong()); - Functions.set("writeFloat", new writeFloat()); - Functions.set("writeDouble", new writeDouble()); - Functions.set("writeUTF", new writeUTF()); - Functions.set("writeLine", new writeLine()); - Functions.set("writeText", new writeText()); + ScopeHandler.setFunction("readBoolean", new readBoolean()); + ScopeHandler.setFunction("readByte", new readByte()); + ScopeHandler.setFunction("readBytes", new readBytes()); + ScopeHandler.setFunction("readAllBytes", new readAllBytes()); + ScopeHandler.setFunction("readChar", new readChar()); + ScopeHandler.setFunction("readShort", new readShort()); + ScopeHandler.setFunction("readInt", new readInt()); + ScopeHandler.setFunction("readLong", new readLong()); + ScopeHandler.setFunction("readFloat", new readFloat()); + ScopeHandler.setFunction("readDouble", new readDouble()); + ScopeHandler.setFunction("readUTF", new readUTF()); + ScopeHandler.setFunction("readLine", new readLine()); + ScopeHandler.setFunction("readText", new readText()); + ScopeHandler.setFunction("writeBoolean", new writeBoolean()); + ScopeHandler.setFunction("writeByte", new writeByte()); + ScopeHandler.setFunction("writeBytes", new writeBytes()); + ScopeHandler.setFunction("writeChar", new writeChar()); + ScopeHandler.setFunction("writeShort", new writeShort()); + ScopeHandler.setFunction("writeInt", new writeInt()); + ScopeHandler.setFunction("writeLong", new writeLong()); + ScopeHandler.setFunction("writeFloat", new writeFloat()); + ScopeHandler.setFunction("writeDouble", new writeDouble()); + ScopeHandler.setFunction("writeUTF", new writeUTF()); + ScopeHandler.setFunction("writeLine", new writeLine()); + ScopeHandler.setFunction("writeText", new writeText()); } private static class filesComparatorFunction implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(2, args.length); final int fd1 = args[0].asInt(); @@ -119,7 +119,7 @@ public Value execute(Value... args) { private static class fopen implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); final File file = Console.fileInstance(args[0].asString()); @@ -160,7 +160,7 @@ private Value process(File file, String mode) throws IOException { private abstract static class FileFunction implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { if (args.length < 1) throw new ArgumentsMismatchException("File descriptor expected"); final int key = args[0].asInt(); try { @@ -183,7 +183,7 @@ protected Value execute(FileInfo fileInfo, Value[] args) throws IOException { private static class copy implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); try { final FileInputStream is = new FileInputStream(fileFrom(args[0])); @@ -202,7 +202,7 @@ public Value execute(Value... args) { private static class rename implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); return NumberValue.fromBoolean( fileFrom(args[0]).renameTo(fileFrom(args[1])) ); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java index 5f909737..4d2fbf80 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/ContainerValue.java @@ -33,7 +33,7 @@ private void init() { set("setLayout", new FunctionValue(this::setLayout)); } - private Value add(Value... args) { + private Value add(Value[] args) { Arguments.checkRange(1, 3, args.length); final Component newComponent; @@ -63,7 +63,7 @@ private Value add(Value... args) { return NumberValue.ZERO; } - private Value remove(Value... args) { + private Value remove(Value[] args) { Arguments.check(1, args.length); if (args[0] instanceof JComponentValue) { container.remove(((JComponentValue) args[0]).component); @@ -73,7 +73,7 @@ private Value remove(Value... args) { return NumberValue.ZERO; } - private Value setLayout(Value... args) { + private Value setLayout(Value[] args) { Arguments.check(1, args.length); container.setLayout(((LayoutManagerValue) args[0]).layout); return NumberValue.ZERO; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java index b43f65b9..5d2edc66 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextFieldValue.java @@ -32,7 +32,7 @@ private void init() { set("setScrollOffset", intToVoid(textField::setScrollOffset)); } - private Value addActionListener(Value... args) { + private Value addActionListener(Value[] args) { Arguments.check(1, args.length); Function action = consumeFunction(args[0], 1); textField.addActionListener(e -> action.execute()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java index ca0c9b0e..26331e30 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java @@ -17,10 +17,10 @@ public final class forms implements Module { public static void initConstants() { // JFrame constants - Variables.define("DISPOSE_ON_CLOSE", NumberValue.of(JFrame.DISPOSE_ON_CLOSE)); - Variables.define("DO_NOTHING_ON_CLOSE", NumberValue.of(JFrame.DO_NOTHING_ON_CLOSE)); - Variables.define("EXIT_ON_CLOSE", NumberValue.of(JFrame.EXIT_ON_CLOSE)); - Variables.define("HIDE_ON_CLOSE", NumberValue.of(JFrame.HIDE_ON_CLOSE)); + ScopeHandler.setConstant("DISPOSE_ON_CLOSE", NumberValue.of(JFrame.DISPOSE_ON_CLOSE)); + ScopeHandler.setConstant("DO_NOTHING_ON_CLOSE", NumberValue.of(JFrame.DO_NOTHING_ON_CLOSE)); + ScopeHandler.setConstant("EXIT_ON_CLOSE", NumberValue.of(JFrame.EXIT_ON_CLOSE)); + ScopeHandler.setConstant("HIDE_ON_CLOSE", NumberValue.of(JFrame.HIDE_ON_CLOSE)); // SwinfConstants final MapValue swing = new MapValue(20); @@ -43,7 +43,7 @@ public static void initConstants() { swing.set("TRAILING", NumberValue.of(SwingConstants.TRAILING)); swing.set("VERTICAL", NumberValue.of(SwingConstants.VERTICAL)); swing.set("WEST", NumberValue.of(SwingConstants.WEST)); - Variables.define("SwingConstants", swing); + ScopeHandler.setConstant("SwingConstants", swing); // LayoutManagers constants final MapValue border = new MapValue(13); @@ -60,7 +60,7 @@ public static void initConstants() { border.set("PAGE_START", new StringValue(BorderLayout.PAGE_START)); border.set("SOUTH", new StringValue(BorderLayout.SOUTH)); border.set("WEST", new StringValue(BorderLayout.WEST)); - Variables.define("BorderLayout", border); + ScopeHandler.setConstant("BorderLayout", border); // ScrollPane constants final MapValue scrollpane = new MapValue(13); @@ -85,14 +85,14 @@ public static void initConstants() { scrollpane.set("VERTICAL_SCROLLBAR_ALWAYS", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS)); scrollpane.set("VERTICAL_SCROLLBAR_AS_NEEDED", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED)); scrollpane.set("VERTICAL_SCROLLBAR_NEVER", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER)); - Variables.define("ScrollPaneConstants", scrollpane); + ScopeHandler.setConstant("ScrollPaneConstants", scrollpane); final MapValue box = new MapValue(4); box.set("LINE_AXIS", NumberValue.of(BoxLayout.LINE_AXIS)); box.set("PAGE_AXIS", NumberValue.of(BoxLayout.PAGE_AXIS)); box.set("X_AXIS", NumberValue.of(BoxLayout.X_AXIS)); box.set("Y_AXIS", NumberValue.of(BoxLayout.Y_AXIS)); - Variables.define("BoxLayout", box); + ScopeHandler.setConstant("BoxLayout", box); final MapValue windowEvent = new MapValue(4); windowEvent.set("WINDOW_FIRST", NumberValue.of(WindowEvent.WINDOW_FIRST)); @@ -107,27 +107,27 @@ public static void initConstants() { windowEvent.set("WINDOW_LOST_FOCUS", NumberValue.of(WindowEvent.WINDOW_LOST_FOCUS)); windowEvent.set("WINDOW_STATE_CHANGED", NumberValue.of(WindowEvent.WINDOW_STATE_CHANGED)); windowEvent.set("WINDOW_LAST", NumberValue.of(WindowEvent.WINDOW_LAST)); - Variables.define("WindowEvent", windowEvent); + ScopeHandler.setConstant("WindowEvent", windowEvent); } @Override public void init() { initConstants(); // Components - Functions.set("newButton", Components::newButton); - Functions.set("newLabel", Components::newLabel); - Functions.set("newPanel", Components::newPanel); - Functions.set("newProgressBar", Components::newProgressBar); - Functions.set("newScrollPane", Components::newScrollPane); - Functions.set("newTextArea", Components::newTextArea); - Functions.set("newTextField", Components::newTextField); - Functions.set("newWindow", Components::newWindow); + ScopeHandler.setFunction("newButton", Components::newButton); + ScopeHandler.setFunction("newLabel", Components::newLabel); + ScopeHandler.setFunction("newPanel", Components::newPanel); + ScopeHandler.setFunction("newProgressBar", Components::newProgressBar); + ScopeHandler.setFunction("newScrollPane", Components::newScrollPane); + ScopeHandler.setFunction("newTextArea", Components::newTextArea); + ScopeHandler.setFunction("newTextField", Components::newTextField); + ScopeHandler.setFunction("newWindow", Components::newWindow); // LayoutManagers - Functions.set("borderLayout", LayoutManagers::borderLayout); - Functions.set("boxLayout", LayoutManagers::boxLayout); - Functions.set("cardLayout", LayoutManagers::cardLayout); - Functions.set("gridLayout", LayoutManagers::gridLayout); - Functions.set("flowLayout", LayoutManagers::flowLayout); + ScopeHandler.setFunction("borderLayout", LayoutManagers::borderLayout); + ScopeHandler.setFunction("boxLayout", LayoutManagers::boxLayout); + ScopeHandler.setFunction("cardLayout", LayoutManagers::cardLayout); + ScopeHandler.setFunction("gridLayout", LayoutManagers::gridLayout); + ScopeHandler.setFunction("flowLayout", LayoutManagers::flowLayout); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index f75d0080..9e2e88d4 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -1,8 +1,7 @@ package com.annimon.ownlang.modules.functional; import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.modules.Module; /** @@ -12,23 +11,23 @@ public final class functional implements Module { public static void initConstants() { - Variables.define("IDENTITY", new FunctionValue(args -> args[0])); + ScopeHandler.setConstant("IDENTITY", new FunctionValue(args -> args[0])); } @Override public void init() { initConstants(); - Functions.set("foreach", new functional_foreach()); - Functions.set("map", new functional_map()); - Functions.set("flatmap", new functional_flatmap()); - Functions.set("reduce", new functional_reduce()); - Functions.set("filter", new functional_filter(false)); - Functions.set("sortby", new functional_sortby()); - Functions.set("takewhile", new functional_filter(true)); - Functions.set("dropwhile", new functional_dropwhile()); + ScopeHandler.setFunction("foreach", new functional_foreach()); + ScopeHandler.setFunction("map", new functional_map()); + ScopeHandler.setFunction("flatmap", new functional_flatmap()); + ScopeHandler.setFunction("reduce", new functional_reduce()); + ScopeHandler.setFunction("filter", new functional_filter(false)); + ScopeHandler.setFunction("sortby", new functional_sortby()); + ScopeHandler.setFunction("takewhile", new functional_filter(true)); + ScopeHandler.setFunction("dropwhile", new functional_dropwhile()); - Functions.set("chain", new functional_chain()); - Functions.set("stream", new functional_stream()); - Functions.set("combine", new functional_combine()); + ScopeHandler.setFunction("chain", new functional_chain()); + ScopeHandler.setFunction("stream", new functional_stream()); + ScopeHandler.setFunction("combine", new functional_combine()); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java index a4b685f8..9fd1f29c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java @@ -8,7 +8,7 @@ public final class functional_chain implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(2, args.length); Value result = args[0]; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java index 08516bb8..e5c9e28d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java @@ -10,7 +10,7 @@ public final class functional_combine implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); Function result = null; for (Value arg : args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java index 254720fa..5817f719 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java @@ -12,7 +12,7 @@ public final class functional_dropwhile implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java index 6752e805..25c73d65 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java @@ -15,7 +15,7 @@ public functional_filter(boolean takeWhile) { } @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); final Value container = args[0]; final Function predicate = ValueUtils.consumeFunction(args[1], 1); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java index 333e1333..868b0c87 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java @@ -13,7 +13,7 @@ public final class functional_flatmap implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java index b13c6566..f22aec44 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java @@ -7,7 +7,7 @@ public final class functional_foreach implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); final Value container = args[0]; final Function consumer = ValueUtils.consumeFunction(args[1], 1); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java index 50979548..82198187 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java @@ -13,7 +13,7 @@ public final class functional_map implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final Value container = args[0]; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java index dbf429ab..005e6864 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -13,7 +13,7 @@ public final class functional_reduce implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final Value container = args[0]; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java index 71f38203..baa0532d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java @@ -13,7 +13,7 @@ public final class functional_sortby implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected at first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java index 86523f68..14084c7c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java @@ -18,10 +18,10 @@ public class gzip implements Module { @Override public void init() { - Functions.set("gzip", this::gzipFile); - Functions.set("gzipBytes", this::gzipBytes); - Functions.set("ungzip", this::ungzipFile); - Functions.set("ungzipBytes", this::ungzipBytes); + ScopeHandler.setFunction("gzip", this::gzipFile); + ScopeHandler.setFunction("gzipBytes", this::gzipBytes); + ScopeHandler.setFunction("ungzip", this::ungzipFile); + ScopeHandler.setFunction("ungzipBytes", this::ungzipBytes); } private Value gzipFile(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java index b8d22cad..ebd36fbe 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.modules.http; -import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.modules.Module; /** @@ -15,8 +15,8 @@ public static void initConstants() { @Override public void init() { initConstants(); - Functions.set("urlencode", new http_urlencode()); - Functions.set("http", new http_http()); - Functions.set("download", new http_download()); + ScopeHandler.setFunction("urlencode", new http_urlencode()); + ScopeHandler.setFunction("http", new http_http()); + ScopeHandler.setFunction("download", new http_download()); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java index 37de2115..35bcf697 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java @@ -9,7 +9,7 @@ public final class http_download implements Function { private final OkHttpClient client = new OkHttpClient(); @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); try { final Response response = client.newCall( diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java index a247a41a..336b9a7f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java @@ -29,7 +29,7 @@ public final class http_http implements Function { private final OkHttpClient client = new OkHttpClient(); @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { String url, method; switch (args.length) { case 1: // http(url) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java index c21c5f58..a62a9054 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java @@ -10,7 +10,7 @@ public final class http_urlencode implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(1, 2, args.length); String charset = "UTF-8"; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java index a64f19b1..aca88ba1 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -26,42 +26,42 @@ public static void initConstants() { @Override public void init() { initConstants(); - Variables.define("null", NULL); - Variables.define("boolean.class", new ClassValue(boolean.class)); - Variables.define("boolean[].class", new ClassValue(boolean[].class)); - Variables.define("boolean[][].class", new ClassValue(boolean[][].class)); - Variables.define("byte.class", new ClassValue(byte.class)); - Variables.define("byte[].class", new ClassValue(byte[].class)); - Variables.define("byte[][].class", new ClassValue(byte[][].class)); - Variables.define("short.class", new ClassValue(short.class)); - Variables.define("short[].class", new ClassValue(short[].class)); - Variables.define("short[][].class", new ClassValue(short[][].class)); - Variables.define("char.class", new ClassValue(char.class)); - Variables.define("char[].class", new ClassValue(char[].class)); - Variables.define("char[][].class", new ClassValue(char[][].class)); - Variables.define("int.class", new ClassValue(int.class)); - Variables.define("int[].class", new ClassValue(int[].class)); - Variables.define("int[][].class", new ClassValue(int[][].class)); - Variables.define("long.class", new ClassValue(long.class)); - Variables.define("long[].class", new ClassValue(long[].class)); - Variables.define("long[][].class", new ClassValue(long[][].class)); - Variables.define("float.class", new ClassValue(float.class)); - Variables.define("float[].class", new ClassValue(float[].class)); - Variables.define("float[][].class", new ClassValue(float[][].class)); - Variables.define("double.class", new ClassValue(double.class)); - Variables.define("double[].class", new ClassValue(double[].class)); - Variables.define("double[][].class", new ClassValue(double[][].class)); - Variables.define("String.class", new ClassValue(String.class)); - Variables.define("String[].class", new ClassValue(String[].class)); - Variables.define("String[][].class", new ClassValue(String[][].class)); - Variables.define("Object.class", new ClassValue(Object.class)); - Variables.define("Object[].class", new ClassValue(Object[].class)); - Variables.define("Object[][].class", new ClassValue(Object[][].class)); - - Functions.set("isNull", this::isNull); - Functions.set("newClass", this::newClass); - Functions.set("toObject", this::toObject); - Functions.set("toValue", this::toValue); + ScopeHandler.setConstant("null", NULL); + ScopeHandler.setConstant("boolean.class", new ClassValue(boolean.class)); + ScopeHandler.setConstant("boolean[].class", new ClassValue(boolean[].class)); + ScopeHandler.setConstant("boolean[][].class", new ClassValue(boolean[][].class)); + ScopeHandler.setConstant("byte.class", new ClassValue(byte.class)); + ScopeHandler.setConstant("byte[].class", new ClassValue(byte[].class)); + ScopeHandler.setConstant("byte[][].class", new ClassValue(byte[][].class)); + ScopeHandler.setConstant("short.class", new ClassValue(short.class)); + ScopeHandler.setConstant("short[].class", new ClassValue(short[].class)); + ScopeHandler.setConstant("short[][].class", new ClassValue(short[][].class)); + ScopeHandler.setConstant("char.class", new ClassValue(char.class)); + ScopeHandler.setConstant("char[].class", new ClassValue(char[].class)); + ScopeHandler.setConstant("char[][].class", new ClassValue(char[][].class)); + ScopeHandler.setConstant("int.class", new ClassValue(int.class)); + ScopeHandler.setConstant("int[].class", new ClassValue(int[].class)); + ScopeHandler.setConstant("int[][].class", new ClassValue(int[][].class)); + ScopeHandler.setConstant("long.class", new ClassValue(long.class)); + ScopeHandler.setConstant("long[].class", new ClassValue(long[].class)); + ScopeHandler.setConstant("long[][].class", new ClassValue(long[][].class)); + ScopeHandler.setConstant("float.class", new ClassValue(float.class)); + ScopeHandler.setConstant("float[].class", new ClassValue(float[].class)); + ScopeHandler.setConstant("float[][].class", new ClassValue(float[][].class)); + ScopeHandler.setConstant("double.class", new ClassValue(double.class)); + ScopeHandler.setConstant("double[].class", new ClassValue(double[].class)); + ScopeHandler.setConstant("double[][].class", new ClassValue(double[][].class)); + ScopeHandler.setConstant("String.class", new ClassValue(String.class)); + ScopeHandler.setConstant("String[].class", new ClassValue(String[].class)); + ScopeHandler.setConstant("String[][].class", new ClassValue(String[][].class)); + ScopeHandler.setConstant("Object.class", new ClassValue(Object.class)); + ScopeHandler.setConstant("Object[].class", new ClassValue(Object[].class)); + ScopeHandler.setConstant("Object[][].class", new ClassValue(Object[][].class)); + + ScopeHandler.setFunction("isNull", this::isNull); + ScopeHandler.setFunction("newClass", this::newClass); + ScopeHandler.setFunction("toObject", this::toObject); + ScopeHandler.setFunction("toValue", this::toValue); } // diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index 176342ad..a027a14a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -1,16 +1,7 @@ package com.annimon.ownlang.modules.jdbc; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.io.IOException; import java.math.BigDecimal; @@ -35,38 +26,38 @@ public final class jdbc implements Module { public static void initConstants() { - Variables.define("TRANSACTION_NONE", NumberValue.of(Connection.TRANSACTION_NONE)); - Variables.define("TRANSACTION_READ_COMMITTED", NumberValue.of(Connection.TRANSACTION_READ_COMMITTED)); - Variables.define("TRANSACTION_READ_UNCOMMITTED", NumberValue.of(Connection.TRANSACTION_READ_UNCOMMITTED)); - Variables.define("TRANSACTION_REPEATABLE_READ", NumberValue.of(Connection.TRANSACTION_REPEATABLE_READ)); - Variables.define("TRANSACTION_SERIALIZABLE", NumberValue.of(Connection.TRANSACTION_SERIALIZABLE)); - - Variables.define("CLOSE_ALL_RESULTS", NumberValue.of(Statement.CLOSE_ALL_RESULTS)); - Variables.define("CLOSE_CURRENT_RESULT", NumberValue.of(Statement.CLOSE_CURRENT_RESULT)); - Variables.define("EXECUTE_FAILED", NumberValue.of(Statement.EXECUTE_FAILED)); - Variables.define("KEEP_CURRENT_RESULT", NumberValue.of(Statement.KEEP_CURRENT_RESULT)); - Variables.define("NO_GENERATED_KEYS", NumberValue.of(Statement.NO_GENERATED_KEYS)); - Variables.define("RETURN_GENERATED_KEYS", NumberValue.of(Statement.RETURN_GENERATED_KEYS)); - Variables.define("SUCCESS_NO_INFO", NumberValue.of(Statement.SUCCESS_NO_INFO)); - - Variables.define("CLOSE_CURSORS_AT_COMMIT", NumberValue.of(ResultSet.CLOSE_CURSORS_AT_COMMIT)); - Variables.define("CONCUR_READ_ONLY", NumberValue.of(ResultSet.CONCUR_READ_ONLY)); - Variables.define("CONCUR_UPDATABLE", NumberValue.of(ResultSet.CONCUR_UPDATABLE)); - Variables.define("FETCH_FORWARD", NumberValue.of(ResultSet.FETCH_FORWARD)); - Variables.define("FETCH_REVERSE", NumberValue.of(ResultSet.FETCH_REVERSE)); - Variables.define("FETCH_UNKNOWN", NumberValue.of(ResultSet.FETCH_UNKNOWN)); - Variables.define("HOLD_CURSORS_OVER_COMMIT", NumberValue.of(ResultSet.HOLD_CURSORS_OVER_COMMIT)); - Variables.define("TYPE_FORWARD_ONLY", NumberValue.of(ResultSet.TYPE_FORWARD_ONLY)); - Variables.define("TYPE_SCROLL_INSENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_INSENSITIVE)); - Variables.define("TYPE_SCROLL_SENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_SENSITIVE)); + ScopeHandler.setConstant("TRANSACTION_NONE", NumberValue.of(Connection.TRANSACTION_NONE)); + ScopeHandler.setConstant("TRANSACTION_READ_COMMITTED", NumberValue.of(Connection.TRANSACTION_READ_COMMITTED)); + ScopeHandler.setConstant("TRANSACTION_READ_UNCOMMITTED", NumberValue.of(Connection.TRANSACTION_READ_UNCOMMITTED)); + ScopeHandler.setConstant("TRANSACTION_REPEATABLE_READ", NumberValue.of(Connection.TRANSACTION_REPEATABLE_READ)); + ScopeHandler.setConstant("TRANSACTION_SERIALIZABLE", NumberValue.of(Connection.TRANSACTION_SERIALIZABLE)); + + ScopeHandler.setConstant("CLOSE_ALL_RESULTS", NumberValue.of(Statement.CLOSE_ALL_RESULTS)); + ScopeHandler.setConstant("CLOSE_CURRENT_RESULT", NumberValue.of(Statement.CLOSE_CURRENT_RESULT)); + ScopeHandler.setConstant("EXECUTE_FAILED", NumberValue.of(Statement.EXECUTE_FAILED)); + ScopeHandler.setConstant("KEEP_CURRENT_RESULT", NumberValue.of(Statement.KEEP_CURRENT_RESULT)); + ScopeHandler.setConstant("NO_GENERATED_KEYS", NumberValue.of(Statement.NO_GENERATED_KEYS)); + ScopeHandler.setConstant("RETURN_GENERATED_KEYS", NumberValue.of(Statement.RETURN_GENERATED_KEYS)); + ScopeHandler.setConstant("SUCCESS_NO_INFO", NumberValue.of(Statement.SUCCESS_NO_INFO)); + + ScopeHandler.setConstant("CLOSE_CURSORS_AT_COMMIT", NumberValue.of(ResultSet.CLOSE_CURSORS_AT_COMMIT)); + ScopeHandler.setConstant("CONCUR_READ_ONLY", NumberValue.of(ResultSet.CONCUR_READ_ONLY)); + ScopeHandler.setConstant("CONCUR_UPDATABLE", NumberValue.of(ResultSet.CONCUR_UPDATABLE)); + ScopeHandler.setConstant("FETCH_FORWARD", NumberValue.of(ResultSet.FETCH_FORWARD)); + ScopeHandler.setConstant("FETCH_REVERSE", NumberValue.of(ResultSet.FETCH_REVERSE)); + ScopeHandler.setConstant("FETCH_UNKNOWN", NumberValue.of(ResultSet.FETCH_UNKNOWN)); + ScopeHandler.setConstant("HOLD_CURSORS_OVER_COMMIT", NumberValue.of(ResultSet.HOLD_CURSORS_OVER_COMMIT)); + ScopeHandler.setConstant("TYPE_FORWARD_ONLY", NumberValue.of(ResultSet.TYPE_FORWARD_ONLY)); + ScopeHandler.setConstant("TYPE_SCROLL_INSENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_INSENSITIVE)); + ScopeHandler.setConstant("TYPE_SCROLL_SENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_SENSITIVE)); } @Override public void init() { initConstants(); - Functions.set("getConnection", getConnectionFunction()); - Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); - Functions.set("mysql", getConnectionFunction("jdbc:")); + ScopeHandler.setFunction("getConnection", getConnectionFunction()); + ScopeHandler.setFunction("sqlite", getConnectionFunction("jdbc:sqlite:")); + ScopeHandler.setFunction("mysql", getConnectionFunction("jdbc:")); } private static com.annimon.ownlang.lib.Function getConnectionFunction() { @@ -151,7 +142,7 @@ private void init() { set("getSchema", stringFunction(connection::getSchema)); } - private Value createStatement(Value... args) { + private Value createStatement(Value[] args) { try { switch (args.length) { case 0: @@ -168,7 +159,7 @@ private Value createStatement(Value... args) { } } - private Value prepareStatement(Value... args) { + private Value prepareStatement(Value[] args) { Arguments.checkRange(1, 4, args.length); try { final String sql = args[0].asString(); @@ -193,7 +184,7 @@ private Value prepareStatement(Value... args) { } } - private Value close(Value... args) { + private Value close(Value[] args) { try { if (connection != null) { connection.close(); @@ -293,7 +284,7 @@ private void init() { } } - private Value addBatch(Value... args) { + private Value addBatch(Value[] args) { if (ps != null) Arguments.checkOrOr(0, 1, args.length); else Arguments.check(1, args.length); try { @@ -305,7 +296,7 @@ private Value addBatch(Value... args) { } } - private Value execute(Value... args) { + private Value execute(Value[] args) { if (ps != null) Arguments.checkRange(0, 2, args.length); else Arguments.checkOrOr(1, 2, args.length); try { @@ -323,7 +314,7 @@ private Value execute(Value... args) { } } - private Value executeQuery(Value... args) { + private Value executeQuery(Value[] args) { if (ps != null) Arguments.checkOrOr(0, 1, args.length); else Arguments.check(1, args.length); try { @@ -334,7 +325,7 @@ private Value executeQuery(Value... args) { } } - private Value executeUpdate(Value... args) { + private Value executeUpdate(Value[] args) { if (ps != null) Arguments.checkRange(0, 2, args.length); else Arguments.checkOrOr(1, 2, args.length); try { @@ -352,7 +343,7 @@ private Value executeUpdate(Value... args) { } } - private Value executeLargeUpdate(Value... args) { + private Value executeLargeUpdate(Value[] args) { if (ps != null) Arguments.checkRange(0, 2, args.length); else Arguments.checkOrOr(1, 2, args.length); try { @@ -462,7 +453,7 @@ private void init() { set("updateTimestamp", updateData(rs::updateTimestamp, rs::updateTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); } - private Value findColumn(Value... args) { + private Value findColumn(Value[] args) { try { return NumberValue.of(rs.findColumn(args[0].asString())); } catch (SQLException sqlex) { @@ -470,7 +461,7 @@ private Value findColumn(Value... args) { } } - private Value updateNull(Value... args) { + private Value updateNull(Value[] args) { Arguments.check(1, args.length); try { if (args[0].type() == Types.NUMBER) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java index 1ff77282..3b3a715e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.modules.json; -import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.modules.Module; /** @@ -15,7 +15,7 @@ public static void initConstants() { @Override public void init() { initConstants(); - Functions.set("jsonencode", new json_encode()); - Functions.set("jsondecode", new json_decode()); + ScopeHandler.setFunction("jsonencode", new json_encode()); + ScopeHandler.setFunction("jsondecode", new json_decode()); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java index c75e4c9c..5522c8e2 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java @@ -10,7 +10,7 @@ public final class json_decode implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); try { final String jsonRaw = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java b/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java index ac3f42ea..3225461e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java @@ -16,52 +16,52 @@ public final class math implements Module { private static final DoubleFunction doubleToNumber = NumberValue::of; public static void initConstants() { - Variables.define("PI", NumberValue.of(Math.PI)); - Variables.define("E", NumberValue.of(Math.E)); + ScopeHandler.setConstant("PI", NumberValue.of(Math.PI)); + ScopeHandler.setConstant("E", NumberValue.of(Math.E)); } @Override public void init() { initConstants(); - Functions.set("abs", math::abs); - Functions.set("acos", functionConvert(Math::acos)); - Functions.set("asin", functionConvert(Math::asin)); - Functions.set("atan", functionConvert(Math::atan)); - Functions.set("atan2", biFunctionConvert(Math::atan2)); - Functions.set("cbrt", functionConvert(Math::cbrt)); - Functions.set("ceil", functionConvert(Math::ceil)); - Functions.set("copySign", math::copySign); - Functions.set("cos", functionConvert(Math::cos)); - Functions.set("cosh", functionConvert(Math::cosh)); - Functions.set("exp", functionConvert(Math::exp)); - Functions.set("expm1", functionConvert(Math::expm1)); - Functions.set("floor", functionConvert(Math::floor)); - Functions.set("getExponent", math::getExponent); - Functions.set("hypot", biFunctionConvert(Math::hypot)); - Functions.set("IEEEremainder", biFunctionConvert(Math::IEEEremainder)); - Functions.set("log", functionConvert(Math::log)); - Functions.set("log1p", functionConvert(Math::log1p)); - Functions.set("log10", functionConvert(Math::log10)); - Functions.set("max", math::max); - Functions.set("min", math::min); - Functions.set("nextAfter", math::nextAfter); - Functions.set("nextUp", functionConvert(Math::nextUp, Math::nextUp)); - Functions.set("nextDown", functionConvert(Math::nextDown, Math::nextDown)); - Functions.set("pow", biFunctionConvert(Math::pow)); - Functions.set("rint", functionConvert(Math::rint)); - Functions.set("round", math::round); - Functions.set("signum", functionConvert(Math::signum, Math::signum)); - Functions.set("sin", functionConvert(Math::sin)); - Functions.set("sinh", functionConvert(Math::sinh)); - Functions.set("sqrt", functionConvert(Math::sqrt)); - Functions.set("tan", functionConvert(Math::tan)); - Functions.set("tanh", functionConvert(Math::tanh)); - Functions.set("toDegrees", functionConvert(Math::toDegrees)); - Functions.set("toRadians", functionConvert(Math::toRadians)); - Functions.set("ulp", functionConvert(Math::ulp, Math::ulp)); + ScopeHandler.setFunction("abs", math::abs); + ScopeHandler.setFunction("acos", functionConvert(Math::acos)); + ScopeHandler.setFunction("asin", functionConvert(Math::asin)); + ScopeHandler.setFunction("atan", functionConvert(Math::atan)); + ScopeHandler.setFunction("atan2", biFunctionConvert(Math::atan2)); + ScopeHandler.setFunction("cbrt", functionConvert(Math::cbrt)); + ScopeHandler.setFunction("ceil", functionConvert(Math::ceil)); + ScopeHandler.setFunction("copySign", math::copySign); + ScopeHandler.setFunction("cos", functionConvert(Math::cos)); + ScopeHandler.setFunction("cosh", functionConvert(Math::cosh)); + ScopeHandler.setFunction("exp", functionConvert(Math::exp)); + ScopeHandler.setFunction("expm1", functionConvert(Math::expm1)); + ScopeHandler.setFunction("floor", functionConvert(Math::floor)); + ScopeHandler.setFunction("getExponent", math::getExponent); + ScopeHandler.setFunction("hypot", biFunctionConvert(Math::hypot)); + ScopeHandler.setFunction("IEEEremainder", biFunctionConvert(Math::IEEEremainder)); + ScopeHandler.setFunction("log", functionConvert(Math::log)); + ScopeHandler.setFunction("log1p", functionConvert(Math::log1p)); + ScopeHandler.setFunction("log10", functionConvert(Math::log10)); + ScopeHandler.setFunction("max", math::max); + ScopeHandler.setFunction("min", math::min); + ScopeHandler.setFunction("nextAfter", math::nextAfter); + ScopeHandler.setFunction("nextUp", functionConvert(Math::nextUp, Math::nextUp)); + ScopeHandler.setFunction("nextDown", functionConvert(Math::nextDown, Math::nextDown)); + ScopeHandler.setFunction("pow", biFunctionConvert(Math::pow)); + ScopeHandler.setFunction("rint", functionConvert(Math::rint)); + ScopeHandler.setFunction("round", math::round); + ScopeHandler.setFunction("signum", functionConvert(Math::signum, Math::signum)); + ScopeHandler.setFunction("sin", functionConvert(Math::sin)); + ScopeHandler.setFunction("sinh", functionConvert(Math::sinh)); + ScopeHandler.setFunction("sqrt", functionConvert(Math::sqrt)); + ScopeHandler.setFunction("tan", functionConvert(Math::tan)); + ScopeHandler.setFunction("tanh", functionConvert(Math::tanh)); + ScopeHandler.setFunction("toDegrees", functionConvert(Math::toDegrees)); + ScopeHandler.setFunction("toRadians", functionConvert(Math::toRadians)); + ScopeHandler.setFunction("ulp", functionConvert(Math::ulp, Math::ulp)); } - private static Value abs(Value... args) { + private static Value abs(Value[] args) { Arguments.check(1, args.length); final Object raw = args[0].raw(); if (raw instanceof Double) { @@ -76,7 +76,7 @@ private static Value abs(Value... args) { return NumberValue.of(Math.abs(args[0].asInt())); } - private static Value copySign(Value... args) { + private static Value copySign(Value[] args) { Arguments.check(2, args.length); final Object raw = args[0].raw(); if (raw instanceof Float) { @@ -85,7 +85,7 @@ private static Value copySign(Value... args) { return NumberValue.of(Math.copySign(args[0].asNumber(), args[1].asNumber())); } - private static Value getExponent(Value... args) { + private static Value getExponent(Value[] args) { Arguments.check(1, args.length); final Object raw = args[0].raw(); if (raw instanceof Float) { @@ -94,7 +94,7 @@ private static Value getExponent(Value... args) { return NumberValue.of(Math.getExponent(args[0].asNumber())); } - private static Value max(Value... args) { + private static Value max(Value[] args) { Arguments.check(2, args.length); final Object raw = args[0].raw(); if (raw instanceof Double) { @@ -109,7 +109,7 @@ private static Value max(Value... args) { return NumberValue.of(Math.max(args[0].asInt(), args[1].asInt())); } - private static Value min(Value... args) { + private static Value min(Value[] args) { Arguments.check(2, args.length); final Object raw = args[0].raw(); if (raw instanceof Double) { @@ -124,7 +124,7 @@ private static Value min(Value... args) { return NumberValue.of(Math.min(args[0].asInt(), args[1].asInt())); } - private static Value nextAfter(Value... args) { + private static Value nextAfter(Value[] args) { Arguments.check(2, args.length); final Object raw = args[0].raw(); if (raw instanceof Float) { @@ -133,7 +133,7 @@ private static Value nextAfter(Value... args) { return NumberValue.of(Math.nextAfter(args[0].asNumber(), args[1].asNumber())); } - private static Value round(Value... args) { + private static Value round(Value[] args) { Arguments.check(1, args.length); final Object raw = args[0].raw(); if (raw instanceof Float) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java index d4066dad..77d465ad 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java @@ -2,13 +2,7 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.ValueUtils; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import okhttp3.MediaType; import okhttp3.MultipartBody; @@ -55,7 +49,7 @@ public static void initConstants() { args[1].asString() )); }); - Variables.define("RequestBody", requestBody); + ScopeHandler.setConstant("RequestBody", requestBody); MapValue multipartBody = new MapValue(10); @@ -65,13 +59,13 @@ public static void initConstants() { multipartBody.set("MIXED", new StringValue(MultipartBody.MIXED.toString())); multipartBody.set("PARALLEL", new StringValue(MultipartBody.PARALLEL.toString())); multipartBody.set("builder", args -> new MultipartBodyBuilderValue()); - Variables.define("MultipartBody", multipartBody); + ScopeHandler.setConstant("MultipartBody", multipartBody); MapValue okhttp = new MapValue(5); okhttp.set("client", defaultClient); okhttp.set("request", args -> new RequestBuilderValue()); - Variables.define("okhttp", okhttp); + ScopeHandler.setConstant("okhttp", okhttp); } @Override diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java index e912fb98..e8a3d0ce 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -1,13 +1,7 @@ package com.annimon.ownlang.modules.ounit; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.text.DecimalFormat; import java.util.List; @@ -24,12 +18,12 @@ public static void initConstants() { @Override public void init() { initConstants(); - Functions.set("assertEquals", new assertEquals()); - Functions.set("assertNotEquals", new assertNotEquals()); - Functions.set("assertSameType", new assertSameType()); - Functions.set("assertTrue", new assertTrue()); - Functions.set("assertFalse", new assertFalse()); - Functions.set("runTests", new runTests()); + ScopeHandler.setFunction("assertEquals", new assertEquals()); + ScopeHandler.setFunction("assertNotEquals", new assertNotEquals()); + ScopeHandler.setFunction("assertSameType", new assertSameType()); + ScopeHandler.setFunction("assertTrue", new assertTrue()); + ScopeHandler.setFunction("assertFalse", new assertFalse()); + ScopeHandler.setFunction("runTests", new runTests()); } private static String microsToSeconds(long micros) { @@ -38,7 +32,7 @@ private static String microsToSeconds(long micros) { private static class assertEquals implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].equals(args[1])) return NumberValue.ONE; throw new OUnitAssertionException("Values are not equals: " @@ -48,7 +42,7 @@ public Value execute(Value... args) { private static class assertNotEquals implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (!args[0].equals(args[1])) return NumberValue.ONE; throw new OUnitAssertionException("Values are equals: " + args[0]); @@ -57,7 +51,7 @@ public Value execute(Value... args) { private static class assertSameType implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].type() == args[1].type()) return NumberValue.ONE; throw new OUnitAssertionException("Types mismatch. " @@ -68,7 +62,7 @@ public Value execute(Value... args) { private static class assertTrue implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); if (args[0].asInt() != 0) return NumberValue.ONE; throw new OUnitAssertionException("Expected true, but found false."); @@ -77,7 +71,7 @@ public Value execute(Value... args) { private static class assertFalse implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); if (args[0].asInt() == 0) return NumberValue.ONE; throw new OUnitAssertionException("Expected false, but found true."); @@ -87,7 +81,7 @@ public Value execute(Value... args) { private static class runTests implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { List tests = Functions.getFunctions().entrySet().stream() .filter(e -> e.getKey().toLowerCase().startsWith("test")) .map(e -> runTest(e.getKey(), e.getValue())) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java index a7c01937..f79ede5b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java @@ -1,13 +1,6 @@ package com.annimon.ownlang.modules.regex; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.util.regex.Pattern; @@ -44,13 +37,13 @@ public static void initConstants() { return ArrayValue.of(pattern.split(args[1].asString(), limit)); }); map.set("compile", regex::compile); - Variables.define("Pattern", map); + ScopeHandler.setConstant("Pattern", map); } @Override public void init() { initConstants(); - Functions.set("regex", regex::compile); + ScopeHandler.setFunction("regex", regex::compile); } private static Value compile(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java index 901c7bd2..fc519ec3 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java @@ -29,15 +29,15 @@ public final class robot implements Module { private static Robot awtRobot; public static void initConstants() { - Variables.define("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.define("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.define("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.define("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.define("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + ScopeHandler.setConstant("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); + ScopeHandler.setConstant("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); + ScopeHandler.setConstant("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); + ScopeHandler.setConstant("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); + ScopeHandler.setConstant("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); - Variables.define("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); - Variables.define("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); - Variables.define("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); + ScopeHandler.setConstant("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); + ScopeHandler.setConstant("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); + ScopeHandler.setConstant("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); } @Override @@ -45,33 +45,33 @@ public void init() { initConstants(); boolean isRobotInitialized = initialize(); if (isRobotInitialized) { - Functions.set("click", convertFunction(robot::click)); - Functions.set("delay", convertFunction(awtRobot::delay)); - Functions.set("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); - Functions.set("keyPress", convertFunction(awtRobot::keyPress)); - Functions.set("keyRelease", convertFunction(awtRobot::keyRelease)); - Functions.set("mousePress", convertFunction(awtRobot::mousePress)); - Functions.set("mouseRelease", convertFunction(awtRobot::mouseRelease)); - Functions.set("mouseWheel", convertFunction(awtRobot::mouseWheel)); - Functions.set("mouseMove", (args) -> { + ScopeHandler.setFunction("click", convertFunction(robot::click)); + ScopeHandler.setFunction("delay", convertFunction(awtRobot::delay)); + ScopeHandler.setFunction("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); + ScopeHandler.setFunction("keyPress", convertFunction(awtRobot::keyPress)); + ScopeHandler.setFunction("keyRelease", convertFunction(awtRobot::keyRelease)); + ScopeHandler.setFunction("mousePress", convertFunction(awtRobot::mousePress)); + ScopeHandler.setFunction("mouseRelease", convertFunction(awtRobot::mouseRelease)); + ScopeHandler.setFunction("mouseWheel", convertFunction(awtRobot::mouseWheel)); + ScopeHandler.setFunction("mouseMove", (args) -> { Arguments.check(2, args.length); try { awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); } catch (IllegalArgumentException iae) { } return NumberValue.ZERO; }); - Functions.set("typeText", (args) -> { + ScopeHandler.setFunction("typeText", (args) -> { Arguments.check(1, args.length); try { typeText(args[0].asString()); } catch (IllegalArgumentException iae) { } return NumberValue.ZERO; }); - Functions.set("toClipboard", new robot_toclipboard()); - Functions.set("fromClipboard", new robot_fromclipboard()); + ScopeHandler.setFunction("toClipboard", new robot_toclipboard()); + ScopeHandler.setFunction("fromClipboard", new robot_fromclipboard()); } - Functions.set("execProcess", new robot_exec(robot_exec.Mode.EXEC)); - Functions.set("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); + ScopeHandler.setFunction("execProcess", new robot_exec(robot_exec.Mode.EXEC)); + ScopeHandler.setFunction("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); } private static boolean initialize() { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java index 322dde16..ae2ad8d0 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java @@ -18,7 +18,7 @@ public robot_exec(Mode mode) { } @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); try { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java index 6e959d44..59260b7f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java @@ -9,7 +9,7 @@ public final class robot_fromclipboard implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { try { Object data = Toolkit.getDefaultToolkit().getSystemClipboard() .getData(DataFlavor.stringFlavor); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java index a1be3d16..0a31fc1e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java @@ -10,7 +10,7 @@ public final class robot_toclipboard implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); Toolkit.getDefaultToolkit().getSystemClipboard() .setContents(new StringSelection(args[0].asString()), null); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java b/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java index 858bb4ad..10b64c91 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java @@ -15,29 +15,29 @@ public final class socket implements Module { public static void initConstants() { - Variables.define("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); - Variables.define("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); - Variables.define("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); - Variables.define("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); - Variables.define("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); - Variables.define("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); - Variables.define("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); - Variables.define("EVENT_PING", new StringValue(Socket.EVENT_PING)); - Variables.define("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); - Variables.define("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); - Variables.define("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); - Variables.define("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); - Variables.define("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); - Variables.define("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); + ScopeHandler.setConstant("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); + ScopeHandler.setConstant("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); + ScopeHandler.setConstant("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); + ScopeHandler.setConstant("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); + ScopeHandler.setConstant("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); + ScopeHandler.setConstant("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); + ScopeHandler.setConstant("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); + ScopeHandler.setConstant("EVENT_PING", new StringValue(Socket.EVENT_PING)); + ScopeHandler.setConstant("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); + ScopeHandler.setConstant("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); + ScopeHandler.setConstant("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); + ScopeHandler.setConstant("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); + ScopeHandler.setConstant("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); + ScopeHandler.setConstant("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); } @Override public void init() { initConstants(); - Functions.set("newSocket", socket::newSocket); + ScopeHandler.setFunction("newSocket", socket::newSocket); } - private static Value newSocket(Value... args) { + private static Value newSocket(Value[] args) { Arguments.checkOrOr(1, 2, args.length); try { @@ -81,33 +81,33 @@ private void init() { set("send", this::send); } - private Value close(Value... args) { + private Value close(Value[] args) { socket.close(); return this; } - private Value connect(Value... args) { + private Value connect(Value[] args) { socket.connect(); return this; } - private Value connected(Value... args) { + private Value connected(Value[] args) { return NumberValue.fromBoolean(socket.connected()); } - private Value disconnect(Value... args) { + private Value disconnect(Value[] args) { socket.disconnect(); return this; } - private Value hasListeners(Value... args) { + private Value hasListeners(Value[] args) { Arguments.check(1, args.length); return NumberValue.fromBoolean( socket.hasListeners(args[0].asString()) ); } - private Value emit(Value... args) { + private Value emit(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String event = args[0].asString(); final Value value = args[1]; @@ -118,11 +118,11 @@ private Value emit(Value... args) { return this; } - private Value id(Value... args) { + private Value id(Value[] args) { return new StringValue(socket.id()); } - private Value off(Value... args) { + private Value off(Value[] args) { Arguments.checkOrOr(0, 1, args.length); if (args.length == 1) { socket.off(args[0].asString()); @@ -132,7 +132,7 @@ private Value off(Value... args) { return this; } - private Value on(Value... args) { + private Value on(Value[] args) { Arguments.check(2, args.length); final String event = args[0].asString(); final Function listener = ((FunctionValue) args[1]).getValue(); @@ -142,7 +142,7 @@ private Value on(Value... args) { return this; } - private Value once(Value... args) { + private Value once(Value[] args) { Arguments.check(2, args.length); final String event = args[0].asString(); final Function listener = ((FunctionValue) args[1]).getValue(); @@ -152,12 +152,12 @@ private Value once(Value... args) { return this; } - private Value open(Value... args) { + private Value open(Value[] args) { socket.open(); return this; } - private Value send(Value... args) { + private Value send(Value[] args) { Arguments.check(1, args.length); socket.send(ValueUtils.toObject(args[0])); return this; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java index 743732ed..2b654511 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java @@ -10,7 +10,7 @@ public final class NumberFunctions { private NumberFunctions() { } - static Value toHexString(Value... args) { + static Value toHexString(Value[] args) { Arguments.check(1, args.length); long value; if (args[0].type() == Types.NUMBER) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java index 272cdc44..18e399bb 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -18,56 +18,56 @@ public static void initConstants() { ownlang.set("VERSION_MAJOR", NumberValue.of(Version.VERSION_MAJOR)); ownlang.set("VERSION_MINOR", NumberValue.of(Version.VERSION_MINOR)); ownlang.set("VERSION_PATCH", NumberValue.of(Version.VERSION_PATCH)); - Variables.define("OwnLang", ownlang); + ScopeHandler.setConstant("OwnLang", ownlang); } @Override public void init() { initConstants(); - Variables.define("ARGS", ArrayValue.of(Shared.getOwnlangArgs())); // is not constant - Functions.set("echo", new std_echo()); - Functions.set("readln", new std_readln()); - Functions.set("length", new std_length()); - Functions.set("rand", new std_rand()); - Functions.set("time", new std_time()); - Functions.set("sleep", new std_sleep()); - Functions.set("thread", new std_thread()); - Functions.set("sync", new std_sync()); - Functions.set("try", new std_try()); - Functions.set("default", new std_default()); + ScopeHandler.setConstant("ARGS", ArrayValue.of(Shared.getOwnlangArgs())); // is not constant + ScopeHandler.setFunction("echo", new std_echo()); + ScopeHandler.setFunction("readln", new std_readln()); + ScopeHandler.setFunction("length", new std_length()); + ScopeHandler.setFunction("rand", new std_rand()); + ScopeHandler.setFunction("time", new std_time()); + ScopeHandler.setFunction("sleep", new std_sleep()); + ScopeHandler.setFunction("thread", new std_thread()); + ScopeHandler.setFunction("sync", new std_sync()); + ScopeHandler.setFunction("try", new std_try()); + ScopeHandler.setFunction("default", new std_default()); // Numbers - Functions.set("toHexString", NumberFunctions::toHexString); + ScopeHandler.setFunction("toHexString", NumberFunctions::toHexString); // String - Functions.set("getBytes", StringFunctions::getBytes); - Functions.set("sprintf", new std_sprintf()); - Functions.set("split", new std_split()); - Functions.set("indexOf", new std_indexof()); - Functions.set("lastIndexOf", new std_lastindexof()); - Functions.set("charAt", new std_charat()); - Functions.set("toChar", new std_tochar()); - Functions.set("substring", new std_substring()); - Functions.set("toLowerCase", new std_tolowercase()); - Functions.set("toUpperCase", new std_touppercase()); - Functions.set("trim", new std_trim()); - Functions.set("replace", new std_replace()); - Functions.set("replaceAll", new std_replaceall()); - Functions.set("replaceFirst", new std_replacefirst()); - Functions.set("parseInt", StringFunctions::parseInt); - Functions.set("parseLong", StringFunctions::parseLong); - Functions.set("stripMargin", StringFunctions::stripMargin); + ScopeHandler.setFunction("getBytes", StringFunctions::getBytes); + ScopeHandler.setFunction("sprintf", new std_sprintf()); + ScopeHandler.setFunction("split", new std_split()); + ScopeHandler.setFunction("indexOf", new std_indexof()); + ScopeHandler.setFunction("lastIndexOf", new std_lastindexof()); + ScopeHandler.setFunction("charAt", new std_charat()); + ScopeHandler.setFunction("toChar", new std_tochar()); + ScopeHandler.setFunction("substring", new std_substring()); + ScopeHandler.setFunction("toLowerCase", new std_tolowercase()); + ScopeHandler.setFunction("toUpperCase", new std_touppercase()); + ScopeHandler.setFunction("trim", new std_trim()); + ScopeHandler.setFunction("replace", new std_replace()); + ScopeHandler.setFunction("replaceAll", new std_replaceall()); + ScopeHandler.setFunction("replaceFirst", new std_replacefirst()); + ScopeHandler.setFunction("parseInt", StringFunctions::parseInt); + ScopeHandler.setFunction("parseLong", StringFunctions::parseLong); + ScopeHandler.setFunction("stripMargin", StringFunctions::stripMargin); // Arrays and maps - Functions.set("newarray", new std_newarray()); - Functions.set("join", new std_join()); - Functions.set("sort", new std_sort()); - Functions.set("arrayCombine", new std_arrayCombine()); - Functions.set("arrayKeyExists", new std_arrayKeyExists()); - Functions.set("arrayKeys", new std_arrayKeys()); - Functions.set("arrayValues", new std_arrayValues()); - Functions.set("arraySplice", new std_arraySplice()); - Functions.set("range", new std_range()); - Functions.set("stringFromBytes", ArrayFunctions::stringFromBytes); + ScopeHandler.setFunction("newarray", new std_newarray()); + ScopeHandler.setFunction("join", new std_join()); + ScopeHandler.setFunction("sort", new std_sort()); + ScopeHandler.setFunction("arrayCombine", new std_arrayCombine()); + ScopeHandler.setFunction("arrayKeyExists", new std_arrayKeyExists()); + ScopeHandler.setFunction("arrayKeys", new std_arrayKeys()); + ScopeHandler.setFunction("arrayValues", new std_arrayValues()); + ScopeHandler.setFunction("arraySplice", new std_arraySplice()); + ScopeHandler.setFunction("range", new std_range()); + ScopeHandler.setFunction("stringFromBytes", ArrayFunctions::stringFromBytes); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java index ac72c83f..d9a32a43 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java @@ -11,7 +11,7 @@ public final class std_arrayCombine implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java index b54eb0c7..fb4b2418 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java @@ -11,7 +11,7 @@ public final class std_arrayKeyExists implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[1].type() != Types.MAP) { throw new TypeException("Map expected in second argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java index 9a32de01..e2f15651 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java @@ -14,7 +14,7 @@ public final class std_arrayKeys implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); if (args[0].type() != Types.MAP) { throw new TypeException("Map expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java index f16f04ff..2614bc24 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java @@ -10,7 +10,7 @@ public final class std_arraySplice implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkRange(2, 4, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected at first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java index a56ee5c7..f263d0fa 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java @@ -14,7 +14,7 @@ public final class std_arrayValues implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); if (args[0].type() != Types.MAP) { throw new TypeException("Map expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java index e1b45a73..de0407ae 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java @@ -8,7 +8,7 @@ public final class std_charat implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java index 68819bf8..76b96ad8 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java @@ -10,7 +10,7 @@ public final class std_default implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(2, args.length); if (isEmpty(args[0])) { return args[1]; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java index c296e8ea..27bdfb6a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java @@ -8,7 +8,7 @@ public final class std_echo implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { final StringBuilder sb = new StringBuilder(); for (Value arg : args) { sb.append(arg.asString()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java index cf905de5..5b9e1dbf 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java @@ -8,7 +8,7 @@ public final class std_indexof implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java index 3696fb14..7806c939 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java @@ -11,7 +11,7 @@ public final class std_join implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkRange(1, 4, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java index 54902092..b4124669 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java @@ -8,7 +8,7 @@ public final class std_lastindexof implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java index 4a7d0b75..67487cea 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java @@ -5,7 +5,7 @@ public final class std_length implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); final Value val = args[0]; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java index 83839d7c..78029e9e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java @@ -8,7 +8,7 @@ public final class std_newarray implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return createArray(args, 0); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java index 542b521c..56962539 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java @@ -11,7 +11,7 @@ public final class std_rand implements Function { private static final Random RND = new Random(); @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkRange(0, 2, args.length); if (args.length == 0) return NumberValue.of(RND.nextDouble()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java index 820ece96..a7336ae7 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java @@ -6,7 +6,7 @@ public final class std_range implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkRange(1, 3, args.length); final long from, to, step; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java index 082b7dbd..50605e7f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java @@ -8,7 +8,7 @@ public final class std_readln implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { try (Scanner sc = new Scanner(System.in)) { return new StringValue(sc.nextLine()); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java index a52cf0bb..8a274a11 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java @@ -8,7 +8,7 @@ public final class std_replace implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java index 4d6407e3..6e3c2830 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java @@ -8,7 +8,7 @@ public final class std_replaceall implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java index 060b8a1b..c02e5540 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java @@ -8,7 +8,7 @@ public final class std_replacefirst implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java index 62566808..f0742aac 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java @@ -8,7 +8,7 @@ public final class std_sleep implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); try { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java index 22608b4d..6735fc7d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java @@ -13,7 +13,7 @@ public final class std_sort implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java index 0c2c4688..7b2c5a13 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java @@ -8,7 +8,7 @@ public final class std_split implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java index 1a6dbcdd..5dfdb110 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java @@ -9,7 +9,7 @@ public final class std_sprintf implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); final String format = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java index 8c65e58a..dcae235c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java @@ -8,7 +8,7 @@ public final class std_substring implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String input = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java index 0cbef55c..5aa961c8 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java @@ -12,7 +12,7 @@ public final class std_sync implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); final BlockingQueue queue = new LinkedBlockingQueue<>(2); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java index e23bdef3..07c8ead7 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java @@ -12,7 +12,7 @@ public final class std_thread implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); Function body; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java index 3a5e81ce..f5fc062a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java @@ -7,7 +7,7 @@ public final class std_time implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { return NumberValue.of(System.currentTimeMillis()); } } \ No newline at end of file diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java index ef31d18e..b59bc0d1 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java @@ -8,7 +8,7 @@ public final class std_tochar implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(String.valueOf((char) args[0].asInt())); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java index a419719a..f27fe018 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java @@ -8,7 +8,7 @@ public final class std_tolowercase implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().toLowerCase()); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java index d6f102af..e59397c4 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java @@ -8,7 +8,7 @@ public final class std_touppercase implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().toUpperCase()); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java index 2853142b..de57bbc7 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java @@ -8,7 +8,7 @@ public final class std_trim implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().trim()); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java index fb3a4530..8546119c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java @@ -5,7 +5,7 @@ public final class std_try implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(1, 2, args.length); try { return ValueUtils.consumeFunction(args[0], 0).execute(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java index fcdc1388..b9614880 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java @@ -10,26 +10,26 @@ public final class types implements Module { public static void initConstants() { - Variables.define("OBJECT", NumberValue.of(Types.OBJECT)); - Variables.define("NUMBER", NumberValue.of(Types.NUMBER)); - Variables.define("STRING", NumberValue.of(Types.STRING)); - Variables.define("ARRAY", NumberValue.of(Types.ARRAY)); - Variables.define("MAP", NumberValue.of(Types.MAP)); - Variables.define("FUNCTION", NumberValue.of(Types.FUNCTION)); + ScopeHandler.setConstant("OBJECT", NumberValue.of(Types.OBJECT)); + ScopeHandler.setConstant("NUMBER", NumberValue.of(Types.NUMBER)); + ScopeHandler.setConstant("STRING", NumberValue.of(Types.STRING)); + ScopeHandler.setConstant("ARRAY", NumberValue.of(Types.ARRAY)); + ScopeHandler.setConstant("MAP", NumberValue.of(Types.MAP)); + ScopeHandler.setConstant("FUNCTION", NumberValue.of(Types.FUNCTION)); } @Override public void init() { initConstants(); - Functions.set("typeof", args -> NumberValue.of(args[0].type())); - Functions.set("string", args -> new StringValue(args[0].asString())); - Functions.set("number", args -> NumberValue.of(args[0].asNumber())); + ScopeHandler.setFunction("typeof", args -> NumberValue.of(args[0].type())); + ScopeHandler.setFunction("string", args -> new StringValue(args[0].asString())); + ScopeHandler.setFunction("number", args -> NumberValue.of(args[0].asNumber())); - Functions.set("byte", args -> NumberValue.of((byte)args[0].asInt())); - Functions.set("short", args -> NumberValue.of((short)args[0].asInt())); - Functions.set("int", args -> NumberValue.of(args[0].asInt())); - Functions.set("long", args -> NumberValue.of((long)args[0].asNumber())); - Functions.set("float", args -> NumberValue.of((float)args[0].asNumber())); - Functions.set("double", args -> NumberValue.of(args[0].asNumber())); + ScopeHandler.setFunction("byte", args -> NumberValue.of((byte)args[0].asInt())); + ScopeHandler.setFunction("short", args -> NumberValue.of((short)args[0].asInt())); + ScopeHandler.setFunction("int", args -> NumberValue.of(args[0].asInt())); + ScopeHandler.setFunction("long", args -> NumberValue.of((long)args[0].asNumber())); + ScopeHandler.setFunction("float", args -> NumberValue.of((float)args[0].asNumber())); + ScopeHandler.setFunction("double", args -> NumberValue.of(args[0].asNumber())); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java index 97ddf2f2..84f45df4 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java @@ -11,7 +11,7 @@ public final class yaml implements Module { @Override public void init() { - Functions.set("yamlencode", new yaml_encode()); - Functions.set("yamldecode", new yaml_decode()); + ScopeHandler.setFunction("yamlencode", new yaml_encode()); + ScopeHandler.setFunction("yamldecode", new yaml_decode()); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java index a8a0db4a..d69c3601 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -10,7 +10,7 @@ public final class yaml_decode implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(1, 2, args.length); try { final String yamlRaw = args[0].asString(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java index 5b30a22e..faccc2a4 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java @@ -11,7 +11,7 @@ public final class yaml_encode implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkOrOr(1, 2, args.length); try { final Object root = process(args[0]); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java index 569979aa..ee18e48b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java @@ -21,11 +21,11 @@ public class zip implements Module { @Override public void init() { - Functions.set("zip", this::zipWithMapper); - Functions.set("zipFiles", this::zipFiles); - Functions.set("unzip", this::unzip); - Functions.set("unzipFiles", this::unzipFiles); - Functions.set("listZipEntries", this::listZipEntries); + ScopeHandler.setFunction("zip", this::zipWithMapper); + ScopeHandler.setFunction("zipFiles", this::zipFiles); + ScopeHandler.setFunction("unzip", this::unzip); + ScopeHandler.setFunction("unzipFiles", this::unzipFiles); + ScopeHandler.setFunction("listZipEntries", this::listZipEntries); } private Value zipWithMapper(Value[] args) { From b7c376f01f1c3e6134bb3ea44df581b40522f6e4 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 4 Sep 2023 21:31:32 +0300 Subject: [PATCH 299/448] Deprecate old Variables/Functions methods --- .../ownlang/modules/std/std_thread.java | 12 +++--------- .../com/annimon/ownlang/lib/Functions.java | 15 ++++++--------- .../com/annimon/ownlang/lib/ScopeHandler.java | 4 ++++ .../com/annimon/ownlang/lib/StringValue.java | 4 ++-- .../com/annimon/ownlang/lib/Variables.java | 19 +++++++++---------- .../ownlang/parser/ast/BinaryExpression.java | 12 +++--------- .../parser/ast/FunctionDefineStatement.java | 4 ++-- .../ast/FunctionReferenceExpression.java | 2 +- .../parser/ast/ObjectCreationExpression.java | 4 ++-- .../parser/ast/VariableExpression.java | 5 ++--- .../parser/linters/AssignValidator.java | 4 ++-- .../DefaultFunctionsOverrideValidator.java | 4 ++-- .../annimon/ownlang/parser/ParserTest.java | 4 ++-- .../annimon/ownlang/parser/ProgramsTest.java | 16 ++++++++-------- 14 files changed, 48 insertions(+), 61 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java index 07c8ead7..d4ece89c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java @@ -1,13 +1,7 @@ package com.annimon.ownlang.modules.std; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; public final class std_thread implements Function { @@ -19,10 +13,10 @@ public Value execute(Value[] args) { if (args[0].type() == Types.FUNCTION) { body = ((FunctionValue) args[0]).getValue(); } else { - body = Functions.get(args[0].asString()); + body = ScopeHandler.getFunction(args[0].asString()); } - // Сдвигаем аргументы + // Shift arguments final Value[] params = new Value[args.length - 1]; if (params.length > 0) { System.arraycopy(args, 1, params, 0, params.length); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java index ed40469e..f0f643fc 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java @@ -12,15 +12,12 @@ private Functions() { } public static Map getFunctions() { return ScopeHandler.functions(); } - - public static boolean isExists(String name) { - return ScopeHandler.isFunctionExists(name); - } - - public static Function get(String name) { - return ScopeHandler.getFunction(name); - } - + + /** + * @deprecated This function remains for backward compatibility with old separate modules + * Use {@link ScopeHandler#setFunction(String, Function)} + */ + @Deprecated public static void set(String key, Function function) { ScopeHandler.setFunction(key, function); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java index 179c4013..79d6a4b7 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -100,6 +100,10 @@ public static void setVariable(String name, Value value) { } } + public static boolean isConstantExists(String name) { + return rootScope.containsConstant(name); + } + public static void setConstant(String name, Value value) { rootScope.setConstant(name, value); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java index db975b8c..1e6bdee0 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java @@ -47,8 +47,8 @@ public Value access(Value propertyValue) { case "isEmpty" -> Converters.voidToBoolean(value::isEmpty); default -> { - if (Functions.isExists(prop)) { - final Function f = Functions.get(prop); + if (ScopeHandler.isFunctionExists(prop)) { + final Function f = ScopeHandler.getFunction(prop); yield new FunctionValue(args -> { final Value[] newArgs = new Value[args.length + 1]; newArgs[0] = this; diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java index a3b41216..f58dd513 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -12,22 +12,21 @@ private Variables() { } public static Map variables() { return ScopeHandler.variables(); } - - public static boolean isExists(String name) { - return ScopeHandler.isVariableOrConstantExists(name); - } - - public static Value get(String name) { - return ScopeHandler.getVariableOrConstant(name); - } - + + /** + * @deprecated This function remains for backward compatibility with old separate modules + * Use {@link ScopeHandler#setVariable(String, Value)} + */ + @Deprecated public static void set(String name, Value value) { ScopeHandler.setVariable(name, value); } /** - * For compatibility with other modules + * @deprecated This function remains for backward compatibility with old separate modules + * Use {@link ScopeHandler#setConstant(String, Value)} */ + @Deprecated public static void define(String name, Value value) { ScopeHandler.setConstant(name, value); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java index d8e49d3a..e6b5499e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -2,13 +2,7 @@ import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.StringValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; /** * @@ -66,8 +60,8 @@ public Value eval() { try { return eval(value1, value2); } catch (OperationIsNotSupportedException ex) { - if (Functions.isExists(operation.toString())) { - return Functions.get(operation.toString()).execute(value1, value2); + if (ScopeHandler.isFunctionExists(operation.toString())) { + return ScopeHandler.getFunction(operation.toString()).execute(value1, value2); } throw ex; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java index 460f8649..80e64c7f 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.UserDefinedFunction; /** @@ -21,7 +21,7 @@ public FunctionDefineStatement(String name, Arguments arguments, Statement body) @Override public void execute() { - Functions.set(name, new UserDefinedFunction(arguments, body)); + ScopeHandler.setFunction(name, new UserDefinedFunction(arguments, body)); } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java index 702d2fa8..891abeb7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java @@ -17,7 +17,7 @@ public FunctionReferenceExpression(String name) { @Override public FunctionValue eval() { super.interruptionCheck(); - return new FunctionValue(Functions.get(name)); + return new FunctionValue(ScopeHandler.getFunction(name)); } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index bd56c597..1f5ec251 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -20,8 +20,8 @@ public Value eval() { final ClassDeclarationStatement cd = ClassDeclarations.get(className); if (cd == null) { // Is Instantiable? - if (Variables.isExists(className)) { - final Value variable = Variables.get(className); + if (ScopeHandler.isVariableOrConstantExists(className)) { + final Value variable = ScopeHandler.getVariableOrConstant(className); if (variable instanceof Instantiable instantiable) { return instantiable.newInstance(ctorArgs()); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java index 4d4bac02..36c39237 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -3,7 +3,6 @@ import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; /** * @@ -25,8 +24,8 @@ public Value eval() { @Override public Value get() { - if (!Variables.isExists(name)) throw new VariableDoesNotExistsException(name); - return Variables.get(name); + if (!ScopeHandler.isVariableOrConstantExists(name)) throw new VariableDoesNotExistsException(name); + return ScopeHandler.getVariableOrConstant(name); } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 7b680bf5..1ce2b764 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.*; /** @@ -15,7 +15,7 @@ public void visit(AssignmentExpression s) { super.visit(s); if (s.target instanceof VariableExpression varExpr) { final String variable = varExpr.name; - if (Variables.isExists(variable)) { + if (ScopeHandler.isConstantExists(variable)) { Console.error(String.format( "Warning: variable \"%s\" overrides constant", variable)); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java index e5bff529..285f1573 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.*; public final class DefaultFunctionsOverrideValidator extends LintVisitor { @@ -9,7 +9,7 @@ public final class DefaultFunctionsOverrideValidator extends LintVisitor { @Override public void visit(FunctionDefineStatement s) { super.visit(s); - if (Functions.isExists(s.name)) { + if (ScopeHandler.isFunctionExists(s.name)) { Console.error(String.format( "Warning: function \"%s\" overrides default module function", s.name)); } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java index 21e11bf9..b1ad7792 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.*; import org.junit.jupiter.api.Test; @@ -35,7 +35,7 @@ public void testParseMultiplicative() { private static void assertEval(Value expectedValue, String input, Expression expected) { BlockStatement program = assertExpression(input, expected); program.execute(); - final Value actual = Variables.get("a"); + final Value actual = ScopeHandler.getVariable("a"); try { assertEquals(expectedValue.asNumber(), actual.asNumber(), 0.001); } catch (NumberFormatException nfe) { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index efd62701..9a413069 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -32,27 +32,27 @@ public static Stream data() { public void initialize() { ScopeHandler.resetScope(); // Let's mock junit methods as ounit functions - Functions.set("assertEquals", (args) -> { + ScopeHandler.setFunction("assertEquals", (args) -> { assertEquals(args[0], args[1]); return NumberValue.ONE; }); - Functions.set("assertNotEquals", (args) -> { + ScopeHandler.setFunction("assertNotEquals", (args) -> { assertNotEquals(args[0], args[1]); return NumberValue.ONE; }); - Functions.set("assertSameType", (args) -> { + ScopeHandler.setFunction("assertSameType", (args) -> { assertEquals(args[0].type(), args[1].type()); return NumberValue.ONE; }); - Functions.set("assertTrue", (args) -> { + ScopeHandler.setFunction("assertTrue", (args) -> { assertTrue(args[0].asInt() != 0); return NumberValue.ONE; }); - Functions.set("assertFalse", (args) -> { + ScopeHandler.setFunction("assertFalse", (args) -> { assertFalse(args[0].asInt() != 0); return NumberValue.ONE; }); - Functions.set("assertFail", (args) -> { + ScopeHandler.setFunction("assertFail", (args) -> { assertThrows(Throwable.class, () -> ((FunctionValue) args[0]).getValue().execute()); return NumberValue.ONE; @@ -73,7 +73,7 @@ public void testProgram(String programPath) throws IOException { } @Test - public void testOutput() throws IOException { + public void testOutput() { OutputSettings oldSettings = Console.getSettings(); Console.useSettings(new StringOutputSettings()); String source = "for i = 0, i <= 5, i++\n print i"; @@ -93,7 +93,7 @@ public void testOutput() throws IOException { public void visit(FunctionDefineStatement s) { if (s.name.startsWith("test")) { try { - Functions.get(s.name).execute(); + ScopeHandler.getFunction(s.name).execute(); } catch (AssertionError err) { throw new AssertionError(s.name + ": " + err.getMessage(), err); } From 32bffaee869b0c328bec179468951e60d3df46cd Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 7 Sep 2023 18:40:28 +0300 Subject: [PATCH 300/448] Fix scope of foreach and match statements v = 100 for v : [1] .. v should be 1, not 100 --- .../ownlang/lib/AutoCloseableScope.java | 8 + .../com/annimon/ownlang/lib/ScopeHandler.java | 5 + .../com/annimon/ownlang/parser/Parser.java | 2 +- .../parser/ast/ForeachArrayStatement.java | 24 +-- .../parser/ast/ForeachMapStatement.java | 30 +-- .../ownlang/parser/ast/MatchExpression.java | 43 +---- .../annimon/ownlang/parser/ProgramsTest.java | 8 + .../parser/ast/VariableExpressionTest.java | 7 + .../resources/expressions/foreachKeyValue.own | 13 ++ .../resources/expressions/foreachValue.own | 2 +- .../resources/expressions/matchExpression.own | 179 ++++++++++++++++++ 11 files changed, 245 insertions(+), 76 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/AutoCloseableScope.java create mode 100644 ownlang-parser/src/test/resources/expressions/matchExpression.own diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/AutoCloseableScope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/AutoCloseableScope.java new file mode 100644 index 00000000..bde190d0 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/AutoCloseableScope.java @@ -0,0 +1,8 @@ +package com.annimon.ownlang.lib; + +public final class AutoCloseableScope implements AutoCloseable { + @Override + public void close() { + ScopeHandler.pop(); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java index 79d6a4b7..67debfca 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -36,6 +36,11 @@ public static void resetScope() { scope = rootScope; } + public static AutoCloseableScope closeableScope() { + push(); + return new AutoCloseableScope(); + } + public static void push() { synchronized (lock) { scope = new Scope(scope); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index ea6333af..0f70be00 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -22,7 +22,7 @@ public static Statement parse(List tokens) { final Parser parser = new Parser(tokens); final Statement program = parser.parse(); if (parser.getParseErrors().hasErrors()) { - throw new ParseException(); + throw new ParseException(parser.getParseErrors().toString()); } return program; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java index 30b4b992..608b301a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -23,22 +23,14 @@ public ForeachArrayStatement(String variable, Expression container, Statement bo @Override public void execute() { super.interruptionCheck(); - // TODO removing without checking shadowing is dangerous - final Value previousVariableValue = ScopeHandler.getVariable(variable); - - final Value containerValue = container.eval(); - switch (containerValue.type()) { - case Types.STRING -> iterateString(containerValue.asString()); - case Types.ARRAY -> iterateArray((ArrayValue) containerValue); - case Types.MAP -> iterateMap((MapValue) containerValue); - default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); - } - - // Restore variables - if (previousVariableValue != null) { - ScopeHandler.setVariable(variable, previousVariableValue); - } else { - ScopeHandler.removeVariable(variable); + try (final var ignored = ScopeHandler.closeableScope()) { + final Value containerValue = container.eval(); + switch (containerValue.type()) { + case Types.STRING -> iterateString(containerValue.asString()); + case Types.ARRAY -> iterateArray((ArrayValue) containerValue); + case Types.MAP -> iterateMap((MapValue) containerValue); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); + } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java index b0588ae6..816d8f45 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -24,28 +24,14 @@ public ForeachMapStatement(String key, String value, Expression container, State @Override public void execute() { super.interruptionCheck(); - // TODO removing without checking shadowing is dangerous - final Value previousVariableValue1 = ScopeHandler.getVariable(key); - final Value previousVariableValue2 = ScopeHandler.getVariable(value); - - final Value containerValue = container.eval(); - switch (containerValue.type()) { - case Types.STRING -> iterateString(containerValue.asString()); - case Types.ARRAY -> iterateArray((ArrayValue) containerValue); - case Types.MAP -> iterateMap((MapValue) containerValue); - default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); - } - - // Restore variables - if (previousVariableValue1 != null) { - ScopeHandler.setVariable(key, previousVariableValue1); - } else { - ScopeHandler.removeVariable(key); - } - if (previousVariableValue2 != null) { - ScopeHandler.setVariable(value, previousVariableValue2); - } else { - ScopeHandler.removeVariable(value); + try (final var ignored = ScopeHandler.closeableScope()) { + final Value containerValue = container.eval(); + switch (containerValue.type()) { + case Types.STRING -> iterateString(containerValue.asString()); + case Types.ARRAY -> iterateArray((ArrayValue) containerValue); + case Types.MAP -> iterateMap((MapValue) containerValue); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); + } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index e1a34045..d1602c02 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -54,13 +54,10 @@ public Value eval() { } } if ((value.type() == Types.ARRAY) && (p instanceof ListPattern pattern)) { - if (matchListPattern((ArrayValue) value, pattern)) { - // Clean up variables if matched - final Value result = evalResult(p.result); - for (String var : pattern.parts) { - ScopeHandler.removeVariable(var); + try (final var ignored = ScopeHandler.closeableScope()) { + if (matchListPattern((ArrayValue) value, pattern)) { + return evalResult(p.result); } - return result; } } if ((value.type() == Types.ARRAY) && (p instanceof TuplePattern pattern)) { @@ -91,20 +88,12 @@ private boolean matchListPattern(ArrayValue array, ListPattern p) { final int arraySize = array.size(); switch (partsSize) { case 0: // match [] { case []: ... } - if ((arraySize == 0) && optMatches(p)) { - return true; - } - return false; + return (arraySize == 0) && optMatches(p); case 1: // match arr { case [x]: x = arr ... } final String variable = parts.get(0); ScopeHandler.defineVariableInCurrentScope(variable, array); - if (optMatches(p)) { - return true; - } - // TODO remove is dangerous - ScopeHandler.removeVariable(variable); - return false; + return optMatches(p); default: { // match arr { case [...]: .. } if (partsSize == arraySize) { @@ -124,16 +113,7 @@ private boolean matchListPatternEqualsSize(ListPattern p, List parts, in for (int i = 0; i < partsSize; i++) { ScopeHandler.defineVariableInCurrentScope(parts.get(i), array.get(i)); } - if (optMatches(p)) { - // Clean up will be provided after evaluate result - return true; - } - // Clean up variables if no match - for (String var : parts) { - // TODO removing without checking shadowing is dangerous - ScopeHandler.removeVariable(var); - } - return false; + return optMatches(p); } private boolean matchListPatternWithTail(ListPattern p, List parts, int partsSize, ArrayValue array, int arraySize) { @@ -148,16 +128,7 @@ private boolean matchListPatternWithTail(ListPattern p, List parts, int tail.set(i - lastPart, array.get(i)); } ScopeHandler.defineVariableInCurrentScope(parts.get(lastPart), tail); - // Check optional condition - if (optMatches(p)) { - // Clean up will be provided after evaluate result - return true; - } - // Clean up variables - for (String var : parts) { - ScopeHandler.removeVariable(var); - } - return false; + return optMatches(p); } private boolean match(Value value, Value constant) { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 9a413069..44d4e508 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -57,6 +57,14 @@ public void initialize() { () -> ((FunctionValue) args[0]).getValue().execute()); return NumberValue.ONE; }); + ScopeHandler.setFunction("fail", (args) -> { + if (args.length > 0) { + fail(args[0].asString()); + } else { + fail(); + } + return NumberValue.ONE; + }); } @ParameterizedTest diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java index 87d9b00a..beb4d5f1 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.ScopeHandler; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; @@ -11,6 +13,11 @@ * @author aNNiMON */ public class VariableExpressionTest { + + @BeforeEach + void setUp() { + ScopeHandler.resetScope(); + } @Test public void testVariable() { diff --git a/ownlang-parser/src/test/resources/expressions/foreachKeyValue.own b/ownlang-parser/src/test/resources/expressions/foreachKeyValue.own index d0256c8a..e45b19b2 100644 --- a/ownlang-parser/src/test/resources/expressions/foreachKeyValue.own +++ b/ownlang-parser/src/test/resources/expressions/foreachKeyValue.own @@ -28,3 +28,16 @@ def testStringIterate() { assertEquals("ABCD", str) assertEquals(394/*97 + 98 + 99 + 100*/, sum) } + +def testScope() { + a = 100 + b = 200 + sum = 0 + for a, b : {14: 3} { + sum += a + sum += b + } + assertEquals(17, sum) + assertEquals(14, a) + assertEquals(3, b) +} \ No newline at end of file diff --git a/ownlang-parser/src/test/resources/expressions/foreachValue.own b/ownlang-parser/src/test/resources/expressions/foreachValue.own index 36942a06..6f1b8a43 100644 --- a/ownlang-parser/src/test/resources/expressions/foreachValue.own +++ b/ownlang-parser/src/test/resources/expressions/foreachValue.own @@ -36,6 +36,6 @@ def testScope() { sum += v } assertEquals(6, sum) - assertEquals(45, v) + assertEquals(3, v) } diff --git a/ownlang-parser/src/test/resources/expressions/matchExpression.own b/ownlang-parser/src/test/resources/expressions/matchExpression.own new file mode 100644 index 00000000..2e1bcb7d --- /dev/null +++ b/ownlang-parser/src/test/resources/expressions/matchExpression.own @@ -0,0 +1,179 @@ +use "types" + +def testMatchValue() { + value = 20 + result = match value { + case 10: "ten" + case 20: "twenty" + } + assertEquals("twenty", result) +} + +def testMatchValueAny() { + value = 20 + result = match value { + case 0: "zero" + case 1: "one" + case _: "other" + } + assertEquals("other", result) +} + +def testMatchAdditionalCheck() { + value = 20 + result = match value { + case 10: "ten" + case x if x < 10: "" + x + "<10" + case x if x > 10: "" + x + ">10" + } + assertEquals("20>10", result) +} + +def testMatchAdditionalCheckScope() { + x = 20 + result = match x { + case 10: "ten" + case x if x < 10: fail() + case y if y > 10: assertEquals(20, y) + } + assertEquals(20, x) + assertEquals(true, result) +} + +def printArrayRecursive(arr) = match arr { + case [head :: tail]: "[" + head + ", " + printArrayRecursive(tail) + "]" + case []: "[]" + case last: "[" + last + ", []]" +} + +def testMatchEmptyArray() { + result = printArrayRecursive([]) + assertEquals("[]", result) +} + +def testMatchOneElementArray() { + result = printArrayRecursive([1]) + assertEquals("[[1], []]", result) +} + +def testMatchTwoElementsArray() { + result = printArrayRecursive([1, 2]) + assertEquals("[1, [2, []]]", result) +} + +def testMatchArray() { + result = printArrayRecursive([1, 2, 3, 4]) + assertEquals("[1, [2, [3, [4, []]]]]", result) +} + +def testMatchArray2() { + def elementsCount(arr) = match arr { + case [a :: b :: c :: d :: e]: 5 + case [a :: b :: c :: d]: 4 + case [a :: b :: c]: 3 + case [a :: b]: 2 + case (7): -7 // special case 1 + case [a] if a == [8]: -8 // special case 2 + case []: 0 + case [a]: 1 + } + assertEquals(4, elementsCount([1, 2, 3, 4])) + assertEquals(3, elementsCount([1, 2, 3])) + assertEquals(2, elementsCount([1, 2])) + assertEquals(1, elementsCount([1])) + assertEquals(-7, elementsCount([7])) + assertEquals(-8, elementsCount([8])) + assertEquals(0, elementsCount([])) +} + +def testMatchOneElementArrayScope() { + head = 100 + tail = 200 + result = match [1] { + case [head :: tail]: fail("Multi-array") + case []: fail("Empty array") + case last: assertEquals(1, last[0]) + } + assertEquals(100, head) + assertEquals(200, tail) + assertEquals(true, result) +} + +def testMatchOneElementArrayDefinedVariableScope() { + head = 100 + tail = 200 + last = 300 + result = match [1] { + case [head :: tail]: fail("Multi-array") + case []: fail("Empty array") + case last: fail("Array should not be equal " + last) + case rest: assertEquals(1, rest[0]) + } + assertEquals(100, head) + assertEquals(200, tail) + assertEquals(300, last) + assertEquals(true, result) +} + +def testMatchArrayScope() { + head = 100 + tail = 200 + result = match [1, 2, 3] { + case [head :: tail]: assertEquals(1, head) + case []: fail("Empty array") + case last: fail("One element") + } + assertEquals(100, head) + assertEquals(200, tail) + assertEquals(true, result) +} + +def testMatchTuple() { + result = match [1, 2] { + case (0, 1): "(0, 1)" + case (1, 2): "(1, 2)" + case (2, 3): "(2, 3)" + } + assertEquals("(1, 2)", result) +} + +def testMatchTupleDifferentLength() { + result = match [1, 2] { + case (1): "(1)" + case (1, 2, 3, 4): "(1, 2, 3, 4)" + case _: "not matched" + } + assertEquals("not matched", result) +} + +def testMatchTupleAny1() { + result = match [1, 2] { + case (0, _): "(0, _)" + case (1, _): "(1, _)" + case (2, _): "(2, _)" + } + assertEquals("(1, _)", result) +} + +def testMatchTupleAny2() { + result = match [2, 3] { + case (0, _): "(0, _)" + case (1, _): "(1, _)" + case (_, _): "(_, _)" + } + assertEquals("(_, _)", result) +} + +def testMatchTupleAny3() { + result = match [2, 3] { + case (0, _): "(0, _)" + case (1, _): "(1, _)" + case _: "_" + } + assertEquals("_", result) +} + +def testScope() { + +} + From cce75927b8f333040d0603def6b64ab78f727ddd Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 7 Sep 2023 19:27:47 +0300 Subject: [PATCH 301/448] Change list pattern matcher behavior for single element match [1] { case [x]: } Before: x = [1] After: x = 1 x will be the first (and single) value of an array --- examples/basics/pattern_matching.own | 2 +- .../ownlang/parser/ast/MatchExpression.java | 12 ++-- .../resources/expressions/matchExpression.own | 63 ++++++++++++++----- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/examples/basics/pattern_matching.own b/examples/basics/pattern_matching.own index 7a0ff506..cc9b118a 100644 --- a/examples/basics/pattern_matching.own +++ b/examples/basics/pattern_matching.own @@ -36,7 +36,7 @@ println printArrayRecursive([1, 2, 3, 4, 5, 6, 7]) def printArrayRecursive(arr) = match arr { case [head :: tail]: "[" + head + ", " + printArrayRecursive(tail) + "]" case []: "[]" - case last: "[" + last + ", []]" + case other: "[" + other + ", []]" } println "\nPattern matching on arrays by value" diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index d1602c02..2a5e43de 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -90,10 +90,14 @@ private boolean matchListPattern(ArrayValue array, ListPattern p) { case 0: // match [] { case []: ... } return (arraySize == 0) && optMatches(p); - case 1: // match arr { case [x]: x = arr ... } - final String variable = parts.get(0); - ScopeHandler.defineVariableInCurrentScope(variable, array); - return optMatches(p); + case 1: // match arr { case [x]: x = arr[0] ... } + if (arraySize == 1) { + final String variable = parts.get(0); + final var value = array.get(0); + ScopeHandler.defineVariableInCurrentScope(variable, value); + return optMatches(p); + } + return false; default: { // match arr { case [...]: .. } if (partsSize == arraySize) { diff --git a/ownlang-parser/src/test/resources/expressions/matchExpression.own b/ownlang-parser/src/test/resources/expressions/matchExpression.own index 2e1bcb7d..5a5a1f3e 100644 --- a/ownlang-parser/src/test/resources/expressions/matchExpression.own +++ b/ownlang-parser/src/test/resources/expressions/matchExpression.own @@ -43,7 +43,8 @@ def testMatchAdditionalCheckScope() { def printArrayRecursive(arr) = match arr { case [head :: tail]: "[" + head + ", " + printArrayRecursive(tail) + "]" case []: "[]" - case last: "[" + last + ", []]" + case [last]: "[" + last + ", []]" + case value: value } def testMatchEmptyArray() { @@ -53,17 +54,17 @@ def testMatchEmptyArray() { def testMatchOneElementArray() { result = printArrayRecursive([1]) - assertEquals("[[1], []]", result) + assertEquals("[1, []]", result) } def testMatchTwoElementsArray() { result = printArrayRecursive([1, 2]) - assertEquals("[1, [2, []]]", result) + assertEquals("[1, 2]", result) } def testMatchArray() { result = printArrayRecursive([1, 2, 3, 4]) - assertEquals("[1, [2, [3, [4, []]]]]", result) + assertEquals("[1, [2, [3, 4]]]", result) } def testMatchArray2() { @@ -73,9 +74,9 @@ def testMatchArray2() { case [a :: b :: c]: 3 case [a :: b]: 2 case (7): -7 // special case 1 - case [a] if a == [8]: -8 // special case 2 - case []: 0 + case [a] if a == 8: -8 // special case 2 case [a]: 1 + case []: 0 } assertEquals(4, elementsCount([1, 2, 3, 4])) assertEquals(3, elementsCount([1, 2, 3])) @@ -86,6 +87,16 @@ def testMatchArray2() { assertEquals(0, elementsCount([])) } +def testMatchArray3() { + def elementD(arr) = match arr { + case [a :: b :: c :: d]: d + case _: [] + } + assertEquals(4, elementD([1, 2, 3, 4])) + assertEquals([4, 5, 6], elementD([1, 2, 3, 4, 5, 6])) + assertEquals([], elementD([1, 2])) +} + def testMatchOneElementArrayScope() { head = 100 tail = 200 @@ -102,16 +113,16 @@ def testMatchOneElementArrayScope() { def testMatchOneElementArrayDefinedVariableScope() { head = 100 tail = 200 - last = 300 + rest = 300 result = match [1] { case [head :: tail]: fail("Multi-array") case []: fail("Empty array") - case last: fail("Array should not be equal " + last) - case rest: assertEquals(1, rest[0]) + case rest: fail("Array should not be equal " + rest) + case [last]: assertEquals(1, last) } assertEquals(100, head) assertEquals(200, tail) - assertEquals(300, last) + assertEquals(300, rest) assertEquals(true, result) } @@ -121,7 +132,7 @@ def testMatchArrayScope() { result = match [1, 2, 3] { case [head :: tail]: assertEquals(1, head) case []: fail("Empty array") - case last: fail("One element") + case [last]: fail("One element") } assertEquals(100, head) assertEquals(200, tail) @@ -173,7 +184,31 @@ def testMatchTupleAny3() { assertEquals("_", result) } -def testScope() { - +def testDestructuringArray() { + parsedData = [ + ["Kyiv", 839, 3017000, "Ukraine", "...", "..."], + ["Shebekino", "N/A", "invalid"], + ["New York", 783.8, 18937000, "USA", "..."], + ["N/A"], + [] + ] + cities = [] + areas = [] + for row : parsedData { + match row { + // Match fully parsed data + case [name :: area :: population :: country]: { + cities ::= name + areas ::= area + } + // Match partially parsed data, which contains a city name and some other unknown values + case [name :: rest]: { + cities ::= name + } + // Match other invalid data + case arr: /* skip */ 0 + } + } + assertEquals(["Kyiv", "Shebekino", "New York"], cities) + assertEquals([839, 783.8], areas) } - From 589856fbf372c384392ce71a33af58bcc25d7e19 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 7 Sep 2023 19:45:16 +0300 Subject: [PATCH 302/448] Fix url in image examples --- examples/canvas/fx_image.own | 2 +- examples/canvas/fx_image_negate.own | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/canvas/fx_image.own b/examples/canvas/fx_image.own index a2f1cec5..3b85381b 100644 --- a/examples/canvas/fx_image.own +++ b/examples/canvas/fx_image.own @@ -1,6 +1,6 @@ use "canvasfx" g = window("JavaFX Image demo", 400, 200) -img = createImage("http://lorempixel.com/400/200/") +img = createImage("https://picsum.photos/400/200/") g.drawImage(img, 0, 0) repaint() \ No newline at end of file diff --git a/examples/canvas/fx_image_negate.own b/examples/canvas/fx_image_negate.own index 2338a0c8..a1fe3f70 100644 --- a/examples/canvas/fx_image_negate.own +++ b/examples/canvas/fx_image_negate.own @@ -2,7 +2,7 @@ use "std" use "canvasfx" graphics = window("JavaFX Image negation demo", 400, 400) -imgSource = createImage("http://lorempixel.com/400/200/") +imgSource = createImage("https://picsum.photos/400/200/") pixels = imgSource.getPixels() size = length(pixels) for i = 0, i < size, i++ { From 59f8c4109e8c0a439d97fe9154d8cd9e9c3efafa Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 9 Sep 2023 15:51:36 +0300 Subject: [PATCH 303/448] Simplify use statement to take variable WORD only arguments --- .../com/annimon/ownlang/parser/Linter.java | 2 - .../com/annimon/ownlang/parser/Parser.java | 21 ++++---- .../ownlang/parser/ast/UseStatement.java | 38 ++++--------- .../UseWithNonStringValueValidator.java | 53 ------------------- .../parser/optimization/ConstantFolding.java | 12 ++--- .../optimization/OptimizationVisitor.java | 8 +-- .../parser/optimization/VariablesGrabber.java | 31 ++++------- .../parser/visitors/AbstractVisitor.java | 2 +- .../parser/visitors/ModuleDetector.java | 9 +--- .../ownlang/parser/visitors/PrintVisitor.java | 2 +- .../ownlang/utils/ModulesInfoCreator.java | 7 ++- .../java/com/annimon/ownlang/utils/Repl.java | 2 +- 12 files changed, 45 insertions(+), 142 deletions(-) delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java index ba2dee1b..cb656716 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java @@ -6,7 +6,6 @@ import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.linters.AssignValidator; import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; -import com.annimon.ownlang.parser.linters.UseWithNonStringValueValidator; public final class Linter { @@ -22,7 +21,6 @@ private Linter(Statement program) { public void execute() { final Visitor[] validators = new Visitor[] { - new UseWithNonStringValueValidator(), new AssignValidator(), new DefaultFunctionsOverrideValidator() }; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 0f70be00..626b0f56 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -5,12 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @@ -145,7 +140,7 @@ private Statement statement() { return new ReturnStatement(expression()); } if (match(TokenType.USE)) { - return new UseStatement(expression()); + return useStatement(); } if (match(TokenType.INCLUDE)) { return new IncludeStatement(expression()); @@ -168,13 +163,21 @@ private Statement statement() { return assignmentStatement(); } + private UseStatement useStatement() { + final var modules = new HashSet(); + do { + modules.add(consume(TokenType.WORD).text()); + } while (match(TokenType.COMMA)); + return new UseStatement(modules); + } + private Statement assignmentStatement() { if (match(TokenType.EXTRACT)) { return destructuringAssignment(); } final Expression expression = expression(); - if (expression instanceof Statement) { - return (Statement) expression; + if (expression instanceof Statement statement) { + return statement; } throw new ParseException("Unknown statement: " + get(0)); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index d381dc1b..69e30cf0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -1,11 +1,8 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; import java.lang.reflect.Method; +import java.util.Collection; /** * @@ -16,24 +13,17 @@ public final class UseStatement extends InterruptableNode implements Statement { private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s"; private static final String INIT_CONSTANTS_METHOD = "initConstants"; - public final Expression expression; + public final Collection modules; - public UseStatement(Expression expression) { - this.expression = expression; + public UseStatement(Collection modules) { + this.modules = modules; } @Override public void execute() { super.interruptionCheck(); - final Value value = expression.eval(); - switch (value.type()) { - case Types.ARRAY -> { - for (Value module : ((ArrayValue) value)) { - loadModule(module.asString()); - } - } - case Types.STRING -> loadModule(value.asString()); - default -> throw typeException(value); + for (String module : modules) { + loadModule(module); } } @@ -49,19 +39,9 @@ private void loadModule(String name) { } public void loadConstants() { - if (expression instanceof ArrayExpression ae) { - for (Expression expr : ae.elements) { - loadConstants(expr.eval().asString()); - } + for (String module : modules) { + loadConstants(module); } - if (expression instanceof ValueExpression ve) { - loadConstants(ve.value.asString()); - } - } - - private TypeException typeException(Value value) { - return new TypeException("Array or string required in 'use' statement, " + - "got " + Types.typeToString(value.type()) + " " + value); } private void loadConstants(String moduleName) { @@ -86,6 +66,6 @@ public R accept(ResultVisitor visitor, T t) { @Override public String toString() { - return "use " + expression; + return "use " + String.join(", ", modules); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java deleted file mode 100644 index a8b24417..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.annimon.ownlang.parser.linters; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.parser.ast.*; - -public final class UseWithNonStringValueValidator extends LintVisitor { - - @Override - public void visit(IncludeStatement st) { - super.visit(st); - applyVisitor(st, this); - } - - @Override - public void visit(UseStatement st) { - super.visit(st); - - if (st.expression instanceof ArrayExpression ae) { - for (Expression expr : ae.elements) { - if (!checkExpression(expr)) { - return; - } - } - } else { - if (!checkExpression(st.expression)) { - return; - } - } - } - - private boolean checkExpression(Expression expr) { - if (expr instanceof ValueExpression valueExpr) { - final Value value = valueExpr.value; - if (value.type() != Types.STRING) { - warnWrongType(value); - return false; - } - return true; - } else { - Console.error(String.format( - "Warning: `use` with %s, not ValueExpression", expr.getClass().getSimpleName())); - return false; - } - } - - private void warnWrongType(Value value) { - Console.error(String.format( - "Warning: `use` with %s - %s, not string", - Types.typeToString(value.type()), value.asString())); - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java index b0f06af7..ea25ce56 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java @@ -1,12 +1,7 @@ package com.annimon.ownlang.parser.optimization; import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; -import com.annimon.ownlang.parser.ast.BinaryExpression; -import com.annimon.ownlang.parser.ast.ConditionalExpression; -import com.annimon.ownlang.parser.ast.FunctionDefineStatement; -import com.annimon.ownlang.parser.ast.Node; -import com.annimon.ownlang.parser.ast.UnaryExpression; -import com.annimon.ownlang.parser.ast.ValueExpression; +import com.annimon.ownlang.parser.ast.*; import com.annimon.ownlang.parser.visitors.VisitorUtils; import java.util.HashSet; import java.util.Set; @@ -104,6 +99,11 @@ public Node visit(UnaryExpression s, Void t) { return super.visit(s, t); } + @Override + public Node visit(UseStatement s, Void unused) { + return null; + } + @Override public Node visit(FunctionDefineStatement s, Void t) { if (OPERATORS.contains(s.name)) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 6fb81e31..a903fa83 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -412,10 +412,6 @@ public Node visit(WhileStatement s, T t) { @Override public Node visit(UseStatement s, T t) { - final Node expression = s.expression.accept(this, t); - if (expression != s.expression) { - return new UseStatement((Expression) expression); - } return s; } @@ -450,8 +446,8 @@ protected boolean visit(final Arguments in, final Arguments out, T t) { } protected Statement consumeStatement(Node node) { - if (node instanceof Statement) { - return (Statement) node; + if (node instanceof Statement statement) { + return statement; } return new ExprStatement((Expression) node); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 7e753a5b..38b6c666 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.optimization; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.*; @@ -99,36 +100,22 @@ public Node visit(UnaryExpression s, Map t) { @Override public Node visit(UseStatement s, Map t) { if (grabModuleConstants) { - // To get module variables we need to store current variables, clear all, then load module. - final Map currentVariables = new HashMap<>(Variables.variables()); - Variables.variables().clear(); - if (canLoadConstants(s.expression)) { - s.loadConstants(); - } - // Grab module variables - for (Map.Entry entry : Variables.variables().entrySet()) { + // To get module constants we need to store current constants, clear all, then load module. + final Map currentConstants = new HashMap<>(ScopeHandler.constants()); + ScopeHandler.constants().clear(); + s.loadConstants(); + // Grab module constants + for (Map.Entry entry : ScopeHandler.constants().entrySet()) { final VariableInfo var = variableInfo(t, entry.getKey()); var.value = entry.getValue(); t.put(entry.getKey(), var); } - // Restore previous variables - Variables.variables().putAll(currentVariables); + // Restore previous constants + ScopeHandler.constants().putAll(currentConstants); } return super.visit(s, t); } - private boolean canLoadConstants(Expression expression) { - if (expression instanceof ArrayExpression ae) { - for (Expression expr : ae.elements) { - if (!isValue(expr)) { - return false; - } - } - return true; - } - return isValue(expression); - } - @Override protected boolean visit(Arguments in, Arguments out, Map t) { for (Argument argument : in) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 6455abfa..11b17fdc 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -192,6 +192,6 @@ public void visit(WhileStatement st) { @Override public void visit(UseStatement st) { - st.expression.accept(this); + } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java index 3bfce125..f3993920 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -23,14 +23,7 @@ public Set detect(Statement s) { @Override public void visit(UseStatement st) { - if (st.expression instanceof ArrayExpression ae) { - for (Expression expr : ae.elements) { - modules.add(expr.eval().asString()); - } - } - if (st.expression instanceof ValueExpression ve) { - modules.add(ve.value.asString()); - } + modules.addAll(st.modules); super.visit(st); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 1da2dcce..093c0d88 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -358,7 +358,7 @@ public StringBuilder visit(UnaryExpression s, StringBuilder t) { @Override public StringBuilder visit(UseStatement s, StringBuilder t) { t.append("use "); - s.expression.accept(this, t); + t.append(String.join(", ", s.modules)); return t; } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index 333f4bf0..d119fd33 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -30,14 +30,13 @@ public static void main(String[] args) for (String moduleName : moduleNames) { final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName); Class moduleClass = Class.forName(moduleClassPath); - Functions.getFunctions().clear(); - Variables.variables().clear(); + ScopeHandler.resetScope(); final Module module = (Module) moduleClass.getDeclaredConstructor().newInstance(); module.init(); final ModuleInfo moduleInfo = new ModuleInfo(moduleName); - moduleInfo.functions.addAll(Functions.getFunctions().keySet()); - moduleInfo.constants.putAll(Variables.variables()); + moduleInfo.functions.addAll(ScopeHandler.functions().keySet()); + moduleInfo.constants.putAll(ScopeHandler.constants()); moduleInfo.types.addAll(listValues(moduleClass)); moduleInfos.add(moduleInfo); } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index fd4c5032..10ee60ee 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -135,7 +135,7 @@ private static void printHelp(boolean full) { final int maxCols = 2; final int size = commands.size(); for (int i = 0; i < size; i += maxCols) { - // Pad to max length and print in tab-separatex maxCols columns + // Pad to max length and print in tab-separated maxCols columns System.out.println(commands .subList(i, Math.min(size, i + maxCols)) .stream() From 6d0886316c29b880f61ff9a0e298400cbfb4bce3 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 9 Sep 2023 15:52:32 +0300 Subject: [PATCH 304/448] Fix use statement in programs --- README.md | 10 +- docs/modules.yml | 132 +++++++++--------- examples.own | 2 +- examples/basics/array.own | 2 +- examples/basics/bitwise_operators.own | 2 +- examples/basics/classes.own | 2 +- examples/basics/destructuring_assignment.own | 2 +- examples/basics/loops.own | 4 +- examples/basics/operator_overloading.own | 2 +- examples/basics/pattern_matching.own | 3 +- examples/basics/thread.own | 2 +- examples/basics/types.own | 3 +- examples/canvas/1.own | 2 +- examples/canvas/2.own | 3 +- examples/canvas/animate_line.own | 3 +- examples/canvas/animate_line_thread.own | 3 +- examples/canvas/control_point.own | 3 +- examples/canvas/fractal_polygon.own | 4 +- examples/canvas/fractal_rect.own | 2 +- examples/canvas/fx_basic_shapes.own | 2 +- examples/canvas/fx_event_handlers.own | 3 +- examples/canvas/fx_global_alpha.own | 2 +- examples/canvas/fx_image.own | 2 +- examples/canvas/fx_image_negate.own | 3 +- examples/canvas/fx_koch_snowflake.own | 4 +- examples/canvas/fx_rotation.own | 3 +- examples/console/colors.own | 2 +- examples/database/hsqldb.own | 2 +- examples/database/sqlite.own | 2 +- examples/formats/gzip.own | 2 +- examples/formats/json.own | 2 +- examples/formats/yaml.own | 2 +- examples/formats/zip.own | 2 +- examples/forms/basic.own | 2 +- examples/forms/button.own | 2 +- examples/forms/complicatedForm.own | 3 +- examples/forms/look_and_feel.own | 2 +- examples/forms/panel.own | 2 +- examples/forms/progressbar.own | 2 +- examples/forms/samobot_chat.own | 2 +- examples/forms/textarea.own | 2 +- examples/forms/textfield.own | 3 +- examples/forms/windowlistener.own | 2 +- examples/functions/basics.own | 4 +- examples/functions/calculator.own | 3 +- examples/functions/chain.own | 3 +- examples/functions/filter_map.own | 3 +- examples/functions/flatmap.own | 3 +- examples/functions/reduce.own | 2 +- examples/functions/sortby.own | 3 +- examples/functions/stream.own | 2 +- examples/game/agar.own | 4 +- examples/game/minesweeper.own | 5 +- examples/game/pipes-online/pipes_online.own | 4 +- examples/game/pipes.own | 3 +- examples/game/pipes_online.own | 4 +- examples/java/system_info.own | 2 +- examples/network/demo.own | 3 +- examples/network/github_timeline.own | 2 +- examples/network/okhttp_imgur_upload.own | 2 +- .../network/okhttp_telegram_sendvoice.own | 2 +- examples/network/okhttp_websocket.own | 2 +- examples/network/telegram_api.own | 5 +- examples/network/twitch_tools.own | 8 +- examples/robot/paint_lines.own | 2 +- examples/versions/whatsnew_1.5.0.own | 2 +- .../resources/benchmarks/useStatement.own | 6 +- .../resources/expressions/foreachValue.own | 2 +- .../test/resources/expressions/include.own | 2 +- .../resources/expressions/matchExpression.own | 2 +- .../test/resources/modules/base64/base64.own | 2 +- .../resources/modules/date/compareDates.own | 2 +- .../resources/modules/date/dateFormat.own | 2 +- .../test/resources/modules/date/dateParse.own | 2 +- .../test/resources/modules/date/newDate.own | 2 +- .../test/resources/modules/files/files.own | 2 +- .../resources/modules/functional/chain.own | 2 +- .../resources/modules/functional/foreach.own | 2 +- .../resources/modules/functional/stream.own | 2 +- .../test/resources/modules/gzip/gzipBytes.own | 2 +- .../test/resources/modules/java/classes.own | 2 +- .../test/resources/modules/regex/match.own | 2 +- .../modules/regex/replaceCallback.own | 2 +- .../resources/modules/std/arraySplice.own | 2 +- .../test/resources/modules/std/default.own | 4 +- .../test/resources/modules/std/getBytes.own | 2 +- .../test/resources/modules/std/indexOf.own | 2 +- .../resources/modules/std/lastIndexOf.own | 2 +- .../test/resources/modules/std/parseInt.own | 2 +- .../test/resources/modules/std/parseLong.own | 2 +- .../src/test/resources/modules/std/range.own | 2 +- .../resources/modules/std/stringFromBytes.own | 2 +- .../resources/modules/std/stripMargin.own | 2 +- .../resources/modules/std/toHexString.own | 2 +- .../src/test/resources/modules/std/try.own | 2 +- .../resources/modules/yaml/yamldecode.own | 2 +- .../resources/modules/yaml/yamlencode.own | 2 +- .../test/resources/other/stringFunctions.own | 2 +- .../resources/other/useStatementScope.own | 26 ++++ program.own | 12 +- tests.own | 6 +- 101 files changed, 202 insertions(+), 226 deletions(-) create mode 100644 ownlang-parser/src/test/resources/other/useStatementScope.own diff --git a/README.md b/README.md index 2b6669c3..313ccaab 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ fizzbuzz() Operate data in functional style. ```scala -use ["std", "functional"] +use std, functional nums = [1,2,3,4,5,6,7,8,9,10] nums = filter(nums, def(x) = x % 2 == 0) @@ -93,7 +93,7 @@ println "Sum: " + stream(range(1, 11)) Why not? ```scala -use ["std", "types", "math"] +use std, types, math def `..`(a, b) = range(a, b) def `**`(a, b) = int(pow(a, b)) @@ -107,13 +107,11 @@ for y : 1 .. 10 { Easy async HTTP requests with `http` module. ```scala -use "std" -use "http" -use "functional" +use std, http, functional // GET request http("https://api.github.com/events", def(r) { - use "json" + use json events = jsondecode(r) println events[0] }) diff --git a/docs/modules.yml b/docs/modules.yml index d9fdbc77..c960cd75 100644 --- a/docs/modules.yml +++ b/docs/modules.yml @@ -50,14 +50,14 @@ desc_ru: возвращает значение `a`, если оно не пустое, иначе возвращается значение `b` since: 1.4.0 example: |- - use "std" + use std user = {"name": "", "lastname": "Doe"} name = default(user.name, "Unknown") lastname = default(user.lastname, "Unknown") println name + " " + lastname // Unknown Doe example_ru: |- - use "std" + use std user = {"name": "", "lastname": "Иванов"} name = default(user.name, "Имя неизвестно") @@ -68,12 +68,12 @@ desc: "prints values to console, separate them by space and puts newline at the end. Takes variable number of arguments" desc_ru: "выводит значения в консоль, разделяя их пробелом, а потом ставит перенос строки. Может принимать переменное значение аргументов" example: |- - use "std" + use std echo(1, "abc") // prints "1 abc" to console echo(1, 2, 3, 4, 5, "a", "b") // prints "1 2 3 4 5 a b" example_ru: |- - use "std" + use std echo(1, "abc") // выведет строку "1 abc" в консоль echo(1, 2, 3, 4, 5, "a", "b") // выведет строку "1 2 3 4 5 a b" в консоль" @@ -103,7 +103,7 @@ desc: "creates array with `size`.\n`newarray(x)` - creates 1D array, `newarray(x,y)` - creates 2D array" desc_ru: "оздаёт массив с размером size. Если указать 1 аргумент - создаётся одномерный массив, если 2 аргумента - двухмерный и т.д" example: |- - use "std" + use std println newarray(4) // [0, 0, 0, 0] println newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] @@ -136,7 +136,7 @@ `range(from, to)` - создаёт промежуток от `from` до `to` (не включительно) с шагом 1 `range(from, to, step)` - создаёт промежуток от `from` до `to` (не включительно) с шагом `step` example: |- - use "std" + use std println range(3) // [0, 1, 2] r = range(-5, 0) // [-5, -4, -3, -2, -1] @@ -175,7 +175,7 @@ desc: "splits string `str` with regular expression `regex` into array. `limit` parameter affects the length of resulting array" desc_ru: "разделяет строку `str` по шаблону `regex` и помещает элементы в массив. Если указан параметр `limit`, то будет произведено не более limit разбиений, соответственно размер результирующего массива будет ограничен этим значением limit" example: |- - use "std" + use std println split("a5b5c5d5e", "5") // ["a", "b", "c", "d", "e"] println split("a5b5c5d5e", "5", 3) // ["a", "b", "c5d5e"] @@ -194,7 +194,7 @@ desc_ru: обрезает начальные пробелы, сопровождаемые `marginPrefix` в каждой строке, и удаляет первую и последнюю строки, если они пустые since: 1.5.0 example: |- - use "std" + use std println " |123 @@ -205,7 +205,7 @@ desc: "returns string from `startIndex` to `endIndex` or to end of string if `endIndex` is not set" desc_ru: "обрезает строку `str`, начиная от символа после позиции `startIndex` и по `endIndex`. Если `endIndex` не указан, обрезается до конца строки" example: |- - use "std" + use std println substring("abcde", 1) // bcde println substring("abcde", 2, 4) // cd @@ -214,7 +214,7 @@ desc: calls an asynchronous function synchronously desc_ru: делает асинхронный вызов синхронным example: |- - use ["std", "http"] + use std, http url = "https://whatthecommit.com/index.txt" result = sync(def(ret) { @@ -231,7 +231,7 @@ `args` - 0 или более аргументов, которые необходимо передать в функцию func example: |- - use "std" + use std thread(def() { println "New Thread" @@ -252,7 +252,7 @@ desc: "converts char code to string" desc_ru: "переводит код символа в строку" example: |- - use "std" + use std println toChar(48) // "0" - name: toHexString @@ -276,14 +276,14 @@ desc: suppress any error in `unsafeFunction` and returns the result of the `catchFunction` if any error occurs desc_ru: подавляет любые ошибки в `unsafeFunction` и возрвщает результат функции `catchFunction`, если была ошибка example: |- - use "std" + use std println try(def() = "success") // success println try(def() = try + 2) // -1 println try(def() = try(), def(type, message) = sprintf("Error handled:\ntype: %s\nmessage: %s", type, message)) println try(def() = try(), 42) // 42 example_ru: |- - use "std" + use std println try(def() = "успех") // успех println try(def() = try + 2) // -1 @@ -356,7 +356,7 @@ desc: "converts value to number if possible" desc_ru: "преобразует значение к числу, если это возможно" example: |- - use "types" + use types println typeof(number("2.3")) // 1 (NUMBER) - @@ -370,7 +370,7 @@ desc: "converts value to string" desc_ru: "преобразует значение в строку" example: |- - use "types" + use types println typeof(string(1)) // 2 (STRING) - @@ -379,7 +379,7 @@ desc: "returns the type of value" desc_ru: "возвращает тип переданного значения" example: |- - use "types" + use types println typeof(1) // 1 (NUMBER) println typeof("text") // 2 (STRING) @@ -436,7 +436,7 @@ desc: "returns the ceiling of `x`" desc_ru: "округляет вещественное число в большую сторону" example: |- - use "math" + use math ceil(6.4) // 7 - @@ -470,7 +470,7 @@ desc: "returns floor of `x`" desc_ru: "округляет вещественное число в меньшую сторону" example: |- - use "math" + use math floor(3.8) // 3 - @@ -677,7 +677,7 @@ desc: formats date by given format and returns string desc_ru: форматирует дату в указанном формате и возвращает строку example: |- - use "date" + use date d = newDate(2016, 4, 8) println formatDate(d, newFormat("yyyy/MM/dd")) // "2016/05/08" @@ -687,7 +687,7 @@ desc: parses date from string by given pattern. Returns DateValue desc_ru: парсит дату из строки в указанном шаблоне. Возвращает DateValue example: |- - use "date" + use date println parseDate("2016/05/08", newFormat("yyyy/MM/dd")) - @@ -795,12 +795,12 @@ Возвращает дескриптор файла, который необходим для остальных функций. example: |- - use "files" + use files f1 = fopen("text.txt") // opens file text.txt for read in text mode f2 = fopen("E:/1.dat", "rbwb") // opens file 1.dat on drive E for binary read and write" example_ru: |- - use "files" + use files f1 = fopen("text.txt") // открывает файл text.txt для текстового чтения f2 = fopen("E:/1.dat", "rbwb") // открывает файл 1.dat на диске E для бинарного чтения и записи" @@ -835,12 +835,12 @@ desc: "returns array with filenames in given directory.\n\n f - directory descriptor" desc_ru: "возвращает массив с именами файлов в указанной директории.\n\n f - дескриптор папки" example: |- - use "files" + use files f1 = fopen("E:/examples", "") // opens directory examples for getting information list = listFiles(f1) // gets array with filenames in directory example_ru: |- - use "files" + use files f1 = fopen("E:/examples", "") // открыть папку examples для получения информации list = listFiles(f1) // получить массив с именами файлов в этой папке @@ -860,7 +860,7 @@ desc: "reads all bytes from file. Returns array with bytes" desc_ru: "чтение всех байт файла. Возвращает массив байт файла" example: |- - use ["std", "files"] + use std, files f1 = fopen("file.bin", "rb") array = readAllBytes(f1) @@ -881,7 +881,7 @@ desc: "reads `length` bytes of file `f` and stores to `array` starting from `offset+1` byte. Returns number of read bytes" desc_ru: "чтение заданного количества байт в массив `array`. Возвращает число прочитанных байт. \nЕсли offset и length не указаны, то читается количество байт равное длине массива. \nЕсли offset и length указаны, то читается length байт в массив array, начиная с `offset+1` байта" example: |- - use "files" + use files f1 = fopen("file.bin", "rb") // file.bin must contain more than 5000 bytes array = newarray(2048) @@ -889,7 +889,7 @@ readCount = readBytes(f1, array, 10) // reads 2048 bytes starting from 11 byte readCount = readBytes(f1, array, 20, 10) // reads 10 bytes, starting from 21 byte example_ru: |- - use "files" + use files f1 = fopen("file.bin", "rb") // file.bin должен иметь больше 5000 байтов array = newarray(2048) @@ -947,7 +947,7 @@ desc: "renames (or moves) file" desc_ru: "переименование (или перемещение) файла" example: |- - use "files" + use files f1 = fopen("C:/file1", "i") f2 = fopen("E:/file2", "i") @@ -1103,7 +1103,7 @@ - `content_length` - Content-Length - `content_type` - Content-Type example: |- - use "http" + use http http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { println "Added: " + v }) @@ -1112,7 +1112,7 @@ desc: "downloads content by url as bytes array" desc_ru: "получает содержимое по указанному адресу в виде массива байт" example: |- - use ["http", "files"] + use http, files bytes = download("http://url") f = fopen("file", "wb") writeBytes(f, bytes) @@ -1297,7 +1297,7 @@ desc: 'downloads file from `downloadUrl` to `filePath`' desc_ru: 'скачивает файл по адресу `downloadUrl` и сохраняет в `filePath`' example: |- - use ["downloader", "std"] + use downloader, std MBYTES = 1048576.0 // 1024*1024 url = "http://www.ovh.net/files/10Mb.dat" @@ -1343,7 +1343,7 @@ desc: "converts data to json string" desc_ru: "преобразует переданные данные в строку в формате json" example: |- - use "json" + use json print jsondecode("{\"key1\":1,\"key2\":[1,2,3],\"key3\":\"text\"}") // {key2=[1, 2, 3], key3=text, key1=1} - name: "jsonencode" @@ -1351,7 +1351,7 @@ desc: "converts string to data" desc_ru: "преобразует строку в формате json в данные" example: |- - use "json" + use json data = { "key1": 1, "key2": [1, 2, 3], @@ -1391,13 +1391,13 @@ `mapper` используется для задания имени конечного файла внутри архива, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. example: |- - use "zip" + use zip // Zip all files in directory zip("/tmp/dir", "/tmp/1.zip") // Zip .txt files zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") example_ru: |- - use "zip" + use zip // Архивировать все файлы в директории zip("/tmp/dir", "/tmp/1.zip") // Архивировать .txt файлы @@ -1418,7 +1418,7 @@ Если `input` — ассоциативный массив, то архивируются файлы и папки перечисленные в ключах, а именами внутри архива будут служить значения. Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. example: |- - use "zip" + use zip zipFiles("/tmp/dir/file.txt", "/tmp/1.zip") zipFiles(["/tmp/dir/file.txt", "/tmp/dir/readme.md"], "/tmp/2.zip") zipFiles({"/tmp/dir/file.txt" : "docs/1.md", "/tmp/dir/readme.md" : "docs/2.md"}, "/tmp/3.zip") @@ -1434,13 +1434,13 @@ `mapper` используется для задания имени конечного файла, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. example: |- - use "zip" + use zip // Unzip all files in directory unzip("/tmp/1.zip", "/tmp/dir") // Unzip .txt files unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") example_ru: |- - use "zip" + use zip // Распаковать все файлы в директории unzip("/tmp/1.zip", "/tmp/dir") // Распаковать .txt файлы @@ -1461,7 +1461,7 @@ Если `output` — ассоциативный массив, то разархивируются файлы перечисленные в ключах, а именами файлов будут служить значения. Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. example: |- - use "zip" + use zip unzipFiles("/tmp/1.zip", "file.txt") unzipFiles("/tmp/2.zip", ["file.txt", "readme.md"]) unzipFiles("/tmp/3.zip", {"docs/1.md" : "/tmp/dir/file.txt", "docs/2.md" : "/tmp/dir/readme.md"}) @@ -1487,7 +1487,7 @@ создаёт gzip архив с файлом `inputFile` и сохраняет в `outputFile`. Возвращает 1 если компрессия завершилась успешно, и -1 в противном случае. example: |- - use "gzip" + use gzip gzip("/tmp/readme.md", "/tmp/readme.md.gz") - name: gzipBytes @@ -1495,7 +1495,7 @@ desc: returns gzip-compressed input bytes. desc_ru: возвращает сжатый в gzip массив байт. example: |- - use "gzip" + use gzip bytes = gzipBytes([0, 119, 87, 80/* ... */]) - name: ungzip @@ -1507,7 +1507,7 @@ распаковывает gzip архив в файл `outputFile`. Возвращает 1 если операция завершилась успешно, и -1 в противном случае. example: |- - use "gzip" + use gzip gzip("/tmp/readme.md.gz", "/tmp/readme.md") - name: ungzipBytes @@ -1515,7 +1515,7 @@ desc: returns uncompressed bytes. desc_ru: возвращает распакованный gzip массив байт. example: |- - use "gzip" + use gzip bytes = ungzipBytes([0, 119, 87, 80/* ... */]) - name: functional scope: "both" @@ -1539,7 +1539,7 @@ desc: "combines functions" desc_ru: "комбинирует функции (композиция)" example: |- - use "functional" + use functional def f1() = 2 def f2(a) = a*2 @@ -1551,7 +1551,7 @@ f = def() = f3(f2(f1())) println f() // 1 example_ru: |- - use "functional" + use functional def f1() = 2 def f2(a) = a*2 @@ -1571,7 +1571,7 @@ desc: "filters array or object.\n\n`predicate` is a function which takes one argument for arrays or two arguments for objects" desc_ru: "фильтрует массив или объект и возвращает массив только с теми элементами, которые удовлетворяют предикату `predicate`.\n\n`predicate` - функция которая принимает один (для массивов) и два (для объектов) аргумента" example: |- - use "functional" + use functional nums = [1,2,3,4,5] print filter(nums, def(x) = x % 2 == 0) // [2, 4] @@ -1580,7 +1580,7 @@ desc: "converts each element of an array to other array" desc_ru: "преобразует каждый элемент массива в массив элементов" example: |- - use "functional" + use functional nums = [1,2,3,4] print flatmap(nums, def(x) { @@ -1594,7 +1594,7 @@ desc: "invokes function `consumer` for each element of array or map `data`\n\nIf `data` - массив, то в функции consumer необходим один параметр, если объект - два (ключ и значение)." desc_ru: "для каждого элемента в массиве или объекте `data` вызывает функцию `consumer`\n\nЕсли `data` - массив, то в функции `consumer` необходим один параметр, если объект - два (ключ и значение)." example: |- - use "functional" + use functional foreach([1, 2, 3], def(v) { print v }) foreach({"key": 1, "key2": "text"}, def(key, value) { @@ -1605,7 +1605,7 @@ desc: "converts elements of array or map. If `data` is array - `mapper` converts his elements, if `data` is object - you need to pass `keyMapper` - converts keys and `valueMapper` - converts values" desc_ru: "преобразует элементы массива или объекта.\n\nЕсли `data` - массив, то функция `mapper` преобразует значения, если объект - необходимо передать две функции: `keyMapper` - преобразует ключи и `valueMapper` - преобразует значения" example: |- - use "functional" + use functional nums = [3,4,5] print map(nums, def(x) = x * x) // [9, 16, 25] @@ -1614,7 +1614,7 @@ desc: "converts elements of an array or a map to one value, e.g. sum of elements or concatenation string. `accumulator` takes one argument for array and two arguments for object (key and value)." desc_ru: "преобразует элементы массива или объекта в одно значение, например сумма элементов или объединение в строку.\n\nЕсли `data` - массив, то в функции `accumulator` необходим один параметр, если объект - два (ключ и значение)" example: |- - use "functional" + use functional nums = [1,2,3,4,5] print reduce(nums, 0, def(x, y) = x + y) // 15 @@ -1623,7 +1623,7 @@ desc: "sorts elements of an array or an object by `function` result" desc_ru: "сортирует элементы массива по данным в функции `function`" example: |- - use "functional" + use functional data = [ {"k1": 2, "k2": "x"}, @@ -1748,11 +1748,11 @@ desc: "performs click with given mouse buttons" desc_ru: "осуществляет клик мышью с заданными клавишами" example: |- - use "robot" + use robot click(BUTTON3) // right mouse button click example_ru: |- - use "robot" + use robot click(BUTTON3) // клик правой кнопкой мыши - @@ -1767,7 +1767,7 @@ desc: "executes the process with parameters" desc_ru: "запускает процесс с параметрами\n\n Если функции переданы несколько аргументов, то они все передаются как параметры.\n Если функции передан только один параметр - массив, то его элементы передаются как параметры.\n Если функции передан только один параметр, то он служит единственным параметром." example: |- - use "robot" + use robot execProcess("mkdir", "Test") execProcess("mkdir Test") @@ -1878,7 +1878,7 @@ desc: "executes tests and returns information about it's results" desc_ru: "запускает тесты и возвращает информацию о них по завершению работы в виде строки" example: |- - use "ounit" + use ounit def testAdditionOnNumbers() { assertEquals(6, 0 + 1 + 2 + 3) @@ -2191,7 +2191,7 @@ Возвращает ImageFXValue. example: |- - use "canvasfx" + use canvasfx g = showcanvas() url = "http://lorempixel.com/640/480/nature" @@ -3608,12 +3608,12 @@ desc: "replaces input with the result of the given callback" desc_ru: "заменяет строку результатом заданной функции" example: |- - use "regex" + use regex in = "dog cat" pattern = regex("(\w+)\s(\w+)", Pattern.I) println pattern.replaceCallback(in, def(m) = m.group(2) + "" + m.group(1)) example_ru: |- - use "regex" + use regex in = "пёс кот" pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) println pattern.replaceCallback(in, def(m) = m.group(2) + "о" + m.group(1)) @@ -3656,13 +3656,13 @@ desc: "replaces input with the result of the given callback" desc_ru: "заменяет строку результатом заданной функции" example: |- - use "regex" + use regex in = "dog cat" pattern = regex("(\w+)\s(\w+)", Pattern.I) matcher = pattern.matcher(in) println matcher.replaceCallback(def(m) = m.group(2) + m.group(1)) example_ru: |- - use "regex" + use regex in = "пёс кот" pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) matcher = pattern.matcher(in) @@ -3872,7 +3872,7 @@ Возвращает BitmapValue. example: |- - use ["http", "canvas"] + use http, canvas g = showcanvas() url = "http://lorempixel.com/640/480/nature" @@ -3952,7 +3952,7 @@ desc: "shows canvas screen and returns GraphicsValue" desc_ru: "показывает экран канваса и возвращает GraphicsValue" example: |- - use "canvas" + use canvas g = showcanvas() types: - name: "BitmapValue" @@ -4599,7 +4599,7 @@ desc: '' desc_ru: '' example: |- - use ["std", "android", "forms"] + use std, android, forms img1 = assetBitmap("ownlang.png") img2 = img1 @@ -4679,7 +4679,7 @@ desc: 'creates ProgressBar' desc_ru: 'создаёт ProgressBar' example: |- - use ["android", "forms"] + use android, forms pb1 = newProgressBar(R.attr.progressBarStyleHorizontal) pb1.setMax(100) pb1.setProgress(10) @@ -6730,7 +6730,7 @@ desc_ru: |- подписывается на обработчик получения местоположения example: |- - use ["std", "gps"] + use std, gps provider = "gps" // or passive, network if exists // requestUpdates(provider, 0, 25, def(loc) = echo("location changed: ", loc)) diff --git a/examples.own b/examples.own index ee107ddc..d55a4297 100644 --- a/examples.own +++ b/examples.own @@ -5,7 +5,7 @@ * */ -use ["date", "files", "robot", "std"] +use date, files, robot, std DEBUG = true EXAMPLES_DIR = "examples" diff --git a/examples/basics/array.own b/examples/basics/array.own index 5b888fd7..2dedaa3c 100644 --- a/examples/basics/array.own +++ b/examples/basics/array.own @@ -3,7 +3,7 @@ println arr1[0] println arr1[1] println arr1 -use "std" +use std arr2 = newarray(5) arr2[2] = 9 arr2 = arr2 :: 4 diff --git a/examples/basics/bitwise_operators.own b/examples/basics/bitwise_operators.own index 57cc487a..9ef60ea6 100644 --- a/examples/basics/bitwise_operators.own +++ b/examples/basics/bitwise_operators.own @@ -1,4 +1,4 @@ -use "std" +use std echo(#ABCDEF - #12345) echo(-8 << 2) diff --git a/examples/basics/classes.own b/examples/basics/classes.own index b46c12f2..23e15da1 100644 --- a/examples/basics/classes.own +++ b/examples/basics/classes.own @@ -1,4 +1,4 @@ -use ["std"] +use std class Point { def Point(x = 0, y = 0) { diff --git a/examples/basics/destructuring_assignment.own b/examples/basics/destructuring_assignment.own index 11b07c48..f6d2e71d 100644 --- a/examples/basics/destructuring_assignment.own +++ b/examples/basics/destructuring_assignment.own @@ -1,4 +1,4 @@ -use "std" +use std println "Destructuring assignment" arr = ["a", "b", "c"] diff --git a/examples/basics/loops.own b/examples/basics/loops.own index 707f473a..327c69e2 100644 --- a/examples/basics/loops.own +++ b/examples/basics/loops.own @@ -28,14 +28,14 @@ arr = [1, 2, 3, 4, 5] for a : arr print a -use "std" +use std println "\n\nForeach loop on map" object = {"key1": "value1", "key2": 100, "arr": [0, 1]} for key, value : object echo(key, ":", value) -use "functional" +use functional // Functional loop println "\n\nFunctional loop on array" diff --git a/examples/basics/operator_overloading.own b/examples/basics/operator_overloading.own index 2ea1ac00..a99d367a 100644 --- a/examples/basics/operator_overloading.own +++ b/examples/basics/operator_overloading.own @@ -1,4 +1,4 @@ -use ["std", "types", "math"] +use std, types, math println "Operator overloading" def `::`(v1, v2) = string(v1) + string(v2) diff --git a/examples/basics/pattern_matching.own b/examples/basics/pattern_matching.own index cc9b118a..85f9e9d8 100644 --- a/examples/basics/pattern_matching.own +++ b/examples/basics/pattern_matching.own @@ -1,5 +1,4 @@ -use "std" -use "types" +use std, types v = rand(10) println match v { diff --git a/examples/basics/thread.own b/examples/basics/thread.own index 5f105bcc..c1ce3901 100644 --- a/examples/basics/thread.own +++ b/examples/basics/thread.own @@ -1,4 +1,4 @@ -use "std" +use std def thread1() { i = 0 diff --git a/examples/basics/types.own b/examples/basics/types.own index e3a89d36..bf058642 100644 --- a/examples/basics/types.own +++ b/examples/basics/types.own @@ -1,5 +1,4 @@ -use "std" -use "types" +use std, types println typeof(1) println typeof("1") diff --git a/examples/canvas/1.own b/examples/canvas/1.own index d3593f2c..47ee0667 100644 --- a/examples/canvas/1.own +++ b/examples/canvas/1.own @@ -1,4 +1,4 @@ -use "canvas" +use canvas w = 800 h = 600 window("canvas example", w, h); diff --git a/examples/canvas/2.own b/examples/canvas/2.own index e9baaa84..1f1dadfc 100644 --- a/examples/canvas/2.own +++ b/examples/canvas/2.own @@ -1,5 +1,4 @@ -use "std" -use "canvas" +use std, canvas w = 800 h = 600 window("canvas example 2", w, h); diff --git a/examples/canvas/animate_line.own b/examples/canvas/animate_line.own index 758d5b61..3eb29ad2 100644 --- a/examples/canvas/animate_line.own +++ b/examples/canvas/animate_line.own @@ -1,5 +1,4 @@ -use "canvas" -use "std" +use canvas, std w = 800 h = 600 window("Animate line", w, h) diff --git a/examples/canvas/animate_line_thread.own b/examples/canvas/animate_line_thread.own index f6a28cbd..41fda76f 100644 --- a/examples/canvas/animate_line_thread.own +++ b/examples/canvas/animate_line_thread.own @@ -1,5 +1,4 @@ -use "canvas" -use "std" +use canvas, std w = 800 h = 600 window("Animate line with thread", w, h) diff --git a/examples/canvas/control_point.own b/examples/canvas/control_point.own index d2d8161e..ee6c1a4e 100644 --- a/examples/canvas/control_point.own +++ b/examples/canvas/control_point.own @@ -1,5 +1,4 @@ -use "canvas" -use "std" +use canvas, std w = 640 h = 480 window("Управление точкой", w, h) diff --git a/examples/canvas/fractal_polygon.own b/examples/canvas/fractal_polygon.own index 8f2cce10..2b52b998 100644 --- a/examples/canvas/fractal_polygon.own +++ b/examples/canvas/fractal_polygon.own @@ -1,6 +1,4 @@ -use "canvas" -use "math" -use "std" +use canvas, math, std msg = "" NUM_POINTS = 0 diff --git a/examples/canvas/fractal_rect.own b/examples/canvas/fractal_rect.own index fcb8854b..b78fd486 100644 --- a/examples/canvas/fractal_rect.own +++ b/examples/canvas/fractal_rect.own @@ -1,4 +1,4 @@ -use "canvas" +use canvas w = 800 h = 600 window("Fractal rectangle demo", w, h) diff --git a/examples/canvas/fx_basic_shapes.own b/examples/canvas/fx_basic_shapes.own index d0f11331..25568566 100644 --- a/examples/canvas/fx_basic_shapes.own +++ b/examples/canvas/fx_basic_shapes.own @@ -1,4 +1,4 @@ -use "canvasfx" +use canvasfx // https://docs.oracle.com/javafx/2/canvas/jfxpub-canvas.htm diff --git a/examples/canvas/fx_event_handlers.own b/examples/canvas/fx_event_handlers.own index 02d722e5..d43b65b1 100644 --- a/examples/canvas/fx_event_handlers.own +++ b/examples/canvas/fx_event_handlers.own @@ -1,5 +1,4 @@ -use "canvasfx" -use "std" +use canvasfx, std w = 800 h = 600 g = window("JavaFX Event handler example", w, h) diff --git a/examples/canvas/fx_global_alpha.own b/examples/canvas/fx_global_alpha.own index 6909c63f..d3af8023 100644 --- a/examples/canvas/fx_global_alpha.own +++ b/examples/canvas/fx_global_alpha.own @@ -1,4 +1,4 @@ -use "canvasfx" +use canvasfx steps = 20 size = 25 diff --git a/examples/canvas/fx_image.own b/examples/canvas/fx_image.own index 3b85381b..efb4e310 100644 --- a/examples/canvas/fx_image.own +++ b/examples/canvas/fx_image.own @@ -1,4 +1,4 @@ -use "canvasfx" +use canvasfx g = window("JavaFX Image demo", 400, 200) img = createImage("https://picsum.photos/400/200/") diff --git a/examples/canvas/fx_image_negate.own b/examples/canvas/fx_image_negate.own index a1fe3f70..fdcaddca 100644 --- a/examples/canvas/fx_image_negate.own +++ b/examples/canvas/fx_image_negate.own @@ -1,5 +1,4 @@ -use "std" -use "canvasfx" +use std, canvasfx graphics = window("JavaFX Image negation demo", 400, 400) imgSource = createImage("https://picsum.photos/400/200/") diff --git a/examples/canvas/fx_koch_snowflake.own b/examples/canvas/fx_koch_snowflake.own index 7e96b6b3..6e3b90f5 100644 --- a/examples/canvas/fx_koch_snowflake.own +++ b/examples/canvas/fx_koch_snowflake.own @@ -1,6 +1,4 @@ -use "canvasfx" -use "math" -use "functional" +use canvasfx, math, functional // https://github.com/SeTSeR/KochSnowflake diff --git a/examples/canvas/fx_rotation.own b/examples/canvas/fx_rotation.own index 89d28ff9..36e99479 100644 --- a/examples/canvas/fx_rotation.own +++ b/examples/canvas/fx_rotation.own @@ -1,5 +1,4 @@ -use "canvasfx" -use "std" +use canvasfx, std // http://www.developer.com/java/data/using-graphics-in-javafx.html diff --git a/examples/console/colors.own b/examples/console/colors.own index b530ad89..c1c7611d 100644 --- a/examples/console/colors.own +++ b/examples/console/colors.own @@ -1,4 +1,4 @@ -use "std" +use std // header print " " * 4 diff --git a/examples/database/hsqldb.own b/examples/database/hsqldb.own index 47fb10aa..62092c5f 100644 --- a/examples/database/hsqldb.own +++ b/examples/database/hsqldb.own @@ -1,4 +1,4 @@ -use ["std", "jdbc"] +use std, jdbc connection = getConnection("jdbc:hsqldb:file:hsql.db", "", "", "org.hsqldb.jdbcDriver") statement = connection.createStatement() diff --git a/examples/database/sqlite.own b/examples/database/sqlite.own index 670d818e..d9ec70d0 100644 --- a/examples/database/sqlite.own +++ b/examples/database/sqlite.own @@ -1,4 +1,4 @@ -use ["std", "jdbc"] +use std, jdbc // Example from https://github.com/xerial/sqlite-jdbc diff --git a/examples/formats/gzip.own b/examples/formats/gzip.own index 08b6c140..ecd6c18d 100644 --- a/examples/formats/gzip.own +++ b/examples/formats/gzip.own @@ -1,4 +1,4 @@ -use ["std", "gzip"] +use std, gzip // println "Gzip single file" // gzip("absolute path to file", "example.gz") diff --git a/examples/formats/json.own b/examples/formats/json.own index 49a76e48..9549c78e 100644 --- a/examples/formats/json.own +++ b/examples/formats/json.own @@ -1,4 +1,4 @@ -use "json" +use json data = { "name": "Json Example", diff --git a/examples/formats/yaml.own b/examples/formats/yaml.own index 0778ad7c..72935192 100644 --- a/examples/formats/yaml.own +++ b/examples/formats/yaml.own @@ -1,4 +1,4 @@ -use "yaml" +use yaml data = { "name": "Yaml Example", diff --git a/examples/formats/zip.own b/examples/formats/zip.own index b3e3db1a..e1eedb50 100644 --- a/examples/formats/zip.own +++ b/examples/formats/zip.own @@ -1,4 +1,4 @@ -use "zip" +use zip // println "Zip single file" // zip("absolute path to file", "example.zip") diff --git a/examples/forms/basic.own b/examples/forms/basic.own index 59eb3ee9..1ef561ee 100644 --- a/examples/forms/basic.own +++ b/examples/forms/basic.own @@ -1,4 +1,4 @@ -use "forms" +use forms window = newWindow("Basic form example") window.add("Hello, world") diff --git a/examples/forms/button.own b/examples/forms/button.own index d30f4f92..e0c599c1 100644 --- a/examples/forms/button.own +++ b/examples/forms/button.own @@ -1,4 +1,4 @@ -use "forms" +use forms button = newButton("Click me") button.onClick(def() { diff --git a/examples/forms/complicatedForm.own b/examples/forms/complicatedForm.own index fce462b4..49237838 100644 --- a/examples/forms/complicatedForm.own +++ b/examples/forms/complicatedForm.own @@ -1,5 +1,4 @@ -use "std" -use "forms" +use std, forms actionsPanel = newPanel() actionsPanel.setLayout(boxLayout(actionsPanel, BoxLayout.PAGE_AXIS)) diff --git a/examples/forms/look_and_feel.own b/examples/forms/look_and_feel.own index d7435d65..2a17dfb4 100644 --- a/examples/forms/look_and_feel.own +++ b/examples/forms/look_and_feel.own @@ -1,4 +1,4 @@ -use ["java", "forms"] +use java, forms UIManager = newClass("javax.swing.UIManager") // UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel") diff --git a/examples/forms/panel.own b/examples/forms/panel.own index 3f7de8d8..15abf403 100644 --- a/examples/forms/panel.own +++ b/examples/forms/panel.own @@ -1,4 +1,4 @@ -use "forms" +use forms // Create Panel with BoxLayout panel = newPanel() diff --git a/examples/forms/progressbar.own b/examples/forms/progressbar.own index 261587fd..59941bd3 100644 --- a/examples/forms/progressbar.own +++ b/examples/forms/progressbar.own @@ -1,4 +1,4 @@ -use "forms" +use forms label = newLabel("Current value: 50") progressBar = newProgressBar() diff --git a/examples/forms/samobot_chat.own b/examples/forms/samobot_chat.own index 2e4515a6..7107c2fc 100644 --- a/examples/forms/samobot_chat.own +++ b/examples/forms/samobot_chat.own @@ -1,4 +1,4 @@ -use ["std", "http", "forms"] +use std, http, forms chatHistory = newLabel("Чат с самоботом
") messageField = newTextField() diff --git a/examples/forms/textarea.own b/examples/forms/textarea.own index f659ff62..9b5e9526 100644 --- a/examples/forms/textarea.own +++ b/examples/forms/textarea.own @@ -1,4 +1,4 @@ -use ["std", "forms", "functional"] +use std, forms, functional text = map(range(1, 16), def(x) = "line " + x).joinToString("\n") label = newLabel() diff --git a/examples/forms/textfield.own b/examples/forms/textfield.own index 33fd1c64..8cc36919 100644 --- a/examples/forms/textfield.own +++ b/examples/forms/textfield.own @@ -1,5 +1,4 @@ -use "std" -use "forms" +use std, forms textField = newTextField("Some text") diff --git a/examples/forms/windowlistener.own b/examples/forms/windowlistener.own index ccb15f67..e22c8ea7 100644 --- a/examples/forms/windowlistener.own +++ b/examples/forms/windowlistener.own @@ -1,4 +1,4 @@ -use "forms" +use forms textArea = newTextArea("Window logs:") diff --git a/examples/functions/basics.own b/examples/functions/basics.own index 63fa1cb2..9448161c 100644 --- a/examples/functions/basics.own +++ b/examples/functions/basics.own @@ -1,6 +1,4 @@ -use "std" -use "math" -use "functional" +use std, math, functional add = def(a,b) = a + b sub = def(a,b) = a - b diff --git a/examples/functions/calculator.own b/examples/functions/calculator.own index 9e6355d2..328674cf 100644 --- a/examples/functions/calculator.own +++ b/examples/functions/calculator.own @@ -1,6 +1,5 @@ // Simple parser example -use "std" -use "types" +use std, types operations = { "+" : def(a,b) = a+b, diff --git a/examples/functions/chain.own b/examples/functions/chain.own index 4d7e5c68..d337d8bc 100644 --- a/examples/functions/chain.own +++ b/examples/functions/chain.own @@ -1,5 +1,4 @@ -use "std" -use "functional" +use std, functional data = [1,2,3,4,5,6,7,8,9] chain(data, diff --git a/examples/functions/filter_map.own b/examples/functions/filter_map.own index f8daa034..0aedaf22 100644 --- a/examples/functions/filter_map.own +++ b/examples/functions/filter_map.own @@ -1,5 +1,4 @@ -use "std" -use "functional" +use std, functional nums = [1,2,3,4,5,6,7,8,9,10] nums = filter(nums, def(x) = x % 2 == 0) diff --git a/examples/functions/flatmap.own b/examples/functions/flatmap.own index b3752063..0a918990 100644 --- a/examples/functions/flatmap.own +++ b/examples/functions/flatmap.own @@ -1,5 +1,4 @@ -use "std" -use "functional" +use std, functional nums = [[1, 2], [3], [], [4, 5]] nums = flatmap(nums, IDENTITY) diff --git a/examples/functions/reduce.own b/examples/functions/reduce.own index 08c6466a..2e98c661 100644 --- a/examples/functions/reduce.own +++ b/examples/functions/reduce.own @@ -1,4 +1,4 @@ -use "functional" +use functional nums = [1,2,3,4,5] println "Sum: " + reduce(nums, 0, def(x, y) = x + y) \ No newline at end of file diff --git a/examples/functions/sortby.own b/examples/functions/sortby.own index 412eb2fa..e3eb2ea8 100644 --- a/examples/functions/sortby.own +++ b/examples/functions/sortby.own @@ -1,5 +1,4 @@ -use "std" -use "functional" +use std, functional nums = [1,2,3,4,5] println "Sort numbers in descending order" diff --git a/examples/functions/stream.own b/examples/functions/stream.own index d654635e..829238d6 100644 --- a/examples/functions/stream.own +++ b/examples/functions/stream.own @@ -1,4 +1,4 @@ -use ["std", "functional"] +use std, functional println "x, square(x), cube(x) for even numbers" data = [1,2,3,4,5,6,7,8,9] diff --git a/examples/game/agar.own b/examples/game/agar.own index 5998989b..1d67f8d3 100644 --- a/examples/game/agar.own +++ b/examples/game/agar.own @@ -1,6 +1,4 @@ -use "canvas" -use "math" -use "std" +use canvas, math, std w = 800 h = 600 w2 = w/2 h2 = h/2 diff --git a/examples/game/minesweeper.own b/examples/game/minesweeper.own index 596701f4..9d32e987 100644 --- a/examples/game/minesweeper.own +++ b/examples/game/minesweeper.own @@ -1,7 +1,4 @@ -use "std" -use "math" -use "types" -use "canvasfx" +use std, math, types, canvasfx // Constants CELL_NONE = -100 diff --git a/examples/game/pipes-online/pipes_online.own b/examples/game/pipes-online/pipes_online.own index fc5548ab..48421d97 100644 --- a/examples/game/pipes-online/pipes_online.own +++ b/examples/game/pipes-online/pipes_online.own @@ -1,6 +1,4 @@ -use "std" -use "canvas" -use "socket" +use std, canvas, socket /// --- PIPES CELL --- CELL_START = 0 diff --git a/examples/game/pipes.own b/examples/game/pipes.own index ff4d2d5f..dc44a839 100644 --- a/examples/game/pipes.own +++ b/examples/game/pipes.own @@ -1,5 +1,4 @@ -use "std" -use "canvas" +use std, canvas /// --- PIPES CELL --- CELL_START = 0 diff --git a/examples/game/pipes_online.own b/examples/game/pipes_online.own index 396744e6..d6d61783 100644 --- a/examples/game/pipes_online.own +++ b/examples/game/pipes_online.own @@ -1,6 +1,4 @@ -use "std" -use "canvas" -use "socket" +use std, canvas, socket /// --- PIPES CELL --- CELL_START = 0 diff --git a/examples/java/system_info.own b/examples/java/system_info.own index d8fc3a0c..868ba02d 100644 --- a/examples/java/system_info.own +++ b/examples/java/system_info.own @@ -1,4 +1,4 @@ -use "java" +use java System = newClass("java.lang.System") println "OS name: " + System.getProperty("os.name") println "OS version: " + System.getProperty("os.version") diff --git a/examples/network/demo.own b/examples/network/demo.own index 448ecb09..6376da9d 100644 --- a/examples/network/demo.own +++ b/examples/network/demo.own @@ -1,5 +1,4 @@ -use "std" -use "http" +use std, http http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { println "Added: " + v diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own index a1c51ed3..c07b6515 100644 --- a/examples/network/github_timeline.own +++ b/examples/network/github_timeline.own @@ -1,4 +1,4 @@ -use ["std", "http", "json", "functional", "date"] +use std, http, json, functional, date header = "* Prints current GitHub timeline *" println "*" * header.length diff --git a/examples/network/okhttp_imgur_upload.own b/examples/network/okhttp_imgur_upload.own index d9ea0a4a..69331317 100644 --- a/examples/network/okhttp_imgur_upload.own +++ b/examples/network/okhttp_imgur_upload.own @@ -1,4 +1,4 @@ -use ["std", "okhttp"] +use std, okhttp // https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/PostMultipart.java diff --git a/examples/network/okhttp_telegram_sendvoice.own b/examples/network/okhttp_telegram_sendvoice.own index 1070ab76..1e8f17d2 100644 --- a/examples/network/okhttp_telegram_sendvoice.own +++ b/examples/network/okhttp_telegram_sendvoice.own @@ -1,4 +1,4 @@ -use ["std", "okhttp"] +use std, okhttp TOKEN = "your bot token" diff --git a/examples/network/okhttp_websocket.own b/examples/network/okhttp_websocket.own index 645260f0..99a1e7ed 100644 --- a/examples/network/okhttp_websocket.own +++ b/examples/network/okhttp_websocket.own @@ -1,4 +1,4 @@ -use ["std", "okhttp"] +use std, okhttp // https://github.com/square/okhttp/blob/b21ed68c08c2a5c1eb0bbe93a6f720d1aa2820da/samples/guide/src/main/java/okhttp3/recipes/WebSocketEcho.java diff --git a/examples/network/telegram_api.own b/examples/network/telegram_api.own index 6619d28a..43884f85 100644 --- a/examples/network/telegram_api.own +++ b/examples/network/telegram_api.own @@ -1,7 +1,4 @@ -use "std" -use "http" -use "json" -use "functional" +use std, http, json, functional // Telegram API example diff --git a/examples/network/twitch_tools.own b/examples/network/twitch_tools.own index 71b87b92..40bddebb 100644 --- a/examples/network/twitch_tools.own +++ b/examples/network/twitch_tools.own @@ -1,11 +1,5 @@ // Twitch Tools -use "std" -use "math" -use "http" -use "json" -use "date" -use "types" -use "functional" +use std, math, http, json, date, types, functional match ARGS { case (): usage() diff --git a/examples/robot/paint_lines.own b/examples/robot/paint_lines.own index 11c7ef3b..6927a3ff 100644 --- a/examples/robot/paint_lines.own +++ b/examples/robot/paint_lines.own @@ -1,4 +1,4 @@ -use "robot" +use robot pause = 5 xstep = 50 ystep = 5 diff --git a/examples/versions/whatsnew_1.5.0.own b/examples/versions/whatsnew_1.5.0.own index 26762cbf..0ec2c743 100644 --- a/examples/versions/whatsnew_1.5.0.own +++ b/examples/versions/whatsnew_1.5.0.own @@ -1,4 +1,4 @@ -use ["std", "functional", "gzip", "json", "java"] +use std, functional, gzip, json, java title("Added std::getBytes, std::stringFromBytes") arr = [119, 111, 114, 108, 100] diff --git a/ownlang-parser/src/test/resources/benchmarks/useStatement.own b/ownlang-parser/src/test/resources/benchmarks/useStatement.own index 0e72fece..4756c8d0 100644 --- a/ownlang-parser/src/test/resources/benchmarks/useStatement.own +++ b/ownlang-parser/src/test/resources/benchmarks/useStatement.own @@ -1,5 +1,5 @@ for i = 0, i < 50, i++ { - use "std" - use "files" - use ["math", "functional"] + use std + use files + use math, functional } \ No newline at end of file diff --git a/ownlang-parser/src/test/resources/expressions/foreachValue.own b/ownlang-parser/src/test/resources/expressions/foreachValue.own index 6f1b8a43..f94d1ec0 100644 --- a/ownlang-parser/src/test/resources/expressions/foreachValue.own +++ b/ownlang-parser/src/test/resources/expressions/foreachValue.own @@ -1,4 +1,4 @@ -use "std" +use std def testArrayIterate() { sum = 0 diff --git a/ownlang-parser/src/test/resources/expressions/include.own b/ownlang-parser/src/test/resources/expressions/include.own index 41eeb1eb..1ee041d6 100644 --- a/ownlang-parser/src/test/resources/expressions/include.own +++ b/ownlang-parser/src/test/resources/expressions/include.own @@ -1,4 +1,4 @@ -use "std" +use std def testIncludeClass() { include "src/test/resources/expressions/includeClass.own.txt" diff --git a/ownlang-parser/src/test/resources/expressions/matchExpression.own b/ownlang-parser/src/test/resources/expressions/matchExpression.own index 5a5a1f3e..fbe892e5 100644 --- a/ownlang-parser/src/test/resources/expressions/matchExpression.own +++ b/ownlang-parser/src/test/resources/expressions/matchExpression.own @@ -1,4 +1,4 @@ -use "types" +use types def testMatchValue() { value = 20 diff --git a/ownlang-parser/src/test/resources/modules/base64/base64.own b/ownlang-parser/src/test/resources/modules/base64/base64.own index b9e11db5..f894c602 100644 --- a/ownlang-parser/src/test/resources/modules/base64/base64.own +++ b/ownlang-parser/src/test/resources/modules/base64/base64.own @@ -1,4 +1,4 @@ -use ["base64", "functional", "types"] +use base64, functional, types base64Example = [0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x20, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65] base64Example_enc = [0x51, 0x6D, 0x46, 0x7A, 0x5A, 0x54, 0x59, 0x30, diff --git a/ownlang-parser/src/test/resources/modules/date/compareDates.own b/ownlang-parser/src/test/resources/modules/date/compareDates.own index 7db9f5db..be3a8a96 100644 --- a/ownlang-parser/src/test/resources/modules/date/compareDates.own +++ b/ownlang-parser/src/test/resources/modules/date/compareDates.own @@ -1,4 +1,4 @@ -use "date" +use date def testCompareDates() { assertTrue(newDate(2016, 04, 10) > newDate(2015, 03, 11)) diff --git a/ownlang-parser/src/test/resources/modules/date/dateFormat.own b/ownlang-parser/src/test/resources/modules/date/dateFormat.own index 6437f7a1..fb1cc3fc 100644 --- a/ownlang-parser/src/test/resources/modules/date/dateFormat.own +++ b/ownlang-parser/src/test/resources/modules/date/dateFormat.own @@ -1,4 +1,4 @@ -use "date" +use date def testDateFormat() { d = formatDate(newDate(2016, 04, 10), newFormat("yyyy/MM/dd HH:mm:ss")) diff --git a/ownlang-parser/src/test/resources/modules/date/dateParse.own b/ownlang-parser/src/test/resources/modules/date/dateParse.own index 361cb9d1..e2ce6de4 100644 --- a/ownlang-parser/src/test/resources/modules/date/dateParse.own +++ b/ownlang-parser/src/test/resources/modules/date/dateParse.own @@ -1,4 +1,4 @@ -use "date" +use date def testDateParse() { d = parseDate("2016/05/10", newFormat("yyyy/MM/dd")) diff --git a/ownlang-parser/src/test/resources/modules/date/newDate.own b/ownlang-parser/src/test/resources/modules/date/newDate.own index f33a3527..8cf3e04d 100644 --- a/ownlang-parser/src/test/resources/modules/date/newDate.own +++ b/ownlang-parser/src/test/resources/modules/date/newDate.own @@ -1,4 +1,4 @@ -use "date" +use date def testNewDate() { d = newDate(2016, 04, 10) diff --git a/ownlang-parser/src/test/resources/modules/files/files.own b/ownlang-parser/src/test/resources/modules/files/files.own index 4f9c7e35..15fb49a3 100644 --- a/ownlang-parser/src/test/resources/modules/files/files.own +++ b/ownlang-parser/src/test/resources/modules/files/files.own @@ -1,4 +1,4 @@ -use ["files", "types"] +use files, types def testFiles() { // writeLong diff --git a/ownlang-parser/src/test/resources/modules/functional/chain.own b/ownlang-parser/src/test/resources/modules/functional/chain.own index 19692093..1900d721 100644 --- a/ownlang-parser/src/test/resources/modules/functional/chain.own +++ b/ownlang-parser/src/test/resources/modules/functional/chain.own @@ -1,4 +1,4 @@ -use "functional" +use functional def testFunctionalChain() { data = [1,2,3,4,5,6,7] diff --git a/ownlang-parser/src/test/resources/modules/functional/foreach.own b/ownlang-parser/src/test/resources/modules/functional/foreach.own index 6d452f4c..ff0a438a 100644 --- a/ownlang-parser/src/test/resources/modules/functional/foreach.own +++ b/ownlang-parser/src/test/resources/modules/functional/foreach.own @@ -1,4 +1,4 @@ -use ["std", "functional"] +use std, functional def testArrayForeach1Arg() { sum = 0 diff --git a/ownlang-parser/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own index b9478b0f..9a4b4053 100644 --- a/ownlang-parser/src/test/resources/modules/functional/stream.own +++ b/ownlang-parser/src/test/resources/modules/functional/stream.own @@ -1,4 +1,4 @@ -use ["std", "functional", "math"] +use std, functional, math def testStream() { data = [1,2,3,4,5,6,7] diff --git a/ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own b/ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own index 37e57a03..9e2fd13e 100644 --- a/ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own +++ b/ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own @@ -1,4 +1,4 @@ -use ["std", "gzip"] +use std, gzip def testGzipText() { text = trim(" diff --git a/ownlang-parser/src/test/resources/modules/java/classes.own b/ownlang-parser/src/test/resources/modules/java/classes.own index 0aaecbfa..45d33d38 100644 --- a/ownlang-parser/src/test/resources/modules/java/classes.own +++ b/ownlang-parser/src/test/resources/modules/java/classes.own @@ -1,4 +1,4 @@ -use ["std", "java"] +use std, java def testCheckNull() { assertTrue(isNull(null)) diff --git a/ownlang-parser/src/test/resources/modules/regex/match.own b/ownlang-parser/src/test/resources/modules/regex/match.own index 24954fe5..deaa9dba 100644 --- a/ownlang-parser/src/test/resources/modules/regex/match.own +++ b/ownlang-parser/src/test/resources/modules/regex/match.own @@ -1,4 +1,4 @@ -use ["regex", "types"] +use regex, types def testMatchGitUrl() { pattern = Pattern.compile("https?://((git(hu|la)b\.com)|bitbucket\.org)/?") diff --git a/ownlang-parser/src/test/resources/modules/regex/replaceCallback.own b/ownlang-parser/src/test/resources/modules/regex/replaceCallback.own index cdfb5255..234ac4a3 100644 --- a/ownlang-parser/src/test/resources/modules/regex/replaceCallback.own +++ b/ownlang-parser/src/test/resources/modules/regex/replaceCallback.own @@ -1,4 +1,4 @@ -use ["regex", "types"] +use regex, types def testReplaceCallback() { in = "[1-2-3-4]" diff --git a/ownlang-parser/src/test/resources/modules/std/arraySplice.own b/ownlang-parser/src/test/resources/modules/std/arraySplice.own index 0ec3d500..ec47aaf0 100644 --- a/ownlang-parser/src/test/resources/modules/std/arraySplice.own +++ b/ownlang-parser/src/test/resources/modules/std/arraySplice.own @@ -1,4 +1,4 @@ -use "std" +use std def testArraySpliceFromStart() { arr = [1,2,3,4,5] diff --git a/ownlang-parser/src/test/resources/modules/std/default.own b/ownlang-parser/src/test/resources/modules/std/default.own index 375aa333..2f9c67e8 100644 --- a/ownlang-parser/src/test/resources/modules/std/default.own +++ b/ownlang-parser/src/test/resources/modules/std/default.own @@ -1,4 +1,4 @@ -use "std" +use std def testDefaultNumber() { assertEquals(123, default(0, 123)) @@ -9,7 +9,7 @@ def testDefaultString() { } def testDefaultNull() { - use "java" + use java assertEquals("not null", default(null, "not null")) } diff --git a/ownlang-parser/src/test/resources/modules/std/getBytes.own b/ownlang-parser/src/test/resources/modules/std/getBytes.own index 48ba8fd8..dbc93f97 100644 --- a/ownlang-parser/src/test/resources/modules/std/getBytes.own +++ b/ownlang-parser/src/test/resources/modules/std/getBytes.own @@ -1,4 +1,4 @@ -use "std" +use std def testGetBytes() { assertEquals([111, 119, 110, 108, 97, 110, 103], getBytes("ownlang")) diff --git a/ownlang-parser/src/test/resources/modules/std/indexOf.own b/ownlang-parser/src/test/resources/modules/std/indexOf.own index 176a40bc..0fcd3940 100644 --- a/ownlang-parser/src/test/resources/modules/std/indexOf.own +++ b/ownlang-parser/src/test/resources/modules/std/indexOf.own @@ -1,4 +1,4 @@ -use "std" +use std def testIndexOf() { assertEquals(3, indexOf("123/456/789", "/")) diff --git a/ownlang-parser/src/test/resources/modules/std/lastIndexOf.own b/ownlang-parser/src/test/resources/modules/std/lastIndexOf.own index fb194084..75d9ddaf 100644 --- a/ownlang-parser/src/test/resources/modules/std/lastIndexOf.own +++ b/ownlang-parser/src/test/resources/modules/std/lastIndexOf.own @@ -1,4 +1,4 @@ -use "std" +use std def testLastIndexOf() { assertEquals(8, lastIndexOf("/123/456/789", "/")) diff --git a/ownlang-parser/src/test/resources/modules/std/parseInt.own b/ownlang-parser/src/test/resources/modules/std/parseInt.own index 7aa4a299..dd1cf0db 100644 --- a/ownlang-parser/src/test/resources/modules/std/parseInt.own +++ b/ownlang-parser/src/test/resources/modules/std/parseInt.own @@ -1,4 +1,4 @@ -use "std" +use std def testParseInt() { assertEquals(141, parseInt("141")) diff --git a/ownlang-parser/src/test/resources/modules/std/parseLong.own b/ownlang-parser/src/test/resources/modules/std/parseLong.own index 77d426d5..30ece4ca 100644 --- a/ownlang-parser/src/test/resources/modules/std/parseLong.own +++ b/ownlang-parser/src/test/resources/modules/std/parseLong.own @@ -1,4 +1,4 @@ -use "std" +use std def testParseInt() { assertEquals(12345654321, parseLong("12345654321")) diff --git a/ownlang-parser/src/test/resources/modules/std/range.own b/ownlang-parser/src/test/resources/modules/std/range.own index 85a60eda..cec28b84 100644 --- a/ownlang-parser/src/test/resources/modules/std/range.own +++ b/ownlang-parser/src/test/resources/modules/std/range.own @@ -1,4 +1,4 @@ -use ["std", "types", "functional"] +use std, types, functional def testRangeParams() { x = range(10) diff --git a/ownlang-parser/src/test/resources/modules/std/stringFromBytes.own b/ownlang-parser/src/test/resources/modules/std/stringFromBytes.own index 6964c31b..3489bf1c 100644 --- a/ownlang-parser/src/test/resources/modules/std/stringFromBytes.own +++ b/ownlang-parser/src/test/resources/modules/std/stringFromBytes.own @@ -1,4 +1,4 @@ -use "std" +use std def testStringFromBytes() { assertEquals("ownlang", stringFromBytes([111, 119, 110, 108, 97, 110, 103])) diff --git a/ownlang-parser/src/test/resources/modules/std/stripMargin.own b/ownlang-parser/src/test/resources/modules/std/stripMargin.own index e5ba625c..6d743614 100644 --- a/ownlang-parser/src/test/resources/modules/std/stripMargin.own +++ b/ownlang-parser/src/test/resources/modules/std/stripMargin.own @@ -1,4 +1,4 @@ -use "std" +use std def testStripMargin() { testCases = [ diff --git a/ownlang-parser/src/test/resources/modules/std/toHexString.own b/ownlang-parser/src/test/resources/modules/std/toHexString.own index f557fc17..2de2d7cf 100644 --- a/ownlang-parser/src/test/resources/modules/std/toHexString.own +++ b/ownlang-parser/src/test/resources/modules/std/toHexString.own @@ -1,4 +1,4 @@ -use "std" +use std def testToHexString() { assertEquals("8d", toHexString(141)) diff --git a/ownlang-parser/src/test/resources/modules/std/try.own b/ownlang-parser/src/test/resources/modules/std/try.own index bb9751f1..161ab5e6 100644 --- a/ownlang-parser/src/test/resources/modules/std/try.own +++ b/ownlang-parser/src/test/resources/modules/std/try.own @@ -1,4 +1,4 @@ -use "std" +use std def testTryOnly() { assertEquals(1, try(def() = 1)) diff --git a/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own b/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own index fc335af7..7906f2ed 100644 --- a/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own +++ b/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own @@ -1,4 +1,4 @@ -use ["std", "yaml", "ounit"] +use std, yaml, ounit x = yamldecode(" name: \"std\" diff --git a/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own b/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own index cf2e465d..77e2e3b2 100644 --- a/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own +++ b/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own @@ -1,4 +1,4 @@ -use ["std", "yaml", "ounit"] +use std, yaml, ounit yml = yamlencode({ "name": "Yaml Example", diff --git a/ownlang-parser/src/test/resources/other/stringFunctions.own b/ownlang-parser/src/test/resources/other/stringFunctions.own index 7e0e60a3..38cad8d0 100644 --- a/ownlang-parser/src/test/resources/other/stringFunctions.own +++ b/ownlang-parser/src/test/resources/other/stringFunctions.own @@ -45,7 +45,7 @@ def testIsEmpty() { } def testExtensionFunction() { - use "std" + use std s = "1es1" assertEquals("test", s.replace("1", "t")) } diff --git a/ownlang-parser/src/test/resources/other/useStatementScope.own b/ownlang-parser/src/test/resources/other/useStatementScope.own new file mode 100644 index 00000000..32124fa2 --- /dev/null +++ b/ownlang-parser/src/test/resources/other/useStatementScope.own @@ -0,0 +1,26 @@ +use std + +def testRegular() { + assertEquals("7f", "127".toHexString()) +} + +def testInCondition() { + if (true) { + use date + } + assertNotEquals("", newDate()) +} + +def testInScope() { + PI = "fallback" + assertEquals("fallback", PI) + + useMath() + + assertEquals("fallback", PI) + assertEquals(3, abs(-3)) +} + +def useMath() { + use math +} \ No newline at end of file diff --git a/program.own b/program.own index fb6e712e..1e74c6f8 100644 --- a/program.own +++ b/program.own @@ -1,6 +1,4 @@ -use "math" -use "std" -use "functional" +use math, std, functional word = 2 + 2 word2 = PI + word @@ -105,7 +103,7 @@ for (v : arr1 << arr2) print "" + v + ", " print "\n" for v : [1,2,3,4,5,6,7,8,9] print "" + v + ", " -use "types" +use types println typeof(1) println typeof("1") println typeof(arr1) @@ -133,7 +131,7 @@ foreach(nums, ::echo) println "sort" foreach(sort(nums, def(a,b) = b - a), ::echo) -use "http" +use http /*http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { println "Added: " + v @@ -144,7 +142,7 @@ def http_get_demo(v) { http("http://jsonplaceholder.typicode.com/users", ::echo) }*/ -use "json" +use json println "json" println jsonencode({ "name": "JSON Example", @@ -242,7 +240,7 @@ println 1 :: 2 :: 3 println "\u042a" -use "date" +use date d = newDate(); println d println formatDate(d) diff --git a/tests.own b/tests.own index 2687ae8c..2eddadef 100644 --- a/tests.own +++ b/tests.own @@ -1,8 +1,4 @@ -use "ounit" -use "types" -use "functional" -use "date" -use "files" +use ounit, types, functional, date, files def testAdditionOnNumbers() { assertEquals(6, 0 + 1 + 2 + 3) From 99bdd1c953c025dff1e9e8ad70efc7adab5ed53b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 9 Sep 2023 18:59:53 +0300 Subject: [PATCH 305/448] Separate constants and functions in module, don't load it twice --- .../ownlang/modules/canvasfx/canvasfx.java | 78 ++++++------ .../ownlang/modules/base64/base64.java | 17 +-- .../ownlang/modules/canvas/canvas.java | 56 +++++---- .../modules/collections/collections.java | 27 ++--- .../annimon/ownlang/modules/date/date.java | 32 ++--- .../modules/downloader/downloader.java | 15 ++- .../annimon/ownlang/modules/files/files.java | 114 +++++++++--------- .../annimon/ownlang/modules/forms/forms.java | 58 +++++---- .../modules/functional/functional.java | 37 +++--- .../annimon/ownlang/modules/gzip/gzip.java | 28 +++-- .../annimon/ownlang/modules/http/http.java | 20 +-- .../annimon/ownlang/modules/java/java.java | 86 ++++++------- .../annimon/ownlang/modules/jdbc/jdbc.java | 66 +++++----- .../annimon/ownlang/modules/json/json.java | 18 ++- .../annimon/ownlang/modules/math/math.java | 88 +++++++------- .../ownlang/modules/okhttp/okhttp.java | 22 ++-- .../annimon/ownlang/modules/ounit/ounit.java | 28 +++-- .../annimon/ownlang/modules/regex/regex.java | 11 +- .../annimon/ownlang/modules/robot/robot.java | 83 +++++++------ .../ownlang/modules/socket/socket.java | 40 +++--- .../com/annimon/ownlang/modules/std/std.java | 99 ++++++++------- .../annimon/ownlang/modules/types/types.java | 44 ++++--- .../annimon/ownlang/modules/yaml/yaml.java | 15 ++- .../com/annimon/ownlang/modules/zip/zip.java | 25 ++-- .../com/annimon/ownlang/lib/ModuleLoader.java | 28 +++++ .../com/annimon/ownlang/lib/RootScope.java | 17 +++ .../com/annimon/ownlang/lib/ScopeHandler.java | 5 +- .../com/annimon/ownlang/modules/Module.java | 10 +- .../ownlang/parser/ast/UseStatement.java | 41 ++----- .../parser/optimization/VariablesGrabber.java | 15 +-- .../ownlang/utils/ModulesInfoCreator.java | 12 +- 31 files changed, 690 insertions(+), 545 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/ModuleLoader.java diff --git a/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java b/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java index 1247d655..4f4ef655 100644 --- a/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java +++ b/modules/canvasfx/src/main/java/com/annimon/ownlang/modules/canvasfx/canvasfx.java @@ -8,6 +8,8 @@ import java.lang.reflect.Modifier; import java.nio.IntBuffer; import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; import javafx.application.Platform; @@ -83,7 +85,9 @@ public EventType getHandler() { } } - public static void initConstants() { + @Override + public Map constants() { + final var result = new HashMap(11); // Color class final Map colors = Arrays.stream(Color.class.getDeclaredFields()) .filter(f -> Modifier.isStatic(f.getModifiers())) @@ -98,94 +102,96 @@ public static void initConstants() { colors.put(new StringValue("rgb"), new FunctionValue(new rgbColor())); colors.put(new StringValue("hsb"), new FunctionValue(new hsbColor())); colors.put(new StringValue("web"), new FunctionValue(new webColor())); - ScopeHandler.setConstant("Color", new MapValue(colors)); + result.put("Color", new MapValue(colors)); final MapValue arcType = new MapValue(ArcType.values().length); for (ArcType value : ArcType.values()) { arcType.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("ArcType", arcType); + result.put("ArcType", arcType); final MapValue fillRule = new MapValue(FillRule.values().length); for (FillRule value : FillRule.values()) { fillRule.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("FillRule", fillRule); + result.put("FillRule", fillRule); final MapValue blendMode = new MapValue(BlendMode.values().length); for (BlendMode value : BlendMode.values()) { blendMode.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("BlendMode", blendMode); + result.put("BlendMode", blendMode); final MapValue lineCap = new MapValue(StrokeLineCap.values().length); for (StrokeLineCap value : StrokeLineCap.values()) { lineCap.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("StrokeLineCap", lineCap); + result.put("StrokeLineCap", lineCap); final MapValue lineJoin = new MapValue(StrokeLineJoin.values().length); for (StrokeLineJoin value : StrokeLineJoin.values()) { lineJoin.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("StrokeLineJoin", lineJoin); + result.put("StrokeLineJoin", lineJoin); final MapValue textAlignment = new MapValue(TextAlignment.values().length); for (TextAlignment value : TextAlignment.values()) { textAlignment.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("TextAlignment", textAlignment); + result.put("TextAlignment", textAlignment); final MapValue vPos = new MapValue(VPos.values().length); for (VPos value : VPos.values()) { vPos.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("VPos", vPos); + result.put("VPos", vPos); final MapValue events = new MapValue(Events.values().length); for (Events value : Events.values()) { events.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("Events", events); + result.put("Events", events); final MapValue mouseButton = new MapValue(MouseButton.values().length); for (MouseButton value : MouseButton.values()) { mouseButton.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("MouseButton", mouseButton); + result.put("MouseButton", mouseButton); final MapValue keyCodes = new MapValue(KeyCode.values().length); for (KeyCode value : KeyCode.values()) { keyCodes.set(value.name(), NumberValue.of(value.ordinal())); } - ScopeHandler.setConstant("KeyCode", keyCodes); + result.put("KeyCode", keyCodes); + return result; } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("window", new CreateWindow()); - ScopeHandler.setFunction("repaint", new Repaint()); - - ScopeHandler.setFunction("BlendEffect", new BlendEffect()); - ScopeHandler.setFunction("BloomEffect", new BloomEffect()); - ScopeHandler.setFunction("BoxBlurEffect", new BoxBlurEffect()); - ScopeHandler.setFunction("ColorAdjustEffect", new ColorAdjustEffect()); - ScopeHandler.setFunction("ColorInputEffect", new ColorInputEffect()); - ScopeHandler.setFunction("DropShadowEffect", new DropShadowEffect()); - ScopeHandler.setFunction("GaussianBlurEffect", new GaussianBlurEffect()); - ScopeHandler.setFunction("GlowEffect", new GlowEffect()); - ScopeHandler.setFunction("InnerShadowEffect", new InnerShadowEffect()); - ScopeHandler.setFunction("LightingEffect", new LightingEffect()); - ScopeHandler.setFunction("MotionBlurEffect", new MotionBlurEffect()); - ScopeHandler.setFunction("PerspectiveTransformEffect", new PerspectiveTransformEffect()); - ScopeHandler.setFunction("ReflectionEffect", new ReflectionEffect()); - ScopeHandler.setFunction("SepiaToneEffect", new SepiaToneEffect()); - ScopeHandler.setFunction("ShadowEffect", new ShadowEffect()); - - ScopeHandler.setFunction("addEventFilter", new addEventFilter()); - ScopeHandler.setFunction("addEventHandler", new addEventHandler()); - ScopeHandler.setFunction("createImage", new createImage()); + public Map functions() { + final var result = new LinkedHashMap(20); + result.put("window", new CreateWindow()); + result.put("repaint", new Repaint()); + + result.put("BlendEffect", new BlendEffect()); + result.put("BloomEffect", new BloomEffect()); + result.put("BoxBlurEffect", new BoxBlurEffect()); + result.put("ColorAdjustEffect", new ColorAdjustEffect()); + result.put("ColorInputEffect", new ColorInputEffect()); + result.put("DropShadowEffect", new DropShadowEffect()); + result.put("GaussianBlurEffect", new GaussianBlurEffect()); + result.put("GlowEffect", new GlowEffect()); + result.put("InnerShadowEffect", new InnerShadowEffect()); + result.put("LightingEffect", new LightingEffect()); + result.put("MotionBlurEffect", new MotionBlurEffect()); + result.put("PerspectiveTransformEffect", new PerspectiveTransformEffect()); + result.put("ReflectionEffect", new ReflectionEffect()); + result.put("SepiaToneEffect", new SepiaToneEffect()); + result.put("ShadowEffect", new ShadowEffect()); + + result.put("addEventFilter", new addEventFilter()); + result.put("addEventHandler", new addEventHandler()); + result.put("createImage", new createImage()); + return result; } private static class ColorValue implements Value { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java index 1796403c..15074a71 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java @@ -4,21 +4,24 @@ import com.annimon.ownlang.modules.Module; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.Map; public class base64 implements Module { private static final int TYPE_URL_SAFE = 8; - public static void initConstants() { - ScopeHandler.setConstant("BASE64_URL_SAFE", NumberValue.of(TYPE_URL_SAFE)); + @Override + public Map constants() { + return Map.of("BASE64_URL_SAFE", NumberValue.of(TYPE_URL_SAFE)); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("base64encode", this::base64encode); - ScopeHandler.setFunction("base64decode", this::base64decode); - ScopeHandler.setFunction("base64encodeToString", this::base64encodeToString); + public Map functions() { + return Map.of( + "base64encode", this::base64encode, + "base64decode", this::base64decode, + "base64encodeToString", this::base64encodeToString + ); } private Value base64encode(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java b/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java index 6172798d..d4b5a6eb 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java @@ -12,9 +12,12 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.image.BufferedImage; +import java.util.LinkedHashMap; +import java.util.Map; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; +import static java.util.Map.entry; /** * @@ -29,36 +32,41 @@ public final class canvas implements Module { private static NumberValue lastKey; private static ArrayValue mouseHover; - public static void initConstants() { - ScopeHandler.setConstant("VK_UP", NumberValue.of(KeyEvent.VK_UP)); - ScopeHandler.setConstant("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - ScopeHandler.setConstant("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - ScopeHandler.setConstant("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - ScopeHandler.setConstant("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - ScopeHandler.setConstant("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); - } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("window", new CreateWindow()); - ScopeHandler.setFunction("prompt", new Prompt()); - ScopeHandler.setFunction("keypressed", new KeyPressed()); - ScopeHandler.setFunction("mousehover", new MouseHover()); - ScopeHandler.setFunction("line", intConsumer4Convert(canvas::line)); - ScopeHandler.setFunction("oval", intConsumer4Convert(canvas::oval)); - ScopeHandler.setFunction("foval", intConsumer4Convert(canvas::foval)); - ScopeHandler.setFunction("rect", intConsumer4Convert(canvas::rect)); - ScopeHandler.setFunction("frect", intConsumer4Convert(canvas::frect)); - ScopeHandler.setFunction("clip", intConsumer4Convert(canvas::clip)); - ScopeHandler.setFunction("drawstring", new DrawString()); - ScopeHandler.setFunction("color", new SetColor()); - ScopeHandler.setFunction("repaint", new Repaint()); + public Map constants() { + return Map.ofEntries( + entry("VK_UP", NumberValue.of(KeyEvent.VK_UP)), + entry("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)), + entry("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)), + entry("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)), + entry("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)), + entry("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)) + ); + } + @Override + public Map functions() { lastKey = NumberValue.MINUS_ONE; mouseHover = new ArrayValue(new Value[] { NumberValue.ZERO, NumberValue.ZERO }); + + final var result = new LinkedHashMap(15); + result.put("window", new CreateWindow()); + result.put("prompt", new Prompt()); + result.put("keypressed", new KeyPressed()); + result.put("mousehover", new MouseHover()); + result.put("line", intConsumer4Convert(canvas::line)); + result.put("oval", intConsumer4Convert(canvas::oval)); + result.put("foval", intConsumer4Convert(canvas::foval)); + result.put("rect", intConsumer4Convert(canvas::rect)); + result.put("frect", intConsumer4Convert(canvas::frect)); + result.put("clip", intConsumer4Convert(canvas::clip)); + result.put("drawstring", new DrawString()); + result.put("color", new SetColor()); + result.put("repaint", new Repaint()); + return result; } - + @FunctionalInterface private interface IntConsumer4 { void accept(int i1, int i2, int i3, int i4); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java index 4a26bb03..b01fa027 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java @@ -3,29 +3,28 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.Supplier; +import static java.util.Map.entry; public class collections implements Module { - public static void initConstants() { + @Override + public Map constants() { + return Collections.emptyMap(); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("hashMap", mapFunction(HashMap::new)); - ScopeHandler.setFunction("linkedHashMap", mapFunction(LinkedHashMap::new)); - ScopeHandler.setFunction("concurrentHashMap", mapFunction(ConcurrentHashMap::new)); - ScopeHandler.setFunction("treeMap", sortedMapFunction(TreeMap::new, TreeMap::new)); - ScopeHandler.setFunction("concurrentSkipListMap", sortedMapFunction(ConcurrentSkipListMap::new, ConcurrentSkipListMap::new)); + public Map functions() { + return Map.ofEntries( + entry("hashMap", mapFunction(HashMap::new)), + entry("linkedHashMap", mapFunction(LinkedHashMap::new)), + entry("concurrentHashMap", mapFunction(ConcurrentHashMap::new)), + entry("treeMap", sortedMapFunction(TreeMap::new, TreeMap::new)), + entry("concurrentSkipListMap", sortedMapFunction(ConcurrentSkipListMap::new, ConcurrentSkipListMap::new)) + ); } private Function mapFunction(final Supplier> mapSupplier) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java index f0185bdb..66424102 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java @@ -6,9 +6,7 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; +import java.util.*; /** * @@ -27,21 +25,25 @@ public final class date implements Module { SECOND = new StringValue("second"), MILLISECOND = new StringValue("millisecond"); - public static void initConstants() { - ScopeHandler.setConstant("STYLE_FULL", NumberValue.of(DateFormat.FULL)); - ScopeHandler.setConstant("STYLE_LONG", NumberValue.of(DateFormat.LONG)); - ScopeHandler.setConstant("STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM)); - ScopeHandler.setConstant("STYLE_SHORT", NumberValue.of(DateFormat.SHORT)); + @Override + public Map constants() { + return Map.of( + "STYLE_FULL", NumberValue.of(DateFormat.FULL), + "STYLE_LONG", NumberValue.of(DateFormat.LONG), + "STYLE_MEDIUM", NumberValue.of(DateFormat.MEDIUM), + "STYLE_SHORT", NumberValue.of(DateFormat.SHORT) + ); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("newDate", new date_newDate()); - ScopeHandler.setFunction("newFormat", new date_newFormat()); - ScopeHandler.setFunction("formatDate", new date_format()); - ScopeHandler.setFunction("parseDate", new date_parse()); - ScopeHandler.setFunction("toTimestamp", new date_toTimestamp()); + public Map functions() { + return Map.of( + "newDate", new date_newDate(), + "newFormat", new date_newFormat(), + "formatDate", new date_format(), + "parseDate", new date_parse(), + "toTimestamp", new date_toTimestamp() + ); } // diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java b/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java index acfcc042..949fc335 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/downloader/downloader.java @@ -9,13 +9,22 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; +import java.util.Collections; +import java.util.Map; public final class downloader implements Module { @Override - public void init() { - ScopeHandler.setFunction("getContentLength", this::getContentLength); - ScopeHandler.setFunction("downloader", this::downloader); + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + return Map.of( + "getContentLength", this::getContentLength, + "downloader", this::downloader + ); } private Value getContentLength(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java index 2335e38d..31e1c76a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -18,6 +18,7 @@ import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -28,71 +29,74 @@ public final class files implements Module { private static Map files; - public static void initConstants() { - ScopeHandler.setConstant("FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction())); + @Override + public Map constants() { + return Map.of( + "FILES_COMPARATOR", new FunctionValue(new filesComparatorFunction()) + ); } @Override - public void init() { + public Map functions() { files = new HashMap<>(); - initConstants(); + final var result = new LinkedHashMap(50); + result.put("fopen", new fopen()); + result.put("flush", new flush()); + result.put("fclose", new fclose()); - ScopeHandler.setFunction("fopen", new fopen()); - ScopeHandler.setFunction("flush", new flush()); - ScopeHandler.setFunction("fclose", new fclose()); - // Operations - ScopeHandler.setFunction("copy", new copy()); - ScopeHandler.setFunction("delete", fileToBoolean(File::delete)); - ScopeHandler.setFunction("listFiles", new listFiles()); - ScopeHandler.setFunction("mkdir", fileToBoolean(File::mkdir)); - ScopeHandler.setFunction("mkdirs", fileToBoolean(File::mkdirs)); - ScopeHandler.setFunction("rename", new rename()); + result.put("copy", new copy()); + result.put("delete", fileToBoolean(File::delete)); + result.put("listFiles", new listFiles()); + result.put("mkdir", fileToBoolean(File::mkdir)); + result.put("mkdirs", fileToBoolean(File::mkdirs)); + result.put("rename", new rename()); // Permissions and statuses - ScopeHandler.setFunction("canExecute", fileToBoolean(File::canExecute)); - ScopeHandler.setFunction("canRead", fileToBoolean(File::canRead)); - ScopeHandler.setFunction("canWrite", fileToBoolean(File::canWrite)); - ScopeHandler.setFunction("isDirectory", fileToBoolean(File::isDirectory)); - ScopeHandler.setFunction("isFile", fileToBoolean(File::isFile)); - ScopeHandler.setFunction("isHidden", fileToBoolean(File::isHidden)); - ScopeHandler.setFunction("setExecutable", new setExecutable()); - ScopeHandler.setFunction("setReadable", new setReadable()); - ScopeHandler.setFunction("setReadOnly", new setReadOnly()); - ScopeHandler.setFunction("setWritable", new setWritable()); - - ScopeHandler.setFunction("exists", fileToBoolean(File::exists)); - ScopeHandler.setFunction("fileSize", new fileSize()); - ScopeHandler.setFunction("getParent", new getParent()); - ScopeHandler.setFunction("lastModified", new lastModified()); - ScopeHandler.setFunction("setLastModified", new setLastModified()); + result.put("canExecute", fileToBoolean(File::canExecute)); + result.put("canRead", fileToBoolean(File::canRead)); + result.put("canWrite", fileToBoolean(File::canWrite)); + result.put("isDirectory", fileToBoolean(File::isDirectory)); + result.put("isFile", fileToBoolean(File::isFile)); + result.put("isHidden", fileToBoolean(File::isHidden)); + result.put("setExecutable", new setExecutable()); + result.put("setReadable", new setReadable()); + result.put("setReadOnly", new setReadOnly()); + result.put("setWritable", new setWritable()); + + result.put("exists", fileToBoolean(File::exists)); + result.put("fileSize", new fileSize()); + result.put("getParent", new getParent()); + result.put("lastModified", new lastModified()); + result.put("setLastModified", new setLastModified()); // IO - ScopeHandler.setFunction("readBoolean", new readBoolean()); - ScopeHandler.setFunction("readByte", new readByte()); - ScopeHandler.setFunction("readBytes", new readBytes()); - ScopeHandler.setFunction("readAllBytes", new readAllBytes()); - ScopeHandler.setFunction("readChar", new readChar()); - ScopeHandler.setFunction("readShort", new readShort()); - ScopeHandler.setFunction("readInt", new readInt()); - ScopeHandler.setFunction("readLong", new readLong()); - ScopeHandler.setFunction("readFloat", new readFloat()); - ScopeHandler.setFunction("readDouble", new readDouble()); - ScopeHandler.setFunction("readUTF", new readUTF()); - ScopeHandler.setFunction("readLine", new readLine()); - ScopeHandler.setFunction("readText", new readText()); - ScopeHandler.setFunction("writeBoolean", new writeBoolean()); - ScopeHandler.setFunction("writeByte", new writeByte()); - ScopeHandler.setFunction("writeBytes", new writeBytes()); - ScopeHandler.setFunction("writeChar", new writeChar()); - ScopeHandler.setFunction("writeShort", new writeShort()); - ScopeHandler.setFunction("writeInt", new writeInt()); - ScopeHandler.setFunction("writeLong", new writeLong()); - ScopeHandler.setFunction("writeFloat", new writeFloat()); - ScopeHandler.setFunction("writeDouble", new writeDouble()); - ScopeHandler.setFunction("writeUTF", new writeUTF()); - ScopeHandler.setFunction("writeLine", new writeLine()); - ScopeHandler.setFunction("writeText", new writeText()); + result.put("readBoolean", new readBoolean()); + result.put("readByte", new readByte()); + result.put("readBytes", new readBytes()); + result.put("readAllBytes", new readAllBytes()); + result.put("readChar", new readChar()); + result.put("readShort", new readShort()); + result.put("readInt", new readInt()); + result.put("readLong", new readLong()); + result.put("readFloat", new readFloat()); + result.put("readDouble", new readDouble()); + result.put("readUTF", new readUTF()); + result.put("readLine", new readLine()); + result.put("readText", new readText()); + result.put("writeBoolean", new writeBoolean()); + result.put("writeByte", new writeByte()); + result.put("writeBytes", new writeBytes()); + result.put("writeChar", new writeChar()); + result.put("writeShort", new writeShort()); + result.put("writeInt", new writeInt()); + result.put("writeLong", new writeLong()); + result.put("writeFloat", new writeFloat()); + result.put("writeDouble", new writeDouble()); + result.put("writeUTF", new writeUTF()); + result.put("writeLine", new writeLine()); + result.put("writeText", new writeText()); + return result; } private static class filesComparatorFunction implements Function { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java index 26331e30..b9dfbd60 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/forms.java @@ -4,6 +4,8 @@ import com.annimon.ownlang.modules.Module; import java.awt.BorderLayout; import java.awt.event.WindowEvent; +import java.util.LinkedHashMap; +import java.util.Map; import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.ScrollPaneConstants; @@ -15,14 +17,16 @@ */ public final class forms implements Module { - public static void initConstants() { + @Override + public Map constants() { + final var result = new LinkedHashMap(10); // JFrame constants - ScopeHandler.setConstant("DISPOSE_ON_CLOSE", NumberValue.of(JFrame.DISPOSE_ON_CLOSE)); - ScopeHandler.setConstant("DO_NOTHING_ON_CLOSE", NumberValue.of(JFrame.DO_NOTHING_ON_CLOSE)); - ScopeHandler.setConstant("EXIT_ON_CLOSE", NumberValue.of(JFrame.EXIT_ON_CLOSE)); - ScopeHandler.setConstant("HIDE_ON_CLOSE", NumberValue.of(JFrame.HIDE_ON_CLOSE)); + result.put("DISPOSE_ON_CLOSE", NumberValue.of(JFrame.DISPOSE_ON_CLOSE)); + result.put("DO_NOTHING_ON_CLOSE", NumberValue.of(JFrame.DO_NOTHING_ON_CLOSE)); + result.put("EXIT_ON_CLOSE", NumberValue.of(JFrame.EXIT_ON_CLOSE)); + result.put("HIDE_ON_CLOSE", NumberValue.of(JFrame.HIDE_ON_CLOSE)); - // SwinfConstants + // SwingConstants final MapValue swing = new MapValue(20); swing.set("BOTTOM", NumberValue.of(SwingConstants.BOTTOM)); swing.set("CENTER", NumberValue.of(SwingConstants.CENTER)); @@ -43,7 +47,7 @@ public static void initConstants() { swing.set("TRAILING", NumberValue.of(SwingConstants.TRAILING)); swing.set("VERTICAL", NumberValue.of(SwingConstants.VERTICAL)); swing.set("WEST", NumberValue.of(SwingConstants.WEST)); - ScopeHandler.setConstant("SwingConstants", swing); + result.put("SwingConstants", swing); // LayoutManagers constants final MapValue border = new MapValue(13); @@ -60,7 +64,7 @@ public static void initConstants() { border.set("PAGE_START", new StringValue(BorderLayout.PAGE_START)); border.set("SOUTH", new StringValue(BorderLayout.SOUTH)); border.set("WEST", new StringValue(BorderLayout.WEST)); - ScopeHandler.setConstant("BorderLayout", border); + result.put("BorderLayout", border); // ScrollPane constants final MapValue scrollpane = new MapValue(13); @@ -85,14 +89,14 @@ public static void initConstants() { scrollpane.set("VERTICAL_SCROLLBAR_ALWAYS", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS)); scrollpane.set("VERTICAL_SCROLLBAR_AS_NEEDED", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED)); scrollpane.set("VERTICAL_SCROLLBAR_NEVER", NumberValue.of(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER)); - ScopeHandler.setConstant("ScrollPaneConstants", scrollpane); + result.put("ScrollPaneConstants", scrollpane); final MapValue box = new MapValue(4); box.set("LINE_AXIS", NumberValue.of(BoxLayout.LINE_AXIS)); box.set("PAGE_AXIS", NumberValue.of(BoxLayout.PAGE_AXIS)); box.set("X_AXIS", NumberValue.of(BoxLayout.X_AXIS)); box.set("Y_AXIS", NumberValue.of(BoxLayout.Y_AXIS)); - ScopeHandler.setConstant("BoxLayout", box); + result.put("BoxLayout", box); final MapValue windowEvent = new MapValue(4); windowEvent.set("WINDOW_FIRST", NumberValue.of(WindowEvent.WINDOW_FIRST)); @@ -107,27 +111,29 @@ public static void initConstants() { windowEvent.set("WINDOW_LOST_FOCUS", NumberValue.of(WindowEvent.WINDOW_LOST_FOCUS)); windowEvent.set("WINDOW_STATE_CHANGED", NumberValue.of(WindowEvent.WINDOW_STATE_CHANGED)); windowEvent.set("WINDOW_LAST", NumberValue.of(WindowEvent.WINDOW_LAST)); - ScopeHandler.setConstant("WindowEvent", windowEvent); + result.put("WindowEvent", windowEvent); + return result; } @Override - public void init() { - initConstants(); + public Map functions() { + final var result = new LinkedHashMap(16); // Components - ScopeHandler.setFunction("newButton", Components::newButton); - ScopeHandler.setFunction("newLabel", Components::newLabel); - ScopeHandler.setFunction("newPanel", Components::newPanel); - ScopeHandler.setFunction("newProgressBar", Components::newProgressBar); - ScopeHandler.setFunction("newScrollPane", Components::newScrollPane); - ScopeHandler.setFunction("newTextArea", Components::newTextArea); - ScopeHandler.setFunction("newTextField", Components::newTextField); - ScopeHandler.setFunction("newWindow", Components::newWindow); + result.put("newButton", Components::newButton); + result.put("newLabel", Components::newLabel); + result.put("newPanel", Components::newPanel); + result.put("newProgressBar", Components::newProgressBar); + result.put("newScrollPane", Components::newScrollPane); + result.put("newTextArea", Components::newTextArea); + result.put("newTextField", Components::newTextField); + result.put("newWindow", Components::newWindow); // LayoutManagers - ScopeHandler.setFunction("borderLayout", LayoutManagers::borderLayout); - ScopeHandler.setFunction("boxLayout", LayoutManagers::boxLayout); - ScopeHandler.setFunction("cardLayout", LayoutManagers::cardLayout); - ScopeHandler.setFunction("gridLayout", LayoutManagers::gridLayout); - ScopeHandler.setFunction("flowLayout", LayoutManagers::flowLayout); + result.put("borderLayout", LayoutManagers::borderLayout); + result.put("boxLayout", LayoutManagers::boxLayout); + result.put("cardLayout", LayoutManagers::cardLayout); + result.put("gridLayout", LayoutManagers::gridLayout); + result.put("flowLayout", LayoutManagers::flowLayout); + return result; } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index 9e2e88d4..0ad61889 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -1,8 +1,11 @@ package com.annimon.ownlang.modules.functional; +import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.ScopeHandler; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; +import java.util.HashMap; +import java.util.Map; /** * @@ -10,24 +13,26 @@ */ public final class functional implements Module { - public static void initConstants() { - ScopeHandler.setConstant("IDENTITY", new FunctionValue(args -> args[0])); + @Override + public Map constants() { + return Map.of("IDENTITY", new FunctionValue(args -> args[0])); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("foreach", new functional_foreach()); - ScopeHandler.setFunction("map", new functional_map()); - ScopeHandler.setFunction("flatmap", new functional_flatmap()); - ScopeHandler.setFunction("reduce", new functional_reduce()); - ScopeHandler.setFunction("filter", new functional_filter(false)); - ScopeHandler.setFunction("sortby", new functional_sortby()); - ScopeHandler.setFunction("takewhile", new functional_filter(true)); - ScopeHandler.setFunction("dropwhile", new functional_dropwhile()); + public Map functions() { + final var result = new HashMap(15); + result.put("foreach", new functional_foreach()); + result.put("map", new functional_map()); + result.put("flatmap", new functional_flatmap()); + result.put("reduce", new functional_reduce()); + result.put("filter", new functional_filter(false)); + result.put("sortby", new functional_sortby()); + result.put("takewhile", new functional_filter(true)); + result.put("dropwhile", new functional_dropwhile()); - ScopeHandler.setFunction("chain", new functional_chain()); - ScopeHandler.setFunction("stream", new functional_stream()); - ScopeHandler.setFunction("combine", new functional_combine()); + result.put("chain", new functional_chain()); + result.put("stream", new functional_stream()); + result.put("combine", new functional_combine()); + return result; } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java index 14084c7c..323b4df2 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java @@ -3,25 +3,27 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; +import java.util.Collections; +import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class gzip implements Module { @Override - public void init() { - ScopeHandler.setFunction("gzip", this::gzipFile); - ScopeHandler.setFunction("gzipBytes", this::gzipBytes); - ScopeHandler.setFunction("ungzip", this::ungzipFile); - ScopeHandler.setFunction("ungzipBytes", this::ungzipBytes); + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + return Map.of( + "gzip", this::gzipFile, + "gzipBytes", this::gzipBytes, + "ungzip", this::ungzipFile, + "ungzipBytes", this::ungzipBytes + ); } private Value gzipFile(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java index ebd36fbe..4c5fe91f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java @@ -1,7 +1,10 @@ package com.annimon.ownlang.modules.http; -import com.annimon.ownlang.lib.ScopeHandler; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; +import java.util.Collections; +import java.util.Map; /** * @@ -9,14 +12,17 @@ */ public final class http implements Module { - public static void initConstants() { + @Override + public Map constants() { + return Collections.emptyMap(); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("urlencode", new http_urlencode()); - ScopeHandler.setFunction("http", new http_http()); - ScopeHandler.setFunction("download", new http_download()); + public Map functions() { + return Map.of( + "urlencode", new http_urlencode(), + "http", new http_http(), + "download", new http_download() + ); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java index aca88ba1..a2f69532 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -7,9 +7,8 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; +import static java.util.Map.entry; /** * Java interoperability module. @@ -20,48 +19,51 @@ public final class java implements Module { private static final Value NULL = new NullValue(); - public static void initConstants() { + @Override + public Map constants() { + final var result = new LinkedHashMap(16); + result.put("null", NULL); + result.put("boolean.class", new ClassValue(boolean.class)); + result.put("boolean[].class", new ClassValue(boolean[].class)); + result.put("boolean[][].class", new ClassValue(boolean[][].class)); + result.put("byte.class", new ClassValue(byte.class)); + result.put("byte[].class", new ClassValue(byte[].class)); + result.put("byte[][].class", new ClassValue(byte[][].class)); + result.put("short.class", new ClassValue(short.class)); + result.put("short[].class", new ClassValue(short[].class)); + result.put("short[][].class", new ClassValue(short[][].class)); + result.put("char.class", new ClassValue(char.class)); + result.put("char[].class", new ClassValue(char[].class)); + result.put("char[][].class", new ClassValue(char[][].class)); + result.put("int.class", new ClassValue(int.class)); + result.put("int[].class", new ClassValue(int[].class)); + result.put("int[][].class", new ClassValue(int[][].class)); + result.put("long.class", new ClassValue(long.class)); + result.put("long[].class", new ClassValue(long[].class)); + result.put("long[][].class", new ClassValue(long[][].class)); + result.put("float.class", new ClassValue(float.class)); + result.put("float[].class", new ClassValue(float[].class)); + result.put("float[][].class", new ClassValue(float[][].class)); + result.put("double.class", new ClassValue(double.class)); + result.put("double[].class", new ClassValue(double[].class)); + result.put("double[][].class", new ClassValue(double[][].class)); + result.put("String.class", new ClassValue(String.class)); + result.put("String[].class", new ClassValue(String[].class)); + result.put("String[][].class", new ClassValue(String[][].class)); + result.put("Object.class", new ClassValue(Object.class)); + result.put("Object[].class", new ClassValue(Object[].class)); + result.put("Object[][].class", new ClassValue(Object[][].class)); + return result; } @Override - public void init() { - initConstants(); - ScopeHandler.setConstant("null", NULL); - ScopeHandler.setConstant("boolean.class", new ClassValue(boolean.class)); - ScopeHandler.setConstant("boolean[].class", new ClassValue(boolean[].class)); - ScopeHandler.setConstant("boolean[][].class", new ClassValue(boolean[][].class)); - ScopeHandler.setConstant("byte.class", new ClassValue(byte.class)); - ScopeHandler.setConstant("byte[].class", new ClassValue(byte[].class)); - ScopeHandler.setConstant("byte[][].class", new ClassValue(byte[][].class)); - ScopeHandler.setConstant("short.class", new ClassValue(short.class)); - ScopeHandler.setConstant("short[].class", new ClassValue(short[].class)); - ScopeHandler.setConstant("short[][].class", new ClassValue(short[][].class)); - ScopeHandler.setConstant("char.class", new ClassValue(char.class)); - ScopeHandler.setConstant("char[].class", new ClassValue(char[].class)); - ScopeHandler.setConstant("char[][].class", new ClassValue(char[][].class)); - ScopeHandler.setConstant("int.class", new ClassValue(int.class)); - ScopeHandler.setConstant("int[].class", new ClassValue(int[].class)); - ScopeHandler.setConstant("int[][].class", new ClassValue(int[][].class)); - ScopeHandler.setConstant("long.class", new ClassValue(long.class)); - ScopeHandler.setConstant("long[].class", new ClassValue(long[].class)); - ScopeHandler.setConstant("long[][].class", new ClassValue(long[][].class)); - ScopeHandler.setConstant("float.class", new ClassValue(float.class)); - ScopeHandler.setConstant("float[].class", new ClassValue(float[].class)); - ScopeHandler.setConstant("float[][].class", new ClassValue(float[][].class)); - ScopeHandler.setConstant("double.class", new ClassValue(double.class)); - ScopeHandler.setConstant("double[].class", new ClassValue(double[].class)); - ScopeHandler.setConstant("double[][].class", new ClassValue(double[][].class)); - ScopeHandler.setConstant("String.class", new ClassValue(String.class)); - ScopeHandler.setConstant("String[].class", new ClassValue(String[].class)); - ScopeHandler.setConstant("String[][].class", new ClassValue(String[][].class)); - ScopeHandler.setConstant("Object.class", new ClassValue(Object.class)); - ScopeHandler.setConstant("Object[].class", new ClassValue(Object[].class)); - ScopeHandler.setConstant("Object[][].class", new ClassValue(Object[][].class)); - - ScopeHandler.setFunction("isNull", this::isNull); - ScopeHandler.setFunction("newClass", this::newClass); - ScopeHandler.setFunction("toObject", this::toObject); - ScopeHandler.setFunction("toValue", this::toValue); + public Map functions() { + return Map.ofEntries( + entry("isNull", this::isNull), + entry("newClass", this::newClass), + entry("toObject", this::toObject), + entry("toValue", this::toValue) + ); } // diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index a027a14a..165eec7b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -16,6 +16,8 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; @@ -25,39 +27,43 @@ */ public final class jdbc implements Module { - public static void initConstants() { - ScopeHandler.setConstant("TRANSACTION_NONE", NumberValue.of(Connection.TRANSACTION_NONE)); - ScopeHandler.setConstant("TRANSACTION_READ_COMMITTED", NumberValue.of(Connection.TRANSACTION_READ_COMMITTED)); - ScopeHandler.setConstant("TRANSACTION_READ_UNCOMMITTED", NumberValue.of(Connection.TRANSACTION_READ_UNCOMMITTED)); - ScopeHandler.setConstant("TRANSACTION_REPEATABLE_READ", NumberValue.of(Connection.TRANSACTION_REPEATABLE_READ)); - ScopeHandler.setConstant("TRANSACTION_SERIALIZABLE", NumberValue.of(Connection.TRANSACTION_SERIALIZABLE)); - - ScopeHandler.setConstant("CLOSE_ALL_RESULTS", NumberValue.of(Statement.CLOSE_ALL_RESULTS)); - ScopeHandler.setConstant("CLOSE_CURRENT_RESULT", NumberValue.of(Statement.CLOSE_CURRENT_RESULT)); - ScopeHandler.setConstant("EXECUTE_FAILED", NumberValue.of(Statement.EXECUTE_FAILED)); - ScopeHandler.setConstant("KEEP_CURRENT_RESULT", NumberValue.of(Statement.KEEP_CURRENT_RESULT)); - ScopeHandler.setConstant("NO_GENERATED_KEYS", NumberValue.of(Statement.NO_GENERATED_KEYS)); - ScopeHandler.setConstant("RETURN_GENERATED_KEYS", NumberValue.of(Statement.RETURN_GENERATED_KEYS)); - ScopeHandler.setConstant("SUCCESS_NO_INFO", NumberValue.of(Statement.SUCCESS_NO_INFO)); - - ScopeHandler.setConstant("CLOSE_CURSORS_AT_COMMIT", NumberValue.of(ResultSet.CLOSE_CURSORS_AT_COMMIT)); - ScopeHandler.setConstant("CONCUR_READ_ONLY", NumberValue.of(ResultSet.CONCUR_READ_ONLY)); - ScopeHandler.setConstant("CONCUR_UPDATABLE", NumberValue.of(ResultSet.CONCUR_UPDATABLE)); - ScopeHandler.setConstant("FETCH_FORWARD", NumberValue.of(ResultSet.FETCH_FORWARD)); - ScopeHandler.setConstant("FETCH_REVERSE", NumberValue.of(ResultSet.FETCH_REVERSE)); - ScopeHandler.setConstant("FETCH_UNKNOWN", NumberValue.of(ResultSet.FETCH_UNKNOWN)); - ScopeHandler.setConstant("HOLD_CURSORS_OVER_COMMIT", NumberValue.of(ResultSet.HOLD_CURSORS_OVER_COMMIT)); - ScopeHandler.setConstant("TYPE_FORWARD_ONLY", NumberValue.of(ResultSet.TYPE_FORWARD_ONLY)); - ScopeHandler.setConstant("TYPE_SCROLL_INSENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_INSENSITIVE)); - ScopeHandler.setConstant("TYPE_SCROLL_SENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_SENSITIVE)); + @Override + public Map constants() { + final var result = new LinkedHashMap(25); + result.put("TRANSACTION_NONE", NumberValue.of(Connection.TRANSACTION_NONE)); + result.put("TRANSACTION_READ_COMMITTED", NumberValue.of(Connection.TRANSACTION_READ_COMMITTED)); + result.put("TRANSACTION_READ_UNCOMMITTED", NumberValue.of(Connection.TRANSACTION_READ_UNCOMMITTED)); + result.put("TRANSACTION_REPEATABLE_READ", NumberValue.of(Connection.TRANSACTION_REPEATABLE_READ)); + result.put("TRANSACTION_SERIALIZABLE", NumberValue.of(Connection.TRANSACTION_SERIALIZABLE)); + + result.put("CLOSE_ALL_RESULTS", NumberValue.of(Statement.CLOSE_ALL_RESULTS)); + result.put("CLOSE_CURRENT_RESULT", NumberValue.of(Statement.CLOSE_CURRENT_RESULT)); + result.put("EXECUTE_FAILED", NumberValue.of(Statement.EXECUTE_FAILED)); + result.put("KEEP_CURRENT_RESULT", NumberValue.of(Statement.KEEP_CURRENT_RESULT)); + result.put("NO_GENERATED_KEYS", NumberValue.of(Statement.NO_GENERATED_KEYS)); + result.put("RETURN_GENERATED_KEYS", NumberValue.of(Statement.RETURN_GENERATED_KEYS)); + result.put("SUCCESS_NO_INFO", NumberValue.of(Statement.SUCCESS_NO_INFO)); + + result.put("CLOSE_CURSORS_AT_COMMIT", NumberValue.of(ResultSet.CLOSE_CURSORS_AT_COMMIT)); + result.put("CONCUR_READ_ONLY", NumberValue.of(ResultSet.CONCUR_READ_ONLY)); + result.put("CONCUR_UPDATABLE", NumberValue.of(ResultSet.CONCUR_UPDATABLE)); + result.put("FETCH_FORWARD", NumberValue.of(ResultSet.FETCH_FORWARD)); + result.put("FETCH_REVERSE", NumberValue.of(ResultSet.FETCH_REVERSE)); + result.put("FETCH_UNKNOWN", NumberValue.of(ResultSet.FETCH_UNKNOWN)); + result.put("HOLD_CURSORS_OVER_COMMIT", NumberValue.of(ResultSet.HOLD_CURSORS_OVER_COMMIT)); + result.put("TYPE_FORWARD_ONLY", NumberValue.of(ResultSet.TYPE_FORWARD_ONLY)); + result.put("TYPE_SCROLL_INSENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_INSENSITIVE)); + result.put("TYPE_SCROLL_SENSITIVE", NumberValue.of(ResultSet.TYPE_SCROLL_SENSITIVE)); + return result; } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("getConnection", getConnectionFunction()); - ScopeHandler.setFunction("sqlite", getConnectionFunction("jdbc:sqlite:")); - ScopeHandler.setFunction("mysql", getConnectionFunction("jdbc:")); + public Map functions() { + return Map.of( + "getConnection", getConnectionFunction(), + "sqlite", getConnectionFunction("jdbc:sqlite:"), + "mysql", getConnectionFunction("jdbc:") + ); } private static com.annimon.ownlang.lib.Function getConnectionFunction() { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java index 3b3a715e..97cce633 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java @@ -1,7 +1,10 @@ package com.annimon.ownlang.modules.json; -import com.annimon.ownlang.lib.ScopeHandler; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; +import java.util.Collections; +import java.util.Map; /** * @@ -9,13 +12,16 @@ */ public final class json implements Module { - public static void initConstants() { + @Override + public Map constants() { + return Collections.emptyMap(); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("jsonencode", new json_encode()); - ScopeHandler.setFunction("jsondecode", new json_decode()); + public Map functions() { + return Map.of( + "jsonencode", new json_encode(), + "jsondecode", new json_decode() + ); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java b/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java index 3225461e..e6b0e56a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/math/math.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleFunction; import java.util.function.DoubleUnaryOperator; @@ -15,50 +17,54 @@ public final class math implements Module { private static final DoubleFunction doubleToNumber = NumberValue::of; - public static void initConstants() { - ScopeHandler.setConstant("PI", NumberValue.of(Math.PI)); - ScopeHandler.setConstant("E", NumberValue.of(Math.E)); + @Override + public Map constants() { + return Map.of( + "PI", NumberValue.of(Math.PI), + "E", NumberValue.of(Math.E) + ); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("abs", math::abs); - ScopeHandler.setFunction("acos", functionConvert(Math::acos)); - ScopeHandler.setFunction("asin", functionConvert(Math::asin)); - ScopeHandler.setFunction("atan", functionConvert(Math::atan)); - ScopeHandler.setFunction("atan2", biFunctionConvert(Math::atan2)); - ScopeHandler.setFunction("cbrt", functionConvert(Math::cbrt)); - ScopeHandler.setFunction("ceil", functionConvert(Math::ceil)); - ScopeHandler.setFunction("copySign", math::copySign); - ScopeHandler.setFunction("cos", functionConvert(Math::cos)); - ScopeHandler.setFunction("cosh", functionConvert(Math::cosh)); - ScopeHandler.setFunction("exp", functionConvert(Math::exp)); - ScopeHandler.setFunction("expm1", functionConvert(Math::expm1)); - ScopeHandler.setFunction("floor", functionConvert(Math::floor)); - ScopeHandler.setFunction("getExponent", math::getExponent); - ScopeHandler.setFunction("hypot", biFunctionConvert(Math::hypot)); - ScopeHandler.setFunction("IEEEremainder", biFunctionConvert(Math::IEEEremainder)); - ScopeHandler.setFunction("log", functionConvert(Math::log)); - ScopeHandler.setFunction("log1p", functionConvert(Math::log1p)); - ScopeHandler.setFunction("log10", functionConvert(Math::log10)); - ScopeHandler.setFunction("max", math::max); - ScopeHandler.setFunction("min", math::min); - ScopeHandler.setFunction("nextAfter", math::nextAfter); - ScopeHandler.setFunction("nextUp", functionConvert(Math::nextUp, Math::nextUp)); - ScopeHandler.setFunction("nextDown", functionConvert(Math::nextDown, Math::nextDown)); - ScopeHandler.setFunction("pow", biFunctionConvert(Math::pow)); - ScopeHandler.setFunction("rint", functionConvert(Math::rint)); - ScopeHandler.setFunction("round", math::round); - ScopeHandler.setFunction("signum", functionConvert(Math::signum, Math::signum)); - ScopeHandler.setFunction("sin", functionConvert(Math::sin)); - ScopeHandler.setFunction("sinh", functionConvert(Math::sinh)); - ScopeHandler.setFunction("sqrt", functionConvert(Math::sqrt)); - ScopeHandler.setFunction("tan", functionConvert(Math::tan)); - ScopeHandler.setFunction("tanh", functionConvert(Math::tanh)); - ScopeHandler.setFunction("toDegrees", functionConvert(Math::toDegrees)); - ScopeHandler.setFunction("toRadians", functionConvert(Math::toRadians)); - ScopeHandler.setFunction("ulp", functionConvert(Math::ulp, Math::ulp)); + public Map functions() { + final var result = new LinkedHashMap(16); + result.put("abs", math::abs); + result.put("acos", functionConvert(Math::acos)); + result.put("asin", functionConvert(Math::asin)); + result.put("atan", functionConvert(Math::atan)); + result.put("atan2", biFunctionConvert(Math::atan2)); + result.put("cbrt", functionConvert(Math::cbrt)); + result.put("ceil", functionConvert(Math::ceil)); + result.put("copySign", math::copySign); + result.put("cos", functionConvert(Math::cos)); + result.put("cosh", functionConvert(Math::cosh)); + result.put("exp", functionConvert(Math::exp)); + result.put("expm1", functionConvert(Math::expm1)); + result.put("floor", functionConvert(Math::floor)); + result.put("getExponent", math::getExponent); + result.put("hypot", biFunctionConvert(Math::hypot)); + result.put("IEEEremainder", biFunctionConvert(Math::IEEEremainder)); + result.put("log", functionConvert(Math::log)); + result.put("log1p", functionConvert(Math::log1p)); + result.put("log10", functionConvert(Math::log10)); + result.put("max", math::max); + result.put("min", math::min); + result.put("nextAfter", math::nextAfter); + result.put("nextUp", functionConvert(Math::nextUp, Math::nextUp)); + result.put("nextDown", functionConvert(Math::nextDown, Math::nextDown)); + result.put("pow", biFunctionConvert(Math::pow)); + result.put("rint", functionConvert(Math::rint)); + result.put("round", math::round); + result.put("signum", functionConvert(Math::signum, Math::signum)); + result.put("sin", functionConvert(Math::sin)); + result.put("sinh", functionConvert(Math::sinh)); + result.put("sqrt", functionConvert(Math::sqrt)); + result.put("tan", functionConvert(Math::tan)); + result.put("tanh", functionConvert(Math::tanh)); + result.put("toDegrees", functionConvert(Math::toDegrees)); + result.put("toRadians", functionConvert(Math::toRadians)); + result.put("ulp", functionConvert(Math::ulp, Math::ulp)); + return result; } private static Value abs(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java index 77d465ad..c593d6d5 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/okhttp.java @@ -8,12 +8,15 @@ import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.RequestBody; +import java.util.Collections; +import java.util.Map; public final class okhttp implements Module { private static final HttpClientValue defaultClient = new HttpClientValue(new OkHttpClient()); - public static void initConstants() { + @Override + public Map constants() { MapValue requestBody = new MapValue(5); requestBody.set("bytes", args -> { Arguments.checkOrOr(2, 4, args.length); @@ -49,27 +52,30 @@ public static void initConstants() { args[1].asString() )); }); - ScopeHandler.setConstant("RequestBody", requestBody); - MapValue multipartBody = new MapValue(10); + MapValue multipartBody = new MapValue(6); multipartBody.set("ALTERNATIVE", new StringValue(MultipartBody.ALTERNATIVE.toString())); multipartBody.set("DIGEST", new StringValue(MultipartBody.DIGEST.toString())); multipartBody.set("FORM", new StringValue(MultipartBody.FORM.toString())); multipartBody.set("MIXED", new StringValue(MultipartBody.MIXED.toString())); multipartBody.set("PARALLEL", new StringValue(MultipartBody.PARALLEL.toString())); multipartBody.set("builder", args -> new MultipartBodyBuilderValue()); - ScopeHandler.setConstant("MultipartBody", multipartBody); - MapValue okhttp = new MapValue(5); + MapValue okhttp = new MapValue(3); okhttp.set("client", defaultClient); okhttp.set("request", args -> new RequestBuilderValue()); - ScopeHandler.setConstant("okhttp", okhttp); + + return Map.of( + "RequestBody", requestBody, + "MultipartBody", multipartBody, + "okhttp", okhttp + ); } @Override - public void init() { - initConstants(); + public Map functions() { + return Collections.emptyMap(); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java index e8a3d0ce..07463c41 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -4,7 +4,10 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.text.DecimalFormat; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * @@ -12,18 +15,21 @@ */ public final class ounit implements Module { - public static void initConstants() { + @Override + public Map constants() { + return Collections.emptyMap(); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("assertEquals", new assertEquals()); - ScopeHandler.setFunction("assertNotEquals", new assertNotEquals()); - ScopeHandler.setFunction("assertSameType", new assertSameType()); - ScopeHandler.setFunction("assertTrue", new assertTrue()); - ScopeHandler.setFunction("assertFalse", new assertFalse()); - ScopeHandler.setFunction("runTests", new runTests()); + public Map functions() { + final var result = new LinkedHashMap(16); + result.put("assertEquals", new assertEquals()); + result.put("assertNotEquals", new assertNotEquals()); + result.put("assertSameType", new assertSameType()); + result.put("assertTrue", new assertTrue()); + result.put("assertFalse", new assertFalse()); + result.put("runTests", new runTests()); + return result; } private static String microsToSeconds(long micros) { @@ -35,7 +41,7 @@ private static class assertEquals implements Function { public Value execute(Value[] args) { Arguments.check(2, args.length); if (args[0].equals(args[1])) return NumberValue.ONE; - throw new OUnitAssertionException("Values are not equals: " + throw new OUnitAssertionException("Values are not equal: " + "1: " + args[0] + ", 2: " + args[1]); } } @@ -45,7 +51,7 @@ private static class assertNotEquals implements Function { public Value execute(Value[] args) { Arguments.check(2, args.length); if (!args[0].equals(args[1])) return NumberValue.ONE; - throw new OUnitAssertionException("Values are equals: " + args[0]); + throw new OUnitAssertionException("Values are equal: " + args[0]); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java index f79ede5b..5ef96688 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java @@ -2,11 +2,13 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; +import java.util.Map; import java.util.regex.Pattern; public final class regex implements Module { - public static void initConstants() { + @Override + public Map constants() { MapValue map = new MapValue(20); map.set("UNIX_LINES", NumberValue.of(Pattern.UNIX_LINES)); map.set("I", NumberValue.of(Pattern.CASE_INSENSITIVE)); @@ -37,13 +39,12 @@ public static void initConstants() { return ArrayValue.of(pattern.split(args[1].asString(), limit)); }); map.set("compile", regex::compile); - ScopeHandler.setConstant("Pattern", map); + return Map.of("Pattern", map); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("regex", regex::compile); + public Map functions() { + return Map.of("regex", regex::compile); } private static Value compile(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java index fc519ec3..f82a9166 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.IntConsumer; +import static java.util.Map.entry; /** * @@ -28,50 +29,42 @@ public final class robot implements Module { private static Robot awtRobot; - public static void initConstants() { - ScopeHandler.setConstant("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - ScopeHandler.setConstant("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - ScopeHandler.setConstant("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - ScopeHandler.setConstant("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - ScopeHandler.setConstant("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + @Override + public Map constants() { + return Map.ofEntries( + entry("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)), + entry("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)), + entry("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)), + entry("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)), + entry("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)), - ScopeHandler.setConstant("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); - ScopeHandler.setConstant("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); - ScopeHandler.setConstant("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)); + entry("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)), + entry("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)), + entry("BUTTON3", NumberValue.of(InputEvent.BUTTON3_MASK)) + ); } @Override - public void init() { - initConstants(); + public Map functions() { + final var result = new HashMap(16); boolean isRobotInitialized = initialize(); if (isRobotInitialized) { - ScopeHandler.setFunction("click", convertFunction(robot::click)); - ScopeHandler.setFunction("delay", convertFunction(awtRobot::delay)); - ScopeHandler.setFunction("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); - ScopeHandler.setFunction("keyPress", convertFunction(awtRobot::keyPress)); - ScopeHandler.setFunction("keyRelease", convertFunction(awtRobot::keyRelease)); - ScopeHandler.setFunction("mousePress", convertFunction(awtRobot::mousePress)); - ScopeHandler.setFunction("mouseRelease", convertFunction(awtRobot::mouseRelease)); - ScopeHandler.setFunction("mouseWheel", convertFunction(awtRobot::mouseWheel)); - ScopeHandler.setFunction("mouseMove", (args) -> { - Arguments.check(2, args.length); - try { - awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); - } catch (IllegalArgumentException iae) { } - return NumberValue.ZERO; - }); - ScopeHandler.setFunction("typeText", (args) -> { - Arguments.check(1, args.length); - try { - typeText(args[0].asString()); - } catch (IllegalArgumentException iae) { } - return NumberValue.ZERO; - }); - ScopeHandler.setFunction("toClipboard", new robot_toclipboard()); - ScopeHandler.setFunction("fromClipboard", new robot_fromclipboard()); + result.put("click", convertFunction(robot::click)); + result.put("delay", convertFunction(awtRobot::delay)); + result.put("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); + result.put("keyPress", convertFunction(awtRobot::keyPress)); + result.put("keyRelease", convertFunction(awtRobot::keyRelease)); + result.put("mousePress", convertFunction(awtRobot::mousePress)); + result.put("mouseRelease", convertFunction(awtRobot::mouseRelease)); + result.put("mouseWheel", convertFunction(awtRobot::mouseWheel)); + result.put("mouseMove", this::mouseMove); + result.put("typeText",this::typeText); + result.put("toClipboard", new robot_toclipboard()); + result.put("fromClipboard", new robot_fromclipboard()); } - ScopeHandler.setFunction("execProcess", new robot_exec(robot_exec.Mode.EXEC)); - ScopeHandler.setFunction("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); + result.put("execProcess", new robot_exec(robot_exec.Mode.EXEC)); + result.put("execProcessAndWait", new robot_exec(robot_exec.Mode.EXEC_AND_WAIT)); + return result; } private static boolean initialize() { @@ -83,6 +76,22 @@ private static boolean initialize() { return false; } } + + private Value mouseMove(Value[] args) { + Arguments.check(2, args.length); + try { + awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + } + + private Value typeText(Value[] args) { + Arguments.check(1, args.length); + try { + typeText(args[0].asString()); + } catch (IllegalArgumentException iae) { } + return NumberValue.ZERO; + } private static Function convertFunction(IntConsumer consumer) { return args -> { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java b/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java index 10b64c91..78c5ca3f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/socket/socket.java @@ -6,6 +6,8 @@ import io.socket.client.IO; import io.socket.client.Socket; import java.net.URISyntaxException; +import java.util.LinkedHashMap; +import java.util.Map; /** * socket.io module. @@ -14,27 +16,29 @@ */ public final class socket implements Module { - public static void initConstants() { - ScopeHandler.setConstant("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); - ScopeHandler.setConstant("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); - ScopeHandler.setConstant("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); - ScopeHandler.setConstant("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); - ScopeHandler.setConstant("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); - ScopeHandler.setConstant("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); - ScopeHandler.setConstant("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); - ScopeHandler.setConstant("EVENT_PING", new StringValue(Socket.EVENT_PING)); - ScopeHandler.setConstant("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); - ScopeHandler.setConstant("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); - ScopeHandler.setConstant("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); - ScopeHandler.setConstant("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); - ScopeHandler.setConstant("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); - ScopeHandler.setConstant("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); + @Override + public Map constants() { + final var result = new LinkedHashMap(15); + result.put("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); + result.put("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); + result.put("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); + result.put("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); + result.put("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); + result.put("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); + result.put("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); + result.put("EVENT_PING", new StringValue(Socket.EVENT_PING)); + result.put("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); + result.put("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); + result.put("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); + result.put("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); + result.put("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); + result.put("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); + return result; } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("newSocket", socket::newSocket); + public Map functions() { + return Map.of("newSocket", socket::newSocket); } private static Value newSocket(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java index 18e399bb..fe76b58c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -4,6 +4,8 @@ import com.annimon.ownlang.Version; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; +import java.util.Map; +import static java.util.Map.entry; /** * @@ -11,63 +13,68 @@ */ public final class std implements Module { - public static void initConstants() { + @Override + public Map constants() { MapValue ownlang = new MapValue(5); ownlang.set("PLATFORM", new StringValue("desktop")); ownlang.set("VERSION", new StringValue(Version.VERSION)); ownlang.set("VERSION_MAJOR", NumberValue.of(Version.VERSION_MAJOR)); ownlang.set("VERSION_MINOR", NumberValue.of(Version.VERSION_MINOR)); ownlang.set("VERSION_PATCH", NumberValue.of(Version.VERSION_PATCH)); - ScopeHandler.setConstant("OwnLang", ownlang); + + return Map.of( + "OwnLang", ownlang, + "ARGS", ArrayValue.of(Shared.getOwnlangArgs()) + ); } @Override - public void init() { - initConstants(); - ScopeHandler.setConstant("ARGS", ArrayValue.of(Shared.getOwnlangArgs())); // is not constant - ScopeHandler.setFunction("echo", new std_echo()); - ScopeHandler.setFunction("readln", new std_readln()); - ScopeHandler.setFunction("length", new std_length()); - ScopeHandler.setFunction("rand", new std_rand()); - ScopeHandler.setFunction("time", new std_time()); - ScopeHandler.setFunction("sleep", new std_sleep()); - ScopeHandler.setFunction("thread", new std_thread()); - ScopeHandler.setFunction("sync", new std_sync()); - ScopeHandler.setFunction("try", new std_try()); - ScopeHandler.setFunction("default", new std_default()); + public Map functions() { + return Map.ofEntries( + entry("echo", new std_echo()), + entry("readln", new std_readln()), + entry("length", new std_length()), + entry("rand", new std_rand()), + entry("time", new std_time()), + entry("sleep", new std_sleep()), + entry("thread", new std_thread()), + entry("sync", new std_sync()), + entry("try", new std_try()), + entry("default", new std_default()), - // Numbers - ScopeHandler.setFunction("toHexString", NumberFunctions::toHexString); + // Numbers + entry("toHexString", NumberFunctions::toHexString), - // String - ScopeHandler.setFunction("getBytes", StringFunctions::getBytes); - ScopeHandler.setFunction("sprintf", new std_sprintf()); - ScopeHandler.setFunction("split", new std_split()); - ScopeHandler.setFunction("indexOf", new std_indexof()); - ScopeHandler.setFunction("lastIndexOf", new std_lastindexof()); - ScopeHandler.setFunction("charAt", new std_charat()); - ScopeHandler.setFunction("toChar", new std_tochar()); - ScopeHandler.setFunction("substring", new std_substring()); - ScopeHandler.setFunction("toLowerCase", new std_tolowercase()); - ScopeHandler.setFunction("toUpperCase", new std_touppercase()); - ScopeHandler.setFunction("trim", new std_trim()); - ScopeHandler.setFunction("replace", new std_replace()); - ScopeHandler.setFunction("replaceAll", new std_replaceall()); - ScopeHandler.setFunction("replaceFirst", new std_replacefirst()); - ScopeHandler.setFunction("parseInt", StringFunctions::parseInt); - ScopeHandler.setFunction("parseLong", StringFunctions::parseLong); - ScopeHandler.setFunction("stripMargin", StringFunctions::stripMargin); + // String + entry("getBytes", StringFunctions::getBytes), + entry("sprintf", new std_sprintf()), + entry("split", new std_split()), + entry("indexOf", new std_indexof()), + entry("lastIndexOf", new std_lastindexof()), + entry("charAt", new std_charat()), + entry("toChar", new std_tochar()), + entry("substring", new std_substring()), + entry("toLowerCase", new std_tolowercase()), + entry("toUpperCase", new std_touppercase()), + entry("trim", new std_trim()), + entry("replace", new std_replace()), + entry("replaceAll", new std_replaceall()), + entry("replaceFirst", new std_replacefirst()), + entry("parseInt", StringFunctions::parseInt), + entry("parseLong", StringFunctions::parseLong), + entry("stripMargin", StringFunctions::stripMargin), - // Arrays and maps - ScopeHandler.setFunction("newarray", new std_newarray()); - ScopeHandler.setFunction("join", new std_join()); - ScopeHandler.setFunction("sort", new std_sort()); - ScopeHandler.setFunction("arrayCombine", new std_arrayCombine()); - ScopeHandler.setFunction("arrayKeyExists", new std_arrayKeyExists()); - ScopeHandler.setFunction("arrayKeys", new std_arrayKeys()); - ScopeHandler.setFunction("arrayValues", new std_arrayValues()); - ScopeHandler.setFunction("arraySplice", new std_arraySplice()); - ScopeHandler.setFunction("range", new std_range()); - ScopeHandler.setFunction("stringFromBytes", ArrayFunctions::stringFromBytes); + // Arrays and map, + entry("newarray", new std_newarray()), + entry("join", new std_join()), + entry("sort", new std_sort()), + entry("arrayCombine", new std_arrayCombine()), + entry("arrayKeyExists", new std_arrayKeyExists()), + entry("arrayKeys", new std_arrayKeys()), + entry("arrayValues", new std_arrayValues()), + entry("arraySplice", new std_arraySplice()), + entry("range", new std_range()), + entry("stringFromBytes", ArrayFunctions::stringFromBytes) + ); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java index b9614880..613080ad 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; +import java.util.Map; +import static java.util.Map.entry; /** * @@ -9,27 +11,31 @@ */ public final class types implements Module { - public static void initConstants() { - ScopeHandler.setConstant("OBJECT", NumberValue.of(Types.OBJECT)); - ScopeHandler.setConstant("NUMBER", NumberValue.of(Types.NUMBER)); - ScopeHandler.setConstant("STRING", NumberValue.of(Types.STRING)); - ScopeHandler.setConstant("ARRAY", NumberValue.of(Types.ARRAY)); - ScopeHandler.setConstant("MAP", NumberValue.of(Types.MAP)); - ScopeHandler.setConstant("FUNCTION", NumberValue.of(Types.FUNCTION)); + @Override + public Map constants() { + return Map.ofEntries( + entry("OBJECT", NumberValue.of(Types.OBJECT)), + entry("NUMBER", NumberValue.of(Types.NUMBER)), + entry("STRING", NumberValue.of(Types.STRING)), + entry("ARRAY", NumberValue.of(Types.ARRAY)), + entry("MAP", NumberValue.of(Types.MAP)), + entry("FUNCTION", NumberValue.of(Types.FUNCTION)) + ); } @Override - public void init() { - initConstants(); - ScopeHandler.setFunction("typeof", args -> NumberValue.of(args[0].type())); - ScopeHandler.setFunction("string", args -> new StringValue(args[0].asString())); - ScopeHandler.setFunction("number", args -> NumberValue.of(args[0].asNumber())); - - ScopeHandler.setFunction("byte", args -> NumberValue.of((byte)args[0].asInt())); - ScopeHandler.setFunction("short", args -> NumberValue.of((short)args[0].asInt())); - ScopeHandler.setFunction("int", args -> NumberValue.of(args[0].asInt())); - ScopeHandler.setFunction("long", args -> NumberValue.of((long)args[0].asNumber())); - ScopeHandler.setFunction("float", args -> NumberValue.of((float)args[0].asNumber())); - ScopeHandler.setFunction("double", args -> NumberValue.of(args[0].asNumber())); + public Map functions() { + return Map.ofEntries( + entry("typeof", args -> NumberValue.of(args[0].type())), + entry("string", args -> new StringValue(args[0].asString())), + entry("number", args -> NumberValue.of(args[0].asNumber())), + + entry("byte", args -> NumberValue.of((byte)args[0].asInt())), + entry("short", args -> NumberValue.of((short)args[0].asInt())), + entry("int", args -> NumberValue.of(args[0].asInt())), + entry("long", args -> NumberValue.of((long)args[0].asNumber())), + entry("float", args -> NumberValue.of((float)args[0].asNumber())), + entry("double", args -> NumberValue.of(args[0].asNumber())) + ); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java index 84f45df4..5a57cc67 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; +import java.util.Collections; +import java.util.Map; /** * @@ -10,8 +12,15 @@ public final class yaml implements Module { @Override - public void init() { - ScopeHandler.setFunction("yamlencode", new yaml_encode()); - ScopeHandler.setFunction("yamldecode", new yaml_decode()); + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + return Map.of( + "yamlencode", new yaml_encode(), + "yamldecode", new yaml_decode() + ); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java index ee18e48b..fdaacbd8 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java @@ -9,23 +9,28 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import static java.util.Map.entry; public class zip implements Module { @Override - public void init() { - ScopeHandler.setFunction("zip", this::zipWithMapper); - ScopeHandler.setFunction("zipFiles", this::zipFiles); - ScopeHandler.setFunction("unzip", this::unzip); - ScopeHandler.setFunction("unzipFiles", this::unzipFiles); - ScopeHandler.setFunction("listZipEntries", this::listZipEntries); + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + return Map.ofEntries( + entry("zip", this::zipWithMapper), + entry("zipFiles", this::zipFiles), + entry("unzip", this::unzip), + entry("unzipFiles", this::unzipFiles), + entry("listZipEntries", this::listZipEntries) + ); } private Value zipWithMapper(Value[] args) { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ModuleLoader.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ModuleLoader.java new file mode 100644 index 00000000..37e7d5fe --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ModuleLoader.java @@ -0,0 +1,28 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.modules.Module; + +public final class ModuleLoader { + private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s"; + + private ModuleLoader() { } + + public static Module load(String name) { + try { + return (Module) Class.forName(String.format(PACKAGE, name, name)) + .getDeclaredConstructor() + .newInstance(); + } catch (Exception ex) { + throw new RuntimeException("Unable to load module " + name, ex); + } + } + + public static void loadAndUse(String name) { + final var rootScope = ScopeHandler.rootScope(); + if (rootScope.isModuleLoaded(name)) return; + + final var module = load(name); + rootScope.getConstants().putAll(module.constants()); + rootScope.getFunctions().putAll(module.functions()); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java index 82ded80f..0cdcd816 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java @@ -1,17 +1,21 @@ package com.annimon.ownlang.lib; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; final class RootScope extends Scope { private final Map constants; private final Map functions; + private final Set loadedModules; RootScope() { functions = new ConcurrentHashMap<>(); constants = new ConcurrentHashMap<>(); constants.put("true", NumberValue.ONE); constants.put("false", NumberValue.ZERO); + loadedModules = new CopyOnWriteArraySet<>(); } @Override @@ -65,4 +69,17 @@ public void setFunction(String name, Function function) { public Map getFunctions() { return functions; } + + + public Set getLoadedModules() { + return loadedModules; + } + + public boolean isModuleLoaded(String name) { + return loadedModules.contains(name); + } + + public void addLoadedModule(String name) { + loadedModules.add(name); + } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java index 67debfca..9571d500 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -28,6 +28,10 @@ public static Map functions() { return rootScope.getFunctions(); } + static RootScope rootScope() { + return rootScope; + } + /** * Resets a scope for new program execution */ @@ -56,7 +60,6 @@ public static void pop() { } - public static boolean isFunctionExists(String name) { return rootScope.containsFunction(name); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java b/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java index cda5ca4f..961a3157 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java @@ -1,10 +1,16 @@ package com.annimon.ownlang.modules; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Value; +import java.util.Map; + /** - * + * Main interface for modules * @author aNNiMON */ public interface Module { - void init(); + Map constants(); + + Map functions(); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 69e30cf0..06c51653 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -1,18 +1,17 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.ModuleLoader; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; -import java.lang.reflect.Method; import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; /** * * @author aNNiMON */ public final class UseStatement extends InterruptableNode implements Statement { - - private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s"; - private static final String INIT_CONSTANTS_METHOD = "initConstants"; - public final Collection modules; public UseStatement(Collection modules) { @@ -23,35 +22,17 @@ public UseStatement(Collection modules) { public void execute() { super.interruptionCheck(); for (String module : modules) { - loadModule(module); - } - } - - private void loadModule(String name) { - try { - final Module module = (Module) Class.forName(String.format(PACKAGE, name, name)) - .getDeclaredConstructor() - .newInstance(); - module.init(); - } catch (Exception ex) { - throw new RuntimeException("Unable to load module " + name, ex); - } - } - - public void loadConstants() { - for (String module : modules) { - loadConstants(module); + ModuleLoader.loadAndUse(module); } } - private void loadConstants(String moduleName) { - try { - final Class moduleClass = Class.forName(String.format(PACKAGE, moduleName, moduleName)); - final Method method = moduleClass.getMethod(INIT_CONSTANTS_METHOD); - method.invoke(this); - } catch (Exception ex) { - // ignore + public Map loadConstants() { + final var result = new LinkedHashMap(); + for (String moduleName : modules) { + final Module module = ModuleLoader.load(moduleName); + result.putAll(module.constants()); } + return result; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 38b6c666..939cc3db 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,13 +1,11 @@ package com.annimon.ownlang.parser.optimization; -import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.parser.ast.*; -import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; -import static com.annimon.ownlang.parser.visitors.VisitorUtils.isVariable; import java.util.HashMap; import java.util.Map; +import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; +import static com.annimon.ownlang.parser.visitors.VisitorUtils.isVariable; public class VariablesGrabber extends OptimizationVisitor> { @@ -100,18 +98,11 @@ public Node visit(UnaryExpression s, Map t) { @Override public Node visit(UseStatement s, Map t) { if (grabModuleConstants) { - // To get module constants we need to store current constants, clear all, then load module. - final Map currentConstants = new HashMap<>(ScopeHandler.constants()); - ScopeHandler.constants().clear(); - s.loadConstants(); - // Grab module constants - for (Map.Entry entry : ScopeHandler.constants().entrySet()) { + for (Map.Entry entry : s.loadConstants().entrySet()) { final VariableInfo var = variableInfo(t, entry.getKey()); var.value = entry.getValue(); t.put(entry.getKey(), var); } - // Restore previous constants - ScopeHandler.constants().putAll(currentConstants); } return super.visit(s, t); } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java index d119fd33..de6447ad 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java @@ -28,16 +28,12 @@ public static void main(String[] args) .map(File::getName) .toArray(String[]::new); for (String moduleName : moduleNames) { - final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName); - Class moduleClass = Class.forName(moduleClassPath); - ScopeHandler.resetScope(); - final Module module = (Module) moduleClass.getDeclaredConstructor().newInstance(); - module.init(); + final Module module = ModuleLoader.load(moduleName); final ModuleInfo moduleInfo = new ModuleInfo(moduleName); - moduleInfo.functions.addAll(ScopeHandler.functions().keySet()); - moduleInfo.constants.putAll(ScopeHandler.constants()); - moduleInfo.types.addAll(listValues(moduleClass)); + moduleInfo.functions.addAll(module.functions().keySet()); + moduleInfo.constants.putAll(module.constants()); + moduleInfo.types.addAll(listValues(module.getClass())); moduleInfos.add(moduleInfo); } From 90db2b0aa33f5dee9eb4d93aa51de4e395aad980 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 9 Sep 2023 21:05:17 +0300 Subject: [PATCH 306/448] Fix concurrent modification exception in ounit, when new modules are loaded in test --- .../annimon/ownlang/modules/ounit/ounit.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java index 07463c41..c087230f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -88,11 +88,13 @@ private static class runTests implements Function { @Override public Value execute(Value[] args) { - List tests = Functions.getFunctions().entrySet().stream() + final var testFunctions = ScopeHandler.functions().entrySet().stream() .filter(e -> e.getKey().toLowerCase().startsWith("test")) + .toList(); + List tests = testFunctions.stream() .map(e -> runTest(e.getKey(), e.getValue())) .toList(); - + int failures = 0; long summaryTime = 0; final StringBuilder result = new StringBuilder(); @@ -132,20 +134,13 @@ public OUnitAssertionException(String message) { super(message); } } - - private static class TestInfo { - final String name; - final boolean isPassed; - final String failureDescription; - final long elapsedTimeInMicros; - public TestInfo(String name, boolean isPassed, String failureDescription, long elapsedTimeInMicros) { - this.name = name; - this.isPassed = isPassed; - this.failureDescription = failureDescription; - this.elapsedTimeInMicros = elapsedTimeInMicros; - } - + private record TestInfo( + String name, + boolean isPassed, + String failureDescription, + long elapsedTimeInMicros + ) { public String info() { return String.format("%s [%s]\n%sElapsed: %s\n", name, From fc73bce943e6a918f2f1a28801b0bed5fff4232e Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 9 Sep 2023 21:58:32 +0300 Subject: [PATCH 307/448] Add columns in position information --- .../ownlang/exceptions/LexerException.java | 8 +++++--- .../com/annimon/ownlang/parser/Lexer.java | 4 ++-- .../annimon/ownlang/parser/ParseError.java | 20 ++----------------- .../annimon/ownlang/parser/ParseErrors.java | 4 ++-- .../com/annimon/ownlang/parser/Parser.java | 12 +++++------ .../java/com/annimon/ownlang/parser/Pos.java | 12 +++++++++++ .../com/annimon/ownlang/parser/Token.java | 8 ++------ .../com/annimon/ownlang/parser/LexerTest.java | 6 +++--- .../java/com/annimon/ownlang/utils/Repl.java | 7 ++----- 9 files changed, 36 insertions(+), 45 deletions(-) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java index 26689e14..383e01f9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.parser.Pos; + /** * * @author aNNiMON @@ -9,8 +11,8 @@ public final class LexerException extends RuntimeException { public LexerException(String message) { super(message); } - - public LexerException(int row, int col, String message) { - super("["+row+":"+col+"] " + message); + + public LexerException(Pos pos, String message) { + super(pos.format() + " " + message); } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 4bed5181..746c5f0f 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -357,10 +357,10 @@ private void addToken(TokenType type) { } private void addToken(TokenType type, String text) { - tokens.add(new Token(type, text, row, col)); + tokens.add(new Token(type, text, new Pos(row, col))); } private LexerException error(String text) { - return new LexerException(row, col, text); + return new LexerException(new Pos(row, col), text); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java index 7506552a..b2e2e27d 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java @@ -1,25 +1,9 @@ package com.annimon.ownlang.parser; -public final class ParseError { - - private final int line; - private final Exception exception; - - public ParseError(int line, Exception exception) { - this.line = line; - this.exception = exception; - } - - public int getLine() { - return line; - } - - public Exception getException() { - return exception; - } +public record ParseError(Exception exception, Pos pos) { @Override public String toString() { - return "ParseError on line " + line + ": " + exception.getMessage(); + return "ParseError on line " + pos.row() + ": " + exception; } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java index 83873491..4d1c44f0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java @@ -17,8 +17,8 @@ public void clear() { errors.clear(); } - public void add(Exception ex, int line) { - errors.add(new ParseError(line, ex)); + public void add(Exception ex, Pos pos) { + errors.add(new ParseError(ex, pos)); } public boolean hasErrors() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 626b0f56..aa6a0a09 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -22,7 +22,7 @@ public static Statement parse(List tokens) { return program; } - private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); + private static final Token EOF = new Token(TokenType.EOF, "", new Pos(-1, -1)); private static final EnumMap ASSIGN_OPERATORS; static { @@ -71,7 +71,7 @@ public Statement parse() { try { result.add(statement()); } catch (Exception ex) { - parseErrors.add(ex, getErrorLine()); + parseErrors.add(ex, getErrorPos()); recover(); } } @@ -79,10 +79,10 @@ public Statement parse() { return result; } - private int getErrorLine() { - if (size == 0) return 0; - if (pos >= size) return tokens.get(size - 1).row(); - return tokens.get(pos).row(); + private Pos getErrorPos() { + if (size == 0) return new Pos(0, 0); + if (pos >= size) return tokens.get(size - 1).pos(); + return tokens.get(pos).pos(); } private void recover() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java new file mode 100644 index 00000000..75bf8b99 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.parser; + +public record Pos(int row, int col) { + + public Pos normalize() { + return new Pos(Math.max(0, row - 1), Math.max(0, col - 1)); + } + + public String format() { + return "[" + row + ":" + col + "]"; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java index c9704cb8..a271d220 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java @@ -3,14 +3,10 @@ /** * @author aNNiMON */ -public record Token(TokenType type, String text, int row, int col) { - - public String position() { - return "[" + row + " " + col + "]"; - } +public record Token(TokenType type, String text, Pos pos) { @Override public String toString() { - return type.name() + " " + position() + " " + text; + return type.name() + " " + pos().format() + " " + text; } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index 0f05acb1..6e081d36 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -172,11 +172,11 @@ private static List list(TokenType... types) { } private static Token token(TokenType type) { - return token(type, "", 0, 0); + return token(type, "", new Pos(0, 0)); } - private static Token token(TokenType type, String text, int row, int col) { - return new Token(type, text, row, col); + private static Token token(TokenType type, String text, Pos pos) { + return new Token(type, text, pos); } } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 10ee60ee..1e384820 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -7,10 +7,7 @@ import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.lib.Variables; -import com.annimon.ownlang.parser.Lexer; -import com.annimon.ownlang.parser.Parser; -import com.annimon.ownlang.parser.Token; -import com.annimon.ownlang.parser.TokenType; +import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.PrintVisitor; @@ -36,7 +33,7 @@ public final class Repl { RESET = ":reset", EXIT = ":exit"; - private static final Token PRINTLN_TOKEN = new Token(TokenType.PRINTLN, "", 0, 0); + private static final Token PRINTLN_TOKEN = new Token(TokenType.PRINTLN, "", new Pos(0, 0)); public static void main(String[] args) { System.out.println("Welcome to OwnLang " + Version.VERSION + " REPL"); From 7baf9f6fc8028539c02f80aec2323ed687802006 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 11 Sep 2023 19:15:01 +0300 Subject: [PATCH 308/448] Fix incorrect token positions in lexer --- build.gradle | 3 +- ownlang-parser/build.gradle | 1 + .../com/annimon/ownlang/parser/Lexer.java | 54 +++++++++++-------- .../ownlang/parser/LexerPositionsTest.java | 44 +++++++++++++++ 4 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java diff --git a/build.gradle b/build.gradle index 1c08a8a6..8c65cb39 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,8 @@ ext { jline: '2.14.5', // jline:jline junit: '5.9.2', // org.junit:junit-bom - jmh: '1.37' // org.openjdk.jmh:jmh-core + jmh: '1.37', // org.openjdk.jmh:jmh-core + assertj: '3.24.2' // org.assertj:assertj-core ] } diff --git a/ownlang-parser/build.gradle b/ownlang-parser/build.gradle index 0354e96c..b411a92c 100644 --- a/ownlang-parser/build.gradle +++ b/ownlang-parser/build.gradle @@ -12,6 +12,7 @@ dependencies { testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation "org.junit.jupiter:junit-jupiter-params:${versions.junit}" testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation("org.assertj:assertj-core:${versions.assertj}") testImplementation "org.openjdk.jmh:jmh-core:${versions.jmh}" testImplementation "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" testAnnotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 746c5f0f..7a121306 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -138,10 +138,7 @@ public List tokenize() { else if (isOwnLangIdentifierStart(current)) tokenizeWord(); else if (current == '`') tokenizeExtendedWord(); else if (current == '"') tokenizeText(); - else if (current == '#') { - next(); - tokenizeHexNumber(1); - } + else if (current == '#') tokenizeHexNumber(1); else if (OPERATOR_CHARS.indexOf(current) != -1) { tokenizeOperator(); } else { @@ -154,10 +151,9 @@ else if (OPERATOR_CHARS.indexOf(current) != -1) { private void tokenizeNumber() { clearBuffer(); + final Pos startPos = markPos(); char current = peek(0); if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { - next(); - next(); tokenizeHexNumber(2); return; } @@ -170,11 +166,15 @@ private void tokenizeNumber() { buffer.append(current); current = next(); } - addToken(TokenType.NUMBER, buffer.toString()); + addToken(TokenType.NUMBER, buffer.toString(), startPos); } - private void tokenizeHexNumber(int skipped) { + private void tokenizeHexNumber(int skipChars) { clearBuffer(); + final Pos startPos = markPos(); + // Skip HEX prefix 0x or # + for (int i = 0; i < skipChars; i++) next(); + char current = peek(0); while (isHexNumber(current) || (current == '_')) { if (current != '_') { @@ -185,7 +185,7 @@ private void tokenizeHexNumber(int skipped) { } final int length = buffer.length(); if (length > 0) { - addToken(TokenType.HEX_NUMBER, buffer.toString()); + addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); } } @@ -210,11 +210,13 @@ private void tokenizeOperator() { return; } } + + final Pos startPos = markPos(); clearBuffer(); while (true) { final String text = buffer.toString(); if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) { - addToken(OPERATORS.get(text)); + addToken(OPERATORS.get(text), startPos); return; } buffer.append(current); @@ -224,6 +226,7 @@ private void tokenizeOperator() { private void tokenizeWord() { clearBuffer(); + final Pos startPos = markPos(); buffer.append(peek(0)); char current = next(); while (true) { @@ -236,13 +239,14 @@ private void tokenizeWord() { final String word = buffer.toString(); if (KEYWORDS.containsKey(word)) { - addToken(KEYWORDS.get(word)); + addToken(KEYWORDS.get(word), startPos); } else { - addToken(TokenType.WORD, word); + addToken(TokenType.WORD, word, startPos); } } private void tokenizeExtendedWord() { + final Pos startPos = markPos(); next();// skip ` clearBuffer(); char current = peek(0); @@ -254,10 +258,11 @@ private void tokenizeExtendedWord() { current = next(); } next(); // skip closing ` - addToken(TokenType.WORD, buffer.toString()); + addToken(TokenType.WORD, buffer.toString(), startPos); } private void tokenizeText() { + final Pos startPos = markPos(); next();// skip " clearBuffer(); char current = peek(0); @@ -303,7 +308,7 @@ private void tokenizeText() { } next(); // skip closing " - addToken(TokenType.TEXT, buffer.toString()); + addToken(TokenType.TEXT, buffer.toString(), startPos); } private void tokenizeComment() { @@ -335,15 +340,20 @@ private boolean isOwnLangIdentifierPart(char current) { private void clearBuffer() { buffer.setLength(0); } + + private Pos markPos() { + return new Pos(row, col); + } private char next() { - pos++; final char result = peek(0); if (result == '\n') { row++; col = 1; } else col++; - return result; + + pos++; + return peek(0); } private char peek(int relativePosition) { @@ -352,15 +362,15 @@ private char peek(int relativePosition) { return input.charAt(position); } - private void addToken(TokenType type) { - addToken(type, ""); + private void addToken(TokenType type, Pos startPos) { + addToken(type, "", startPos); } - private void addToken(TokenType type, String text) { - tokens.add(new Token(type, text, new Pos(row, col))); + private void addToken(TokenType type, String text, Pos startRow) { + tokens.add(new Token(type, text, startRow)); } - + private LexerException error(String text) { - return new LexerException(new Pos(row, col), text); + return new LexerException(markPos(), text); } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java new file mode 100644 index 00000000..900eaae2 --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java @@ -0,0 +1,44 @@ +package com.annimon.ownlang.parser; + +import org.junit.jupiter.api.Test; +import java.util.List; +import static com.annimon.ownlang.parser.TokenType.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +class LexerPositionsTest { + + @Test + void testMultiline() { + String input = """ + x = 123 + y = "abc" + """.stripIndent(); + List result = Lexer.tokenize(input); + + assertThat(result) + .hasSize(6) + .extracting(s -> s.pos().row(), s -> s.pos().col(), Token::type) + .containsExactly( + tuple(1, 1, WORD), tuple(1, 3, EQ), tuple(1, 5, NUMBER), + tuple(2, 1, WORD), tuple(2, 3, EQ), tuple(2, 5, TEXT) + ); + } + + @Test + void testMultilineText() { + String input = """ + text = "line1 + line2 + line3" + """.stripIndent(); + List result = Lexer.tokenize(input); + + assertThat(result) + .hasSize(3) + .extracting(s -> s.pos().row(), s -> s.pos().col(), Token::type) + .containsExactly( + tuple(1, 1, WORD), tuple(1, 6, EQ), tuple(1, 8, TEXT) + ); + } +} \ No newline at end of file From 15c277d145c1fd255d02507ed8461c83d1fd45ec Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 11 Sep 2023 19:57:01 +0300 Subject: [PATCH 309/448] Use immutable map, fast skip whitespaces in lexer --- .../com/annimon/ownlang/parser/Lexer.java | 184 +++++++++--------- 1 file changed, 91 insertions(+), 93 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 7a121306..3a15e61c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -1,11 +1,7 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.exceptions.LexerException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @@ -21,92 +17,94 @@ public static List tokenize(String input) { private static final Map OPERATORS; static { - OPERATORS = new HashMap<>(); - OPERATORS.put("+", TokenType.PLUS); - OPERATORS.put("-", TokenType.MINUS); - OPERATORS.put("*", TokenType.STAR); - OPERATORS.put("/", TokenType.SLASH); - OPERATORS.put("%", TokenType.PERCENT); - OPERATORS.put("(", TokenType.LPAREN); - OPERATORS.put(")", TokenType.RPAREN); - OPERATORS.put("[", TokenType.LBRACKET); - OPERATORS.put("]", TokenType.RBRACKET); - OPERATORS.put("{", TokenType.LBRACE); - OPERATORS.put("}", TokenType.RBRACE); - OPERATORS.put("=", TokenType.EQ); - OPERATORS.put("<", TokenType.LT); - OPERATORS.put(">", TokenType.GT); - OPERATORS.put(".", TokenType.DOT); - OPERATORS.put(",", TokenType.COMMA); - OPERATORS.put("^", TokenType.CARET); - OPERATORS.put("~", TokenType.TILDE); - OPERATORS.put("?", TokenType.QUESTION); - OPERATORS.put(":", TokenType.COLON); + final var operators = new HashMap(); + operators.put("+", TokenType.PLUS); + operators.put("-", TokenType.MINUS); + operators.put("*", TokenType.STAR); + operators.put("/", TokenType.SLASH); + operators.put("%", TokenType.PERCENT); + operators.put("(", TokenType.LPAREN); + operators.put(")", TokenType.RPAREN); + operators.put("[", TokenType.LBRACKET); + operators.put("]", TokenType.RBRACKET); + operators.put("{", TokenType.LBRACE); + operators.put("}", TokenType.RBRACE); + operators.put("=", TokenType.EQ); + operators.put("<", TokenType.LT); + operators.put(">", TokenType.GT); + operators.put(".", TokenType.DOT); + operators.put(",", TokenType.COMMA); + operators.put("^", TokenType.CARET); + operators.put("~", TokenType.TILDE); + operators.put("?", TokenType.QUESTION); + operators.put(":", TokenType.COLON); - OPERATORS.put("!", TokenType.EXCL); - OPERATORS.put("&", TokenType.AMP); - OPERATORS.put("|", TokenType.BAR); + operators.put("!", TokenType.EXCL); + operators.put("&", TokenType.AMP); + operators.put("|", TokenType.BAR); - OPERATORS.put("==", TokenType.EQEQ); - OPERATORS.put("!=", TokenType.EXCLEQ); - OPERATORS.put("<=", TokenType.LTEQ); - OPERATORS.put(">=", TokenType.GTEQ); + operators.put("==", TokenType.EQEQ); + operators.put("!=", TokenType.EXCLEQ); + operators.put("<=", TokenType.LTEQ); + operators.put(">=", TokenType.GTEQ); - OPERATORS.put("+=", TokenType.PLUSEQ); - OPERATORS.put("-=", TokenType.MINUSEQ); - OPERATORS.put("*=", TokenType.STAREQ); - OPERATORS.put("/=", TokenType.SLASHEQ); - OPERATORS.put("%=", TokenType.PERCENTEQ); - OPERATORS.put("&=", TokenType.AMPEQ); - OPERATORS.put("^=", TokenType.CARETEQ); - OPERATORS.put("|=", TokenType.BAREQ); - OPERATORS.put("::=", TokenType.COLONCOLONEQ); - OPERATORS.put("<<=", TokenType.LTLTEQ); - OPERATORS.put(">>=", TokenType.GTGTEQ); - OPERATORS.put(">>>=", TokenType.GTGTGTEQ); + operators.put("+=", TokenType.PLUSEQ); + operators.put("-=", TokenType.MINUSEQ); + operators.put("*=", TokenType.STAREQ); + operators.put("/=", TokenType.SLASHEQ); + operators.put("%=", TokenType.PERCENTEQ); + operators.put("&=", TokenType.AMPEQ); + operators.put("^=", TokenType.CARETEQ); + operators.put("|=", TokenType.BAREQ); + operators.put("::=", TokenType.COLONCOLONEQ); + operators.put("<<=", TokenType.LTLTEQ); + operators.put(">>=", TokenType.GTGTEQ); + operators.put(">>>=", TokenType.GTGTGTEQ); - OPERATORS.put("++", TokenType.PLUSPLUS); - OPERATORS.put("--", TokenType.MINUSMINUS); + operators.put("++", TokenType.PLUSPLUS); + operators.put("--", TokenType.MINUSMINUS); - OPERATORS.put("::", TokenType.COLONCOLON); + operators.put("::", TokenType.COLONCOLON); - OPERATORS.put("&&", TokenType.AMPAMP); - OPERATORS.put("||", TokenType.BARBAR); + operators.put("&&", TokenType.AMPAMP); + operators.put("||", TokenType.BARBAR); - OPERATORS.put("<<", TokenType.LTLT); - OPERATORS.put(">>", TokenType.GTGT); - OPERATORS.put(">>>", TokenType.GTGTGT); + operators.put("<<", TokenType.LTLT); + operators.put(">>", TokenType.GTGT); + operators.put(">>>", TokenType.GTGTGT); - OPERATORS.put("@", TokenType.AT); - OPERATORS.put("@=", TokenType.ATEQ); - OPERATORS.put("..", TokenType.DOTDOT); - OPERATORS.put("**", TokenType.STARSTAR); - OPERATORS.put("^^", TokenType.CARETCARET); - OPERATORS.put("?:", TokenType.QUESTIONCOLON); - OPERATORS.put("??", TokenType.QUESTIONQUESTION); + operators.put("@", TokenType.AT); + operators.put("@=", TokenType.ATEQ); + operators.put("..", TokenType.DOTDOT); + operators.put("**", TokenType.STARSTAR); + operators.put("^^", TokenType.CARETCARET); + operators.put("?:", TokenType.QUESTIONCOLON); + operators.put("??", TokenType.QUESTIONQUESTION); + OPERATORS = Map.copyOf(operators); } private static final Map KEYWORDS; static { - KEYWORDS = new HashMap<>(); - KEYWORDS.put("print", TokenType.PRINT); - KEYWORDS.put("println", TokenType.PRINTLN); - KEYWORDS.put("if", TokenType.IF); - KEYWORDS.put("else", TokenType.ELSE); - KEYWORDS.put("while", TokenType.WHILE); - KEYWORDS.put("for", TokenType.FOR); - KEYWORDS.put("do", TokenType.DO); - KEYWORDS.put("break", TokenType.BREAK); - KEYWORDS.put("continue", TokenType.CONTINUE); - KEYWORDS.put("def", TokenType.DEF); - KEYWORDS.put("return", TokenType.RETURN); - KEYWORDS.put("use", TokenType.USE); - KEYWORDS.put("match", TokenType.MATCH); - KEYWORDS.put("case", TokenType.CASE); - KEYWORDS.put("extract", TokenType.EXTRACT); - KEYWORDS.put("include", TokenType.INCLUDE); - KEYWORDS.put("class", TokenType.CLASS); - KEYWORDS.put("new", TokenType.NEW); + final var keywords = new HashMap(); + keywords.put("print", TokenType.PRINT); + keywords.put("println", TokenType.PRINTLN); + keywords.put("if", TokenType.IF); + keywords.put("else", TokenType.ELSE); + keywords.put("while", TokenType.WHILE); + keywords.put("for", TokenType.FOR); + keywords.put("do", TokenType.DO); + keywords.put("break", TokenType.BREAK); + keywords.put("continue", TokenType.CONTINUE); + keywords.put("def", TokenType.DEF); + keywords.put("return", TokenType.RETURN); + keywords.put("use", TokenType.USE); + keywords.put("match", TokenType.MATCH); + keywords.put("case", TokenType.CASE); + keywords.put("extract", TokenType.EXTRACT); + keywords.put("include", TokenType.INCLUDE); + keywords.put("class", TokenType.CLASS); + keywords.put("new", TokenType.NEW); + KEYWORDS = Map.copyOf(keywords); } public static Set getKeywords() { @@ -133,6 +131,11 @@ public Lexer(String input) { public List tokenize() { while (pos < length) { + // Fast path for skipping whitespaces + while (Character.isWhitespace(peek(0))) { + next(); + } + final char current = peek(0); if (Character.isDigit(current)) tokenizeNumber(); else if (isOwnLangIdentifierStart(current)) tokenizeWord(); @@ -157,9 +160,11 @@ private void tokenizeNumber() { tokenizeHexNumber(2); return; } + boolean hasDot = false; while (true) { if (current == '.') { - if (buffer.indexOf(".") != -1) throw error("Invalid float number"); + if (hasDot) throw error("Invalid float number"); + hasDot = true; } else if (!Character.isDigit(current)) { break; } @@ -183,8 +188,7 @@ private void tokenizeHexNumber(int skipChars) { } current = next(); } - final int length = buffer.length(); - if (length > 0) { + if (!buffer.isEmpty()) { addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); } } @@ -214,9 +218,8 @@ private void tokenizeOperator() { final Pos startPos = markPos(); clearBuffer(); while (true) { - final String text = buffer.toString(); - if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) { - addToken(OPERATORS.get(text), startPos); + if (!buffer.isEmpty() && !OPERATORS.containsKey(buffer.toString() + current)) { + addToken(OPERATORS.get(buffer.toString()), startPos); return; } buffer.append(current); @@ -229,10 +232,7 @@ private void tokenizeWord() { final Pos startPos = markPos(); buffer.append(peek(0)); char current = next(); - while (true) { - if (!isOwnLangIdentifierPart(current)) { - break; - } + while (isOwnLangIdentifierPart(current)) { buffer.append(current); current = next(); } @@ -250,8 +250,7 @@ private void tokenizeExtendedWord() { next();// skip ` clearBuffer(); char current = peek(0); - while (true) { - if (current == '`') break; + while (current != '`') { if (current == '\0') throw error("Reached end of file while parsing extended word."); if (current == '\n' || current == '\r') throw error("Reached end of line while parsing extended word."); buffer.append(current); @@ -320,8 +319,7 @@ private void tokenizeComment() { private void tokenizeMultilineComment() { char current = peek(0); - while (true) { - if (current == '*' && peek(1) == '/') break; + while (current != '*' || peek(1) != '/') { if (current == '\0') throw error("Reached end of file while parsing multiline comment"); current = next(); } From 2c0b19eb0aae48f28735319a0634c3d0eb9b4b13 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 16 Sep 2023 20:12:01 +0300 Subject: [PATCH 310/448] More strict lexer, fixed HEX numbers and quote escaping --- .../com/annimon/ownlang/parser/Lexer.java | 71 ++++--- .../ownlang/parser/LexerPositionsTest.java | 22 +- .../com/annimon/ownlang/parser/LexerTest.java | 195 ++++++------------ .../parser/LexerValidDataProvider.java | 91 ++++++++ 4 files changed, 212 insertions(+), 167 deletions(-) create mode 100644 ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 3a15e61c..48efd156 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -133,21 +133,20 @@ public List tokenize() { while (pos < length) { // Fast path for skipping whitespaces while (Character.isWhitespace(peek(0))) { - next(); + skip(); } final char current = peek(0); - if (Character.isDigit(current)) tokenizeNumber(); + if (isNumber(current)) tokenizeNumber(); else if (isOwnLangIdentifierStart(current)) tokenizeWord(); - else if (current == '`') tokenizeExtendedWord(); else if (current == '"') tokenizeText(); + else if (OPERATOR_CHARS.indexOf(current) != -1) tokenizeOperator(); + else if (Character.isWhitespace(current)) skip(); + else if (current == '`') tokenizeExtendedWord(); else if (current == '#') tokenizeHexNumber(1); - else if (OPERATOR_CHARS.indexOf(current) != -1) { - tokenizeOperator(); - } else { - // whitespaces - next(); - } + else if (current == ';') skip(); // ignore semicolon + else if (current == '\0') break; + else throw error("Unknown token " + current); } return tokens; } @@ -163,7 +162,7 @@ private void tokenizeNumber() { boolean hasDot = false; while (true) { if (current == '.') { - if (hasDot) throw error("Invalid float number"); + if (hasDot) throw error("Invalid float number " + buffer); hasDot = true; } else if (!Character.isDigit(current)) { break; @@ -178,7 +177,7 @@ private void tokenizeHexNumber(int skipChars) { clearBuffer(); final Pos startPos = markPos(); // Skip HEX prefix 0x or # - for (int i = 0; i < skipChars; i++) next(); + for (int i = 0; i < skipChars; i++) skip(); char current = peek(0); while (isHexNumber(current) || (current == '_')) { @@ -188,13 +187,18 @@ private void tokenizeHexNumber(int skipChars) { } current = next(); } - if (!buffer.isEmpty()) { - addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); - } + + if (buffer.isEmpty()) throw error("Empty HEX value"); + if (peek(-1) == '_') throw error("HEX value cannot end with _"); + addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); + } + + private static boolean isNumber(char current) { + return ('0' <= current && current <= '9'); } private static boolean isHexNumber(char current) { - return Character.isDigit(current) + return ('0' <= current && current <= '9') || ('a' <= current && current <= 'f') || ('A' <= current && current <= 'F'); } @@ -203,13 +207,9 @@ private void tokenizeOperator() { char current = peek(0); if (current == '/') { if (peek(1) == '/') { - next(); - next(); tokenizeComment(); return; } else if (peek(1) == '*') { - next(); - next(); tokenizeMultilineComment(); return; } @@ -247,7 +247,7 @@ private void tokenizeWord() { private void tokenizeExtendedWord() { final Pos startPos = markPos(); - next();// skip ` + skip();// skip ` clearBuffer(); char current = peek(0); while (current != '`') { @@ -256,19 +256,20 @@ private void tokenizeExtendedWord() { buffer.append(current); current = next(); } - next(); // skip closing ` + skip(); // skip closing ` addToken(TokenType.WORD, buffer.toString(), startPos); } private void tokenizeText() { final Pos startPos = markPos(); - next();// skip " + skip();// skip " clearBuffer(); char current = peek(0); while (true) { if (current == '\\') { current = next(); switch (current) { + case '\\': current = next(); buffer.append('\\'); continue; case '"': current = next(); buffer.append('"'); continue; case '0': current = next(); buffer.append('\0'); continue; case 'b': current = next(); buffer.append('\b'); continue; @@ -305,12 +306,14 @@ private void tokenizeText() { buffer.append(current); current = next(); } - next(); // skip closing " + skip(); // skip closing " addToken(TokenType.TEXT, buffer.toString(), startPos); } private void tokenizeComment() { + skip(); // / + skip(); // / char current = peek(0); while ("\r\n\0".indexOf(current) == -1) { current = next(); @@ -318,13 +321,15 @@ private void tokenizeComment() { } private void tokenizeMultilineComment() { + skip(); // / + skip(); // * char current = peek(0); while (current != '*' || peek(1) != '/') { if (current == '\0') throw error("Reached end of file while parsing multiline comment"); current = next(); } - next(); // * - next(); // / + skip(); // * + skip(); // / } private boolean isOwnLangIdentifierStart(char current) { @@ -332,7 +337,7 @@ private boolean isOwnLangIdentifierStart(char current) { } private boolean isOwnLangIdentifierPart(char current) { - return (Character.isLetterOrDigit(current) || (current == '_') || (current == '$')); + return isOwnLangIdentifierStart(current) || isNumber(current); } private void clearBuffer() { @@ -342,18 +347,22 @@ private void clearBuffer() { private Pos markPos() { return new Pos(row, col); } - - private char next() { - final char result = peek(0); + + private void skip() { + if (pos >= length) return; + final char result = input.charAt(pos); if (result == '\n') { row++; col = 1; } else col++; - pos++; - return peek(0); } + private char next() { + skip(); + return peek(0); + } + private char peek(int relativePosition) { final int position = pos + relativePosition; if (position >= length) return '\0'; diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java index 900eaae2..5f0c7993 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java @@ -31,6 +31,26 @@ void testMultilineText() { text = "line1 line2 line3" + a = 3 + """.stripIndent(); + List result = Lexer.tokenize(input); + + assertThat(result) + .hasSize(6) + .extracting(s -> s.pos().row(), s -> s.pos().col(), Token::type) + .containsExactly( + tuple(1, 1, WORD), tuple(1, 6, EQ), tuple(1, 8, TEXT), + tuple(4, 1, WORD), tuple(4, 3, EQ), tuple(4, 5, NUMBER) + ); + } + + @Test + void testMultilineComment() { + String input = """ + /* + line2 + line*/a =/* + */3 """.stripIndent(); List result = Lexer.tokenize(input); @@ -38,7 +58,7 @@ void testMultilineText() { .hasSize(3) .extracting(s -> s.pos().row(), s -> s.pos().col(), Token::type) .containsExactly( - tuple(1, 1, WORD), tuple(1, 6, EQ), tuple(1, 8, TEXT) + tuple(3, 9, WORD), tuple(3, 11, EQ), tuple(4, 3, NUMBER) ); } } \ No newline at end of file diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index 6e081d36..c3e97852 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -2,181 +2,106 @@ import com.annimon.ownlang.exceptions.LexerException; import org.junit.jupiter.api.Test; - -import java.util.ArrayList; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import java.io.IOException; import java.util.List; - +import java.util.stream.Stream; import static com.annimon.ownlang.parser.TokenType.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * * @author aNNiMON */ public class LexerTest { - - @Test - public void testNumbers() { - String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF #"; - List expList = list(NUMBER, NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("0", result.get(0).text()); - assertEquals("3.1415", result.get(1).text()); - assertEquals("CAFEBABE", result.get(2).text()); - assertEquals("f7d6c5", result.get(3).text()); - } - - @Test - public void testNumbersError() { - final String input = "3.14.15 0Xf7_p6_s5"; - assertThrows(LexerException.class, () -> Lexer.tokenize(input)); - } - - @Test - public void testArithmetic() { - String input = "x = -1 + 2 * 3 % 4 / 5"; - List expList = list(WORD, EQ, MINUS, NUMBER, PLUS, NUMBER, STAR, NUMBER, PERCENT, NUMBER, SLASH, NUMBER); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("x", result.get(0).text()); + + public static Stream validData() { + return LexerValidDataProvider.getAll(); } - - @Test - public void testKeywords() { - String input = "if else while for include"; - List expList = list(IF, ELSE, WHILE, FOR, INCLUDE); - List result = Lexer.tokenize(input); - assertTokens(expList, result); + + public static Stream invalidData() { + return Stream.builder() + .add(Arguments.of("Wrong float point", "3.14.15")) + .add(Arguments.of("Wrong HEX number", "0Xf7_p6_s5")) + .add(Arguments.of("HEX number ends with _", "0Xf7_")) + .add(Arguments.of("Empty rest of HEX number", "#")) + .add(Arguments.of("Unicode character identifier", "€ = 1")) + .add(Arguments.of("Unicode character only", "€")) + .add(Arguments.of("String error", "\"1\"\"")) + .add(Arguments.of("Multiline comment EOF", "/* 1234 \n")) + .add(Arguments.of("Extended word EOF", "` 1234")) + .build(); } @Test - public void testWord() { - String input = "if bool include \"text\n\ntext\""; - List expList = list(IF, WORD, INCLUDE, TEXT); + public void testNumbers() { + String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF"; List result = Lexer.tokenize(input); - assertTokens(expList, result); + assertTokens(result, NUMBER, NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); + assertThat(result) + .extracting(Token::text) + .containsExactly("0", "3.1415", "CAFEBABE", "f7d6c5", "FFFF"); } @Test public void testString() { String input = "\"1\\\"2\""; - List expList = list(TEXT); List result = Lexer.tokenize(input); - assertTokens(expList, result); + assertTokens(result, TEXT); assertEquals("1\"2", result.get(0).text()); } - - @Test - public void testEmptyString() { - String input = "\"\""; - List expList = list(TEXT); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("", result.get(0).text()); - } - - @Test - public void testStringError() { - String input = "\"1\"\""; - List expList = list(TEXT); - assertThrows(LexerException.class, () -> { - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("1", result.get(0).text()); - }); - } - + @Test - public void testOperators() { - String input = "=+-*/%<>!&|"; - List expList = list(EQ, PLUS, MINUS, STAR, SLASH, PERCENT, LT, GT, EXCL, AMP, BAR); + public void testEscapeString() { + String input = """ + "\\\\/\\\\" + """.stripIndent(); List result = Lexer.tokenize(input); - assertTokens(expList, result); + assertTokens(result, TEXT); + assertEquals("\\/\\", result.get(0).text()); } @Test - public void testOperators2Char() { - String input = "== != <= >= && || ==+ >=- ->"; - List expList = list(EQEQ, EXCLEQ, LTEQ, GTEQ, AMPAMP, BARBAR, - EQEQ, PLUS, GTEQ, MINUS, MINUS, GT); + public void testEmptyString() { + String input = "\"\""; List result = Lexer.tokenize(input); - assertTokens(expList, result); + assertTokens(result, TEXT); + assertEquals("", result.get(0).text()); } @Test public void testComments() { String input = "// 1234 \n /* */ 123 /* \n 12345 \n\n\n */"; - List expList = list(NUMBER); List result = Lexer.tokenize(input); - assertTokens(expList, result); + assertTokens(result, NUMBER); assertEquals("123", result.get(0).text()); } - - @Test - public void testComments2() { - String input = "// /* 1234 \n */"; - List expList = list(STAR, SLASH); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - public void testCommentsError() { - final String input = "/* 1234 \n"; - assertThrows(LexerException.class, () -> Lexer.tokenize(input)); - } - - @Test - public void testExtendedWordError() { - final String input = "` 1234"; - assertThrows(LexerException.class, () -> Lexer.tokenize(input)); - } - @Test - public void testUnicodeCharacterIdentifier() { - String input = "€ = 1"; - List expList = list(EQ, NUMBER); + @ParameterizedTest + @MethodSource("validData") + public void testValidInput(String name, String input, List tokenTypes) throws IOException { List result = Lexer.tokenize(input); - assertTokens(expList, result); + assertThat(result) + .hasSize(tokenTypes.size()) + .extracting(Token::type) + .containsAll(tokenTypes); } - @Test - public void testUnicodeCharacterExtendedWordIdentifier() { - String input = "`€` = 1"; - List expList = list(WORD, EQ, NUMBER); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - public void testUnicodeCharacterEOF() { - String input = "€"; - assertTrue(Lexer.tokenize(input).isEmpty()); + @ParameterizedTest + @MethodSource("invalidData") + public void testInvalidInput(String name, String input) throws IOException { + assertThatThrownBy(() -> Lexer.tokenize(input)) + .isInstanceOf(LexerException.class); } - private static void assertTokens(List expList, List result) { - final int length = expList.size(); - assertEquals(length, result.size()); - for (int i = 0; i < length; i++) { - assertEquals(expList.get(i).type(), result.get(i).type()); - } + private static void assertTokens(List result, TokenType... tokenTypes) { + assertThat(result) + .hasSize(tokenTypes.length) + .extracting(Token::type) + .containsExactly(tokenTypes); } - - private static List list(TokenType... types) { - final List list = new ArrayList<>(); - for (TokenType t : types) { - list.add(token(t)); - } - return list; - } - - private static Token token(TokenType type) { - return token(type, "", new Pos(0, 0)); - } - - private static Token token(TokenType type, String text, Pos pos) { - return new Token(type, text, pos); - } - } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java new file mode 100644 index 00000000..5c0390cc --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java @@ -0,0 +1,91 @@ +package com.annimon.ownlang.parser; + +import org.junit.jupiter.params.provider.Arguments; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import static com.annimon.ownlang.parser.TokenType.*; + +public class LexerValidDataProvider { + + public static Stream getAll() { + final var result = new ArrayList(); + result.addAll(numbers()); + result.addAll(keywords()); + result.addAll(words()); + result.addAll(operators()); + result.addAll(comments()); + result.addAll(other()); + result.addAll(notSupported()); + return result.stream(); + } + + private static List numbers() { + return List.of( + Arguments.of("Numbers", + "12 7.8 90000000 10.03", + List.of(NUMBER, NUMBER, NUMBER, NUMBER)), + Arguments.of("Hex numbers", + "#FF 0xCA 0x12fb 0xFF", + List.of(HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER)) + ); + } + + private static List keywords() { + return List.of( + Arguments.of("Keywords", + "if else while for include", + List.of(IF, ELSE, WHILE, FOR, INCLUDE)) + ); + } + + private static List words() { + return List.of( + Arguments.of("Word", + "if bool include \"text\n\ntext\"", + List.of(IF, WORD, INCLUDE, TEXT)), + Arguments.of("Extended word identifier", + "`€` = 1", + List.of(WORD, EQ, NUMBER)) + ); + } + + private static List operators() { + return List.of( + Arguments.of("Operators", + "=+-*/%<>!&|", + List.of(EQ, PLUS, MINUS, STAR, SLASH, PERCENT, LT, GT, EXCL, AMP, BAR)), + Arguments.of("Operators 2 characters", + "== != <= >= && || ==+ >=- ->", + List.of(EQEQ, EXCLEQ, LTEQ, GTEQ, AMPAMP, BARBAR, + EQEQ, PLUS, GTEQ, MINUS, MINUS, GT)) + ); + } + + private static List comments() { + return List.of( + Arguments.of("Comments", + "// /* 1234 \n */", + List.of(STAR, SLASH)) + ); + } + + private static List other() { + return List.of( + Arguments.of("Arithmetic", + "x = -1 + 2 * 3 % 4 / 5", + List.of(WORD, EQ, MINUS, NUMBER, PLUS, NUMBER, STAR, NUMBER, PERCENT, NUMBER, SLASH, NUMBER)) + ); + } + + private static List notSupported() { + return List.of( + Arguments.of("Float notation", + "7e8", + List.of(NUMBER, WORD)), + Arguments.of("Float hex numbers", + "0Xf7p6", + List.of(HEX_NUMBER, WORD)) + ); + } +} From a87d5f76858a58766ff9fae5d42aba4d6baf0aad Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 16 Sep 2023 20:13:27 +0300 Subject: [PATCH 311/448] Added floating number scientific notation --- .../com/annimon/ownlang/parser/Lexer.java | 32 +++++++++++++++++++ .../com/annimon/ownlang/parser/LexerTest.java | 13 ++++++++ .../parser/LexerValidDataProvider.java | 6 ++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 48efd156..d4af6334 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -164,6 +164,10 @@ private void tokenizeNumber() { if (current == '.') { if (hasDot) throw error("Invalid float number " + buffer); hasDot = true; + } else if (current == 'e' || current == 'E') { + int exp = subTokenizeScientificNumber(); + buffer.append(current).append(exp); + break; } else if (!Character.isDigit(current)) { break; } @@ -172,6 +176,34 @@ private void tokenizeNumber() { } addToken(TokenType.NUMBER, buffer.toString(), startPos); } + + private int subTokenizeScientificNumber() { + int sign = 1; + switch (next()) { + case '-': sign = -1; + case '+': skip(); break; + } + + boolean hasValue = false; + char current = peek(0); + while (current == '0') { + hasValue = true; + current = next(); + } + int result = 0; + int position = 0; + while (Character.isDigit(current)) { + result = result * 10 + (current - '0'); + current = next(); + position++; + } + if (position == 0 && !hasValue) throw error("Empty floating point exponent"); + if (position >= 4) { + if (sign > 0) throw error("Float number too large"); + else throw error("Float number too small"); + } + return sign * result; + } private void tokenizeHexNumber(int skipChars) { clearBuffer(); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index c3e97852..92b39c94 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -26,6 +26,9 @@ public static Stream validData() { public static Stream invalidData() { return Stream.builder() .add(Arguments.of("Wrong float point", "3.14.15")) + .add(Arguments.of("Empty float exponent", "3e")) + .add(Arguments.of("Float number too small", "3e-00009000")) + .add(Arguments.of("Float number too large", "3e+00009000")) .add(Arguments.of("Wrong HEX number", "0Xf7_p6_s5")) .add(Arguments.of("HEX number ends with _", "0Xf7_")) .add(Arguments.of("Empty rest of HEX number", "#")) @@ -46,6 +49,16 @@ public void testNumbers() { .extracting(Token::text) .containsExactly("0", "3.1415", "CAFEBABE", "f7d6c5", "FFFF"); } + + @Test + public void testFloatNumbersExponent() { + String input = "4e+7 0.3E-19 2e0 5e0000000000000200 5E-000000089"; + List result = Lexer.tokenize(input); + assertThat(result) + .allMatch(t -> t.type().equals(NUMBER)) + .extracting(Token::text) + .containsExactly("4e7", "0.3E-19", "2e0", "5e200", "5E-89"); + } @Test public void testString() { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java index 5c0390cc..c90cb44b 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java @@ -25,6 +25,9 @@ private static List numbers() { Arguments.of("Numbers", "12 7.8 90000000 10.03", List.of(NUMBER, NUMBER, NUMBER, NUMBER)), + Arguments.of("Float notation", + "7e8", + List.of(NUMBER)), Arguments.of("Hex numbers", "#FF 0xCA 0x12fb 0xFF", List.of(HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER)) @@ -80,9 +83,6 @@ private static List other() { private static List notSupported() { return List.of( - Arguments.of("Float notation", - "7e8", - List.of(NUMBER, WORD)), Arguments.of("Float hex numbers", "0Xf7p6", List.of(HEX_NUMBER, WORD)) From 95d32a6c91c518612dadcdf41f6bf5f0571d957f Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 16 Sep 2023 21:31:22 +0300 Subject: [PATCH 312/448] Use local variable for frequently used fields in Lexer --- .../com/annimon/ownlang/parser/Lexer.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index d4af6334..fa000d10 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -116,7 +116,7 @@ public static Set getKeywords() { private final List tokens; private final StringBuilder buffer; - + private int pos; private int row, col; @@ -125,7 +125,7 @@ public Lexer(String input) { length = input.length(); tokens = new ArrayList<>(); - buffer = new StringBuilder(); + buffer = new StringBuilder(40); row = col = 1; } @@ -152,7 +152,7 @@ public List tokenize() { } private void tokenizeNumber() { - clearBuffer(); + final var buffer = createBuffer(); final Pos startPos = markPos(); char current = peek(0); if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { @@ -206,7 +206,7 @@ private int subTokenizeScientificNumber() { } private void tokenizeHexNumber(int skipChars) { - clearBuffer(); + final var buffer = createBuffer(); final Pos startPos = markPos(); // Skip HEX prefix 0x or # for (int i = 0; i < skipChars; i++) skip(); @@ -248,7 +248,7 @@ private void tokenizeOperator() { } final Pos startPos = markPos(); - clearBuffer(); + final var buffer = createBuffer(); while (true) { if (!buffer.isEmpty() && !OPERATORS.containsKey(buffer.toString() + current)) { addToken(OPERATORS.get(buffer.toString()), startPos); @@ -260,7 +260,7 @@ private void tokenizeOperator() { } private void tokenizeWord() { - clearBuffer(); + final var buffer = createBuffer(); final Pos startPos = markPos(); buffer.append(peek(0)); char current = next(); @@ -280,7 +280,7 @@ private void tokenizeWord() { private void tokenizeExtendedWord() { final Pos startPos = markPos(); skip();// skip ` - clearBuffer(); + final var buffer = createBuffer(); char current = peek(0); while (current != '`') { if (current == '\0') throw error("Reached end of file while parsing extended word."); @@ -295,7 +295,7 @@ private void tokenizeExtendedWord() { private void tokenizeText() { final Pos startPos = markPos(); skip();// skip " - clearBuffer(); + final var buffer = createBuffer(); char current = peek(0); while (true) { if (current == '\\') { @@ -372,8 +372,9 @@ private boolean isOwnLangIdentifierPart(char current) { return isOwnLangIdentifierStart(current) || isNumber(current); } - private void clearBuffer() { + private StringBuilder createBuffer() { buffer.setLength(0); + return buffer; } private Pos markPos() { From da950f96c25e60122f1beec2d3e2913bff6c08c6 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 16 Sep 2023 22:22:29 +0300 Subject: [PATCH 313/448] Better explaining parse errors --- .../ownlang/exceptions/ParseException.java | 31 ++++-- .../annimon/ownlang/parser/ParseError.java | 2 +- .../com/annimon/ownlang/parser/Parser.java | 95 +++++++++++++++---- .../java/com/annimon/ownlang/parser/Pos.java | 2 + .../com/annimon/ownlang/parser/Token.java | 4 + 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index ed2d47e4..0049c6d4 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -1,16 +1,35 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.parser.Pos; + /** * * @author aNNiMON */ public final class ParseException extends RuntimeException { - - public ParseException() { - super(); + + private final Pos start; + private final Pos end; + + public ParseException(String message) { + this(message, Pos.ZERO, Pos.ZERO); + } + + public ParseException(String message, Pos pos) { + this(message, pos, pos); } - - public ParseException(String string) { - super(string); + + public ParseException(String message, Pos start, Pos end) { + super(message); + this.start = start; + this.end = end; + } + + public Pos getStart() { + return start; + } + + public Pos getEnd() { + return end; } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java index b2e2e27d..264f0430 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java @@ -4,6 +4,6 @@ public record ParseError(Exception exception, Pos pos) { @Override public String toString() { - return "ParseError on line " + pos.row() + ": " + exception; + return "Error on line " + pos.row() + ": " + exception; } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index aa6a0a09..980300f7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -6,6 +6,8 @@ import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; /** * @@ -70,8 +72,11 @@ public Statement parse() { while (!match(TokenType.EOF)) { try { result.add(statement()); + } catch (ParseException parseException) { + parseErrors.add(parseException, parseException.getStart()); + recover(); } catch (Exception ex) { - parseErrors.add(ex, getErrorPos()); + parseErrors.add(ex, getPos()); recover(); } } @@ -79,7 +84,7 @@ public Statement parse() { return result; } - private Pos getErrorPos() { + private Pos getPos() { if (size == 0) return new Pos(0, 0); if (pos >= size) return tokens.get(size - 1).pos(); return tokens.get(pos).pos(); @@ -166,7 +171,8 @@ private Statement statement() { private UseStatement useStatement() { final var modules = new HashSet(); do { - modules.add(consume(TokenType.WORD).text()); + modules.add(consumeOrExplainError(TokenType.WORD, + Parser::explainUseStatementError).text()); } while (match(TokenType.COMMA)); return new UseStatement(modules); } @@ -179,21 +185,26 @@ private Statement assignmentStatement() { if (expression instanceof Statement statement) { return statement; } - throw new ParseException("Unknown statement: " + get(0)); + throw error("Unknown statement: " + get(0)); } private DestructuringAssignmentStatement destructuringAssignment() { // extract(var1, var2, ...) = ... + final var startPos = getPos(); consume(TokenType.LPAREN); final List variables = new ArrayList<>(); while (!match(TokenType.RPAREN)) { - if (lookMatch(0, TokenType.WORD)) { - variables.add(consume(TokenType.WORD).text()); - } else { - variables.add(null); - } + final Token current = get(0); + variables.add(switch (current.type()) { + case WORD -> consume(TokenType.WORD).text(); + case COMMA -> null; + default -> throw error(errorUnexpectedTokens(current, TokenType.WORD, TokenType.COMMA)); + }); match(TokenType.COMMA); } + if (variables.isEmpty() || variables.stream().allMatch(Objects::isNull)) { + throw error(errorDestructuringAssignmentEmpty(), startPos, getPos()); + } consume(TokenType.EQ); return new DestructuringAssignmentStatement(variables, expression()); } @@ -299,7 +310,7 @@ private Arguments arguments() { } else if (!startsOptionalArgs) { arguments.addRequired(name); } else { - throw new ParseException("Required argument cannot be after optional"); + throw error(errorRequiredArgumentAfterOptional()); } match(TokenType.COMMA); } @@ -374,7 +385,7 @@ private Expression map() { private MatchExpression match() { // match expression { // case pattern1: result1 - // case pattern2 if extr: result2 + // case pattern2 if expr: result2 // } final Expression expression = expression(); consume(TokenType.LBRACE); @@ -425,7 +436,7 @@ private MatchExpression match() { } if (pattern == null) { - throw new ParseException("Wrong pattern in match expression: " + current); + throw error("Wrong pattern in match expression: " + current); } if (match(TokenType.IF)) { // case e if e > 0: @@ -461,7 +472,7 @@ private Statement classDeclaration() { if (fieldDeclaration != null) { classDeclaration.addField(fieldDeclaration); } else { - throw new ParseException("Class can contain only assignments and function declarations"); + throw error("Class can contain only assignments and function declarations"); } } } while (!match(TokenType.RBRACE)); @@ -873,12 +884,12 @@ private Expression value() { } return strExpr; } - throw new ParseException("Unknown expression: " + current); + throw error("Unknown expression: " + current); } private Number createNumber(String text, int radix) { // Double - if (text.contains(".")) { + if (text.contains(".") || text.contains("e") || text.contains("E")) { return Double.parseDouble(text); } // Integer @@ -889,13 +900,23 @@ private Number createNumber(String text, int radix) { } } - private Token consume(TokenType type) { - final Token current = get(0); - if (type != current.type()) { - throw new ParseException("Token " + current + " doesn't match " + type); + private Token consume(TokenType expectedType) { + final Token actual = get(0); + if (expectedType != actual.type()) { + throw error(errorUnexpectedToken(actual, expectedType)); } pos++; - return current; + return actual; + } + + private Token consumeOrExplainError(TokenType expectedType, Function errorMessageFunction) { + final Token actual = get(0); + if (expectedType != actual.type()) { + throw error(errorUnexpectedToken(actual, expectedType) + + errorMessageFunction.apply(actual)); + } + pos++; + return actual; } private boolean match(TokenType type) { @@ -916,4 +937,38 @@ private Token get(int relativePosition) { if (position >= size) return EOF; return tokens.get(position); } + + private ParseException error(String message) { + return new ParseException(message, getPos()); + } + + private static ParseException error(String message, Pos start, Pos end) { + return new ParseException(message, start, end); + } + + private static String errorUnexpectedToken(Token actual, TokenType expectedType) { + return "Expected token with type " + expectedType + ", but found " + actual.shortDescription(); + } + + private static String errorUnexpectedTokens(Token actual, TokenType... expectedTypes) { + String tokenTypes = Arrays.stream(expectedTypes).map(Enum::toString).collect(Collectors.joining(", ")); + return "Expected tokens with types one of " + tokenTypes + ", but found " + actual.shortDescription(); + } + + private static String errorDestructuringAssignmentEmpty() { + return "Destructuring assignment should contain at least one variable name to assign." + + "\nCorrect syntax: extract(v1, , , v4) = "; + } + + private static String errorRequiredArgumentAfterOptional() { + return "Required argument cannot be placed after optional."; + } + + private static String explainUseStatementError(Token current) { + String example = current.type().equals(TokenType.TEXT) + ? "use " + current.text() + : "use std, math"; + return "\nNote: as of OwnLang 2.0.0 use statement simplifies modules list syntax. " + + "Correct syntax: " + example; + } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java index 75bf8b99..f5a12173 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser; public record Pos(int row, int col) { + public static final Pos UNKNOWN = new Pos(-1, -1); + public static final Pos ZERO = new Pos(0, 0); public Pos normalize() { return new Pos(Math.max(0, row - 1), Math.max(0, col - 1)); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java index a271d220..8d151cd4 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java @@ -5,6 +5,10 @@ */ public record Token(TokenType type, String text, Pos pos) { + public String shortDescription() { + return type().name() + " " + text; + } + @Override public String toString() { return type.name() + " " + pos().format() + " " + text; From 312a05576cce511fd3f1840de404e232a92c967f Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 17 Sep 2023 18:34:11 +0300 Subject: [PATCH 314/448] Use ranges in parser --- .../ownlang/exceptions/LexerException.java | 2 +- .../ownlang/exceptions/ParseException.java | 19 +++--- .../com/annimon/ownlang/parser/Lexer.java | 2 +- .../annimon/ownlang/parser/ParseError.java | 14 +++- .../annimon/ownlang/parser/ParseErrors.java | 4 +- .../com/annimon/ownlang/parser/Parser.java | 64 +++++++++++-------- .../com/annimon/ownlang/parser/Range.java | 27 ++++++++ .../java/com/annimon/ownlang/utils/Repl.java | 2 +- 8 files changed, 90 insertions(+), 44 deletions(-) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java index 383e01f9..66f8a950 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java @@ -12,7 +12,7 @@ public LexerException(String message) { super(message); } - public LexerException(Pos pos, String message) { + public LexerException(String message, Pos pos) { super(pos.format() + " " + message); } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index 0049c6d4..1046f04a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.exceptions; import com.annimon.ownlang.parser.Pos; +import com.annimon.ownlang.parser.Range; /** * @@ -8,11 +9,10 @@ */ public final class ParseException extends RuntimeException { - private final Pos start; - private final Pos end; + private final Range range; public ParseException(String message) { - this(message, Pos.ZERO, Pos.ZERO); + this(message, Range.ZERO); } public ParseException(String message, Pos pos) { @@ -20,16 +20,15 @@ public ParseException(String message, Pos pos) { } public ParseException(String message, Pos start, Pos end) { - super(message); - this.start = start; - this.end = end; + this(message, new Range(start, end)); } - public Pos getStart() { - return start; + public ParseException(String message, Range range) { + super(message); + this.range = range; } - public Pos getEnd() { - return end; + public Range getRange() { + return range; } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index fa000d10..28ee57e7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -411,6 +411,6 @@ private void addToken(TokenType type, String text, Pos startRow) { } private LexerException error(String text) { - return new LexerException(markPos(), text); + return new LexerException(text, markPos()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java index 264f0430..d5059211 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java @@ -1,9 +1,19 @@ package com.annimon.ownlang.parser; -public record ParseError(Exception exception, Pos pos) { +import java.util.Collections; +import java.util.List; + +public record ParseError(String message, Range range, List stackTraceElements) { + public ParseError(String message, Range range) { + this(message, range, Collections.emptyList()); + } + + public boolean hasStackTrace() { + return !stackTraceElements.isEmpty(); + } @Override public String toString() { - return "Error on line " + pos.row() + ": " + exception; + return "Error on line " + range().start().row() + ": " + message; } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java index 4d1c44f0..d0de90e3 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java @@ -17,8 +17,8 @@ public void clear() { errors.clear(); } - public void add(Exception ex, Pos pos) { - errors.add(new ParseError(ex, pos)); + public void add(ParseError parseError) { + errors.add(parseError); } public boolean hasErrors() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 980300f7..f4df30d8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -24,7 +24,7 @@ public static Statement parse(List tokens) { return program; } - private static final Token EOF = new Token(TokenType.EOF, "", new Pos(-1, -1)); + private static final Token EOF = new Token(TokenType.EOF, "", Pos.UNKNOWN); private static final EnumMap ASSIGN_OPERATORS; static { @@ -50,7 +50,7 @@ public static Statement parse(List tokens) { private final ParseErrors parseErrors; private Statement parsedStatement; - private int pos; + private int index; public Parser(List tokens) { this.tokens = tokens; @@ -72,11 +72,11 @@ public Statement parse() { while (!match(TokenType.EOF)) { try { result.add(statement()); - } catch (ParseException parseException) { - parseErrors.add(parseException, parseException.getStart()); + } catch (ParseException ex) { + parseErrors.add(new ParseError(ex.getMessage(), ex.getRange())); recover(); } catch (Exception ex) { - parseErrors.add(ex, getPos()); + parseErrors.add(new ParseError(ex.getMessage(), getRange(), List.of(ex.getStackTrace()))); recover(); } } @@ -84,20 +84,14 @@ public Statement parse() { return result; } - private Pos getPos() { - if (size == 0) return new Pos(0, 0); - if (pos >= size) return tokens.get(size - 1).pos(); - return tokens.get(pos).pos(); - } - private void recover() { - int preRecoverPosition = pos; - for (int i = preRecoverPosition; i <= size; i++) { - pos = i; + int preRecoverIndex = index; + for (int i = preRecoverIndex; i <= size; i++) { + index = i; try { statement(); // successfully parsed, - pos = i; // restore position + index = i; // restore position return; } catch (Exception ex) { // fail @@ -190,7 +184,7 @@ private Statement assignmentStatement() { private DestructuringAssignmentStatement destructuringAssignment() { // extract(var1, var2, ...) = ... - final var startPos = getPos(); + final var startTokenIndex = index; consume(TokenType.LPAREN); final List variables = new ArrayList<>(); while (!match(TokenType.RPAREN)) { @@ -203,7 +197,7 @@ private DestructuringAssignmentStatement destructuringAssignment() { match(TokenType.COMMA); } if (variables.isEmpty() || variables.stream().allMatch(Objects::isNull)) { - throw error(errorDestructuringAssignmentEmpty(), startPos, getPos()); + throw error(errorDestructuringAssignmentEmpty(), startTokenIndex, index); } consume(TokenType.EQ); return new DestructuringAssignmentStatement(variables, expression()); @@ -493,16 +487,16 @@ private Expression assignment() { private AssignmentExpression assignmentStrict() { // x[0].prop += ... - final int position = pos; + final int position = index; final Expression targetExpr = qualifiedName(); if (!(targetExpr instanceof Accessible)) { - pos = position; + index = position; return null; } final TokenType currentType = get(0).type(); if (!ASSIGN_OPERATORS.containsKey(currentType)) { - pos = position; + index = position; return null; } match(currentType); @@ -905,7 +899,7 @@ private Token consume(TokenType expectedType) { if (expectedType != actual.type()) { throw error(errorUnexpectedToken(actual, expectedType)); } - pos++; + index++; return actual; } @@ -915,7 +909,7 @@ private Token consumeOrExplainError(TokenType expectedType, Function= size) return EOF; return tokens.get(position); } + private Range getRange() { + return getRange(index, index); + } + + private Range getRange(int startIndex, int endIndex) { + if (size == 0) return Range.ZERO; + final int last = size - 1; + Pos start = tokens.get(Math.min(startIndex, last)).pos(); + if (startIndex == endIndex) { + return new Range(start, start); + } else { + Pos end = tokens.get(Math.min(endIndex, last)).pos(); + return new Range(start, end); + } + } + private ParseException error(String message) { - return new ParseException(message, getPos()); + return new ParseException(message, getRange()); } - private static ParseException error(String message, Pos start, Pos end) { - return new ParseException(message, start, end); + private ParseException error(String message, int startIndex, int endIndex) { + return new ParseException(message, getRange(startIndex, endIndex)); } private static String errorUnexpectedToken(Token actual, TokenType expectedType) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java new file mode 100644 index 00000000..6902d483 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.parser; + +import java.util.Objects; + +public record Range(Pos start, Pos end) { + public static final Range ZERO = new Range(Pos.ZERO, Pos.ZERO); + + public Range normalize() { + return new Range(start.normalize(), end.normalize()); + } + + public boolean isEqualPosition() { + return Objects.equals(start, end); + } + + public boolean isOnSameLine() { + return start.row() == end.row(); + } + + public String format() { + if (isOnSameLine()) { + return start.format(); + } else { + return start.format() + "..." + end.format(); + } + } +} diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 1e384820..17a055e7 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -33,7 +33,7 @@ public final class Repl { RESET = ":reset", EXIT = ":exit"; - private static final Token PRINTLN_TOKEN = new Token(TokenType.PRINTLN, "", new Pos(0, 0)); + private static final Token PRINTLN_TOKEN = new Token(TokenType.PRINTLN, "", Pos.ZERO); public static void main(String[] args) { System.out.println("Welcome to OwnLang " + Version.VERSION + " REPL"); From cdf0219ca1a6a86b91ef722c7d31dd73fc9638c3 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 17 Sep 2023 18:46:09 +0300 Subject: [PATCH 315/448] Add base type for exceptions --- .../exceptions/ArgumentsMismatchException.java | 2 +- .../OperationIsNotSupportedException.java | 2 +- .../exceptions/OwnLangRuntimeException.java | 15 +++++++++++++++ .../exceptions/PatternMatchingException.java | 2 +- .../ownlang/exceptions/StoppedException.java | 6 ++++++ .../annimon/ownlang/exceptions/TypeException.java | 2 +- .../ownlang/exceptions/UnknownClassException.java | 2 +- .../exceptions/UnknownFunctionException.java | 2 +- .../exceptions/UnknownPropertyException.java | 2 +- .../VariableDoesNotExistsException.java | 2 +- .../ownlang/exceptions/LexerException.java | 2 +- .../exceptions/OwnLangParserException.java | 15 +++++++++++++++ .../ownlang/exceptions/ParseException.java | 2 +- .../ownlang/exceptions/StoppedException.java | 6 ------ 14 files changed, 46 insertions(+), 16 deletions(-) rename {ownlang-parser => ownlang-core}/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java (79%) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java rename {ownlang-parser => ownlang-core}/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java (69%) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java rename {ownlang-parser => ownlang-core}/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java (78%) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java index 165f8631..082e9b34 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class ArgumentsMismatchException extends RuntimeException { +public final class ArgumentsMismatchException extends OwnLangRuntimeException { public ArgumentsMismatchException() { } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java similarity index 79% rename from ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java index 6daec73e..191f9f9b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class OperationIsNotSupportedException extends RuntimeException { +public final class OperationIsNotSupportedException extends OwnLangRuntimeException { public OperationIsNotSupportedException(Object operation) { super("Operation " + operation + " is not supported"); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java new file mode 100644 index 00000000..d1212815 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.exceptions; + +/** + * Base type for all runtime exceptions + */ +public abstract class OwnLangRuntimeException extends RuntimeException { + + public OwnLangRuntimeException() { + super(); + } + + public OwnLangRuntimeException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java similarity index 69% rename from ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java index 05075f89..494264b2 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class PatternMatchingException extends RuntimeException { +public final class PatternMatchingException extends OwnLangRuntimeException { public PatternMatchingException() { } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java new file mode 100644 index 00000000..67ad19b2 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java @@ -0,0 +1,6 @@ +package com.annimon.ownlang.exceptions; + +public class StoppedException extends OwnLangRuntimeException { + + +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java index c2e853b1..e5323079 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class TypeException extends RuntimeException { +public final class TypeException extends OwnLangRuntimeException { public TypeException(String message) { super(message); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java index 40c29b9d..65b2fc1d 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class UnknownClassException extends RuntimeException { +public final class UnknownClassException extends OwnLangRuntimeException { private final String className; diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java index 74ad7d3e..9aa2a9e4 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class UnknownFunctionException extends RuntimeException { +public final class UnknownFunctionException extends OwnLangRuntimeException { private final String functionName; diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java index 4156c59e..bcfa22c3 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class UnknownPropertyException extends RuntimeException { +public final class UnknownPropertyException extends OwnLangRuntimeException { private final String propertyName; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java similarity index 78% rename from ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java index 0981de17..419807e4 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -public final class VariableDoesNotExistsException extends RuntimeException { +public final class VariableDoesNotExistsException extends OwnLangRuntimeException { private final String variable; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java index 66f8a950..d1bd4cb0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java @@ -6,7 +6,7 @@ * * @author aNNiMON */ -public final class LexerException extends RuntimeException { +public final class LexerException extends OwnLangParserException { public LexerException(String message) { super(message); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java new file mode 100644 index 00000000..8aaa4a78 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.exceptions; + +/** + * Base type for all lexer and parser exceptions + */ +public abstract class OwnLangParserException extends RuntimeException { + + public OwnLangParserException() { + super(); + } + + public OwnLangParserException(String message) { + super(message); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index 1046f04a..6b67ca14 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -7,7 +7,7 @@ * * @author aNNiMON */ -public final class ParseException extends RuntimeException { +public final class ParseException extends OwnLangParserException { private final Range range; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java deleted file mode 100644 index 8ce24c33..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.annimon.ownlang.exceptions; - -public class StoppedException extends RuntimeException { - - -} From f6c3f3fe17c8ec0f77c47b575bb73d28dfbbebd0 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 17 Sep 2023 18:49:18 +0300 Subject: [PATCH 316/448] Constants access priority --- .../java/com/annimon/ownlang/lib/ScopeHandler.java | 7 +++++++ .../annimon/ownlang/parser/ast/MatchExpression.java | 11 +++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java index 9571d500..8913fc7f 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -76,12 +76,19 @@ public static void setFunction(String name, Function function) { public static boolean isVariableOrConstantExists(String name) { + if (rootScope().containsConstant(name)) { + return true; + } synchronized (lock) { return findScope(name).isFound; } } public static Value getVariableOrConstant(String name) { + Value constant = rootScope().getConstant(name); + if (constant != null) { + return constant; + } synchronized (lock) { final ScopeFindData scopeData = findScope(name); if (scopeData.isFound) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 2a5e43de..8af6163c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -44,13 +44,12 @@ public Value eval() { return evalResult(p.result); } } else { - ScopeHandler.defineVariableInCurrentScope(pattern.variable, value); - if (optMatches(p)) { - final Value result = evalResult(p.result); - ScopeHandler.removeVariable(pattern.variable); - return result; + try (final var ignored = ScopeHandler.closeableScope()) { + ScopeHandler.defineVariableInCurrentScope(pattern.variable, value); + if (optMatches(p)) { + return evalResult(p.result); + } } - ScopeHandler.removeVariable(pattern.variable); } } if ((value.type() == Types.ARRAY) && (p instanceof ListPattern pattern)) { From 923f0edbcb92d03a94e5eeedaaf20f9fcc707202 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 17 Sep 2023 19:00:17 +0300 Subject: [PATCH 317/448] Introduce DECIMAL_NUMBER token, fix HEX numbers with 'E' incorrectly treated as decimal --- .../com/annimon/ownlang/parser/Lexer.java | 9 ++++++++- .../com/annimon/ownlang/parser/Parser.java | 19 ++++++++++++++----- .../com/annimon/ownlang/parser/TokenType.java | 1 + .../ownlang/parser/LexerPositionsTest.java | 4 ++-- .../com/annimon/ownlang/parser/LexerTest.java | 6 +++--- .../parser/LexerValidDataProvider.java | 9 ++++++--- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 28ee57e7..9a11d548 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -159,12 +159,15 @@ private void tokenizeNumber() { tokenizeHexNumber(2); return; } + boolean decimal = false; boolean hasDot = false; while (true) { if (current == '.') { + decimal = true; if (hasDot) throw error("Invalid float number " + buffer); hasDot = true; } else if (current == 'e' || current == 'E') { + decimal = true; int exp = subTokenizeScientificNumber(); buffer.append(current).append(exp); break; @@ -174,7 +177,11 @@ private void tokenizeNumber() { buffer.append(current); current = next(); } - addToken(TokenType.NUMBER, buffer.toString(), startPos); + if (decimal) { + addToken(TokenType.DECIMAL_NUMBER, buffer.toString(), startPos); + } else { + addToken(TokenType.NUMBER, buffer.toString(), startPos); + } } private int subTokenizeScientificNumber() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index f4df30d8..668894cc 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -389,10 +389,15 @@ private MatchExpression match() { MatchExpression.Pattern pattern = null; final Token current = get(0); if (match(TokenType.NUMBER)) { - // case 0.5: + // case 20: pattern = new MatchExpression.ConstantPattern( NumberValue.of(createNumber(current.text(), 10)) ); + } else if (match(TokenType.DECIMAL_NUMBER)) { + // case 0.5: + pattern = new MatchExpression.ConstantPattern( + NumberValue.of(createDecimalNumber(current.text())) + ); } else if (match(TokenType.HEX_NUMBER)) { // case #FF: pattern = new MatchExpression.ConstantPattern( @@ -856,6 +861,9 @@ private Expression value() { if (match(TokenType.NUMBER)) { return new ValueExpression(createNumber(current.text(), 10)); } + if (match(TokenType.DECIMAL_NUMBER)) { + return new ValueExpression(createDecimalNumber(current.text())); + } if (match(TokenType.HEX_NUMBER)) { return new ValueExpression(createNumber(current.text(), 16)); } @@ -882,10 +890,6 @@ private Expression value() { } private Number createNumber(String text, int radix) { - // Double - if (text.contains(".") || text.contains("e") || text.contains("E")) { - return Double.parseDouble(text); - } // Integer try { return Integer.parseInt(text, radix); @@ -894,6 +898,11 @@ private Number createNumber(String text, int radix) { } } + private Number createDecimalNumber(String text) { + // Double + return Double.parseDouble(text); + } + private Token consume(TokenType expectedType) { final Token actual = get(0); if (expectedType != actual.type()) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java index 87d15eb5..cf6a71db 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java @@ -7,6 +7,7 @@ public enum TokenType { NUMBER, + DECIMAL_NUMBER, HEX_NUMBER, WORD, TEXT, diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java index 5f0c7993..aac808e3 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java @@ -50,7 +50,7 @@ void testMultilineComment() { /* line2 line*/a =/* - */3 + */3.1 """.stripIndent(); List result = Lexer.tokenize(input); @@ -58,7 +58,7 @@ void testMultilineComment() { .hasSize(3) .extracting(s -> s.pos().row(), s -> s.pos().col(), Token::type) .containsExactly( - tuple(3, 9, WORD), tuple(3, 11, EQ), tuple(4, 3, NUMBER) + tuple(3, 9, WORD), tuple(3, 11, EQ), tuple(4, 3, DECIMAL_NUMBER) ); } } \ No newline at end of file diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index 92b39c94..d3bd0abf 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -44,18 +44,18 @@ public static Stream invalidData() { public void testNumbers() { String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF"; List result = Lexer.tokenize(input); - assertTokens(result, NUMBER, NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); + assertTokens(result, NUMBER, DECIMAL_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); assertThat(result) .extracting(Token::text) .containsExactly("0", "3.1415", "CAFEBABE", "f7d6c5", "FFFF"); } @Test - public void testFloatNumbersExponent() { + public void testDecimalNumbersExponent() { String input = "4e+7 0.3E-19 2e0 5e0000000000000200 5E-000000089"; List result = Lexer.tokenize(input); assertThat(result) - .allMatch(t -> t.type().equals(NUMBER)) + .allMatch(t -> t.type().equals(DECIMAL_NUMBER)) .extracting(Token::text) .containsExactly("4e7", "0.3E-19", "2e0", "5e200", "5E-89"); } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java index c90cb44b..891eab2e 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java @@ -23,11 +23,14 @@ public static Stream getAll() { private static List numbers() { return List.of( Arguments.of("Numbers", - "12 7.8 90000000 10.03", - List.of(NUMBER, NUMBER, NUMBER, NUMBER)), + "12 90000000", + List.of(NUMBER, NUMBER)), + Arguments.of("Decimal numbers", + "7.8 10.03", + List.of(DECIMAL_NUMBER, DECIMAL_NUMBER)), Arguments.of("Float notation", "7e8", - List.of(NUMBER)), + List.of(DECIMAL_NUMBER)), Arguments.of("Hex numbers", "#FF 0xCA 0x12fb 0xFF", List.of(HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER)) From ea38227b446b39515e03b22147aa7ecd3de568fb Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 27 Sep 2023 17:34:20 +0300 Subject: [PATCH 318/448] More informative parser errors with ranges, removed LexerException --- .../exceptions/BaseParserException.java | 20 +++++++++ .../ownlang/exceptions/LexerException.java | 18 -------- .../exceptions/OwnLangParserException.java | 26 ++++++++--- .../ownlang/exceptions/ParseException.java | 22 ++------- .../com/annimon/ownlang/parser/Lexer.java | 45 ++++++++++++------- .../com/annimon/ownlang/parser/Parser.java | 5 ++- .../ownlang/parser/ast/IncludeStatement.java | 8 +--- .../parser/{ => error}/ParseError.java | 3 +- .../parser/{ => error}/ParseErrors.java | 2 +- .../com/annimon/ownlang/parser/LexerTest.java | 4 +- .../java/com/annimon/ownlang/utils/Repl.java | 4 +- 11 files changed, 83 insertions(+), 74 deletions(-) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java rename ownlang-parser/src/main/java/com/annimon/ownlang/parser/{ => error}/ParseError.java (85%) rename ownlang-parser/src/main/java/com/annimon/ownlang/parser/{ => error}/ParseErrors.java (95%) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java new file mode 100644 index 00000000..1d59a0c4 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.exceptions; + +import com.annimon.ownlang.parser.Range; + +/** + * Base type for all lexer and parser exceptions + */ +public abstract class BaseParserException extends RuntimeException { + + private final Range range; + + public BaseParserException(String message, Range range) { + super(message); + this.range = range; + } + + public Range getRange() { + return range; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java deleted file mode 100644 index d1bd4cb0..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/LexerException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.annimon.ownlang.exceptions; - -import com.annimon.ownlang.parser.Pos; - -/** - * - * @author aNNiMON - */ -public final class LexerException extends OwnLangParserException { - - public LexerException(String message) { - super(message); - } - - public LexerException(String message, Pos pos) { - super(pos.format() + " " + message); - } -} \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java index 8aaa4a78..6e3d5324 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java @@ -1,15 +1,27 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.parser.error.ParseError; +import com.annimon.ownlang.parser.error.ParseErrors; + /** - * Base type for all lexer and parser exceptions + * Single Exception for Lexer and Parser errors */ -public abstract class OwnLangParserException extends RuntimeException { +public class OwnLangParserException extends RuntimeException { + + private final ParseErrors parseErrors; + + public OwnLangParserException(ParseError parseError) { + super(parseError.toString()); + this.parseErrors = new ParseErrors(); + parseErrors.add(parseError);; + } - public OwnLangParserException() { - super(); + public OwnLangParserException(ParseErrors parseErrors) { + super(parseErrors.toString()); + this.parseErrors = parseErrors; } - public OwnLangParserException(String message) { - super(message); + public ParseErrors getParseErrors() { + return parseErrors; } -} +} \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index 6b67ca14..2113cbc7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -1,34 +1,18 @@ package com.annimon.ownlang.exceptions; -import com.annimon.ownlang.parser.Pos; import com.annimon.ownlang.parser.Range; /** * * @author aNNiMON */ -public final class ParseException extends OwnLangParserException { - - private final Range range; +public final class ParseException extends BaseParserException { public ParseException(String message) { - this(message, Range.ZERO); - } - - public ParseException(String message, Pos pos) { - this(message, pos, pos); - } - - public ParseException(String message, Pos start, Pos end) { - this(message, new Range(start, end)); + super(message, Range.ZERO); } public ParseException(String message, Range range) { - super(message); - this.range = range; - } - - public Range getRange() { - return range; + super(message, range); } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 9a11d548..7ba7fc57 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser; -import com.annimon.ownlang.exceptions.LexerException; +import com.annimon.ownlang.exceptions.OwnLangParserException; +import com.annimon.ownlang.parser.error.ParseError; import java.util.*; /** @@ -146,7 +147,7 @@ public List tokenize() { else if (current == '#') tokenizeHexNumber(1); else if (current == ';') skip(); // ignore semicolon else if (current == '\0') break; - else throw error("Unknown token " + current); + else throw error("Unknown token " + current, markPos()); } return tokens; } @@ -164,11 +165,11 @@ private void tokenizeNumber() { while (true) { if (current == '.') { decimal = true; - if (hasDot) throw error("Invalid float number " + buffer); + if (hasDot) throw error("Invalid float number " + buffer, startPos); hasDot = true; } else if (current == 'e' || current == 'E') { decimal = true; - int exp = subTokenizeScientificNumber(); + int exp = subTokenizeScientificNumber(startPos); buffer.append(current).append(exp); break; } else if (!Character.isDigit(current)) { @@ -184,7 +185,7 @@ private void tokenizeNumber() { } } - private int subTokenizeScientificNumber() { + private int subTokenizeScientificNumber(Pos startPos) { int sign = 1; switch (next()) { case '-': sign = -1; @@ -204,10 +205,10 @@ private int subTokenizeScientificNumber() { current = next(); position++; } - if (position == 0 && !hasValue) throw error("Empty floating point exponent"); + if (position == 0 && !hasValue) throw error("Empty floating point exponent", startPos, markEndPos()); if (position >= 4) { - if (sign > 0) throw error("Float number too large"); - else throw error("Float number too small"); + if (sign > 0) throw error("Float number too large", startPos, markEndPos()); + else throw error("Float number too small", startPos, markEndPos()); } return sign * result; } @@ -227,8 +228,8 @@ private void tokenizeHexNumber(int skipChars) { current = next(); } - if (buffer.isEmpty()) throw error("Empty HEX value"); - if (peek(-1) == '_') throw error("HEX value cannot end with _"); + if (buffer.isEmpty()) throw error("Empty HEX value", startPos); + if (peek(-1) == '_') throw error("HEX value cannot end with _", startPos, markEndPos()); addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); } @@ -290,8 +291,9 @@ private void tokenizeExtendedWord() { final var buffer = createBuffer(); char current = peek(0); while (current != '`') { - if (current == '\0') throw error("Reached end of file while parsing extended word."); - if (current == '\n' || current == '\r') throw error("Reached end of line while parsing extended word."); + if ("\r\n\0".indexOf(current) != -1) { + throw error("Reached end of line while parsing extended word.", startPos, markEndPos()); + } buffer.append(current); current = next(); } @@ -341,7 +343,7 @@ private void tokenizeText() { continue; } if (current == '"') break; - if (current == '\0') throw error("Reached end of file while parsing text string."); + if (current == '\0') throw error("Reached end of file while parsing text string.", startPos, markEndPos()); buffer.append(current); current = next(); } @@ -360,11 +362,14 @@ private void tokenizeComment() { } private void tokenizeMultilineComment() { + final Pos startPos = markPos(); skip(); // / skip(); // * char current = peek(0); while (current != '*' || peek(1) != '/') { - if (current == '\0') throw error("Reached end of file while parsing multiline comment"); + if (current == '\0') { + throw error("Reached end of file while parsing multiline comment", startPos, markEndPos()); + } current = next(); } skip(); // * @@ -388,6 +393,10 @@ private Pos markPos() { return new Pos(row, col); } + private Pos markEndPos() { + return new Pos(row, Math.max(0, col - 1)); + } + private void skip() { if (pos >= length) return; final char result = input.charAt(pos); @@ -417,7 +426,11 @@ private void addToken(TokenType type, String text, Pos startRow) { tokens.add(new Token(type, text, startRow)); } - private LexerException error(String text) { - return new LexerException(text, markPos()); + private OwnLangParserException error(String text, Pos position) { + return error(text, position, position); + } + + private OwnLangParserException error(String text, Pos startPos, Pos endPos) { + return new OwnLangParserException(new ParseError(text, new Range(startPos, endPos))); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 668894cc..0cc90a29 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -1,10 +1,13 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.ParseException; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.parser.ast.*; +import com.annimon.ownlang.parser.error.ParseError; +import com.annimon.ownlang.parser.error.ParseErrors; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -19,7 +22,7 @@ public static Statement parse(List tokens) { final Parser parser = new Parser(tokens); final Statement program = parser.parse(); if (parser.getParseErrors().hasErrors()) { - throw new ParseException(parser.getParseErrors().toString()); + throw new OwnLangParserException(parser.getParseErrors()); } return program; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java index 0c131292..3b2c5de2 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.exceptions.ParseException; import com.annimon.ownlang.parser.Lexer; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.SourceLoader; @@ -38,12 +37,7 @@ public void execute() { public Statement loadProgram(String path) throws IOException { final String input = SourceLoader.readSource(path); final List tokens = Lexer.tokenize(input); - final Parser parser = new Parser(tokens); - final Statement program = parser.parse(); - if (parser.getParseErrors().hasErrors()) { - throw new ParseException(parser.getParseErrors().toString()); - } - return program; + return Parser.parse(tokens); } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java similarity index 85% rename from ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java index d5059211..8fba8eff 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseError.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java @@ -1,5 +1,6 @@ -package com.annimon.ownlang.parser; +package com.annimon.ownlang.parser.error; +import com.annimon.ownlang.parser.Range; import java.util.Collections; import java.util.List; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java similarity index 95% rename from ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java index d0de90e3..76627421 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParseErrors.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.parser; +package com.annimon.ownlang.parser.error; import com.annimon.ownlang.Console; import java.util.ArrayList; diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index d3bd0abf..062cfc8e 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser; -import com.annimon.ownlang.exceptions.LexerException; +import com.annimon.ownlang.exceptions.OwnLangParserException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -108,7 +108,7 @@ public void testValidInput(String name, String input, List tokenTypes @MethodSource("invalidData") public void testInvalidInput(String name, String input) throws IOException { assertThatThrownBy(() -> Lexer.tokenize(input)) - .isInstanceOf(LexerException.class); + .isInstanceOf(OwnLangParserException.class); } private static void assertTokens(List result, TokenType... tokenTypes) { diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 17a055e7..248008c8 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.Version; -import com.annimon.ownlang.exceptions.LexerException; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.UserDefinedFunction; @@ -84,7 +84,7 @@ public static void main(String[] args) { } } program.execute(); - } catch (LexerException lex) { + } catch (OwnLangParserException lex) { continue; } catch (StoppedException ex) { // skip From 0d820c8a911f3f3b7a21eb9e03b462aba1366c74 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 30 Sep 2023 23:53:55 +0300 Subject: [PATCH 319/448] Introduce stages for each main feature --- .../annimon/ownlang/stages/ScopedStage.java | 29 ++++++ .../ownlang/stages/ScopedStageFactory.java | 17 ++++ .../com/annimon/ownlang/stages/Stage.java | 16 ++++ .../annimon/ownlang/stages/StagesData.java | 8 ++ .../annimon/ownlang/stages/StagesDataMap.java | 19 ++++ .../main/java/com/annimon/ownlang/Main.java | 94 +++++++------------ .../ownlang/stages/ExecutionStage.java | 12 +++ .../ownlang/stages/FunctionAddingStage.java | 13 +++ .../annimon/ownlang/stages/LexerStage.java | 18 ++++ .../annimon/ownlang/stages/LinterStage.java | 13 +++ .../ownlang/stages/OptimizationStage.java | 16 ++++ .../annimon/ownlang/stages/ParserStage.java | 26 +++++ .../ownlang/stages/SourceLoaderStage.java | 21 +++++ .../ownlang/utils/TimeMeasurement.java | 33 +++---- 14 files changed, 253 insertions(+), 82 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java new file mode 100644 index 00000000..1660dfe5 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.stages; + +import java.util.function.Consumer; + +public class ScopedStage implements Stage { + + private final String stageName; + private final Stage stage; + private final Consumer startStage; + private final Consumer endStage; + + ScopedStage(String stageName, Stage stage, + Consumer startStage, Consumer endStage) { + this.stageName = stageName; + this.stage = stage; + this.startStage = startStage; + this.endStage = endStage; + } + + @Override + public R perform(StagesData stagesData, I input) { + try { + startStage.accept(stageName); + return stage.perform(stagesData, input); + } finally { + endStage.accept(stageName); + } + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java new file mode 100644 index 00000000..f6d91fb3 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.stages; + +import java.util.function.Consumer; + +public final class ScopedStageFactory { + private final Consumer startStage; + private final Consumer endStage; + + public ScopedStageFactory(Consumer startStage, Consumer endStage) { + this.startStage = startStage; + this.endStage = endStage; + } + + public ScopedStage create(String stageName, Stage stage) { + return new ScopedStage<>(stageName, stage, startStage, endStage); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java new file mode 100644 index 00000000..f2a3820c --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java @@ -0,0 +1,16 @@ +package com.annimon.ownlang.stages; + +public interface Stage { + R perform(StagesData stagesData, I input); + + default Stage then(Stage next) { + return (scope, input) -> { + R result = perform(scope, input); + return next.perform(scope, result); + }; + } + + default Stage thenConditional(boolean enabled, Stage next) { + return enabled ? then(next) : this; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java new file mode 100644 index 00000000..b4aadbd2 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java @@ -0,0 +1,8 @@ +package com.annimon.ownlang.stages; + +public interface StagesData { + + T get(String tag); + + void put(String tag, Object input); +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java new file mode 100644 index 00000000..f1be63e5 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.stages; + +import java.util.HashMap; +import java.util.Map; + +public class StagesDataMap implements StagesData { + private final Map data = new HashMap<>(); + + @SuppressWarnings("unchecked") + @Override + public T get(String tag) { + return (T) data.get(tag); + } + + @Override + public void put(String tag, Object input) { + data.put(tag, input); + } +} diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index d957c7c8..517748a3 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -1,15 +1,10 @@ package com.annimon.ownlang; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; -import com.annimon.ownlang.parser.Beautifier; -import com.annimon.ownlang.parser.Lexer; -import com.annimon.ownlang.parser.Linter; -import com.annimon.ownlang.parser.Optimizer; -import com.annimon.ownlang.parser.Parser; -import com.annimon.ownlang.parser.SourceLoader; -import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.Statement; -import com.annimon.ownlang.parser.visitors.FunctionAdder; +import com.annimon.ownlang.stages.*; import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.Sandbox; import com.annimon.ownlang.utils.TimeMeasurement; @@ -140,57 +135,43 @@ private static void createOwnLangArgs(String[] javaArgs, int index) { System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); Shared.setOwnlangArgs(ownlangArgs); } - + private static void run(String input, RunOptions options) { - options.validate(); - final TimeMeasurement measurement = new TimeMeasurement(); - measurement.start("Tokenize time"); - final List tokens = Lexer.tokenize(input); - measurement.stop("Tokenize time"); - if (options.showTokens) { - final int tokensCount = tokens.size(); - for (int i = 0; i < tokensCount; i++) { - System.out.println(i + " " + tokens.get(i)); - } - } - - measurement.start("Parse time"); - final Parser parser = new Parser(tokens); - final Statement parsedProgram = parser.parse(); - measurement.stop("Parse time"); - if (options.showAst) { - System.out.println(parsedProgram); - } - if (parser.getParseErrors().hasErrors()) { - System.out.println(parser.getParseErrors()); - return; - } - if (options.lintMode) { - Linter.lint(parsedProgram); - return; - } - final Statement program; - if (options.optimizationLevel > 0) { - measurement.start("Optimization time"); - program = Optimizer.optimize(parsedProgram, options.optimizationLevel, options.showAst); - measurement.stop("Optimization time"); - if (options.showAst) { - System.out.println(program.toString()); - } - } else { - program = parsedProgram; - } - program.accept(new FunctionAdder()); + final var measurement = new TimeMeasurement(); + final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop); + + final var stagesData = new StagesDataMap(); + stagesData.put(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, options.showAst); + stagesData.put(SourceLoaderStage.TAG_SOURCE, input); try { - measurement.start("Execution time"); - program.execute(); + scopedStages.create("Lexer", new LexerStage()) + .then(scopedStages.create("Parser", new ParserStage())) + .thenConditional(options.optimizationLevel > 0, + scopedStages.create("Optimization", new OptimizationStage(options.optimizationLevel))) + .thenConditional(options.lintMode, + scopedStages.create("Linter", new LinterStage())) + .then(scopedStages.create("Function adding", new FunctionAddingStage())) + .then(scopedStages.create("Execution", new ExecutionStage())) + .perform(stagesData, input); + } catch (OwnLangParserException ex) { + System.err.println(ex.getParseErrors()); } catch (StoppedException ex) { // skip } catch (Exception ex) { Console.handleException(Thread.currentThread(), ex); } finally { - if (options.showMeasurements) { - measurement.stop("Execution time"); + if (options.showTokens) { + final List tokens = stagesData.get(LexerStage.TAG_TOKENS); + int i = 0; + for (Token token : tokens) { + System.out.println(i++ + " " + token); + } + } + if (options.showAst) { + Statement program = stagesData.get(ParserStage.TAG_PROGRAM); + System.out.println(program); + } + if (!options.showMeasurements) { System.out.println("======================"); System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); } @@ -211,14 +192,5 @@ private static class RunOptions { beautifyMode = false; optimizationLevel = 0; } - - void validate() { - if (lintMode) { - showTokens = false; - showAst = false; - showMeasurements = false; - optimizationLevel = 0; - } - } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java new file mode 100644 index 00000000..2d7b91a7 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.ast.Statement; + +public class ExecutionStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + input.execute(); + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java new file mode 100644 index 00000000..28d917d6 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.visitors.FunctionAdder; + +public class FunctionAddingStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + input.accept(new FunctionAdder()); + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java new file mode 100644 index 00000000..ca77627f --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Token; +import java.util.List; + +public class LexerStage implements Stage> { + + public static final String TAG_TOKENS = "tokens"; + + @Override + public List perform(StagesData stagesData, String input) { + Lexer lexer = new Lexer(input); + List tokens = lexer.tokenize(); + stagesData.put(TAG_TOKENS, tokens); + return tokens; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java new file mode 100644 index 00000000..66cbc962 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.Linter; +import com.annimon.ownlang.parser.ast.Statement; + +public class LinterStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + Linter.lint(input); + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java new file mode 100644 index 00000000..145309b4 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java @@ -0,0 +1,16 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.parser.Optimizer; +import com.annimon.ownlang.parser.ast.Statement; + +public record OptimizationStage(int level) + implements Stage { + + public static final String TAG_OPTIMIZATION_SUMMARY = "optimizationSummary"; + + @Override + public Statement perform(StagesData stagesData, Statement input) { + boolean showSummary = stagesData.get(TAG_OPTIMIZATION_SUMMARY); + return Optimizer.optimize(input, level, showSummary); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java new file mode 100644 index 00000000..7e22fa6c --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java @@ -0,0 +1,26 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.exceptions.OwnLangParserException; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.ast.Statement; +import java.util.List; + +public class ParserStage implements Stage, Statement> { + + public static final String TAG_PROGRAM = "program"; + public static final String TAG_HAS_PARSE_ERRORS = "hasParseErrors"; + + @Override + public Statement perform(StagesData stagesData, List input) { + final Parser parser = new Parser(input); + final Statement program = parser.parse(); + final var parseErrors = parser.getParseErrors(); + stagesData.put(TAG_PROGRAM, program); + stagesData.put(TAG_HAS_PARSE_ERRORS, parseErrors.hasErrors()); + if (parseErrors.hasErrors()) { + throw new OwnLangParserException(parseErrors); + } + return program; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java new file mode 100644 index 00000000..a9d70fcb --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java @@ -0,0 +1,21 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; +import com.annimon.ownlang.parser.SourceLoader; +import java.io.IOException; + +public class SourceLoaderStage implements Stage { + + public static final String TAG_SOURCE = "source"; + + @Override + public String perform(StagesData stagesData, String input) { + try { + String result = SourceLoader.readSource(input); + stagesData.put(TAG_SOURCE, result); + return result; + } catch (IOException e) { + throw new OwnLangRuntimeException("Unable to read input " + input, e); + } + } +} diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java index 2c1e4e28..ca39ca54 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.utils; import com.annimon.ownlang.Console; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -10,8 +10,8 @@ public final class TimeMeasurement { private final Map finished, running; public TimeMeasurement() { - finished = new HashMap<>(); - running = new HashMap<>(); + finished = new LinkedHashMap<>(); + running = new LinkedHashMap<>(); } public void clear() { @@ -19,29 +19,20 @@ public void clear() { running.clear(); } - public void start(String... names) { - final long time = System.nanoTime(); - for (String name : names) { - running.put(name, time); - } + public void start(String name) { + running.put(name, System.nanoTime()); } - public void pause(String... names) { - final long time = System.nanoTime(); - for (String name : names) { - if (running.containsKey(name)) { - addTime(name, time - running.get(name)); - running.remove(name); - } + public void pause(String name) { + if (running.containsKey(name)) { + addTime(name, System.nanoTime() - running.get(name)); + running.remove(name); } } - public void stop(String... names) { - final long time = System.nanoTime(); - for (String name : names) { - if (running.containsKey(name)) { - addTime(name, time - running.get(name)); - } + public void stop(String name) { + if (running.containsKey(name)) { + addTime(name, System.nanoTime() - running.get(name)); } } From 2578f0a6b4c8f22e14db65d6cbc5784d66e3b38b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 30 Sep 2023 23:55:00 +0300 Subject: [PATCH 320/448] Add parse errors formatter --- .../main/java/com/annimon/ownlang/Main.java | 4 +- .../stages/ParseErrorsFormatterStage.java | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 517748a3..ccd0d7a6 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -154,7 +154,9 @@ private static void run(String input, RunOptions options) { .then(scopedStages.create("Execution", new ExecutionStage())) .perform(stagesData, input); } catch (OwnLangParserException ex) { - System.err.println(ex.getParseErrors()); + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + System.err.println(error); } catch (StoppedException ex) { // skip } catch (Exception ex) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java new file mode 100644 index 00000000..5864dd95 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.parser.Pos; +import com.annimon.ownlang.parser.Range; +import com.annimon.ownlang.parser.error.ParseError; +import com.annimon.ownlang.parser.error.ParseErrors; + +public class ParseErrorsFormatterStage implements Stage { + + @Override + public String perform(StagesData stagesData, ParseErrors input) { + final var sb = new StringBuilder(); + final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE); + final var lines = source.split("\r?\n"); + for (ParseError parseError : input) { + sb.append(Console.newline()); + sb.append(parseError); + sb.append(Console.newline()); + final Range range = parseError.range().normalize(); + printPosition(sb, range, lines); + if (parseError.hasStackTrace()) { + sb.append("Stack trace:"); + sb.append(Console.newline()); + for (StackTraceElement el : parseError.stackTraceElements()) { + sb.append(" ").append(el).append(Console.newline()); + } + } + } + return sb.toString(); + } + + private static void printPosition(StringBuilder sb, Range range, String[] lines) { + final Pos start = range.start(); + final int linesCount = lines.length;; + if (range.isOnSameLine()) { + if (start.row() < linesCount) { + sb.append(lines[start.row()]); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(range.end().col() - start.col() + 1)); + sb.append(Console.newline()); + } + } else { + if (start.row() < linesCount) { + String line = lines[start.row()]; + sb.append(line); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(Math.max(1, line.length() - start.col()))); + sb.append(Console.newline()); + } + final Pos end = range.end(); + if (end.row() < linesCount) { + sb.append(lines[end.row()]); + sb.append(Console.newline()); + sb.append("^".repeat(end.col())); + sb.append(Console.newline()); + } + } + } +} From ce14581bf4c1aaa226d04fa3e1c9296ba6314ac3 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 1 Oct 2023 14:42:27 +0300 Subject: [PATCH 321/448] Show linter results after validation --- .../main/java/com/annimon/ownlang/Main.java | 1 + .../com/annimon/ownlang/parser/Linter.java | 38 ------------------ .../ownlang/parser/ast/UseStatement.java | 10 +++++ .../parser/linters/AssignValidator.java | 20 +++++++--- .../DefaultFunctionsOverrideValidator.java | 21 ++++++---- .../ownlang/parser/linters/LintVisitor.java | 8 +++- .../ownlang/parser/linters/LinterResult.java | 11 ++++++ .../ownlang/parser/linters/LinterStage.java | 39 +++++++++++++++++++ .../annimon/ownlang/stages/LinterStage.java | 13 ------- 9 files changed, 96 insertions(+), 65 deletions(-) delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index ccd0d7a6..d6544363 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -4,6 +4,7 @@ import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.linters.LinterStage; import com.annimon.ownlang.stages.*; import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.Sandbox; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java deleted file mode 100644 index cb656716..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.annimon.ownlang.parser; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.ScopeHandler; -import com.annimon.ownlang.parser.ast.Statement; -import com.annimon.ownlang.parser.ast.Visitor; -import com.annimon.ownlang.parser.linters.AssignValidator; -import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; - -public final class Linter { - - public static void lint(Statement program) { - new Linter(program).execute(); - } - - private final Statement program; - - private Linter(Statement program) { - this.program = program; - } - - public void execute() { - final Visitor[] validators = new Visitor[] { - new AssignValidator(), - new DefaultFunctionsOverrideValidator() - }; - resetState(); - for (Visitor validator : validators) { - program.accept(validator); - resetState(); - } - Console.println("Lint validation complete!"); - } - - private void resetState() { - ScopeHandler.resetScope(); - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index 06c51653..c4d9054b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.ModuleLoader; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; @@ -34,6 +35,15 @@ public Map loadConstants() { } return result; } + + public Map loadFunctions() { + final var result = new LinkedHashMap(); + for (String moduleName : modules) { + final Module module = ModuleLoader.load(moduleName); + result.putAll(module.functions()); + } + return result; + } @Override public void accept(Visitor visitor) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 1ce2b764..70149f10 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -1,23 +1,31 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; /** * * @author aNNiMON */ -public final class AssignValidator extends LintVisitor { +final class AssignValidator extends LintVisitor { + + private final Set moduleConstants = new HashSet<>(); + + AssignValidator(Collection results) { + super(results); + } @Override public void visit(AssignmentExpression s) { super.visit(s); if (s.target instanceof VariableExpression varExpr) { final String variable = varExpr.name; - if (ScopeHandler.isConstantExists(variable)) { - Console.error(String.format( - "Warning: variable \"%s\" overrides constant", variable)); + if (moduleConstants.contains(variable)) { + results.add(new LinterResult(LinterResult.Severity.WARNING, + String.format("Variable \"%s\" overrides constant", variable))); } } } @@ -31,6 +39,6 @@ public void visit(IncludeStatement st) { @Override public void visit(UseStatement st) { super.visit(st); - st.execute(); + moduleConstants.addAll(st.loadConstants().keySet()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java index 285f1573..f5cbde0d 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -1,17 +1,24 @@ package com.annimon.ownlang.parser.linters; -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; -public final class DefaultFunctionsOverrideValidator extends LintVisitor { +final class DefaultFunctionsOverrideValidator extends LintVisitor { + + private final Set moduleFunctions = new HashSet<>(); + + DefaultFunctionsOverrideValidator(Collection results) { + super(results); + } @Override public void visit(FunctionDefineStatement s) { super.visit(s); - if (ScopeHandler.isFunctionExists(s.name)) { - Console.error(String.format( - "Warning: function \"%s\" overrides default module function", s.name)); + if (moduleFunctions.contains(s.name)) { + results.add(new LinterResult(LinterResult.Severity.WARNING, + String.format("Function \"%s\" overrides default module function", s.name))); } } @@ -24,6 +31,6 @@ public void visit(IncludeStatement st) { @Override public void visit(UseStatement st) { super.visit(st); - st.execute(); + moduleFunctions.addAll(st.loadFunctions().keySet()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java index c6fa5a97..a97f919b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java @@ -5,8 +5,14 @@ import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.parser.visitors.VisitorUtils; +import java.util.Collection; -public abstract class LintVisitor extends AbstractVisitor { +abstract class LintVisitor extends AbstractVisitor { + protected final Collection results; + + LintVisitor(Collection results) { + this.results = results; + } protected void applyVisitor(IncludeStatement s, Visitor visitor) { final Statement program = VisitorUtils.includeProgram(s); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java new file mode 100644 index 00000000..d02d1e32 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java @@ -0,0 +1,11 @@ +package com.annimon.ownlang.parser.linters; + +record LinterResult(Severity severity, String message) { + + enum Severity { ERROR, WARNING } + + @Override + public String toString() { + return severity.name() + ": " + message; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java new file mode 100644 index 00000000..49726208 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -0,0 +1,39 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.ScopeHandler; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Visitor; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class LinterStage implements Stage { + + @Override + public Statement perform(StagesData stagesData, Statement input) { + final List results = new ArrayList<>(); + final Visitor[] validators = new Visitor[] { + new AssignValidator(results), + new DefaultFunctionsOverrideValidator(results) + }; + + ScopeHandler.resetScope(); + for (Visitor validator : validators) { + input.accept(validator); + ScopeHandler.resetScope(); + } + + results.sort(Comparator.comparing(LinterResult::severity)); + Console.println(String.format("Lint validation completed. %d results found!", results.size())); + for (LinterResult r : results) { + switch (r.severity()) { + case ERROR -> Console.error(r.toString()); + case WARNING -> Console.println(r.toString()); + } + } + return input; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java deleted file mode 100644 index 66cbc962..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/LinterStage.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.annimon.ownlang.stages; - -import com.annimon.ownlang.parser.Linter; -import com.annimon.ownlang.parser.ast.Statement; - -public class LinterStage implements Stage { - - @Override - public Statement perform(StagesData stagesData, Statement input) { - Linter.lint(input); - return input; - } -} From 42e55bd4cc94af24b30f8320a2164c91fdcb7578 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 1 Oct 2023 16:27:36 +0300 Subject: [PATCH 322/448] Add listing constants to REPL --- .../com/annimon/ownlang/lib/Functions.java | 6 -- .../com/annimon/ownlang/lib/Variables.java | 6 -- .../java/com/annimon/ownlang/utils/Repl.java | 55 +++++++++++++------ .../ownlang/utils/repl/OwnLangCompleter.java | 8 +-- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java index f0f643fc..6d5c7524 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java @@ -1,7 +1,5 @@ package com.annimon.ownlang.lib; -import java.util.Map; - /** * * @author aNNiMON @@ -9,10 +7,6 @@ public final class Functions { private Functions() { } - public static Map getFunctions() { - return ScopeHandler.functions(); - } - /** * @deprecated This function remains for backward compatibility with old separate modules * Use {@link ScopeHandler#setFunction(String, Function)} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java index f58dd513..25fe43b1 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -1,7 +1,5 @@ package com.annimon.ownlang.lib; -import java.util.Map; - /** * * @author aNNiMON @@ -9,10 +7,6 @@ public final class Variables { private Variables() { } - public static Map variables() { - return ScopeHandler.variables(); - } - /** * @deprecated This function remains for backward compatibility with old separate modules * Use {@link ScopeHandler#setVariable(String, Value)} diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 248008c8..8d1bf06d 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -4,9 +4,7 @@ import com.annimon.ownlang.Version; import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.UserDefinedFunction; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.Statement; @@ -116,9 +114,9 @@ private static void printHelp(boolean full) { System.out.println("Type in expressions to have them evaluated."); final List commands = new ArrayList<>(); if (full) { - commands.add(VARS + " - listing variables"); - commands.add(FUNCS + " - listing functions"); - commands.add(SOURCE + " - listing source"); + commands.add(VARS + " - list variables/constants"); + commands.add(FUNCS + " - list functions"); + commands.add(SOURCE + " - show source"); } commands.add(HELP + " - show help"); commands.add(RESET + " - clear buffer"); @@ -143,24 +141,45 @@ private static void printHelp(boolean full) { } private static void printVariables() { - Variables.variables().entrySet().stream() + System.out.println("Variables:"); + ScopeHandler.variables().entrySet().stream() .sorted(Map.Entry.comparingByKey()) - .forEach(e -> System.out.printf("\t%s = %s%n", + .forEach(e -> System.out.printf(" %s = %s%n", + e.getKey(), e.getValue().toString())); + + System.out.println("Constants:"); + ScopeHandler.constants().entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> System.out.printf(" %s = %s%n", e.getKey(), e.getValue().toString())); } private static void printFunctions() { - System.out.println("User functions:"); - Functions.getFunctions().entrySet().stream() - .filter(p -> p.getValue() instanceof UserDefinedFunction) - .sorted(Map.Entry.comparingByKey()) - .forEach(e -> System.out.printf("\t%s%s%n", - e.getKey(), ((UserDefinedFunction)e.getValue()).arguments)); + if (ScopeHandler.functions().isEmpty()) { + System.out.println("No functions declared yet!"); + return; + } - System.out.println("Library functions:"); - Functions.getFunctions().entrySet().stream() - .filter(p -> !(p.getValue() instanceof UserDefinedFunction)) + final var functions = ScopeHandler.functions().entrySet().stream() .sorted(Map.Entry.comparingByKey()) - .forEach(e -> System.out.printf("\t%s%n", e.getKey())); + .collect(Collectors.partitioningBy(p -> p.getValue() instanceof UserDefinedFunction)); + + final var userFunctions = functions.get(true); + if (!userFunctions.isEmpty()) { + System.out.println("User functions:"); + for (Map.Entry e : userFunctions) { + System.out.printf(" %s%s%n", + e.getKey(), ((UserDefinedFunction) e.getValue()).arguments); + } + } + + final var libraryFunctions = functions.get(false); + if (!libraryFunctions.isEmpty()) { + System.out.printf("Library functions:%n "); + final var libraryFunctionNames = libraryFunctions.stream() + .map(Map.Entry::getKey) + .collect(Collectors.joining(", ")); + System.out.println(libraryFunctionNames); + } } } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java index e0a2f9e9..38e8abad 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/OwnLangCompleter.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.utils.repl; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.Lexer; import java.util.Collections; import java.util.HashSet; @@ -28,7 +27,8 @@ private void updateCandidates() { getStrings().clear(); getStrings().addAll(Lexer.getKeywords()); getStrings().addAll(staticCandidates); - getStrings().addAll(Variables.variables().keySet()); - getStrings().addAll(Functions.getFunctions().keySet()); + getStrings().addAll(ScopeHandler.constants().keySet()); + getStrings().addAll(ScopeHandler.variables().keySet()); + getStrings().addAll(ScopeHandler.functions().keySet()); } } From f0317c44c73f4502bcfd8f5b1ce867501f9343cb Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 1 Oct 2023 18:48:19 +0300 Subject: [PATCH 323/448] Use stages in programs test --- .../annimon/ownlang/stages/StagesData.java | 5 ++ .../error}/ParseErrorsFormatterStage.java | 7 +-- .../ownlang/stages/OptimizationStage.java | 2 +- .../annimon/ownlang/parser/ProgramsTest.java | 51 ++++++++----------- 4 files changed, 31 insertions(+), 34 deletions(-) rename ownlang-parser/src/main/java/com/annimon/ownlang/{stages => parser/error}/ParseErrorsFormatterStage.java (92%) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java index b4aadbd2..0f7a4030 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java @@ -4,5 +4,10 @@ public interface StagesData { T get(String tag); + default T getOrDefault(String tag, T other) { + T value = get(tag); + return value != null ? value : other; + } + void put(String tag, Object input); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java similarity index 92% rename from ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java index 5864dd95..4a305bd8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java @@ -1,10 +1,11 @@ -package com.annimon.ownlang.stages; +package com.annimon.ownlang.parser.error; import com.annimon.ownlang.Console; import com.annimon.ownlang.parser.Pos; import com.annimon.ownlang.parser.Range; -import com.annimon.ownlang.parser.error.ParseError; -import com.annimon.ownlang.parser.error.ParseErrors; +import com.annimon.ownlang.stages.SourceLoaderStage; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; public class ParseErrorsFormatterStage implements Stage { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java index 145309b4..dfbe00fa 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java @@ -10,7 +10,7 @@ public record OptimizationStage(int level) @Override public Statement perform(StagesData stagesData, Statement input) { - boolean showSummary = stagesData.get(TAG_OPTIMIZATION_SUMMARY); + boolean showSummary = stagesData.getOrDefault(TAG_OPTIMIZATION_SUMMARY, false); return Optimizer.optimize(input, level, showSummary); } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 44d4e508..1244a6dd 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -1,33 +1,43 @@ package com.annimon.ownlang.parser; -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.outputsettings.OutputSettings; -import com.annimon.ownlang.outputsettings.StringOutputSettings; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.visitors.AbstractVisitor; +import com.annimon.ownlang.stages.*; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; - import java.io.File; -import java.io.IOException; import java.util.stream.Stream; - import static com.annimon.ownlang.parser.TestDataUtil.scanDirectory; import static org.junit.jupiter.api.Assertions.*; public class ProgramsTest { private static final String RES_DIR = "src/test/resources"; + private static Stage testPipeline; public static Stream data() { return scanDirectory(RES_DIR) .map(File::getPath); } + @BeforeAll + public static void createStage() { + testPipeline = new SourceLoaderStage() + .then(new LexerStage()) + .then(new ParserStage()) + .then(new ExecutionStage()) + .then((stagesData, input) -> { + input.accept(testFunctionsExecutor); + return input; + }); + } + @BeforeEach public void initialize() { ScopeHandler.resetScope(); @@ -69,30 +79,11 @@ public void initialize() { @ParameterizedTest @MethodSource("data") - public void testProgram(String programPath) throws IOException { - final String source = SourceLoader.readSource(programPath); - final Statement s = Parser.parse(Lexer.tokenize(source)); - try { - s.execute(); - s.accept(testFunctionsExecutor); - } catch (Exception oae) { - fail(oae.toString()); - } - } - - @Test - public void testOutput() { - OutputSettings oldSettings = Console.getSettings(); - Console.useSettings(new StringOutputSettings()); - String source = "for i = 0, i <= 5, i++\n print i"; - final Statement s = Parser.parse(Lexer.tokenize(source)); + public void testProgram(String programPath) { try { - s.execute(); - assertEquals("012345", Console.text()); + testPipeline.perform(new StagesDataMap(), programPath); } catch (Exception oae) { - fail(oae.toString()); - } finally { - Console.useSettings(oldSettings); + fail(oae); } } From 94bbc05b93f704c05113a90429387a48e0f0a28e Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 2 Oct 2023 17:39:31 +0300 Subject: [PATCH 324/448] Move optimization logic to OptimizationStage class, don't print summary --- .../main/java/com/annimon/ownlang/Main.java | 9 ++- .../com/annimon/ownlang/parser/Optimizer.java | 49 ---------------- .../optimization/OptimizationStage.java | 56 +++++++++++++++++++ .../ownlang/stages/OptimizationStage.java | 16 ------ 4 files changed, 62 insertions(+), 68 deletions(-) delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/Optimizer.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index d6544363..dd02879c 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -4,7 +4,9 @@ import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.parser.linters.LinterStage; +import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.stages.*; import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.Sandbox; @@ -142,13 +144,13 @@ private static void run(String input, RunOptions options) { final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop); final var stagesData = new StagesDataMap(); - stagesData.put(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, options.showAst); stagesData.put(SourceLoaderStage.TAG_SOURCE, input); try { scopedStages.create("Lexer", new LexerStage()) .then(scopedStages.create("Parser", new ParserStage())) .thenConditional(options.optimizationLevel > 0, - scopedStages.create("Optimization", new OptimizationStage(options.optimizationLevel))) + scopedStages.create("Optimization", + new OptimizationStage(options.optimizationLevel, options.showAst))) .thenConditional(options.lintMode, scopedStages.create("Linter", new LinterStage())) .then(scopedStages.create("Function adding", new FunctionAddingStage())) @@ -173,8 +175,9 @@ private static void run(String input, RunOptions options) { if (options.showAst) { Statement program = stagesData.get(ParserStage.TAG_PROGRAM); System.out.println(program); + System.out.println(stagesData.getOrDefault(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, "")); } - if (!options.showMeasurements) { + if (options.showMeasurements) { System.out.println("======================"); System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Optimizer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Optimizer.java deleted file mode 100644 index a4f2ff61..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Optimizer.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.annimon.ownlang.parser; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.parser.ast.Node; -import com.annimon.ownlang.parser.ast.Statement; -import com.annimon.ownlang.parser.optimization.ConstantFolding; -import com.annimon.ownlang.parser.optimization.ConstantPropagation; -import com.annimon.ownlang.parser.optimization.DeadCodeElimination; -import com.annimon.ownlang.parser.optimization.ExpressionSimplification; -import com.annimon.ownlang.parser.optimization.InstructionCombining; -import com.annimon.ownlang.parser.optimization.Optimizable; -import com.annimon.ownlang.parser.optimization.SummaryOptimization; - -public final class Optimizer { - - private Optimizer() { } - - public static Statement optimize(Statement statement, int level, boolean showSummary) { - if (level == 0) return statement; - - final Optimizable optimization = new SummaryOptimization(new Optimizable[] { - new ConstantFolding(), - new ConstantPropagation(), - new DeadCodeElimination(), - new ExpressionSimplification(), - new InstructionCombining() - }); - - Node result = statement; - if (level >= 9) { - int iteration = 0, lastModifications = 0; - do { - lastModifications = optimization.optimizationsCount(); - result = optimization.optimize(result); - iteration++; - } while (lastModifications != optimization.optimizationsCount()); - if (showSummary) - Console.print("Performs " + iteration + " optimization iterations"); - } else { - for (int i = 0; i < level; i++) { - result = optimization.optimize(result); - } - } - if (showSummary) { - Console.println(optimization.summaryInfo()); - } - return (Statement) result; - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java new file mode 100644 index 00000000..ee7811c5 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.parser.optimization; + +import com.annimon.ownlang.parser.ast.Node; +import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; + +public class OptimizationStage implements Stage { + + public static final String TAG_OPTIMIZATION_SUMMARY = "optimizationSummary"; + + private final int level; + private final boolean summary; + private final Optimizable optimization; + + public OptimizationStage(int level) { + this(level, false); + } + + public OptimizationStage(int level, boolean summary) { + this.level = level; + this.summary = summary; + optimization = new SummaryOptimization(new Optimizable[] { + new ConstantFolding(), + new ConstantPropagation(), + new DeadCodeElimination(), + new ExpressionSimplification(), + new InstructionCombining() + }); + } + + @Override + public Statement perform(StagesData stagesData, Statement input) { + if (level == 0) return input; + + Node result = input; + final int maxIterations = level >= 9 ? Integer.MAX_VALUE : level; + int lastModifications; + int iteration = 0; + do { + lastModifications = optimization.optimizationsCount(); + result = optimization.optimize(result); + iteration++; + } while (lastModifications != optimization.optimizationsCount() && iteration < maxIterations); + + if (this.summary) { + stagesData.put(TAG_OPTIMIZATION_SUMMARY, """ + Performed %d optimization iterations + %s + """.formatted(iteration, optimization.summaryInfo()) + ); + } + + return (Statement) result; + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java deleted file mode 100644 index dfbe00fa..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/OptimizationStage.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.annimon.ownlang.stages; - -import com.annimon.ownlang.parser.Optimizer; -import com.annimon.ownlang.parser.ast.Statement; - -public record OptimizationStage(int level) - implements Stage { - - public static final String TAG_OPTIMIZATION_SUMMARY = "optimizationSummary"; - - @Override - public Statement perform(StagesData stagesData, Statement input) { - boolean showSummary = stagesData.getOrDefault(TAG_OPTIMIZATION_SUMMARY, false); - return Optimizer.optimize(input, level, showSummary); - } -} From 1abcffda5df0e7c3ca51f89f588a85566d73d588 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 2 Oct 2023 18:37:11 +0300 Subject: [PATCH 325/448] Fix optimization in use statement and match expression with _ matcher --- ownlang-desktop/build.gradle | 12 +++++- .../ownlang/parser/ast/MatchExpression.java | 38 +++++++++---------- .../parser/optimization/ConstantFolding.java | 5 --- .../optimization/OptimizationVisitor.java | 10 +++-- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index d13ebda5..d1e77406 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -46,4 +46,14 @@ tasks.register('runOptimizing', JavaExec) { classpath = sourceSets.main.runtimeClasspath ignoreExitValue true args '-o 9 -m -a -f ../program.own'.split(' ') -} \ No newline at end of file +} + +tasks.register('runOptimizationDumper', JavaExec) { + group = "application" + description = "Run optmizer and dump results" + dependsOn classes + mainClass = 'com.annimon.ownlang.utils.OptimizationDumper' + classpath = sourceSets.main.runtimeClasspath + args '../program.own' +} +// diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 8af6163c..824cc7e8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -74,7 +74,7 @@ private boolean matchTuplePattern(ArrayValue array, TuplePattern p) { final int size = array.size(); for (int i = 0; i < size; i++) { final Expression expr = p.values.get(i); - if ( (expr != TuplePattern.ANY) && (expr.eval().compareTo(array.get(i)) != 0) ) { + if ( (expr != ANY) && (expr.eval().compareTo(array.get(i)) != 0) ) { return false; } } @@ -279,26 +279,26 @@ public String toString() { } return "()".concat(super.toString()); } + } - private static final Expression ANY = new Expression() { - @Override - public Value eval() { - return NumberValue.ONE; - } + public static final Expression ANY = new Expression() { + @Override + public Value eval() { + return NumberValue.ONE; + } - @Override - public void accept(Visitor visitor) { - } + @Override + public void accept(Visitor visitor) { + } - @Override - public R accept(ResultVisitor visitor, T input) { - return null; - } + @Override + public R accept(ResultVisitor visitor, T input) { + return null; + } - @Override - public String toString() { - return "_".concat(super.toString()); - } - }; - } + @Override + public String toString() { + return "_"; + } + }; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java index ea25ce56..2982af28 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/ConstantFolding.java @@ -99,11 +99,6 @@ public Node visit(UnaryExpression s, Void t) { return super.visit(s, t); } - @Override - public Node visit(UseStatement s, Void unused) { - return null; - } - @Override public Node visit(FunctionDefineStatement s, Void t) { if (OPERATORS.contains(s.name)) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index a903fa83..e3b28d85 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -280,10 +280,12 @@ public Node visit(MatchExpression s, T t) { final List newValues = new ArrayList<>(tuple.values.size()); boolean valuesChanged = false; for (Expression value : tuple.values) { - final Node node = value.accept(this, t); - if (node != value) { - valuesChanged = true; - value = (Expression) node; + if (value != MatchExpression.ANY) { + final Node node = value.accept(this, t); + if (node != value) { + valuesChanged = true; + value = (Expression) node; + } } newValues.add(value); } From 66d86a1b6c795246e157a199b9b84d1365c76a18 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 2 Oct 2023 18:59:16 +0300 Subject: [PATCH 326/448] Fix dead code elimination optimization removes assignment on shadowing module constants --- .../ownlang/parser/optimization/DeadCodeElimination.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java index d5ba7cc3..188e89a9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java @@ -30,7 +30,7 @@ public class DeadCodeElimination extends OptimizationVisitor variableInfos = VariablesGrabber.getInfo(node); + final Map variableInfos = VariablesGrabber.getInfo(node, true); return node.accept(this, variableInfos); } @@ -99,7 +99,7 @@ public Node visit(WhileStatement s, Map t) { @Override public Node visit(AssignmentExpression s, Map t) { - if (!isVariable((Node)s.target)) return super.visit(s, t); + if (!isVariable(s.target)) return super.visit(s, t); final String variableName = ((VariableExpression) s.target).name; if (!t.containsKey(variableName)) return super.visit(s, t); From 3e01978f227dae3857924124179ca65971128749 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 2 Oct 2023 19:00:34 +0300 Subject: [PATCH 327/448] Run tests with maximum optimization --- .../src/test/java/com/annimon/ownlang/parser/ProgramsTest.java | 2 ++ ownlang-parser/src/test/resources/other/useStatementScope.own | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 1244a6dd..a6e059d6 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -6,6 +6,7 @@ import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; +import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.stages.*; import org.junit.jupiter.api.BeforeAll; @@ -31,6 +32,7 @@ public static void createStage() { testPipeline = new SourceLoaderStage() .then(new LexerStage()) .then(new ParserStage()) + .thenConditional(true, new OptimizationStage(9)) .then(new ExecutionStage()) .then((stagesData, input) -> { input.accept(testFunctionsExecutor); diff --git a/ownlang-parser/src/test/resources/other/useStatementScope.own b/ownlang-parser/src/test/resources/other/useStatementScope.own index 32124fa2..f90a6e4e 100644 --- a/ownlang-parser/src/test/resources/other/useStatementScope.own +++ b/ownlang-parser/src/test/resources/other/useStatementScope.own @@ -16,8 +16,7 @@ def testInScope() { assertEquals("fallback", PI) useMath() - - assertEquals("fallback", PI) + assertNotEquals("fallback", PI) assertEquals(3, abs(-3)) } From 8a4dcde2a1858e1abb043020dacfedff0c8706a9 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 2 Oct 2023 19:02:16 +0300 Subject: [PATCH 328/448] Call stack memory optimization --- .../exceptions/OwnLangRuntimeException.java | 6 +++++- .../java/com/annimon/ownlang/lib/CallStack.java | 6 +++--- .../ownlang/parser/ast/FunctionalExpression.java | 15 ++++----------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java index d1212815..b85c0dff 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java @@ -3,7 +3,7 @@ /** * Base type for all runtime exceptions */ -public abstract class OwnLangRuntimeException extends RuntimeException { +public class OwnLangRuntimeException extends RuntimeException { public OwnLangRuntimeException() { super(); @@ -12,4 +12,8 @@ public OwnLangRuntimeException() { public OwnLangRuntimeException(String message) { super(message); } + + public OwnLangRuntimeException(String message, Throwable ex) { + super(message, ex); + } } \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java index eec2060a..5a0b00dc 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -14,7 +14,7 @@ public static synchronized void clear() { } public static synchronized void enter(String name, Function function) { - calls.push(new CallInfo(name, function)); + calls.push(new CallInfo(name, function.toString())); } public static synchronized void exit() { @@ -25,10 +25,10 @@ public static synchronized Deque getCalls() { return calls; } - public record CallInfo(String name, Function function) { + public record CallInfo(String name, String function) { @Override public String toString() { - return String.format("%s: %s", name, function.toString().trim()); + return String.format("%s: %s", name, function); } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 0ffc8338..663e8652 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -1,9 +1,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; -import com.annimon.ownlang.exceptions.UnknownFunctionException; +import com.annimon.ownlang.exceptions.*; import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.Iterator; @@ -42,13 +39,9 @@ public Value eval() { } final Function f = consumeFunction(functionExpr); CallStack.enter(functionExpr.toString(), f); - try { - final Value result = f.execute(values); - CallStack.exit(); - return result; - } catch (ArgumentsMismatchException | TypeException | VariableDoesNotExistsException ex) { - throw new RuntimeException(ex.getMessage() + " in function " + functionExpr, ex); - } + final Value result = f.execute(values); + CallStack.exit(); + return result; } private Function consumeFunction(Expression expr) { From 3e661c81c4e404f62db01aced7e192369d6c5285 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 2 Oct 2023 19:32:45 +0300 Subject: [PATCH 329/448] Simplify variables grabber --- .../parser/optimization/VariablesGrabber.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 939cc3db..928b10f7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -36,12 +36,11 @@ public Node visit(AssignmentExpression s, Map t) { } final String variableName = ((VariableExpression) s.target).name; - final VariableInfo var = variableInfo(t, variableName); + final VariableInfo var = grabVariableInfo(t, variableName); if (s.operation == null && isValue(s.expression)) { var.value = ((ValueExpression) s.expression).value; } - t.put(variableName, var); return super.visit(s, t); } @@ -49,21 +48,21 @@ public Node visit(AssignmentExpression s, Map t) { public Node visit(DestructuringAssignmentStatement s, Map t) { for (String variableName : s.variables) { if (variableName == null) continue; - t.put(variableName, variableInfo(t, variableName)); + grabVariableInfo(t, variableName); } return super.visit(s, t); } @Override public Node visit(ForeachArrayStatement s, Map t) { - t.put(s.variable, variableInfo(t, s.variable)); + grabVariableInfo(t, s.variable); return super.visit(s, t); } @Override public Node visit(ForeachMapStatement s, Map t) { - t.put(s.key, variableInfo(t, s.key)); - t.put(s.value, variableInfo(t, s.value)); + grabVariableInfo(t, s.key); + grabVariableInfo(t, s.value); return super.visit(s, t); } @@ -72,7 +71,7 @@ public Node visit(MatchExpression s, Map t) { for (MatchExpression.Pattern pattern : s.patterns) { if (pattern instanceof MatchExpression.VariablePattern varPattern) { final String variableName = varPattern.variable; - t.put(variableName, variableInfo(t, variableName)); + grabVariableInfo(t, variableName); } } return super.visit(s, t); @@ -82,13 +81,12 @@ public Node visit(MatchExpression s, Map t) { public Node visit(UnaryExpression s, Map t) { if (s.expr1 instanceof Accessible) { if (s.expr1 instanceof VariableExpression varExpr) { - final String variableName = varExpr.name; - t.put(variableName, variableInfo(t, variableName)); + grabVariableInfo(t, varExpr.name); } if (s.expr1 instanceof ContainerAccessExpression conExpr) { if (conExpr.rootIsVariable()) { final String variableName = ((VariableExpression) conExpr.root).name; - t.put(variableName, variableInfo(t, variableName)); + grabVariableInfo(t, variableName); } } } @@ -99,9 +97,8 @@ public Node visit(UnaryExpression s, Map t) { public Node visit(UseStatement s, Map t) { if (grabModuleConstants) { for (Map.Entry entry : s.loadConstants().entrySet()) { - final VariableInfo var = variableInfo(t, entry.getKey()); + final VariableInfo var = grabVariableInfo(t, entry.getKey()); var.value = entry.getValue(); - t.put(entry.getKey(), var); } } return super.visit(s, t); @@ -111,20 +108,19 @@ public Node visit(UseStatement s, Map t) { protected boolean visit(Arguments in, Arguments out, Map t) { for (Argument argument : in) { final String variableName = argument.name(); - final VariableInfo var = variableInfo(t, variableName); + grabVariableInfo(t, variableName); /* No need to add value - it is optional arguments final Expression expr = argument.getValueExpr(); if (expr != null && isValue(expr)) { var.value = ((ValueExpression) expr).value; }*/ - t.put(variableName, var); } return super.visit(in, out, t); } - private VariableInfo variableInfo(Map t, final String variableName) { + private VariableInfo grabVariableInfo(Map t, final String variableName) { final VariableInfo var; if (t.containsKey(variableName)) { var = t.get(variableName); @@ -132,6 +128,7 @@ private VariableInfo variableInfo(Map t, final String vari } else { var = new VariableInfo(); var.modifications = 1; + t.put(variableName, var); } return var; } From 34298bceb81b0f8c373435305d0784563f533fd6 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Tue, 3 Oct 2023 22:48:43 +0300 Subject: [PATCH 330/448] Fix function call statement passes Variable expression instead of function name value --- .../main/java/com/annimon/ownlang/parser/Parser.java | 8 +++++++- .../ownlang/parser/ast/FunctionalExpression.java | 12 ++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 0cc90a29..53950bb0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -160,7 +160,7 @@ private Statement statement() { return classDeclaration(); } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return new ExprStatement(functionChain(qualifiedName())); + return functionCallStatement(); } return assignmentStatement(); } @@ -321,6 +321,12 @@ private Statement statementBody() { return statementOrBlock(); } + private ExprStatement functionCallStatement() { + return new ExprStatement( + functionChain(new ValueExpression(consume(TokenType.WORD).text())) + ); + } + private Expression functionChain(Expression qualifiedNameExpr) { // f1()()() || f1().f2().f3() || f1().key final Expression expr = function(qualifiedNameExpr); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 663e8652..b84985eb 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -45,15 +45,11 @@ public Value eval() { } private Function consumeFunction(Expression expr) { - try { - final Value value = expr.eval(); - if (value.type() == Types.FUNCTION) { - return ((FunctionValue) value).getValue(); - } - return getFunction(value.asString()); - } catch (VariableDoesNotExistsException ex) { - return getFunction(ex.getVariable()); + final Value value = expr.eval(); + if (value.type() == Types.FUNCTION) { + return ((FunctionValue) value).getValue(); } + return getFunction(value.asString()); } private Function getFunction(String key) { From 39b94a01134de71398e83b8b373b218c4ba0e576 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Tue, 3 Oct 2023 23:08:18 +0300 Subject: [PATCH 331/448] [files] Detailed error message for fopen --- .../annimon/ownlang/modules/files/files.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java index 31e1c76a..8cfd6f6e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -124,16 +124,18 @@ private static class fopen implements Function { @Override public Value execute(Value[] args) { - Arguments.checkAtLeast(1, args.length); + Arguments.checkOrOr(1, 2, args.length); final File file = Console.fileInstance(args[0].asString()); try { - if (args.length > 1) { - return process(file, args[1].asString().toLowerCase()); - } - return process(file, "r"); + final String mode = (args.length == 2) + ? args[1].asString().toLowerCase() + : "r"; + return process(file, mode); } catch (IOException ioe) { - return NumberValue.MINUS_ONE; + final int key = -files.size() - 1; + files.put(key, new FileInfo(ioe.getMessage())); + return NumberValue.of(key); } } @@ -167,8 +169,10 @@ private abstract static class FileFunction implements Function { public Value execute(Value[] args) { if (args.length < 1) throw new ArgumentsMismatchException("File descriptor expected"); final int key = args[0].asInt(); + final FileInfo fileInfo = files.get(key); + if (key < 0) throw new ArgumentsMismatchException(fileInfo.error); try { - return execute(files.get(key), args); + return execute(fileInfo, args); } catch (IOException ioe) { return NumberValue.MINUS_ONE; } @@ -578,12 +582,17 @@ private static Function fileToBoolean(FileToBooleanFunction f) { private static class FileInfo { File file; + String error; DataInputStream dis; DataOutputStream dos; BufferedReader reader; BufferedWriter writer; - public FileInfo(File file, DataInputStream dis, DataOutputStream dos, BufferedReader reader, BufferedWriter writer) { + FileInfo(String errorMessage) { + this.error = errorMessage; + } + + FileInfo(File file, DataInputStream dis, DataOutputStream dos, BufferedReader reader, BufferedWriter writer) { this.file = file; this.dis = dis; this.dos = dos; From 1fb9c8b3c580c00e14f606ae6766dd4dfe2b6c3d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 15:09:44 +0300 Subject: [PATCH 332/448] Show function call position in call stack --- .../com/annimon/ownlang/lib/CallStack.java | 20 +++++++++++++++---- .../java/com/annimon/ownlang/util}/Pos.java | 2 +- .../java/com/annimon/ownlang/util}/Range.java | 6 ++++-- .../annimon/ownlang/util/SourceLocation.java | 13 ++++++++++++ .../exceptions/BaseParserException.java | 2 +- .../ownlang/exceptions/ParseException.java | 2 +- .../ownlang/lib/UserDefinedFunction.java | 2 +- .../com/annimon/ownlang/parser/Lexer.java | 2 ++ .../com/annimon/ownlang/parser/Parser.java | 4 ++++ .../com/annimon/ownlang/parser/Token.java | 2 ++ .../parser/ast/FunctionalExpression.java | 19 +++++++++++++++--- .../ownlang/parser/error/ParseError.java | 2 +- .../error/ParseErrorsFormatterStage.java | 4 ++-- .../java/com/annimon/ownlang/utils/Repl.java | 1 + 14 files changed, 65 insertions(+), 16 deletions(-) rename {ownlang-parser/src/main/java/com/annimon/ownlang/parser => ownlang-core/src/main/java/com/annimon/ownlang/util}/Pos.java (90%) rename {ownlang-parser/src/main/java/com/annimon/ownlang/parser => ownlang-core/src/main/java/com/annimon/ownlang/util}/Range.java (75%) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java index 5a0b00dc..e0a8cfec 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -13,8 +13,16 @@ public static synchronized void clear() { calls.clear(); } - public static synchronized void enter(String name, Function function) { - calls.push(new CallInfo(name, function.toString())); + public static synchronized void enter(String name, Function function, String position) { + String func = function.toString(); + if (func.contains("com.annimon.ownlang.modules")) { + func = func.replaceAll( + "com.annimon.ownlang.modules.(\\w+)\\.?\\1?", "module $1"); + } + if (func.contains("\n")) { + func = func.substring(0, func.indexOf("\n")).trim(); + } + calls.push(new CallInfo(name, func, position)); } public static synchronized void exit() { @@ -25,10 +33,14 @@ public static synchronized Deque getCalls() { return calls; } - public record CallInfo(String name, String function) { + public record CallInfo(String name, String function, String position) { @Override public String toString() { - return String.format("%s: %s", name, function); + if (position == null) { + return String.format("%s: %s", name, function); + } else { + return String.format("%s: %s %s", name, function, position); + } } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/Pos.java similarity index 90% rename from ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java rename to ownlang-core/src/main/java/com/annimon/ownlang/util/Pos.java index f5a12173..6c72e523 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Pos.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/Pos.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.parser; +package com.annimon.ownlang.util; public record Pos(int row, int col) { public static final Pos UNKNOWN = new Pos(-1, -1); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java similarity index 75% rename from ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java rename to ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java index 6902d483..b753d9d9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Range.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java @@ -1,4 +1,4 @@ -package com.annimon.ownlang.parser; +package com.annimon.ownlang.util; import java.util.Objects; @@ -18,8 +18,10 @@ public boolean isOnSameLine() { } public String format() { - if (isOnSameLine()) { + if (isEqualPosition()) return start.format(); + else if (isOnSameLine()) { + return "[%d:%d~%d]".formatted(start.row(), start.col(), end.col()); } else { return start.format() + "..." + end.format(); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java new file mode 100644 index 00000000..f3803144 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.util; + +public interface SourceLocation { + + default Range getRange() { + return null; + } + + default String formatRange() { + final var range = getRange(); + return range == null ? "" : range.format(); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java index 1d59a0c4..c323a116 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -import com.annimon.ownlang.parser.Range; +import com.annimon.ownlang.util.Range; /** * Base type for all lexer and parser exceptions diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index 2113cbc7..c4adc1d0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.exceptions; -import com.annimon.ownlang.parser.Range; +import com.annimon.ownlang.util.Range; /** * diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index b0a92be7..ce5b15ec 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -31,7 +31,7 @@ public String getArgsName(int index) { } @Override - public Value execute(Value... values) { + public Value execute(Value[] values) { final int size = values.length; final int requiredArgsCount = arguments.getRequiredArgumentsCount(); if (size < requiredArgsCount) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 7ba7fc57..72663ef3 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.parser.error.ParseError; +import com.annimon.ownlang.util.Pos; +import com.annimon.ownlang.util.Range; import java.util.*; /** diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 53950bb0..149c3b7e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -8,6 +8,8 @@ import com.annimon.ownlang.parser.ast.*; import com.annimon.ownlang.parser.error.ParseError; import com.annimon.ownlang.parser.error.ParseErrors; +import com.annimon.ownlang.util.Pos; +import com.annimon.ownlang.util.Range; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -351,12 +353,14 @@ private Expression functionChain(Expression qualifiedNameExpr) { private FunctionalExpression function(Expression qualifiedNameExpr) { // function(arg1, arg2, ...) + final var startTokenIndex = index - 1; consume(TokenType.LPAREN); final FunctionalExpression function = new FunctionalExpression(qualifiedNameExpr); while (!match(TokenType.RPAREN)) { function.addArgument(expression()); match(TokenType.COMMA); } + function.setRange(getRange(startTokenIndex, index - 1)); return function; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java index 8d151cd4..134681d1 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.util.Pos; + /** * @author aNNiMON */ diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index b84985eb..59d538e7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.exceptions.*; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -10,10 +12,12 @@ * * @author aNNiMON */ -public final class FunctionalExpression extends InterruptableNode implements Expression, Statement { +public final class FunctionalExpression extends InterruptableNode + implements Expression, Statement, SourceLocation { public final Expression functionExpr; public final List arguments; + private Range range; public FunctionalExpression(Expression functionExpr) { this.functionExpr = functionExpr; @@ -23,7 +27,16 @@ public FunctionalExpression(Expression functionExpr) { public void addArgument(Expression arg) { arguments.add(arg); } - + + public void setRange(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } + @Override public void execute() { eval(); @@ -38,7 +51,7 @@ public Value eval() { values[i] = arguments.get(i).eval(); } final Function f = consumeFunction(functionExpr); - CallStack.enter(functionExpr.toString(), f); + CallStack.enter(functionExpr.toString(), f, formatRange()); final Value result = f.execute(values); CallStack.exit(); return result; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java index 8fba8eff..d4dd19d8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser.error; -import com.annimon.ownlang.parser.Range; +import com.annimon.ownlang.util.Range; import java.util.Collections; import java.util.List; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java index 4a305bd8..cafc52f4 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java @@ -1,8 +1,8 @@ package com.annimon.ownlang.parser.error; import com.annimon.ownlang.Console; -import com.annimon.ownlang.parser.Pos; -import com.annimon.ownlang.parser.Range; +import com.annimon.ownlang.util.Pos; +import com.annimon.ownlang.util.Range; import com.annimon.ownlang.stages.SourceLoaderStage; import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 8d1bf06d..3f3f9599 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -8,6 +8,7 @@ import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.util.Pos; import com.annimon.ownlang.parser.visitors.PrintVisitor; import com.annimon.ownlang.utils.repl.JLineConsole; import com.annimon.ownlang.utils.repl.OwnLangCompleter; From 02e9d1f6c5a75d44cede4853456013540432cca9 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 19:30:35 +0300 Subject: [PATCH 333/448] Generalized way to show source located errors (parse and runtime errors) --- .../java/com/annimon/ownlang/Console.java | 16 +++++ .../exceptions/OwnLangRuntimeException.java | 19 +++++- .../com/annimon/ownlang/lib/CallStack.java | 22 +++++-- .../util/ErrorsLocationFormatterStage.java | 55 +++++++++++++++++ .../util/ErrorsStackTraceFormatterStage.java | 20 +++++++ .../ownlang/util/ExceptionConverterStage.java | 12 ++++ .../ExceptionStackTraceToStringStage.java | 20 +++++++ .../com/annimon/ownlang/util/SimpleError.java | 13 ++++ .../ownlang/util/SourceLoaderStage.java | 48 +++++++++++++++ .../ownlang/util/SourceLocatedError.java | 19 ++++++ .../annimon/ownlang/util/SourceLocation.java | 5 -- .../main/java/com/annimon/ownlang/Main.java | 6 +- .../com/annimon/ownlang/parser/Parser.java | 2 +- .../parser/ast/FunctionalExpression.java | 2 +- .../ownlang/parser/error/ParseError.java | 27 +++++++-- .../error/ParseErrorsFormatterStage.java | 59 +++---------------- .../ownlang/stages/SourceLoaderStage.java | 21 ------- .../annimon/ownlang/parser/ProgramsTest.java | 1 + .../com/annimon/ownlang/utils/Sandbox.java | 2 +- 19 files changed, 274 insertions(+), 95 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsStackTraceFormatterStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionConverterStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionStackTraceToStringStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocatedError.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/Console.java b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java index 888d9f75..76cafa9c 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/Console.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java @@ -3,10 +3,15 @@ import com.annimon.ownlang.lib.CallStack; import com.annimon.ownlang.outputsettings.ConsoleOutputSettings; import com.annimon.ownlang.outputsettings.OutputSettings; +import com.annimon.ownlang.stages.StagesData; +import com.annimon.ownlang.util.ErrorsLocationFormatterStage; +import com.annimon.ownlang.util.ExceptionConverterStage; +import com.annimon.ownlang.util.ExceptionStackTraceToStringStage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; import java.nio.charset.StandardCharsets; +import java.util.List; public class Console { @@ -58,6 +63,17 @@ public static void error(CharSequence value) { outputSettings.error(value); } + public static void handleException(StagesData stagesData, Thread thread, Exception exception) { + String mainError = new ExceptionConverterStage() + .then((data, error) -> List.of(error)) + .then(new ErrorsLocationFormatterStage()) + .perform(stagesData, exception); + String callStack = CallStack.getFormattedCalls(); + String stackTrace = new ExceptionStackTraceToStringStage() + .perform(stagesData, exception); + error(String.join("\n", mainError, "Thread: " + thread.getName(), callStack, stackTrace)); + } + public static void handleException(Thread thread, Throwable throwable) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try(final PrintStream ps = new PrintStream(baos)) { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java index b85c0dff..b353fc07 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java @@ -1,19 +1,36 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocatedError; + /** * Base type for all runtime exceptions */ -public class OwnLangRuntimeException extends RuntimeException { +public class OwnLangRuntimeException extends RuntimeException implements SourceLocatedError { + + private final Range range; public OwnLangRuntimeException() { super(); + this.range = null; } public OwnLangRuntimeException(String message) { + this(message, (Range) null); + } + + public OwnLangRuntimeException(String message, Range range) { super(message); + this.range = range; } public OwnLangRuntimeException(String message, Throwable ex) { super(message, ex); + this.range = null; + } + + @Override + public Range getRange() { + return range; } } \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java index e0a8cfec..0b8fffbd 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -1,7 +1,9 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.util.Range; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.stream.Collectors; public final class CallStack { @@ -13,7 +15,7 @@ public static synchronized void clear() { calls.clear(); } - public static synchronized void enter(String name, Function function, String position) { + public static synchronized void enter(String name, Function function, Range range) { String func = function.toString(); if (func.contains("com.annimon.ownlang.modules")) { func = func.replaceAll( @@ -22,7 +24,7 @@ public static synchronized void enter(String name, Function function, String pos if (func.contains("\n")) { func = func.substring(0, func.indexOf("\n")).trim(); } - calls.push(new CallInfo(name, func, position)); + calls.push(new CallInfo(name, func, range)); } public static synchronized void exit() { @@ -32,14 +34,24 @@ public static synchronized void exit() { public static synchronized Deque getCalls() { return calls; } + + public static String getFormattedCalls() { + return calls.stream() + .map(CallInfo::format) + .collect(Collectors.joining("\n")); + } - public record CallInfo(String name, String function, String position) { + public record CallInfo(String name, String function, Range range) { + String format() { + return "\tat " + this; + } + @Override public String toString() { - if (position == null) { + if (range == null) { return String.format("%s: %s", name, function); } else { - return String.format("%s: %s %s", name, function, position); + return String.format("%s: %s %s", name, function, range.format()); } } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java new file mode 100644 index 00000000..8712cbfa --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.util; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; + +public class ErrorsLocationFormatterStage implements Stage, String> { + + @Override + public String perform(StagesData stagesData, Iterable input) { + final var sb = new StringBuilder(); + final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE); + final var lines = source.split("\r?\n"); + for (SourceLocatedError error : input) { + sb.append(Console.newline()); + sb.append(error); + sb.append(Console.newline()); + final Range range = error.getRange(); + if (range != null) { + printPosition(sb, range.normalize(), lines); + } + } + return sb.toString(); + } + + private static void printPosition(StringBuilder sb, Range range, String[] lines) { + final Pos start = range.start(); + final int linesCount = lines.length;; + if (range.isOnSameLine()) { + if (start.row() < linesCount) { + sb.append(lines[start.row()]); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(range.end().col() - start.col() + 1)); + sb.append(Console.newline()); + } + } else { + if (start.row() < linesCount) { + String line = lines[start.row()]; + sb.append(line); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(Math.max(1, line.length() - start.col()))); + sb.append(Console.newline()); + } + final Pos end = range.end(); + if (end.row() < linesCount) { + sb.append(lines[end.row()]); + sb.append(Console.newline()); + sb.append("^".repeat(end.col())); + sb.append(Console.newline()); + } + } + } +} \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsStackTraceFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsStackTraceFormatterStage.java new file mode 100644 index 00000000..1f0356ed --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsStackTraceFormatterStage.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.util; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; + +public class ErrorsStackTraceFormatterStage implements Stage, String> { + + @Override + public String perform(StagesData stagesData, Iterable input) { + final var sb = new StringBuilder(); + for (SourceLocatedError error : input) { + if (!error.hasStackTrace()) continue; + for (StackTraceElement el : error.getStackTrace()) { + sb.append("\t").append(el).append(Console.newline()); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionConverterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionConverterStage.java new file mode 100644 index 00000000..1b912d17 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionConverterStage.java @@ -0,0 +1,12 @@ +package com.annimon.ownlang.util; + +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; + +public class ExceptionConverterStage implements Stage { + @Override + public SourceLocatedError perform(StagesData stagesData, Exception ex) { + if (ex instanceof SourceLocatedError sle) return sle; + return new SimpleError(ex.getMessage()); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionStackTraceToStringStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionStackTraceToStringStage.java new file mode 100644 index 00000000..3b5150f5 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionStackTraceToStringStage.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.util; + +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + +public class ExceptionStackTraceToStringStage implements Stage { + @Override + public String perform(StagesData stagesData, Exception ex) { + final var baos = new ByteArrayOutputStream(); + try (final PrintStream ps = new PrintStream(baos)) { + for (StackTraceElement traceElement : ex.getStackTrace()) { + ps.println("\tat " + traceElement); + } + } + return baos.toString(StandardCharsets.UTF_8); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java new file mode 100644 index 00000000..3b174940 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.util; + +public record SimpleError(String message) implements SourceLocatedError { + @Override + public String getMessage() { + return message; + } + + @Override + public String toString() { + return message; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java new file mode 100644 index 00000000..f117c2e9 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java @@ -0,0 +1,48 @@ +package com.annimon.ownlang.util; + +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class SourceLoaderStage implements Stage { + + public static final String TAG_SOURCE = "source"; + + @Override + public String perform(StagesData stagesData, String name) { + try { + String result = readSource(name); + stagesData.put(TAG_SOURCE, result); + return result; + } catch (IOException e) { + throw new OwnLangRuntimeException("Unable to read input " + name, e); + } + } + + private String readSource(String name) throws IOException { + try (InputStream is = getClass().getResourceAsStream("/" + name)) { + if (is != null) { + return readStream(is); + } + } + try (InputStream is = new FileInputStream(name)) { + return readStream(is); + } + } + + public static String readStream(InputStream is) throws IOException { + final ByteArrayOutputStream result = new ByteArrayOutputStream(); + final int bufferSize = 1024; + final byte[] buffer = new byte[bufferSize]; + int read; + while ((read = is.read(buffer)) != -1) { + result.write(buffer, 0, read); + } + return result.toString(StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocatedError.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocatedError.java new file mode 100644 index 00000000..9487aaf2 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocatedError.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.util; + +public interface SourceLocatedError extends SourceLocation { + + String getMessage(); + + default StackTraceElement[] getStackTrace() { + return new StackTraceElement[0]; + } + + default boolean hasStackTrace() { + return !stackTraceIsEmpty(); + } + + private boolean stackTraceIsEmpty() { + final var st = getStackTrace(); + return st == null || st.length == 0; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java index f3803144..af7aa003 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java @@ -5,9 +5,4 @@ public interface SourceLocation { default Range getRange() { return null; } - - default String formatRange() { - final var range = getRange(); - return range == null ? "" : range.format(); - } } diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index dd02879c..4ba94694 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -2,7 +2,9 @@ import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; -import com.annimon.ownlang.parser.*; +import com.annimon.ownlang.parser.Beautifier; +import com.annimon.ownlang.parser.SourceLoader; +import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.parser.linters.LinterStage; @@ -163,7 +165,7 @@ private static void run(String input, RunOptions options) { } catch (StoppedException ex) { // skip } catch (Exception ex) { - Console.handleException(Thread.currentThread(), ex); + Console.handleException(stagesData, Thread.currentThread(), ex); } finally { if (options.showTokens) { final List tokens = stagesData.get(LexerStage.TAG_TOKENS); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 149c3b7e..a9dbd624 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -81,7 +81,7 @@ public Statement parse() { parseErrors.add(new ParseError(ex.getMessage(), ex.getRange())); recover(); } catch (Exception ex) { - parseErrors.add(new ParseError(ex.getMessage(), getRange(), List.of(ex.getStackTrace()))); + parseErrors.add(new ParseError(ex.getMessage(), getRange(), ex.getStackTrace())); recover(); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 59d538e7..f64734f9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -51,7 +51,7 @@ public Value eval() { values[i] = arguments.get(i).eval(); } final Function f = consumeFunction(functionExpr); - CallStack.enter(functionExpr.toString(), f, formatRange()); + CallStack.enter(functionExpr.toString(), f, range); final Value result = f.execute(values); CallStack.exit(); return result; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java index d4dd19d8..d67b1086 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java @@ -1,16 +1,31 @@ package com.annimon.ownlang.parser.error; import com.annimon.ownlang.util.Range; -import java.util.Collections; -import java.util.List; +import com.annimon.ownlang.util.SourceLocatedError; + +public record ParseError( + String message, + Range range, + StackTraceElement[] stackTraceElements +) implements SourceLocatedError { -public record ParseError(String message, Range range, List stackTraceElements) { public ParseError(String message, Range range) { - this(message, range, Collections.emptyList()); + this(message, range, new StackTraceElement[0]); + } + + @Override + public String getMessage() { + return message; } - public boolean hasStackTrace() { - return !stackTraceElements.isEmpty(); + @Override + public Range getRange() { + return range; + } + + @Override + public StackTraceElement[] getStackTrace() { + return stackTraceElements; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java index cafc52f4..9e9611f0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java @@ -1,63 +1,18 @@ package com.annimon.ownlang.parser.error; -import com.annimon.ownlang.Console; -import com.annimon.ownlang.util.Pos; -import com.annimon.ownlang.util.Range; -import com.annimon.ownlang.stages.SourceLoaderStage; import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; +import com.annimon.ownlang.util.ErrorsLocationFormatterStage; +import com.annimon.ownlang.util.ErrorsStackTraceFormatterStage; public class ParseErrorsFormatterStage implements Stage { @Override public String perform(StagesData stagesData, ParseErrors input) { - final var sb = new StringBuilder(); - final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE); - final var lines = source.split("\r?\n"); - for (ParseError parseError : input) { - sb.append(Console.newline()); - sb.append(parseError); - sb.append(Console.newline()); - final Range range = parseError.range().normalize(); - printPosition(sb, range, lines); - if (parseError.hasStackTrace()) { - sb.append("Stack trace:"); - sb.append(Console.newline()); - for (StackTraceElement el : parseError.stackTraceElements()) { - sb.append(" ").append(el).append(Console.newline()); - } - } - } - return sb.toString(); - } - - private static void printPosition(StringBuilder sb, Range range, String[] lines) { - final Pos start = range.start(); - final int linesCount = lines.length;; - if (range.isOnSameLine()) { - if (start.row() < linesCount) { - sb.append(lines[start.row()]); - sb.append(Console.newline()); - sb.append(" ".repeat(start.col())); - sb.append("^".repeat(range.end().col() - start.col() + 1)); - sb.append(Console.newline()); - } - } else { - if (start.row() < linesCount) { - String line = lines[start.row()]; - sb.append(line); - sb.append(Console.newline()); - sb.append(" ".repeat(start.col())); - sb.append("^".repeat(Math.max(1, line.length() - start.col()))); - sb.append(Console.newline()); - } - final Pos end = range.end(); - if (end.row() < linesCount) { - sb.append(lines[end.row()]); - sb.append(Console.newline()); - sb.append("^".repeat(end.col())); - sb.append(Console.newline()); - } - } + String error = new ErrorsLocationFormatterStage() + .perform(stagesData, input); + String stackTrace = new ErrorsStackTraceFormatterStage() + .perform(stagesData, input); + return error + "\n" + stackTrace; } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java deleted file mode 100644 index a9d70fcb..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/SourceLoaderStage.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.annimon.ownlang.stages; - -import com.annimon.ownlang.exceptions.OwnLangRuntimeException; -import com.annimon.ownlang.parser.SourceLoader; -import java.io.IOException; - -public class SourceLoaderStage implements Stage { - - public static final String TAG_SOURCE = "source"; - - @Override - public String perform(StagesData stagesData, String input) { - try { - String result = SourceLoader.readSource(input); - stagesData.put(TAG_SOURCE, result); - return result; - } catch (IOException e) { - throw new OwnLangRuntimeException("Unable to read input " + input, e); - } - } -} diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index a6e059d6..afc44c5e 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -9,6 +9,7 @@ import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.stages.*; +import com.annimon.ownlang.util.SourceLoaderStage; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java index 7d23ed69..27f75ed7 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -46,7 +46,7 @@ public File fileInstance(String path) { } catch (Exception ex) { // ownlang call stack to stdout System.out.format("%s in %s%n", ex.getMessage(), Thread.currentThread().getName()); - CallStack.getCalls().forEach(call -> System.out.format("\tat %s%n", call)); + System.out.println(CallStack.getFormattedCalls()); // java stack trace to stderr Console.handleException(Thread.currentThread(), ex); } From 368cc8612c7f24ff7f7b7bc0f2542f7a49682a6d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 19:34:29 +0300 Subject: [PATCH 334/448] Limit call stack function output length --- .../src/main/java/com/annimon/ownlang/lib/CallStack.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java index 0b8fffbd..bf016551 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -6,7 +6,8 @@ import java.util.stream.Collectors; public final class CallStack { - + + private static final int MAX_FUNCTION_LENGTH = 62; private static final Deque calls = new ConcurrentLinkedDeque<>(); private CallStack() { } @@ -24,6 +25,9 @@ public static synchronized void enter(String name, Function function, Range rang if (func.contains("\n")) { func = func.substring(0, func.indexOf("\n")).trim(); } + if (func.length() > MAX_FUNCTION_LENGTH) { + func = func.substring(0, MAX_FUNCTION_LENGTH) + "..."; + } calls.push(new CallInfo(name, func, range)); } From 5267ff6144fb1e4dbb239c4753e392ef403e8752 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 19:38:12 +0300 Subject: [PATCH 335/448] Call stack for class constructor --- .../java/com/annimon/ownlang/lib/ClassInstanceValue.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java index 9a2ff767..3def100e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java @@ -40,7 +40,9 @@ public void addMethod(String name, ClassMethod method) { public void callConstructor(Value[] args) { if (constructor != null) { + CallStack.enter("class " + className, constructor, null); constructor.execute(args); + CallStack.exit(); } } @@ -64,18 +66,18 @@ public Object raw() { @Override public int asInt() { - throw new TypeException("Cannot cast class to integer"); + throw new TypeException("Cannot cast class " + className + " to integer"); } @Override public double asNumber() { - throw new TypeException("Cannot cast class to integer"); + throw new TypeException("Cannot cast class " + className + " to number"); } @Override public String asString() { if (toString != null) { - return toString.execute(new Value[] {}).asString(); + return toString.execute(new Value[0]).asString(); } return className + "@" + thisMap; } From 807ffd44f8f7d204c0c66e3a2037aefdad63639b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 19:39:13 +0300 Subject: [PATCH 336/448] Source location for unknown class/function exceptions --- .../exceptions/UnknownClassException.java | 7 +++++++ .../exceptions/UnknownFunctionException.java | 7 +++++++ .../java/com/annimon/ownlang/parser/Parser.java | 7 +++++-- .../ownlang/parser/ast/FunctionalExpression.java | 2 +- .../parser/ast/ObjectCreationExpression.java | 16 ++++++++++++++-- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java index 65b2fc1d..8c2bed3b 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.util.Range; + public final class UnknownClassException extends OwnLangRuntimeException { private final String className; @@ -9,6 +11,11 @@ public UnknownClassException(String name) { this.className = name; } + public UnknownClassException(String name, Range range) { + super("Unknown class " + name, range); + this.className = name; + } + public String getClassName() { return className; } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java index 9aa2a9e4..3d5328d4 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.util.Range; + public final class UnknownFunctionException extends OwnLangRuntimeException { private final String functionName; @@ -9,6 +11,11 @@ public UnknownFunctionException(String name) { this.functionName = name; } + public UnknownFunctionException(String name, Range range) { + super("Unknown function " + name, range); + this.functionName = name; + } + public String getFunctionName() { return functionName; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index a9dbd624..cf63114b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -746,7 +746,8 @@ private Expression multiplicative() { } private Expression objectCreation() { - if (match(TokenType.NEW)) { + if (match(TokenType.NEW)) { + final var startTokenIndex = index - 1; final String className = consume(TokenType.WORD).text(); final List args = new ArrayList<>(); consume(TokenType.LPAREN); @@ -754,7 +755,9 @@ private Expression objectCreation() { args.add(expression()); match(TokenType.COMMA); } - return new ObjectCreationExpression(className, args); + final var expr = new ObjectCreationExpression(className, args); + expr.setRange(getRange(startTokenIndex, index - 1)); + return expr; } return unary(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index f64734f9..733a22b6 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -75,7 +75,7 @@ private Function getFunction(String key) { return ((FunctionValue)variable).getValue(); } } - throw new UnknownFunctionException(key); + throw new UnknownFunctionException(key, getRange()); } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index 1f5ec251..03476e1c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -2,18 +2,30 @@ import com.annimon.ownlang.exceptions.UnknownClassException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; import java.util.Iterator; import java.util.List; -public final class ObjectCreationExpression implements Expression { +public final class ObjectCreationExpression implements Expression, SourceLocation { public final String className; public final List constructorArguments; + private Range range; public ObjectCreationExpression(String className, List constructorArguments) { this.className = className; this.constructorArguments = constructorArguments; } + + public void setRange(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } @Override public Value eval() { @@ -26,7 +38,7 @@ public Value eval() { return instantiable.newInstance(ctorArgs()); } } - throw new UnknownClassException(className); + throw new UnknownClassException(className, getRange()); } // Create an instance and put evaluated fields with method declarations From 8474f87358f2d30dca969a068adfcce44ca1a008 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 19:47:23 +0300 Subject: [PATCH 337/448] Formatted error output in program tests --- .../com/annimon/ownlang/parser/ProgramsTest.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index afc44c5e..63ae75e8 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -1,11 +1,14 @@ package com.annimon.ownlang.parser; +import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; +import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.stages.*; @@ -83,10 +86,16 @@ public void initialize() { @ParameterizedTest @MethodSource("data") public void testProgram(String programPath) { + final StagesDataMap stagesData = new StagesDataMap(); try { - testPipeline.perform(new StagesDataMap(), programPath); + testPipeline.perform(stagesData, programPath); + } catch (OwnLangParserException ex) { + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + fail(programPath + "\n" + error, ex); } catch (Exception oae) { - fail(oae); + fail(programPath, oae); + Console.handleException(stagesData, Thread.currentThread(), oae); } } From 5dcf31c106b3de310c46a0db2eb1595f807347a0 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 4 Oct 2023 19:47:54 +0300 Subject: [PATCH 338/448] Remove unused methods in Scopes --- .../src/main/java/com/annimon/ownlang/lib/Scope.java | 4 ---- .../src/main/java/com/annimon/ownlang/lib/ScopeHandler.java | 6 ------ 2 files changed, 10 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java index a92d6046..078d7d9e 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java @@ -44,10 +44,6 @@ public final void setVariable(String name, Value value) { variables.put(name, value); } - public final void removeVariable(String name) { - variables.remove(name); - } - public Map getVariables() { return variables; } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java index 8913fc7f..a61d6caa 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -129,12 +129,6 @@ public static void defineVariableInCurrentScope(String name, Value value) { } } - public static void removeVariable(String name) { - synchronized (lock) { - findScope(name).scope.removeVariable(name); - } - } - private static ScopeFindData findScope(String name) { Scope current = scope; do { From c5810f0deb30843dd448561449094c4d8895649d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 13:41:47 +0300 Subject: [PATCH 339/448] Add input sources and source loading stage --- .../util/ErrorsLocationFormatterStage.java | 5 +- .../ownlang/util/input/InputSource.java | 15 +++ .../ownlang/util/input/InputSourceFile.java | 30 ++++++ .../util/input/InputSourceProgram.java | 19 ++++ .../util/input/InputSourceResource.java | 27 +++++ .../util/{ => input}/SourceLoaderStage.java | 22 +---- ownlang-desktop/build.gradle | 1 - .../main/java/com/annimon/ownlang/Main.java | 99 +++++++------------ .../java/com/annimon/ownlang/RunOptions.java | 55 +++++++++++ .../annimon/ownlang/parser/ProgramsTest.java | 19 ++-- 10 files changed, 202 insertions(+), 90 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSource.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceFile.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceProgram.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceResource.java rename ownlang-core/src/main/java/com/annimon/ownlang/util/{ => input}/SourceLoaderStage.java (61%) create mode 100644 ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java index 8712cbfa..2b73da72 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java @@ -3,20 +3,21 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; +import com.annimon.ownlang.util.input.SourceLoaderStage; public class ErrorsLocationFormatterStage implements Stage, String> { @Override public String perform(StagesData stagesData, Iterable input) { final var sb = new StringBuilder(); - final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE); + final String source = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE, ""); final var lines = source.split("\r?\n"); for (SourceLocatedError error : input) { sb.append(Console.newline()); sb.append(error); sb.append(Console.newline()); final Range range = error.getRange(); - if (range != null) { + if (range != null && lines.length > 0) { printPosition(sb, range.normalize(), lines); } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSource.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSource.java new file mode 100644 index 00000000..c54d1a2e --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSource.java @@ -0,0 +1,15 @@ +package com.annimon.ownlang.util.input; + +import java.io.IOException; + +public interface InputSource { + String getPath(); + + String load() throws IOException; + + default String getBasePath() { + int i = getPath().lastIndexOf("/"); + if (i == -1) return ""; + return getPath().substring(0, i + 1); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceFile.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceFile.java new file mode 100644 index 00000000..8a9a7864 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceFile.java @@ -0,0 +1,30 @@ +package com.annimon.ownlang.util.input; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +public record InputSourceFile(String path) implements InputSource { + + @Override + public String getPath() { + return path; + } + + @Override + public String load() throws IOException { + if (Files.isReadable(Path.of(path))) { + try (InputStream is = new FileInputStream(path)) { + return SourceLoaderStage.readStream(is); + } + } + throw new IOException(path + " not found"); + } + + @Override + public String toString() { + return "File " + path; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceProgram.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceProgram.java new file mode 100644 index 00000000..392bfb04 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceProgram.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.util.input; + +public record InputSourceProgram(String program) implements InputSource { + + @Override + public String getPath() { + return "."; + } + + @Override + public String load() { + return program; + } + + @Override + public String toString() { + return "Program"; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceResource.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceResource.java new file mode 100644 index 00000000..bece07ed --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceResource.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.util.input; + +import java.io.IOException; +import java.io.InputStream; + +public record InputSourceResource(String path) implements InputSource { + + @Override + public String getPath() { + return path; + } + + @Override + public String load() throws IOException { + try (InputStream is = getClass().getResourceAsStream(path)) { + if (is != null) { + return SourceLoaderStage.readStream(is); + } + } + throw new IOException(path + " not found"); + } + + @Override + public String toString() { + return "Resource " + path; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java similarity index 61% rename from ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java rename to ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java index f117c2e9..f25682d1 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLoaderStage.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java @@ -1,37 +1,25 @@ -package com.annimon.ownlang.util; +package com.annimon.ownlang.util.input; import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -public class SourceLoaderStage implements Stage { +public class SourceLoaderStage implements Stage { public static final String TAG_SOURCE = "source"; @Override - public String perform(StagesData stagesData, String name) { + public String perform(StagesData stagesData, InputSource inputSource) { try { - String result = readSource(name); + String result = inputSource.load(); stagesData.put(TAG_SOURCE, result); return result; } catch (IOException e) { - throw new OwnLangRuntimeException("Unable to read input " + name, e); - } - } - - private String readSource(String name) throws IOException { - try (InputStream is = getClass().getResourceAsStream("/" + name)) { - if (is != null) { - return readStream(is); - } - } - try (InputStream is = new FileInputStream(name)) { - return readStream(is); + throw new OwnLangRuntimeException("Unable to read input " + inputSource, e); } } diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index d1e77406..89176bb8 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -56,4 +56,3 @@ tasks.register('runOptimizationDumper', JavaExec) { classpath = sourceSets.main.runtimeClasspath args '../program.own' } -// diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 4ba94694..c5452bbf 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -3,13 +3,13 @@ import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; import com.annimon.ownlang.parser.Beautifier; -import com.annimon.ownlang.parser.SourceLoader; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.parser.linters.LinterStage; import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.stages.*; +import com.annimon.ownlang.util.input.SourceLoaderStage; import com.annimon.ownlang.utils.Repl; import com.annimon.ownlang.utils.Sandbox; import com.annimon.ownlang.utils.TimeMeasurement; @@ -23,17 +23,12 @@ public final class Main { public static void main(String[] args) throws IOException { - if (args.length == 0) { - try { - runDefault(); - } catch (IOException ioe) { - printUsage(); - } + final RunOptions options = new RunOptions(); + if (args.length == 0 && (options.detectDefaultProgramPath() == null)) { + printUsage(); return; } - final RunOptions options = new RunOptions(); - String input = null; for (int i = 0; i < args.length; i++) { switch (args[i]) { case "-a": @@ -82,73 +77,69 @@ public static void main(String[] args) throws IOException { case "-f": case "--file": - if (i + 1 < args.length) { - input = SourceLoader.readSource(args[i + 1]); + if (i + 1 < args.length) { + options.programPath = args[i + 1]; createOwnLangArgs(args, i + 2); i++; } break; case "--sandbox": - createOwnLangArgs(args, i + 1); - final String[] ownlangArgs = Shared.getOwnlangArgs(); - String[] newArgs = new String[ownlangArgs.length]; - System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length); - Sandbox.main(newArgs); + Sandbox.main(createOwnLangArgs(args, i + 1)); return; default: - if (input == null) { - input = args[i]; + if (options.programSource == null) { + options.programSource = args[i]; createOwnLangArgs(args, i + 1); } break; } } - if (input == null) { - throw new IllegalArgumentException("Empty input"); - } if (options.beautifyMode) { + String input = new SourceLoaderStage() + .perform(new StagesDataMap(), options.toInputSource()); System.out.println(Beautifier.beautify(input)); return; } - run(input, options); - } - - private static void runDefault() throws IOException { - final RunOptions options = new RunOptions(); - run(SourceLoader.readSource("program.own"), options); + run(options); } private static void printUsage() { - System.out.println("OwnLang version " + Version.VERSION + "\n\n" + - "Usage: ownlang [options]\n" + - " options:\n" + - " -f, --file [input] Run program file. Required.\n" + - " -r, --repl Enter to a REPL mode\n" + - " -l, --lint Find bugs in code\n" + - " -o N, --optimize N Perform optimization with N passes\n" + - " -b, --beautify Beautify source code\n" + - " -a, --showast Show AST of program\n" + - " -t, --showtokens Show lexical tokens\n" + - " -m, --showtime Show elapsed time of parsing and execution"); + System.out.println("OwnLang version %s\n\n".formatted(Version.VERSION) + """ + Usage: ownlang [options] + options: + -f, --file [input] Run program file. Required. + -r, --repl Enter to a REPL mode + -l, --lint Find bugs in code + -o N, --optimize N Perform optimization with N (0...9) passes + -b, --beautify Beautify source code + -a, --showast Show AST of program + -t, --showtokens Show lexical tokens + -m, --showtime Show elapsed time of parsing and execution + """); } - private static void createOwnLangArgs(String[] javaArgs, int index) { - if (index >= javaArgs.length) return; - final String[] ownlangArgs = new String[javaArgs.length - index]; - System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); + private static String[] createOwnLangArgs(String[] javaArgs, int index) { + final String[] ownlangArgs; + if (index >= javaArgs.length) { + ownlangArgs = new String[0]; + } else { + ownlangArgs = new String[javaArgs.length - index]; + System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); + } Shared.setOwnlangArgs(ownlangArgs); + return ownlangArgs; } - private static void run(String input, RunOptions options) { + private static void run(RunOptions options) { final var measurement = new TimeMeasurement(); final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop); final var stagesData = new StagesDataMap(); - stagesData.put(SourceLoaderStage.TAG_SOURCE, input); try { - scopedStages.create("Lexer", new LexerStage()) + scopedStages.create("Source loader", new SourceLoaderStage()) + .then(scopedStages.create("Lexer", new LexerStage())) .then(scopedStages.create("Parser", new ParserStage())) .thenConditional(options.optimizationLevel > 0, scopedStages.create("Optimization", @@ -157,7 +148,7 @@ private static void run(String input, RunOptions options) { scopedStages.create("Linter", new LinterStage())) .then(scopedStages.create("Function adding", new FunctionAddingStage())) .then(scopedStages.create("Execution", new ExecutionStage())) - .perform(stagesData, input); + .perform(stagesData, options.toInputSource()); } catch (OwnLangParserException ex) { final var error = new ParseErrorsFormatterStage() .perform(stagesData, ex.getParseErrors()); @@ -185,20 +176,4 @@ private static void run(String input, RunOptions options) { } } } - - private static class RunOptions { - boolean showTokens, showAst, showMeasurements; - boolean lintMode; - boolean beautifyMode; - int optimizationLevel; - - RunOptions() { - showTokens = false; - showAst = false; - showMeasurements = false; - lintMode = false; - beautifyMode = false; - optimizationLevel = 0; - } - } } diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java new file mode 100644 index 00000000..10ed0c3f --- /dev/null +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang; + +import com.annimon.ownlang.util.input.InputSource; +import com.annimon.ownlang.util.input.InputSourceFile; +import com.annimon.ownlang.util.input.InputSourceProgram; +import com.annimon.ownlang.util.input.InputSourceResource; +import java.nio.file.Files; +import java.nio.file.Path; + +public class RunOptions { + private static final String RESOURCE_PREFIX = "resource:"; + private static final String DEFAULT_PROGRAM = "program.own"; + + // input + String programPath; + String programSource; + // modes + boolean lintMode; + boolean beautifyMode; + int optimizationLevel; + // flags + boolean showTokens; + boolean showAst; + boolean showMeasurements; + + String detectDefaultProgramPath() { + if (getClass().getResource("/" + DEFAULT_PROGRAM) != null) { + return RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM; + } + if (Files.isReadable(Path.of(DEFAULT_PROGRAM))) { + return DEFAULT_PROGRAM; + } + return null; + } + + InputSource toInputSource() { + if (programSource != null) { + return new InputSourceProgram(programSource); + } + if (programPath == null) { + // No arguments. Default to program.own + programPath = detectDefaultProgramPath(); + if (programPath == null) { + throw new IllegalArgumentException("Empty input"); + } + } + + if (programPath.startsWith(RESOURCE_PREFIX)) { + String path = programPath.substring(RESOURCE_PREFIX.length()); + return new InputSourceResource(path); + } else { + return new InputSourceFile(programPath); + } + } +} diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 63ae75e8..4bf86c40 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -12,7 +12,9 @@ import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.stages.*; -import com.annimon.ownlang.util.SourceLoaderStage; +import com.annimon.ownlang.util.input.InputSource; +import com.annimon.ownlang.util.input.InputSourceFile; +import com.annimon.ownlang.util.input.SourceLoaderStage; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; @@ -24,11 +26,12 @@ public class ProgramsTest { private static final String RES_DIR = "src/test/resources"; - private static Stage testPipeline; + private static Stage testPipeline; - public static Stream data() { + public static Stream data() { return scanDirectory(RES_DIR) - .map(File::getPath); + .map(File::getPath) + .map(InputSourceFile::new); } @BeforeAll @@ -85,16 +88,16 @@ public void initialize() { @ParameterizedTest @MethodSource("data") - public void testProgram(String programPath) { + public void testProgram(InputSource inputSource) { final StagesDataMap stagesData = new StagesDataMap(); try { - testPipeline.perform(stagesData, programPath); + testPipeline.perform(stagesData, inputSource); } catch (OwnLangParserException ex) { final var error = new ParseErrorsFormatterStage() .perform(stagesData, ex.getParseErrors()); - fail(programPath + "\n" + error, ex); + fail(inputSource + "\n" + error, ex); } catch (Exception oae) { - fail(programPath, oae); + fail(inputSource.toString(), oae); Console.handleException(stagesData, Thread.currentThread(), oae); } } From fb6a8b8ea1c338352aa5299768bdcca4ce8256ed Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 14:08:56 +0300 Subject: [PATCH 340/448] Add Beautifier as a stage --- .../main/java/com/annimon/ownlang/Main.java | 7 ++++--- .../{Beautifier.java => BeautifierStage.java} | 21 ++++++++----------- 2 files changed, 13 insertions(+), 15 deletions(-) rename ownlang-parser/src/main/java/com/annimon/ownlang/parser/{Beautifier.java => BeautifierStage.java} (95%) diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index c5452bbf..3255e3d2 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.StoppedException; -import com.annimon.ownlang.parser.Beautifier; +import com.annimon.ownlang.parser.BeautifierStage; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; @@ -97,9 +97,10 @@ public static void main(String[] args) throws IOException { } } if (options.beautifyMode) { - String input = new SourceLoaderStage() + String result = new SourceLoaderStage() + .then(new BeautifierStage()) .perform(new StagesDataMap(), options.toInputSource()); - System.out.println(Beautifier.beautify(input)); + System.out.println(result); return; } run(options); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/BeautifierStage.java similarity index 95% rename from ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/BeautifierStage.java index ae96ced2..bf8453b9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Beautifier.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/BeautifierStage.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.Console; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; import java.util.HashMap; import java.util.Map; @@ -8,11 +10,7 @@ * * @author aNNiMON */ -public final class Beautifier { - - public static String beautify(String input) { - return new Beautifier(input).beautify(); - } +public final class BeautifierStage implements Stage { private enum OperatorMode { SPACES, RSPACES, TRIM, RTRIM, AS_SOURCE, @@ -77,21 +75,20 @@ private enum OperatorMode { OPERATORS.put(">>>", OperatorMode.SPACES); } - private final String input; - private final int length; - - private final StringBuilder beautifiedCode, buffer; - + private String input; + private int length; + private StringBuilder beautifiedCode, buffer; private int pos; private int indentLevel; - public Beautifier(String input) { + @Override + public String perform(StagesData stagesData, String input) { this.input = input; length = input.length(); beautifiedCode = new StringBuilder(); buffer = new StringBuilder(); - indentLevel = 0; + return beautify(); } public String beautify() { From 110aa6264a9d1cc03458506b64542b8ff039d207 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 16:19:26 +0300 Subject: [PATCH 341/448] [functional] Add Stream.filterNot --- .../modules/functional/StreamValue.java | 116 ++++++++++++++++++ .../modules/functional/functional.java | 8 +- .../modules/functional/functional_filter.java | 31 ++--- .../functional/functional_filterNot.java | 18 +++ .../modules/functional/functional_stream.java | 115 ----------------- .../functional/functional_takeWhile.java | 49 ++++++++ .../resources/modules/functional/stream.own | 10 ++ 7 files changed, 209 insertions(+), 138 deletions(-) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java new file mode 100644 index 00000000..90d0abc0 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -0,0 +1,116 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.*; +import java.util.Arrays; + +class StreamValue extends MapValue { + + private final ArrayValue container; + + public StreamValue(ArrayValue container) { + super(16); + this.container = container; + init(); + } + + private void init() { + set("filter", wrapIntermediate(new functional_filter())); + set("filterNot", wrapIntermediate(new functional_filterNot())); + set("map", wrapIntermediate(new functional_map())); + set("flatMap", wrapIntermediate(new functional_flatmap())); + set("sorted", this::sorted); + set("sortBy", wrapIntermediate(new functional_sortby())); + set("takeWhile", wrapIntermediate(new functional_takeWhile())); + set("dropWhile", wrapIntermediate(new functional_dropwhile())); + set("peek", wrapIntermediate(new functional_foreach())); + set("skip", this::skip); + set("limit", this::limit); + set("custom", this::custom); + + set("reduce", wrapTerminal(new functional_reduce())); + set("forEach", wrapTerminal(new functional_foreach())); + set("toArray", args -> container); + set("joining", container::joinToString); + set("count", args -> NumberValue.of(container.size())); + } + + private Value skip(Value[] args) { + Arguments.check(1, args.length); + + final int skipCount = args[0].asInt(); + final int size = container.size(); + + if (skipCount <= 0) return this; + if (skipCount >= size) { + return new StreamValue(new ArrayValue(0)); + } + + final Value[] result = new Value[size - skipCount]; + System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length); + return new StreamValue(new ArrayValue(result)); + } + + private Value limit(Value[] args) { + Arguments.check(1, args.length); + + final int limitCount = args[0].asInt(); + final int size = container.size(); + + if (limitCount >= size) return this; + if (limitCount <= 0) { + return new StreamValue(new ArrayValue(0)); + } + + final Value[] result = new Value[limitCount]; + System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount); + return new StreamValue(new ArrayValue(result)); + } + + private Value sorted(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + final Value[] elements = container.getCopyElements(); + + switch (args.length) { + case 0 -> Arrays.sort(elements); + case 1 -> { + final Function comparator = ValueUtils.consumeFunction(args[0], 0); + Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); + } + default -> throw new ArgumentsMismatchException("Wrong number of arguments"); + } + + return new StreamValue(new ArrayValue(elements)); + } + + private Value custom(Value[] args) { + Arguments.check(1, args.length); + final Function f = ValueUtils.consumeFunction(args[0], 0); + final Value result = f.execute(container); + if (result.type() == Types.ARRAY) { + return new StreamValue((ArrayValue) result); + } + return result; + } + + private FunctionValue wrapIntermediate(Function f) { + return wrap(f, true); + } + + private FunctionValue wrapTerminal(Function f) { + return wrap(f, false); + } + + private FunctionValue wrap(Function f, boolean intermediate) { + return new FunctionValue(args -> { + final Value[] newArgs = new Value[args.length + 1]; + System.arraycopy(args, 0, newArgs, 1, args.length); + newArgs[0] = container; + final Value result = f.execute(newArgs); + if (intermediate && result.type() == Types.ARRAY) { + return new StreamValue((ArrayValue) result); + } + return result; + }); + } +} \ No newline at end of file diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index 0ad61889..66c0dcca 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -1,8 +1,6 @@ package com.annimon.ownlang.modules.functional; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.util.HashMap; import java.util.Map; @@ -25,9 +23,9 @@ public Map functions() { result.put("map", new functional_map()); result.put("flatmap", new functional_flatmap()); result.put("reduce", new functional_reduce()); - result.put("filter", new functional_filter(false)); + result.put("filter", new functional_filter()); result.put("sortby", new functional_sortby()); - result.put("takewhile", new functional_filter(true)); + result.put("takewhile", new functional_takeWhile()); result.put("dropwhile", new functional_dropwhile()); result.put("chain", new functional_chain()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java index 25c73d65..6f1c1ca6 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java @@ -8,46 +8,41 @@ public final class functional_filter implements Function { - private final boolean takeWhile; - - public functional_filter(boolean takeWhile) { - this.takeWhile = takeWhile; - } - @Override public Value execute(Value[] args) { Arguments.check(2, args.length); final Value container = args[0]; final Function predicate = ValueUtils.consumeFunction(args[1], 1); + return filter(container, predicate); + } + + static Value filter(Value container, Function predicate) { if (container.type() == Types.ARRAY) { - return filterArray((ArrayValue) container, predicate, takeWhile); + return filterArray((ArrayValue) container, predicate); } - if (container.type() == Types.MAP) { - return filterMap((MapValue) container, predicate, takeWhile); + return filterMap((MapValue) container, predicate); } - throw new TypeException("Invalid first argument. Array or map expected"); } - - private Value filterArray(ArrayValue array, Function predicate, boolean takeWhile) { + + static ArrayValue filterArray(ArrayValue array, Function predicate) { final int size = array.size(); final List values = new ArrayList<>(size); for (Value value : array) { if (predicate.execute(value) != NumberValue.ZERO) { values.add(value); - } else if (takeWhile) break; + } } - final int newSize = values.size(); - return new ArrayValue(values.toArray(new Value[newSize])); + return new ArrayValue(values); } - - private Value filterMap(MapValue map, Function predicate, boolean takeWhile) { + + static MapValue filterMap(MapValue map, Function predicate) { final MapValue result = new MapValue(map.size()); for (Map.Entry element : map) { if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { result.set(element.getKey(), element.getValue()); - } else if (takeWhile) break; + } } return result; } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java new file mode 100644 index 00000000..7ab6e204 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.lib.*; + +public final class functional_filterNot implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + final Value container = args[0]; + final Function predicate = ValueUtils.consumeFunction(args[1], 1); + return functional_filter.filter(container, negate(predicate)); + } + + static Function negate(Function f) { + return args -> NumberValue.fromBoolean(f.execute(args) == NumberValue.ZERO); + } +} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java index f0888aa5..5ba6d705 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java @@ -1,9 +1,7 @@ package com.annimon.ownlang.modules.functional; -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; -import java.util.Arrays; public final class functional_stream implements Function { @@ -25,117 +23,4 @@ public Value execute(Value[] args) { throw new TypeException("Invalid argument. Array or map expected"); } } - - private static class StreamValue extends MapValue { - - private final ArrayValue container; - - public StreamValue(ArrayValue container) { - super(16); - this.container = container; - init(); - } - - private void init() { - set("filter", wrapIntermediate(new functional_filter(false))); - set("map", wrapIntermediate(new functional_map())); - set("flatMap", wrapIntermediate(new functional_flatmap())); - set("sorted", this::sorted); - set("sortBy", wrapIntermediate(new functional_sortby())); - set("takeWhile", wrapIntermediate(new functional_filter(true))); - set("dropWhile", wrapIntermediate(new functional_dropwhile())); - set("peek", wrapIntermediate(new functional_foreach())); - set("skip", this::skip); - set("limit", this::limit); - set("custom", this::custom); - - set("reduce", wrapTerminal(new functional_reduce())); - set("forEach", wrapTerminal(new functional_foreach())); - set("toArray", args -> container); - set("joining", container::joinToString); - set("count", args -> NumberValue.of(container.size())); - } - - private Value skip(Value[] args) { - Arguments.check(1, args.length); - - final int skipCount = args[0].asInt(); - final int size = container.size(); - - if (skipCount <= 0) return this; - if (skipCount >= size) { - return new StreamValue(new ArrayValue(0)); - } - - final Value[] result = new Value[size - skipCount]; - System.arraycopy(container.getCopyElements(), skipCount, result, 0, result.length); - return new StreamValue(new ArrayValue(result)); - } - - private Value limit(Value[] args) { - Arguments.check(1, args.length); - - final int limitCount = args[0].asInt(); - final int size = container.size(); - - if (limitCount >= size) return this; - if (limitCount <= 0) { - return new StreamValue(new ArrayValue(0)); - } - - final Value[] result = new Value[limitCount]; - System.arraycopy(container.getCopyElements(), 0, result, 0, limitCount); - return new StreamValue(new ArrayValue(result)); - } - - private Value sorted(Value[] args) { - Arguments.checkOrOr(0, 1, args.length); - final Value[] elements = container.getCopyElements(); - - switch (args.length) { - case 0: - Arrays.sort(elements); - break; - case 1: - final Function comparator = ValueUtils.consumeFunction(args[0], 0); - Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); - break; - default: - throw new ArgumentsMismatchException("Wrong number of arguments"); - } - - return new StreamValue(new ArrayValue(elements)); - } - - private Value custom(Value[] args) { - Arguments.check(1, args.length); - final Function f = ValueUtils.consumeFunction(args[0], 0); - final Value result = f.execute(container); - if (result.type() == Types.ARRAY) { - return new StreamValue((ArrayValue) result); - } - return result; - } - - private FunctionValue wrapIntermediate(Function f) { - return wrap(f, true); - } - - private FunctionValue wrapTerminal(Function f) { - return wrap(f, false); - } - - private FunctionValue wrap(Function f, boolean intermediate) { - return new FunctionValue(args -> { - final Value[] newArgs = new Value[args.length + 1]; - System.arraycopy(args, 0, newArgs, 1, args.length); - newArgs[0] = container; - final Value result = f.execute(newArgs); - if (intermediate && result.type() == Types.ARRAY) { - return new StreamValue((ArrayValue) result); - } - return result; - }); - } - } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java new file mode 100644 index 00000000..3a0569d3 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java @@ -0,0 +1,49 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public final class functional_takeWhile implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + final Value container = args[0]; + final Function predicate = ValueUtils.consumeFunction(args[1], 1); + return takeWhile(container, predicate); + } + + static Value takeWhile(Value container, Function predicate) { + if (container.type() == Types.ARRAY) { + return takeWhileArray((ArrayValue) container, predicate); + } + if (container.type() == Types.MAP) { + return takeWhileMap((MapValue) container, predicate); + } + throw new TypeException("Invalid first argument. Array or map expected"); + } + + static ArrayValue takeWhileArray(ArrayValue array, Function predicate) { + final int size = array.size(); + final List values = new ArrayList<>(size); + for (Value value : array) { + if (predicate.execute(value) != NumberValue.ZERO) { + values.add(value); + } else break; + } + return new ArrayValue(values); + } + + static MapValue takeWhileMap(MapValue map, Function predicate) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry element : map) { + if (predicate.execute(element.getKey(), element.getValue()) != NumberValue.ZERO) { + result.set(element.getKey(), element.getValue()); + } else break; + } + return result; + } +} diff --git a/ownlang-parser/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own index 9a4b4053..aba004c8 100644 --- a/ownlang-parser/src/test/resources/modules/functional/stream.own +++ b/ownlang-parser/src/test/resources/modules/functional/stream.own @@ -10,6 +10,16 @@ def testStream() { assertEquals([8,6,4,2], result) } +def testFilter() { + data = [1,2,3,4,5,6,7] + assertEquals([2, 4, 6], stream(data).filter(def(x) = x % 2 == 0).toArray()) +} + +def testFilterNot() { + data = [1,2,3,4,5,6,7] + assertEquals([1, 3, 5, 7], stream(data).filterNot(def(x) = x % 2 == 0).toArray()) +} + def testSkip() { data = [1,2,3,4,5,6,7] assertEquals(7, stream(data).skip(0).count()) From 2de94d94d5c194a245ed7bb526660d0e4adeeb46 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 16:31:46 +0300 Subject: [PATCH 342/448] [functional] Unify functional operators --- .../modules/functional/StreamValue.java | 8 +-- .../modules/functional/functional.java | 6 +- ...opwhile.java => functional_dropWhile.java} | 16 +++-- .../functional/functional_flatmap.java | 15 +++-- .../functional/functional_forEach.java | 60 +++++++++++++++++++ .../functional/functional_foreach.java | 55 ----------------- .../modules/functional/functional_map.java | 4 +- .../modules/functional/functional_reduce.java | 34 +++++++---- ...nal_sortby.java => functional_sortBy.java} | 2 +- 9 files changed, 112 insertions(+), 88 deletions(-) rename modules/main/src/main/java/com/annimon/ownlang/modules/functional/{functional_dropwhile.java => functional_dropWhile.java} (79%) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java delete mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java rename modules/main/src/main/java/com/annimon/ownlang/modules/functional/{functional_sortby.java => functional_sortBy.java} (93%) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java index 90d0abc0..e4ce9625 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -20,16 +20,16 @@ private void init() { set("map", wrapIntermediate(new functional_map())); set("flatMap", wrapIntermediate(new functional_flatmap())); set("sorted", this::sorted); - set("sortBy", wrapIntermediate(new functional_sortby())); + set("sortBy", wrapIntermediate(new functional_sortBy())); set("takeWhile", wrapIntermediate(new functional_takeWhile())); - set("dropWhile", wrapIntermediate(new functional_dropwhile())); - set("peek", wrapIntermediate(new functional_foreach())); + set("dropWhile", wrapIntermediate(new functional_dropWhile())); + set("peek", wrapIntermediate(new functional_forEach())); set("skip", this::skip); set("limit", this::limit); set("custom", this::custom); set("reduce", wrapTerminal(new functional_reduce())); - set("forEach", wrapTerminal(new functional_foreach())); + set("forEach", wrapTerminal(new functional_forEach())); set("toArray", args -> container); set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index 66c0dcca..22c7ffad 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -19,14 +19,14 @@ public Map constants() { @Override public Map functions() { final var result = new HashMap(15); - result.put("foreach", new functional_foreach()); + result.put("foreach", new functional_forEach()); result.put("map", new functional_map()); result.put("flatmap", new functional_flatmap()); result.put("reduce", new functional_reduce()); result.put("filter", new functional_filter()); - result.put("sortby", new functional_sortby()); + result.put("sortby", new functional_sortBy()); result.put("takewhile", new functional_takeWhile()); - result.put("dropwhile", new functional_dropwhile()); + result.put("dropwhile", new functional_dropWhile()); result.put("chain", new functional_chain()); result.put("stream", new functional_stream()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java similarity index 79% rename from modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java index 5817f719..11cbff37 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropwhile.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java @@ -9,20 +9,24 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.ValueUtils; -public final class functional_dropwhile implements Function { +public final class functional_dropWhile implements Function { @Override public Value execute(Value[] args) { Arguments.check(2, args.length); - if (args[0].type() != Types.ARRAY) { - throw new TypeException("Array expected in first argument"); - } final Value container = args[0]; final Function predicate = ValueUtils.consumeFunction(args[1], 1); - return dropWhileArray((ArrayValue) container, predicate); + return dropWhile(container, predicate); } - private Value dropWhileArray(ArrayValue array, Function predicate) { + static ArrayValue dropWhile(Value container, Function predicate) { + if (container.type() != Types.ARRAY) { + throw new TypeException("Array expected in first argument"); + } + return dropWhileArray((ArrayValue) container, predicate); + } + + static ArrayValue dropWhileArray(ArrayValue array, Function predicate) { int skipCount = 0; for (Value value : array) { if (predicate.execute(value) != NumberValue.ZERO) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java index 868b0c87..775be2b4 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java @@ -15,14 +15,19 @@ public final class functional_flatmap implements Function { @Override public Value execute(Value[] args) { Arguments.check(2, args.length); - if (args[0].type() != Types.ARRAY) { + final Value container = args[0]; + final Function mapper = ValueUtils.consumeFunction(args[1], 1); + return flatMap(container, mapper); + } + + static Value flatMap(Value container, Function mapper) { + if (container.type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); } - final Function mapper = ValueUtils.consumeFunction(args[1], 1); - return flatMapArray((ArrayValue) args[0], mapper); + return flatMapArray((ArrayValue) container, mapper); } - - private Value flatMapArray(ArrayValue array, Function mapper) { + + static Value flatMapArray(ArrayValue array, Function mapper) { final List values = new ArrayList<>(); final int size = array.size(); for (int i = 0; i < size; i++) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java new file mode 100644 index 00000000..ff4175ba --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java @@ -0,0 +1,60 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.Map; + +public final class functional_forEach implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + final Value container = args[0]; + final Function consumer = ValueUtils.consumeFunction(args[1], 1); + return forEach(container, consumer); + } + + static Value forEach(Value container, Function consumer) { + final int argsCount = consumer.getArgsCount(); + return switch (container.type()) { + case Types.STRING -> forEachString((StringValue) container, argsCount, consumer); + case Types.ARRAY -> forEachArray((ArrayValue) container, argsCount, consumer); + case Types.MAP -> forEachMap((MapValue) container, consumer); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); + }; + } + + static StringValue forEachString(StringValue string, int argsCount, Function consumer) { + if (argsCount == 2) { + for (char ch : string.asString().toCharArray()) { + consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); + } + } else { + for (char ch : string.asString().toCharArray()) { + consumer.execute(new StringValue(String.valueOf(ch))); + } + } + return string; + } + + static ArrayValue forEachArray(ArrayValue array, int argsCount, Function consumer) { + if (argsCount == 2) { + int index = 0; + for (Value element : array) { + consumer.execute(element, NumberValue.of(index++)); + } + } else { + for (Value element : array) { + consumer.execute(element); + } + } + return array; + } + + static MapValue forEachMap(MapValue map, Function consumer) { + for (Map.Entry element : map) { + consumer.execute(element.getKey(), element.getValue()); + } + return map; + } +} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java deleted file mode 100644 index f22aec44..00000000 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.annimon.ownlang.modules.functional; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; -import java.util.Map; - -public final class functional_foreach implements Function { - - @Override - public Value execute(Value[] args) { - Arguments.check(2, args.length); - final Value container = args[0]; - final Function consumer = ValueUtils.consumeFunction(args[1], 1); - final int argsCount = consumer.getArgsCount(); - - switch (container.type()) { - case Types.STRING: - final StringValue string = (StringValue) container; - if (argsCount == 2) { - for (char ch : string.asString().toCharArray()) { - consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); - } - } else { - for (char ch : string.asString().toCharArray()) { - consumer.execute(new StringValue(String.valueOf(ch))); - } - } - return string; - - case Types.ARRAY: - final ArrayValue array = (ArrayValue) container; - if (argsCount == 2) { - int index = 0; - for (Value element : array) { - consumer.execute(element, NumberValue.of(index++)); - } - } else { - for (Value element : array) { - consumer.execute(element); - } - } - return array; - - case Types.MAP: - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - consumer.execute(element.getKey(), element.getValue()); - } - return map; - - default: - throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); - } - } -} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java index 82198187..5ca801cc 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java @@ -31,7 +31,7 @@ public Value execute(Value[] args) { throw new TypeException("Invalid first argument. Array or map expected"); } - private Value mapArray(ArrayValue array, Function mapper) { + static ArrayValue mapArray(ArrayValue array, Function mapper) { final int size = array.size(); final ArrayValue result = new ArrayValue(size); for (int i = 0; i < size; i++) { @@ -40,7 +40,7 @@ private Value mapArray(ArrayValue array, Function mapper) { return result; } - private Value mapMap(MapValue map, Function keyMapper, Function valueMapper) { + static MapValue mapMap(MapValue map, Function keyMapper, Function valueMapper) { final MapValue result = new MapValue(map.size()); for (Map.Entry element : map) { final Value newKey = keyMapper.execute(element.getKey()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java index 005e6864..cc03dfd9 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -19,22 +19,32 @@ public Value execute(Value[] args) { final Value container = args[0]; final Value identity = args[1]; final Function accumulator = ValueUtils.consumeFunction(args[2], 2); + return reduce(container, identity, accumulator); + } + + static Value reduce(Value container, Value identity, Function accumulator) { if (container.type() == Types.ARRAY) { - Value result = identity; - final ArrayValue array = (ArrayValue) container; - for (Value element : array) { - result = accumulator.execute(result, element); - } - return result; + return reduceArray(identity, (ArrayValue) container, accumulator); } if (container.type() == Types.MAP) { - Value result = identity; - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - result = accumulator.execute(result, element.getKey(), element.getValue()); - } - return result; + return reduceMap(identity, (MapValue) container, accumulator); } throw new TypeException("Invalid first argument. Array or map expected"); } + + static Value reduceArray(Value identity, ArrayValue array, Function accumulator) { + Value result = identity; + for (Value element : array) { + result = accumulator.execute(result, element); + } + return result; + } + + static Value reduceMap(Value identity, MapValue map, Function accumulator) { + Value result = identity; + for (Map.Entry element : map) { + result = accumulator.execute(result, element.getKey(), element.getValue()); + } + return result; + } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java similarity index 93% rename from modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java index baa0532d..a395ee93 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortby.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java @@ -10,7 +10,7 @@ import java.util.Arrays; import java.util.Comparator; -public final class functional_sortby implements Function { +public final class functional_sortBy implements Function { @Override public Value execute(Value[] args) { From d643f596a8cfc0d6e8ff2820def076c4e6bb6811 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 16:32:18 +0300 Subject: [PATCH 343/448] [functional] Deny varargs input argument for stream --- .../modules/functional/functional_stream.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java index 5ba6d705..20173266 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java @@ -9,18 +9,11 @@ public final class functional_stream implements Function { public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); - if (args.length > 1) { - return new StreamValue(new ArrayValue(args)); - } - final Value value = args[0]; - switch (value.type()) { - case Types.MAP: - return new StreamValue(((MapValue) value).toPairs()); - case Types.ARRAY: - return new StreamValue((ArrayValue) value); - default: - throw new TypeException("Invalid argument. Array or map expected"); - } + return switch (value.type()) { + case Types.MAP -> new StreamValue(((MapValue) value).toPairs()); + case Types.ARRAY -> new StreamValue((ArrayValue) value); + default -> throw new TypeException("Invalid argument. Array or map expected"); + }; } } From 2bb5e455178166b18c6c61812baee2b51ea08ed7 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 18:23:20 +0300 Subject: [PATCH 344/448] [functional] Move indexed variant of Stream.forEach to Stream,forEachIndexed --- .../modules/functional/StreamValue.java | 1 + .../functional/functional_forEach.java | 22 +++++-------- .../functional/functional_forEachIndexed.java | 31 +++++++++++++++++++ .../resources/modules/functional/foreach.own | 10 +----- .../resources/modules/functional/stream.own | 22 +++++++++++++ 5 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java index e4ce9625..e2463035 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -30,6 +30,7 @@ private void init() { set("reduce", wrapTerminal(new functional_reduce())); set("forEach", wrapTerminal(new functional_forEach())); + set("forEachIndexed", wrapTerminal(new functional_forEachIndexed())); set("toArray", args -> container); set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java index ff4175ba..a9b9b7a9 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java @@ -15,17 +15,16 @@ public Value execute(Value[] args) { } static Value forEach(Value container, Function consumer) { - final int argsCount = consumer.getArgsCount(); return switch (container.type()) { - case Types.STRING -> forEachString((StringValue) container, argsCount, consumer); - case Types.ARRAY -> forEachArray((ArrayValue) container, argsCount, consumer); + case Types.STRING -> forEachString((StringValue) container, consumer); + case Types.ARRAY -> forEachArray((ArrayValue) container, consumer); case Types.MAP -> forEachMap((MapValue) container, consumer); default -> throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); }; } - static StringValue forEachString(StringValue string, int argsCount, Function consumer) { - if (argsCount == 2) { + static StringValue forEachString(StringValue string, Function consumer) { + if (consumer.getArgsCount() == 2) { for (char ch : string.asString().toCharArray()) { consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch)); } @@ -37,16 +36,9 @@ static StringValue forEachString(StringValue string, int argsCount, Function con return string; } - static ArrayValue forEachArray(ArrayValue array, int argsCount, Function consumer) { - if (argsCount == 2) { - int index = 0; - for (Value element : array) { - consumer.execute(element, NumberValue.of(index++)); - } - } else { - for (Value element : array) { - consumer.execute(element); - } + static ArrayValue forEachArray(ArrayValue array, Function consumer) { + for (Value element : array) { + consumer.execute(element); } return array; } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java new file mode 100644 index 00000000..4ab3ee76 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java @@ -0,0 +1,31 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; + +public final class functional_forEachIndexed implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + final Value container = args[0]; + final Function consumer = ValueUtils.consumeFunction(args[1], 1); + return forEachIndexed(container, consumer); + } + + static Value forEachIndexed(Value container, Function consumer) { + if (container.type() == Types.ARRAY) { + return forEachIndexedArray((ArrayValue) container, consumer); + } + // Only used in Streams -> no Map implementation + throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); + } + + static ArrayValue forEachIndexedArray(ArrayValue array, Function consumer) { + int index = 0; + for (Value element : array) { + consumer.execute(element, NumberValue.of(index++)); + } + return array; + } +} diff --git a/ownlang-parser/src/test/resources/modules/functional/foreach.own b/ownlang-parser/src/test/resources/modules/functional/foreach.own index ff0a438a..24dc5674 100644 --- a/ownlang-parser/src/test/resources/modules/functional/foreach.own +++ b/ownlang-parser/src/test/resources/modules/functional/foreach.own @@ -1,6 +1,6 @@ use std, functional -def testArrayForeach1Arg() { +def testArrayForeachArg() { sum = 0 foreach([1, 2, 3], def(v) { sum += v @@ -8,14 +8,6 @@ def testArrayForeach1Arg() { assertEquals(6, sum) } -def testArrayForeach2Args() { - sum = 0 - foreach([1, 2, 3], def(v, index) { - sum += v * index - }) - assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) -} - def testStringForeach1Arg() { sum = 0 foreach("abcd", def(s) { diff --git a/ownlang-parser/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own index aba004c8..bfef4c1d 100644 --- a/ownlang-parser/src/test/resources/modules/functional/stream.own +++ b/ownlang-parser/src/test/resources/modules/functional/stream.own @@ -79,6 +79,28 @@ def testSorted() { assertEquals([-2,3,4,-5,6,6,-8], stream(data).sorted(def(a,b) = abs(a) - abs(b)).toArray()) } +def testForEachArrayIndexed() { + data = [1, 2, 3] + sum = 0 + stream(data) + .forEachIndexed(def(v, index) { + sum += (v * index) + }) + assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) +} + +def testForEachMapIndexed() { + data = {"a": "1", "b": 2} + result = "" + stream(data) + .sorted() + .forEachIndexed(def(entry, index) { + extract(key, value) = entry + result += "" + key + value + index + }) + assertEquals("a10b21", result) +} + def reverse(container) { size = length(container) result = newarray(size) From d3dd8feb108b6eda9c03a56eeca86226c7bcf7ef Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 18:57:36 +0300 Subject: [PATCH 345/448] Source located function, class and arguments --- .../ArgumentsMismatchException.java | 6 +++++ .../com/annimon/ownlang/lib/ClassMethod.java | 5 ++-- .../ownlang/lib/UserDefinedFunction.java | 27 +++++++++++++------ .../com/annimon/ownlang/parser/Parser.java | 8 ++++-- .../annimon/ownlang/parser/ast/Arguments.java | 16 +++++++++-- .../parser/ast/FunctionDefineStatement.java | 15 ++++++++--- .../parser/ast/ObjectCreationExpression.java | 4 +-- .../optimization/OptimizationVisitor.java | 5 ++-- .../annimon/ownlang/parser/ProgramsTest.java | 2 +- 9 files changed, 66 insertions(+), 22 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java index 082e9b34..24c05ff0 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.util.Range; + public final class ArgumentsMismatchException extends OwnLangRuntimeException { public ArgumentsMismatchException() { @@ -8,4 +10,8 @@ public ArgumentsMismatchException() { public ArgumentsMismatchException(String message) { super(message); } + + public ArgumentsMismatchException(String message, Range range) { + super(message, range); + } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java index 6155a64a..5ad723e0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java @@ -2,13 +2,14 @@ import com.annimon.ownlang.parser.ast.Arguments; import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.util.Range; public class ClassMethod extends UserDefinedFunction { public final ClassInstanceValue classInstance; - public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance) { - super(arguments, body); + public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance, Range range) { + super(arguments, body, range); this.classInstance = classInstance; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index ce5b15ec..f6015c02 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -5,21 +5,30 @@ import com.annimon.ownlang.parser.ast.Arguments; import com.annimon.ownlang.parser.ast.ReturnStatement; import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public class UserDefinedFunction implements Function { +public class UserDefinedFunction implements Function, SourceLocation { public final Arguments arguments; public final Statement body; - - public UserDefinedFunction(Arguments arguments, Statement body) { + private final Range range; + + public UserDefinedFunction(Arguments arguments, Statement body, Range range) { this.arguments = arguments; this.body = body; + this.range = range; } - + + @Override + public Range getRange() { + return range; + } + @Override public int getArgsCount() { return arguments.size(); @@ -35,13 +44,15 @@ public Value execute(Value[] values) { final int size = values.length; final int requiredArgsCount = arguments.getRequiredArgumentsCount(); if (size < requiredArgsCount) { - throw new ArgumentsMismatchException(String.format( - "Arguments count mismatch. Required %d, got %d", requiredArgsCount, size)); + String error = String.format( + "Arguments count mismatch. Required %d, got %d", requiredArgsCount, size); + throw new ArgumentsMismatchException(error, arguments.getRange()); } final int totalArgsCount = getArgsCount(); if (size > totalArgsCount) { - throw new ArgumentsMismatchException(String.format( - "Arguments count mismatch. Total %d, got %d", totalArgsCount, size)); + String error = String.format( + "Arguments count mismatch. Total %d, got %d", totalArgsCount, size); + throw new ArgumentsMismatchException(error, arguments.getRange()); } try { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index cf63114b..0f52743a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -290,16 +290,18 @@ private ForeachMapStatement foreachMapStatement() { private FunctionDefineStatement functionDefine() { // def name(arg1, arg2 = value) { ... } || def name(args) = expr + final var startTokenIndex = index - 1; final String name = consume(TokenType.WORD).text(); final Arguments arguments = arguments(); final Statement body = statementBody(); - return new FunctionDefineStatement(name, arguments, body); + return new FunctionDefineStatement(name, arguments, body, getRange(startTokenIndex, index - 1)); } private Arguments arguments() { // (arg1, arg2, arg3 = expr1, arg4 = expr2) final Arguments arguments = new Arguments(); boolean startsOptionalArgs = false; + final var startTokenIndex = index; consume(TokenType.LPAREN); while (!match(TokenType.RPAREN)) { final String name = consume(TokenType.WORD).text(); @@ -313,6 +315,7 @@ private Arguments arguments() { } match(TokenType.COMMA); } + arguments.setRange(getRange(startTokenIndex, index - 1)); return arguments; } @@ -802,9 +805,10 @@ private Expression primary() { } if (match(TokenType.DEF)) { // anonymous function def(args) ... + final var startTokenIndex = index - 1; final Arguments arguments = arguments(); final Statement statement = statementBody(); - return new ValueExpression(new UserDefinedFunction(arguments, statement)); + return new ValueExpression(new UserDefinedFunction(arguments, statement, getRange(startTokenIndex, index - 1))); } return variable(); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java index e2cb0d57..7dc068f7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java @@ -1,19 +1,22 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public final class Arguments implements Iterable { +public final class Arguments implements Iterable, SourceLocation { private final List arguments; + private Range range; private int requiredArgumentsCount; public Arguments() { arguments = new ArrayList<>(); requiredArgumentsCount = 0; } - + public void addRequired(String name) { arguments.add(new Argument(name)); requiredArgumentsCount++; @@ -35,6 +38,15 @@ public int size() { return arguments.size(); } + public void setRange(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } + @Override public Iterator iterator() { return arguments.iterator(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java index 80e64c7f..9664e1b3 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -2,26 +2,35 @@ import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.UserDefinedFunction; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class FunctionDefineStatement implements Statement { +public final class FunctionDefineStatement implements Statement, SourceLocation { public final String name; public final Arguments arguments; public final Statement body; + private final Range range; - public FunctionDefineStatement(String name, Arguments arguments, Statement body) { + public FunctionDefineStatement(String name, Arguments arguments, Statement body, Range range) { this.name = name; this.arguments = arguments; this.body = body; + this.range = range; + } + + @Override + public Range getRange() { + return range; } @Override public void execute() { - ScopeHandler.setFunction(name, new UserDefinedFunction(arguments, body)); + ScopeHandler.setFunction(name, new UserDefinedFunction(arguments, body, range)); } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index 03476e1c..52863274 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -38,7 +38,7 @@ public Value eval() { return instantiable.newInstance(ctorArgs()); } } - throw new UnknownClassException(className, getRange()); + throw new UnknownClassException(className, range); } // Create an instance and put evaluated fields with method declarations @@ -49,7 +49,7 @@ public Value eval() { instance.addField(fieldName, f.eval()); } for (FunctionDefineStatement m : cd.methods) { - instance.addMethod(m.name, new ClassMethod(m.arguments, m.body, instance)); + instance.addMethod(m.name, new ClassMethod(m.arguments, m.body, instance, m.getRange())); } // Call a constructor diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index e3b28d85..1ba0d845 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -174,7 +174,7 @@ public Node visit(FunctionDefineStatement s, T t) { final Node body = s.body.accept(this, t); if (changed || body != s.body) { - return new FunctionDefineStatement(s.name, newArgs, consumeStatement(body)); + return new FunctionDefineStatement(s.name, newArgs, consumeStatement(body), s.getRange()); } return s; } @@ -423,7 +423,7 @@ public UserDefinedFunction visit(UserDefinedFunction s, T t) { final Node body = s.body.accept(this, t); if (changed || body != s.body) { - return new UserDefinedFunction(newArgs, consumeStatement(body)); + return new UserDefinedFunction(newArgs, consumeStatement(body), s.getRange()); } return s; } @@ -432,6 +432,7 @@ public UserDefinedFunction visit(UserDefinedFunction s, T t) { protected boolean visit(final Arguments in, final Arguments out, T t) { boolean changed = false; + out.setRange(in.getRange()); for (Argument argument : in) { final Expression valueExpr = argument.valueExpr(); if (valueExpr == null) { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 4bf86c40..951b50a4 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -97,8 +97,8 @@ public void testProgram(InputSource inputSource) { .perform(stagesData, ex.getParseErrors()); fail(inputSource + "\n" + error, ex); } catch (Exception oae) { - fail(inputSource.toString(), oae); Console.handleException(stagesData, Thread.currentThread(), oae); + fail(inputSource.toString(), oae); } } From e304aafedd6f46d1c48e1035db503cb9705e2c60 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 5 Oct 2023 19:11:34 +0300 Subject: [PATCH 346/448] Replace RuntimeException with OwnLangRuntimeException in modules --- .../annimon/ownlang/modules/date/date.java | 3 +- .../ownlang/modules/forms/JTextAreaValue.java | 3 +- .../annimon/ownlang/modules/gzip/gzip.java | 9 ++-- .../annimon/ownlang/modules/java/java.java | 7 +-- .../annimon/ownlang/modules/jdbc/jdbc.java | 53 ++++++++++--------- .../ownlang/modules/json/json_decode.java | 3 +- .../ownlang/modules/json/json_encode.java | 3 +- .../ownlang/modules/okhttp/CallValue.java | 3 +- .../modules/okhttp/ResponseBodyValue.java | 7 +-- .../annimon/ownlang/modules/robot/robot.java | 2 +- .../ownlang/modules/std/ArrayFunctions.java | 3 +- .../ownlang/modules/std/StringFunctions.java | 3 +- .../annimon/ownlang/modules/std/std_sync.java | 3 +- .../ownlang/modules/yaml/yaml_decode.java | 3 +- .../ownlang/modules/yaml/yaml_encode.java | 3 +- .../com/annimon/ownlang/modules/zip/zip.java | 7 +-- .../exceptions/OwnLangRuntimeException.java | 5 ++ 17 files changed, 70 insertions(+), 50 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java index 66424102..95a18f65 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.date; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; @@ -274,7 +275,7 @@ public Value execute(Value[] args) { try { return DateValue.from(format.parse(args[0].asString())); } catch (ParseException ex) { - throw new RuntimeException(ex); + throw new OwnLangRuntimeException(ex); } } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java index 083ac4b6..b7917612 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/forms/JTextAreaValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.forms; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import static com.annimon.ownlang.lib.Converters.*; import com.annimon.ownlang.lib.FunctionValue; @@ -55,7 +56,7 @@ private FunctionValue offsetFunction(OffsetFunction f) { int result = f.accept(args[0].asInt()); return NumberValue.of(result); } catch (BadLocationException ex) { - throw new RuntimeException(ex); + throw new OwnLangRuntimeException(ex); } }); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java index 323b4df2..df6aff46 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.gzip; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; @@ -45,7 +46,7 @@ private Value gzipFile(Value[] args) { gzos.finish(); return NumberValue.ONE; } catch (IOException ex) { - throw new RuntimeException("gzipFile", ex); + throw new OwnLangRuntimeException("gzipFile", ex); } } @@ -63,7 +64,7 @@ private Value gzipBytes(Value[] args) { gzos.finish(); return ArrayValue.of(baos.toByteArray()); } catch (IOException ex) { - throw new RuntimeException("gzipBytes", ex); + throw new OwnLangRuntimeException("gzipBytes", ex); } } @@ -85,7 +86,7 @@ private Value ungzipFile(Value[] args) { copy(gzis, os); return NumberValue.ONE; } catch (IOException ex) { - throw new RuntimeException("ungzipFile", ex); + throw new OwnLangRuntimeException("ungzipFile", ex); } } @@ -102,7 +103,7 @@ private Value ungzipBytes(Value[] args) { copy(gzis, baos); return ArrayValue.of(baos.toByteArray()); } catch (IOException ex) { - throw new RuntimeException("ungzipBytes", ex); + throw new OwnLangRuntimeException("ungzipBytes", ex); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java index a2f69532..059f4057 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.java; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.lang.reflect.Array; @@ -245,7 +246,7 @@ private Value newClass(Value[] args) { try { return new ClassValue(Class.forName(className)); } catch (ClassNotFoundException ce) { - throw new RuntimeException("Class " + className + " not found.", ce); + throw new OwnLangRuntimeException("Class " + className + " not found.", ce); } } @@ -307,7 +308,7 @@ private static Value findConstructorAndInstantiate(Value[] args, Constructor[ // skip } } - throw new RuntimeException("Constructor for " + args.length + " arguments" + throw new OwnLangRuntimeException("Constructor for " + args.length + " arguments" + " not found or non accessible"); } @@ -327,7 +328,7 @@ private static Function methodsToFunction(Object object, List methods) { } } final String className = (object == null ? "null" : object.getClass().getName()); - throw new RuntimeException("Method for " + args.length + " arguments" + throw new OwnLangRuntimeException("Method for " + args.length + " arguments" + " not found or non accessible in " + className); }; } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java index 165eec7b..f2cef93a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/jdbc/jdbc.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.modules.jdbc; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.io.IOException; @@ -99,7 +100,7 @@ private static com.annimon.ownlang.lib.Function getConnectionFunction(String con throw new ArgumentsMismatchException("Wrong number of arguments"); } } catch (Exception ex) { - throw new RuntimeException(ex); + throw new OwnLangRuntimeException(ex); } }; } @@ -161,7 +162,7 @@ private Value createStatement(Value[] args) { throw new ArgumentsMismatchException("Wrong number of arguments"); } } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -186,7 +187,7 @@ private Value prepareStatement(Value[] args) { throw new ArgumentsMismatchException("Wrong number of arguments"); } } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -284,7 +285,7 @@ private void init() { try { return new URL(args[1].asString()); } catch (IOException ioe) { - throw new RuntimeException(ioe); + throw new OwnLangRuntimeException(ioe); } })); } @@ -298,7 +299,7 @@ private Value addBatch(Value[] args) { else statement.addBatch(args[0].asString()); return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -316,7 +317,7 @@ private Value execute(Value[] args) { (String[] columnNames) -> statement.execute(sql, columnNames)); return NumberValue.fromBoolean(result); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -345,7 +346,7 @@ private Value executeUpdate(Value[] args) { (String[] columnNames) -> statement.executeUpdate(sql, columnNames)); return NumberValue.of(rowCount); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -363,7 +364,7 @@ private Value executeLargeUpdate(Value[] args) { (String[] columnNames) -> statement.executeLargeUpdate(sql, columnNames)); return NumberValue.of(rowCount); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } } @@ -463,7 +464,7 @@ private Value findColumn(Value[] args) { try { return NumberValue.of(rs.findColumn(args[0].asString())); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -476,7 +477,7 @@ private Value updateNull(Value[] args) { rs.updateNull(args[0].asString()); return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } } @@ -514,7 +515,7 @@ private static T columnData(Value value, AutogeneratedKeys autogeneratedK return columnNamesFunction.apply(columnNames); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } @@ -539,7 +540,7 @@ private static FunctionValue voidFunction(VoidResult result) { result.execute(); return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -551,7 +552,7 @@ private static FunctionValue voidIntFunction(VoidResultInt result) { result.execute(args[0].asInt()); return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -561,7 +562,7 @@ private static FunctionValue booleanFunction(BooleanResult result) { try { return NumberValue.fromBoolean(result.get()); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -571,7 +572,7 @@ private static Value intFunction(IntResult numberResult) { try { return NumberValue.of(numberResult.get()); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -581,7 +582,7 @@ private static Value stringFunction(StringResult stringResult) { try { return new StringValue(stringResult.get()); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -591,7 +592,7 @@ private static Value objectFunction(ObjectResult objectResult, Function Value getObjectResult(ObjectColumnResultInt numberResult, } return converter.apply(stringResult.get(args[0].asString())); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -664,7 +665,7 @@ private static Value getObjectResult(ObjectColumnResultInt numberResult, } return converter.apply(stringResult.get(args[0].asString()), args); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -676,7 +677,7 @@ private static Value updateData(VoidResultObject result, Function Value updateData(VoidColumnResultIntObject numberResult, F numberResult.execute(args[0].asInt(), converter.apply(args)); return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -705,7 +706,7 @@ private static Value updateData(VoidColumnResultIntObject numberResult, V } return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -728,7 +729,7 @@ private static Value arrayToResultSetValue(Array array, Value[] args) { } return new ResultSetValue(result); } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java index 5522c8e2..d0f96ce9 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.json; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.Value; @@ -17,7 +18,7 @@ public Value execute(Value[] args) { final Object root = new JSONTokener(jsonRaw).nextValue(); return ValueUtils.toValue(root); } catch (JSONException ex) { - throw new RuntimeException("Error while parsing json", ex); + throw new OwnLangRuntimeException("Error while parsing json", ex); } } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_encode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_encode.java index a7b4e649..53c7c8a3 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_encode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json_encode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.json; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Function; @@ -36,7 +37,7 @@ public Value execute(Value[] args) { return new StringValue(jsonRaw); } catch (JSONException ex) { - throw new RuntimeException("Error while creating json", ex); + throw new OwnLangRuntimeException("Error while creating json", ex); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java index c9452bac..95d30732 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/CallValue.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.okhttp; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Converters; import com.annimon.ownlang.lib.Function; @@ -56,7 +57,7 @@ private Value execute(Value[] args) { try { return new ResponseValue(call.execute()); } catch (IOException e) { - throw new RuntimeException(e); + throw new OwnLangRuntimeException(e); } } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java index 79dc95e5..495b4939 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/ResponseBodyValue.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.modules.okhttp; import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.Converters; @@ -27,7 +28,7 @@ private void init() { try { return ArrayValue.of(responseBody.bytes()); } catch (IOException e) { - throw new RuntimeException(e); + throw new OwnLangRuntimeException(e); } }); set("close", Converters.voidToVoid(responseBody::close)); @@ -37,7 +38,7 @@ private void init() { try { return new StringValue(responseBody.string()); } catch (IOException e) { - throw new RuntimeException(e); + throw new OwnLangRuntimeException(e); } }); set("file", args -> { @@ -48,7 +49,7 @@ private void init() { sink.close(); return NumberValue.ONE; } catch (IOException e) { - throw new RuntimeException(e); + throw new OwnLangRuntimeException(e); } }); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java index f82a9166..b97603cb 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot.java @@ -72,7 +72,7 @@ private static boolean initialize() { awtRobot = new Robot(); return true; } catch (AWTException awte) { - //throw new RuntimeException("Unable to create robot instance", awte); + //throw new OwnLangRuntimeException("Unable to create robot instance", awte); return false; } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java index b5692890..0bae4cac 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.std; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; @@ -23,7 +24,7 @@ static StringValue stringFromBytes(Value[] args) { try { return new StringValue(new String(bytes, charset)); } catch (UnsupportedEncodingException uee) { - throw new RuntimeException(uee); + throw new OwnLangRuntimeException(uee); } } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java index 774bd60b..b89de094 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.std; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.NumberValue; @@ -17,7 +18,7 @@ static ArrayValue getBytes(Value[] args) { try { return ArrayValue.of(args[0].asString().getBytes(charset)); } catch (UnsupportedEncodingException uee) { - throw new RuntimeException(uee); + throw new OwnLangRuntimeException(uee); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java index 5aa961c8..2c91c82d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.std; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.FunctionValue; @@ -31,7 +32,7 @@ public Value execute(Value[] args) { return queue.take(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); - throw new RuntimeException(ex); + throw new OwnLangRuntimeException(ex); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java index d69c3601..59529847 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.yaml; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.*; import java.util.LinkedHashMap; import java.util.List; @@ -21,7 +22,7 @@ public Value execute(Value[] args) { final Object root = new Yaml(options).load(yamlRaw); return process(root); } catch (Exception ex) { - throw new RuntimeException("Error while parsing yaml", ex); + throw new OwnLangRuntimeException("Error while parsing yaml", ex); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java index faccc2a4..3005e9c7 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.yaml; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -22,7 +23,7 @@ public Value execute(Value[] args) { final String yamlRaw = new Yaml(options).dump(root); return new StringValue(yamlRaw); } catch (Exception ex) { - throw new RuntimeException("Error while creating yaml", ex); + throw new OwnLangRuntimeException("Error while creating yaml", ex); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java index fdaacbd8..5000d47e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/zip/zip.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.modules.zip; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; @@ -98,7 +99,7 @@ private Value zipFileList(Map mappings, File output) { } } } catch (IOException ex) { - throw new RuntimeException("zip files", ex); + throw new OwnLangRuntimeException("zip files", ex); } return NumberValue.of(count); } @@ -193,7 +194,7 @@ private Value unzipFileList(File input, Map mappings) { } zis.closeEntry(); } catch (IOException ex) { - throw new RuntimeException("unzip files", ex); + throw new OwnLangRuntimeException("unzip files", ex); } return NumberValue.of(count); } @@ -261,7 +262,7 @@ private String[] listEntries(File input) { } zis.closeEntry(); } catch (IOException ex) { - throw new RuntimeException("list zip entries", ex); + throw new OwnLangRuntimeException("list zip entries", ex); } return entries.toArray(new String[0]); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java index b353fc07..83159a63 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java @@ -15,6 +15,11 @@ public OwnLangRuntimeException() { this.range = null; } + public OwnLangRuntimeException(Exception ex) { + super(ex); + this.range = null; + } + public OwnLangRuntimeException(String message) { this(message, (Range) null); } From ae94a1dc8a253c34bccaf1f4feac30076b5320cf Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 6 Oct 2023 20:08:07 +0300 Subject: [PATCH 347/448] Show location of the closest error from call stack --- .../java/com/annimon/ownlang/Console.java | 35 +++++++++---- .../annimon/ownlang/stages/StagesData.java | 7 +++ .../util/ErrorsLocationFormatterStage.java | 39 +++------------ .../com/annimon/ownlang/util/SimpleError.java | 11 ++++- .../util/SourceLocationFormatterStage.java | 49 +++++++++++++++++++ .../ownlang/util/input/SourceLoaderStage.java | 7 ++- 6 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocationFormatterStage.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/Console.java b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java index 76cafa9c..3b7676e0 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/Console.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java @@ -4,14 +4,16 @@ import com.annimon.ownlang.outputsettings.ConsoleOutputSettings; import com.annimon.ownlang.outputsettings.OutputSettings; import com.annimon.ownlang.stages.StagesData; -import com.annimon.ownlang.util.ErrorsLocationFormatterStage; -import com.annimon.ownlang.util.ExceptionConverterStage; -import com.annimon.ownlang.util.ExceptionStackTraceToStringStage; +import com.annimon.ownlang.util.*; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; import java.nio.charset.StandardCharsets; +import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; +import static com.annimon.ownlang.util.ErrorsLocationFormatterStage.*; public class Console { @@ -64,14 +66,29 @@ public static void error(CharSequence value) { } public static void handleException(StagesData stagesData, Thread thread, Exception exception) { - String mainError = new ExceptionConverterStage() + final var joiner = new StringJoiner("\n"); + joiner.add(new ExceptionConverterStage() .then((data, error) -> List.of(error)) .then(new ErrorsLocationFormatterStage()) - .perform(stagesData, exception); - String callStack = CallStack.getFormattedCalls(); - String stackTrace = new ExceptionStackTraceToStringStage() - .perform(stagesData, exception); - error(String.join("\n", mainError, "Thread: " + thread.getName(), callStack, stackTrace)); + .perform(stagesData, exception)); + final var processedPositions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new); + if (processedPositions.isEmpty()) { + // In case no source located errors were printed + // Find closest SourceLocated call stack frame + CallStack.getCalls().stream() + .limit(4) + .map(CallStack.CallInfo::range) + .filter(Objects::nonNull) + .findFirst() + .map(range -> new SourceLocationFormatterStage() + .perform(stagesData, range)) + .ifPresent(joiner::add); + } + joiner.add("Thread: " + thread.getName()); + joiner.add(CallStack.getFormattedCalls()); + joiner.add(new ExceptionStackTraceToStringStage() + .perform(stagesData, exception)); + error(joiner.toString()); } public static void handleException(Thread thread, Throwable throwable) { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java index 0f7a4030..4633a087 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.stages; +import java.util.function.Supplier; + public interface StagesData { T get(String tag); @@ -9,5 +11,10 @@ default T getOrDefault(String tag, T other) { return value != null ? value : other; } + default T getOrDefault(String tag, Supplier otherSuppler) { + T value = get(tag); + return value != null ? value : otherSuppler.get(); + } + void put(String tag, Object input); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java index 2b73da72..22d5c98c 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java @@ -4,53 +4,28 @@ import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; import com.annimon.ownlang.util.input.SourceLoaderStage; +import java.util.HashSet; +import static com.annimon.ownlang.util.SourceLocationFormatterStage.printPosition; public class ErrorsLocationFormatterStage implements Stage, String> { + public static final String TAG_POSITIONS = "formattedPositions"; @Override public String perform(StagesData stagesData, Iterable input) { final var sb = new StringBuilder(); - final String source = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE, ""); - final var lines = source.split("\r?\n"); + final var lines = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE_LINES, new String[0]); for (SourceLocatedError error : input) { sb.append(Console.newline()); sb.append(error); sb.append(Console.newline()); final Range range = error.getRange(); if (range != null && lines.length > 0) { + var positions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new); + positions.add(range); + stagesData.put(TAG_POSITIONS, positions); printPosition(sb, range.normalize(), lines); } } return sb.toString(); } - - private static void printPosition(StringBuilder sb, Range range, String[] lines) { - final Pos start = range.start(); - final int linesCount = lines.length;; - if (range.isOnSameLine()) { - if (start.row() < linesCount) { - sb.append(lines[start.row()]); - sb.append(Console.newline()); - sb.append(" ".repeat(start.col())); - sb.append("^".repeat(range.end().col() - start.col() + 1)); - sb.append(Console.newline()); - } - } else { - if (start.row() < linesCount) { - String line = lines[start.row()]; - sb.append(line); - sb.append(Console.newline()); - sb.append(" ".repeat(start.col())); - sb.append("^".repeat(Math.max(1, line.length() - start.col()))); - sb.append(Console.newline()); - } - final Pos end = range.end(); - if (end.row() < linesCount) { - sb.append(lines[end.row()]); - sb.append(Console.newline()); - sb.append("^".repeat(end.col())); - sb.append(Console.newline()); - } - } - } } \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java index 3b174940..16057c2d 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java @@ -1,6 +1,10 @@ package com.annimon.ownlang.util; -public record SimpleError(String message) implements SourceLocatedError { +public record SimpleError(String message, Range range) implements SourceLocatedError { + public SimpleError(String message) { + this(message, null); + } + @Override public String getMessage() { return message; @@ -10,4 +14,9 @@ public String getMessage() { public String toString() { return message; } + + @Override + public Range getRange() { + return range; + } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocationFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocationFormatterStage.java new file mode 100644 index 00000000..b798f9f7 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocationFormatterStage.java @@ -0,0 +1,49 @@ +package com.annimon.ownlang.util; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; +import com.annimon.ownlang.util.input.SourceLoaderStage; + +public class SourceLocationFormatterStage implements Stage { + + @Override + public String perform(StagesData stagesData, Range input) { + final var lines = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE_LINES, new String[0]); + final var sb = new StringBuilder(); + if (input != null && lines.length > 0) { + printPosition(sb, input.normalize(), lines); + } + return sb.toString(); + } + + static void printPosition(StringBuilder sb, Range range, String[] lines) { + final Pos start = range.start(); + final int linesCount = lines.length;; + if (range.isOnSameLine()) { + if (start.row() < linesCount) { + sb.append(lines[start.row()]); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(range.end().col() - start.col() + 1)); + sb.append(Console.newline()); + } + } else { + if (start.row() < linesCount) { + String line = lines[start.row()]; + sb.append(line); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(Math.max(1, line.length() - start.col()))); + sb.append(Console.newline()); + } + final Pos end = range.end(); + if (end.row() < linesCount) { + sb.append(lines[end.row()]); + sb.append(Console.newline()); + sb.append("^".repeat(end.col())); + sb.append(Console.newline()); + } + } + } +} \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java index f25682d1..40790546 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java @@ -10,13 +10,16 @@ public class SourceLoaderStage implements Stage { - public static final String TAG_SOURCE = "source"; + public static final String TAG_SOURCE_LINES = "sourceLines"; @Override public String perform(StagesData stagesData, InputSource inputSource) { try { String result = inputSource.load(); - stagesData.put(TAG_SOURCE, result); + final var lines = (result == null || result.isEmpty()) + ? new String[0] + : result.split("\r?\n"); + stagesData.put(TAG_SOURCE_LINES, lines); return result; } catch (IOException e) { throw new OwnLangRuntimeException("Unable to read input " + inputSource, e); From 22622b301302fee467ad99ea2e759ab2db8c13cb Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 6 Oct 2023 23:16:40 +0300 Subject: [PATCH 348/448] Better container access formatting --- .../parser/ast/ContainerAccessExpression.java | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index e6af0506..20424a75 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -3,16 +3,20 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.util.List; +import java.util.regex.Pattern; /** * * @author aNNiMON */ public final class ContainerAccessExpression implements Expression, Accessible { - + + private static final Pattern PATTERN_SIMPLE_INDEX = Pattern.compile("^\"[a-zA-Z$_]\\w*\""); + public final Expression root; public final List indices; - private boolean rootIsVariable; + private final boolean[] simpleIndices; + private final boolean rootIsVariable; public ContainerAccessExpression(String variable, List indices) { this(new VariableExpression(variable), indices); @@ -22,6 +26,7 @@ public ContainerAccessExpression(Expression root, List indices) { rootIsVariable = root instanceof VariableExpression; this.root = root; this.indices = indices; + simpleIndices = precomputeSimpleIndices(); } public boolean rootIsVariable() { @@ -55,21 +60,12 @@ public Value set(Value value) { final Value container = getContainer(); final Value lastIndex = lastIndex(); switch (container.type()) { - case Types.ARRAY: - ((ArrayValue) container).set(lastIndex.asInt(), value); - return value; - - case Types.MAP: - ((MapValue) container).set(lastIndex, value); - return value; - - case Types.CLASS: - ((ClassInstanceValue) container).set(lastIndex, value); - return value; - - default: - throw new TypeException("Array or map expected. Got " + container.type()); + case Types.ARRAY -> ((ArrayValue) container).set(lastIndex.asInt(), value); + case Types.MAP -> ((MapValue) container).set(lastIndex, value); + case Types.CLASS -> ((ClassInstanceValue) container).set(lastIndex, value); + default -> throw new TypeException("Array or map expected. Got " + container.type()); } + return value; } public Value getContainer() { @@ -111,8 +107,30 @@ public R accept(ResultVisitor visitor, T t) { return visitor.visit(this, t); } + private boolean[] precomputeSimpleIndices() { + final boolean[] result = new boolean[indices.size()]; + int i = 0; + for (Expression index : indices) { + String indexStr = index.toString(); + result[i] = PATTERN_SIMPLE_INDEX.matcher(indexStr).matches(); + i++; + } + return result; + } + @Override public String toString() { - return root.toString() + indices; + final var sb = new StringBuilder(root.toString()); + int i = 0; + for (Expression index : indices) { + String indexStr = index.toString(); + if (simpleIndices[i]) { + sb.append('.').append(indexStr, 1, indexStr.length() - 1); + } else { + sb.append('[').append(indexStr).append(']'); + } + i++; + } + return sb.toString(); } } From 3bed30ca59204390c0f3e83661e6b352795e8498 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 14 Oct 2023 00:07:15 +0300 Subject: [PATCH 349/448] Add syntax schema for Intellij Idea --- editors/README.md | 7 +++++++ editors/idea/IntelliJ IDEA Global Settings | 0 editors/idea/filetypes/OwnLang.xml | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 editors/README.md create mode 100644 editors/idea/IntelliJ IDEA Global Settings create mode 100644 editors/idea/filetypes/OwnLang.xml diff --git a/editors/README.md b/editors/README.md new file mode 100644 index 00000000..2c52659d --- /dev/null +++ b/editors/README.md @@ -0,0 +1,7 @@ +# Syntax for Editors + +## Intellij IDEA + +1. Open an `idea` folder +2. Add all files and folders to zip archive, e.g. `settings.zip` +3. File -> Manage IDE Settings -> Import. Select your zip file. diff --git a/editors/idea/IntelliJ IDEA Global Settings b/editors/idea/IntelliJ IDEA Global Settings new file mode 100644 index 00000000..e69de29b diff --git a/editors/idea/filetypes/OwnLang.xml b/editors/idea/filetypes/OwnLang.xml new file mode 100644 index 00000000..324962ef --- /dev/null +++ b/editors/idea/filetypes/OwnLang.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + \ No newline at end of file From a05e9e55e3ea76b036f3daf13bc448b83ad23db0 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 14 Oct 2023 00:09:08 +0300 Subject: [PATCH 350/448] [java] Add Boolean converter to Value, add collections example --- examples/java/collections.own | 44 ++++++++++++++++++ .../annimon/ownlang/modules/java/java.java | 9 +++- .../test/resources/benchmarks/calculator.own | 45 +++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 examples/java/collections.own create mode 100644 ownlang-parser/src/test/resources/benchmarks/calculator.own diff --git a/examples/java/collections.own b/examples/java/collections.own new file mode 100644 index 00000000..0041a2cc --- /dev/null +++ b/examples/java/collections.own @@ -0,0 +1,44 @@ +use java + +println "OwnLang array to Java array" +arr = toObject([1, 2, 4, 2, 0, 4, 3]) +println arr + +println "\nArrays.asList" +Arrays = newClass("java.util.Arrays") +list1 = Arrays.asList(arr) +println list1 + +println "\nStack" +Stack = newClass("java.util.Stack") +stack = new Stack() +stack.push(1) +stack.push(2) +stack.push(3) +println stack.pop() +println stack.pop() + + +println "\nArrayList from Stack" +ArrayList = newClass("java.util.ArrayList") +list2 = new ArrayList(stack) +list2.add(4) +for element : list2.toArray() { + println element +} + + +println "\nHashSet" +HashSet = newClass("java.util.HashSet") +set = new HashSet(list1) +println set +containsFour = set.contains(4) +// NOTE: containsFour is java.lang.Boolean +println containsFour.getClass() +isContainsFour = toValue(containsFour) +if (isContainsFour) { + println "Set contains 4" +} +for element : set.toArray() { + println element +} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java index 059f4057..55eca9d8 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/java/java.java @@ -24,6 +24,8 @@ public final class java implements Module { public Map constants() { final var result = new LinkedHashMap(16); result.put("null", NULL); + result.put("TRUE", new ObjectValue(Boolean.TRUE)); + result.put("FALSE", new ObjectValue(Boolean.FALSE)); result.put("boolean.class", new ClassValue(boolean.class)); result.put("boolean[].class", new ClassValue(boolean[].class)); result.put("boolean[][].class", new ClassValue(boolean[][].class)); @@ -258,8 +260,11 @@ private Value toObject(Value[] args) { private Value toValue(Value[] args) { Arguments.check(1, args.length); - if (args[0] instanceof ObjectValue) { - return objectToValue( ((ObjectValue) args[0]).object ); + if (args[0] instanceof ObjectValue obj) { + if (obj.object != null && Boolean.class.isAssignableFrom(obj.object.getClass())) { + return NumberValue.fromBoolean((Boolean) obj.object); + } + return objectToValue(obj.object); } return NULL; } diff --git a/ownlang-parser/src/test/resources/benchmarks/calculator.own b/ownlang-parser/src/test/resources/benchmarks/calculator.own new file mode 100644 index 00000000..a4386a8c --- /dev/null +++ b/ownlang-parser/src/test/resources/benchmarks/calculator.own @@ -0,0 +1,45 @@ +// Simple parser example +use std, types + +operations = { + "+" : def(a,b) = a+b, + "-" : def(a,b) = a-b, + "*" : def(a,b) = a*b, + "/" : def(a,b) = a/b, + "%" : def(a,b) = a%b, + ">" : def(a,b) = a>b, + "<" : def(a,b) = a4") From f6d4ff5cc947dae6a2c7e8a574ccac9150e2c99c Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 14 Oct 2023 14:34:05 +0300 Subject: [PATCH 351/448] Parse error highlighting for include statement --- .../ownlang/parser/ast/IncludeStatement.java | 39 +++++++++---------- .../ownlang/parser/ast/UnaryExpression.java | 16 ++++---- .../ownlang/parser/visitors/VisitorUtils.java | 12 +++++- .../resources/modules/yaml/yamldecode.own | 14 +++---- .../resources/modules/yaml/yamlencode.own | 34 ++++++++-------- 5 files changed, 61 insertions(+), 54 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java index 3b2c5de2..8963de6c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -1,12 +1,11 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.parser.Lexer; -import com.annimon.ownlang.parser.Parser; -import com.annimon.ownlang.parser.SourceLoader; -import com.annimon.ownlang.parser.Token; -import com.annimon.ownlang.parser.visitors.FunctionAdder; -import java.io.IOException; -import java.util.List; +import com.annimon.ownlang.exceptions.OwnLangParserException; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; +import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; +import com.annimon.ownlang.stages.*; +import com.annimon.ownlang.util.input.InputSourceFile; +import com.annimon.ownlang.util.input.SourceLoaderStage; /** * @@ -23,22 +22,22 @@ public IncludeStatement(Expression expression) { @Override public void execute() { super.interruptionCheck(); + + final var stagesData = new StagesDataMap(); try { - final Statement program = loadProgram(expression.eval().asString()); - if (program != null) { - program.accept(new FunctionAdder()); - program.execute(); - } - } catch (Exception ex) { - throw new RuntimeException(ex); + final String path = expression.eval().asString(); + new SourceLoaderStage() + .then(new LexerStage()) + .then(new ParserStage()) + .then(new FunctionAddingStage()) + .then(new ExecutionStage()) + .perform(stagesData, new InputSourceFile(path)); + } catch (OwnLangParserException ex) { + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + throw new OwnLangRuntimeException(error, ex); } } - - public Statement loadProgram(String path) throws IOException { - final String input = SourceLoader.readSource(path); - final List tokens = Lexer.tokenize(input); - return Parser.parse(tokens); - } @Override public void accept(Visitor visitor) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java index 551ba6ac..20d35b15 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -53,27 +53,27 @@ public Value eval() { final Value value = expr1.eval(); switch (operation) { case INCREMENT_PREFIX: { - if (expr1 instanceof Accessible) { - return ((Accessible) expr1).set(increment(value)); + if (expr1 instanceof Accessible accessible) { + return accessible.set(increment(value)); } return increment(value); } case DECREMENT_PREFIX: { - if (expr1 instanceof Accessible) { - return ((Accessible) expr1).set(decrement(value)); + if (expr1 instanceof Accessible accessible) { + return accessible.set(decrement(value)); } return decrement(value); } case INCREMENT_POSTFIX: { - if (expr1 instanceof Accessible) { - ((Accessible) expr1).set(increment(value)); + if (expr1 instanceof Accessible accessible) { + accessible.set(increment(value)); return value; } return increment(value); } case DECREMENT_POSTFIX: { - if (expr1 instanceof Accessible) { - ((Accessible) expr1).set(decrement(value)); + if (expr1 instanceof Accessible accessible) { + accessible.set(decrement(value)); return value; } return decrement(value); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java index 1c593b81..007f7643 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java @@ -3,6 +3,10 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.parser.Lexer; +import com.annimon.ownlang.parser.Parser; +import com.annimon.ownlang.parser.SourceLoader; +import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.BinaryExpression; import com.annimon.ownlang.parser.ast.ConditionalExpression; import com.annimon.ownlang.parser.ast.IncludeStatement; @@ -13,6 +17,7 @@ import com.annimon.ownlang.parser.ast.VariableExpression; import java.io.IOException; import java.util.HashSet; +import java.util.List; import java.util.Set; public final class VisitorUtils { @@ -28,9 +33,12 @@ public static boolean isVariable(Node node) { } public static Statement includeProgram(IncludeStatement s) { - if (!isValue(s)) return null; + if (!isValue(s.expression)) return null; try { - return s.loadProgram(s.expression.eval().asString()); + final String path = s.expression.eval().asString(); + final String input = SourceLoader.readSource(path); + final List tokens = Lexer.tokenize(input); + return Parser.parse(tokens); } catch (IOException ex) { return null; } diff --git a/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own b/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own index 7906f2ed..4deea1a4 100644 --- a/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own +++ b/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own @@ -1,4 +1,4 @@ -use std, yaml, ounit +use std, yaml x = yamldecode(" name: \"std\" @@ -23,9 +23,9 @@ x = yamldecode(" print typeof([]) // 3 (ARRAY) ") -assertEquals("std", x.name) -assertEquals("both", x.scope) -assertEquals(0, length(x.constants)) -assertEquals(2, length(x.functions)) -assertEquals("arrayCombine", x.functions[0].name) -assertEquals("возвращает тип переданного значения", x.functions[1].desc_ru) \ No newline at end of file + assertEquals("std", x.name) + assertEquals("both", x.scope) + assertEquals(0, x.constants.length) + assertEquals(2, x.functions.length) + assertEquals("arrayCombine", x.functions[0].name) + assertEquals("возвращает тип переданного значения", x.functions[1].desc_ru) diff --git a/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own b/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own index 77e2e3b2..7ee56093 100644 --- a/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own +++ b/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own @@ -1,20 +1,20 @@ -use std, yaml, ounit +use std, yaml yml = yamlencode({ - "name": "Yaml Example", - "version": 1, - "arrayData": [ - 1, 2, 3, 4 - ], - "objectData": { - "key": "value", - 10: "1000" - } -}) -obj = yamldecode(yml) + "name": "Yaml Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } + }) + obj = yamldecode(yml) -assertEquals("Yaml Example", obj.name) -assertEquals(1, obj.version) -assertEquals(4, length(obj.arrayData)) -assertEquals("value", obj.objectData.key) -assertEquals("1000", obj.objectData["10"]) \ No newline at end of file + assertEquals("Yaml Example", obj.name) + assertEquals(1, obj.version) + assertEquals(4, length(obj.arrayData)) + assertEquals("value", obj.objectData.key) + assertEquals("1000", obj.objectData["10"]) \ No newline at end of file From 1535e86472e221d60720e3b7c412a8f5c7486f1a Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 14 Oct 2023 18:47:27 +0300 Subject: [PATCH 352/448] Remove unnecessary abstraction Expression, use Statement only as marker --- .../ownlang/lib/UserDefinedFunction.java | 2 +- .../com/annimon/ownlang/parser/Parser.java | 132 +++++++++--------- .../annimon/ownlang/parser/ast/Argument.java | 2 +- .../annimon/ownlang/parser/ast/Arguments.java | 2 +- .../ownlang/parser/ast/ArrayExpression.java | 6 +- .../parser/ast/AssignmentExpression.java | 15 +- .../ownlang/parser/ast/BinaryExpression.java | 6 +- .../ownlang/parser/ast/BlockStatement.java | 15 +- .../ownlang/parser/ast/BreakStatement.java | 4 +- .../parser/ast/ClassDeclarationStatement.java | 5 +- .../parser/ast/ConditionalExpression.java | 6 +- .../parser/ast/ContainerAccessExpression.java | 16 +-- .../ownlang/parser/ast/ContinueStatement.java | 4 +- .../ast/DestructuringAssignmentStatement.java | 7 +- .../ownlang/parser/ast/DoWhileStatement.java | 12 +- .../ownlang/parser/ast/ExprStatement.java | 13 +- .../ownlang/parser/ast/Expression.java | 12 -- .../ownlang/parser/ast/ForStatement.java | 14 +- .../parser/ast/ForeachArrayStatement.java | 13 +- .../parser/ast/ForeachMapStatement.java | 13 +- .../parser/ast/FunctionDefineStatement.java | 5 +- .../ast/FunctionReferenceExpression.java | 2 +- .../parser/ast/FunctionalExpression.java | 19 +-- .../ownlang/parser/ast/IfStatement.java | 14 +- .../ownlang/parser/ast/IncludeStatement.java | 9 +- .../ownlang/parser/ast/MapExpression.java | 12 +- .../ownlang/parser/ast/MatchExpression.java | 27 ++-- .../com/annimon/ownlang/parser/ast/Node.java | 4 + .../parser/ast/ObjectCreationExpression.java | 8 +- .../ownlang/parser/ast/PrintStatement.java | 9 +- .../ownlang/parser/ast/PrintlnStatement.java | 9 +- .../ownlang/parser/ast/ReturnStatement.java | 6 +- .../annimon/ownlang/parser/ast/Statement.java | 3 +- .../ownlang/parser/ast/TernaryExpression.java | 8 +- .../ownlang/parser/ast/UnaryExpression.java | 11 +- .../ownlang/parser/ast/UseStatement.java | 4 +- .../ownlang/parser/ast/ValueExpression.java | 2 +- .../parser/ast/VariableExpression.java | 6 +- .../ownlang/parser/ast/WhileStatement.java | 12 +- .../ownlang/parser/linters/LintVisitor.java | 4 +- .../ownlang/parser/linters/LinterStage.java | 6 +- .../optimization/DeadCodeElimination.java | 7 +- .../optimization/InstructionCombining.java | 16 +-- .../optimization/OptimizationStage.java | 7 +- .../optimization/OptimizationVisitor.java | 94 ++++++------- .../parser/optimization/VariablesGrabber.java | 2 +- .../parser/visitors/AbstractVisitor.java | 12 +- .../parser/visitors/FunctionAdder.java | 2 +- .../parser/visitors/ModuleDetector.java | 7 +- .../ownlang/parser/visitors/PrintVisitor.java | 12 +- .../ownlang/parser/visitors/VisitorUtils.java | 3 +- .../ownlang/stages/ExecutionStage.java | 8 +- .../ownlang/stages/FunctionAddingStage.java | 6 +- .../annimon/ownlang/stages/ParserStage.java | 8 +- .../annimon/ownlang/parser/ParserTest.java | 8 +- .../ownlang/parser/ProgramsBenchmarkTest.java | 6 +- .../annimon/ownlang/parser/ProgramsTest.java | 4 +- .../annimon/ownlang/parser/ast/ASTHelper.java | 8 +- .../parser/ast/VariableExpressionTest.java | 8 +- .../java/com/annimon/ownlang/utils/Repl.java | 6 +- .../com/annimon/ownlang/utils/Sandbox.java | 5 +- 61 files changed, 355 insertions(+), 353 deletions(-) delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Expression.java diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index f6015c02..c62877aa 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -65,7 +65,7 @@ public Value execute(Value[] values) { final Argument arg = arguments.get(i); ScopeHandler.defineVariableInCurrentScope(arg.name(), arg.valueExpr().eval()); } - body.execute(); + body.eval(); return NumberValue.ZERO; } catch (ReturnStatement rt) { return rt.getResult(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 0f52743a..555e3ce8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -20,9 +20,9 @@ */ public final class Parser { - public static Statement parse(List tokens) { + public static Node parse(List tokens) { final Parser parser = new Parser(tokens); - final Statement program = parser.parse(); + final Node program = parser.parse(); if (parser.getParseErrors().hasErrors()) { throw new OwnLangParserException(parser.getParseErrors()); } @@ -71,7 +71,7 @@ public ParseErrors getParseErrors() { return parseErrors; } - public Statement parse() { + public Node parse() { parseErrors.clear(); final BlockStatement result = new BlockStatement(); while (!match(TokenType.EOF)) { @@ -180,7 +180,7 @@ private Statement assignmentStatement() { if (match(TokenType.EXTRACT)) { return destructuringAssignment(); } - final Expression expression = expression(); + final Node expression = expression(); if (expression instanceof Statement statement) { return statement; } @@ -209,7 +209,7 @@ private DestructuringAssignmentStatement destructuringAssignment() { } private Statement ifElse() { - final Expression condition = expression(); + final Node condition = expression(); final Statement ifStatement = statementOrBlock(); final Statement elseStatement; if (match(TokenType.ELSE)) { @@ -221,7 +221,7 @@ private Statement ifElse() { } private Statement whileStatement() { - final Expression condition = expression(); + final Node condition = expression(); final Statement statement = statementOrBlock(); return new WhileStatement(condition, statement); } @@ -229,7 +229,7 @@ private Statement whileStatement() { private Statement doWhileStatement() { final Statement statement = statementOrBlock(); consume(TokenType.WHILE); - final Expression condition = expression(); + final Node condition = expression(); return new DoWhileStatement(condition, statement); } @@ -252,7 +252,7 @@ && lookMatch(foreachIndex + 3, TokenType.COLON)) { boolean optParentheses = match(TokenType.LPAREN); final Statement initialization = assignmentStatement(); consume(TokenType.COMMA); - final Expression termination = expression(); + final Node termination = expression(); consume(TokenType.COMMA); final Statement increment = assignmentStatement(); if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses @@ -265,7 +265,7 @@ private ForeachArrayStatement foreachArrayStatement() { boolean optParentheses = match(TokenType.LPAREN); final String variable = consume(TokenType.WORD).text(); consume(TokenType.COLON); - final Expression container = expression(); + final Node container = expression(); if (optParentheses) { consume(TokenType.RPAREN); // close opt parentheses } @@ -280,7 +280,7 @@ private ForeachMapStatement foreachMapStatement() { consume(TokenType.COMMA); final String value = consume(TokenType.WORD).text(); consume(TokenType.COLON); - final Expression container = expression(); + final Node container = expression(); if (optParentheses) { consume(TokenType.RPAREN); // close opt parentheses } @@ -332,14 +332,14 @@ private ExprStatement functionCallStatement() { ); } - private Expression functionChain(Expression qualifiedNameExpr) { + private Node functionChain(Node qualifiedNameExpr) { // f1()()() || f1().f2().f3() || f1().key - final Expression expr = function(qualifiedNameExpr); + final Node expr = function(qualifiedNameExpr); if (lookMatch(0, TokenType.LPAREN)) { return functionChain(expr); } if (lookMatch(0, TokenType.DOT)) { - final List indices = variableSuffix(); + final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { return expr; } @@ -354,7 +354,7 @@ private Expression functionChain(Expression qualifiedNameExpr) { return expr; } - private FunctionalExpression function(Expression qualifiedNameExpr) { + private FunctionalExpression function(Node qualifiedNameExpr) { // function(arg1, arg2, ...) final var startTokenIndex = index - 1; consume(TokenType.LPAREN); @@ -367,10 +367,10 @@ private FunctionalExpression function(Expression qualifiedNameExpr) { return function; } - private Expression array() { + private Node array() { // [value1, value2, ...] consume(TokenType.LBRACKET); - final List elements = new ArrayList<>(); + final List elements = new ArrayList<>(); while (!match(TokenType.RBRACKET)) { elements.add(expression()); match(TokenType.COMMA); @@ -378,14 +378,14 @@ private Expression array() { return new ArrayExpression(elements); } - private Expression map() { + private Node map() { // {key1 : value1, key2 : value2, ...} consume(TokenType.LBRACE); - final Map elements = new HashMap<>(); + final Map elements = new HashMap<>(); while (!match(TokenType.RBRACE)) { - final Expression key = primary(); + final Node key = primary(); consume(TokenType.COLON); - final Expression value = expression(); + final Node value = expression(); elements.put(key, value); match(TokenType.COMMA); } @@ -397,7 +397,7 @@ private MatchExpression match() { // case pattern1: result1 // case pattern2 if expr: result2 // } - final Expression expression = expression(); + final Node expression = expression(); consume(TokenType.LBRACE); final List patterns = new ArrayList<>(); do { @@ -494,12 +494,12 @@ private Statement classDeclaration() { return classDeclaration; } - private Expression expression() { + private Node expression() { return assignment(); } - private Expression assignment() { - final Expression assignment = assignmentStrict(); + private Node assignment() { + final Node assignment = assignmentStrict(); if (assignment != null) { return assignment; } @@ -509,7 +509,7 @@ private Expression assignment() { private AssignmentExpression assignmentStrict() { // x[0].prop += ... final int position = index; - final Expression targetExpr = qualifiedName(); + final Node targetExpr = qualifiedName(); if (!(targetExpr instanceof Accessible)) { index = position; return null; @@ -523,18 +523,18 @@ private AssignmentExpression assignmentStrict() { match(currentType); final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType); - final Expression expression = expression(); + final Node expression = expression(); return new AssignmentExpression(op, (Accessible) targetExpr, expression); } - private Expression ternary() { - Expression result = nullCoalesce(); + private Node ternary() { + Node result = nullCoalesce(); if (match(TokenType.QUESTION)) { - final Expression trueExpr = expression(); + final Node trueExpr = expression(); consume(TokenType.COLON); - final Expression falseExpr = expression(); + final Node falseExpr = expression(); return new TernaryExpression(result, trueExpr, falseExpr); } if (match(TokenType.QUESTIONCOLON)) { @@ -543,8 +543,8 @@ private Expression ternary() { return result; } - private Expression nullCoalesce() { - Expression result = logicalOr(); + private Node nullCoalesce() { + Node result = logicalOr(); while (true) { if (match(TokenType.QUESTIONQUESTION)) { @@ -557,8 +557,8 @@ private Expression nullCoalesce() { return result; } - private Expression logicalOr() { - Expression result = logicalAnd(); + private Node logicalOr() { + Node result = logicalAnd(); while (true) { if (match(TokenType.BARBAR)) { @@ -571,8 +571,8 @@ private Expression logicalOr() { return result; } - private Expression logicalAnd() { - Expression result = bitwiseOr(); + private Node logicalAnd() { + Node result = bitwiseOr(); while (true) { if (match(TokenType.AMPAMP)) { @@ -585,8 +585,8 @@ private Expression logicalAnd() { return result; } - private Expression bitwiseOr() { - Expression expression = bitwiseXor(); + private Node bitwiseOr() { + Node expression = bitwiseXor(); while (true) { if (match(TokenType.BAR)) { @@ -599,8 +599,8 @@ private Expression bitwiseOr() { return expression; } - private Expression bitwiseXor() { - Expression expression = bitwiseAnd(); + private Node bitwiseXor() { + Node expression = bitwiseAnd(); while (true) { if (match(TokenType.CARET)) { @@ -613,8 +613,8 @@ private Expression bitwiseXor() { return expression; } - private Expression bitwiseAnd() { - Expression expression = equality(); + private Node bitwiseAnd() { + Node expression = equality(); while (true) { if (match(TokenType.AMP)) { @@ -627,8 +627,8 @@ private Expression bitwiseAnd() { return expression; } - private Expression equality() { - Expression result = conditional(); + private Node equality() { + Node result = conditional(); if (match(TokenType.EQEQ)) { return new ConditionalExpression(ConditionalExpression.Operator.EQUALS, result, conditional()); @@ -640,8 +640,8 @@ private Expression equality() { return result; } - private Expression conditional() { - Expression result = shift(); + private Node conditional() { + Node result = shift(); while (true) { if (match(TokenType.LT)) { @@ -666,8 +666,8 @@ private Expression conditional() { return result; } - private Expression shift() { - Expression expression = additive(); + private Node shift() { + Node expression = additive(); while (true) { if (match(TokenType.LTLT)) { @@ -692,8 +692,8 @@ private Expression shift() { return expression; } - private Expression additive() { - Expression result = multiplicative(); + private Node additive() { + Node result = multiplicative(); while (true) { if (match(TokenType.PLUS)) { @@ -722,8 +722,8 @@ private Expression additive() { return result; } - private Expression multiplicative() { - Expression result = objectCreation(); + private Node multiplicative() { + Node result = objectCreation(); while (true) { if (match(TokenType.STAR)) { @@ -748,11 +748,11 @@ private Expression multiplicative() { return result; } - private Expression objectCreation() { + private Node objectCreation() { if (match(TokenType.NEW)) { final var startTokenIndex = index - 1; final String className = consume(TokenType.WORD).text(); - final List args = new ArrayList<>(); + final List args = new ArrayList<>(); consume(TokenType.LPAREN); while (!match(TokenType.RPAREN)) { args.add(expression()); @@ -766,7 +766,7 @@ private Expression objectCreation() { return unary(); } - private Expression unary() { + private Node unary() { if (match(TokenType.PLUSPLUS)) { return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary()); } @@ -788,9 +788,9 @@ private Expression unary() { return primary(); } - private Expression primary() { + private Node primary() { if (match(TokenType.LPAREN)) { - Expression result = expression(); + Node result = expression(); consume(TokenType.RPAREN); return result; } @@ -813,13 +813,13 @@ private Expression primary() { return variable(); } - private Expression variable() { + private Node variable() { // function(... if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return functionChain(new ValueExpression(consume(TokenType.WORD).text())); } - final Expression qualifiedNameExpr = qualifiedName(); + final Node qualifiedNameExpr = qualifiedName(); if (qualifiedNameExpr != null) { // variable(args) || arr["key"](args) || obj.key(args) if (lookMatch(0, TokenType.LPAREN)) { @@ -844,28 +844,28 @@ private Expression variable() { return value(); } - private Expression qualifiedName() { + private Node qualifiedName() { // var || var.key[index].key2 final Token current = get(0); if (!match(TokenType.WORD)) return null; - final List indices = variableSuffix(); + final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { return new VariableExpression(current.text()); } return new ContainerAccessExpression(current.text(), indices); } - private List variableSuffix() { + private List variableSuffix() { // .key1.arr1[expr1][expr2].key2 if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) { return null; } - final List indices = new ArrayList<>(); + final List indices = new ArrayList<>(); while (lookMatch(0, TokenType.DOT) || lookMatch(0, TokenType.LBRACKET)) { if (match(TokenType.DOT)) { final String fieldName = consume(TokenType.WORD).text(); - final Expression key = new ValueExpression(fieldName); + final Node key = new ValueExpression(fieldName); indices.add(key); } if (match(TokenType.LBRACKET)) { @@ -876,7 +876,7 @@ private List variableSuffix() { return indices; } - private Expression value() { + private Node value() { final Token current = get(0); if (match(TokenType.NUMBER)) { return new ValueExpression(createNumber(current.text(), 10)); @@ -898,7 +898,7 @@ private Expression value() { new ValueExpression(consume(TokenType.WORD).text()) ))); } - final List indices = variableSuffix(); + final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { return strExpr; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java index 34cea902..9c923038 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser.ast; -public record Argument(String name, Expression valueExpr) { +public record Argument(String name, Node valueExpr) { public Argument(String name) { this(name, null); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java index 7dc068f7..c3cb2ccf 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java @@ -22,7 +22,7 @@ public void addRequired(String name) { requiredArgumentsCount++; } - public void addOptional(String name, Expression expr) { + public void addOptional(String name, Node expr) { arguments.add(new Argument(name, expr)); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java index 7feed693..11a740f6 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java @@ -8,11 +8,11 @@ * * @author aNNiMON */ -public final class ArrayExpression implements Expression { +public final class ArrayExpression implements Node { - public final List elements; + public final List elements; - public ArrayExpression(List arguments) { + public ArrayExpression(List arguments) { this.elements = arguments; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java index 170cab2f..a6982842 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java @@ -6,22 +6,17 @@ * * @author aNNiMON */ -public final class AssignmentExpression extends InterruptableNode implements Expression, Statement { +public final class AssignmentExpression extends InterruptableNode implements Statement { public final Accessible target; public final BinaryExpression.Operator operation; - public final Expression expression; + public final Node expression; - public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Expression expr) { + public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Node expr) { this.operation = operation; this.target = target; this.expression = expr; } - - @Override - public void execute() { - eval(); - } @Override public Value eval() { @@ -30,8 +25,8 @@ public Value eval() { // Simple assignment return target.set(expression.eval()); } - final Expression expr1 = new ValueExpression(target.get()); - final Expression expr2 = new ValueExpression(expression.eval()); + final Node expr1 = new ValueExpression(target.get()); + final Node expr2 = new ValueExpression(expression.eval()); return target.set(new BinaryExpression(operation, expr1, expr2).eval()); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java index e6b5499e..0524499c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -8,7 +8,7 @@ * * @author aNNiMON */ -public final class BinaryExpression implements Expression { +public final class BinaryExpression implements Node { public enum Operator { ADD("+"), @@ -45,9 +45,9 @@ public String toString() { } public final Operator operation; - public final Expression expr1, expr2; + public final Node expr1, expr2; - public BinaryExpression(Operator operation, Expression expr1, Expression expr2) { + public BinaryExpression(Operator operation, Node expr1, Node expr2) { this.operation = operation; this.expr1 = expr1; this.expr2 = expr2; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java index 17ddc23c..13d37b0a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BlockStatement.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; import java.util.ArrayList; import java.util.List; @@ -10,22 +12,23 @@ */ public final class BlockStatement extends InterruptableNode implements Statement { - public final List statements; + public final List statements; public BlockStatement() { statements = new ArrayList<>(); } - public void add(Statement statement) { + public void add(Node statement) { statements.add(statement); } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); - for (Statement statement : statements) { - statement.execute(); + for (Node statement : statements) { + statement.eval(); } + return NumberValue.ZERO; } @Override @@ -41,7 +44,7 @@ public R accept(ResultVisitor visitor, T t) { @Override public String toString() { final StringBuilder result = new StringBuilder(); - for (Statement statement : statements) { + for (Node statement : statements) { result.append(statement.toString()).append(Console.newline()); } return result.toString(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java index e27d350b..925a7c99 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON @@ -7,7 +9,7 @@ public final class BreakStatement extends RuntimeException implements Statement { @Override - public void execute() { + public Value eval() { throw this; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java index 92a272c4..b8f3e94e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.ClassDeclarations; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; import java.util.ArrayList; import java.util.List; @@ -25,8 +27,9 @@ public void addMethod(FunctionDefineStatement statement) { } @Override - public void execute() { + public Value eval() { ClassDeclarations.set(name, this); + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java index 01630014..e6f14e79 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ConditionalExpression.java @@ -9,7 +9,7 @@ * * @author aNNiMON */ -public final class ConditionalExpression implements Expression { +public final class ConditionalExpression implements Node { public enum Operator { EQUALS("=="), @@ -36,10 +36,10 @@ public String getName() { } } - public final Expression expr1, expr2; + public final Node expr1, expr2; public final Operator operation; - public ConditionalExpression(Operator operation, Expression expr1, Expression expr2) { + public ConditionalExpression(Operator operation, Node expr1, Node expr2) { this.operation = operation; this.expr1 = expr1; this.expr2 = expr2; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 20424a75..b60c19c6 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -9,20 +9,20 @@ * * @author aNNiMON */ -public final class ContainerAccessExpression implements Expression, Accessible { +public final class ContainerAccessExpression implements Node, Accessible { private static final Pattern PATTERN_SIMPLE_INDEX = Pattern.compile("^\"[a-zA-Z$_]\\w*\""); - public final Expression root; - public final List indices; + public final Node root; + public final List indices; private final boolean[] simpleIndices; private final boolean rootIsVariable; - public ContainerAccessExpression(String variable, List indices) { + public ContainerAccessExpression(String variable, List indices) { this(new VariableExpression(variable), indices); } - public ContainerAccessExpression(Expression root, List indices) { + public ContainerAccessExpression(Node root, List indices) { rootIsVariable = root instanceof VariableExpression; this.root = root; this.indices = indices; @@ -33,7 +33,7 @@ public boolean rootIsVariable() { return rootIsVariable; } - public Expression getRoot() { + public Node getRoot() { return root; } @@ -110,7 +110,7 @@ public R accept(ResultVisitor visitor, T t) { private boolean[] precomputeSimpleIndices() { final boolean[] result = new boolean[indices.size()]; int i = 0; - for (Expression index : indices) { + for (Node index : indices) { String indexStr = index.toString(); result[i] = PATTERN_SIMPLE_INDEX.matcher(indexStr).matches(); i++; @@ -122,7 +122,7 @@ private boolean[] precomputeSimpleIndices() { public String toString() { final var sb = new StringBuilder(root.toString()); int i = 0; - for (Expression index : indices) { + for (Node index : indices) { String indexStr = index.toString(); if (simpleIndices[i]) { sb.append('.').append(indexStr, 1, indexStr.length() - 1); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java index 442ff090..d8ea6310 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java @@ -1,5 +1,7 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON @@ -7,7 +9,7 @@ public final class ContinueStatement extends RuntimeException implements Statement { @Override - public void execute() { + public Value eval() { throw this; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java index 9e7a7c0b..253328b2 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java @@ -13,21 +13,22 @@ public final class DestructuringAssignmentStatement extends InterruptableNode implements Statement { public final List variables; - public final Expression containerExpression; + public final Node containerExpression; - public DestructuringAssignmentStatement(List arguments, Expression container) { + public DestructuringAssignmentStatement(List arguments, Node container) { this.variables = arguments; this.containerExpression = container; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); final Value container = containerExpression.eval(); switch (container.type()) { case Types.ARRAY -> execute((ArrayValue) container); case Types.MAP -> execute((MapValue) container); } + return NumberValue.ZERO; } private void execute(ArrayValue array) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java index 5bbfa435..980212ef 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DoWhileStatement.java @@ -1,25 +1,28 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON */ public final class DoWhileStatement extends InterruptableNode implements Statement { - public final Expression condition; + public final Node condition; public final Statement statement; - public DoWhileStatement(Expression condition, Statement statement) { + public DoWhileStatement(Node condition, Statement statement) { this.condition = condition; this.statement = statement; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); do { try { - statement.execute(); + statement.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -27,6 +30,7 @@ public void execute() { } } while (condition.eval().asInt() != 0); + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java index 7688e46a..5e849638 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java @@ -7,22 +7,17 @@ * * @author aNNiMON */ -public final class ExprStatement extends InterruptableNode implements Expression, Statement { +public final class ExprStatement extends InterruptableNode implements Statement { - public final Expression expr; + public final Node expr; - public ExprStatement(Expression function) { + public ExprStatement(Node function) { this.expr = function; } - @Override - public void execute() { - super.interruptionCheck(); - expr.eval(); - } - @Override public Value eval() { + super.interruptionCheck(); return expr.eval(); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Expression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Expression.java deleted file mode 100644 index 98865f52..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Expression.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.annimon.ownlang.parser.ast; - -import com.annimon.ownlang.lib.Value; - -/** - * - * @author aNNiMON - */ -public interface Expression extends Node { - - Value eval(); -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java index e0553718..0648b42c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForStatement.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 @@ -7,11 +10,11 @@ public final class ForStatement extends InterruptableNode implements Statement { public final Statement initialization; - public final Expression termination; + public final Node termination; public final Statement increment; public final Statement statement; - public ForStatement(Statement initialization, Expression termination, Statement increment, Statement block) { + public ForStatement(Statement initialization, Node termination, Statement increment, Statement block) { this.initialization = initialization; this.termination = termination; this.increment = increment; @@ -19,17 +22,18 @@ public ForStatement(Statement initialization, Expression termination, Statement } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); - for (initialization.execute(); termination.eval().asInt() != 0; increment.execute()) { + for (initialization.eval(); termination.eval().asInt() != 0; increment.eval()) { try { - statement.execute(); + statement.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { // continue; } } + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java index 608b301a..33200242 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -11,17 +11,17 @@ public final class ForeachArrayStatement extends InterruptableNode implements Statement { public final String variable; - public final Expression container; + public final Node container; public final Statement body; - public ForeachArrayStatement(String variable, Expression container, Statement body) { + public ForeachArrayStatement(String variable, Node container, Statement body) { this.variable = variable; this.container = container; this.body = body; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); try (final var ignored = ScopeHandler.closeableScope()) { final Value containerValue = container.eval(); @@ -32,13 +32,14 @@ public void execute() { default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); } } + return NumberValue.ZERO; } private void iterateString(String str) { for (char ch : str.toCharArray()) { ScopeHandler.setVariable(variable, new StringValue(String.valueOf(ch))); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -51,7 +52,7 @@ private void iterateArray(ArrayValue containerValue) { for (Value value : containerValue) { ScopeHandler.setVariable(variable, value); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -67,7 +68,7 @@ private void iterateMap(MapValue containerValue) { entry.getValue() })); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java index 816d8f45..a1199493 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -11,10 +11,10 @@ public final class ForeachMapStatement extends InterruptableNode implements Statement { public final String key, value; - public final Expression container; + public final Node container; public final Statement body; - public ForeachMapStatement(String key, String value, Expression container, Statement body) { + public ForeachMapStatement(String key, String value, Node container, Statement body) { this.key = key; this.value = value; this.container = container; @@ -22,7 +22,7 @@ public ForeachMapStatement(String key, String value, Expression container, State } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); try (final var ignored = ScopeHandler.closeableScope()) { final Value containerValue = container.eval(); @@ -33,6 +33,7 @@ public void execute() { default -> throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); } } + return NumberValue.ZERO; } private void iterateString(String str) { @@ -40,7 +41,7 @@ private void iterateString(String str) { ScopeHandler.setVariable(key, new StringValue(String.valueOf(ch))); ScopeHandler.setVariable(value, NumberValue.of(ch)); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -55,7 +56,7 @@ private void iterateArray(ArrayValue containerValue) { ScopeHandler.setVariable(key, v); ScopeHandler.setVariable(value, NumberValue.of(index++)); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -69,7 +70,7 @@ private void iterateMap(MapValue containerValue) { ScopeHandler.setVariable(key, entry.getKey()); ScopeHandler.setVariable(value, entry.getValue()); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java index 9664e1b3..f8127560 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -1,7 +1,9 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.UserDefinedFunction; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.util.Range; import com.annimon.ownlang.util.SourceLocation; @@ -29,8 +31,9 @@ public Range getRange() { } @Override - public void execute() { + public Value eval() { ScopeHandler.setFunction(name, new UserDefinedFunction(arguments, body, range)); + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java index 891abeb7..f6f09ce6 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java @@ -6,7 +6,7 @@ * * @author aNNiMON */ -public final class FunctionReferenceExpression extends InterruptableNode implements Expression { +public final class FunctionReferenceExpression extends InterruptableNode { public final String name; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 733a22b6..1475ef47 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -13,18 +13,18 @@ * @author aNNiMON */ public final class FunctionalExpression extends InterruptableNode - implements Expression, Statement, SourceLocation { + implements Statement, SourceLocation { - public final Expression functionExpr; - public final List arguments; + public final Node functionExpr; + public final List arguments; private Range range; - public FunctionalExpression(Expression functionExpr) { + public FunctionalExpression(Node functionExpr) { this.functionExpr = functionExpr; arguments = new ArrayList<>(); } - public void addArgument(Expression arg) { + public void addArgument(Node arg) { arguments.add(arg); } @@ -36,11 +36,6 @@ public void setRange(Range range) { public Range getRange() { return range; } - - @Override - public void execute() { - eval(); - } @Override public Value eval() { @@ -57,7 +52,7 @@ public Value eval() { return result; } - private Function consumeFunction(Expression expr) { + private Function consumeFunction(Node expr) { final Value value = expr.eval(); if (value.type() == Types.FUNCTION) { return ((FunctionValue) value).getValue(); @@ -96,7 +91,7 @@ public String toString() { } else { sb.append(functionExpr).append('('); } - final Iterator it = arguments.iterator(); + final Iterator it = arguments.iterator(); if (it.hasNext()) { sb.append(it.next()); while (it.hasNext()) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java index 985dbcdb..ae92e9f0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java @@ -1,29 +1,33 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON */ public final class IfStatement extends InterruptableNode implements Statement { - public final Expression expression; + public final Node expression; public final Statement ifStatement, elseStatement; - public IfStatement(Expression expression, Statement ifStatement, Statement elseStatement) { + public IfStatement(Node expression, Statement ifStatement, Statement elseStatement) { this.expression = expression; this.ifStatement = ifStatement; this.elseStatement = elseStatement; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); final int result = expression.eval().asInt(); if (result != 0) { - ifStatement.execute(); + ifStatement.eval(); } else if (elseStatement != null) { - elseStatement.execute(); + elseStatement.eval(); } + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java index 8963de6c..0262a6f8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -2,6 +2,8 @@ import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.exceptions.OwnLangRuntimeException; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.stages.*; import com.annimon.ownlang.util.input.InputSourceFile; @@ -13,14 +15,14 @@ */ public final class IncludeStatement extends InterruptableNode implements Statement { - public final Expression expression; + public final Node expression; - public IncludeStatement(Expression expression) { + public IncludeStatement(Node expression) { this.expression = expression; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); final var stagesData = new StagesDataMap(); @@ -37,6 +39,7 @@ public void execute() { .perform(stagesData, ex.getParseErrors()); throw new OwnLangRuntimeException(error, ex); } + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java index 988d3b6a..05c399db 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MapExpression.java @@ -9,11 +9,11 @@ * * @author aNNiMON */ -public final class MapExpression implements Expression { +public final class MapExpression implements Node { - public final Map elements; + public final Map elements; - public MapExpression(Map arguments) { + public MapExpression(Map arguments) { this.elements = arguments; } @@ -21,7 +21,7 @@ public MapExpression(Map arguments) { public Value eval() { final int size = elements.size(); final MapValue map = new MapValue(size); - for (Map.Entry entry : elements.entrySet()) { + for (Map.Entry entry : elements.entrySet()) { map.set(entry.getKey().eval(), entry.getValue().eval()); } return map; @@ -41,9 +41,9 @@ public R accept(ResultVisitor visitor, T t) { public String toString() { final StringBuilder sb = new StringBuilder(); sb.append('{'); - Iterator> it = elements.entrySet().iterator(); + Iterator> it = elements.entrySet().iterator(); if (it.hasNext()) { - Map.Entry entry = it.next(); + Map.Entry entry = it.next(); sb.append(entry.getKey()).append(" : ").append(entry.getValue()); while (it.hasNext()) { entry = it.next(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 824cc7e8..228fb5bb 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -11,21 +11,16 @@ * * @author aNNiMON */ -public final class MatchExpression extends InterruptableNode implements Expression, Statement { +public final class MatchExpression extends InterruptableNode implements Statement { - public final Expression expression; + public final Node expression; public final List patterns; - public MatchExpression(Expression expression, List patterns) { + public MatchExpression(Node expression, List patterns) { this.expression = expression; this.patterns = patterns; } - @Override - public void execute() { - eval(); - } - @Override public Value eval() { super.interruptionCheck(); @@ -73,7 +68,7 @@ private boolean matchTuplePattern(ArrayValue array, TuplePattern p) { final int size = array.size(); for (int i = 0; i < size; i++) { - final Expression expr = p.values.get(i); + final Node expr = p.values.get(i); if ( (expr != ANY) && (expr.eval().compareTo(array.get(i)) != 0) ) { return false; } @@ -146,7 +141,7 @@ private boolean optMatches(Pattern pattern) { private Value evalResult(Statement s) { try { - s.execute(); + s.eval(); } catch (ReturnStatement ret) { return ret.getResult(); } @@ -176,7 +171,7 @@ public String toString() { public abstract static sealed class Pattern { public Statement result; - public Expression optCondition; + public Node optCondition; @Override public String toString() { @@ -247,13 +242,13 @@ public String toString() { } public static final class TuplePattern extends Pattern { - public final List values; + public final List values; public TuplePattern() { this(new ArrayList<>()); } - public TuplePattern(List parts) { + public TuplePattern(List parts) { this.values = parts; } @@ -261,13 +256,13 @@ public void addAny() { values.add(ANY); } - public void add(Expression value) { + public void add(Node value) { values.add(value); } @Override public String toString() { - final Iterator it = values.iterator(); + final Iterator it = values.iterator(); if (it.hasNext()) { final StringBuilder sb = new StringBuilder(); sb.append('(').append(it.next()); @@ -281,7 +276,7 @@ public String toString() { } } - public static final Expression ANY = new Expression() { + public static final Node ANY = new Node() { @Override public Value eval() { return NumberValue.ONE; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java index c9d2d834..a9c883d4 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java @@ -1,10 +1,14 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON */ public interface Node { + + Value eval(); void accept(Visitor visitor); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index 52863274..5204f3da 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -7,13 +7,13 @@ import java.util.Iterator; import java.util.List; -public final class ObjectCreationExpression implements Expression, SourceLocation { +public final class ObjectCreationExpression implements Node, SourceLocation { public final String className; - public final List constructorArguments; + public final List constructorArguments; private Range range; - public ObjectCreationExpression(String className, List constructorArguments) { + public ObjectCreationExpression(String className, List constructorArguments) { this.className = className; this.constructorArguments = constructorArguments; } @@ -80,7 +80,7 @@ public R accept(ResultVisitor visitor, T t) { public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("new ").append(className).append(' '); - final Iterator it = constructorArguments.iterator(); + final Iterator it = constructorArguments.iterator(); if (it.hasNext()) { sb.append(it.next()); while (it.hasNext()) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java index e4aa6dd2..27c3580c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; /** * @@ -8,16 +10,17 @@ */ public final class PrintStatement extends InterruptableNode implements Statement { - public final Expression expression; + public final Node expression; - public PrintStatement(Expression expression) { + public PrintStatement(Node expression) { this.expression = expression; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); Console.print(expression.eval().asString()); + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java index 20276db8..134747cd 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.Console; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; /** * @@ -8,16 +10,17 @@ */ public final class PrintlnStatement extends InterruptableNode implements Statement { - public final Expression expression; + public final Node expression; - public PrintlnStatement(Expression expression) { + public PrintlnStatement(Node expression) { this.expression = expression; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); Console.println(expression.eval().asString()); + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java index 8e6aaaad..3a62d193 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java @@ -8,10 +8,10 @@ */ public final class ReturnStatement extends RuntimeException implements Statement { - public final Expression expression; + public final Node expression; private Value result; - public ReturnStatement(Expression expression) { + public ReturnStatement(Node expression) { this.expression = expression; } @@ -20,7 +20,7 @@ public Value getResult() { } @Override - public void execute() { + public Value eval() { result = expression.eval(); throw this; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java index db7ce9c1..a09e0737 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java @@ -5,6 +5,5 @@ * @author aNNiMON */ public interface Statement extends Node { - - void execute(); + } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java index 255f2b31..31af483b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java @@ -6,12 +6,12 @@ * * @author aNNiMON */ -public final class TernaryExpression implements Expression { +public final class TernaryExpression implements Node { - public final Expression condition; - public final Expression trueExpr, falseExpr; + public final Node condition; + public final Node trueExpr, falseExpr; - public TernaryExpression(Expression condition, Expression trueExpr, Expression falseExpr) { + public TernaryExpression(Node condition, Node trueExpr, Node falseExpr) { this.condition = condition; this.trueExpr = trueExpr; this.falseExpr = falseExpr; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java index 20d35b15..4892d85b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UnaryExpression.java @@ -10,7 +10,7 @@ * * @author aNNiMON */ -public final class UnaryExpression implements Expression, Statement { +public final class UnaryExpression implements Statement { public enum Operator { INCREMENT_PREFIX("++"), @@ -35,19 +35,14 @@ public String toString() { } } - public final Expression expr1; + public final Node expr1; public final Operator operation; - public UnaryExpression(Operator operation, Expression expr1) { + public UnaryExpression(Operator operation, Node expr1) { this.operation = operation; this.expr1 = expr1; } - @Override - public void execute() { - eval(); - } - @Override public Value eval() { final Value value = expr1.eval(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java index c4d9054b..9869c71d 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -2,6 +2,7 @@ import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.ModuleLoader; +import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.modules.Module; import java.util.Collection; @@ -20,11 +21,12 @@ public UseStatement(Collection modules) { } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); for (String module : modules) { ModuleLoader.loadAndUse(module); } + return NumberValue.ZERO; } public Map loadConstants() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java index 1b3c9ef4..02171b7a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ValueExpression.java @@ -11,7 +11,7 @@ * * @author aNNiMON */ -public final class ValueExpression extends InterruptableNode implements Expression { +public final class ValueExpression extends InterruptableNode { public final Value value; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java index 36c39237..b997f040 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -8,7 +8,7 @@ * * @author aNNiMON */ -public final class VariableExpression extends InterruptableNode implements Expression, Accessible { +public final class VariableExpression extends InterruptableNode implements Accessible { public final String name; @@ -24,7 +24,9 @@ public Value eval() { @Override public Value get() { - if (!ScopeHandler.isVariableOrConstantExists(name)) throw new VariableDoesNotExistsException(name); + if (!ScopeHandler.isVariableOrConstantExists(name)) { + throw new VariableDoesNotExistsException(name); + } return ScopeHandler.getVariableOrConstant(name); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java index 73256eb8..995a0f23 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/WhileStatement.java @@ -1,31 +1,35 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Value; + /** * * @author aNNiMON */ public final class WhileStatement extends InterruptableNode implements Statement { - public final Expression condition; + public final Node condition; public final Statement statement; - public WhileStatement(Expression condition, Statement statement) { + public WhileStatement(Node condition, Statement statement) { this.condition = condition; this.statement = statement; } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); while (condition.eval().asInt() != 0) { try { - statement.execute(); + statement.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { // continue; } } + return NumberValue.ZERO; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java index a97f919b..7906fa12 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java @@ -1,7 +1,7 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.parser.ast.IncludeStatement; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.parser.visitors.VisitorUtils; @@ -15,7 +15,7 @@ abstract class LintVisitor extends AbstractVisitor { } protected void applyVisitor(IncludeStatement s, Visitor visitor) { - final Statement program = VisitorUtils.includeProgram(s); + final Node program = VisitorUtils.includeProgram(s); if (program != null) { program.accept(visitor); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java index 49726208..70235308 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.ScopeHandler; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; @@ -10,10 +10,10 @@ import java.util.Comparator; import java.util.List; -public class LinterStage implements Stage { +public class LinterStage implements Stage { @Override - public Statement perform(StagesData stagesData, Statement input) { + public Node perform(StagesData stagesData, Node input) { final List results = new ArrayList<>(); final Visitor[] validators = new Visitor[] { new AssignValidator(results), diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java index 188e89a9..a2b20e92 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/DeadCodeElimination.java @@ -4,7 +4,6 @@ import com.annimon.ownlang.parser.ast.AssignmentExpression; import com.annimon.ownlang.parser.ast.BlockStatement; import com.annimon.ownlang.parser.ast.ExprStatement; -import com.annimon.ownlang.parser.ast.Expression; import com.annimon.ownlang.parser.ast.IfStatement; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Statement; @@ -123,7 +122,7 @@ public Node visit(AssignmentExpression s, Map t) { public Node visit(BlockStatement s, Map t) { final BlockStatement result = new BlockStatement(); boolean changed = false; - for (Statement statement : s.statements) { + for (Node statement : s.statements) { final Node node = statement.accept(this, t); if (node != statement) { changed = true; @@ -135,8 +134,8 @@ public Node visit(BlockStatement s, Map t) { if (node instanceof Statement stmt) { result.add(stmt); - } else if (node instanceof Expression expr) { - result.add(new ExprStatement(expr)); + } else if (node != null) { + result.add(new ExprStatement(node)); } } if (changed) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java index 75004dfa..8230cb8a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/InstructionCombining.java @@ -2,11 +2,9 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.parser.ast.BlockStatement; -import com.annimon.ownlang.parser.ast.Expression; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.PrintStatement; import com.annimon.ownlang.parser.ast.PrintlnStatement; -import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.ValueExpression; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isConstantValue; @@ -48,8 +46,8 @@ public Node visit(BlockStatement s, Void t) { final BlockStatement result = new BlockStatement(); int i; for (i = 1; i < size; i++) { - Statement s1 = s.statements.get(i - 1); - Statement s2 = s.statements.get(i); + Node s1 = s.statements.get(i - 1); + Node s2 = s.statements.get(i); Node n1 = s1.accept(this, t); Node n2 = s2.accept(this, t); if (n1 != s1 || n2 != s2) { @@ -57,7 +55,7 @@ public Node visit(BlockStatement s, Void t) { } final Node combined = tryCombine(n1, n2); if (combined == null) { - result.add((Statement) n1); + result.add(n1); } else { changed = true; result.add(consumeStatement(combined)); @@ -66,12 +64,12 @@ public Node visit(BlockStatement s, Void t) { } if (i == size) { // Last node - Statement s2 = s.statements.get(size - 1); + Node s2 = s.statements.get(size - 1); Node n2 = s2.accept(this, t); if (n2 != s2) { changed = true; } - result.add((Statement) n2); + result.add(n2); } if (changed) { return result; @@ -91,10 +89,10 @@ private Node tryCombine(Node n1, Node n2) { else n2Type = 0; if (n1Type != 0 && n2Type != 0) { - final Expression e1 = (n1Type == 1) + final Node e1 = (n1Type == 1) ? ((PrintStatement) n1).expression : ((PrintlnStatement) n1).expression; - final Expression e2 = (n2Type == 1) + final Node e2 = (n2Type == 1) ? ((PrintStatement) n2).expression : ((PrintlnStatement) n2).expression; if (isConstantValue(e1) && isConstantValue(e2)) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java index ee7811c5..0f7d1cd9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java @@ -1,11 +1,10 @@ package com.annimon.ownlang.parser.optimization; import com.annimon.ownlang.parser.ast.Node; -import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; -public class OptimizationStage implements Stage { +public class OptimizationStage implements Stage { public static final String TAG_OPTIMIZATION_SUMMARY = "optimizationSummary"; @@ -30,7 +29,7 @@ public OptimizationStage(int level, boolean summary) { } @Override - public Statement perform(StagesData stagesData, Statement input) { + public Node perform(StagesData stagesData, Node input) { if (level == 0) return input; Node result = input; @@ -51,6 +50,6 @@ public Statement perform(StagesData stagesData, Statement input) { ); } - return (Statement) result; + return result; } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 1ba0d845..4a42c701 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -14,14 +14,14 @@ public abstract class OptimizationVisitor implements ResultVisitor { @Override public Node visit(ArrayExpression s, T t) { - final List elements = new ArrayList<>(s.elements.size()); + final List elements = new ArrayList<>(s.elements.size()); boolean changed = false; - for (Expression expression : s.elements) { + for (Node expression : s.elements) { final Node node = expression.accept(this, t); if (node != expression) { changed = true; } - elements.add((Expression) node); + elements.add(node); } if (changed) { return new ArrayExpression(elements); @@ -34,7 +34,7 @@ public Node visit(AssignmentExpression s, T t) { final Node exprNode = s.expression.accept(this, t); final Node targetNode = s.target.accept(this, t); if ( (exprNode != s.expression || targetNode != s.target) && (targetNode instanceof Accessible) ) { - return new AssignmentExpression(s.operation, (Accessible) targetNode, (Expression) exprNode); + return new AssignmentExpression(s.operation, (Accessible) targetNode, exprNode); } return s; } @@ -44,7 +44,7 @@ public Node visit(BinaryExpression s, T t) { final Node expr1 = s.expr1.accept(this, t); final Node expr2 = s.expr2.accept(this, t); if (expr1 != s.expr1 || expr2 != s.expr2) { - return new BinaryExpression(s.operation, (Expression) expr1, (Expression) expr2); + return new BinaryExpression(s.operation, expr1, expr2); } return s; } @@ -53,15 +53,13 @@ public Node visit(BinaryExpression s, T t) { public Node visit(BlockStatement s, T t) { boolean changed = false; final BlockStatement result = new BlockStatement(); - for (Statement statement : s.statements) { + for (Node statement : s.statements) { final Node node = statement.accept(this, t); if (node != statement) { changed = true; } - if (node instanceof Statement) { - result.add((Statement) node); - } else if (node instanceof Expression) { - result.add(new ExprStatement((Expression) node)); + if (node != null) { + result.add(consumeStatement(node)); } } if (changed) { @@ -85,7 +83,7 @@ public Node visit(ConditionalExpression s, T t) { final Node expr1 = s.expr1.accept(this, t); final Node expr2 = s.expr2.accept(this, t); if (expr1 != s.expr1 || expr2 != s.expr2) { - return new ConditionalExpression(s.operation, (Expression) expr1, (Expression) expr2); + return new ConditionalExpression(s.operation, expr1, expr2); } return s; } @@ -95,16 +93,16 @@ public Node visit(ContainerAccessExpression s, T t) { final Node root = s.root.accept(this, t); boolean changed = (root != s.root); - final List indices = new ArrayList<>(s.indices.size()); - for (Expression expression : s.indices) { + final List indices = new ArrayList<>(s.indices.size()); + for (Node expression : s.indices) { final Node node = expression.accept(this, t); if (node != expression) { changed = true; } - indices.add((Expression) node); + indices.add(node); } if (changed) { - return new ContainerAccessExpression((Expression) root, indices); + return new ContainerAccessExpression(root, indices); } return s; } @@ -119,7 +117,7 @@ public Node visit(DoWhileStatement s, T t) { final Node condition = s.condition.accept(this, t); final Node statement = s.statement.accept(this, t); if (condition != s.condition || statement != s.statement) { - return new DoWhileStatement((Expression) condition, consumeStatement(statement)); + return new DoWhileStatement(condition, consumeStatement(statement)); } return s; } @@ -128,7 +126,7 @@ public Node visit(DoWhileStatement s, T t) { public Node visit(DestructuringAssignmentStatement s, T t) { final Node expr = s.containerExpression.accept(this, t); if (expr != s.containerExpression) { - return new DestructuringAssignmentStatement(s.variables, (Expression) expr); + return new DestructuringAssignmentStatement(s.variables, expr); } return s; } @@ -142,7 +140,7 @@ public Node visit(ForStatement s, T t) { if (initialization != s.initialization || termination != s.termination || increment != s.increment || statement != s.statement) { return new ForStatement(consumeStatement(initialization), - (Expression) termination, consumeStatement(increment), consumeStatement(statement)); + termination, consumeStatement(increment), consumeStatement(statement)); } return s; } @@ -152,7 +150,7 @@ public Node visit(ForeachArrayStatement s, T t) { final Node container = s.container.accept(this, t); final Node body = s.body.accept(this, t); if (container != s.container || body != s.body) { - return new ForeachArrayStatement(s.variable, (Expression) container, consumeStatement(body)); + return new ForeachArrayStatement(s.variable, container, consumeStatement(body)); } return s; } @@ -162,7 +160,7 @@ public Node visit(ForeachMapStatement s, T t) { final Node container = s.container.accept(this, t); final Node body = s.body.accept(this, t); if (container != s.container || body != s.body) { - return new ForeachMapStatement(s.key, s.value, (Expression) container, consumeStatement(body)); + return new ForeachMapStatement(s.key, s.value, container, consumeStatement(body)); } return s; } @@ -188,7 +186,7 @@ public Node visit(FunctionReferenceExpression s, T t) { public Node visit(ExprStatement s, T t) { final Node expr = s.expr.accept(this, t); if (expr != s.expr) { - return new ExprStatement((Expression) expr); + return new ExprStatement(expr); } return s; } @@ -196,14 +194,14 @@ public Node visit(ExprStatement s, T t) { @Override public Node visit(FunctionalExpression s, T t) { final Node functionExpr = s.functionExpr.accept(this, t); - final FunctionalExpression result = new FunctionalExpression((Expression) functionExpr); + final FunctionalExpression result = new FunctionalExpression(functionExpr); boolean changed = functionExpr != s.functionExpr; - for (Expression argument : s.arguments) { + for (Node argument : s.arguments) { final Node expr = argument.accept(this, t); if (expr != argument) { changed = true; } - result.addArgument((Expression) expr); + result.addArgument(expr); } if (changed) { return result; @@ -222,7 +220,7 @@ public Node visit(IfStatement s, T t) { elseStatement = null; } if (expression != s.expression || ifStatement != s.ifStatement || elseStatement != s.elseStatement) { - return new IfStatement((Expression) expression, consumeStatement(ifStatement), + return new IfStatement(expression, consumeStatement(ifStatement), (elseStatement == null ? null : consumeStatement(elseStatement)) ); } return s; @@ -232,22 +230,22 @@ public Node visit(IfStatement s, T t) { public Node visit(IncludeStatement s, T t) { final Node expression = s.expression.accept(this, t); if (expression != s.expression) { - return new IncludeStatement((Expression) expression); + return new IncludeStatement(expression); } return s; } @Override public Node visit(MapExpression s, T t) { - final Map elements = new HashMap<>(s.elements.size()); + final Map elements = new HashMap<>(s.elements.size()); boolean changed = false; - for (Map.Entry entry : s.elements.entrySet()) { + for (Map.Entry entry : s.elements.entrySet()) { final Node key = entry.getKey().accept(this, t); final Node value = entry.getValue().accept(this, t); if (key != entry.getKey() || value != entry.getValue()) { changed = true; } - elements.put((Expression) key, (Expression) value); + elements.put(key, value); } if (changed) { return new MapExpression(elements); @@ -268,7 +266,7 @@ public Node visit(MatchExpression s, T t) { if ((node != expr) && isValue(node)) { changed = true; final Value value = ((ValueExpression) node).value; - final Expression optCondition = pattern.optCondition; + final Node optCondition = pattern.optCondition; final Statement result = pattern.result; pattern = new MatchExpression.ConstantPattern(value); pattern.optCondition = optCondition; @@ -277,21 +275,21 @@ public Node visit(MatchExpression s, T t) { } if (pattern instanceof MatchExpression.TuplePattern tuple) { - final List newValues = new ArrayList<>(tuple.values.size()); + final List newValues = new ArrayList<>(tuple.values.size()); boolean valuesChanged = false; - for (Expression value : tuple.values) { + for (Node value : tuple.values) { if (value != MatchExpression.ANY) { final Node node = value.accept(this, t); if (node != value) { valuesChanged = true; - value = (Expression) node; + value = node; } } newValues.add(value); } if (valuesChanged) { changed = true; - final Expression optCondition = pattern.optCondition; + final Node optCondition = pattern.optCondition; final Statement result = pattern.result; pattern = new MatchExpression.TuplePattern(newValues); pattern.optCondition = optCondition; @@ -309,28 +307,28 @@ public Node visit(MatchExpression s, T t) { Node optCond = pattern.optCondition.accept(this, t); if (optCond != pattern.optCondition) { changed = true; - pattern.optCondition = (Expression) optCond; + pattern.optCondition = optCond; } } patterns.add(pattern); } if (changed) { - return new MatchExpression((Expression) expression, patterns); + return new MatchExpression(expression, patterns); } return s; } @Override public Node visit(ObjectCreationExpression s, T t) { - final List args = new ArrayList<>(); + final List args = new ArrayList<>(); boolean changed = false; - for (Expression argument : s.constructorArguments) { + for (Node argument : s.constructorArguments) { final Node expr = argument.accept(this, t); if (expr != argument) { changed = true; } - args.add((Expression) expr); + args.add(expr); } if (changed) { @@ -343,7 +341,7 @@ public Node visit(ObjectCreationExpression s, T t) { public Node visit(PrintStatement s, T t) { final Node expression = s.expression.accept(this, t); if (expression != s.expression) { - return new PrintStatement((Expression) expression); + return new PrintStatement(expression); } return s; } @@ -352,7 +350,7 @@ public Node visit(PrintStatement s, T t) { public Node visit(PrintlnStatement s, T t) { final Node expression = s.expression.accept(this, t); if (expression != s.expression) { - return new PrintlnStatement((Expression) expression); + return new PrintlnStatement(expression); } return s; } @@ -361,7 +359,7 @@ public Node visit(PrintlnStatement s, T t) { public Node visit(ReturnStatement s, T t) { final Node expression = s.expression.accept(this, t); if (expression != s.expression) { - return new ReturnStatement((Expression) expression); + return new ReturnStatement(expression); } return s; } @@ -372,7 +370,7 @@ public Node visit(TernaryExpression s, T t) { final Node trueExpr = s.trueExpr.accept(this, t); final Node falseExpr = s.falseExpr.accept(this, t); if (condition != s.condition || trueExpr != s.trueExpr || falseExpr != s.falseExpr) { - return new TernaryExpression((Expression) condition, (Expression) trueExpr, (Expression) falseExpr); + return new TernaryExpression(condition, trueExpr, falseExpr); } return s; } @@ -381,7 +379,7 @@ public Node visit(TernaryExpression s, T t) { public Node visit(UnaryExpression s, T t) { final Node expr1 = s.expr1.accept(this, t); if (expr1 != s.expr1) { - return new UnaryExpression(s.operation, (Expression) expr1); + return new UnaryExpression(s.operation, expr1); } return s; } @@ -407,7 +405,7 @@ public Node visit(WhileStatement s, T t) { final Node condition = s.condition.accept(this, t); final Node statement = s.statement.accept(this, t); if (condition != s.condition || statement != s.statement) { - return new WhileStatement((Expression) condition, consumeStatement(statement)); + return new WhileStatement(condition, consumeStatement(statement)); } return s; } @@ -434,7 +432,7 @@ protected boolean visit(final Arguments in, final Arguments out, T t) { boolean changed = false; out.setRange(in.getRange()); for (Argument argument : in) { - final Expression valueExpr = argument.valueExpr(); + final Node valueExpr = argument.valueExpr(); if (valueExpr == null) { out.addRequired(argument.name()); } else { @@ -442,7 +440,7 @@ protected boolean visit(final Arguments in, final Arguments out, T t) { if (expr != valueExpr) { changed = true; } - out.addOptional(argument.name(), (Expression) expr); + out.addOptional(argument.name(), expr); } } return changed; @@ -452,6 +450,6 @@ protected Statement consumeStatement(Node node) { if (node instanceof Statement statement) { return statement; } - return new ExprStatement((Expression) node); + return new ExprStatement(node); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index 928b10f7..6553e91c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -110,7 +110,7 @@ protected boolean visit(Arguments in, Arguments out, Map t final String variableName = argument.name(); grabVariableInfo(t, variableName); /* No need to add value - it is optional arguments - final Expression expr = argument.getValueExpr(); + final Node expr = argument.getValueExpr(); if (expr != null && isValue(expr)) { var.value = ((ValueExpression) expr).value; }*/ diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 11b17fdc..f2bac0d8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -12,7 +12,7 @@ public abstract class AbstractVisitor implements Visitor { @Override public void visit(ArrayExpression s) { - for (Expression index : s.elements) { + for (Node index : s.elements) { index.accept(this); } } @@ -30,7 +30,7 @@ public void visit(BinaryExpression s) { @Override public void visit(BlockStatement s) { - for (Statement statement : s.statements) { + for (Node statement : s.statements) { statement.accept(this); } } @@ -53,7 +53,7 @@ public void visit(ConditionalExpression s) { @Override public void visit(ContainerAccessExpression s) { s.root.accept(this); - for (Expression index : s.indices) { + for (Node index : s.indices) { index.accept(this); } } @@ -110,7 +110,7 @@ public void visit(ExprStatement s) { @Override public void visit(FunctionalExpression s) { s.functionExpr.accept(this); - for (Expression argument : s.arguments) { + for (Node argument : s.arguments) { argument.accept(this); } } @@ -131,7 +131,7 @@ public void visit(IncludeStatement s) { @Override public void visit(MapExpression s) { - for (Map.Entry entry : s.elements.entrySet()) { + for (Map.Entry entry : s.elements.entrySet()) { entry.getKey().accept(this); entry.getValue().accept(this); } @@ -144,7 +144,7 @@ public void visit(MatchExpression s) { @Override public void visit(ObjectCreationExpression s) { - for (Expression argument : s.constructorArguments) { + for (Node argument : s.constructorArguments) { argument.accept(this); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java index 96a25abe..2f905150 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java @@ -11,6 +11,6 @@ public final class FunctionAdder extends AbstractVisitor { @Override public void visit(FunctionDefineStatement s) { super.visit(s); - s.execute(); + s.eval(); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java index f3993920..d83020f8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -1,10 +1,7 @@ package com.annimon.ownlang.parser.visitors; -import com.annimon.ownlang.parser.ast.ArrayExpression; -import com.annimon.ownlang.parser.ast.Expression; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.UseStatement; -import com.annimon.ownlang.parser.ast.ValueExpression; import java.util.HashSet; import java.util.Set; @@ -16,7 +13,7 @@ public ModuleDetector() { modules = new HashSet<>(); } - public Set detect(Statement s) { + public Set detect(Node s) { s.accept(this); return modules; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java index 093c0d88..7fdadc80 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java @@ -21,7 +21,7 @@ public PrintVisitor() { @Override public StringBuilder visit(ArrayExpression s, StringBuilder t) { t.append('['); - final Iterator it = s.elements.iterator(); + final Iterator it = s.elements.iterator(); if (it.hasNext()) { it.next().accept(this, t); while (it.hasNext()) { @@ -58,7 +58,7 @@ public StringBuilder visit(BlockStatement s, StringBuilder t) { } increaseIndent(); - for (Statement statement : s.statements) { + for (Node statement : s.statements) { newLine(t); printIndent(t); statement.accept(this, t); @@ -108,7 +108,7 @@ public StringBuilder visit(ConditionalExpression s, StringBuilder t) { @Override public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) { s.root.accept(this, t); - for (Expression index : s.indices) { + for (Node index : s.indices) { t.append('['); index.accept(this, t); t.append(']'); @@ -266,7 +266,7 @@ public StringBuilder visit(MapExpression s, StringBuilder t) { t.append('{'); increaseIndent(); boolean firstElement = true; - for (Map.Entry entry : s.elements.entrySet()) { + for (Map.Entry entry : s.elements.entrySet()) { if (firstElement) firstElement = false; else t.append(","); newLine(t); @@ -440,10 +440,10 @@ private StringBuilder visitFunctionBody(Statement s, StringBuilder t) { return t; } - private void printArgs(StringBuilder t, List args) { + private void printArgs(StringBuilder t, List args) { t.append("("); boolean firstElement = true; - for (Expression expr : args) { + for (Node expr : args) { if (firstElement) firstElement = false; else t.append(", "); expr.accept(this, t); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java index 007f7643..1b842a51 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java @@ -11,7 +11,6 @@ import com.annimon.ownlang.parser.ast.ConditionalExpression; import com.annimon.ownlang.parser.ast.IncludeStatement; import com.annimon.ownlang.parser.ast.Node; -import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.UnaryExpression; import com.annimon.ownlang.parser.ast.ValueExpression; import com.annimon.ownlang.parser.ast.VariableExpression; @@ -32,7 +31,7 @@ public static boolean isVariable(Node node) { return (node instanceof VariableExpression); } - public static Statement includeProgram(IncludeStatement s) { + public static Node includeProgram(IncludeStatement s) { if (!isValue(s.expression)) return null; try { final String path = s.expression.eval().asString(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java index 2d7b91a7..ceb69b10 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java @@ -1,12 +1,12 @@ package com.annimon.ownlang.stages; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; -public class ExecutionStage implements Stage { +public class ExecutionStage implements Stage { @Override - public Statement perform(StagesData stagesData, Statement input) { - input.execute(); + public Node perform(StagesData stagesData, Node input) { + input.eval(); return input; } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java index 28d917d6..effb6234 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java @@ -1,12 +1,12 @@ package com.annimon.ownlang.stages; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.visitors.FunctionAdder; -public class FunctionAddingStage implements Stage { +public class FunctionAddingStage implements Stage { @Override - public Statement perform(StagesData stagesData, Statement input) { + public Node perform(StagesData stagesData, Node input) { input.accept(new FunctionAdder()); return input; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java index 7e22fa6c..9b522af9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java @@ -3,18 +3,18 @@ import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.Token; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import java.util.List; -public class ParserStage implements Stage, Statement> { +public class ParserStage implements Stage, Node> { public static final String TAG_PROGRAM = "program"; public static final String TAG_HAS_PARSE_ERRORS = "hasParseErrors"; @Override - public Statement perform(StagesData stagesData, List input) { + public Node perform(StagesData stagesData, List input) { final Parser parser = new Parser(input); - final Statement program = parser.parse(); + final Node program = parser.parse(); final var parseErrors = parser.getParseErrors(); stagesData.put(TAG_PROGRAM, program); stagesData.put(TAG_HAS_PARSE_ERRORS, parseErrors.hasErrors()); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java index b1ad7792..2d8434a0 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java @@ -32,9 +32,9 @@ public void testParseMultiplicative() { assertEval( number(2), "12 % 5", operator(BinaryExpression.Operator.REMAINDER, value(12), value(5)) ); } - private static void assertEval(Value expectedValue, String input, Expression expected) { + private static void assertEval(Value expectedValue, String input, Node expected) { BlockStatement program = assertExpression(input, expected); - program.execute(); + program.eval(); final Value actual = ScopeHandler.getVariable("a"); try { assertEquals(expectedValue.asNumber(), actual.asNumber(), 0.001); @@ -43,7 +43,7 @@ private static void assertEval(Value expectedValue, String input, Expression exp } } - private static BlockStatement assertExpression(String input, Expression expected) { + private static BlockStatement assertExpression(String input, Node expected) { return assertProgram("a = " + input, block(assign("a", expected))); } @@ -60,7 +60,7 @@ private static void assertStatements(BlockStatement expected, BlockStatement act } } - private static Statement parse(String input) { + private static Node parse(String input) { return Parser.parse(Lexer.tokenize(input)); } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java index cf898312..01a459cf 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsBenchmarkTest.java @@ -1,6 +1,6 @@ package com.annimon.ownlang.parser; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openjdk.jmh.annotations.*; @@ -28,7 +28,7 @@ public class ProgramsBenchmarkTest { @Param({"-"}) private String path; - private Statement program; + private Node program; @Setup(Level.Trial) public void initializeTrial() throws IOException { @@ -41,7 +41,7 @@ public void initializeTrial() throws IOException { @Benchmark public void programBenchmark() { for (int i = 0; i < iterations; i++) { - program.execute(); + program.eval(); } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 951b50a4..8533e2d2 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -6,7 +6,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.parser.optimization.OptimizationStage; @@ -26,7 +26,7 @@ public class ProgramsTest { private static final String RES_DIR = "src/test/resources"; - private static Stage testPipeline; + private static Stage testPipeline; public static Stream data() { return scanDirectory(RES_DIR) diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java index aaf4d3c4..ef65b332 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java @@ -35,19 +35,19 @@ public static BlockStatement block(Statement... statements) { return result; } - public static AssignmentExpression assign(String variable, Expression expr) { + public static AssignmentExpression assign(String variable, Node expr) { return assign(var(variable), expr); } - public static AssignmentExpression assign(Accessible accessible, Expression expr) { + public static AssignmentExpression assign(Accessible accessible, Node expr) { return assign(null, accessible, expr); } - public static AssignmentExpression assign(BinaryExpression.Operator op, Accessible accessible, Expression expr) { + public static AssignmentExpression assign(BinaryExpression.Operator op, Accessible accessible, Node expr) { return new AssignmentExpression(op, accessible, expr); } - public static BinaryExpression operator(BinaryExpression.Operator op, Expression left, Expression right) { + public static BinaryExpression operator(BinaryExpression.Operator op, Node left, Node right) { return new BinaryExpression(op, left, right); } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java index beb4d5f1..8d48a64d 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/VariableExpressionTest.java @@ -21,8 +21,8 @@ void setUp() { @Test public void testVariable() { - assign("a", value(4)).execute(); - assign("b", value("ABCD")).execute(); + assign("a", value(4)).eval(); + assign("b", value("ABCD")).eval(); assertValue(number(4), var("a").eval()); assertValue(string("ABCD"), var("b").eval()); @@ -30,8 +30,8 @@ public void testVariable() { @Test public void testVariableReplace() { - assign("a", value(4)).execute(); - assign("a", value(8)).execute(); + assign("a", value(4)).eval(); + assign("a", value(8)).eval(); assertValue(number(8), var("a").eval()); } diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java index 3f3f9599..9d8aece7 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -7,7 +7,7 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.parser.*; import com.annimon.ownlang.parser.ast.BlockStatement; -import com.annimon.ownlang.parser.ast.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.util.Pos; import com.annimon.ownlang.parser.visitors.PrintVisitor; import com.annimon.ownlang.utils.repl.JLineConsole; @@ -66,7 +66,7 @@ public static void main(String[] args) { } buffer.append(line).append(Console.newline()); - Statement program = null; + Node program = null; try { final List tokens = Lexer.tokenize(buffer.toString()); final Parser parser = new Parser(tokens); @@ -82,7 +82,7 @@ public static void main(String[] args) { continue; } } - program.execute(); + program.eval(); } catch (OwnLangParserException lex) { continue; } catch (StoppedException ex) { diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java index 27f75ed7..b36659de 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -8,6 +8,7 @@ import com.annimon.ownlang.parser.Parser; import com.annimon.ownlang.parser.SourceLoader; import com.annimon.ownlang.parser.Token; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.FunctionAdder; import java.io.File; @@ -31,7 +32,7 @@ public File fileInstance(String path) { final List tokens = Lexer.tokenize(input); final Parser parser = new Parser(tokens); - final Statement program = parser.parse(); + final Node program = parser.parse(); if (parser.getParseErrors().hasErrors()) { System.out.print(parser.getParseErrors()); return; @@ -40,7 +41,7 @@ public File fileInstance(String path) { program.accept(new FunctionAdder()); try { - program.execute(); + program.eval(); } catch (StoppedException ex) { // skip } catch (Exception ex) { From 35971e874b8e8c823d0422a50b256d16bec1c26f Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 15 Oct 2023 22:47:39 +0300 Subject: [PATCH 353/448] Add semantic linter as a required stage --- .../util/input/InputSourceDetector.java | 27 +++++++++ .../main/java/com/annimon/ownlang/Main.java | 18 ++++-- .../java/com/annimon/ownlang/RunOptions.java | 33 +++++------ .../exceptions/BaseParserException.java | 20 ------- .../exceptions/OwnLangParserException.java | 26 ++++----- .../ownlang/exceptions/ParseException.java | 13 +++-- .../ownlang/parser/error/ParseErrors.java | 22 ++++++-- .../error/ParseErrorsFormatterStage.java | 6 +- .../parser/linters/AssignValidator.java | 8 +-- .../DefaultFunctionsOverrideValidator.java | 7 +-- .../linters/IncludeSourceValidator.java | 33 +++++++++++ .../ownlang/parser/linters/LintVisitor.java | 5 +- .../ownlang/parser/linters/LinterResult.java | 35 +++++++++++- .../ownlang/parser/linters/LinterResults.java | 55 +++++++++++++++++++ .../ownlang/parser/linters/LinterStage.java | 34 +++++++++--- .../annimon/ownlang/parser/ProgramsTest.java | 8 ++- 16 files changed, 258 insertions(+), 92 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java new file mode 100644 index 00000000..81178b63 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.util.input; + +import java.nio.file.Files; +import java.nio.file.Path; + +public record InputSourceDetector() { + public static final String RESOURCE_PREFIX = "resource:"; + + public boolean isReadable(String programPath) { + if (programPath.startsWith(RESOURCE_PREFIX)) { + String path = programPath.substring(RESOURCE_PREFIX.length()); + return getClass().getResource(path) != null; + } else { + Path path = Path.of(programPath); + return Files.isReadable(path) && Files.isRegularFile(path); + } + } + + public InputSource toInputSource(String programPath) { + if (programPath.startsWith(RESOURCE_PREFIX)) { + String path = programPath.substring(RESOURCE_PREFIX.length()); + return new InputSourceResource(path); + } else { + return new InputSourceFile(programPath); + } + } +} diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 3255e3d2..78460e13 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -15,6 +15,7 @@ import com.annimon.ownlang.utils.TimeMeasurement; import java.io.IOException; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; /** @@ -72,8 +73,13 @@ public static void main(String[] args) throws IOException { case "-l": case "--lint": - options.lintMode = true; - return; + final String lintMode = i + 1 < args.length ? args[++i] : LinterStage.Mode.SEMANTIC.name(); + options.lintMode = switch (lintMode.toLowerCase(Locale.ROOT)) { + case "none" -> LinterStage.Mode.NONE; + case "full" -> LinterStage.Mode.FULL; + default -> LinterStage.Mode.SEMANTIC; + }; + break; case "-f": case "--file": @@ -112,8 +118,8 @@ private static void printUsage() { options: -f, --file [input] Run program file. Required. -r, --repl Enter to a REPL mode - -l, --lint Find bugs in code - -o N, --optimize N Perform optimization with N (0...9) passes + -l, --lint Find bugs in code. Mode: none, semantic, full + -o, --optimize N Perform optimization with N (0...9) passes -b, --beautify Beautify source code -a, --showast Show AST of program -t, --showtokens Show lexical tokens @@ -145,8 +151,8 @@ private static void run(RunOptions options) { .thenConditional(options.optimizationLevel > 0, scopedStages.create("Optimization", new OptimizationStage(options.optimizationLevel, options.showAst))) - .thenConditional(options.lintMode, - scopedStages.create("Linter", new LinterStage())) + .thenConditional(options.linterEnabled(), + scopedStages.create("Linter", new LinterStage(options.lintMode))) .then(scopedStages.create("Function adding", new FunctionAddingStage())) .then(scopedStages.create("Execution", new ExecutionStage())) .perform(stagesData, options.toInputSource()); diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java index 10ed0c3f..2a81c4e6 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java @@ -1,21 +1,17 @@ package com.annimon.ownlang; -import com.annimon.ownlang.util.input.InputSource; -import com.annimon.ownlang.util.input.InputSourceFile; -import com.annimon.ownlang.util.input.InputSourceProgram; -import com.annimon.ownlang.util.input.InputSourceResource; -import java.nio.file.Files; -import java.nio.file.Path; +import com.annimon.ownlang.parser.linters.LinterStage; +import com.annimon.ownlang.util.input.*; +import static com.annimon.ownlang.util.input.InputSourceDetector.RESOURCE_PREFIX; public class RunOptions { - private static final String RESOURCE_PREFIX = "resource:"; private static final String DEFAULT_PROGRAM = "program.own"; // input String programPath; String programSource; // modes - boolean lintMode; + LinterStage.Mode lintMode = LinterStage.Mode.SEMANTIC; boolean beautifyMode; int optimizationLevel; // flags @@ -23,11 +19,18 @@ public class RunOptions { boolean showAst; boolean showMeasurements; + private final InputSourceDetector inputSourceDetector = new InputSourceDetector(); + + boolean linterEnabled() { + return lintMode != null && lintMode != LinterStage.Mode.NONE; + } + String detectDefaultProgramPath() { - if (getClass().getResource("/" + DEFAULT_PROGRAM) != null) { - return RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM; + final String resourcePath = RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM; + if (inputSourceDetector.isReadable(resourcePath)) { + return resourcePath; } - if (Files.isReadable(Path.of(DEFAULT_PROGRAM))) { + if (inputSourceDetector.isReadable(DEFAULT_PROGRAM)) { return DEFAULT_PROGRAM; } return null; @@ -44,12 +47,6 @@ InputSource toInputSource() { throw new IllegalArgumentException("Empty input"); } } - - if (programPath.startsWith(RESOURCE_PREFIX)) { - String path = programPath.substring(RESOURCE_PREFIX.length()); - return new InputSourceResource(path); - } else { - return new InputSourceFile(programPath); - } + return inputSourceDetector.toInputSource(programPath); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java deleted file mode 100644 index c323a116..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.annimon.ownlang.exceptions; - -import com.annimon.ownlang.util.Range; - -/** - * Base type for all lexer and parser exceptions - */ -public abstract class BaseParserException extends RuntimeException { - - private final Range range; - - public BaseParserException(String message, Range range) { - super(message); - this.range = range; - } - - public Range getRange() { - return range; - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java index 6e3d5324..1fb4cf81 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java @@ -1,27 +1,27 @@ package com.annimon.ownlang.exceptions; -import com.annimon.ownlang.parser.error.ParseError; -import com.annimon.ownlang.parser.error.ParseErrors; +import com.annimon.ownlang.util.SourceLocatedError; +import java.util.Collection; +import java.util.List; /** - * Single Exception for Lexer and Parser errors + * Single Exception for Lexer, Parser and Linter errors */ public class OwnLangParserException extends RuntimeException { - private final ParseErrors parseErrors; + private final Collection errors; - public OwnLangParserException(ParseError parseError) { - super(parseError.toString()); - this.parseErrors = new ParseErrors(); - parseErrors.add(parseError);; + public OwnLangParserException(SourceLocatedError error) { + super(error.toString()); + errors = List.of(error);; } - public OwnLangParserException(ParseErrors parseErrors) { - super(parseErrors.toString()); - this.parseErrors = parseErrors; + public OwnLangParserException(Collection errors) { + super(errors.toString()); + this.errors = errors; } - public ParseErrors getParseErrors() { - return parseErrors; + public Collection getParseErrors() { + return errors; } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index c4adc1d0..4a803e17 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -6,13 +6,16 @@ * * @author aNNiMON */ -public final class ParseException extends BaseParserException { +public final class ParseException extends RuntimeException { - public ParseException(String message) { - super(message, Range.ZERO); - } + private final Range range; public ParseException(String message, Range range) { - super(message, range); + super(message); + this.range = range; + } + + public Range getRange() { + return range; } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java index 76627421..bab8e9df 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java @@ -1,11 +1,12 @@ package com.annimon.ownlang.parser.error; import com.annimon.ownlang.Console; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public final class ParseErrors implements Iterable { +public final class ParseErrors extends AbstractList { private final List errors; @@ -16,11 +17,17 @@ public ParseErrors() { public void clear() { errors.clear(); } - - public void add(ParseError parseError) { - errors.add(parseError); + + @Override + public boolean add(ParseError parseError) { + return errors.add(parseError); } - + + @Override + public ParseError get(int index) { + return errors.get(index); + } + public boolean hasErrors() { return !errors.isEmpty(); } @@ -30,6 +37,11 @@ public Iterator iterator() { return errors.iterator(); } + @Override + public int size() { + return errors.size(); + } + @Override public String toString() { final StringBuilder result = new StringBuilder(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java index 9e9611f0..89444485 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java @@ -4,11 +4,13 @@ import com.annimon.ownlang.stages.StagesData; import com.annimon.ownlang.util.ErrorsLocationFormatterStage; import com.annimon.ownlang.util.ErrorsStackTraceFormatterStage; +import com.annimon.ownlang.util.SourceLocatedError; +import java.util.Collection; -public class ParseErrorsFormatterStage implements Stage { +public class ParseErrorsFormatterStage implements Stage, String> { @Override - public String perform(StagesData stagesData, ParseErrors input) { + public String perform(StagesData stagesData, Collection input) { String error = new ErrorsLocationFormatterStage() .perform(stagesData, input); String stackTrace = new ErrorsStackTraceFormatterStage() diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 70149f10..5fee2527 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -1,8 +1,6 @@ package com.annimon.ownlang.parser.linters; -import com.annimon.ownlang.Console; import com.annimon.ownlang.parser.ast.*; -import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -14,7 +12,7 @@ final class AssignValidator extends LintVisitor { private final Set moduleConstants = new HashSet<>(); - AssignValidator(Collection results) { + AssignValidator(LinterResults results) { super(results); } @@ -24,8 +22,8 @@ public void visit(AssignmentExpression s) { if (s.target instanceof VariableExpression varExpr) { final String variable = varExpr.name; if (moduleConstants.contains(variable)) { - results.add(new LinterResult(LinterResult.Severity.WARNING, - String.format("Variable \"%s\" overrides constant", variable))); + results.add(LinterResult.warning( + "Variable \"%s\" overrides constant".formatted(variable))); } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java index f5cbde0d..12c765c8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.parser.ast.*; -import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,7 +8,7 @@ final class DefaultFunctionsOverrideValidator extends LintVisitor { private final Set moduleFunctions = new HashSet<>(); - DefaultFunctionsOverrideValidator(Collection results) { + DefaultFunctionsOverrideValidator(LinterResults results) { super(results); } @@ -17,8 +16,8 @@ final class DefaultFunctionsOverrideValidator extends LintVisitor { public void visit(FunctionDefineStatement s) { super.visit(s); if (moduleFunctions.contains(s.name)) { - results.add(new LinterResult(LinterResult.Severity.WARNING, - String.format("Function \"%s\" overrides default module function", s.name))); + results.add(LinterResult.warning( + "Function \"%s\" overrides default module function".formatted(s.name))); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java new file mode 100644 index 00000000..91c1a00d --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java @@ -0,0 +1,33 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.parser.ast.IncludeStatement; +import com.annimon.ownlang.parser.ast.ValueExpression; +import com.annimon.ownlang.util.input.InputSourceDetector; + +/** + * + * @author aNNiMON + */ +final class IncludeSourceValidator extends LintVisitor { + private final InputSourceDetector detector; + + IncludeSourceValidator(LinterResults results) { + super(results); + detector = new InputSourceDetector(); + } + + @Override + public void visit(IncludeStatement s) { + super.visit(s); + if (s.expression instanceof ValueExpression expr) { + final String path = expr.eval().asString(); + if (!detector.isReadable(path)) { + results.add(LinterResult.error( + "Include statement path \"%s\" is not readable".formatted(path))); + } + } else { + results.add(LinterResult.warning( + "Include statement path \"%s\" is not a constant string".formatted(s.expression))); + } + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java index 7906fa12..aba3a3a7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java @@ -5,12 +5,11 @@ import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.parser.visitors.VisitorUtils; -import java.util.Collection; abstract class LintVisitor extends AbstractVisitor { - protected final Collection results; + protected final LinterResults results; - LintVisitor(Collection results) { + LintVisitor(LinterResults results) { this.results = results; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java index d02d1e32..54016777 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java @@ -1,9 +1,42 @@ package com.annimon.ownlang.parser.linters; -record LinterResult(Severity severity, String message) { +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocatedError; + +record LinterResult(Severity severity, String message, Range range) implements SourceLocatedError { enum Severity { ERROR, WARNING } + static LinterResult warning(String message) { + return new LinterResult(Severity.WARNING, message); + } + + static LinterResult error(String message) { + return new LinterResult(Severity.ERROR, message); + } + + LinterResult(Severity severity, String message) { + this(severity, message, null); + } + + public boolean isError() { + return severity == Severity.ERROR; + } + + public boolean isWarning() { + return severity == Severity.WARNING; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Range getRange() { + return range; + } + @Override public String toString() { return severity.name() + ": " + message; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java new file mode 100644 index 00000000..af9b08dc --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.parser.linters; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +class LinterResults extends AbstractList { + private final List results; + + LinterResults() { + this(new ArrayList<>()); + } + + LinterResults(List results) { + this.results = results; + } + + @Override + public boolean add(LinterResult result) { + return results.add(result); + } + + @Override + public LinterResult get(int index) { + return results.get(index); + } + + @Override + public Iterator iterator() { + return results.iterator(); + } + + @Override + public int size() { + return results.size(); + } + + public boolean hasErrors() { + return results.stream().anyMatch(LinterResult::isError); + } + + public Stream errors() { + return results.stream().filter(LinterResult::isError); + } + + public boolean hasWarnings() { + return results.stream().anyMatch(LinterResult::isWarning); + } + + public Stream warnings() { + return results.stream().filter(LinterResult::isWarning); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java index 70235308..b815778c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; @@ -11,23 +12,42 @@ import java.util.List; public class LinterStage implements Stage { + public enum Mode { NONE, SEMANTIC, FULL } + + private final Mode mode; + + public LinterStage(Mode mode) { + this.mode = mode; + } @Override public Node perform(StagesData stagesData, Node input) { - final List results = new ArrayList<>(); - final Visitor[] validators = new Visitor[] { - new AssignValidator(results), - new DefaultFunctionsOverrideValidator(results) - }; + if (mode == Mode.NONE) return input; + + final LinterResults results = new LinterResults(); + final List validators = new ArrayList<>(); + validators.add(new IncludeSourceValidator(results)); + + if (mode == Mode.SEMANTIC) { + validators.forEach(input::accept); + if (results.hasErrors()) { + throw new OwnLangParserException(results.errors().toList()); + } + return input; + } + + // Full lint validation with Console output + validators.add(new AssignValidator(results)); + validators.add(new DefaultFunctionsOverrideValidator(results)); - ScopeHandler.resetScope(); + ScopeHandler.resetScope(); // TODO special linter scope? for (Visitor validator : validators) { input.accept(validator); ScopeHandler.resetScope(); } results.sort(Comparator.comparing(LinterResult::severity)); - Console.println(String.format("Lint validation completed. %d results found!", results.size())); + Console.println("Lint validation completed. %d results found!".formatted(results.size())); for (LinterResult r : results) { switch (r.severity()) { case ERROR -> Console.error(r.toString()); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 8533e2d2..af113afe 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -9,6 +9,7 @@ import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; +import com.annimon.ownlang.parser.linters.LinterStage; import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.stages.*; @@ -16,7 +17,6 @@ import com.annimon.ownlang.util.input.InputSourceFile; import com.annimon.ownlang.util.input.SourceLoaderStage; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.io.File; @@ -39,7 +39,9 @@ public static void createStage() { testPipeline = new SourceLoaderStage() .then(new LexerStage()) .then(new ParserStage()) + .then(new LinterStage(LinterStage.Mode.SEMANTIC)) .thenConditional(true, new OptimizationStage(9)) + .then(ProgramsTest::mockOUnit) .then(new ExecutionStage()) .then((stagesData, input) -> { input.accept(testFunctionsExecutor); @@ -47,8 +49,7 @@ public static void createStage() { }); } - @BeforeEach - public void initialize() { + private static Node mockOUnit(StagesData stagesData, Node input) { ScopeHandler.resetScope(); // Let's mock junit methods as ounit functions ScopeHandler.setFunction("assertEquals", (args) -> { @@ -84,6 +85,7 @@ public void initialize() { } return NumberValue.ONE; }); + return input; } @ParameterizedTest From 6a35f6e66a42c6d2a8a24a05e9c50712366e2f36 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 15 Oct 2023 22:57:27 +0300 Subject: [PATCH 354/448] Source located error in linter for include statement --- .../java/com/annimon/ownlang/parser/Parser.java | 9 ++++++++- .../ownlang/parser/ast/IncludeStatement.java | 15 ++++++++++++++- .../parser/linters/IncludeSourceValidator.java | 6 ++++-- .../ownlang/parser/linters/LinterResult.java | 8 ++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 555e3ce8..0c2ca9ea 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -147,7 +147,7 @@ private Statement statement() { return useStatement(); } if (match(TokenType.INCLUDE)) { - return new IncludeStatement(expression()); + return includeStatement(); } if (match(TokenType.FOR)) { return forStatement(); @@ -167,6 +167,13 @@ private Statement statement() { return assignmentStatement(); } + private IncludeStatement includeStatement() { + final var startTokenIndex = index - 1; + final var include = new IncludeStatement(expression()); + include.setRange(getRange(startTokenIndex, index)); + return include; + } + private UseStatement useStatement() { final var modules = new HashSet(); do { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java index 0262a6f8..45a95b3e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -6,6 +6,8 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; import com.annimon.ownlang.stages.*; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; import com.annimon.ownlang.util.input.InputSourceFile; import com.annimon.ownlang.util.input.SourceLoaderStage; @@ -13,14 +15,24 @@ * * @author aNNiMON */ -public final class IncludeStatement extends InterruptableNode implements Statement { +public final class IncludeStatement extends InterruptableNode implements Statement, SourceLocation { public final Node expression; + private Range range; public IncludeStatement(Node expression) { this.expression = expression; } + public void setRange(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } + @Override public Value eval() { super.interruptionCheck(); @@ -31,6 +43,7 @@ public Value eval() { new SourceLoaderStage() .then(new LexerStage()) .then(new ParserStage()) + // TODO LinterStage based on main context .then(new FunctionAddingStage()) .then(new ExecutionStage()) .perform(stagesData, new InputSourceFile(path)); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java index 91c1a00d..c02995bf 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java @@ -23,11 +23,13 @@ public void visit(IncludeStatement s) { final String path = expr.eval().asString(); if (!detector.isReadable(path)) { results.add(LinterResult.error( - "Include statement path \"%s\" is not readable".formatted(path))); + "Include statement path \"%s\" is not readable".formatted(path), + s.getRange())); } } else { results.add(LinterResult.warning( - "Include statement path \"%s\" is not a constant string".formatted(s.expression))); + "Include statement path \"%s\" is not a constant string".formatted(s.expression), + s.getRange())); } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java index 54016777..f555456c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java @@ -11,10 +11,18 @@ static LinterResult warning(String message) { return new LinterResult(Severity.WARNING, message); } + static LinterResult warning(String message, Range range) { + return new LinterResult(Severity.WARNING, message, range); + } + static LinterResult error(String message) { return new LinterResult(Severity.ERROR, message); } + static LinterResult error(String message, Range range) { + return new LinterResult(Severity.ERROR, message, range); + } + LinterResult(Severity severity, String message) { this(severity, message, null); } From bcce9783415dfff5673cc2bfbbaae1d872a39029 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 15 Oct 2023 23:09:36 +0300 Subject: [PATCH 355/448] Source located VariableDoesNotExistsException --- .../VariableDoesNotExistsException.java | 6 ++++-- .../java/com/annimon/ownlang/parser/Parser.java | 5 ++++- .../ownlang/parser/ast/VariableExpression.java | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java index 419807e4..8ed000d9 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java @@ -1,11 +1,13 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.util.Range; + public final class VariableDoesNotExistsException extends OwnLangRuntimeException { private final String variable; - public VariableDoesNotExistsException(String variable) { - super("Variable " + variable + " does not exists"); + public VariableDoesNotExistsException(String variable, Range range) { + super("Variable " + variable + " does not exists", range); this.variable = variable; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 0c2ca9ea..278dee5e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -853,12 +853,15 @@ private Node variable() { private Node qualifiedName() { // var || var.key[index].key2 + final var startTokenIndex = index; final Token current = get(0); if (!match(TokenType.WORD)) return null; final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { - return new VariableExpression(current.text()); + final var variable = new VariableExpression(current.text()); + variable.setRange(getRange(startTokenIndex, index - 1)); + return variable; } return new ContainerAccessExpression(current.text(), indices); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java index b997f040..fa04ab71 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -3,19 +3,31 @@ import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class VariableExpression extends InterruptableNode implements Accessible { +public final class VariableExpression extends InterruptableNode implements Accessible, SourceLocation { public final String name; + private Range range; public VariableExpression(String name) { this.name = name; } + public void setRange(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } + @Override public Value eval() { super.interruptionCheck(); @@ -25,7 +37,7 @@ public Value eval() { @Override public Value get() { if (!ScopeHandler.isVariableOrConstantExists(name)) { - throw new VariableDoesNotExistsException(name); + throw new VariableDoesNotExistsException(name, getRange()); } return ScopeHandler.getVariableOrConstant(name); } From d9ae2c7e823ab891f42629c8bb0b48ca705d21f4 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 15 Oct 2023 23:17:52 +0300 Subject: [PATCH 356/448] Don't include canvasfx module to desktop app by default --- ownlang-desktop/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index 89176bb8..04c30364 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -17,7 +17,6 @@ dependencies { implementation project(":ownlang-parser") implementation project(":ownlang-utils") implementation project(":modules:main") - implementation project(":modules:canvasfx") testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' From ec04e5faca74313754e3ad1fffe33bbb76e85d13 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 23 Oct 2023 22:08:22 +0300 Subject: [PATCH 357/448] Move classes implementation outside of parser module --- .../exceptions/UnknownClassException.java | 5 -- .../annimon/ownlang/lib/ClassDeclaration.java | 24 ++++++++ .../com/annimon/ownlang/lib/ClassField.java | 7 +++ .../annimon/ownlang/lib/ClassInstance.java | 49 ++++++++------- .../com/annimon/ownlang/lib/ClassMethod.java | 60 +++++++++++++++++++ .../annimon/ownlang/lib/EvaluableValue.java | 6 ++ .../com/annimon/ownlang/lib/RootScope.java | 14 +++++ .../com/annimon/ownlang/lib/ScopeHandler.java | 14 +++++ .../ownlang/lib/ClassDeclarations.java | 31 ---------- .../com/annimon/ownlang/lib/ClassMethod.java | 27 --------- .../java/com/annimon/ownlang/lib/Classes.java | 36 ----------- .../com/annimon/ownlang/parser/Parser.java | 3 +- .../parser/ast/AssignmentExpression.java | 3 +- .../parser/ast/ClassDeclarationStatement.java | 24 ++++++-- .../parser/ast/ContainerAccessExpression.java | 4 +- .../parser/ast/FunctionDefineStatement.java | 4 +- .../parser/ast/ObjectCreationExpression.java | 48 ++++++--------- .../optimization/OptimizationVisitor.java | 1 + 18 files changed, 199 insertions(+), 161 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassDeclaration.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassField.java rename ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java => ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java (65%) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassMethod.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/EvaluableValue.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/lib/Classes.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java index 8c2bed3b..d963c25a 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java @@ -6,11 +6,6 @@ public final class UnknownClassException extends OwnLangRuntimeException { private final String className; - public UnknownClassException(String name) { - super("Unknown class " + name); - this.className = name; - } - public UnknownClassException(String name, Range range) { super("Unknown class " + name, range); this.className = name; diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassDeclaration.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassDeclaration.java new file mode 100644 index 00000000..6f0e50e5 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassDeclaration.java @@ -0,0 +1,24 @@ +package com.annimon.ownlang.lib; + +import java.util.List; + +public record ClassDeclaration( + String name, + List classFields, + List classMethods) implements Instantiable { + + /** + * Create an instance and put evaluated fields with method declarations + * @return new {@link ClassInstance} + */ + public ClassInstance newInstance(Value[] args) { + final var instance = new ClassInstance(name); + for (ClassField f : classFields) { + instance.addField(f); + } + for (ClassMethod m : classMethods) { + instance.addMethod(m); + } + return instance.callConstructor(args); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassField.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassField.java new file mode 100644 index 00000000..469c6b03 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassField.java @@ -0,0 +1,7 @@ +package com.annimon.ownlang.lib; + +public record ClassField( + String name, + EvaluableValue evaluableValue +) { +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java similarity index 65% rename from ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java index 3def100e..75c12923 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java @@ -1,16 +1,18 @@ package com.annimon.ownlang.lib; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.exceptions.TypeException; import java.util.Objects; -public class ClassInstanceValue implements Value { - +public class ClassInstance implements Value { + private final String className; private final MapValue thisMap; private ClassMethod constructor; - private UserDefinedFunction toString; + private ClassMethod toString; + private boolean isInstantiated; - public ClassInstanceValue(String name) { + public ClassInstance(String name) { this.className = name; thisMap = new MapValue(10); } @@ -19,31 +21,33 @@ public MapValue getThisMap() { return thisMap; } - public String getClassName() { - return className; - } - - public void addField(String name, Value value) { - thisMap.set(name, value); + public void addField(ClassField f) { + thisMap.set(f.name(), f.evaluableValue().eval()); } - public void addMethod(String name, ClassMethod method) { - if (name.equals("toString")) { - toString = method; - } + public void addMethod(ClassMethod method) { + method.setClassInstance(this); + final String name = method.getName(); thisMap.set(name, method); if (name.equals(className)) { constructor = method; + } else if (name.equals("toString")) { + toString = method; } } - - public void callConstructor(Value[] args) { + public ClassInstance callConstructor(Value[] args) { + if (isInstantiated) { + throw new OwnLangRuntimeException( + "Class %s was already instantiated".formatted(className)); + } if (constructor != null) { CallStack.enter("class " + className, constructor, null); constructor.execute(args); CallStack.exit(); } + isInstantiated = true; + return this; } public Value access(Value value) { @@ -53,15 +57,16 @@ public Value access(Value value) { public void set(Value key, Value value) { final Value v = thisMap.get(key); if (v == null) { - throw new RuntimeException("Unable to add new field " - + key.asString() + " to class " + className); + throw new OwnLangRuntimeException( + "Unable to add new field %s to class %s" + .formatted(key.asString(), className)); } thisMap.set(key, value); } @Override public Object raw() { - return null; + return thisMap; } @Override @@ -77,9 +82,9 @@ public double asNumber() { @Override public String asString() { if (toString != null) { - return toString.execute(new Value[0]).asString(); + return toString.execute().asString(); } - return className + "@" + thisMap; + return className + "@" + thisMap.asString(); } @Override @@ -100,7 +105,7 @@ public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; - final ClassInstanceValue other = (ClassInstanceValue) obj; + final ClassInstance other = (ClassInstance) obj; return Objects.equals(this.className, other.className) && Objects.equals(this.thisMap, other.thisMap); } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassMethod.java new file mode 100644 index 00000000..643b7002 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassMethod.java @@ -0,0 +1,60 @@ +package com.annimon.ownlang.lib; + +import java.util.Objects; + +public class ClassMethod implements Function { + private final String name; + private final Function function; + private ClassInstance classInstance; + + public ClassMethod(String name, Function function) { + this.name = name; + this.function = function; + } + + public String getName() { + return name; + } + + public void setClassInstance(ClassInstance classInstance) { + this.classInstance = classInstance; + } + + @Override + public Value execute(Value... args) { + ScopeHandler.push(); + ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap()); + + try { + return function.execute(args); + } finally { + ScopeHandler.pop(); + } + } + + @Override + public int getArgsCount() { + return function.getArgsCount(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (ClassMethod) obj; + return Objects.equals(this.name, that.name) && + Objects.equals(this.function, that.function); + } + + @Override + public int hashCode() { + return Objects.hash(name, function); + } + + @Override + public String toString() { + return "ClassMethod[" + + "name=" + name + ", " + + "function=" + function + ']'; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/EvaluableValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/EvaluableValue.java new file mode 100644 index 00000000..bc90ba0d --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/EvaluableValue.java @@ -0,0 +1,6 @@ +package com.annimon.ownlang.lib; + +public interface EvaluableValue { + + Value eval(); +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java index 0cdcd816..19682bf8 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java @@ -8,11 +8,13 @@ final class RootScope extends Scope { private final Map constants; private final Map functions; + private final Map classDeclarations; private final Set loadedModules; RootScope() { functions = new ConcurrentHashMap<>(); constants = new ConcurrentHashMap<>(); + classDeclarations = new ConcurrentHashMap<>(); constants.put("true", NumberValue.ONE); constants.put("false", NumberValue.ZERO); loadedModules = new CopyOnWriteArraySet<>(); @@ -71,6 +73,18 @@ public Map getFunctions() { } + public ClassDeclaration getClassDeclaration(String name) { + return classDeclarations.get(name); + } + + public void setClassDeclaration(ClassDeclaration classDeclaration) { + classDeclarations.put(classDeclaration.name(), classDeclaration); + } + + public Map getClassDeclarations() { + return classDeclarations; + } + public Set getLoadedModules() { return loadedModules; } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java index a61d6caa..f82af080 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -28,6 +28,11 @@ public static Map functions() { return rootScope.getFunctions(); } + public static Map classDeclarations() { + return rootScope.getClassDeclarations(); + } + + static RootScope rootScope() { return rootScope; } @@ -75,6 +80,15 @@ public static void setFunction(String name, Function function) { } + public static ClassDeclaration getClassDeclaration(String name) { + return rootScope.getClassDeclaration(name); + } + + public static void setClassDeclaration(ClassDeclaration classDeclaration) { + rootScope.setClassDeclaration(classDeclaration); + } + + public static boolean isVariableOrConstantExists(String name) { if (rootScope().containsConstant(name)) { return true; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java deleted file mode 100644 index b079ef3d..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.annimon.ownlang.lib; - -import com.annimon.ownlang.parser.ast.ClassDeclarationStatement; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public final class ClassDeclarations { - - private static final Map declarations; - static { - declarations = new ConcurrentHashMap<>(); - } - - private ClassDeclarations() { } - - public static void clear() { - declarations.clear(); - } - - public static Map getAll() { - return declarations; - } - - public static ClassDeclarationStatement get(String key) { - return declarations.get(key); - } - - public static void set(String key, ClassDeclarationStatement classDef) { - declarations.put(key, classDef); - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java deleted file mode 100644 index 5ad723e0..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.annimon.ownlang.lib; - -import com.annimon.ownlang.parser.ast.Arguments; -import com.annimon.ownlang.parser.ast.Statement; -import com.annimon.ownlang.util.Range; - -public class ClassMethod extends UserDefinedFunction { - - public final ClassInstanceValue classInstance; - - public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance, Range range) { - super(arguments, body, range); - this.classInstance = classInstance; - } - - @Override - public Value execute(Value[] values) { - ScopeHandler.push(); - ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap()); - - try { - return super.execute(values); - } finally { - ScopeHandler.pop(); - } - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/Classes.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/Classes.java deleted file mode 100644 index 488d5466..00000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/Classes.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.annimon.ownlang.lib; - -import com.annimon.ownlang.exceptions.UnknownClassException; -import java.util.HashMap; -import java.util.Map; - -public final class Classes { - - private static final Map classes; - static { - classes = new HashMap<>(); - } - - private Classes() { } - - public static void clear() { - classes.clear(); - } - - public static Map getFunctions() { - return classes; - } - - public static boolean isExists(String key) { - return classes.containsKey(key); - } - - public static ClassInstanceValue get(String key) { - if (!isExists(key)) throw new UnknownClassException(key); - return classes.get(key); - } - - public static void set(String key, ClassInstanceValue classDef) { - classes.put(key, classDef); - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 278dee5e..1c8417f9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -815,7 +815,8 @@ private Node primary() { final var startTokenIndex = index - 1; final Arguments arguments = arguments(); final Statement statement = statementBody(); - return new ValueExpression(new UserDefinedFunction(arguments, statement, getRange(startTokenIndex, index - 1))); + final Range range = getRange(startTokenIndex, index - 1); + return new ValueExpression(new UserDefinedFunction(arguments, statement, range)); } return variable(); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java index a6982842..e76c0afb 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java @@ -1,12 +1,13 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.EvaluableValue; import com.annimon.ownlang.lib.Value; /** * * @author aNNiMON */ -public final class AssignmentExpression extends InterruptableNode implements Statement { +public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue { public final Accessible target; public final BinaryExpression.Operator operation; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java index b8f3e94e..482cfba8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ClassDeclarationStatement.java @@ -1,8 +1,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.ClassDeclarations; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.List; @@ -28,9 +26,27 @@ public void addMethod(FunctionDefineStatement statement) { @Override public Value eval() { - ClassDeclarations.set(name, this); + final var classFields = fields.stream() + .map(this::toClassField) + .toList(); + final var classMethods = methods.stream() + .map(this::toClassMethod) + .toList(); + final var declaration = new ClassDeclaration(name, classFields, classMethods); + ScopeHandler.setClassDeclaration(declaration); return NumberValue.ZERO; } + + private ClassField toClassField(AssignmentExpression f) { + // TODO check only variable assignments + final String fieldName = ((VariableExpression) f.target).name; + return new ClassField(fieldName, f); + } + + private ClassMethod toClassMethod(FunctionDefineStatement m) { + final var function = new UserDefinedFunction(m.arguments, m.body, m.getRange()); + return new ClassMethod(m.name, function); + } @Override public void accept(Visitor visitor) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index b60c19c6..09b3f993 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -50,7 +50,7 @@ public Value get() { case Types.ARRAY -> ((ArrayValue) container).get(lastIndex); case Types.MAP -> ((MapValue) container).get(lastIndex); case Types.STRING -> ((StringValue) container).access(lastIndex); - case Types.CLASS -> ((ClassInstanceValue) container).access(lastIndex); + case Types.CLASS -> ((ClassInstance) container).access(lastIndex); default -> throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); }; } @@ -62,7 +62,7 @@ public Value set(Value value) { switch (container.type()) { case Types.ARRAY -> ((ArrayValue) container).set(lastIndex.asInt(), value); case Types.MAP -> ((MapValue) container).set(lastIndex, value); - case Types.CLASS -> ((ClassInstanceValue) container).set(lastIndex, value); + case Types.CLASS -> ((ClassInstance) container).set(lastIndex, value); default -> throw new TypeException("Array or map expected. Got " + container.type()); } return value; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java index f8127560..08768022 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -48,8 +48,8 @@ public R accept(ResultVisitor visitor, T t) { @Override public String toString() { - if (body instanceof ReturnStatement) { - return String.format("def %s%s = %s", name, arguments, ((ReturnStatement)body).expression); + if (body instanceof ReturnStatement rs) { + return String.format("def %s%s = %s", name, arguments, rs.expression); } return String.format("def %s%s %s", name, arguments, body); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index 5204f3da..a19bbf6b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -29,41 +29,29 @@ public Range getRange() { @Override public Value eval() { - final ClassDeclarationStatement cd = ClassDeclarations.get(className); - if (cd == null) { - // Is Instantiable? - if (ScopeHandler.isVariableOrConstantExists(className)) { - final Value variable = ScopeHandler.getVariableOrConstant(className); - if (variable instanceof Instantiable instantiable) { - return instantiable.newInstance(ctorArgs()); - } - } - throw new UnknownClassException(className, range); - } - - // Create an instance and put evaluated fields with method declarations - final ClassInstanceValue instance = new ClassInstanceValue(className); - for (AssignmentExpression f : cd.fields) { - // TODO check only variable assignments - final String fieldName = ((VariableExpression) f.target).name; - instance.addField(fieldName, f.eval()); + final ClassDeclaration cd = ScopeHandler.getClassDeclaration(className); + if (cd != null) { + return cd.newInstance(constructorArgs()); } - for (FunctionDefineStatement m : cd.methods) { - instance.addMethod(m.name, new ClassMethod(m.arguments, m.body, instance, m.getRange())); + + // Is Instantiable? + if (ScopeHandler.isVariableOrConstantExists(className)) { + final Value variable = ScopeHandler.getVariableOrConstant(className); + if (variable instanceof Instantiable instantiable) { + return instantiable.newInstance(constructorArgs()); + } } - - // Call a constructor - instance.callConstructor(ctorArgs()); - return instance; + throw new UnknownClassException(className, range); } - - private Value[] ctorArgs() { + + private Value[] constructorArgs() { final int argsSize = constructorArguments.size(); - final Value[] ctorArgs = new Value[argsSize]; - for (int i = 0; i < argsSize; i++) { - ctorArgs[i] = constructorArguments.get(i).eval(); + final Value[] args = new Value[argsSize]; + int i = 0; + for (Node argument : constructorArguments) { + args[i++] = argument.eval(); } - return ctorArgs; + return args; } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 4a42c701..fee18314 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -75,6 +75,7 @@ public Node visit(BreakStatement s, T t) { @Override public Node visit(ClassDeclarationStatement s, T t) { + // TODO fields and methods return s; } From cef845f0f8933f101ca4144675c4de10b1223393 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Tue, 24 Oct 2023 19:35:40 +0300 Subject: [PATCH 358/448] Add lint validation for break/continue statement outside the loop body --- .../util/ErrorsLocationFormatterStage.java | 5 +- .../com/annimon/ownlang/parser/Parser.java | 4 +- .../ownlang/parser/ast/BreakStatement.java | 14 +++- .../ownlang/parser/ast/ContinueStatement.java | 14 +++- .../parser/linters/AssignValidator.java | 6 +- .../DefaultFunctionsOverrideValidator.java | 12 +-- .../ownlang/parser/linters/LinterStage.java | 1 + .../linters/LoopStatementsValidator.java | 76 +++++++++++++++++++ .../parser/visitors/AbstractVisitor.java | 8 +- .../parser/visitors/ModuleDetector.java | 6 +- 10 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java index 22d5c98c..1ddc74e7 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java @@ -17,8 +17,11 @@ public String perform(StagesData stagesData, Iterable 0) { var positions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new); positions.add(range); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 1c8417f9..cf8522f1 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -135,10 +135,10 @@ private Statement statement() { return doWhileStatement(); } if (match(TokenType.BREAK)) { - return new BreakStatement(); + return new BreakStatement(getRange(index - 1, index - 1)); } if (match(TokenType.CONTINUE)) { - return new ContinueStatement(); + return new ContinueStatement(getRange(index - 1, index - 1)); } if (match(TokenType.RETURN)) { return new ReturnStatement(expression()); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java index 925a7c99..7813855c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java @@ -1,12 +1,24 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class BreakStatement extends RuntimeException implements Statement { +public final class BreakStatement extends RuntimeException implements Statement, SourceLocation { + private final Range range; + + public BreakStatement(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } @Override public Value eval() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java index d8ea6310..311c97a3 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java @@ -1,12 +1,24 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class ContinueStatement extends RuntimeException implements Statement { +public final class ContinueStatement extends RuntimeException implements Statement, SourceLocation { + private final Range range; + + public ContinueStatement(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } @Override public Value eval() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 5fee2527..d5727f2b 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -35,8 +35,8 @@ public void visit(IncludeStatement st) { } @Override - public void visit(UseStatement st) { - super.visit(st); - moduleConstants.addAll(st.loadConstants().keySet()); + public void visit(UseStatement s) { + super.visit(s); + moduleConstants.addAll(s.loadConstants().keySet()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java index 12c765c8..129ea427 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -22,14 +22,14 @@ public void visit(FunctionDefineStatement s) { } @Override - public void visit(IncludeStatement st) { - super.visit(st); - applyVisitor(st, this); + public void visit(IncludeStatement s) { + super.visit(s); + applyVisitor(s, this); } @Override - public void visit(UseStatement st) { - super.visit(st); - moduleFunctions.addAll(st.loadFunctions().keySet()); + public void visit(UseStatement s) { + super.visit(s); + moduleFunctions.addAll(s.loadFunctions().keySet()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java index b815778c..61044c6a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -27,6 +27,7 @@ public Node perform(StagesData stagesData, Node input) { final LinterResults results = new LinterResults(); final List validators = new ArrayList<>(); validators.add(new IncludeSourceValidator(results)); + validators.add(new LoopStatementsValidator(results)); if (mode == Mode.SEMANTIC) { validators.forEach(input::accept); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java new file mode 100644 index 00000000..f464f371 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java @@ -0,0 +1,76 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.parser.ast.*; +import java.util.ArrayDeque; +import java.util.Deque; + +final class LoopStatementsValidator extends LintVisitor { + private enum LoopScope { FOR, WHILE, DO_WHILE, FOREACH_ARR, FOREACH_MAP }; + + private final Deque loopScope; + + LoopStatementsValidator(LinterResults results) { + super(results); + loopScope = new ArrayDeque<>(10); + } + + @Override + public void visit(ForStatement s) { + s.initialization.accept(this); + s.termination.accept(this); + s.increment.accept(this); + loopScope.push(LoopScope.FOR); + s.statement.accept(this); + loopScope.remove(); + } + + @Override + public void visit(DoWhileStatement s) { + s.condition.accept(this); + loopScope.push(LoopScope.DO_WHILE); + s.statement.accept(this); + loopScope.remove(); + } + + @Override + public void visit(ForeachArrayStatement s) { + s.container.accept(this); + loopScope.push(LoopScope.FOREACH_ARR); + s.body.accept(this); + loopScope.remove(); + } + + @Override + public void visit(ForeachMapStatement s) { + s.container.accept(this); + loopScope.push(LoopScope.FOREACH_MAP); + s.body.accept(this); + loopScope.remove(); + } + + @Override + public void visit(WhileStatement s) { + s.condition.accept(this); + loopScope.push(LoopScope.WHILE); + s.statement.accept(this); + loopScope.remove(); + } + + @Override + public void visit(BreakStatement s) { + if (loopScope.isEmpty()) { + results.add(LinterResult.error( + "break statement shouldn't be placed outside the loop body", + s.getRange())); + } + } + + @Override + public void visit(ContinueStatement s) { + if (loopScope.isEmpty()) { + results.add(LinterResult.error( + "continue statement shouldn't be placed outside the loop body", + s.getRange())); + } + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index f2bac0d8..c352b373 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -185,13 +185,13 @@ public void visit(VariableExpression s) { } @Override - public void visit(WhileStatement st) { - st.condition.accept(this); - st.statement.accept(this); + public void visit(WhileStatement s) { + s.condition.accept(this); + s.statement.accept(this); } @Override - public void visit(UseStatement st) { + public void visit(UseStatement s) { } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java index d83020f8..7b8e3dfa 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -19,8 +19,8 @@ public Set detect(Node s) { } @Override - public void visit(UseStatement st) { - modules.addAll(st.modules); - super.visit(st); + public void visit(UseStatement s) { + modules.addAll(s.modules); + super.visit(s); } } From 7a64d7f99dc26c8ee88e6332055e3a458f23e03b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Tue, 24 Oct 2023 19:48:46 +0300 Subject: [PATCH 359/448] Fix visitors skip class declaration --- .../optimization/OptimizationVisitor.java | 28 ++++++++++++++++++- .../parser/visitors/AbstractVisitor.java | 7 ++++- .../parser/visitors/FunctionAdder.java | 5 ++++ .../annimon/ownlang/parser/ProgramsTest.java | 6 ++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index fee18314..0f15305c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -75,7 +75,33 @@ public Node visit(BreakStatement s, T t) { @Override public Node visit(ClassDeclarationStatement s, T t) { - // TODO fields and methods + final var newClassDeclaration = new ClassDeclarationStatement(s.name); + boolean changed = false; + for (AssignmentExpression field : s.fields) { + final Node fieldExpr = field.expression.accept(this, t); + final AssignmentExpression newField; + if (fieldExpr != field.expression) { + changed = true; + newField = new AssignmentExpression(field.operation, field.target, fieldExpr); + } else { + newField = field; + } + newClassDeclaration.addField(newField); + } + + for (FunctionDefineStatement method : s.methods) { + final var newMethod = method.accept(this, t); + if (newMethod != method) { + changed = true; + newClassDeclaration.addMethod((FunctionDefineStatement) newMethod); + } else { + newClassDeclaration.addMethod(method); + } + } + + if (changed) { + return newClassDeclaration; + } return s; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index c352b373..5bc53ea7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -41,7 +41,12 @@ public void visit(BreakStatement s) { @Override public void visit(ClassDeclarationStatement s) { - + for (Node field : s.fields) { + field.accept(this); + } + for (Node method : s.methods) { + method.accept(this); + } } @Override diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java index 2f905150..7f03cb2e 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java @@ -13,4 +13,9 @@ public void visit(FunctionDefineStatement s) { super.visit(s); s.eval(); } + + @Override + public void visit(ClassDeclarationStatement s) { + // skip, otherwise class methods will be visible outside of class + } } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index af113afe..d08787d4 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -5,6 +5,7 @@ import com.annimon.ownlang.lib.FunctionValue; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.ScopeHandler; +import com.annimon.ownlang.parser.ast.ClassDeclarationStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; @@ -115,5 +116,10 @@ public void visit(FunctionDefineStatement s) { } } } + + @Override + public void visit(ClassDeclarationStatement s) { + + } }; } From c3a893ea25410b835315b9a553015971f4e0648b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 25 Oct 2023 21:36:56 +0300 Subject: [PATCH 360/448] [functional] Add groupby and Stream.groupBy --- .../modules/functional/StreamValue.java | 1 + .../modules/functional/functional.java | 1 + .../functional/functional_groupBy.java | 67 +++++++++++++++++++ .../resources/modules/functional/groupby.own | 15 +++++ .../resources/modules/functional/stream.own | 32 ++++++--- 5 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java create mode 100644 ownlang-parser/src/test/resources/modules/functional/groupby.own diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java index e2463035..ff26031e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -31,6 +31,7 @@ private void init() { set("reduce", wrapTerminal(new functional_reduce())); set("forEach", wrapTerminal(new functional_forEach())); set("forEachIndexed", wrapTerminal(new functional_forEachIndexed())); + set("groupBy", wrapTerminal(new functional_groupBy())); set("toArray", args -> container); set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index 22c7ffad..711b170f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -27,6 +27,7 @@ public Map functions() { result.put("sortby", new functional_sortBy()); result.put("takewhile", new functional_takeWhile()); result.put("dropwhile", new functional_dropWhile()); + result.put("groupby", new functional_groupBy()); result.put("chain", new functional_chain()); result.put("stream", new functional_stream()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java new file mode 100644 index 00000000..8b8c484a --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java @@ -0,0 +1,67 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class functional_groupBy implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + + final Value container = args[0]; + final Function classifier = ValueUtils.consumeFunction(args[1], 1); + return groupBy(container, classifier); + } + + static Value groupBy(Value container, Function classifier) { + if (container.type() == Types.ARRAY) { + return groupByArray((ArrayValue) container, classifier); + } + if (container.type() == Types.MAP) { + return groupByMap((MapValue) container, classifier); + } + throw new TypeException("Invalid first argument. Array or map expected"); + } + + @SuppressWarnings("Java8MapApi") + static Value groupByArray(ArrayValue array, Function classifier) { + final var result = new LinkedHashMap>(); + for (Value element : array) { + final var key = classifier.execute(element); + var container = result.get(key); + if (container == null) { + container = new ArrayList<>(); + result.put(key, container); + } + container.add(element); + } + return fromMapOfArrays(result); + } + + static Value groupByMap(MapValue map, Function classifier) { + final var result = new LinkedHashMap(); + for (Map.Entry element : map) { + final var k = element.getKey(); + final var v = element.getValue(); + final var key = classifier.execute(k, v); + var container = (MapValue) result.get(key); + if (container == null) { + container = new MapValue(10); + result.put(key, container); + } + container.set(k, v); + } + return new MapValue(result); + } + + private static MapValue fromMapOfArrays(Map> map) { + final var result = new LinkedHashMap(); + map.forEach((key, value) -> result.put(key, new ArrayValue(value))); + return new MapValue(result); + } +} diff --git a/ownlang-parser/src/test/resources/modules/functional/groupby.own b/ownlang-parser/src/test/resources/modules/functional/groupby.own new file mode 100644 index 00000000..49a3237a --- /dev/null +++ b/ownlang-parser/src/test/resources/modules/functional/groupby.own @@ -0,0 +1,15 @@ +use std, functional + +def testArraysGroupBy() { + arr = [1, 2, 3, 4, 1, 2, 3, 1, 2, 3] + result = groupby(arr, def(v) = v % 2 == 0) + assertEquals([2, 4, 2, 2], result[true]) + assertEquals([1, 3, 1, 3, 1, 3], result[false]) +} + +def testMapsGroupBy() { + map = {"abc": 123, "test1": 234, "test2": 345, "test3": 456, "def": 567} + result = groupby(map, def(k, v) = k.startsWith("test")) + assertEquals({"test1": 234, "test2": 345, "test3": 456}, result[true]) + assertEquals({"abc": 123, "def": 567}, result[false]) +} diff --git a/ownlang-parser/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own index bfef4c1d..394edc98 100644 --- a/ownlang-parser/src/test/resources/modules/functional/stream.own +++ b/ownlang-parser/src/test/resources/modules/functional/stream.own @@ -53,6 +53,15 @@ def testCustom() { assertEquals([5,6,4,2], stream(data).custom(::reverse).toArray()) } +def reverse(container) { + size = length(container) + result = newarray(size) + for i : range(size) { + result[size - i - 1] = container[i] + } + return result +} + def testJoining() { data = [1,2,3,4] assertEquals("1234", stream(data).joining()) @@ -101,11 +110,18 @@ def testForEachMapIndexed() { assertEquals("a10b21", result) } -def reverse(container) { - size = length(container) - result = newarray(size) - for i : range(size) { - result[size - i - 1] = container[i] - } - return result -} \ No newline at end of file +def testArraysGroupBy() { + data = [1, 2, 3, 4, 1, 2, 3, 1, 2, 3] + result = stream(data) + .groupBy(def(v) = v % 2 == 0) + assertEquals([2, 4, 2, 2], result[true]) + assertEquals([1, 3, 1, 3, 1, 3], result[false]) +} + +def testMapsGroupBy() { + data = {"abc": 123, "test1": 234, "test2": 345, "test3": 456, "def": 567} + result = stream(data) + .groupBy(def(entry) = entry[0].startsWith("test")) + assertEquals([["test1", 234], ["test2", 345], ["test3", 456]], sort(result[true])) + assertEquals([["abc", 123], ["def", 567]], sort(result[false])) +} From 5d00598e8c341952904730b923b369ff5704109a Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 28 Oct 2023 18:54:19 +0300 Subject: [PATCH 361/448] [std] Add parseDouble, nanotime, exit, getenv, getprop --- .../ownlang/modules/std/ArrayFunctions.java | 2 +- .../ownlang/modules/std/NumberFunctions.java | 2 +- .../ownlang/modules/std/StringFunctions.java | 15 +++---- .../ownlang/modules/std/SystemFunctions.java | 42 +++++++++++++++++++ .../com/annimon/ownlang/modules/std/std.java | 30 +++++++++---- .../ownlang/modules/std/std_arrayCombine.java | 2 +- .../modules/std/std_arrayKeyExists.java | 2 +- .../ownlang/modules/std/std_arrayKeys.java | 2 +- .../ownlang/modules/std/std_arraySplice.java | 2 +- .../ownlang/modules/std/std_arrayValues.java | 2 +- .../ownlang/modules/std/std_charat.java | 2 +- .../ownlang/modules/std/std_default.java | 29 +++++-------- .../annimon/ownlang/modules/std/std_echo.java | 4 +- .../ownlang/modules/std/std_indexof.java | 2 +- .../annimon/ownlang/modules/std/std_join.java | 21 ++++------ .../ownlang/modules/std/std_lastindexof.java | 2 +- .../ownlang/modules/std/std_length.java | 34 ++++++--------- .../ownlang/modules/std/std_newarray.java | 2 +- .../annimon/ownlang/modules/std/std_rand.java | 2 +- .../ownlang/modules/std/std_range.java | 28 +++---------- .../ownlang/modules/std/std_readln.java | 2 +- .../ownlang/modules/std/std_replace.java | 2 +- .../ownlang/modules/std/std_replaceall.java | 2 +- .../ownlang/modules/std/std_replacefirst.java | 2 +- .../ownlang/modules/std/std_sleep.java | 2 +- .../annimon/ownlang/modules/std/std_sort.java | 15 +++---- .../ownlang/modules/std/std_split.java | 2 +- .../ownlang/modules/std/std_sprintf.java | 2 +- .../ownlang/modules/std/std_substring.java | 2 +- .../annimon/ownlang/modules/std/std_sync.java | 2 +- .../ownlang/modules/std/std_thread.java | 2 +- .../annimon/ownlang/modules/std/std_time.java | 13 ------ .../ownlang/modules/std/std_tochar.java | 2 +- .../ownlang/modules/std/std_tolowercase.java | 2 +- .../ownlang/modules/std/std_touppercase.java | 2 +- .../annimon/ownlang/modules/std/std_trim.java | 2 +- .../annimon/ownlang/modules/std/std_try.java | 2 +- .../annimon/ownlang/modules/types/types.java | 3 +- 38 files changed, 146 insertions(+), 142 deletions(-) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/std/SystemFunctions.java delete mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java index 0bae4cac..91b85987 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/ArrayFunctions.java @@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils; import java.io.UnsupportedEncodingException; -public final class ArrayFunctions { +final class ArrayFunctions { private ArrayFunctions() { } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java index 2b654511..9bdbd248 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java @@ -6,7 +6,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class NumberFunctions { +final class NumberFunctions { private NumberFunctions() { } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java index b89de094..c36ca1bf 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/StringFunctions.java @@ -8,7 +8,7 @@ import com.annimon.ownlang.lib.Value; import java.io.UnsupportedEncodingException; -public final class StringFunctions { +final class StringFunctions { private StringFunctions() { } @@ -21,6 +21,11 @@ static ArrayValue getBytes(Value[] args) { throw new OwnLangRuntimeException(uee); } } + + static Value parseDouble(Value[] args) { + Arguments.check(1, args.length); + return NumberValue.of(Double.parseDouble(args[0].asString())); + } static Value parseInt(Value[] args) { Arguments.checkOrOr(1, 2, args.length); @@ -45,7 +50,7 @@ static Value stripMargin(Value[] args) { // First blank line is omitted final StringBuilder sb = new StringBuilder(); - final int firstLineIndex = (isBlank(lines[0])) ? 1 : 0; + final int firstLineIndex = (lines[0].isBlank()) ? 1 : 0; final int lastLineIndex = lines.length - 1; int index = firstLineIndex; while (true) { @@ -54,7 +59,7 @@ static Value stripMargin(Value[] args) { sb.append('\n'); } // Process last line - if (lastLineIndex >= (firstLineIndex + 1) && !isBlank(lines[lastLineIndex])) { + if (lastLineIndex >= (firstLineIndex + 1) && !lines[lastLineIndex].isBlank()) { sb.append('\n').append(strip(lines[lastLineIndex], marginPrefix)); } return new StringValue(sb.toString()); @@ -68,10 +73,6 @@ private static String strip(String str, String marginPrefix) { return str; } } - - private static boolean isBlank(String str) { - return firstNonBlankIndex(str) == str.length(); - } private static int firstNonBlankIndex(String str) { final int length = str.length(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/SystemFunctions.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/SystemFunctions.java new file mode 100644 index 00000000..9c8f7a36 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/SystemFunctions.java @@ -0,0 +1,42 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.lib.*; + +final class SystemFunctions { + + private SystemFunctions() { } + + static Value exit(Value[] args) { + Arguments.check(1, args.length); + System.exit(args[0].asInt()); + return NumberValue.ZERO; + } + + static Value getenv(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final var env = System.getenv(args[0].asString()); + if (env == null) { + return args.length == 2 ? args[1] : StringValue.EMPTY; + } + return new StringValue(env); + } + + static Value getprop(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + final var env = System.getProperty(args[0].asString()); + if (env == null) { + return args.length == 2 ? args[1] : StringValue.EMPTY; + } + return new StringValue(env); + } + + static Value time(Value[] args) { + Arguments.check(0, args.length); + return NumberValue.of(System.currentTimeMillis()); + } + + static Value nanotime(Value[] args) { + Arguments.check(0, args.length); + return NumberValue.of(System.nanoTime()); + } +} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java index fe76b58c..c3fafa0d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -4,6 +4,7 @@ import com.annimon.ownlang.Version; import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; +import java.util.HashMap; import java.util.Map; import static java.util.Map.entry; @@ -30,22 +31,31 @@ public Map constants() { @Override public Map functions() { - return Map.ofEntries( + // std, System + final var result = new HashMap<>(Map.ofEntries( entry("echo", new std_echo()), entry("readln", new std_readln()), entry("length", new std_length()), entry("rand", new std_rand()), - entry("time", new std_time()), + entry("time", SystemFunctions::time), + entry("nanotime", SystemFunctions::nanotime), entry("sleep", new std_sleep()), entry("thread", new std_thread()), entry("sync", new std_sync()), entry("try", new std_try()), entry("default", new std_default()), + entry("exit", SystemFunctions::exit), + entry("getenv", SystemFunctions::getenv), + entry("getprop", SystemFunctions::getprop) + )); - // Numbers - entry("toHexString", NumberFunctions::toHexString), + // Numbers + result.putAll(Map.ofEntries( + entry("toHexString", NumberFunctions::toHexString) + )); - // String + // String + result.putAll(Map.ofEntries( entry("getBytes", StringFunctions::getBytes), entry("sprintf", new std_sprintf()), entry("split", new std_split()), @@ -62,9 +72,12 @@ public Map functions() { entry("replaceFirst", new std_replacefirst()), entry("parseInt", StringFunctions::parseInt), entry("parseLong", StringFunctions::parseLong), - entry("stripMargin", StringFunctions::stripMargin), + entry("parseDouble", StringFunctions::parseDouble), + entry("stripMargin", StringFunctions::stripMargin) + )); - // Arrays and map, + // Arrays and map + result.putAll(Map.ofEntries( entry("newarray", new std_newarray()), entry("join", new std_join()), entry("sort", new std_sort()), @@ -75,6 +88,7 @@ public Map functions() { entry("arraySplice", new std_arraySplice()), entry("range", new std_range()), entry("stringFromBytes", ArrayFunctions::stringFromBytes) - ); + )); + return Map.copyOf(result); } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java index d9a32a43..78a48377 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayCombine.java @@ -8,7 +8,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class std_arrayCombine implements Function { +final class std_arrayCombine implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java index fb4b2418..51eff703 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java @@ -8,7 +8,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class std_arrayKeyExists implements Function { +final class std_arrayKeyExists implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java index e2f15651..2bd2e477 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; -public final class std_arrayKeys implements Function { +final class std_arrayKeys implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java index 2614bc24..daf68707 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arraySplice.java @@ -7,7 +7,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class std_arraySplice implements Function { +final class std_arraySplice implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java index f263d0fa..1a7de755 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; -public final class std_arrayValues implements Function { +final class std_arrayValues implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java index de0407ae..35b8ba14 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; -public final class std_charat implements Function { +final class std_charat implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java index 76b96ad8..f2d3a81b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java @@ -1,13 +1,8 @@ package com.annimon.ownlang.modules.std; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.*; -public final class std_default implements Function { +final class std_default implements Function { @Override public Value execute(Value[] args) { @@ -22,17 +17,13 @@ private boolean isEmpty(Value value) { if (value == null || value.raw() == null) { return true; } - switch (value.type()) { - case Types.NUMBER: - return (value.asInt() == 0); - case Types.STRING: - return (value.asString().isEmpty()); - case Types.ARRAY: - return ((ArrayValue) value).size() == 0; - case Types.MAP: - return ((MapValue) value).size() == 0; - default: - return false; - } + return switch (value.type()) { + case Types.NUMBER -> (value.asInt() == 0); + case Types.STRING -> (value.asString().isEmpty()); + case Types.ARRAY -> ((ArrayValue) value).size() == 0; + case Types.MAP -> ((MapValue) value).size() == 0; + case Types.CLASS -> ((ClassInstance) value).getThisMap().size() == 0; + default -> false; + }; } } \ No newline at end of file diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java index 27bdfb6a..3c4e68b0 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java @@ -5,14 +5,14 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; -public final class std_echo implements Function { +final class std_echo implements Function { @Override public Value execute(Value[] args) { final StringBuilder sb = new StringBuilder(); for (Value arg : args) { sb.append(arg.asString()); - sb.append(" "); + sb.append(' '); } Console.println(sb.toString()); return NumberValue.ZERO; diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java index 5b9e1dbf..6168042c 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; -public final class std_indexof implements Function { +final class std_indexof implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java index 7806c939..033ac1c2 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java @@ -8,7 +8,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class std_join implements Function { +final class std_join implements Function { @Override public Value execute(Value[] args) { @@ -18,17 +18,12 @@ public Value execute(Value[] args) { } final ArrayValue array = (ArrayValue) args[0]; - switch (args.length) { - case 1: - return ArrayValue.joinToString(array, "", "", ""); - case 2: - return ArrayValue.joinToString(array, args[1].asString(), "", ""); - case 3: - return ArrayValue.joinToString(array, args[1].asString(), args[2].asString(), args[2].asString()); - case 4: - return ArrayValue.joinToString(array, args[1].asString(), args[2].asString(), args[3].asString()); - default: - throw new ArgumentsMismatchException("Wrong number of arguments"); - } + return switch (args.length) { + case 1 -> ArrayValue.joinToString(array, "", "", ""); + case 2 -> ArrayValue.joinToString(array, args[1].asString(), "", ""); + case 3 -> ArrayValue.joinToString(array, args[1].asString(), args[2].asString(), args[2].asString()); + case 4 -> ArrayValue.joinToString(array, args[1].asString(), args[2].asString(), args[3].asString()); + default -> throw new ArgumentsMismatchException("Wrong number of arguments"); + }; } } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java index b4124669..ab60e3ac 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; -public final class std_lastindexof implements Function { +final class std_lastindexof implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java index 67487cea..5e58b505 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java @@ -2,32 +2,24 @@ import com.annimon.ownlang.lib.*; -public final class std_length implements Function { +final class std_length implements Function { @Override public Value execute(Value[] args) { Arguments.check(1, args.length); - final Value val = args[0]; - final int length; - switch (val.type()) { - case Types.ARRAY: - length = ((ArrayValue) val).size(); - break; - case Types.MAP: - length = ((MapValue) val).size(); - break; - case Types.STRING: - length = ((StringValue) val).length(); - break; - case Types.FUNCTION: - final Function func = ((FunctionValue) val).getValue(); - length = func.getArgsCount(); - break; - default: - length = 0; - - } + final Value value = args[0]; + final int length = switch (value.type()) { + case Types.ARRAY -> ((ArrayValue) value).size(); + case Types.MAP -> ((MapValue) value).size(); + case Types.CLASS -> ((ClassInstance) value).getThisMap().size(); + case Types.STRING -> ((StringValue) value).length(); + case Types.FUNCTION -> { + final Function func = ((FunctionValue) value).getValue(); + yield func.getArgsCount(); + } + default -> 0; + }; return NumberValue.of(length); } } \ No newline at end of file diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java index 78029e9e..33c1b4de 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; -public final class std_newarray implements Function { +final class std_newarray implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java index 56962539..0888abca 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_rand.java @@ -6,7 +6,7 @@ import com.annimon.ownlang.lib.Value; import java.util.Random; -public final class std_rand implements Function { +final class std_rand implements Function { private static final Random RND = new Random(); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java index a7336ae7..1feb1b9d 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java @@ -3,32 +3,16 @@ import com.annimon.ownlang.lib.*; import java.util.Iterator; -public final class std_range implements Function { +final class std_range implements Function { @Override public Value execute(Value[] args) { Arguments.checkRange(1, 3, args.length); - - final long from, to, step; - switch (args.length) { - default: - case 1: - from = 0; - to = getLong(args[0]); - step = 1; - break; - case 2: - from = getLong(args[0]); - to = getLong(args[1]); - step = 1; - break; - case 3: - from = getLong(args[0]); - to = getLong(args[1]); - step = getLong(args[2]); - break; - } - return RangeValue.of(from, to, step); + return switch (args.length) { + default -> RangeValue.of(0, getLong(args[0]), 1); + case 2 -> RangeValue.of(getLong(args[0]), getLong(args[1]), 1); + case 3 -> RangeValue.of(getLong(args[0]), getLong(args[1]), getLong(args[2])); + }; } private static long getLong(Value v) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java index 50605e7f..703f7890 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.Value; import java.util.Scanner; -public final class std_readln implements Function { +final class std_readln implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java index 8a274a11..aee1dd5a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_replace implements Function { +final class std_replace implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java index 6e3c2830..74e425dc 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_replaceall implements Function { +final class std_replaceall implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java index c02e5540..04ba3733 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_replacefirst implements Function { +final class std_replacefirst implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java index f0742aac..9d01de13 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.Value; -public final class std_sleep implements Function { +final class std_sleep implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java index 6735fc7d..adb67285 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sort.java @@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils; import java.util.Arrays; -public final class std_sort implements Function { +final class std_sort implements Function { @Override public Value execute(Value[] args) { @@ -19,17 +19,14 @@ public Value execute(Value[] args) { throw new TypeException("Array expected in first argument"); } final Value[] elements = ((ArrayValue) args[0]).getCopyElements(); - + switch (args.length) { - case 1: - Arrays.sort(elements); - break; - case 2: + case 1 -> Arrays.sort(elements); + case 2 -> { final Function comparator = ValueUtils.consumeFunction(args[1], 1); Arrays.sort(elements, (o1, o2) -> comparator.execute(o1, o2).asInt()); - break; - default: - throw new ArgumentsMismatchException("Wrong number of arguments"); + } + default -> throw new ArgumentsMismatchException("Wrong number of arguments"); } return new ArrayValue(elements); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java index 7b2c5a13..73aa73f4 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.Value; -public final class std_split implements Function { +final class std_split implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java index 5dfdb110..944f640a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java @@ -6,7 +6,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class std_sprintf implements Function { +final class std_sprintf implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java index dcae235c..84f769e2 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_substring implements Function { +final class std_substring implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java index 2c91c82d..04a4d31b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sync.java @@ -10,7 +10,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -public final class std_sync implements Function { +final class std_sync implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java index d4ece89c..ca36114b 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java @@ -3,7 +3,7 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.lib.*; -public final class std_thread implements Function { +final class std_thread implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java deleted file mode 100644 index f5fc062a..00000000 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_time.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.annimon.ownlang.modules.std; - -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Value; - -public final class std_time implements Function { - - @Override - public Value execute(Value[] args) { - return NumberValue.of(System.currentTimeMillis()); - } -} \ No newline at end of file diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java index b59bc0d1..d052cf82 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_tochar implements Function { +final class std_tochar implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java index f27fe018..813f78d0 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_tolowercase implements Function { +final class std_tolowercase implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java index e59397c4..74c071c3 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_touppercase implements Function { +final class std_touppercase implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java index de57bbc7..7ee1cbad 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; -public final class std_trim implements Function { +final class std_trim implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java index 8546119c..bf37ae44 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.lib.*; -public final class std_try implements Function { +final class std_try implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java index 613080ad..5a845708 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java @@ -19,7 +19,8 @@ public Map constants() { entry("STRING", NumberValue.of(Types.STRING)), entry("ARRAY", NumberValue.of(Types.ARRAY)), entry("MAP", NumberValue.of(Types.MAP)), - entry("FUNCTION", NumberValue.of(Types.FUNCTION)) + entry("FUNCTION", NumberValue.of(Types.FUNCTION)), + entry("CLASS", NumberValue.of(Types.CLASS)) ); } From 523c76dd386bc57c890f9572e2d032d6ce478d0d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 9 Nov 2023 19:53:47 +0200 Subject: [PATCH 362/448] Convert OwnLang value to Java object --- .../java/com/annimon/ownlang/lib/ArrayValue.java | 9 ++++++--- .../com/annimon/ownlang/lib/ClassInstance.java | 5 +++++ .../java/com/annimon/ownlang/lib/MapValue.java | 14 +++++++++----- .../main/java/com/annimon/ownlang/lib/Value.java | 4 ++++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java index 94b7a764..44aa31ca 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java @@ -2,9 +2,7 @@ import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.exceptions.TypeException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; +import java.util.*; /** * Represents array type. @@ -131,6 +129,11 @@ public Object raw() { return elements; } + @Override + public Object asJavaObject() { + return Arrays.stream(elements).map(Value::asJavaObject).toArray(Object[]::new); + } + @Override public int asInt() { throw new TypeException("Cannot cast array to integer"); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java index 75c12923..a5a0fe2f 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java @@ -69,6 +69,11 @@ public Object raw() { return thisMap; } + @Override + public Object asJavaObject() { + return thisMap.asJavaObject(); + } + @Override public int asInt() { throw new TypeException("Cannot cast class " + className + " to integer"); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java index 1d8c7d1e..6fecbcf3 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -1,10 +1,7 @@ package com.annimon.ownlang.lib; import com.annimon.ownlang.exceptions.TypeException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Consumer; /** @@ -60,7 +57,7 @@ public ArrayValue toPairs() { public int type() { return Types.MAP; } - + public int size() { return map.size(); } @@ -93,6 +90,13 @@ public Map getMap() { public Object raw() { return map; } + + @Override + public Object asJavaObject() { + Map result = new HashMap<>(map.size()); + map.forEach((k, v) -> result.put(k.asJavaObject(), v.asJavaObject())); + return result; + } @Override public int asInt() { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java index 0bac3e4e..f334e6da 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java @@ -15,4 +15,8 @@ public interface Value extends Comparable { String asString(); int type(); + + default Object asJavaObject() { + return raw(); + } } From e59e09aeb82005a3b5dc3a6bce17b2bc53bf8712 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 9 Nov 2023 19:54:26 +0200 Subject: [PATCH 363/448] [server] Add server module --- build.gradle | 4 + modules/server/build.gradle | 21 +++++ .../ownlang/modules/server/ContextValue.java | 91 +++++++++++++++++++ .../ownlang/modules/server/ServerValue.java | 86 ++++++++++++++++++ .../ownlang/modules/server/server.java | 40 ++++++++ settings.gradle | 10 +- 6 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 modules/server/build.gradle create mode 100644 modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java create mode 100644 modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java create mode 100644 modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java diff --git a/build.gradle b/build.gradle index 8c65cb39..386aa773 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,10 @@ ext { socket: '1.0.2', // io.socket:socket.io-client jline: '2.14.5', // jline:jline + javalin: '5.6.3', // io.javalin:javalin + slf4j: '2.0.9', // org.slf4j:slf4j-simple + jackson: '2.15.3', // com.fasterxml.jackson.core:jackson-databind + junit: '5.9.2', // org.junit:junit-bom jmh: '1.37', // org.openjdk.jmh:jmh-core assertj: '3.24.2' // org.assertj:assertj-core diff --git a/modules/server/build.gradle b/modules/server/build.gradle new file mode 100644 index 00000000..80f05708 --- /dev/null +++ b/modules/server/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java-library' + id 'com.github.johnrengelman.shadow' version '8.1.1' +} + +group = 'com.annimon.module' +version = '2.0-SNAPSHOT' + +dependencies { + compileOnlyApi project(":ownlang-core") + implementation "io.javalin:javalin:${versions.javalin}" + implementation "org.slf4j:slf4j-simple:${versions.slf4j}" + implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + + testImplementation platform("org.junit:junit-bom:") + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java new file mode 100644 index 00000000..6d8477c5 --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java @@ -0,0 +1,91 @@ +package com.annimon.ownlang.modules.server; + +import com.annimon.ownlang.lib.*; +import io.javalin.http.Context; +import io.javalin.http.HttpStatus; +import org.jetbrains.annotations.NotNull; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class ContextValue extends MapValue { + + private final Context ctx; + + public ContextValue(@NotNull Context ctx) { + super(10); + this.ctx = ctx; + init(); + } + + private void init() { + set("body", Converters.voidToString(ctx::body)); + set("characterEncoding", Converters.voidToString(ctx::characterEncoding)); + set("contentType", Converters.voidToString(ctx::contentType)); + set("contextPath", Converters.voidToString(ctx::contextPath)); + set("fullUrl", Converters.voidToString(ctx::fullUrl)); + set("host", Converters.voidToString(ctx::host)); + set("ip", Converters.voidToString(ctx::ip)); + set("matchedPath", Converters.voidToString(ctx::matchedPath)); + set("path", Converters.voidToString(ctx::path)); + set("protocol", Converters.voidToString(ctx::protocol)); + set("queryString", Converters.voidToString(ctx::queryString)); + set("url", Converters.voidToString(ctx::url)); + set("userAgent", Converters.voidToString(ctx::userAgent)); + + set("contentLength", Converters.voidToInt(ctx::contentLength)); + set("port", Converters.voidToInt(ctx::port)); + set("statusCode", Converters.voidToInt(ctx::statusCode)); + + set("json", objectToContext(ctx::json)); + set("jsonStream", objectToContext(ctx::jsonStream)); + + set("render", this::render); + set("result", this::result); + set("redirect", this::redirect); + } + + private Value render(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + String filePath = args[0].asString(); + if (args.length == 1) { + ctx.render(filePath); + } else { + MapValue map = (MapValue) args[1]; + Map data = new HashMap<>(map.size()); + map.getMap().forEach((k, v) -> data.put(k.asString(), v.asJavaObject())); + ctx.render(filePath, data); + } + return this; + } + + private Value redirect(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + HttpStatus status = args.length == 1 ? HttpStatus.FOUND : HttpStatus.forStatus(args[1].asInt()); + ctx.redirect(args[0].asString(), status); + return this; + } + + private Value result(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + return new StringValue(ctx.result()); + } else { + final var arg = args[0]; + if (arg.type() == Types.ARRAY) { + ctx.result(ValueUtils.toByteArray((ArrayValue) arg)); + } else { + ctx.result(arg.asString()); + } + return this; + } + } + + private Value objectToContext(Consumer consumer) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + consumer.accept(args[0].asJavaObject()); + return this; + }); + } +} diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java new file mode 100644 index 00000000..b9b7a47a --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java @@ -0,0 +1,86 @@ +package com.annimon.ownlang.modules.server; + +import com.annimon.ownlang.lib.*; +import io.javalin.Javalin; +import io.javalin.http.Handler; +import io.javalin.security.RouteRole; +import java.util.Arrays; + +public class ServerValue extends MapValue { + + private final Javalin server; + + public ServerValue(Javalin server) { + super(10); + this.server = server; + init(); + } + + private void init() { + set("get", httpMethod(server::get)); + set("post", httpMethod(server::post)); + set("put", httpMethod(server::put)); + set("patch", httpMethod(server::patch)); + set("head", httpMethod(server::head)); + set("delete", httpMethod(server::delete)); + set("options", httpMethod(server::options)); + set("error", this::error); + set("start", this::start); + } + + private Value error(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + final int handlerIndex; + final String contentType; + if (args.length == 2) { + contentType = "*"; + handlerIndex = 1; + } else { + contentType = args[1].asString(); + handlerIndex = 2; + } + int status = args[0].asInt(); + final Handler handler = toHandler(ValueUtils.consumeFunction(args[handlerIndex], handlerIndex)); + server.error(status, contentType, handler); + return this; + } + + private Value start(Value[] args) { + Arguments.checkRange(0, 2, args.length); + switch (args.length) { + case 0 -> server.start(); + case 1 -> server.start(args[0].asInt()); + case 2 -> server.start(args[0].asString(), args[1].asInt()); + } + return this; + } + + private FunctionValue httpMethod(HttpMethodHandler httpHandler) { + return new FunctionValue(args -> { + Arguments.checkAtLeast(2, args.length); + final String path = args[0].asString(); + final Handler handler = toHandler(ValueUtils.consumeFunction(args[1], 1)); + final Role[] roles; + if (args.length == 2) { + roles = new Role[0]; + } else { + roles = Arrays.stream(args) + .skip(2) + .map(Role::new) + .toArray(Role[]::new); + } + httpHandler.handle(path, handler, roles); + return this; + }); + } + + private Handler toHandler(Function function) { + return ctx -> function.execute(new ContextValue(ctx)); + } + + private interface HttpMethodHandler { + void handle(String path, Handler handler, RouteRole[] roles); + } + + private record Role(Value value) implements RouteRole { } +} diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java new file mode 100644 index 00000000..fd508b93 --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java @@ -0,0 +1,40 @@ +package com.annimon.ownlang.modules.server; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import io.javalin.Javalin; +import java.util.Map; +import static java.util.Map.entry; + +public final class server implements Module { + + @Override + public Map constants() { + return Map.of(); + } + + @Override + public Map functions() { + return Map.ofEntries( + entry("newServer", this::newServer), + entry("serve", this::serve) + ); + } + + private Value newServer(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + return new ServerValue(Javalin.create()); + } else { + final Function configConsumer = ValueUtils.consumeFunction(args[0], 0); + return new ServerValue(Javalin.create(config -> configConsumer.execute())); + } + } + + private Value serve(Value[] args) { + // get path, port + // javalin start() + return NumberValue.ZERO; + + } +} diff --git a/settings.gradle b/settings.gradle index 28d12867..72a528c1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,9 @@ include 'ownlang-parser' include 'ownlang-desktop' include 'ownlang-utils' -include 'modules:main' -findProject(':modules:main')?.name = 'main' -include 'modules:canvasfx' -findProject(':modules:canvasfx')?.name = 'canvasfx' \ No newline at end of file +final def modules = ['main', 'canvasfx', 'server'] + +for (final def module in modules) { + include "modules:$module" + findProject(":modules:$module")?.name = module +} From f575b43b51346471bb292fe5d3e2b5a4d0a56dac Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 10 Nov 2023 23:41:06 +0200 Subject: [PATCH 364/448] [server] More server functions --- .../ownlang/modules/server/Config.java | 107 +++++++++++++++++ .../ownlang/modules/server/ContextValue.java | 108 +++++++++++++++--- .../ownlang/modules/server/ServerValue.java | 30 ++++- .../ownlang/modules/server/server.java | 17 ++- .../com/annimon/ownlang/lib/Converters.java | 8 ++ .../com/annimon/ownlang/lib/MapValue.java | 11 ++ .../com/annimon/ownlang/lib/ValueUtils.java | 9 ++ 7 files changed, 267 insertions(+), 23 deletions(-) create mode 100644 modules/server/src/main/java/com/annimon/ownlang/modules/server/Config.java diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/Config.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/Config.java new file mode 100644 index 00000000..fef668a9 --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/Config.java @@ -0,0 +1,107 @@ +package com.annimon.ownlang.modules.server; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import io.javalin.config.JavalinConfig; +import io.javalin.http.staticfiles.Location; +import java.util.Map; +import java.util.function.Consumer; + +/* + * Sample config: + * { + * "webjars": true, + * "classpathDirs": ["dir1", "dir2"], + * "externalDirs": ["dir1", "dir2"], + * + * "asyncTimeout": 6_000, + * "defaultContentType": "text/plain", + * "etags": true, + * "maxRequestSize": 1_000_000, + * + * "caseInsensitiveRoutes": true, + * "ignoreTrailingSlashes": true, + * "multipleSlashesAsSingle": true, + * "contextPath": "/", + * + * "basicAuth": ["user", "password"], + * "dev": true, + * "showBanner": false, + * "sslRedirects": true + * } + */ + +class Config { + private final Map map; + + public Config(Map map) { + this.map = map; + } + + public void setup(JavalinConfig config) { + // staticFiles + ifTrue("webjars", config.staticFiles::enableWebjars); + ifArray("classpathDirs", directories -> { + for (Value directory : directories) { + config.staticFiles.add(directory.asString(), Location.CLASSPATH); + } + }); + ifArray("externalDirs", directories -> { + for (Value directory : directories) { + config.staticFiles.add(directory.asString(), Location.EXTERNAL); + } + }); + + // http + ifNumber("asyncTimeout", value -> config.http.asyncTimeout = value.asLong()); + ifString("defaultContentType", value -> config.http.defaultContentType = value); + ifBoolean("etags", flag -> config.http.generateEtags = flag); + ifNumber("maxRequestSize", value -> config.http.maxRequestSize = value.asLong()); + + // routing + ifBoolean("caseInsensitiveRoutes", flag -> config.routing.caseInsensitiveRoutes = flag); + ifBoolean("ignoreTrailingSlashes", flag -> config.routing.ignoreTrailingSlashes = flag); + ifBoolean("multipleSlashesAsSingle", flag -> config.routing.treatMultipleSlashesAsSingleSlash = flag); + ifString("contextPath", path -> config.routing.contextPath = path); + + // other + ifArray("basicAuth", arr -> config.plugins.enableBasicAuth(arr.get(0).asString(), arr.get(1).asString())); + ifBoolean("showBanner", flag -> config.showJavalinBanner = flag); + ifTrue("dev", config.plugins::enableDevLogging); + ifTrue("sslRedirects", config.plugins::enableSslRedirects); + } + + private void ifTrue(String key, Runnable action) { + if (map.containsKey(key) && map.get(key).asInt() != 0) { + action.run(); + } + } + + private void ifBoolean(String key, Consumer consumer) { + if (!map.containsKey(key)) return; + consumer.accept(map.get(key).asInt() != 0); + } + + private void ifNumber(String key, Consumer consumer) { + if (!map.containsKey(key)) return; + final Value value = map.get(key); + if (value.type() == Types.NUMBER) { + consumer.accept((NumberValue) value); + } + } + + private void ifString(String key, Consumer consumer) { + if (!map.containsKey(key)) return; + consumer.accept(map.get(key).asString()); + } + + private void ifArray(String key, Consumer consumer) { + if (!map.containsKey(key)) return; + final Value value = map.get(key); + if (value.type() == Types.ARRAY) { + consumer.accept((ArrayValue) value); + } + } +} diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java index 6d8477c5..fe9d2ca4 100644 --- a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java @@ -4,45 +4,108 @@ import io.javalin.http.Context; import io.javalin.http.HttpStatus; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; -public class ContextValue extends MapValue { +class ContextValue extends MapValue { private final Context ctx; public ContextValue(@NotNull Context ctx) { - super(10); + super(32); this.ctx = ctx; init(); } private void init() { + set("attribute", this::attribute); + set("basicAuthCredentials", this::basicAuthCredentials); set("body", Converters.voidToString(ctx::body)); + set("bodyAsBytes", this::bodyAsBytes); set("characterEncoding", Converters.voidToString(ctx::characterEncoding)); - set("contentType", Converters.voidToString(ctx::contentType)); + set("cookie", this::cookie); + set("contentLength", Converters.voidToInt(ctx::contentLength)); + set("contentType", this::contentType); set("contextPath", Converters.voidToString(ctx::contextPath)); + set("endpointHandlerPath", Converters.voidToString(ctx::endpointHandlerPath)); + set("formParam", Converters.stringToString(ctx::formParam)); set("fullUrl", Converters.voidToString(ctx::fullUrl)); + set("handlerType", Converters.voidToString(() -> ctx.handlerType().name())); + set("header", this::header); set("host", Converters.voidToString(ctx::host)); + set("html", stringToContext(ctx::html)); set("ip", Converters.voidToString(ctx::ip)); + set("json", objectToContext(ctx::json)); + set("jsonStream", objectToContext(ctx::jsonStream)); set("matchedPath", Converters.voidToString(ctx::matchedPath)); set("path", Converters.voidToString(ctx::path)); + set("port", Converters.voidToInt(ctx::port)); set("protocol", Converters.voidToString(ctx::protocol)); set("queryString", Converters.voidToString(ctx::queryString)); + set("redirect", this::redirect); + set("removeCookie", this::removeCookie); + set("render", this::render); + set("result", this::result); + set("statusCode", Converters.voidToInt(ctx::statusCode)); + set("scheme", Converters.voidToString(ctx::scheme)); set("url", Converters.voidToString(ctx::url)); set("userAgent", Converters.voidToString(ctx::userAgent)); + } - set("contentLength", Converters.voidToInt(ctx::contentLength)); - set("port", Converters.voidToInt(ctx::port)); - set("statusCode", Converters.voidToInt(ctx::statusCode)); + private Value attribute(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + String key = args[0].asString(); + if (args.length == 1) { + return ctx.attribute(key); + } else { + ctx.attribute(key, args[1]); + } + return this; + } - set("json", objectToContext(ctx::json)); - set("jsonStream", objectToContext(ctx::jsonStream)); + private Value basicAuthCredentials(Value[] args) { + Arguments.check(0, args.length); + final var cred = ctx.basicAuthCredentials(); + return ArrayValue.of(new String[] { + cred.getUsername(), + cred.getPassword() + }); + } - set("render", this::render); - set("result", this::result); - set("redirect", this::redirect); + private Value bodyAsBytes(Value[] args) { + Arguments.check(0, args.length); + return ArrayValue.of(ctx.bodyAsBytes()); + } + + private Value cookie(Value[] args) { + Arguments.checkRange(1, 3, args.length); + if (args.length == 1) { + return new StringValue(ctx.cookie(args[0].asString())); + } + int maxAge = args.length == 3 ? args[2].asInt() : -1; + ctx.cookie(args[0].asString(), args[1].asString(), maxAge); + return this; + } + + private Value contentType(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + return new StringValue(ctx.contentType()); + } else { + ctx.contentType(args[0].asString()); + return this; + } + } + + private Value header(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + String name = args[0].asString(); + if (args.length == 1) { + return new StringValue(ctx.header(name)); + } else { + ctx.header(name, args[1].asString()); + return this; + } } private Value render(Value[] args) { @@ -51,9 +114,8 @@ private Value render(Value[] args) { if (args.length == 1) { ctx.render(filePath); } else { - MapValue map = (MapValue) args[1]; - Map data = new HashMap<>(map.size()); - map.getMap().forEach((k, v) -> data.put(k.asString(), v.asJavaObject())); + MapValue map = ValueUtils.consumeMap(args[1], 1); + Map data = map.convertMap(Value::asString, Value::asJavaObject); ctx.render(filePath, data); } return this; @@ -66,6 +128,14 @@ private Value redirect(Value[] args) { return this; } + private Value removeCookie(Value[] args) { + Arguments.checkOrOr(1, 2, args.length); + String name = args[0].asString(); + String path = args.length == 2 ? args[1].asString() : "/"; + ctx.removeCookie(name, path); + return this; + } + private Value result(Value[] args) { Arguments.checkOrOr(0, 1, args.length); if (args.length == 0) { @@ -81,6 +151,14 @@ private Value result(Value[] args) { } } + private Value stringToContext(Consumer consumer) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + consumer.accept(args[0].asString()); + return this; + }); + } + private Value objectToContext(Consumer consumer) { return new FunctionValue(args -> { Arguments.check(1, args.length); diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java index b9b7a47a..05dbcd7a 100644 --- a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java @@ -1,17 +1,18 @@ package com.annimon.ownlang.modules.server; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.*; import io.javalin.Javalin; import io.javalin.http.Handler; import io.javalin.security.RouteRole; import java.util.Arrays; -public class ServerValue extends MapValue { +class ServerValue extends MapValue { private final Javalin server; public ServerValue(Javalin server) { - super(10); + super(12); this.server = server; init(); } @@ -25,7 +26,9 @@ private void init() { set("delete", httpMethod(server::delete)); set("options", httpMethod(server::options)); set("error", this::error); + set("exception", this::exception); set("start", this::start); + set("stop", this::stop); } private Value error(Value[] args) { @@ -45,6 +48,23 @@ private Value error(Value[] args) { return this; } + @SuppressWarnings("unchecked") + private Value exception(Value[] args) { + Arguments.check(2, args.length); + try { + String className = args[0].asString(); + final Class clazz = Class.forName(className); + final Function handler = ValueUtils.consumeFunction(args[1], 1); + server.exception((Class) clazz, (exc, ctx) -> { + Value exceptionType = new StringValue(exc.getClass().getName()); + handler.execute(exceptionType, new ContextValue(ctx)); + }); + } catch (ClassNotFoundException e) { + throw new OwnLangRuntimeException(e); + } + return this; + } + private Value start(Value[] args) { Arguments.checkRange(0, 2, args.length); switch (args.length) { @@ -55,6 +75,12 @@ private Value start(Value[] args) { return this; } + private Value stop(Value[] args) { + Arguments.check(0, args.length); + server.stop(); + return this; + } + private FunctionValue httpMethod(HttpMethodHandler httpHandler) { return new FunctionValue(args -> { Arguments.checkAtLeast(2, args.length); diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java index fd508b93..514369b2 100644 --- a/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import io.javalin.Javalin; +import io.javalin.http.staticfiles.Location; import java.util.Map; import static java.util.Map.entry; @@ -26,15 +27,19 @@ private Value newServer(Value[] args) { if (args.length == 0) { return new ServerValue(Javalin.create()); } else { - final Function configConsumer = ValueUtils.consumeFunction(args[0], 0); - return new ServerValue(Javalin.create(config -> configConsumer.execute())); + final Map map = ValueUtils.consumeMap(args[0], 0).getMapStringKeys(); + final Config config = new Config(map); + return new ServerValue(Javalin.create(config::setup)); } } private Value serve(Value[] args) { - // get path, port - // javalin start() - return NumberValue.ZERO; - + Arguments.checkRange(0, 2, args.length); + int port = args.length >= 1 ? args[0].asInt() : 8080; + String dir = args.length >= 2 ? args[1].asString() : "."; + return new ServerValue(Javalin.create(config -> { + config.staticFiles.add(dir, Location.EXTERNAL); + config.showJavalinBanner = false; + }).start(port)); } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java index 9415df00..32934ac3 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.lib; import java.util.function.Predicate; +import java.util.function.UnaryOperator; import static com.annimon.ownlang.lib.ValueUtils.getFloatNumber; /** @@ -273,4 +274,11 @@ public static FunctionValue stringToBoolean(Predicate f) { return NumberValue.fromBoolean(f.test(args[0].asString())); }); } + + public static FunctionValue stringToString(UnaryOperator f) { + return new FunctionValue(args -> { + Arguments.check(1, args.length); + return new StringValue(f.apply(args[0].asString())); + }); + } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java index 6fecbcf3..eedac0dc 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -85,6 +85,17 @@ public void set(Value key, Value value) { public Map getMap() { return map; } + + public Map getMapStringKeys() { + return convertMap(Value::asString, java.util.function.Function.identity()); + } + + public Map convertMap(java.util.function.Function keyMapper, + java.util.function.Function valueMapper) { + final Map result = new LinkedHashMap<>(map.size()); + map.forEach((key, value) -> result.put(keyMapper.apply(key), valueMapper.apply(value))); + return result; + } @Override public Object raw() { diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 46c3dd94..3b74a10f 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -101,6 +101,15 @@ public static byte[] toByteArray(ArrayValue array) { return result; } + public static MapValue consumeMap(Value value, int argumentNumber) { + final int type = value.type(); + if (type != Types.MAP) { + throw new TypeException("Map expected at argument " + (argumentNumber + 1) + + ", but found " + Types.typeToString(type)); + } + return (MapValue) value; + } + public static Function consumeFunction(Value value, int argumentNumber) { return consumeFunction(value, " at argument " + (argumentNumber + 1)); } From bd836c868a05b77a7734ab03be4327796378c694 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 15 Nov 2023 21:16:08 +0200 Subject: [PATCH 365/448] Add Prism.js and highlight.js syntax highlight definitions --- editors/README.md | 8 ++++++ editors/highlighjs/own.js | 44 ++++++++++++++++++++++++++++++ editors/idea/filetypes/OwnLang.xml | 2 +- editors/prismjs/own-language.js | 19 +++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 editors/highlighjs/own.js create mode 100644 editors/prismjs/own-language.js diff --git a/editors/README.md b/editors/README.md index 2c52659d..9d41cb8e 100644 --- a/editors/README.md +++ b/editors/README.md @@ -5,3 +5,11 @@ 1. Open an `idea` folder 2. Add all files and folders to zip archive, e.g. `settings.zip` 3. File -> Manage IDE Settings -> Import. Select your zip file. + +## Prism.js + +```javascript +import Prism from 'prismjs'; +import definePrismOwnLang from './prismjs/own-language.js' +definePrismOwnLang(Prism) +``` diff --git a/editors/highlighjs/own.js b/editors/highlighjs/own.js new file mode 100644 index 00000000..309ce062 --- /dev/null +++ b/editors/highlighjs/own.js @@ -0,0 +1,44 @@ +export default function(hljs) { + const STRING = { + className: 'string', + variants: [{ + begin: '"', end: '"', + contains: [hljs.BACKSLASH_ESCAPE] + }] + }; + + const EXTENDED_LITERAL = { + className: 'literal', + variants: [{ + begin: '`', end: '`', + illegal: '\\n' + }] + }; + + const METHOD = { + className: 'function', + beginKeywords: 'def', + end: /[:={\[(\n;]/, + excludeEnd: true, + contains: [{ + className: 'title', + begin: /[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/, + relevance: 0 + }] + }; + + return { + keywords: { + literal: 'true false this null', + keyword: 'break class continue def else for if match print println return use while do case extract include' + }, + contains: [ + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + STRING, + EXTENDED_LITERAL, + METHOD, + hljs.C_NUMBER_MODE + ] + }; +}; \ No newline at end of file diff --git a/editors/idea/filetypes/OwnLang.xml b/editors/idea/filetypes/OwnLang.xml index 324962ef..af5ab474 100644 --- a/editors/idea/filetypes/OwnLang.xml +++ b/editors/idea/filetypes/OwnLang.xml @@ -13,7 +13,7 @@ - + diff --git a/editors/prismjs/own-language.js b/editors/prismjs/own-language.js new file mode 100644 index 00000000..d12c7862 --- /dev/null +++ b/editors/prismjs/own-language.js @@ -0,0 +1,19 @@ +export default function(Prism) { + Prism.languages.own = Prism.languages.extend('clike', { + 'string': { + pattern: /(^|[^\\])"(?:\\.|[^"\\])*"/, + lookbehind: true, + greedy: true + }, + 'keyword': /\b(?:break|case|class|continue|def|do|else|extract|for|if|include|match|new|print|println|return|while|use)\b/, + 'function': { + pattern: /((?:^|\s)def\s*)([a-zA-Z_]\w*)?(?=\s*\()/g, + lookbehind: true + }, + 'operator': { + pattern: /(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\*\*|\|\||::|\.\.\.?|[?:~]|[-+*/%&|^!=<>]=?)/m, + lookbehind: true + }, + 'punctuation': /[{}[\];(),.:`]/ + }); +} \ No newline at end of file From 2a45a30778529b5839cdd728584026fabdf77be3 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 15 Nov 2023 21:53:21 +0200 Subject: [PATCH 366/448] Add VuePress documentation subproject --- docs/.gitignore | 4 + docs/docs/.vuepress/config.js | 43 + docs/docs/.vuepress/configs/navbar.js | 16 + docs/docs/.vuepress/configs/pages.js | 30 + docs/docs/.vuepress/configs/sidebar.js | 17 + docs/docs/README.md | 15 + .../code/basics/destructuring_assignment1.own | 5 + .../code/basics/destructuring_assignment2.own | 4 + .../code/basics/destructuring_assignment3.own | 4 + .../code/basics/destructuring_assignment4.own | 3 + docs/docs/code/basics/fibonacci.own | 9 + docs/docs/code/basics/loops1.own | 9 + docs/docs/code/basics/pattern_matching1.own | 7 + docs/docs/code/basics/pattern_matching2.own | 9 + docs/docs/code/basics/pattern_matching3.own | 10 + docs/docs/code/basics/pattern_matching4.own | 9 + docs/docs/code/basics/pattern_matching5.own | 7 + docs/docs/code/basics/pattern_matching6.own | 8 + docs/docs/code/basics/string_functions1.own | 7 + docs/docs/code/functional_en.own | 15 + docs/docs/code/functional_ru.own | 15 + docs/docs/code/high_order_functions_en.own | 14 + docs/docs/code/high_order_functions_ru.own | 14 + docs/docs/code/http_en.own | 20 + docs/docs/code/http_ru.own | 20 + docs/docs/code/operator_overloading.own | 7 + docs/docs/code/pattern_matching.own | 16 + docs/docs/en/README.md | 35 + docs/docs/en/basics/README.md | 11 + docs/docs/en/basics/array_functions.md | 8 + docs/docs/en/basics/comments.md | 9 + .../en/basics/destructuring_assignment.md | 19 + docs/docs/en/basics/functions.md | 54 + docs/docs/en/basics/loops.md | 113 ++ docs/docs/en/basics/pattern_matching.md | 79 + docs/docs/en/basics/string_functions.md | 20 + docs/docs/en/basics/strings.md | 12 + docs/docs/en/basics/types.md | 24 + docs/docs/en/links.md | 13 + docs/docs/ru/README.md | 35 + docs/docs/ru/basics/README.md | 11 + docs/docs/ru/basics/array_functions.md | 8 + docs/docs/ru/basics/comments.md | 9 + .../ru/basics/destructuring_assignment.md | 19 + docs/docs/ru/basics/functions.md | 54 + docs/docs/ru/basics/loops.md | 115 ++ docs/docs/ru/basics/pattern_matching.md | 79 + docs/docs/ru/basics/string_functions.md | 20 + docs/docs/ru/basics/strings.md | 12 + docs/docs/ru/basics/types.md | 24 + docs/docs/ru/links.md | 13 + docs/package.json | 20 + docs/pnpm-lock.yaml | 1780 +++++++++++++++++ 53 files changed, 2933 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/docs/.vuepress/config.js create mode 100644 docs/docs/.vuepress/configs/navbar.js create mode 100644 docs/docs/.vuepress/configs/pages.js create mode 100644 docs/docs/.vuepress/configs/sidebar.js create mode 100644 docs/docs/README.md create mode 100644 docs/docs/code/basics/destructuring_assignment1.own create mode 100644 docs/docs/code/basics/destructuring_assignment2.own create mode 100644 docs/docs/code/basics/destructuring_assignment3.own create mode 100644 docs/docs/code/basics/destructuring_assignment4.own create mode 100644 docs/docs/code/basics/fibonacci.own create mode 100644 docs/docs/code/basics/loops1.own create mode 100644 docs/docs/code/basics/pattern_matching1.own create mode 100644 docs/docs/code/basics/pattern_matching2.own create mode 100644 docs/docs/code/basics/pattern_matching3.own create mode 100644 docs/docs/code/basics/pattern_matching4.own create mode 100644 docs/docs/code/basics/pattern_matching5.own create mode 100644 docs/docs/code/basics/pattern_matching6.own create mode 100644 docs/docs/code/basics/string_functions1.own create mode 100644 docs/docs/code/functional_en.own create mode 100644 docs/docs/code/functional_ru.own create mode 100644 docs/docs/code/high_order_functions_en.own create mode 100644 docs/docs/code/high_order_functions_ru.own create mode 100644 docs/docs/code/http_en.own create mode 100644 docs/docs/code/http_ru.own create mode 100644 docs/docs/code/operator_overloading.own create mode 100644 docs/docs/code/pattern_matching.own create mode 100644 docs/docs/en/README.md create mode 100644 docs/docs/en/basics/README.md create mode 100644 docs/docs/en/basics/array_functions.md create mode 100644 docs/docs/en/basics/comments.md create mode 100644 docs/docs/en/basics/destructuring_assignment.md create mode 100644 docs/docs/en/basics/functions.md create mode 100644 docs/docs/en/basics/loops.md create mode 100644 docs/docs/en/basics/pattern_matching.md create mode 100644 docs/docs/en/basics/string_functions.md create mode 100644 docs/docs/en/basics/strings.md create mode 100644 docs/docs/en/basics/types.md create mode 100644 docs/docs/en/links.md create mode 100644 docs/docs/ru/README.md create mode 100644 docs/docs/ru/basics/README.md create mode 100644 docs/docs/ru/basics/array_functions.md create mode 100644 docs/docs/ru/basics/comments.md create mode 100644 docs/docs/ru/basics/destructuring_assignment.md create mode 100644 docs/docs/ru/basics/functions.md create mode 100644 docs/docs/ru/basics/loops.md create mode 100644 docs/docs/ru/basics/pattern_matching.md create mode 100644 docs/docs/ru/basics/string_functions.md create mode 100644 docs/docs/ru/basics/strings.md create mode 100644 docs/docs/ru/basics/types.md create mode 100644 docs/docs/ru/links.md create mode 100644 docs/package.json create mode 100644 docs/pnpm-lock.yaml diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..7d2109ab --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,4 @@ +node_modules +.temp +.cache +/docs/*/modules/ diff --git a/docs/docs/.vuepress/config.js b/docs/docs/.vuepress/config.js new file mode 100644 index 00000000..ffc9adb7 --- /dev/null +++ b/docs/docs/.vuepress/config.js @@ -0,0 +1,43 @@ +import { defineUserConfig, defaultTheme } from 'vuepress' +import { prismjsPlugin } from '@vuepress/plugin-prismjs' +import { sidebarConfig } from './configs/sidebar' +import { navbarConfig } from './configs/navbar' +import Prism from 'prismjs'; +import definePrismOwnLang from '../../../editors/prismjs/own-language.js' +definePrismOwnLang(Prism) + +export default defineUserConfig({ + locales: { + '/en/': { + lang: 'en-US', + title: 'OwnLang', + description: 'OwnLang documentation', + }, + '/ru/': { + lang: 'ru-RU', + title: 'OwnLang', + description: 'Документация OwnLang', + } + }, + + theme: defaultTheme({ + locales: { + '/en/': { + selectLanguageName: 'English', + sidebar: sidebarConfig.en, + navbar: navbarConfig.en + }, + '/ru/': { + selectLanguageName: 'Русский', + sidebar: sidebarConfig.ru, + navbar: navbarConfig.ru + } + } + }), + + plugins: [ + prismjsPlugin({ + preloadLanguages: ['own', 'json'] + }), + ], +}) \ No newline at end of file diff --git a/docs/docs/.vuepress/configs/navbar.js b/docs/docs/.vuepress/configs/navbar.js new file mode 100644 index 00000000..2c7a2a69 --- /dev/null +++ b/docs/docs/.vuepress/configs/navbar.js @@ -0,0 +1,16 @@ +import pages from './pages' + +let navbar = {} +for (let lang of ['en', 'ru']) { + let config = [] + for (let [relativePath, entry] of Object.entries(pages)) { + const path = '/' + lang + relativePath + config.push({ + text: entry.text[lang], + children: entry.pages.map(r => path + r) + }) + } + navbar[lang] = config +} + +export const navbarConfig = navbar \ No newline at end of file diff --git a/docs/docs/.vuepress/configs/pages.js b/docs/docs/.vuepress/configs/pages.js new file mode 100644 index 00000000..55fed5be --- /dev/null +++ b/docs/docs/.vuepress/configs/pages.js @@ -0,0 +1,30 @@ +export default { + '/': { + text: {'en': 'OwnLang', 'ru': 'OwnLang'}, + pages: [ + 'README.md', + 'links.md' + ] + }, + + '/basics/': { + text: {'en': 'Basics', 'ru': 'Основы'}, + pages: [ + 'comments.md', + 'strings.md', + 'types.md', + 'loops.md', + 'functions.md', + 'destructuring_assignment.md', + 'pattern_matching.md', + 'string_functions.md', + 'array_functions.md' + ] + }, + + '/modules/': { + text: {'en': 'Modules', 'ru': 'Модули'}, + pages: [ + ] + } +} \ No newline at end of file diff --git a/docs/docs/.vuepress/configs/sidebar.js b/docs/docs/.vuepress/configs/sidebar.js new file mode 100644 index 00000000..057ca20d --- /dev/null +++ b/docs/docs/.vuepress/configs/sidebar.js @@ -0,0 +1,17 @@ +import pages from './pages' + +let sidebar = {} +for (let lang of ['en', 'ru']) { + let config = {} + for (let [relativePath, entry] of Object.entries(pages)) { + const path = '/' + lang + relativePath + config[path] = (path in config) ? config[path] : [] + config[path].push({ + text: entry.text[lang], + children: entry.pages.map(r => path + r) + }) + } + sidebar[lang] = config +} + +export const sidebarConfig = sidebar \ No newline at end of file diff --git a/docs/docs/README.md b/docs/docs/README.md new file mode 100644 index 00000000..7c274b94 --- /dev/null +++ b/docs/docs/README.md @@ -0,0 +1,15 @@ +--- +home: true +title: OwnLang +heroText: OwnLang +tagline: Dynamic functional programming language +actions: + - text: 🇺🇸 English + link: /en/ + type: primary + - text: 🇷🇺 Русский + link: /ru/ + type: primary +footer: © 2023 aNNiMON +--- + \ No newline at end of file diff --git a/docs/docs/code/basics/destructuring_assignment1.own b/docs/docs/code/basics/destructuring_assignment1.own new file mode 100644 index 00000000..893d4c8a --- /dev/null +++ b/docs/docs/code/basics/destructuring_assignment1.own @@ -0,0 +1,5 @@ +arr = ["a", "b", "c"] +extract(var1, var2, var3) = arr +print var1 // a +print var2 // b +print var3 // c \ No newline at end of file diff --git a/docs/docs/code/basics/destructuring_assignment2.own b/docs/docs/code/basics/destructuring_assignment2.own new file mode 100644 index 00000000..bb1b128a --- /dev/null +++ b/docs/docs/code/basics/destructuring_assignment2.own @@ -0,0 +1,4 @@ +arr = ["a", "b", "c"] +var1 = arr[0] +var2 = arr[1] +var3 = arr[2] \ No newline at end of file diff --git a/docs/docs/code/basics/destructuring_assignment3.own b/docs/docs/code/basics/destructuring_assignment3.own new file mode 100644 index 00000000..661d4ece --- /dev/null +++ b/docs/docs/code/basics/destructuring_assignment3.own @@ -0,0 +1,4 @@ +map = {"key1": 1, "test", "text"} +extract(var1, var2) = map +println var1 // [key1, 1] +println var2 // [test, text] \ No newline at end of file diff --git a/docs/docs/code/basics/destructuring_assignment4.own b/docs/docs/code/basics/destructuring_assignment4.own new file mode 100644 index 00000000..3538b909 --- /dev/null +++ b/docs/docs/code/basics/destructuring_assignment4.own @@ -0,0 +1,3 @@ +extract(x, , z) = [93, 58, 90] +println x // 93 +println z // 90 \ No newline at end of file diff --git a/docs/docs/code/basics/fibonacci.own b/docs/docs/code/basics/fibonacci.own new file mode 100644 index 00000000..eae9e8bf --- /dev/null +++ b/docs/docs/code/basics/fibonacci.own @@ -0,0 +1,9 @@ +def fibonacci(count) { + def fib(n) { + if n < 2 return n + return fib(n-2) + fib(n-1) + } + return fib(count) +} + +println fibonacci(10) // 55 \ No newline at end of file diff --git a/docs/docs/code/basics/loops1.own b/docs/docs/code/basics/loops1.own new file mode 100644 index 00000000..4a2ae95b --- /dev/null +++ b/docs/docs/code/basics/loops1.own @@ -0,0 +1,9 @@ +arr = [1, 2, 3, 4] +for v : arr { + println v +} + +map = {"key1": 1, "key2": 2} +for key, value : map + println key + " = " value +} \ No newline at end of file diff --git a/docs/docs/code/basics/pattern_matching1.own b/docs/docs/code/basics/pattern_matching1.own new file mode 100644 index 00000000..a67666f8 --- /dev/null +++ b/docs/docs/code/basics/pattern_matching1.own @@ -0,0 +1,7 @@ +x = 2 +print match x { + case 1: "One" + case 2: "Two" + case "str": "String" + case _: "Unknown" +} \ No newline at end of file diff --git a/docs/docs/code/basics/pattern_matching2.own b/docs/docs/code/basics/pattern_matching2.own new file mode 100644 index 00000000..80e6dccb --- /dev/null +++ b/docs/docs/code/basics/pattern_matching2.own @@ -0,0 +1,9 @@ +x = "str" +match x { + case "": { + println "Empty string" + } + case "str": { + println "String!" + } +} \ No newline at end of file diff --git a/docs/docs/code/basics/pattern_matching3.own b/docs/docs/code/basics/pattern_matching3.own new file mode 100644 index 00000000..ddacb268 --- /dev/null +++ b/docs/docs/code/basics/pattern_matching3.own @@ -0,0 +1,10 @@ +def test(x) = match x { + case a: "case a: " + a + case b: "case b: " + b + case c: "case c: " + c +} +a = 10 +b = 20 +println test(15) // case c: 15 +println test(20) // case b: 20 +println test("test") // case c: test \ No newline at end of file diff --git a/docs/docs/code/basics/pattern_matching4.own b/docs/docs/code/basics/pattern_matching4.own new file mode 100644 index 00000000..3ddbd67e --- /dev/null +++ b/docs/docs/code/basics/pattern_matching4.own @@ -0,0 +1,9 @@ +def test(x) = match x { + case x if x < 0: "(-∞ .. 0)" + case x if x > 0: "(0 .. +∞)" + case x: "0" +} + +println test(-10) // (-∞ .. 0) +println test(0) // 0 +println test(10) // (0 .. +∞) \ No newline at end of file diff --git a/docs/docs/code/basics/pattern_matching5.own b/docs/docs/code/basics/pattern_matching5.own new file mode 100644 index 00000000..97d5e40d --- /dev/null +++ b/docs/docs/code/basics/pattern_matching5.own @@ -0,0 +1,7 @@ +def arrayRecursive(arr) = match arr { + case [head :: tail]: "[" + head + ", " + arrayRecursive(tail) + "]" + case []: "[]" + case last: "[" + last + ", []]" +} + +println arrayRecursive([1, 2, 3, 4, 5, 6, 7]) // [1, [2, [3, [4, [5, [6, [7, []]]]]]]] \ No newline at end of file diff --git a/docs/docs/code/basics/pattern_matching6.own b/docs/docs/code/basics/pattern_matching6.own new file mode 100644 index 00000000..0ff5e70a --- /dev/null +++ b/docs/docs/code/basics/pattern_matching6.own @@ -0,0 +1,8 @@ +for i = 1, i <= 100, i++ { + println match [i % 3 == 0, i % 5 == 0] { + case (true, false): "Fizz" + case (false, true): "Buzz" + case (true, true): "FizzBuzz" + case _: i + } +} \ No newline at end of file diff --git a/docs/docs/code/basics/string_functions1.own b/docs/docs/code/basics/string_functions1.own new file mode 100644 index 00000000..954d1714 --- /dev/null +++ b/docs/docs/code/basics/string_functions1.own @@ -0,0 +1,7 @@ +str = " ababcaab " +println indexOf(str, "abc") +println str.indexOf("abc") + +def isBlank(s) = s.trim().isEmpty() +println isBlank(str) +println str.isBlank() \ No newline at end of file diff --git a/docs/docs/code/functional_en.own b/docs/docs/code/functional_en.own new file mode 100644 index 00000000..9de63c2e --- /dev/null +++ b/docs/docs/code/functional_en.own @@ -0,0 +1,15 @@ +use std, functional + +nums = [1,2,3,4,5,6,7,8,9,10] +nums = filter(nums, def(x) = x % 2 == 0) +// Squares of even numbers +squares = map(nums, def(x) = x * x) +foreach(squares, ::echo) +// Sum of squares +sum = reduce(squares, 0, def(x, y) = x + y) +println "Sum: " + sum +// Same using stream +println "Sum: " + stream(range(1, 11)) + .filter(def(x) = x % 2 == 0) + .map(def(x) = x * x) + .reduce(0, def(x, y) = x + y) diff --git a/docs/docs/code/functional_ru.own b/docs/docs/code/functional_ru.own new file mode 100644 index 00000000..ef710b16 --- /dev/null +++ b/docs/docs/code/functional_ru.own @@ -0,0 +1,15 @@ +use std, functional + +nums = [1,2,3,4,5,6,7,8,9,10] +nums = filter(nums, def(x) = x % 2 == 0) +// Квадраты чётных чисел +squares = map(nums, def(x) = x * x) +foreach(squares, ::echo) +// Сумма квадратов +sum = reduce(squares, 0, def(x, y) = x + y) +println "Сумма: " + sum +// То же самое с использованием stream +println "Сумма: " + stream(range(1, 11)) + .filter(def(x) = x % 2 == 0) + .map(def(x) = x * x) + .reduce(0, def(x, y) = x + y) diff --git a/docs/docs/code/high_order_functions_en.own b/docs/docs/code/high_order_functions_en.own new file mode 100644 index 00000000..a6c24356 --- /dev/null +++ b/docs/docs/code/high_order_functions_en.own @@ -0,0 +1,14 @@ +operations = { + "+" : def(a,b) = a+b, + "-" : def(a,b) = a-b, + "*" : def(a,b) = a*b, + "/" : ::division +} +def division(v1, v2) { + if (v2 == 0) return "error: division by zero" + return v1 / v2 +} + +for operation : operations { + println operation(2, 3) +} \ No newline at end of file diff --git a/docs/docs/code/high_order_functions_ru.own b/docs/docs/code/high_order_functions_ru.own new file mode 100644 index 00000000..f3abd153 --- /dev/null +++ b/docs/docs/code/high_order_functions_ru.own @@ -0,0 +1,14 @@ +operations = { + "+" : def(a,b) = a+b, + "-" : def(a,b) = a-b, + "*" : def(a,b) = a*b, + "/" : ::division +} +def division(v1, v2) { + if (v2 == 0) return "ошибка: деление на ноль" + return v1 / v2 +} + +for operation : operations { + println operation(2, 3) +} \ No newline at end of file diff --git a/docs/docs/code/http_en.own b/docs/docs/code/http_en.own new file mode 100644 index 00000000..38e283b1 --- /dev/null +++ b/docs/docs/code/http_en.own @@ -0,0 +1,20 @@ +use std, http, functional + +// GET request +http("https://api.github.com/events", def(r) { + use json + events = jsondecode(r) +}) + +// POST request +http("http://jsonplaceholder.typicode.com/users", "POST", { + "name": "OwnLang", + "versionCode": 10 +}, ::echo) + +// PATCH request +http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Patched Name"}, ::patch_callback) + +def patch_callback(v) { + println v +} diff --git a/docs/docs/code/http_ru.own b/docs/docs/code/http_ru.own new file mode 100644 index 00000000..abc3f799 --- /dev/null +++ b/docs/docs/code/http_ru.own @@ -0,0 +1,20 @@ +use std, http, functional + +// GET-запрос +http("https://api.github.com/events", def(r) { + use json + events = jsondecode(r) +}) + +// POST-запрос +http("http://jsonplaceholder.typicode.com/users", "POST", { + "name": "OwnLang", + "versionCode": 10 +}, ::echo) + +// PATCH-запрос +http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Патч"}, ::patch_callback) + +def patch_callback(v) { + println v +} diff --git a/docs/docs/code/operator_overloading.own b/docs/docs/code/operator_overloading.own new file mode 100644 index 00000000..d823b227 --- /dev/null +++ b/docs/docs/code/operator_overloading.own @@ -0,0 +1,7 @@ +use std, types, math + +def `..`(a, b) = range(a, b - 1) +def `**`(a, b) = int(pow(a, b)) +for y : 1 .. 10 { + println sprintf("2 ^ %d = %d", y, 2 ** y) +} \ No newline at end of file diff --git a/docs/docs/code/pattern_matching.own b/docs/docs/code/pattern_matching.own new file mode 100644 index 00000000..6beb3e85 --- /dev/null +++ b/docs/docs/code/pattern_matching.own @@ -0,0 +1,16 @@ +def factorial(n) = match n { + case 0: 1 + case n if n < 0: 0 + case _: n * factorial(n - 1) +} + +def fizzbuzz(limit = 100) { + for i = 1, i <= limit, i++ { + println match [i % 3 == 0, i % 5 == 0] { + case (true, false): "Fizz" + case (false, true): "Buzz" + case (true, true): "FizzBuzz" + case _: i + } + } +} \ No newline at end of file diff --git a/docs/docs/en/README.md b/docs/docs/en/README.md new file mode 100644 index 00000000..7b4b5931 --- /dev/null +++ b/docs/docs/en/README.md @@ -0,0 +1,35 @@ +# Overview + +OwnLang — dynamic functional programming language inspired by Scala and Python. Available for PC and Android devices. + +## Key features + +### Higher-order functions + +Functions are values, so we can store them to variables for operating. + +@[code](../code/high_order_functions_en.own) + +### Pattern Matching + +Pattern matching with value pattern, tuple pattern, list pattern and optional condition. + +@[code](../code/pattern_matching.own) + +### Functional data operations + +Operate data in functional style. + +@[code](../code/functional_en.own) + +### Operator overloading + +Why not? + +@[code](../code/operator_overloading.own) + +### Network module + +Easy async HTTP requests with `http` module. + +@[code](../code/http_en.own) \ No newline at end of file diff --git a/docs/docs/en/basics/README.md b/docs/docs/en/basics/README.md new file mode 100644 index 00000000..b68cc02f --- /dev/null +++ b/docs/docs/en/basics/README.md @@ -0,0 +1,11 @@ +# Basics + +* [Comments](comments.md) +* [Strings](strings.md) +* [Types](types.md) +* [Loops](loops.md) +* [Functions definition](functions.md) +* [Destructuring assignment](destructuring_assignment.md) +* [Pattern matching](pattern_matching.md) +* [String functions](string_functions.md) +* [Array functions](array_functions.md) diff --git a/docs/docs/en/basics/array_functions.md b/docs/docs/en/basics/array_functions.md new file mode 100644 index 00000000..16e827dc --- /dev/null +++ b/docs/docs/en/basics/array_functions.md @@ -0,0 +1,8 @@ +# Array functions + +Fields: + - `length` - number of elements of the array + +Functions: + - `isEmpty()` - returns true, if the array is empty + - `joinToString(delimiter = "", prefix = "", suffix = "")` - joins array into a string diff --git a/docs/docs/en/basics/comments.md b/docs/docs/en/basics/comments.md new file mode 100644 index 00000000..01eaa68b --- /dev/null +++ b/docs/docs/en/basics/comments.md @@ -0,0 +1,9 @@ +# Comments + +```own +// Line comment +/* multiline + comment +*/ +print /*inner comment*/ "Text" +``` \ No newline at end of file diff --git a/docs/docs/en/basics/destructuring_assignment.md b/docs/docs/en/basics/destructuring_assignment.md new file mode 100644 index 00000000..fca2558b --- /dev/null +++ b/docs/docs/en/basics/destructuring_assignment.md @@ -0,0 +1,19 @@ +# Destructuring assignment + +Destructuring assignment allows to define multiple variables for each element of an array or map. + +For arrays, value is assigned to variable: + +@[code](../../code/basics/destructuring_assignment1.own) + +Which is equivalent to: + +@[code](../../code/basics/destructuring_assignment2.own) + +For maps, key and value are assigned to variable: + +@[code](../../code/basics/destructuring_assignment3.own) + +To skip value just leave argument empty: + +@[code](../../code/basics/destructuring_assignment4.own) diff --git a/docs/docs/en/basics/functions.md b/docs/docs/en/basics/functions.md new file mode 100644 index 00000000..b12be6e2 --- /dev/null +++ b/docs/docs/en/basics/functions.md @@ -0,0 +1,54 @@ +# Functions definition + +To define function uses the `def` keyword: + +```own +def function(arg1, arg2) { + println arg1 +} +``` + +## Shorthand definition + +There is short syntax fot function body: + +```own +def repeat(str, count) = str * count +``` + +Which is equivalent to: + +```own +def repeat(str, count) { + return str * count +} +``` + +## Default arguments + +Function arguments can have default values. + +```own +def repeat(str, count = 5) = str * count +``` + +In this case only `str` argument is required. + +```own +repeat("*") // ***** +repeat("+", 3) // +++ +``` + +Default arguments can't be declared before required arguments. + +```own +def repeat(str = "*", count) = str * count +``` + +Causes parsing error: `ParseError on line 1: Required argument cannot be after optional` + +## Inner functions + +You can define function in other function. + +@[code](../../code/basics/fibonacci.own) diff --git a/docs/docs/en/basics/loops.md b/docs/docs/en/basics/loops.md new file mode 100644 index 00000000..ace5bff0 --- /dev/null +++ b/docs/docs/en/basics/loops.md @@ -0,0 +1,113 @@ +# Loops + +## while loop + +```own +while condition { + body +} +``` + +Parentheses in condition are not necessary. + +```own +i = 0 +while i < 5 { + print i++ +} + +// or + +i = 0 +while (i < 5) { + print i++ +} +``` + +## do-while loop + +```own +do { + body +} while condition +``` + +Parentheses in condition are not necessary. + +```own +i = 0 +do { + print i++ +} while i < 5 + +// or + +i = 0 +do { + print i++ +} while (i < 5) +``` + +## for loop + +```own +for initializing, condition, increment { + body +} + +for (initializing, condition, increment) { + body +} +``` + +```own +for i = 0, i < 5, i++ + print i++ + +// or + +for (i = 0, i < 5, i++) { + print i++ +} +``` + +## foreach loop + +Iterates elements of an string, array or map. + +Iterating over string: + +```own +for char : string { + body +} +for char, code : string { + body +} +``` + +Iterating over array: + +```own +for value : array { + body +} +for value, index : array { + body +} +``` + +Iterating over map: + +```own +for key, value : map { + body +} +for (key, value : map) { + body +} +``` + +Parentheses are not necessary. + +@[code](../../code/basics/loops1.own) diff --git a/docs/docs/en/basics/pattern_matching.md b/docs/docs/en/basics/pattern_matching.md new file mode 100644 index 00000000..86d775cc --- /dev/null +++ b/docs/docs/en/basics/pattern_matching.md @@ -0,0 +1,79 @@ +# Pattern matching + +The `match` operator allows to match values by pattern. + +@[code](../../code/basics/pattern_matching1.own) + +@[code](../../code/basics/pattern_matching2.own) + +In this case value and type are checking. If none of `case` branches doesn't match, the body of `case _` branch will executes. + + +In addition to the constant values, you can set variable name to `case`. + +@[code](../../code/basics/pattern_matching3.own) + +In this case there is two scenarios: + +1. Variable is already defined. Matching to its value. +2. Variable is not defined. Assign matching value to it and executes body of the `case` branch. + +In the example above, the interpreter sees the first two branches as: + +```own +case 10: +case 20: +``` + +For the last branch `c` variable is not defined, so assign `c = x` and execute body of the `case c` branch. + + +## Refinements + +`case` branch may have additional comparison + +@[code](../../code/basics/pattern_matching4.own) + + +## Matching arrays + +To compare elements of arrays, the following syntax is used: + +* `case []:` executes if there are no elements in array +* `case [a]:` executes if an array contains one element +* `case [a :: b]:` executes if an array contains two or more elements +* `case [a :: b :: c :: d :: e]:` executes if an array contain five or more elements + +There are two rules for the last two cases: + +* If variables count matches array elements count - all variables are assigned to the value of the array. + +```own +match [0, 1, 2] { + case [x :: y :: z]: // x = 0, y = 1, z = 2 +} +``` + +* If array elements count is greater, then the rest of the array will be assigned to the last variable. + +```own +match [0, 1, 2, 3, 4] { + case [x :: y :: z]: // x = 0, y = 1, z = [2, 3, 4] +} +``` + +An example of a recursive output array + +@[code](../../code/basics/pattern_matching5.own) + + +## Matching array's value + +To compare values of array's elements, the following syntax is used: + +* `case (expr1, expr2, expr3):` executes if an array contain 3 elements and first element is equal to expr1 result, second element is equal to expr2 and third element is equal to expr3. +* `case (expr1, _):` executes if an array contain 2 elements and first element is equal to expr1 result and result of the second element is not importand. + +FizzBuzz classical problem can be solved using Pattern Matching: + +@[code](../../code/basics/pattern_matching6.own) diff --git a/docs/docs/en/basics/string_functions.md b/docs/docs/en/basics/string_functions.md new file mode 100644 index 00000000..070dcb63 --- /dev/null +++ b/docs/docs/en/basics/string_functions.md @@ -0,0 +1,20 @@ +# String functions + +Fields: + - `length` - string length + - `lower` - lower case string + - `upper` - upper case string + - `chars` - ASCII characters array + +Functions: + - `trim()` - removes any leading and trailing whitespaces in string + - `startsWith(str, offset = 0)` - checks whether the string starts with the substring str at offset + - `endsWith(str)` - checks whether the string ends with the str + - `matches(regex)` - checks whether the string matches regex pattern + - `contains(str)` - checks whether the string contains substring str + - `equalsIgnoreCase(str)` - checks equality of two strings ignore case (tEsT = TEST) + - `isEmpty()` - returns true, if the string is empty + +In addition, there are automatic function extensions available if the function accepts a string as the first argument: + +@[code](../../code/basics/string_functions1.own) diff --git a/docs/docs/en/basics/strings.md b/docs/docs/en/basics/strings.md new file mode 100644 index 00000000..d44a99bb --- /dev/null +++ b/docs/docs/en/basics/strings.md @@ -0,0 +1,12 @@ +# Strings + +Strings are defined in double quotes and can be multiline. Escaping Unicode characters is also supported.: + +```own +str = "\n\tThis is +\tmultiline +\ttext +" +``` + +`print` and `println` operators are used to output text. \ No newline at end of file diff --git a/docs/docs/en/basics/types.md b/docs/docs/en/basics/types.md new file mode 100644 index 00000000..ec4e3b29 --- /dev/null +++ b/docs/docs/en/basics/types.md @@ -0,0 +1,24 @@ +# Types + +OwnLang types are: + + * Number - numbers (integer, float) + * String - strings + * Array - arrays + * Map - objects (an associative arrays) + * Function - functions + +Since OwnLang is dynamic programming language, which means that explicitly declare the types is not necessary. + +```own +x = 10 // integer +y = 1.61803 // float +z = "abcd" // string +``` + +If some function requires string as argument, but number was passed, then numeric value will automatically converts to string. + +```own +x = 90 +print x // Ok, 90 converts to "90" +``` \ No newline at end of file diff --git a/docs/docs/en/links.md b/docs/docs/en/links.md new file mode 100644 index 00000000..345725c1 --- /dev/null +++ b/docs/docs/en/links.md @@ -0,0 +1,13 @@ +# Links + +## Downloads + +Android: [Free](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) / [Pro](https://play.google.com/store/apps/details?id=com.annimon.ownlang) +PC / Netbeans Plugin / etc: [GitHub Releases](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases) +Source code: [GitHub](https://github.com/aNNiMON/Own-Programming-Language-Tutorial) + +Also available as AUR package: + +``` +paru -S ownlang +``` diff --git a/docs/docs/ru/README.md b/docs/docs/ru/README.md new file mode 100644 index 00000000..0e7c2b2e --- /dev/null +++ b/docs/docs/ru/README.md @@ -0,0 +1,35 @@ +# Возможности + +OwnLang — скриптовый функциональный язык программирования с динамической типизацией для ПК и Android устройств. + +## Ключевые особенности + +### Функции высшего порядка + +Функции выступают как значения, а значит мы можем сохранять их в переменные для дальнейшего использования. + +@[code](../code/high_order_functions_ru.own) + +### Pattern Matching + +Сопоставление по образцу с шаблоном значений, шаблоном кортежей, шаблоном списков и дополнительным сравнением. + +@[code](../code/pattern_matching.own) + +### Функциональные операции над данными + +Оперирование данными в функциональном стиле. + +@[code](../code/functional_ru.own) + +### Перегрузка операторов + +Почему бы и нет? + +@[code](../code/operator_overloading.own) + +### Модуль для работы с сетью Интернет + +Простые асинхронные HTTP-запросы с модулем `http`. + +@[code](../code/http_ru.own) \ No newline at end of file diff --git a/docs/docs/ru/basics/README.md b/docs/docs/ru/basics/README.md new file mode 100644 index 00000000..0b843ddb --- /dev/null +++ b/docs/docs/ru/basics/README.md @@ -0,0 +1,11 @@ +# Синтаксис и основы языка + +* [Комментарии](comments.md) +* [Строки](strings.md) +* [Типы](types.md) +* [Циклы](loops.md) +* [Определение функций](functions.md) +* [Реструктуризующее присваивание](destructuring_assignment.md) +* [Pattern matching](pattern_matching.md) (сопоставление с образцом) +* [Функции строк](string_functions.md) +* [Функции массивов](array_functions.md) \ No newline at end of file diff --git a/docs/docs/ru/basics/array_functions.md b/docs/docs/ru/basics/array_functions.md new file mode 100644 index 00000000..cba7892e --- /dev/null +++ b/docs/docs/ru/basics/array_functions.md @@ -0,0 +1,8 @@ +# Функции массивов + +Поля: + - `length` - количество элементов массива + +Функции: + - `isEmpty()` - возвращает true, если массив пуст + - `joinToString(delimiter = "", prefix = "", suffix = "")` - склеивает массив в строку diff --git a/docs/docs/ru/basics/comments.md b/docs/docs/ru/basics/comments.md new file mode 100644 index 00000000..189f8405 --- /dev/null +++ b/docs/docs/ru/basics/comments.md @@ -0,0 +1,9 @@ +# Комментарии + +```own +// Однострочный комментарий +/* многострочный + комментарий +*/ +print /*или так*/ "Текст" +``` \ No newline at end of file diff --git a/docs/docs/ru/basics/destructuring_assignment.md b/docs/docs/ru/basics/destructuring_assignment.md new file mode 100644 index 00000000..b2b21cb3 --- /dev/null +++ b/docs/docs/ru/basics/destructuring_assignment.md @@ -0,0 +1,19 @@ +# Реструктуризующее присваивание + +Реструктуризующее (деструктивное) присваивание позволяет определить сразу несколько переменных по каждому элементу массива или объекта. + +Для массивов, переменным присваивается значение. + +@[code](../../code/basics/destructuring_assignment1.own) + +Что равносильно: + +@[code](../../code/basics/destructuring_assignment2.own) + +Для объектов, переменным присваивается массив [ключ, значение] + +@[code](../../code/basics/destructuring_assignment3.own) + +Если нужно пропустить какое-либо значение, название переменной можно не писать: + +@[code](../../code/basics/destructuring_assignment4.own) \ No newline at end of file diff --git a/docs/docs/ru/basics/functions.md b/docs/docs/ru/basics/functions.md new file mode 100644 index 00000000..a1cc0fc5 --- /dev/null +++ b/docs/docs/ru/basics/functions.md @@ -0,0 +1,54 @@ +# Определение функций + +Для определения функции используется ключевое слово `def`. Затем идёт имя, аргументы и тело функции. Пример: + +```own +def function(arg1, arg2) { + println arg1 +} +``` + +## Короткий синтаксис + +Возможен короткий синтаксис: + +```own +def repeat(str, count) = str * count +``` + +что равносильно: + +```own +def repeat(str, count) { + return str * count +} +``` + +## Аргументы по умолчанию + +Аргументы функции могут иметь значения по умолчанию. + +```own +def repeat(str, count = 5) = str * count +``` + +В этом случае обязательным будет только аргумент `str` + +```own +repeat("*") // ***** +repeat("+", 3) // +++ +``` + +Аргументы по умолчанию обязательно должны идти после обязательных аргументов, если такие были. + +```own +def repeat(str = "*", count) = str * count +``` + +Приведёт к ошибки парсинга: `ParseError on line 1: Required argument cannot be after optional` + +## Внутренние функции + +Внутри функции можно объявить другую функцию. + +@[code](../../code/basics/fibonacci.own) \ No newline at end of file diff --git a/docs/docs/ru/basics/loops.md b/docs/docs/ru/basics/loops.md new file mode 100644 index 00000000..fae20b42 --- /dev/null +++ b/docs/docs/ru/basics/loops.md @@ -0,0 +1,115 @@ +# Циклы + +## Цикл while + +```own +while условие { + тело цикла +} +``` + +Скобки в условии необязательны. + +```own +i = 0 +while i < 5 { + print i++ +} + +// или + +i = 0 +while (i < 5) { + print i++ +} +``` + +## Цикл do-while + +```own +do { + тело цикла +} while условие +``` + +Скобки в условии необязательны. + +```own +i = 0 +do { + print i++ +} while i < 5 + +// или + +i = 0 +do { + print i++ +} while (i < 5) +``` + +## Цикл for + +```own +for инициализация, условие_работы, инкремент { + тело цикла +} + +for (инициализация, условие_работы, инкремент) { + тело цикла +} +``` + +Скобки в условии необязательны. + +```own +for i = 0, i < 5, i++ + print i++ + +// или + +for (i = 0, i < 5, i++) { + print i++ +} +``` + +## Цикл foreach + +Перебирает элементы строки, массива или карты. + +Перебор строки: + +```own +for символ : строка { + тело цикла +} +for символ, код : строка { + тело цикла +} +``` + +Перебор массива: + +```own +for значение : массив { + тело цикла +} +for значение, индекс : массив { + тело цикла +} +for (значение : массив) { + тело цикла +} +``` + +Перебор карты: + +```own +for (ключ, значение : карта) { + тело цикла +} +``` + +Скобки необязательны. + +@[code](../../code/basics/loops1.own) \ No newline at end of file diff --git a/docs/docs/ru/basics/pattern_matching.md b/docs/docs/ru/basics/pattern_matching.md new file mode 100644 index 00000000..ab96bc37 --- /dev/null +++ b/docs/docs/ru/basics/pattern_matching.md @@ -0,0 +1,79 @@ +# Pattern Matching (сопоставление с образцом) + +Оператор `match` позволяет выполнить сопоставление значения с образцом. + +@[code](../../code/basics/pattern_matching1.own) + +@[code](../../code/basics/pattern_matching2.own) + +Проверяется тип и значение. Если ни одна из веток `case` не обнаружила совпадение, выполняется тело ветки `case _`. + + +Помимо константных значений, в `case` может присутствовать имя переменной. + +@[code](../../code/basics/pattern_matching3.own) + +В таком случае возможен один из двух сценариев: + +1. Переменная уже определена. Сравнивается её значение. +2. Переменная не определена. Ей присваивается сопоставляемое значение и выполняется ветка `case`. + +В примере выше, интерпретатор видит первые две ветки так: + +```own +case 10: +case 20: +``` + +Для последней ветки переменная `c` не определена, поэтому выполнится присваивание `c = x`, после чего вызов передаётся телу ветки `case c`. + + +## Уточнения + +Ветка `case` может иметь дополнительное сравнение + +@[code](../../code/basics/pattern_matching4.own) + + +## Сопоставление массивов + +Для сопоставления элементов массивов, в блоке case используется следующий синтаксис: + +* `case []:` выполняется, если в массиве нет элементов +* `case [a]:` выполняется, если в массиве есть один элемент +* `case [a :: b]:` выполняется, если в массиве есть два и более элементов +* `case [a :: b :: c :: d :: e]:` выполняется, если в массиве есть пять и более элементов + +Для двух последних случаев справедливы такие правила: + +* Если количество переменных в списке совпадает с количество элементов массива, то всем переменным присваивается значение массива. + +```own +match [0, 1, 2] { + case [x :: y :: z]: // x = 0, y = 1, z = 2 +} +``` + +* Если элементов массива больше, то в последней переменной будут сохранены оставшиеся элементы массива. + +```own +match [0, 1, 2, 3, 4] { + case [x :: y :: z]: // x = 0, y = 1, z = [2, 3, 4] +} +``` + +Пример рекурсивного вывода элементов массива + +@[code](../../code/basics/pattern_matching5.own) + + +## Сопоставление значений массивов + +Для сопоставления значений элементов массивов, используется синтаксис: + +* `case (expr1, expr2, expr3):` выполняется, если в массиве есть 3 элемента и первый элемент равен результату выражения expr1, второй - результату expr2 и третий - результату expr3. +* `case (expr1, _):` выполняется, если в массиве есть 2 элемента и первый элемент равен результату выражения expr1, а результат второго не важен. + +Классическая задача FizzBuzz может быть решена с использованием Pattern Matching: + +@[code](../../code/basics/pattern_matching6.own) diff --git a/docs/docs/ru/basics/string_functions.md b/docs/docs/ru/basics/string_functions.md new file mode 100644 index 00000000..17d1c9ed --- /dev/null +++ b/docs/docs/ru/basics/string_functions.md @@ -0,0 +1,20 @@ +# Функции строк + +Поля: + - `length` - длина строки + - `lower` - строка в нижнем регистре + - `upper` - строка в верхнем регистре + - `chars` - массив символов в виде ASCII-кодов + +Функции: + - `trim()` - обрезает пробельные невидимые символы по обоим концам строки + - `startsWith(str, offset = 0)` - проверяет, начинается ли строка с подстроки str в позиции offset + - `endsWith(str)` - проверяет, заканчивается ли строка подстрокой str + - `matches(regex)` - проверяет соответствие строки с заданным шаблоном + - `contains(str)` - проверяет, содержится ли в строке подстрока str + - `equalsIgnoreCase(str)` - проверяет, равны ли строки вне зависимости от регистра (tEsT = TEST) + - `isEmpty()` - возвращает true, если строка пустая + +Кроме того, доступны автоматические функции-расширения, если функция принимает в качестве первого аргумента строку: + +@[code](../../code/basics/string_functions1.own) diff --git a/docs/docs/ru/basics/strings.md b/docs/docs/ru/basics/strings.md new file mode 100644 index 00000000..8ad5fa99 --- /dev/null +++ b/docs/docs/ru/basics/strings.md @@ -0,0 +1,12 @@ +# Строки + +Строки задаются в двойных кавычках и могут быть многострочные. Поддерживается юникод и экранирование символов: + +```own +str = "\n\tЭто +\tмногострочный +\tтекст +" +``` + +Для вывода строк есть два оператора `print` и `println` \ No newline at end of file diff --git a/docs/docs/ru/basics/types.md b/docs/docs/ru/basics/types.md new file mode 100644 index 00000000..088844dc --- /dev/null +++ b/docs/docs/ru/basics/types.md @@ -0,0 +1,24 @@ +# Типы + +В OwnLang есть такие типы: + + * Number - числа (охватывает как целые, так и вещественные числа) + * String - строки + * Array - массивы + * Map - объекты (ассоциативные массивы) + * Function - функции + +Поскольку OwnLang - динамически типизируемый язык программирования, это значит, что явно объявлять типы не нужно. + +```own +x = 10 // целое число +y = 1.61803 // вещественное число +z = "abcd" // строка +``` + +Если какая-либо функция предполагает использование строк в качестве аргументов, а были переданы числа, то значения автоматически приведутся к строке. + +```own +x = 90 +print x +``` \ No newline at end of file diff --git a/docs/docs/ru/links.md b/docs/docs/ru/links.md new file mode 100644 index 00000000..60d91f40 --- /dev/null +++ b/docs/docs/ru/links.md @@ -0,0 +1,13 @@ +# Ссылки + +## Загрузки + +Android: [Free](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) / [Pro](https://play.google.com/store/apps/details?id=com.annimon.ownlang) +PC / плагин Netbeans / прочее: [GitHub Releases](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases) +Исходный код: [GitHub](https://github.com/aNNiMON/Own-Programming-Language-Tutorial) + +Также доступно в виде пакета в AUR: + +``` +paru -S ownlang +``` diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..a0c46b2a --- /dev/null +++ b/docs/package.json @@ -0,0 +1,20 @@ +{ + "name": "ownlang-docs", + "version": "1.0.0", + "description": "OwnLang Documentation", + "main": "index.js", + "scripts": { + "docs:dev": "vuepress dev docs", + "docs:build": "vuepress build docs" + }, + "keywords": ["documentation", "ownlang", "programming-language"], + "author": "aNNiMON", + "license": "MIT", + "devDependencies": { + "prismjs": "^1.29.0", + "@vuepress/client": "2.0.0-beta.68", + "@vuepress/plugin-prismjs": "2.0.0-beta.68", + "vue": "^3.3.8", + "vuepress": "2.0.0-beta.68" + } +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 00000000..ab624755 --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,1780 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@vuepress/client': + specifier: 2.0.0-beta.68 + version: 2.0.0-beta.68 + '@vuepress/plugin-prismjs': + specifier: 2.0.0-beta.68 + version: 2.0.0-beta.68 + prismjs: + specifier: ^1.29.0 + version: 1.29.0 + vue: + specifier: ^3.3.8 + version: 3.3.8 + vuepress: + specifier: 2.0.0-beta.68 + version: 2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8) + +packages: + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/parser@7.23.3: + resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.3 + dev: true + + /@babel/types@7.23.3: + resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@mdit-vue/plugin-component@1.0.0: + resolution: {integrity: sha512-ZXsJwxkG5yyTHARIYbR74cT4AZ0SfMokFFjiHYCbypHIeYWgJhso4+CZ8+3V9EWFG3EHlGoKNGqKp9chHnqntQ==} + dependencies: + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/plugin-frontmatter@1.0.0: + resolution: {integrity: sha512-MMA7Ny+YPZA7eDOY1t4E+rKuEWO39mzDdP/M68fKdXJU6VfcGkPr7gnpnJfW2QBJ5qIvMrK/3lDAA2JBy5TfpA==} + dependencies: + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + gray-matter: 4.0.3 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/plugin-headers@1.0.0: + resolution: {integrity: sha512-0rK/iKy6x13d/Pp5XxdLBshTD0+YjZvtHIaIV+JO+/H2WnOv7oaRgs48G5d44z3XJVUE2u6fNnTlI169fef0/A==} + dependencies: + '@mdit-vue/shared': 1.0.0 + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/plugin-sfc@1.0.0: + resolution: {integrity: sha512-agMUe0fY4YHxsZivSvplBwRwrFvsIf/JNUJCAYq1+2Sg9+2hviTBZwjZDxYqHDHOVLtiNr+wuo68tE24mAx3AQ==} + dependencies: + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/plugin-title@1.0.0: + resolution: {integrity: sha512-8yC60fCZ95xcJ/cvJH4Lv43Rs4k+33UGyKrRWj5J8TNyMwUyGcwur0XyPM+ffJH4/Bzq4myZLsj/TTFSkXRxvw==} + dependencies: + '@mdit-vue/shared': 1.0.0 + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/plugin-toc@1.0.0: + resolution: {integrity: sha512-WN8blfX0X/5Nolic0ClDWP7eVo9IB+U4g0jbycX3lolIZX5Bai1UpsD3QYZr5VVsPbQJMKMGvTrCEtCNTGvyWQ==} + dependencies: + '@mdit-vue/shared': 1.0.0 + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/shared@1.0.0: + resolution: {integrity: sha512-nbYBfmEi+pR2Lm0Z6TMVX2/iBjfr/kGEsHW8CC0rQw+3+sG5dY6VG094HuFAkiAmmvZx9DZZb+7ZMWp9vkwCRw==} + dependencies: + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /@mdit-vue/types@1.0.0: + resolution: {integrity: sha512-xeF5+sHLzRNF7plbksywKCph4qli20l72of2fMlZQQ7RECvXYrRkE9+bjRFQCyULC7B8ydUYbpbkux5xJlVWyw==} + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@sindresorhus/merge-streams@1.0.0: + resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==} + engines: {node: '>=18'} + dev: true + + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + dependencies: + '@types/ms': 0.7.34 + dev: true + + /@types/fs-extra@11.0.4: + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 20.9.0 + dev: true + + /@types/hash-sum@1.0.2: + resolution: {integrity: sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==} + dev: true + + /@types/jsonfile@6.1.4: + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + dependencies: + '@types/node': 20.9.0 + dev: true + + /@types/linkify-it@3.0.5: + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + dev: true + + /@types/markdown-it-emoji@2.0.4: + resolution: {integrity: sha512-H6ulk/ZmbDxOayPwI/leJzrmoW1YKX1Z+MVSCHXuYhvqckV4I/c+hPTf6UiqJyn2avWugfj30XroheEb6/Ekqg==} + dependencies: + '@types/markdown-it': 13.0.6 + dev: true + + /@types/markdown-it@13.0.6: + resolution: {integrity: sha512-0VqpvusJn1/lwRegCxcHVdmLfF+wIsprsKMC9xW8UPcTxhFcQtoN/fBU1zMe8pH7D/RuueMh2CaBaNv+GrLqTw==} + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + dev: true + + /@types/mdurl@1.0.5: + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + dev: true + + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + dev: true + + /@types/node@20.9.0: + resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/web-bluetooth@0.0.20: + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + dev: true + + /@vitejs/plugin-vue@4.4.1(vite@4.5.0)(vue@3.3.8): + resolution: {integrity: sha512-HCQG8VDFDM7YDAdcj5QI5DvUi+r6xvo9LgvYdk7LSkUNwdpempdB5horkMSZsbdey9Ywsf5aaU8kEPw9M5kREA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 + vue: ^3.2.25 + dependencies: + vite: 4.5.0 + vue: 3.3.8 + dev: true + + /@vue/compiler-core@3.3.8: + resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==} + dependencies: + '@babel/parser': 7.23.3 + '@vue/shared': 3.3.8 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-dom@3.3.8: + resolution: {integrity: sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==} + dependencies: + '@vue/compiler-core': 3.3.8 + '@vue/shared': 3.3.8 + dev: true + + /@vue/compiler-sfc@3.3.8: + resolution: {integrity: sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==} + dependencies: + '@babel/parser': 7.23.3 + '@vue/compiler-core': 3.3.8 + '@vue/compiler-dom': 3.3.8 + '@vue/compiler-ssr': 3.3.8 + '@vue/reactivity-transform': 3.3.8 + '@vue/shared': 3.3.8 + estree-walker: 2.0.2 + magic-string: 0.30.5 + postcss: 8.4.31 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-ssr@3.3.8: + resolution: {integrity: sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==} + dependencies: + '@vue/compiler-dom': 3.3.8 + '@vue/shared': 3.3.8 + dev: true + + /@vue/devtools-api@6.5.1: + resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} + dev: true + + /@vue/reactivity-transform@3.3.8: + resolution: {integrity: sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==} + dependencies: + '@babel/parser': 7.23.3 + '@vue/compiler-core': 3.3.8 + '@vue/shared': 3.3.8 + estree-walker: 2.0.2 + magic-string: 0.30.5 + dev: true + + /@vue/reactivity@3.3.8: + resolution: {integrity: sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==} + dependencies: + '@vue/shared': 3.3.8 + dev: true + + /@vue/runtime-core@3.3.8: + resolution: {integrity: sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==} + dependencies: + '@vue/reactivity': 3.3.8 + '@vue/shared': 3.3.8 + dev: true + + /@vue/runtime-dom@3.3.8: + resolution: {integrity: sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==} + dependencies: + '@vue/runtime-core': 3.3.8 + '@vue/shared': 3.3.8 + csstype: 3.1.2 + dev: true + + /@vue/server-renderer@3.3.8(vue@3.3.8): + resolution: {integrity: sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==} + peerDependencies: + vue: 3.3.8 + dependencies: + '@vue/compiler-ssr': 3.3.8 + '@vue/shared': 3.3.8 + vue: 3.3.8 + dev: true + + /@vue/shared@3.3.8: + resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==} + dev: true + + /@vuepress/bundler-vite@2.0.0-beta.68: + resolution: {integrity: sha512-N2grrjKIQEZJcb+JaG7ZimCoUH3bRK4zwHjPOLpBpplTQ/V5l99I90FMswpaCs7bKBiXTO0fiEUYn4Nw8V/qkQ==} + dependencies: + '@vitejs/plugin-vue': 4.4.1(vite@4.5.0)(vue@3.3.8) + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + autoprefixer: 10.4.16(postcss@8.4.31) + connect-history-api-fallback: 2.0.0 + postcss: 8.4.31 + postcss-load-config: 4.0.1(postcss@8.4.31) + rollup: 3.29.4 + vite: 4.5.0 + vue: 3.3.8 + vue-router: 4.2.5(vue@3.3.8) + transitivePeerDependencies: + - '@types/node' + - '@vue/composition-api' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + - ts-node + - typescript + dev: true + + /@vuepress/cli@2.0.0-beta.68: + resolution: {integrity: sha512-Br0aaJIWBtKjXBMmulLcN5hFOx8kbVHgs8K+EOASC9fLrq6LolsUJIdAiR+KyeMwQMyRInKrF3SF7k7AJetVeQ==} + hasBin: true + dependencies: + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + cac: 6.7.14 + chokidar: 3.5.3 + envinfo: 7.11.0 + esbuild: 0.18.20 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/client@2.0.0-beta.68: + resolution: {integrity: sha512-Y6amMnkPxpmn51vcgy5yzm3gpIaqZo4Pa8ItPFd7MW6GQy6HVZRNaV9ufzWRPOAedLHgpT4aVXomidvTMEKHVw==} + dependencies: + '@vue/devtools-api': 6.5.1 + '@vuepress/shared': 2.0.0-beta.68 + '@vueuse/core': 10.6.0(vue@3.3.8) + vue: 3.3.8 + vue-router: 4.2.5(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - typescript + dev: true + + /@vuepress/core@2.0.0-beta.68: + resolution: {integrity: sha512-/c+3gdduDyiyeGARzui6Z5ZeZurRGcbVSmqcUfb8SjB7sHojDt+bq/7gYeXKXrJ4R0zPpmqshlZdNGOSY4+uGQ==} + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/markdown': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + vue: 3.3.8 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/markdown@2.0.0-beta.68: + resolution: {integrity: sha512-wQOVw1QQSnkdKClTnv3dHw1A7Y+XF2eu2hJmhTf9XOnEMxQ9taacIq5iRuQdcfR+Y8rjWmrzrqWZL+MiJbxKMQ==} + dependencies: + '@mdit-vue/plugin-component': 1.0.0 + '@mdit-vue/plugin-frontmatter': 1.0.0 + '@mdit-vue/plugin-headers': 1.0.0 + '@mdit-vue/plugin-sfc': 1.0.0 + '@mdit-vue/plugin-title': 1.0.0 + '@mdit-vue/plugin-toc': 1.0.0 + '@mdit-vue/shared': 1.0.0 + '@mdit-vue/types': 1.0.0 + '@types/markdown-it': 13.0.6 + '@types/markdown-it-emoji': 2.0.4 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + markdown-it: 13.0.2 + markdown-it-anchor: 8.6.7(@types/markdown-it@13.0.6)(markdown-it@13.0.2) + markdown-it-emoji: 2.0.2 + mdurl: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@vuepress/plugin-active-header-links@2.0.0-beta.68: + resolution: {integrity: sha512-yMOvnzYrzZ70hCPWXlPrm6nU8q8MvrfhLf3R007ino7TWhlumTioYEnXKX3TH5+us1QM3W/CI+LUyr1si6leGg==} + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + ts-debounce: 4.0.0 + vue: 3.3.8 + vue-router: 4.2.5(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-back-to-top@2.0.0-beta.68: + resolution: {integrity: sha512-YobSlJUltm+zzTgJttmU1iDI0qUotRMl7TXnutAqJ7FTsPBUVrLQsXpfSEJnwwBbZ99VHTAF5FvXwtlZRuoLNg==} + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + ts-debounce: 4.0.0 + vue: 3.3.8 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-container@2.0.0-beta.68: + resolution: {integrity: sha512-oRGO9B9KgT9ZqjOcxBdTZI7eeI80qzYOYy8BGA+tYeKVy2AaLQk7GsUm3mMQn6Z82AdqBtRag/eUUiMo3p6toA==} + dependencies: + '@types/markdown-it': 13.0.6 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/markdown': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + markdown-it: 13.0.2 + markdown-it-container: 3.0.0 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-external-link-icon@2.0.0-beta.68: + resolution: {integrity: sha512-6oHeD0HT8SsFMxaKYEfc35Qx4vlJivJXbdZr/pcYbAEDSv3eORKrVnY9yZk5i6aTI/sxeTyEmoahqdcx+6uV6w==} + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/markdown': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + vue: 3.3.8 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-git@2.0.0-beta.68: + resolution: {integrity: sha512-L3F5fMu0zVzl90xlZBjoHJSCHdGFfWGs624xcC66QKeFXU6xVt7lMB4wyuPYfi6opCSfDwigmVYcJOsMmbCdBg==} + dependencies: + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + execa: 8.0.1 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-medium-zoom@2.0.0-beta.68: + resolution: {integrity: sha512-2bnxcvNQM+i9b5cDDgzitfyLawssPzcxVVOcscUozhvNSkiVre6aCVjStmLk9uWpsFPhtkWawdXCFYpCdBF7Ug==} + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + medium-zoom: 1.0.8 + vue: 3.3.8 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-nprogress@2.0.0-beta.68: + resolution: {integrity: sha512-72yFcUIaON4YUwjf/6qK1DkZcGnZAST/At2t2/esUVm3XOEPxqz2HwKYfU89Rp/+VZfTp0Nn8W4kXuLcC+V0KA==} + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + vue: 3.3.8 + vue-router: 4.2.5(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-palette@2.0.0-beta.68: + resolution: {integrity: sha512-LILoXCY9NMi+doNz09HiUeNiElJy6ECbR/yodOBp+jcwGZ4RVPFp8PEeK3jCZ8+UuJxa1mmmi6dqTxp02xrAFQ==} + dependencies: + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + chokidar: 3.5.3 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-prismjs@2.0.0-beta.68: + resolution: {integrity: sha512-IARRHzZ2XeLQPfelimqU/eexoItnwnz6z4tSkTIrV4PQeWg6EjMc92TxHyE+EEWAcka/DZxd42+xq0QV7FSJJQ==} + dependencies: + '@vuepress/core': 2.0.0-beta.68 + prismjs: 1.29.0 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-theme-data@2.0.0-beta.68: + resolution: {integrity: sha512-UFiMxJAD20mOK29P1H8zoHFNeDVer+2goQ9qy/VjDAbzE2I2yOa6TbJ7fWhSO8Vq0dCy7cX92wZMhXQIyUeNgQ==} + dependencies: + '@vue/devtools-api': 6.5.1 + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + vue: 3.3.8 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/shared@2.0.0-beta.68: + resolution: {integrity: sha512-vnlOOchZ7ZHeTQuFDKcTC1AKF5zl4+XKwZZdpX9cUkIl3rYbM4y80yoWvfG5SQnPjjoYG57g4Qz21Fa8u/CnCQ==} + dependencies: + '@mdit-vue/types': 1.0.0 + '@vue/shared': 3.3.8 + dev: true + + /@vuepress/theme-default@2.0.0-beta.68: + resolution: {integrity: sha512-qsIaM3ZVjJb6KeuScxVRLfLylBa3kK7IriZ9YlmkHl2NwzspsqVMTh4Ozd2MlkhzMH4TnB1XLybTQKGCZnQBVw==} + peerDependencies: + sass-loader: ^13.2.1 + peerDependenciesMeta: + sass-loader: + optional: true + dependencies: + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/plugin-active-header-links': 2.0.0-beta.68 + '@vuepress/plugin-back-to-top': 2.0.0-beta.68 + '@vuepress/plugin-container': 2.0.0-beta.68 + '@vuepress/plugin-external-link-icon': 2.0.0-beta.68 + '@vuepress/plugin-git': 2.0.0-beta.68 + '@vuepress/plugin-medium-zoom': 2.0.0-beta.68 + '@vuepress/plugin-nprogress': 2.0.0-beta.68 + '@vuepress/plugin-palette': 2.0.0-beta.68 + '@vuepress/plugin-prismjs': 2.0.0-beta.68 + '@vuepress/plugin-theme-data': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/utils': 2.0.0-beta.68 + '@vueuse/core': 10.6.0(vue@3.3.8) + sass: 1.69.5 + vue: 3.3.8 + vue-router: 4.2.5(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/utils@2.0.0-beta.68: + resolution: {integrity: sha512-asRN+c8JCIVJWusP/V0FY8rgArGwuKXarEIKwFHcaR7x9IeB3Iww4p8raQHb1xYJADM7QFXx1gs2oM6Fx4XsUw==} + dependencies: + '@types/debug': 4.1.12 + '@types/fs-extra': 11.0.4 + '@types/hash-sum': 1.0.2 + '@vuepress/shared': 2.0.0-beta.68 + debug: 4.3.4 + fs-extra: 11.1.1 + globby: 14.0.0 + hash-sum: 2.0.0 + ora: 7.0.1 + picocolors: 1.0.0 + upath: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@vueuse/core@10.6.0(vue@3.3.8): + resolution: {integrity: sha512-+Yee+g9+9BEbvkyGdn4Bf4yZx9EfocAytpV2ZlrlP7xcz+qznLmZIDqDroTvc5vtMkWZicisgEv8dt3+jL+HQg==} + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 10.6.0 + '@vueuse/shared': 10.6.0(vue@3.3.8) + vue-demi: 0.14.6(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: true + + /@vueuse/metadata@10.6.0: + resolution: {integrity: sha512-mzKHkHoiK6xVz01VzQjM2l6ofUanEaofgEGPgDHcAzlvOTccPRTIdEuzneOUTYxgfm1vkDikS6rtrEw/NYlaTQ==} + dev: true + + /@vueuse/shared@10.6.0(vue@3.3.8): + resolution: {integrity: sha512-0t4MVE18sO+/4Gh0jfeOXBTjKeV4606N9kIrDOLPjFl8Rwnlodn+QC5A4LfJuysK7aOsTMjF3KnzNeueaI0xlQ==} + dependencies: + vue-demi: 0.14.6(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /autoprefixer@10.4.16(postcss@8.4.31): + resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.22.1 + caniuse-lite: 1.0.30001561 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.31 + postcss-value-parser: 4.2.0 + dev: true + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bl@5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001561 + electron-to-chromium: 1.4.581 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true + + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /caniuse-lite@1.0.30001561: + resolution: {integrity: sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==} + dev: true + + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: true + + /cli-spinners@2.9.1: + resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} + engines: {node: '>=6'} + dev: true + + /connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /electron-to-chromium@1.4.581: + resolution: {integrity: sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==} + dev: true + + /emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + dev: true + + /entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + dev: true + + /envinfo@7.11.0: + resolution: {integrity: sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + + /extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: true + + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /globby@14.0.0: + resolution: {integrity: sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==} + engines: {node: '>=18'} + dependencies: + '@sindresorhus/merge-streams': 1.0.0 + fast-glob: 3.3.2 + ignore: 5.2.4 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + dev: true + + /hash-sum@2.0.0: + resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} + dev: true + + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /immutable@4.3.4: + resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /linkify-it@4.0.1: + resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} + dependencies: + uc.micro: 1.0.6 + dev: true + + /log-symbols@5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.3.0 + is-unicode-supported: 1.3.0 + dev: true + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /markdown-it-anchor@8.6.7(@types/markdown-it@13.0.6)(markdown-it@13.0.2): + resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} + peerDependencies: + '@types/markdown-it': '*' + markdown-it: '*' + dependencies: + '@types/markdown-it': 13.0.6 + markdown-it: 13.0.2 + dev: true + + /markdown-it-container@3.0.0: + resolution: {integrity: sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw==} + dev: true + + /markdown-it-emoji@2.0.2: + resolution: {integrity: sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==} + dev: true + + /markdown-it@13.0.2: + resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 3.0.1 + linkify-it: 4.0.1 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: true + + /mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + dev: true + + /medium-zoom@1.0.8: + resolution: {integrity: sha512-CjFVuFq/IfrdqesAXfg+hzlDKu6A2n80ZIq0Kl9kWjoHh9j1N9Uvk5X0/MmN0hOfm5F9YBswlClhcwnmtwz7gA==} + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /ora@7.0.1: + resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==} + engines: {node: '>=16'} + dependencies: + chalk: 5.3.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.1 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + stdin-discarder: 0.1.0 + string-width: 6.1.0 + strip-ansi: 7.1.0 + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /postcss-load-config@4.0.1(postcss@8.4.31): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.31 + yaml: 2.3.4 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /sass@1.69.5: + resolution: {integrity: sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.3.4 + source-map-js: 1.0.2 + dev: true + + /section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stdin-discarder@0.1.0: + resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + dev: true + + /string-width@6.1.0: + resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} + engines: {node: '>=16'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 10.3.0 + strip-ansi: 7.1.0 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-debounce@4.0.0: + resolution: {integrity: sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==} + dev: true + + /uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + dev: true + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: true + + /upath@2.0.1: + resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} + engines: {node: '>=4'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite@4.5.0: + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vue-demi@0.14.6(vue@3.3.8): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.8 + dev: true + + /vue-router@4.2.5(vue@3.3.8): + resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@vue/devtools-api': 6.5.1 + vue: 3.3.8 + dev: true + + /vue@3.3.8: + resolution: {integrity: sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@vue/compiler-dom': 3.3.8 + '@vue/compiler-sfc': 3.3.8 + '@vue/runtime-dom': 3.3.8 + '@vue/server-renderer': 3.3.8(vue@3.3.8) + '@vue/shared': 3.3.8 + dev: true + + /vuepress-vite@2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8): + resolution: {integrity: sha512-/+qO1uO8EX6ERFFgaWqKNW/nD89JrmS9sgMYxREPM0lsWj1E+bgkvc8KJ6DIgWRc1lRs0kMrEhK3UHC0md8ESQ==} + engines: {node: '>=18.16.0'} + hasBin: true + peerDependencies: + '@vuepress/client': 2.0.0-beta.68 + vue: ^3.3.4 + dependencies: + '@vuepress/bundler-vite': 2.0.0-beta.68 + '@vuepress/cli': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-beta.68 + '@vuepress/theme-default': 2.0.0-beta.68 + vue: 3.3.8 + transitivePeerDependencies: + - '@types/node' + - '@vue/composition-api' + - less + - lightningcss + - sass + - sass-loader + - stylus + - sugarss + - supports-color + - terser + - ts-node + - typescript + dev: true + + /vuepress@2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8): + resolution: {integrity: sha512-75naWJMIwyD1WiVswN01Am4JwcRxlUPLTkxN/345dVaVAgKyzIDKKJDgmUN6MKZkDN2z2dDKLMcquI6VUrlCRg==} + engines: {node: '>=18.16.0'} + hasBin: true + dependencies: + vuepress-vite: 2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8) + transitivePeerDependencies: + - '@types/node' + - '@vue/composition-api' + - '@vuepress/client' + - less + - lightningcss + - sass + - sass-loader + - stylus + - sugarss + - supports-color + - terser + - ts-node + - typescript + - vue + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /yaml@2.3.4: + resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} + engines: {node: '>= 14'} + dev: true From c76ef4a8cc817f7a629cffd6439a07d97ecc509a Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 16 Nov 2023 23:47:27 +0200 Subject: [PATCH 367/448] Split modules.yml documentation per module --- docs/modules.yml | 6742 ------------------ docs/src/modules/android.yml | 62 + docs/src/modules/base64.yml | 24 + docs/src/modules/canvas.yml | 98 + docs/src/modules/canvas_android.yml | 658 ++ docs/src/modules/canvasfx.yml | 417 ++ docs/src/modules/date.yml | 107 + docs/src/modules/downloader.yml | 24 + docs/src/modules/files.yml | 288 + docs/src/modules/forms.yml | 89 + docs/src/modules/forms_android.yml | 2015 ++++++ docs/src/modules/functional.yml | 161 + docs/src/modules/gps_android.yml | 45 + docs/src/modules/gzip.yml | 43 + docs/src/modules/http.yml | 82 + docs/src/modules/imageprocessing_android.yml | 109 + docs/src/modules/java.yml | 253 + docs/src/modules/jdbc.yml | 685 ++ docs/src/modules/json.yml | 25 + docs/src/modules/math.yml | 162 + docs/src/modules/ounit.yml | 60 + docs/src/modules/regex.yml | 147 + docs/src/modules/robot.yml | 139 + docs/src/modules/socket.yml | 161 + docs/src/modules/std.yml | 290 + docs/src/modules/types.yml | 80 + docs/src/modules/yaml.yml | 14 + docs/src/modules/zip.yml | 98 + 28 files changed, 6336 insertions(+), 6742 deletions(-) delete mode 100644 docs/modules.yml create mode 100644 docs/src/modules/android.yml create mode 100644 docs/src/modules/base64.yml create mode 100644 docs/src/modules/canvas.yml create mode 100644 docs/src/modules/canvas_android.yml create mode 100644 docs/src/modules/canvasfx.yml create mode 100644 docs/src/modules/date.yml create mode 100644 docs/src/modules/downloader.yml create mode 100644 docs/src/modules/files.yml create mode 100644 docs/src/modules/forms.yml create mode 100644 docs/src/modules/forms_android.yml create mode 100644 docs/src/modules/functional.yml create mode 100644 docs/src/modules/gps_android.yml create mode 100644 docs/src/modules/gzip.yml create mode 100644 docs/src/modules/http.yml create mode 100644 docs/src/modules/imageprocessing_android.yml create mode 100644 docs/src/modules/java.yml create mode 100644 docs/src/modules/jdbc.yml create mode 100644 docs/src/modules/json.yml create mode 100644 docs/src/modules/math.yml create mode 100644 docs/src/modules/ounit.yml create mode 100644 docs/src/modules/regex.yml create mode 100644 docs/src/modules/robot.yml create mode 100644 docs/src/modules/socket.yml create mode 100644 docs/src/modules/std.yml create mode 100644 docs/src/modules/types.yml create mode 100644 docs/src/modules/yaml.yml create mode 100644 docs/src/modules/zip.yml diff --git a/docs/modules.yml b/docs/modules.yml deleted file mode 100644 index c960cd75..00000000 --- a/docs/modules.yml +++ /dev/null @@ -1,6742 +0,0 @@ ---- - - name: std - scope: "both" - desc: "Contains common functions" - desc_ru: "Содержит вспомогательные функции общего назначения" - constants: - - name: "ARGS" - typeName: string - scope: "desktop" - type: 2 - value: "command-line arguments" - - name: OwnLang - typeName: map - type: 4 - value: "{PLATFORM=android/desktop, VERSION=1.5.0_000000, VERSION_MAJOR=1, VERSION_MINOR=5, VERSION_PATCH=0}" - since: 1.4.0 - functions: - - name: arrayCombine - args: "keys, values" - desc: "creates map by combining two arrays" - desc_ru: "создаёт объект на основе двух массивов" - - name: arrayKeyExists - args: "key, map" - desc: "checks existing key in map. 1 - exists, 0 - no" - desc_ru: "проверяет, содержится ли ключ key в объекте map. 1 - содержится, 0 - нет" - - name: arrayKeys - args: "map" - desc: "returns array of map keys" - desc_ru: "возвращает массив ключей карты" - - name: arraySplice - args: "array, start, deleteCount = length(array) - start, additions = []" - desc: "returns new array with removed `deleteCount` elements starting from `start` and/or added new elements from `start` index" - desc_ru: "возвращает новый массив с удалёнными `deleteCount` элементами, начиная с позиции `start` и/или добавляет новые элементы с позиции `start`" - - name: arrayValues - args: "map" - desc: "returns array of map values" - desc_ru: "возвращает массив значений карты" - - name: charAt - args: "input, index" - desc: returns char code in position `index` of string `input` - desc_ru: возвращает код символа в позиции `index` строки `input` - - name: clearConsole - scope: "android" - args: "" - desc: "clears console" - desc_ru: "очищает консоль" - - name: default - args: a, b - desc: returns value `a` if it it non empty, returns `b` otherwise - desc_ru: возвращает значение `a`, если оно не пустое, иначе возвращается значение `b` - since: 1.4.0 - example: |- - use std - - user = {"name": "", "lastname": "Doe"} - name = default(user.name, "Unknown") - lastname = default(user.lastname, "Unknown") - println name + " " + lastname // Unknown Doe - example_ru: |- - use std - - user = {"name": "", "lastname": "Иванов"} - name = default(user.name, "Имя неизвестно") - lastname = default(user.lastname, "фамилия неизвестна") - println name + " " + lastname // Имя неизвестно Иванов - - name: echo - args: "arg..." - desc: "prints values to console, separate them by space and puts newline at the end. Takes variable number of arguments" - desc_ru: "выводит значения в консоль, разделяя их пробелом, а потом ставит перенос строки. Может принимать переменное значение аргументов" - example: |- - use std - - echo(1, "abc") // prints "1 abc" to console - echo(1, 2, 3, 4, 5, "a", "b") // prints "1 2 3 4 5 a b" - example_ru: |- - use std - - echo(1, "abc") // выведет строку "1 abc" в консоль - echo(1, 2, 3, 4, 5, "a", "b") // выведет строку "1 2 3 4 5 a b" в консоль" - - name: getBytes - args: input, charset = "UTF-8" - desc: returns byte array of the string in the given charset - desc_ru: возвращает массив байт строки в заданной кодировке - since: 1.5.0 - - name: indexOf - args: "input, what, index = 0" - desc: "finds first occurrence of `what` in string `input`, starting at position `index`" - desc_ru: "поиск первого вхождения подстроки `what` в строке `input`, начиная с позиции `index`" - - name: join - args: "array, delimiter = \"\", prefix = \"\", suffix = \"\"" - desc: "join array to string with `delimiter`, `prefix` and `suffix`" - desc_ru: "объединяет массив в строку с разделителем `delimiter`, префиксом `prefix` и суффиксом `suffix`" - - name: lastIndexOf - args: "input, what, index = 0" - desc: "finds last occurrence of `what` in string `input`, starting at position `index`" - desc_ru: "поиск последнего вхождения подстроки `what` в строке `input`, начиная с позиции `index`" - - name: length - args: "x" - desc: "returns length of string, array/map size or number of function arguments" - desc_ru: "возвращает длину строки, размер массива/объекта или количество аргументов функции в зависимости от типа аргумента x" - - name: newarray - args: "size..." - desc: "creates array with `size`.\n`newarray(x)` - creates 1D array, `newarray(x,y)` - creates 2D array" - desc_ru: "оздаёт массив с размером size. Если указать 1 аргумент - создаётся одномерный массив, если 2 аргумента - двухмерный и т.д" - example: |- - use std - - println newarray(4) // [0, 0, 0, 0] - println newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] - - name: parseInt - args: "str, radix" - desc: parses string into integer in the `radix` - desc_ru: парсит строку в целое число с указанным основанием - - name: parseLong - args: "str, radix" - desc: parses string into long in the `radix` - desc_ru: парсит строку в длинное целое число с указанным основанием - - name: rand - args: "from = 0, to = .." - desc: |- - returns pseudo-random number. - `rand()` - returns float number from 0 to 1 - `rand(max)` - returns random number from 0 to max - `rand(from, to)` - return random number from `from` to `to` - desc_ru: "возвращает псевдослучайное число. Если вызвать функцию без аргументов, возвращается вещественное число от 0 до 1. Если указан один аргумент - возвращается целое число в диапазоне [0...значение). Если указаны два аргумента - возвращается псевдослучайное число в промежутке [from...to)" - - name: range - args: "from = 0, to, step = 1" - desc: |- - creates lazy array by number range. - `range(to)` - creates range from 0 to `to` (exclusive) with step 1 - `range(from, to)` - creates range from `from` to `to` (exclusive) with step 1 - `range(from, to, step)` - creates range from `from` to `to` (exclusive) with step `step` - desc_ru: |- - создаёт массив с элементами числового промежутка. Элементы вычисляются по мере их использования, так что в цикле foreach можно использовать любые промежутки. - `range(to)` - создаёт промежуток от 0 до `to` (не включительно) с шагом 1 - `range(from, to)` - создаёт промежуток от `from` до `to` (не включительно) с шагом 1 - `range(from, to, step)` - создаёт промежуток от `from` до `to` (не включительно) с шагом `step` - example: |- - use std - - println range(3) // [0, 1, 2] - r = range(-5, 0) // [-5, -4, -3, -2, -1] - println r[0] // -5 - println r[2] // -3 - for x : range(20, 9, -5) { - println x - } // 20 15 10 - - name: readln - scope: "desktop" - args: "x" - desc: "reads a line from console" - desc_ru: "чтение строки из консоли" - - name: replace - args: "str, target, replacement" - desc: "replaces all occurrences of string `target` with string `replacement`" - desc_ru: "заменяет все вхождения подстроки `target` на строку `replacement`" - - name: replaceAll - args: "str, regex, replacement" - desc: "replaces all occurrences of regular expression `regex` with string `replacement`" - desc_ru: "заменяет все вхождения регулярного выражения `regex` на строку `replacement`" - - name: replaceFirst - args: "str, regex, replacement" - desc: "replaces first occurrence of regular expression `regex` with string `replacement`" - desc_ru: "заменяет первое вхождение регулярного выражения `regex` на строку `replacement`" - - name: sleep - args: "time" - desc: "causes current thread to sleep for `time` milliseconds" - desc_ru: "приостановка текущего потока на time миллисекунд" - - name: sort - args: "array, comparator = .." - desc: "sorts array by natural order or by `comparator` function" - desc_ru: "сортирует массив. Если задана функция `comparator`, то сортировка будет производится на основе результата функции сравнения" - - name: split - args: "str, regex, limit = 0" - desc: "splits string `str` with regular expression `regex` into array. `limit` parameter affects the length of resulting array" - desc_ru: "разделяет строку `str` по шаблону `regex` и помещает элементы в массив. Если указан параметр `limit`, то будет произведено не более limit разбиений, соответственно размер результирующего массива будет ограничен этим значением limit" - example: |- - use std - - println split("a5b5c5d5e", "5") // ["a", "b", "c", "d", "e"] - println split("a5b5c5d5e", "5", 3) // ["a", "b", "c5d5e"] - - name: sprintf - args: "format, args..." - desc: "formats string by arguments" - desc_ru: "форматирует строку" - - name: stringFromBytes - args: input, charset = "UTF-8" - desc: returns a string from byte array in the given charset - desc_ru: возвращает строку из массива байт в заданной кодировке - since: 1.5.0 - - name: stripMargin - args: input, marginPrefix = "|" - desc: trims leading whitespaces followed by `marginPrefix` on each line and removes the first and the last lines if they are blank - desc_ru: обрезает начальные пробелы, сопровождаемые `marginPrefix` в каждой строке, и удаляет первую и последнюю строки, если они пустые - since: 1.5.0 - example: |- - use std - - println " - |123 - |456 - ".stripMargin() // "123\n456" - - name: substring - args: "str, startIndex, endIndex = .." - desc: "returns string from `startIndex` to `endIndex` or to end of string if `endIndex` is not set" - desc_ru: "обрезает строку `str`, начиная от символа после позиции `startIndex` и по `endIndex`. Если `endIndex` не указан, обрезается до конца строки" - example: |- - use std - - println substring("abcde", 1) // bcde - println substring("abcde", 2, 4) // cd - - name: sync - args: "callback" - desc: calls an asynchronous function synchronously - desc_ru: делает асинхронный вызов синхронным - example: |- - use std, http - - url = "https://whatthecommit.com/index.txt" - result = sync(def(ret) { - http(url, def(t) = ret(t)) - }) - println result - - name: thread - args: "func, args..." - desc: "creates new thread with parameters if passed" - desc_ru: |- - создаёт новый поток и передаёт параметры, если есть. - - `func` - функция, ссылка на функцию (`::function`) или имя функции (`"function"`) - - `args` - 0 или более аргументов, которые необходимо передать в функцию func - example: |- - use std - - thread(def() { - println "New Thread" - }) - - thread(::newthread, 10) - thread("newthread", 20) - - def newthread(x) { - println "New Thread. x = " + x - } - - name: time - args: "" - desc: "returns current time in milliseconds from 01.01.1970" - desc_ru: "возвращает текущее время в миллисекундах начиная с 1970 года" - - name: toChar - args: "code" - desc: "converts char code to string" - desc_ru: "переводит код символа в строку" - example: |- - use std - - println toChar(48) // "0" - - name: toHexString - args: 'number' - desc: 'converts number into hex string' - desc_ru: 'конвертирует число в строку с шестнадцатиричным представлением' - - name: toLowerCase - args: "str" - desc: "converts all symbols to lower case" - desc_ru: "переводит все символы строки в нижний регистр" - - name: toUpperCase - args: "str" - desc: "converts all symbols to upper case" - desc_ru: "переводит все символы строки в верхний регистр" - - name: trim - args: "str" - desc: "removes any leading and trailing whitespaces in string" - desc_ru: "обрезает пробельные невидимые символы по обоим концам строки" - - name: try - args: "unsafeFunction, catchFunction = def(type, message) = -1" - desc: suppress any error in `unsafeFunction` and returns the result of the `catchFunction` if any error occurs - desc_ru: подавляет любые ошибки в `unsafeFunction` и возрвщает результат функции `catchFunction`, если была ошибка - example: |- - use std - - println try(def() = "success") // success - println try(def() = try + 2) // -1 - println try(def() = try(), def(type, message) = sprintf("Error handled:\ntype: %s\nmessage: %s", type, message)) - println try(def() = try(), 42) // 42 - example_ru: |- - use std - - println try(def() = "успех") // успех - println try(def() = try + 2) // -1 - println try(def() = try(), def(type, message) = sprintf("Обработана ошибка:\nтип: %s\nсообщение: %s", type, message)) - println try(def() = try(), 42) // 42 - - name: types - scope: "both" - desc: "Contains functions for type checking and conversion" - desc_ru: "Содержит функции для проверки и преобразования типов" - constants: - - - name: "OBJECT" - typeName: number - type: 1 - value: "0" - - - name: "NUMBER" - typeName: number - type: 1 - value: "1" - - - name: "STRING" - typeName: number - type: 1 - value: "2" - - - name: "ARRAY" - typeName: number - type: 1 - value: "3" - - - name: "MAP" - typeName: number - type: 1 - value: "4" - - - name: "FUNCTION" - typeName: number - type: 1 - value: "5" - functions: - - - name: "byte" - args: "value" - desc: "converts value to byte" - desc_ru: "преобразует значение к типу byte" - - - name: "double" - args: "value" - desc: "converts value to double" - desc_ru: "преобразует значение к типу double" - - - name: "float" - args: "value" - desc: "converts value to float" - desc_ru: "преобразует значение к типу float" - - - name: "int" - args: "value" - desc: "converts value to int" - desc_ru: "преобразует значение к типу int" - - - name: "long" - args: "value" - desc: "converts value to long" - desc_ru: "преобразует значение к типу long" - - - name: "number" - args: "value" - desc: "converts value to number if possible" - desc_ru: "преобразует значение к числу, если это возможно" - example: |- - use types - - println typeof(number("2.3")) // 1 (NUMBER) - - - name: "short" - args: "value" - desc: "converts value to short" - desc_ru: "преобразует значение к типу short" - - - name: "string" - args: "value" - desc: "converts value to string" - desc_ru: "преобразует значение в строку" - example: |- - use types - - println typeof(string(1)) // 2 (STRING) - - - name: "typeof" - args: "value" - desc: "returns the type of value" - desc_ru: "возвращает тип переданного значения" - example: |- - use types - - println typeof(1) // 1 (NUMBER) - println typeof("text") // 2 (STRING) - println typeof([]) // 3 (ARRAY) - - name: math - scope: "both" - desc: "Contains math functions and constants" - desc_ru: "Содержит математические функции и константы" - constants: - - - name: "E" - typeName: number - type: 1 - value: "2.718281828459045" - - - name: "PI" - typeName: number - type: 1 - value: "3.141592653589793" - functions: - - - name: "abs" - args: "x" - desc: "absolute value of `x`" - desc_ru: "модуль числа `x`" - - - name: "acos" - args: "x" - desc: "arc cosine" - desc_ru: "арккосинус" - - - name: "asin" - args: "x" - desc: "arc sine" - desc_ru: "арксинус" - - - name: "atan" - args: "x" - desc: "arc tangent" - desc_ru: "арктангенс" - - - name: "atan2" - args: "y, x" - desc: "returns angle θ whose tangent is the ratio of two numbers" - desc_ru: "угол θ, тангенс которого равен отношению двух указанных чисел" - - - name: "cbrt" - args: "x" - desc: "cube root" - desc_ru: "кубический корень числа x" - - - name: "ceil" - args: "x" - desc: "returns the ceiling of `x`" - desc_ru: "округляет вещественное число в большую сторону" - example: |- - use math - - ceil(6.4) // 7 - - - name: "copySign" - args: "magnitude, sign" - desc: "returns a value with the magnitude of x and the sign of y" - desc_ru: "возвращает значение с величиной x и знаком y" - - - name: "cos" - args: "x" - desc: "trigonometric cosine" - desc_ru: "косинус" - - - name: "cosh" - args: "x" - desc: "hyperbolic cosine" - desc_ru: "гиперболический косинус" - - - name: "exp" - args: "x" - desc: "ex" - desc_ru: "ex" - - - name: "expm1" - args: "x" - desc: "ex-1" - desc_ru: "ex-1" - - - name: "floor" - args: "x" - desc: "returns floor of `x`" - desc_ru: "округляет вещественное число в меньшую сторону" - example: |- - use math - - floor(3.8) // 3 - - - name: "getExponent" - args: "x" - desc: "returns the unbiased exponent used in the representation of a double or float" - desc_ru: "возвращают несмещенное значение экспоненты числа" - - - name: "hypot" - args: "x, y" - desc: "returns the square root of the sum of squares of its arguments" - desc_ru: "расчёт гипотенузы sqrt(x2 + y2) без переполнения" - - - name: "IEEEremainder" - args: "x, y" - desc: "returns the remainder resulting from the division of a specified number by another specified number. This operation complies with the remainder operation defined in Section 5.1 of ANSI/IEEE Std 754-1985; IEEE Standard for Binary Floating-Point Arithmetic; Institute of Electrical and Electronics Engineers, Inc; 1985." - desc_ru: "возвращает остаток от деления x на y по стандарту ANSI/IEEE Std 754-1985, раздел 5.1" - - - name: "log" - args: "x" - desc: "returns the logarithm of a specified number" - desc_ru: "логарифм" - - - name: "log1p" - args: "x" - desc: "" - desc_ru: "натуральный логарифм от x + 1 (`ln(x + 1)`)" - - - name: "log10" - args: "x" - desc: "returns the base 10 logarithm of a specified number" - desc_ru: "десятичный логарифм" - - - name: "max" - args: "x, y" - desc: "returns the larger of two specified numbers" - desc_ru: "максимальное из двух чисел" - - - name: "min" - args: "x, y" - desc: "returns the smaller of two numbers" - desc_ru: "минимальное из двух чисел" - - - name: "nextAfter" - args: "x, y" - desc: "" - desc_ru: "" - - - name: "nextUp" - args: "x" - desc: "" - desc_ru: "" - - - name: "pow" - args: "x, y" - desc: "returns a specified number raised to the specified power" - desc_ru: "возведение x в степень y" - - - name: "rint" - args: "x" - desc: "" - desc_ru: "" - - - name: "round" - args: "x" - desc: "rounds a value to the nearest integer or to the specified number of fractional digits" - desc_ru: "округляет вещественное число до ближайшего целого" - - - name: "signum" - args: "x" - desc: "returns an integer that indicates the sign of a number" - desc_ru: "возвращает целое число, указывающее знак числа" - - - name: "sin" - args: "x" - desc: "" - desc_ru: "синус" - - - name: "sinh" - args: "x" - desc: "" - desc_ru: "гиперболический синус" - - - name: "sqrt" - args: "x" - desc: "" - desc_ru: "квадратный корень" - - - name: "tan" - args: "x" - desc: "" - desc_ru: "тангенс" - - - name: "tanh" - args: "x" - desc: "" - desc_ru: "гиперболический тангенс" - - - name: "toDegrees" - args: "x" - desc: "" - desc_ru: "перевод радиан в градусы" - - - name: "toRadians" - args: "x" - desc: "" - desc_ru: "перевод градусов в радианы" - - - name: "ulp" - args: "x" - desc: "" - desc_ru: "" - - name: date - scope: "both" - desc: "Contains functions for working with date and time" - desc_ru: "Содержит функции для работы с датой и временем" - constants: - - - name: "STYLE_FULL" - typeName: number - type: 1 - value: "0" - - - name: "STYLE_LONG" - typeName: number - type: 1 - value: "1" - - - name: "STYLE_MEDIUM" - typeName: number - type: 1 - value: "2" - - - name: "STYLE_SHORT" - typeName: number - type: 1 - value: "3" - functions: - - - name: "newDate" - args: "..." - desc: |- - `newDate()` - returns current date. - - `newDate(timestamp)` - returns date by given timestamp. - - `newDate(dateString)` - parses and returns date by given string. - - `newDate(pattern, dateString)` - parses and returns date by given string in `pattern` format. - - `newDate(year, month, day)` - returns date by year, month and day. - - `newDate(year, month, day, hour, minute)` - returns date by year, month, day, hour and minute. - - `newDate(year, month, day, hour, minute, second)` - returns date by year, month, day, hour, minute and second. - - Returns DateValue. - desc_ru: |- - `newDate()` - возвращает текущую дату. - - `newDate(timestamp)` - возвращает дату для указанной метки времени. - - `newDate(dateString)` - парсит и возвращает дату, записанную в виде строки. - - `newDate(pattern, dateString)` - парсит и возвращает дату, записанную в виде строки в формате `pattern`. - - `newDate(year, month, day)` - возвращает дату для указанных года, месяца и дня. - - `newDate(year, month, day, hour, minute)` - возвращает дату для указанных года, месяца, дня, часа и минуты. - - `newDate(year, month, day, hour, minute, second)` - возвращает дату для указанных года, месяца, дня, часа, минуты и секунды. - - Возвращает DateValue. - - - name: "newFormat" - args: "..." - desc: |- - `newFormat()` - returns default date format. - - `newFormat(pattern)` - returns date format by given pattern. - - `newFormat(type)` - returns format: 0 - default, 1 - date, 2 - time, 3 - date and time. - - `newFormat(pattern, locale)` - returns date format by given pattern and locale. - - `newFormat(type, style)` - returns format: 0 - default, 1 - date, 2 - time, 3 - date and time. `style`: 0 - full, 1 - long, 2 - medium, 3 - short. - - Returns DateFormatValue. - desc_ru: |- - `newFormat()` - возвращает формат даты по умолчанию. - - `newFormat(pattern)` - возвращает формат с указанным шаблоном. - - `newFormat(type)` - возвращает формат: 0 - по умолчанию, 1 - для даты, 2 - для времени, 3 - для времени и даты. - - `newFormat(pattern, locale)` - возвращает формат для указанного шаблона в заданной локализации. - - `newFormat(type, style)` - возвращает формат: 0 - по умолчанию, 1 - для даты, 2 - для времени, 3 - для времени и даты. `style`: 0 - полный, 1 - длинный, 2 - средний, 3 - короткий. - - Возвращает DateFormatValue. - - - name: "formatDate" - args: "date, format = default" - desc: formats date by given format and returns string - desc_ru: форматирует дату в указанном формате и возвращает строку - example: |- - use date - - d = newDate(2016, 4, 8) - println formatDate(d, newFormat("yyyy/MM/dd")) // "2016/05/08" - - - name: "parseDate" - args: "dateString, format = default" - desc: parses date from string by given pattern. Returns DateValue - desc_ru: парсит дату из строки в указанном шаблоне. Возвращает DateValue - example: |- - use date - - println parseDate("2016/05/08", newFormat("yyyy/MM/dd")) - - - name: "toTimestamp" - args: "date" - desc: returns timestamp in milliseconds - desc_ru: возвращает время в миллисекундах - types: - - - name: "DateValue" - value: "year, month, day, hour, minute, second, millisecond" - - - name: "DateFormatValue" - - name: files - scope: "both" - desc: "Contains functions for working with files" - desc_ru: "Содержит функции для работы с файлами" - constants: - - - name: "FILES_COMPARATOR" - typeName: "function" - scope: "both" - type: 5 - value: "def(f1, f2) = compare(f1, f2)" - desc: "function which compares two file descriptors" - desc_ru: "функция, которая сравнивает два файловых дескриптора" - - - name: "SDCARD" - typeName: string - scope: "android" - type: 2 - value: "path to SDCARD" - desc: "path to SDCARD" - desc_ru: "путь к внешнему хранилищу" - functions: - - - name: "canExecute" - args: "f" - desc: "checks execute permission of the descriptor `f`" - desc_ru: "проверяет права на выполнение дескриптора `f`" - - - name: "canRead" - args: "f" - desc: "checks read permission of the descriptor `f`" - desc_ru: "проверяет права на чтение дескриптора `f`" - - - name: "canWrite" - args: "f" - desc: "checks write permission of the descriptor `f`" - desc_ru: "проверяет права на запись дескриптора `f`" - - name: copy - args: 'src, dst' - desc: 'copies file src to dst location' - desc_ru: 'копирует файл src в dst' - - - name: "delete" - args: "f" - desc: "removes file or directory. Returns 1 if delete was successfull, 0 otherwise" - desc_ru: "удаляет файл или папку. Возвращает 1, если удаление прошло успешно, иначе - 0" - - - name: "exists" - args: "f" - desc: "checks file or directory existing. Returns 1 if exists, 0 otherwise" - desc_ru: "проверяет, существует ли файл или папка. Возвращает 1, если существует, иначе - 0" - - - name: "fclose" - args: "f" - desc: "closes file" - desc_ru: "закрывает файл" - - - name: "fileSize" - args: "f" - desc: "returns file size in bytes" - desc_ru: "возвращает размер файла в байтах" - - - name: "flush" - args: "f" - desc: "flushes write buffer into file" - desc_ru: "сбрасывает буфер записи в файл" - - - name: "fopen" - args: "path, mode = \"r\"" - desc: |- - opens file файл with `path` in given `mode`: - - - "" - opens file or directory for getting info; - - "r" - opens file for read in text mode; - - "rb" - opens file for read in binary mode; - - "w" - opens file for write in text mode; - - "w+" - opens file for append in text mode; - - "wb" - opens file for write in binary mode; - - "wb+" - opens file for append in binary mode. - - Returns a file descriptor for using in other functions. - desc_ru: |- - открывает файл по пути `path` в заданном режиме `mode`: - - - "" - открывает файл или папку для получения информации; - - "r" - открывает файл для чтения в текстовом режиме; - - "rb" - открывает файл для чтения в бинарном режиме; - - "w" - открывает файл для записи в текстовом режиме; - - "w+" - открывает файл для дозаписи в текстовом режиме; - - "wb" - открывает файл для записи в бинарном режиме; - - "wb+" - открывает файл для дозаписи в бинарном режиме. - - Возвращает дескриптор файла, который необходим для остальных функций. - example: |- - use files - - f1 = fopen("text.txt") // opens file text.txt for read in text mode - f2 = fopen("E:/1.dat", "rbwb") // opens file 1.dat on drive E for binary read and write" - example_ru: |- - use files - - f1 = fopen("text.txt") // открывает файл text.txt для текстового чтения - f2 = fopen("E:/1.dat", "rbwb") // открывает файл 1.dat на диске E для бинарного чтения и записи" - - - name: "getParent" - args: "f" - desc: "returns parent path of the given descriptor `f`" - desc_ru: "возвращает родительский путь для заданного дескриптора `f`" - - - name: "isDirectory" - args: "f" - desc: "checks if descriptor `f` is directory" - desc_ru: "проверяет, является ли дескриптор `f` папкой. 1 - является, 0 - нет" - - - name: "isFile" - args: "f" - desc: "checks if descriptor `f` is file" - desc_ru: "проверяет, является ли дескриптор f файлом. 1 - является, 0 - нет" - - - name: "isHidden" - args: "f" - desc: "checks if descriptor `f` is hidden" - desc_ru: "проверяет, скрыт ли дескриптор f. 1 - скрыт, 0 - нет" - - - name: "lastModified" - args: "f" - desc: "returns last modification time" - desc_ru: "возвращает время последнего изменения" - - - name: "listFiles" - args: "f" - desc: "returns array with filenames in given directory.\n\n f - directory descriptor" - desc_ru: "возвращает массив с именами файлов в указанной директории.\n\n f - дескриптор папки" - example: |- - use files - - f1 = fopen("E:/examples", "") // opens directory examples for getting information - list = listFiles(f1) // gets array with filenames in directory - example_ru: |- - use files - - f1 = fopen("E:/examples", "") // открыть папку examples для получения информации - list = listFiles(f1) // получить массив с именами файлов в этой папке - - - name: "mkdir" - args: "f" - desc: "creates the directory. Returns 1 if operation was successfull, 0 otherwise" - desc_ru: "создаёт папку. Возвращает 1, если создание прошло успешно, иначе - 0" - - - name: "mkdirs" - args: "f" - desc: "creates the directories. Returns 1 if operation was successfull, 0 otherwise" - desc_ru: "создаёт папки. Возвращает 1, если создание прошло успешно, иначе - 0" - - - name: "readAllBytes" - args: "f" - desc: "reads all bytes from file. Returns array with bytes" - desc_ru: "чтение всех байт файла. Возвращает массив байт файла" - example: |- - use std, files - - f1 = fopen("file.bin", "rb") - array = readAllBytes(f1) - println length(array) - - - name: "readBoolean" - args: "f" - desc: "reads boolean (1 byte). Returns 0 if byte was 0, 1 otherwise" - desc_ru: "чтение boolean-значения (1 байт). Возвращает 0, если байт имеет значение 0, 1 - если значение не равно 0" - - - name: "readByte" - args: "f" - desc: "reads one byte" - desc_ru: "чтение одного байта" - - - name: "readBytes" - args: "f, array, offset = 0, length = length(array)" - desc: "reads `length` bytes of file `f` and stores to `array` starting from `offset+1` byte. Returns number of read bytes" - desc_ru: "чтение заданного количества байт в массив `array`. Возвращает число прочитанных байт. \nЕсли offset и length не указаны, то читается количество байт равное длине массива. \nЕсли offset и length указаны, то читается length байт в массив array, начиная с `offset+1` байта" - example: |- - use files - - f1 = fopen("file.bin", "rb") // file.bin must contain more than 5000 bytes - array = newarray(2048) - readCount = readBytes(f1, array) // reads 2048 bytes - readCount = readBytes(f1, array, 10) // reads 2048 bytes starting from 11 byte - readCount = readBytes(f1, array, 20, 10) // reads 10 bytes, starting from 21 byte - example_ru: |- - use files - - f1 = fopen("file.bin", "rb") // file.bin должен иметь больше 5000 байтов - array = newarray(2048) - readCount = readBytes(f1, array) // читает 2048 байт из файла - readCount = readBytes(f1, array, 10) // читает 2048 байт, начиная с 11 байта - readCount = readBytes(f1, array, 20, 10) // читает 10 байт, начиная с 21 байта - - - name: "readChar" - args: "f" - desc: "reads one char (2 bytes). Returns number char's code" - desc_ru: "чтение одного символа (2 байта). Возвращает число - код символа" - - - name: "readDouble" - args: "f" - desc: "reads 8 bytes double number" - desc_ru: "чтение 8 байт (вещественное число двойной точности)" - - - name: "readFloat" - args: "f" - desc: "reads 4 bytes float number" - desc_ru: "чтение 4 байт (вещественное число)" - - - name: "readInt" - args: "f" - desc: "reads 4 bytes integer number" - desc_ru: "чтение 4 байт (целое число)" - - - name: "readLine" - args: "f" - desc: "reads line from file opened in text mode" - desc_ru: "чтение строки в текстовом режиме" - - - name: "readLong" - args: "f" - desc: "reads 8 bytes long number" - desc_ru: "чтение 8 байт (длинное целое число)" - - - name: "readShort" - args: "f" - desc: "reads 2 bytes short number" - desc_ru: "чтение 2 байт (короткое целое число)" - - - name: "readText" - args: "f" - desc: "reads all file's content as string" - desc_ru: "чтение всего файла в текстовом режиме в строку" - - - name: "readUTF" - args: "f" - desc: "reads string in binary mode" - desc_ru: "чтение строки в бинарном режиме" - - - name: "rename" - args: "from, to" - desc: "renames (or moves) file" - desc_ru: "переименование (или перемещение) файла" - example: |- - use files - - f1 = fopen("C:/file1", "i") - f2 = fopen("E:/file2", "i") - rename(f1, f2) - fclose(f1) - fclose(f2) - - - name: "setLastModified" - args: "f, time" - desc: "sets last modified time" - desc_ru: "устанавливает время изменения" - - - name: "setReadOnly" - args: "f" - desc: "marks descriptor read only" - desc_ru: "помечает дескриптор только для чтения" - - - name: "setExecutable" - args: "f, executable, ownerOnly = true" - desc: "sets execute permission" - desc_ru: "устанавливает права на выполнение" - - - name: "setReadable" - args: "f, readable, ownerOnly = true" - desc: "sets read permission" - desc_ru: "устанавливает права на чтение" - - - name: "setWritable" - args: "f, writable, ownerOnly = true" - desc: "sets write permission" - desc_ru: "устанавливает права на запись" - - - name: "writeBoolean" - args: "f, v" - desc: "writes boolean (0 or 1) to file" - desc_ru: "запись одного байта boolean (0 или 1) в файл" - - - name: "writeByte" - args: "f, v" - desc: "writes one byte to file" - desc_ru: "запись одного байта в файл" - - - name: "writeBytes" - args: "f, array, offset = 0, length = length(array)" - desc: "writes `length` bytes to file `f` from byte `array` starting from `offset`" - desc_ru: "запись заданного количества байт в файл `f` из массива байт `array`. \nЕсли offset и length не указаны, то записывается количество байт равное длине массива. \nЕсли offset и length указаны, то пропускается offset байт и записывается length байт" - - - name: "writeChar" - args: "f, v" - desc: "writes one char (2 bytes) to file. `v` can be number - writes number, or string - writes code of first symbol" - desc_ru: "запись одного символа (2 байта) в файл. `v` может быть как числом (пишется это число), так и строкой (пишется код первого символа)" - - - name: "writeDouble" - args: "f, v" - desc: "writes 8 bytes double number to file" - desc_ru: "запись 8 байт (вещественное число двойной точности)" - - - name: "writeFloat" - args: "f, v" - desc: "writes 4 bytes float number to file" - desc_ru: "запись 4 байт (вещественное число)" - - - name: "writeInt" - args: "f, v" - desc: "writes 4 bytes integer number to file" - desc_ru: "запись 4 байт (целое число)" - - - name: "writeLine" - args: "f, v" - desc: "writes string to file in text mode **adds line break at the end of the string**" - desc_ru: "запись строки в текстовом режиме **Добавляет в конец символ переноса строки**" - - - name: "writeLong" - args: "f, v" - desc: "writes 8 bytes long number to file" - desc_ru: "запись 8 байт (длинное целое число)" - - - name: "writeShort" - args: "f, v" - desc: "writes 2 bytes short number to file" - desc_ru: "запись двух байт (короткое целое число)" - - - name: "writeText" - args: "f, v" - desc: "writes string to file in text mode. Unlike `writeLine` does not add line break" - desc_ru: "запись всего текста в текстовом режиме. В отличие от `writeLine`, не добавляет символ переноса строки" - - - name: "writeUTF" - args: "f, v" - desc: "writes string to file in binary mode" - desc_ru: "запись строки в бинарном режиме" - - name: http - scope: "both" - desc: "Contains network functions" - desc_ru: "Содержит функции для взаимодействия с сетью" - constants: [] - functions: - - - name: "http" - args: "url" - desc: |- - performs GET-request to `url`. - - `http(url, method)` - performs request with `method` (GET, POST, PUT, DELETE, PATCH, OPTIONS) to `url`. - - `http(url, callback)` - performs GET-request to `url`, response will be send to function `callback`. - - `http(url, method, params)` - performs request with given `method` and object `params` to `url`. - - `http(url, method, callback)` - performs request with given `method` to `url`, response will be send to function `callback`. - - `http(url, method, params, callback)` - performs request with given `method` and object `params` to `url`, response will be send to function `callback`. - - `http(url, method, params, options, callback)` - performs request with given `method`, object `params` and connection `options` to `url`, response will be send to function `callback`. - - Connection options is a object (map): - - - `header` - sets http-header (string or array). - - `encoded` - is `params` object already urlencoded. - - `content_type` - sets Content-Type. - - `extended_result` - marks that response should be extended and should contains: - - `text` - server response text - - `message` - server response message - - `code` - server response code - - `headers` - response http-header - - `content_length` - Content-Length - - `content_type` - Content-Type - desc_ru: |- - `http(url)` - выполняет GET-запрос на указанный адрес `url`. - - `http(url, method)` - выполняет запрос на указанный адрес `url` методом method (GET, POST, PUT, DELETE, PATCH, OPTIONS). - - `http(url, callback)` - выполняет GET-запрос на указанный адрес `url`, ответ сервера передаёт в функцию `callback`. - - `http(url, method, params)` - выполняет запрос на указанный адрес `url`, методом `method` c данными `params` (объект). - - `http(url, method, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, ответ сервера передаёт в функцию `callback`. - - `http(url, method, params, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, с данными `params`, ответ сервера передаёт в функцию `callback`. - - `http(url, method, params, options, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, с данными `params`, параметрами подключения `options`, ответ сервера передаёт в функцию `callback`. - - Параметрами подключения выступает объект. Допустимы следующие параметры - - - `header` - задаёт http-заголовок, если передана строка или несколько заголовков, если массив. - - `encoded` - указывает, что данные `params` уже закодированы в URL-формате. - - `content_type` - указывает Content-Type. - - `extended_result` - указывает, что ответ сервера нужно вернуть в расширенном виде, а именно объектом с данными: - - `text` - текст ответа сервера - - `message` - сообщение сервера - - `code` - код ответа сервера - - `headers` - http-заголовки ответа - - `content_length` - Content-Length - - `content_type` - Content-Type - example: |- - use http - http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { - println "Added: " + v - }) - - name: "download" - args: "url" - desc: "downloads content by url as bytes array" - desc_ru: "получает содержимое по указанному адресу в виде массива байт" - example: |- - use http, files - bytes = download("http://url") - f = fopen("file", "wb") - writeBytes(f, bytes) - flush(f) - fclose(f) - - name: "urlencode" - args: "str" - desc: "converts string to URL-format" - desc_ru: "преобразует строку в URL-формат" - - name: socket - scope: both - constants: - - name: EVENT_CONNECT - type: 2 - typeName: string - value: connect - - name: EVENT_CONNECTING - type: 2 - typeName: string - value: connecting - - name: EVENT_CONNECT_ERROR - type: 2 - typeName: string - value: connect_error - - name: EVENT_CONNECT_TIMEOUT - type: 2 - typeName: string - value: connect_timeout - - name: EVENT_DISCONNECT - type: 2 - typeName: string - value: disconnect - - name: EVENT_ERROR - type: 2 - typeName: string - value: error - - name: EVENT_MESSAGE - type: 2 - typeName: string - value: message - - name: EVENT_PING - type: 2 - typeName: string - value: ping - - name: EVENT_PONG - type: 2 - typeName: string - value: pong - - name: EVENT_RECONNECT - type: 2 - typeName: string - value: reconnect - - name: EVENT_RECONNECTING - type: 2 - typeName: string - value: reconnecting - - name: EVENT_RECONNECT_ATTEMPT - type: 2 - typeName: string - value: reconnect_attempt - - name: EVENT_RECONNECT_ERROR - type: 2 - typeName: string - value: reconnect_error - - name: EVENT_RECONNECT_FAILED - type: 2 - typeName: string - value: reconnect_failed - functions: - - name: newSocket - args: 'url, options = {}' - desc: |- - creates new SocketValue - - options (map with keys): - - forceNew (boolean) - - multiplex (boolean) - - reconnection (boolean) - - rememberUpgrade (boolean) - - secure (boolean) - - timestampRequests (boolean) - - upgrade (boolean) - - policyPort (integer) - - port (integer) - - reconnectionAttempts (integer) - - reconnectionDelay (timestamp - long) - - reconnectionDelayMax (timestamp - long) - - timeout (timestamp - long) - set -1 to disable - - randomizationFactor (double) - - host (string) - - hostname (string) - - path (string) - - query (string) - - timestampParam (string) - - transports (array of strings) - desc_ru: |- - создаёт новый SocketValue - - options (map с ключами): - - forceNew (boolean) - - multiplex (boolean) - - reconnection (boolean) - - rememberUpgrade (boolean) - - secure (boolean) - - timestampRequests (boolean) - - upgrade (boolean) - - policyPort (integer) - - port (integer) - - reconnectionAttempts (integer) - - reconnectionDelay (timestamp - long) - - reconnectionDelayMax (timestamp - long) - - timeout (timestamp - long) - -1 для отключения - - randomizationFactor (double) - - host (string) - - hostname (string) - - path (string) - - query (string) - - timestampParam (string) - - transports (array of strings) - types: - - name: SocketValue - functions: - - name: "close" - args: "" - desc: "disconnects the socket" - desc_ru: "закрывает соединение сокета" - - name: "connect" - args: "" - desc: "connects the socket" - desc_ru: "подключает сокет" - - name: "connected" - args: "" - desc: "returns connected status (1 - connected, 0 - no)" - desc_ru: "возвращает состояние подключения (1 - подключен, 0 - нет)" - - name: "disconnect" - args: "" - desc: "disconnects the socket" - desc_ru: "закрывает соединение сокета" - - name: "emit" - args: "event, data" - desc: "emits an event" - desc_ru: "посылает событие" - - name: "hasListeners" - args: "event" - desc: "returns true if there is listeners for specified event" - desc_ru: "возвращает true, если для указанного события есть обработчики" - - name: "id" - args: "" - desc: "returns socket id" - desc_ru: "возвращает id сокета" - - name: "off" - args: "event = .." - desc: "removes specified event handler, or removes all if no arguments were passed" - desc_ru: "удаляет обработчик указанного события или удаляет все обработчики, если не было передано ни одного аргумента" - - name: "on" - args: "event, listener" - desc: "adds event listener" - desc_ru: "добавляет обработчик указанного события" - - name: "once" - args: "event, listener" - desc: "adds one time event listener" - desc_ru: "добавляет одноразовый обработчик указанного события" - - name: "open" - args: "" - desc: "connects the socket" - desc_ru: "подключает сокет" - - name: "send" - args: "data" - desc: "send messages" - desc_ru: "отправляет сообщения" - - name: downloader - scope: both - desc: "Contains functions for downloading large files" - desc_ru: "Содержит функции для скачивания больших файлов" - functions: - - name: getContentLength - args: 'url' - desc: 'gets content length by sending HEAD request to the given url' - desc_ru: 'получает значение заголовка Content-Length путём отправки HEAD-запроса на указанный url' - - name: downloader - args: 'downloadUrl, filePath, progressCallback = def() {}, bufferSize = 16384' - desc: 'downloads file from `downloadUrl` to `filePath`' - desc_ru: 'скачивает файл по адресу `downloadUrl` и сохраняет в `filePath`' - example: |- - use downloader, std - - MBYTES = 1048576.0 // 1024*1024 - url = "http://www.ovh.net/files/10Mb.dat" - file = "10Mb.dat" - - downloader(url, file, def(progress, bytesDownloaded, bytesMax) { - bar = "#" * (progress / 2) - print sprintf("%-50s %d%% %.2f / %.2f MiB\\r", bar, progress, bytesDownloaded / MBYTES, bytesMax / MBYTES) - }) - - name: base64 - scope: both - desc: "Contains base64 encoding and decoding functions" - desc_ru: "Содержит функции кодирования данных в base64 и наоборот" - constants: - - name: BASE64_URL_SAFE - type: 1 - typeName: number - value: '8' - desc: 'Url safe encoding output' - desc_ru: 'Вывод данных в безопасном для ссылок формате' - functions: - - name: base64decode - args: 'data, type = 0' - desc: 'decodes base64-encoded byte array or string into byte array' - desc_ru: 'декодирует массив байт или строку, закодированную в base64, в массив байт' - - name: base64encode - args: 'data, type = 0' - desc: 'encodes byte array or string into base64-encoded byte array' - desc_ru: 'кодирует массив байт или строку в закодированный base64 массив байт' - - name: base64encodeToString - args: 'data, type = 0' - desc: 'encodes byte array or string into base64-encoded string' - desc_ru: 'кодирует массив байт или строку в закодированную base64 строку' - - name: json - scope: "both" - desc: "Contains functions for working with the json format" - desc_ru: "Содержит функции преобразования данных в формат json и наоборот" - constants: [] - functions: - - - name: "jsondecode" - args: "data" - desc: "converts data to json string" - desc_ru: "преобразует переданные данные в строку в формате json" - example: |- - use json - print jsondecode("{\"key1\":1,\"key2\":[1,2,3],\"key3\":\"text\"}") // {key2=[1, 2, 3], key3=text, key1=1} - - - name: "jsonencode" - args: "jsonString, indent = 0" - desc: "converts string to data" - desc_ru: "преобразует строку в формате json в данные" - example: |- - use json - data = { - "key1": 1, - "key2": [1, 2, 3], - "key3": "text" - } - print jsonencode(data) // {"key1":1,"key2":[1,2,3],"key3":"text"} - - name: yaml - scope: desktop - desc: "Contains functions for working with the yaml format" - desc_ru: "Содержит функции преобразования данных в формат yaml и наоборот" - constants: [] - functions: - - name: yamldecode - args: "data" - desc: "converts data to yaml string" - desc_ru: "преобразует переданные данные в строку в формате yaml" - - name: yamlencode - args: "yamlString" - desc: "converts yaml string to data" - desc_ru: "преобразует строку в формате yaml в данные" - - name: zip - since: 1.5.0 - scope: both - desc: "Contains functions for working with zip archives" - desc_ru: "Содержит функции для работы с zip архивами" - constants: [] - functions: - - - name: zip - args: "inputPath, outputFile, mapper = def(entryPath) = entryPath" - desc: |- - creates a zip archive with the contents of `inputPath` and saves to `outputFile`. - `mapper` is used to set the name of the final file inside the archive and for filtering. If the mapper returns an empty string, the file will be skipped. - Returns the number of archived files, or -1 if the archive could not be created. - desc_ru: |- - создаёт zip архив с содержимым `inputPath` и сохраняет в `outputFile`. - `mapper` используется для задания имени конечного файла внутри архива, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. - Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. - example: |- - use zip - // Zip all files in directory - zip("/tmp/dir", "/tmp/1.zip") - // Zip .txt files - zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") - example_ru: |- - use zip - // Архивировать все файлы в директории - zip("/tmp/dir", "/tmp/1.zip") - // Архивировать .txt файлы - zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") - - - name: zipFiles - args: "input, outputFile" - desc: |- - creates a zip archive with the contents of `inputPath` and saves to `outputFile`. - If `input` is a string, then a single file or the contents of a folder is archived. - If `input` is an array, then the files and folders listed in it are archived. - If `input` is an associative array, then the files and folders listed in the keys are archived and the names inside the archive will be the values of an array. - Returns the number of archived files, or -1 if the archive could not be created. - desc_ru: |- - создаёт zip архив с содержимым `input` и сохраняет в `outputFile`. - Если `input` — строка, то архивируется один файл или содержимое папки. - Если `input` — массив, то архивируются файлы и папки, перечисленные в нём. - Если `input` — ассоциативный массив, то архивируются файлы и папки перечисленные в ключах, а именами внутри архива будут служить значения. - Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. - example: |- - use zip - zipFiles("/tmp/dir/file.txt", "/tmp/1.zip") - zipFiles(["/tmp/dir/file.txt", "/tmp/dir/readme.md"], "/tmp/2.zip") - zipFiles({"/tmp/dir/file.txt" : "docs/1.md", "/tmp/dir/readme.md" : "docs/2.md"}, "/tmp/3.zip") - - - name: unzip - args: "input, output, mapper = def(entryName) = entryPath" - desc: |- - unpacks a zip archive to `output` directory. - `mapper` is used to set the name of the final file and for filtering. If the mapper returns an empty string, the file will be skipped. - Returns the number of unzipped files, or -1 if unzipping the archive was failed. - desc_ru: |- - распаковывает zip архив `input` в папку `output`. - `mapper` используется для задания имени конечного файла, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. - Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. - example: |- - use zip - // Unzip all files in directory - unzip("/tmp/1.zip", "/tmp/dir") - // Unzip .txt files - unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") - example_ru: |- - use zip - // Распаковать все файлы в директории - unzip("/tmp/1.zip", "/tmp/dir") - // Распаковать .txt файлы - unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") - - - name: unzipFiles - args: "input, output" - desc: |- - unpacks a `output` files from zip archive . - If `output` is a string, then a single file is unzipped. - If `output` is an array, then the files listed in it are unzipped. - If `output` is an associative array, the files listed in the keys are unzipped and the values will be file names. - Returns the number of unzipped files, or -1 if unzipping the archive was failed. - desc_ru: |- - распаковывает `output` файлы из zip архива. - Если `output` — строка, то разархивируется один файл. - Если `output` — массив, то разархивируются файлы, перечисленные в нём. - Если `output` — ассоциативный массив, то разархивируются файлы перечисленные в ключах, а именами файлов будут служить значения. - Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. - example: |- - use zip - unzipFiles("/tmp/1.zip", "file.txt") - unzipFiles("/tmp/2.zip", ["file.txt", "readme.md"]) - unzipFiles("/tmp/3.zip", {"docs/1.md" : "/tmp/dir/file.txt", "docs/2.md" : "/tmp/dir/readme.md"}) - - - name: listZipEntries - args: "input" - desc: returns an array of zip archive filenames - desc_ru: возвращает массив с именами файлов zip архива - - name: gzip - since: 1.5.0 - scope: both - desc: "Contains functions for working with gzip compression" - desc_ru: "Содержит функции для работы с gzip компрессией" - constants: [] - functions: - - - name: gzip - args: "inputFile, outputFile" - desc: |- - creates a gzip archive with the `inputFile` file and saves to `outputFile`. - Returns 1 if compression was successfull, -1 otherwise. - desc_ru: |- - создаёт gzip архив с файлом `inputFile` и сохраняет в `outputFile`. - Возвращает 1 если компрессия завершилась успешно, и -1 в противном случае. - example: |- - use gzip - gzip("/tmp/readme.md", "/tmp/readme.md.gz") - - - name: gzipBytes - args: "bytes" - desc: returns gzip-compressed input bytes. - desc_ru: возвращает сжатый в gzip массив байт. - example: |- - use gzip - bytes = gzipBytes([0, 119, 87, 80/* ... */]) - - - name: ungzip - args: "inputFile, outputFile" - desc: |- - unpacks a gzip archive to `outputFile` file. - Returns 1 if operation was successfull, -1 otherwise. - desc_ru: |- - распаковывает gzip архив в файл `outputFile`. - Возвращает 1 если операция завершилась успешно, и -1 в противном случае. - example: |- - use gzip - gzip("/tmp/readme.md.gz", "/tmp/readme.md") - - - name: ungzipBytes - args: "bytes" - desc: returns uncompressed bytes. - desc_ru: возвращает распакованный gzip массив байт. - example: |- - use gzip - bytes = ungzipBytes([0, 119, 87, 80/* ... */]) - - name: functional - scope: "both" - desc: "Contains functions for operating data in functional style" - desc_ru: "Содержит функции для работы с данными в функциональном стиле" - constants: - - - name: "IDENTITY" - typeName: "function" - type: 5 - value: "def(x) = x" - desc: "function which returns passed argument" - desc_ru: "функция, которая возвращает переданный в неё аргумент" - functions: - - name: "chain" - args: "data, functions..." - desc: "" - desc_ru: "" - - name: "combine" - args: "functions..." - desc: "combines functions" - desc_ru: "комбинирует функции (композиция)" - example: |- - use functional - - def f1() = 2 - def f2(a) = a*2 - def f3(a) = a/4 - - f = combine(::f1, ::f2, ::f3) - println f() // 1 - // same as - f = def() = f3(f2(f1())) - println f() // 1 - example_ru: |- - use functional - - def f1() = 2 - def f2(a) = a*2 - def f3(a) = a/4 - - f = combine(::f1, ::f2, ::f3) - println f() // 1 - // равносильно - f = def() = f3(f2(f1())) - println f() // 1 - - name: dropwhile - args: 'data, predicate' - desc: 'skips elements while predicate function returns true' - desc_ru: 'пропускает элементы пока функция-предикат возвращает true' - - name: "filter" - args: "data, predicate" - desc: "filters array or object.\n\n`predicate` is a function which takes one argument for arrays or two arguments for objects" - desc_ru: "фильтрует массив или объект и возвращает массив только с теми элементами, которые удовлетворяют предикату `predicate`.\n\n`predicate` - функция которая принимает один (для массивов) и два (для объектов) аргумента" - example: |- - use functional - - nums = [1,2,3,4,5] - print filter(nums, def(x) = x % 2 == 0) // [2, 4] - - name: "flatmap" - args: "array, mapper" - desc: "converts each element of an array to other array" - desc_ru: "преобразует каждый элемент массива в массив элементов" - example: |- - use functional - - nums = [1,2,3,4] - print flatmap(nums, def(x) { - arr = newarray(x) - for i = 0, i < x, i++ - arr[i] = x - return arr - }) // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] - - name: "foreach" - args: "data, consumer" - desc: "invokes function `consumer` for each element of array or map `data`\n\nIf `data` - массив, то в функции consumer необходим один параметр, если объект - два (ключ и значение)." - desc_ru: "для каждого элемента в массиве или объекте `data` вызывает функцию `consumer`\n\nЕсли `data` - массив, то в функции `consumer` необходим один параметр, если объект - два (ключ и значение)." - example: |- - use functional - - foreach([1, 2, 3], def(v) { print v }) - foreach({"key": 1, "key2": "text"}, def(key, value) { - print key + ": " + value - }) - - name: "map" - args: "data, mapper..." - desc: "converts elements of array or map. If `data` is array - `mapper` converts his elements, if `data` is object - you need to pass `keyMapper` - converts keys and `valueMapper` - converts values" - desc_ru: "преобразует элементы массива или объекта.\n\nЕсли `data` - массив, то функция `mapper` преобразует значения, если объект - необходимо передать две функции: `keyMapper` - преобразует ключи и `valueMapper` - преобразует значения" - example: |- - use functional - - nums = [3,4,5] - print map(nums, def(x) = x * x) // [9, 16, 25] - - name: "reduce" - args: "data, identity, accumulator" - desc: "converts elements of an array or a map to one value, e.g. sum of elements or concatenation string. `accumulator` takes one argument for array and two arguments for object (key and value)." - desc_ru: "преобразует элементы массива или объекта в одно значение, например сумма элементов или объединение в строку.\n\nЕсли `data` - массив, то в функции `accumulator` необходим один параметр, если объект - два (ключ и значение)" - example: |- - use functional - - nums = [1,2,3,4,5] - print reduce(nums, 0, def(x, y) = x + y) // 15 - - name: "sortby" - args: "array, function" - desc: "sorts elements of an array or an object by `function` result" - desc_ru: "сортирует элементы массива по данным в функции `function`" - example: |- - use functional - - data = [ - {"k1": 2, "k2": "x"}, - {"k1": 7, "k2": "d"}, - {"k1": 4, "k2": "z"}, - {"k1": 5, "k2": "p"}, - ] - print sortby(data, def(v) = v.k1) // [{k1=2, k2=x}, {k1=4, k2=z}, {k1=5, k2=p}, {k1=7, k2=d}] - print sortby(data, def(v) = v.k2) // [{k1=7, k2=d}, {k1=5, k2=p}, {k1=2, k2=x}, {k1=4, k2=z}] - - name: "stream" - args: "data" - desc: |- - creates stream from data and returns StreamValue - - StreamValue functions: - - `filter(func)` - filters elements - - `map(func)` - converts each element - - `flatMap(func)` - converts each element to array - - `sorted(func)` - sorts elements with comparator function - - `sortBy(func)` - applies function, then sorts elements - - `takeWhile(func)` - takes elements while predicate function returns true - - `dropWhile(func)` - skips elements while predicate function returns true - - `peek(func)` - executes function for each element and returns stream - - `skip(count)` - skips count elements - - `limit(count)` - limits elements size - - `custom(func)` - performs custom operation - - `reduce(func)` - converts elements to one value - - `forEach(func)` - executes function for each element - - `joining(delimiter = "", prefix = "", suffix = "")` - joins elements into a string - - `toArray()` - returns array of elements - - `count()` - returns count of elements - desc_ru: |- - создаёт stream из данных и возвращает StreamValue - - Функции StreamValue: - - `filter(func)` - фильтрует элементы - - `map(func)` - преобразует каждый элемент - - `flatMap(func)` - преобразует каждый элемент в массив - - `sorted(func)` - сортирует элементы в соответствии с функцией-компаратором - - `sortBy(func)` - применяет функцию, затем сортирует элементы - - `takeWhile(func)` - собирает элементы пока функция-предикат возвращает true - - `dropWhile(func)` - пропускает элементы пока функция-предикат возвращает true - - `peek(func)` - вызывает функцию для каждого элемента и возвращает stream - - `skip(count)` - пропускает указанное количество элементов - - `limit(count)` - ограничивает количество элементов - - `custom(func)` - выполняет пользовательскую операцию над данными - - `reduce(func)` - преобразует элементы в одно значение - - `forEach(func)` - вызывает функцию для каждого элемента - - `joining(delimiter = "", prefix = "", suffix = "")` - склеивает элементы в строку - - `toArray()` - возвращает массив элементов - - `count()` - возвращает количество элементов - - name: takewhile - args: 'data, predicate' - desc: 'takes elements while predicate function returns true' - desc_ru: 'собирает элементы пока функция-предикат возвращает true' - - name: robot - scope: "both" - desc: "Contains functions for working with clipboard, processes, automation" - desc_ru: "Содержит функции для работы с буфером обмена, процессами, автоматизацией" - constants: - - - name: "BUTTON1" - typeName: number - type: 1 - value: "16" - desc: "left mouse button code" - desc_ru: "код левой кнопки мыши" - - - name: "BUTTON2" - typeName: number - type: 1 - value: "8" - desc: "middle mouse button code" - desc_ru: "код средней кнопки мыши" - - - name: "BUTTON3" - typeName: number - type: 1 - value: "4" - desc: "right mouse button code" - desc_ru: "код правой кнопки мыши" - - - name: "VK_DOWN" - typeName: number - type: 1 - value: "40" - desc: "key down code" - desc_ru: "код клавиши вниз" - - - name: "VK_ESCAPE" - typeName: number - type: 1 - value: "27" - desc: "Escape key code" - desc_ru: "код клавиши Escape" - - - name: "VK_FIRE" - typeName: number - type: 1 - value: "10" - desc: "Enter key code" - desc_ru: "код клавиши Enter" - - - name: "VK_LEFT" - typeName: number - type: 1 - value: "37" - desc: "key left code" - desc_ru: "код клавиши влево" - - - name: "VK_RIGHT" - typeName: number - type: 1 - value: "39" - desc: "key right code" - desc_ru: "код клавиши вправо" - functions: - - - name: "click" - args: "buttons" - scope: "desktop" - desc: "performs click with given mouse buttons" - desc_ru: "осуществляет клик мышью с заданными клавишами" - example: |- - use robot - - click(BUTTON3) // right mouse button click - example_ru: |- - use robot - - click(BUTTON3) // клик правой кнопкой мыши - - - name: "delay" - args: "ms" - scope: "desktop" - desc: "delay by given milliseconds" - desc_ru: "задержка на заданной количество миллисекунд" - - - name: "execProcess" - args: "args..." - desc: "executes the process with parameters" - desc_ru: "запускает процесс с параметрами\n\n Если функции переданы несколько аргументов, то они все передаются как параметры.\n Если функции передан только один параметр - массив, то его элементы передаются как параметры.\n Если функции передан только один параметр, то он служит единственным параметром." - example: |- - use robot - - execProcess("mkdir", "Test") - execProcess("mkdir Test") - execProcess(["mkdir", "Test"]) - - - name: "execProcessAndWait" - args: "args..." - desc: "same as `execProcess`, but waits until process completes, returns it's exit code" - desc_ru: "аналогичен функции `execProcess`, но ожидает завершение порождаемого процесса и возвращает его статус" - - - name: "fromClipboard" - args: "" - desc: "gets text from clipboard" - desc_ru: "получает строку из буфера обмена" - - - name: "keyPress" - args: "key" - scope: "desktop" - desc: "performs pressing key" - desc_ru: "осуществляет зажатие клавиши с кодом key" - - - name: "keyRelease" - args: "key" - scope: "desktop" - desc: "performs releasing key" - desc_ru: "осуществляет отпускание клавиши с кодом key" - - - name: "mouseMove" - args: "x, y" - scope: "desktop" - desc: "moves mouse pointer to given point" - desc_ru: "перемещает указатель мыши в заданную координату" - - - name: "mousePress" - args: "buttons" - scope: "desktop" - desc: "performs pressing the given mouse button" - desc_ru: "осуществляет зажатие заданной кнопки мыши" - - - name: "mouseRelease" - args: "buttons" - scope: "desktop" - desc: "performs releasing the given mouse button" - desc_ru: "осуществляет отпускание заданной кнопки мыши" - - - name: "mouseWheel" - args: "value" - scope: "desktop" - desc: "performs scrolling (< 0 - up, > 0 - down)" - desc_ru: "осуществляет прокрутку колеса мыши (отрицательное значение - вверх, положительное - вниз)" - - - name: "setAutoDelay" - args: "ms" - scope: "desktop" - desc: "sets delay after each automation event" - desc_ru: "установка длительности автоматической задержки после каждого события автоматизации" - - - name: "toClipboard" - args: "text" - desc: "adds text to clipboards" - desc_ru: "копирует строку в буфер обмена" - - - name: "typeText" - args: "text" - scope: "desktop" - desc: "performs typing text by pressing keys for each character" - desc_ru: "осуществляет последовательное нажатие клавиш для набора заданного текста" - - - name: "sudo" - args: "args..." - scope: "android" - desc: "same as `execProcess`, but executes command as root (requires rooted device)" - desc_ru: "аналогичен функции `execProcess`, но выполняет команду от имени администратора (нужен Root)" - - name: ounit - scope: "both" - desc: "Contains functions for testing. Invokes all functions with prefix `test` and checks expected and actual values, counts execution time" - desc_ru: "Содержит функции для тестирования. Поочерёдно вызывает все функции программы, которые имеют приставку `test` и подсчитывает время выполнение и расхождения с ожидаемыми значениями" - constants: [] - functions: - - - name: "assertEquals" - args: "expected, actual" - desc: "checks that two values are equal" - desc_ru: "проверяет, равны ли два значения" - - - name: "assertFalse" - args: "actual" - desc: "checks that value is false (equals 0)" - desc_ru: "проверяет, является ли значение ложным (равным нулю)" - - - name: "assertNotEquals" - args: "expected, actual" - desc: "checks that two values are not equal" - desc_ru: "проверяет, отличаются ли два значения" - - - name: "assertSameType" - args: "expected, actual" - desc: "checks that types of two values are equal" - desc_ru: "проверяет, одинаковы ли типы у двух значений" - - - name: "assertTrue" - args: "actual" - desc: "checks that value is true (not equals 0)" - desc_ru: "проверяет, является ли значение истинным (не равным нулю)" - - - name: "runTests" - args: "" - desc: "executes tests and returns information about it's results" - desc_ru: "запускает тесты и возвращает информацию о них по завершению работы в виде строки" - example: |- - use ounit - - def testAdditionOnNumbers() { - assertEquals(6, 0 + 1 + 2 + 3) - } - - def testTypes() { - assertSameType(0, 0.0) - } - - def testFail() { - assertTrue(false) - } - - println runTests() - - /* - testTypes [passed] - Elapsed: 0,0189 sec - - testAdditionOnNumbers [passed] - Elapsed: 0,0008 sec - - testFail [FAILED] - Expected true, but found false. - Elapsed: 0,0001 sec - - Tests run: 3, Failures: 1, Time elapsed: 0,0198 sec - */ - - name: canvas - scope: "desktop" - desc: "Contains functions for working with graphics" - desc_ru: "Содержит функции для работы с графикой" - constants: - - - name: "VK_DOWN" - typeName: number - type: 1 - value: "40" - desc: "arrow down key code" - desc_ru: "код клавиши стрелка вниз" - - - name: "VK_ESCAPE" - typeName: number - type: 1 - value: "27" - desc: "Esc key code" - desc_ru: "код клавиши Esc" - - - name: "VK_FIRE" - typeName: number - type: 1 - value: "10" - desc: "Enter key code" - desc_ru: "код клавиши Enter" - - - name: "VK_LEFT" - typeName: number - type: 1 - value: "37" - desc: "arrow left key code" - desc_ru: "код клавиши стрелка влево" - - - name: "VK_RIGHT" - typeName: number - type: 1 - value: "39" - desc: "arrow left key code" - desc_ru: "код клавиши стрелка вправо" - - - name: "VK_UP" - typeName: number - type: 1 - value: "38" - desc: "arrow up key code" - desc_ru: "код клавиши стрелка вверх" - functions: - - - name: "clip" - args: "x, y, w, h" - desc: "sets the current clip to the rectangle specified by the given coordinates" - desc_ru: "устанавливает текущий клип в прямоугольник, заданный данными координатами" - - - name: "color" - args: "rgb" - desc: "sets color drawing. `rgb` - color with the specified combined RGB value" - desc_ru: "устанвливает цвет рисования. `rgb` - целое, комбинация цветов RGB, например `#FFGGFF`" - - - name: "color" - args: "red, green, blue" - desc: "sets color with the specified red, green, and blue values in the range (0 - 255)" - desc_ru: "устанвливает цвет рисования c отдельными уровнями красного, зеленого и синего в диапазоне (0 - 255)" - - - name: "drawstring" - args: "text, x, y" - desc: "draws string `text` at position `x`, `y`" - desc_ru: "рисует строку `text` с координатами `x`, `y`" - - - name: "foval" - args: "x, y, w, h" - desc: "draws a filled oval at position `x`,` y`, size `w`,` h`" - desc_ru: "рисует закрашенный овал на позиции `x`, `y`, размером `w`, `h`" - - - name: "frect" - args: "x, y, w, h" - desc: "draws a filled rectangle at position `x`,` y`, size `w`,` h`" - desc_ru: "рисует закрашенный прямоугольник на позиции `x`, `y`, размером `w`, `h`" - - - name: "keypressed" - args: "" - desc: "returns the code of the pressed key (see the constant section)" - desc_ru: "возрвращает код нажатой клавиши (см. раздел константы)" - - - name: "line" - args: "x1, y1, x2, y2" - desc: "draws line from point (`x1`;y1`) to (`x2`;y2`)" - desc_ru: "рисует линию от позиции (`x1`;y1`) до (`x2`;y2`)" - - - name: "mousehover" - args: "" - desc: "returns array with current mouse pointer coordinates" - desc_ru: "возвращает массив с текущими координатами указателя мыши" - - - name: "oval" - args: "x, y, w, h" - desc: "draws a oval at position `x`,` y`, size `w`,` h`" - desc_ru: "рисует овал на позиции `x`, `y`, размером `w`, `h`" - - - name: "prompt" - args: "message" - desc: "displays a dialog box that prompts the visitor for input" - desc_ru: "показывает диалог для ввода значения от пользователя" - - - name: "rect" - args: "x, y, w, h" - desc: "draws a rectangle at position `x`,` y`, size `w`,` h`" - desc_ru: "рисует прямоугольник на позиции `x`, `y`, размером `w`, `h`" - - - name: "repaint" - args: "" - desc: "draws elements from graphics buffer on canvas" - desc_ru: "прорисовывает элементы из буфера на холсте" - - - name: "window" - args: "name, width, hight" - desc: "creates a new window with the specified `name` and size `width`x`height`" - desc_ru: "создает новое окно с именем `name` и размером `width`x`height`" - - name: canvasfx - scope: "desktop" - desc: "Contains functions for working with Java FX graphics" - desc_ru: "Содержит функции для работы с графикой Java FX" - constants: - - - name: "ArcType" - typeName: map - type: 4 - value: "{OPEN=0, CHORD=1, ROUND=2}" - - - name: "BlendMode" - typeName: map - type: 4 - value: "{SRC_OVER=0, SRC_ATOP=1, ADD=2, MULTIPLY=3, SCREEN=4, OVERLAY=5, DARKEN=6, LIGHTEN=7, COLOR_DODGE=8, COLOR_BURN=9, HARD_LIGHT=10, SOFT_LIGHT=11, DIFFERENCE=12, EXCLUSION=13, RED=14, GREEN=15, BLUE=16}" - - - name: "Color" - typeName: map - type: 4 - value: "{hsb=def(hue,saturation,brightness,opacity=1.0), new=def(rgb) def(r,g,b,opacity=1.0), rgb=def(r,g,b,opacity=1.0), web=def(name,opacity=1.0, ALICEBLUE=ColorValue 0xf0f8ffff, ANTIQUEWHITE=ColorValue 0xfaebd7ff, AQUA=ColorValue 0x00ffffff, AQUAMARINE=ColorValue 0x7fffd4ff, AZURE=ColorValue 0xf0ffffff, BEIGE=ColorValue 0xf5f5dcff, BISQUE=ColorValue 0xffe4c4ff, BLACK=ColorValue 0x000000ff, BLANCHEDALMOND=ColorValue 0xffebcdff, BLUE=ColorValue 0x0000ffff, BLUEVIOLET=ColorValue 0x8a2be2ff, BROWN=ColorValue 0xa52a2aff, BURLYWOOD=ColorValue 0xdeb887ff, CADETBLUE=ColorValue 0x5f9ea0ff, CHARTREUSE=ColorValue 0x7fff00ff, CHOCOLATE=ColorValue 0xd2691eff, CORAL=ColorValue 0xff7f50ff, CORNFLOWERBLUE=ColorValue 0x6495edff, CORNSILK=ColorValue 0xfff8dcff, CRIMSON=ColorValue 0xdc143cff, CYAN=ColorValue 0x00ffffff, DARKBLUE=ColorValue 0x00008bff, DARKCYAN=ColorValue 0x008b8bff, DARKGOLDENROD=ColorValue 0xb8860bff, DARKGRAY=ColorValue 0xa9a9a9ff, DARKGREEN=ColorValue 0x006400ff, DARKGREY=ColorValue 0xa9a9a9ff, DARKKHAKI=ColorValue 0xbdb76bff, DARKMAGENTA=ColorValue 0x8b008bff, DARKOLIVEGREEN=ColorValue 0x556b2fff, DARKORANGE=ColorValue 0xff8c00ff, DARKORCHID=ColorValue 0x9932ccff, DARKRED=ColorValue 0x8b0000ff, DARKSALMON=ColorValue 0xe9967aff, DARKSEAGREEN=ColorValue 0x8fbc8fff, DARKSLATEBLUE=ColorValue 0x483d8bff, DARKSLATEGRAY=ColorValue 0x2f4f4fff, DARKSLATEGREY=ColorValue 0x2f4f4fff, DARKTURQUOISE=ColorValue 0x00ced1ff, DARKVIOLET=ColorValue 0x9400d3ff, DEEPPINK=ColorValue 0xff1493ff, DEEPSKYBLUE=ColorValue 0x00bfffff, DIMGRAY=ColorValue 0x696969ff, DIMGREY=ColorValue 0x696969ff, DODGERBLUE=ColorValue 0x1e90ffff, FIREBRICK=ColorValue 0xb22222ff, FLORALWHITE=ColorValue 0xfffaf0ff, FORESTGREEN=ColorValue 0x228b22ff, FUCHSIA=ColorValue 0xff00ffff, GAINSBORO=ColorValue 0xdcdcdcff, GHOSTWHITE=ColorValue 0xf8f8ffff, GOLD=ColorValue 0xffd700ff, GOLDENROD=ColorValue 0xdaa520ff, GRAY=ColorValue 0x808080ff, GREEN=ColorValue 0x008000ff, GREENYELLOW=ColorValue 0xadff2fff, GREY=ColorValue 0x808080ff, HONEYDEW=ColorValue 0xf0fff0ff, HOTPINK=ColorValue 0xff69b4ff, INDIANRED=ColorValue 0xcd5c5cff, INDIGO=ColorValue 0x4b0082ff, IVORY=ColorValue 0xfffff0ff, KHAKI=ColorValue 0xf0e68cff, LAVENDER=ColorValue 0xe6e6faff, LAVENDERBLUSH=ColorValue 0xfff0f5ff, LAWNGREEN=ColorValue 0x7cfc00ff, LEMONCHIFFON=ColorValue 0xfffacdff, LIGHTBLUE=ColorValue 0xadd8e6ff, LIGHTCORAL=ColorValue 0xf08080ff, LIGHTCYAN=ColorValue 0xe0ffffff, LIGHTGOLDENRODYELLOW=ColorValue 0xfafad2ff, LIGHTGRAY=ColorValue 0xd3d3d3ff, LIGHTGREEN=ColorValue 0x90ee90ff, LIGHTGREY=ColorValue 0xd3d3d3ff, LIGHTPINK=ColorValue 0xffb6c1ff, LIGHTSALMON=ColorValue 0xffa07aff, LIGHTSEAGREEN=ColorValue 0x20b2aaff, LIGHTSKYBLUE=ColorValue 0x87cefaff, LIGHTSLATEGRAY=ColorValue 0x778899ff, LIGHTSLATEGREY=ColorValue 0x778899ff, LIGHTSTEELBLUE=ColorValue 0xb0c4deff, LIGHTYELLOW=ColorValue 0xffffe0ff, LIME=ColorValue 0x00ff00ff, LIMEGREEN=ColorValue 0x32cd32ff, LINEN=ColorValue 0xfaf0e6ff, MAGENTA=ColorValue 0xff00ffff, MAROON=ColorValue 0x800000ff, MEDIUMAQUAMARINE=ColorValue 0x66cdaaff, MEDIUMBLUE=ColorValue 0x0000cdff, MEDIUMORCHID=ColorValue 0xba55d3ff, MEDIUMPURPLE=ColorValue 0x9370dbff, MEDIUMSEAGREEN=ColorValue 0x3cb371ff, MEDIUMSLATEBLUE=ColorValue 0x7b68eeff, MEDIUMSPRINGGREEN=ColorValue 0x00fa9aff, MEDIUMTURQUOISE=ColorValue 0x48d1ccff, MEDIUMVIOLETRED=ColorValue 0xc71585ff, MIDNIGHTBLUE=ColorValue 0x191970ff, MINTCREAM=ColorValue 0xf5fffaff, MISTYROSE=ColorValue 0xffe4e1ff, MOCCASIN=ColorValue 0xffe4b5ff, NAVAJOWHITE=ColorValue 0xffdeadff, NAVY=ColorValue 0x000080ff, OLDLACE=ColorValue 0xfdf5e6ff, OLIVE=ColorValue 0x808000ff, OLIVEDRAB=ColorValue 0x6b8e23ff, ORANGE=ColorValue 0xffa500ff, ORANGERED=ColorValue 0xff4500ff, ORCHID=ColorValue 0xda70d6ff, PALEGOLDENROD=ColorValue 0xeee8aaff, PALEGREEN=ColorValue 0x98fb98ff, PALETURQUOISE=ColorValue 0xafeeeeff, PALEVIOLETRED=ColorValue 0xdb7093ff, PAPAYAWHIP=ColorValue 0xffefd5ff, PEACHPUFF=ColorValue 0xffdab9ff, PERU=ColorValue 0xcd853fff, PINK=ColorValue 0xffc0cbff, PLUM=ColorValue 0xdda0ddff, POWDERBLUE=ColorValue 0xb0e0e6ff, PURPLE=ColorValue 0x800080ff, RED=ColorValue 0xff0000ff, ROSYBROWN=ColorValue 0xbc8f8fff, ROYALBLUE=ColorValue 0x4169e1ff, SADDLEBROWN=ColorValue 0x8b4513ff, SALMON=ColorValue 0xfa8072ff, SANDYBROWN=ColorValue 0xf4a460ff, SEAGREEN=ColorValue 0x2e8b57ff, SEASHELL=ColorValue 0xfff5eeff, SIENNA=ColorValue 0xa0522dff, SILVER=ColorValue 0xc0c0c0ff, SKYBLUE=ColorValue 0x87ceebff, SLATEBLUE=ColorValue 0x6a5acdff, SLATEGRAY=ColorValue 0x708090ff, SLATEGREY=ColorValue 0x708090ff, SNOW=ColorValue 0xfffafaff, SPRINGGREEN=ColorValue 0x00ff7fff, STEELBLUE=ColorValue 0x4682b4ff, TAN=ColorValue 0xd2b48cff, TEAL=ColorValue 0x008080ff, THISTLE=ColorValue 0xd8bfd8ff, TOMATO=ColorValue 0xff6347ff, TRANSPARENT=ColorValue 0x00000000, TURQUOISE=ColorValue 0x40e0d0ff, VIOLET=ColorValue 0xee82eeff, WHEAT=ColorValue 0xf5deb3ff, WHITE=ColorValue 0xffffffff, WHITESMOKE=ColorValue 0xf5f5f5ff, YELLOW=ColorValue 0xffff00ff, YELLOWGREEN=ColorValue 0x9acd32ff}" - - - name: "Events" - typeName: map - type: 4 - value: "{DRAG_DETECTED=0, MOUSE_CLICKED=1, MOUSE_DRAGGED=2, MOUSE_ENTERED=3, MOUSE_ENTERED_TARGET=4, MOUSE_EXITED=5, MOUSE_EXITED_TARGET=6, MOUSE_MOVED=7, MOUSE_PRESSED=8, MOUSE_RELEASED=9, KEY_PRESSED=10, KEY_RELEASED=11, KEY_TYPED=12, SWIPE_DOWN=13, SWIPE_LEFT=14, SWIPE_RIGHT=15, SWIPE_UP=16}" - - - name: "FillRule" - typeName: map - type: 4 - value: "{EVEN_ODD=0, NON_ZERO=1}" - - - name: "KeyCode" - typeName: map - type: 4 - value: "{A=36, ACCEPT=158, ADD=76, AGAIN=180, ALL_CANDIDATES=168, ALPHANUMERIC=162, ALT=7, ALT_GRAPH=185, AMPERSAND=134, ASTERISK=135, AT=141, B=37, BACK_QUOTE=112, BACK_SLASH=63, BACK_SPACE=1, BEGIN=186, BRACELEFT=139, BRACERIGHT=140, C=38, CANCEL=3, CAPS=9, CHANNEL_DOWN=218, CHANNEL_UP=217, CIRCUMFLEX=143, CLEAR=4, CLOSE_BRACKET=64, CODE_INPUT=170, COLON=142, COLORED_KEY_0=206, COLORED_KEY_1=207, COLORED_KEY_2=208, COLORED_KEY_3=209, COMMA=20, COMMAND=222, COMPOSE=184, CONTEXT_MENU=154, CONTROL=6, CONVERT=156, COPY=177, CUT=176, D=39, DEAD_ABOVEDOT=124, DEAD_ABOVERING=126, DEAD_ACUTE=119, DEAD_BREVE=123, DEAD_CARON=128, DEAD_CEDILLA=129, DEAD_CIRCUMFLEX=120, DEAD_DIAERESIS=125, DEAD_DOUBLEACUTE=127, DEAD_GRAVE=118, DEAD_IOTA=131, DEAD_MACRON=122, DEAD_OGONEK=130, DEAD_SEMIVOICED_SOUND=133, DEAD_TILDE=121, DEAD_VOICED_SOUND=132, DECIMAL=79, DELETE=81, DIGIT0=24, DIGIT1=25, DIGIT2=26, DIGIT3=27, DIGIT4=28, DIGIT5=29, DIGIT6=30, DIGIT7=31, DIGIT8=32, DIGIT9=33, DIVIDE=80, DOLLAR=144, DOWN=19, E=40, EJECT_TOGGLE=210, END=14, ENTER=0, EQUALS=35, ESCAPE=10, EURO_SIGN=145, EXCLAMATION_MARK=146, F=41, F1=84, F10=93, F11=94, F12=95, F13=96, F14=97, F15=98, F16=99, F17=100, F18=101, F19=102, F2=85, F20=103, F21=104, F22=105, F23=106, F24=107, F3=86, F4=87, F5=88, F6=89, F7=90, F8=91, F9=92, FAST_FWD=213, FINAL=155, FIND=181, FULL_WIDTH=165, G=42, GAME_A=198, GAME_B=199, GAME_C=200, GAME_D=201, GREATER=138, H=43, HALF_WIDTH=166, HELP=110, HIRAGANA=164, HOME=15, I=44, INFO=205, INPUT_METHOD_ON_OFF=175, INSERT=109, INVERTED_EXCLAMATION_MARK=147, J=45, JAPANESE_HIRAGANA=172, JAPANESE_KATAKANA=171, JAPANESE_ROMAN=173, K=46, KANA=160, KANA_LOCK=174, KANJI=161, KATAKANA=163, KP_DOWN=115, KP_LEFT=116, KP_RIGHT=117, KP_UP=114, L=47, LEFT=16, LEFT_PARENTHESIS=148, LESS=137, M=48, META=111, MINUS=21, MODECHANGE=159, MULTIPLY=75, MUTE=221, N=49, NONCONVERT=157, NUMBER_SIGN=149, NUMPAD0=65, NUMPAD1=66, NUMPAD2=67, NUMPAD3=68, NUMPAD4=69, NUMPAD5=70, NUMPAD6=71, NUMPAD7=72, NUMPAD8=73, NUMPAD9=74, NUM_LOCK=82, O=50, OPEN_BRACKET=62, P=51, PAGE_DOWN=13, PAGE_UP=12, PASTE=178, PAUSE=8, PERIOD=22, PLAY=211, PLUS=150, POUND=203, POWER=204, PREVIOUS_CANDIDATE=169, PRINTSCREEN=108, PROPS=182, Q=52, QUOTE=113, QUOTEDBL=136, R=53, RECORD=212, REWIND=214, RIGHT=18, RIGHT_PARENTHESIS=151, ROMAN_CHARACTERS=167, S=54, SCROLL_LOCK=83, SEMICOLON=34, SEPARATOR=77, SHIFT=5, SHORTCUT=223, SLASH=23, SOFTKEY_0=188, SOFTKEY_1=189, SOFTKEY_2=190, SOFTKEY_3=191, SOFTKEY_4=192, SOFTKEY_5=193, SOFTKEY_6=194, SOFTKEY_7=195, SOFTKEY_8=196, SOFTKEY_9=197, SPACE=11, STAR=202, STOP=183, SUBTRACT=78, T=55, TAB=2, TRACK_NEXT=216, TRACK_PREV=215, U=56, UNDEFINED=187, UNDERSCORE=152, UNDO=179, UP=17, V=57, VOLUME_DOWN=220, VOLUME_UP=219, W=58, WINDOWS=153, X=59, Y=60, Z=61}" - - - name: "MouseButton" - typeName: map - type: 4 - value: "{NONE=0, PRIMARY=1, MIDDLE=2, SECONDARY=3}" - - - name: "StrokeLineCap" - typeName: map - type: 4 - value: "{SQUARE=0, BUTT=1, ROUND=2}" - - - name: "StrokeLineJoin" - typeName: map - type: 4 - value: "{MITER=0, BEVEL=1, ROUND=2}" - - - name: "TextAlignment" - typeName: map - type: 4 - value: "{LEFT=0, CENTER=1, RIGHT=2, JUSTIFY=3}" - - - name: "VPos" - typeName: map - type: 4 - value: "{TOP=0, CENTER=1, BASELINE=2, BOTTOM=3}" - functions: - - - name: "BlendEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "BloomEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "BoxBlurEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "ColorAdjustEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "ColorInputEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "DropShadowEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "GaussianBlurEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "GlowEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "InnerShadowEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "LightingEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "MotionBlurEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "PerspectiveTransformEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "ReflectionEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "SepiaToneEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "ShadowEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "addEventFilter" - args: "" - desc: "" - desc_ru: "" - - - name: "addEventHandler" - args: "" - desc: "" - desc_ru: "" - - - name: "createImage" - args: "..." - desc: |- - `createImage(url)` - creates an image from url. - - `createImage(w, h)` - creates an image with the given size. - - `createBitmap(w, h, pixels)` - creates an image from pixels array. - - Returns ImageFXValue. - desc_ru: |- - `createImage(url)` - создаёт изображение из пути к ресурсу. - - `createImage(w, h)` - создаёт новое изображение с заданным размером. - - `createBitmap(w, h, pixels)` - создаёт изображение из массива пикселей. - - Возвращает ImageFXValue. - example: |- - use canvasfx - - g = showcanvas() - url = "http://lorempixel.com/640/480/nature" - bitmap = createImage(url) - g.drawBitmap(bitmap, 0, 0) - bitmap = createImage("file:image.png") - g.drawBitmap(bitmap, 200, 0) - - - name: "repaint" - args: "" - desc: "" - desc_ru: "" - - - name: "window" - args: "" - desc: "" - desc_ru: "" - types: - - - name: "ColorValue" - - - name: "EffectValue" - - - name: "GraphicsFXValue" - functions: - - - name: "appendSVGPath" - args: "" - desc: "" - desc_ru: "" - - - name: "applyEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "arc" - args: "" - desc: "" - desc_ru: "" - - - name: "arcTo" - args: "" - desc: "" - desc_ru: "" - - - name: "beginPath" - args: "" - desc: "" - desc_ru: "" - - - name: "bezierCurveTo" - args: "" - desc: "" - desc_ru: "" - - - name: "clearRect" - args: "" - desc: "" - desc_ru: "" - - - name: "clip" - args: "" - desc: "" - desc_ru: "" - - - name: "closePath" - args: "" - desc: "" - desc_ru: "" - - - name: "fill" - args: "" - desc: "" - desc_ru: "" - - - name: "fillArc" - args: "" - desc: "" - desc_ru: "" - - - name: "fillOval" - args: "" - desc: "" - desc_ru: "" - - - name: "fillPolygon" - args: "" - desc: "" - desc_ru: "" - - - name: "fillRect" - args: "" - desc: "" - desc_ru: "" - - - name: "fillRoundRect" - args: "" - desc: "" - desc_ru: "" - - - name: "fillText" - args: "" - desc: "" - desc_ru: "" - - - name: "getFill" - args: "" - desc: "" - desc_ru: "" - - - name: "getFillRule" - args: "" - desc: "" - desc_ru: "" - - - name: "getGlobalAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "getGlobalBlendMode" - args: "" - desc: "" - desc_ru: "" - - - name: "getLineCap" - args: "" - desc: "" - desc_ru: "" - - - name: "getLineJoin" - args: "" - desc: "" - desc_ru: "" - - - name: "getLineWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "getMiterLimit" - args: "" - desc: "" - desc_ru: "" - - - name: "getStroke" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextAlign" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextBaseline" - args: "" - desc: "" - desc_ru: "" - - - name: "isPointInPath" - args: "" - desc: "" - desc_ru: "" - - - name: "lineTo" - args: "" - desc: "" - desc_ru: "" - - - name: "moveTo" - args: "" - desc: "" - desc_ru: "" - - - name: "quadraticCurveTo" - args: "" - desc: "" - desc_ru: "" - - - name: "rect" - args: "" - desc: "" - desc_ru: "" - - - name: "restore" - args: "" - desc: "" - desc_ru: "" - - - name: "rotate" - args: "" - desc: "" - desc_ru: "" - - - name: "save" - args: "" - desc: "" - desc_ru: "" - - - name: "scale" - args: "" - desc: "" - desc_ru: "" - - - name: "setEffect" - args: "" - desc: "" - desc_ru: "" - - - name: "setFill" - args: "" - desc: "" - desc_ru: "" - - - name: "setFillRule" - args: "" - desc: "" - desc_ru: "" - - - name: "setGlobalAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "setGlobalBlendMode" - args: "" - desc: "" - desc_ru: "" - - - name: "setLineCap" - args: "" - desc: "" - desc_ru: "" - - - name: "setLineJoin" - args: "" - desc: "" - desc_ru: "" - - - name: "setLineWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "setMiterLimit" - args: "" - desc: "" - desc_ru: "" - - - name: "setStroke" - args: "" - desc: "" - desc_ru: "" - - - name: "setTextAlign" - args: "" - desc: "" - desc_ru: "" - - - name: "setTextBaseline" - args: "" - desc: "" - desc_ru: "" - - - name: "stroke" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeArc" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeLine" - args: "x1, y1, x2, y2" - desc: "" - desc_ru: "" - - - name: "strokeOval" - args: "" - desc: "" - desc_ru: "" - - - name: "strokePolygon" - args: "" - desc: "" - desc_ru: "" - - - name: "strokePolyline" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeRect" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeRoundRect" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeText" - args: "" - desc: "" - desc_ru: "" - - - name: "transform" - args: "" - desc: "" - desc_ru: "" - - - name: "translate" - args: "" - desc: "" - desc_ru: "" - - - name: "ImageFXValue" - constants: - - - name: "width" - typeName: number - type: 1 - value: "/*width of the image*/" - - - name: "height" - typeName: number - type: 1 - value: "/*height of the image*/" - - - name: "preserveRatio" - typeName: number - type: 1 - value: "/*is preserve ratio*/" - - - name: "smooth" - typeName: number - type: 1 - value: "/*is smooth*/" - functions: - - - name: "getPixels" - args: "" - desc: "returns pixels array of the image" - desc_ru: "возвращает массив пикселей изображения" - - name: forms - scope: desktop - desc: "Contains functions for working with forms" - desc_ru: "Содержит функции для работы с формами" - constants: - - name: BorderLayout - type: 4 - typeName: map - value: '{AFTER_LINE_ENDS=After, LINE_END=After, LINE_START=Before, BEFORE_LINE_BEGINS=Before, CENTER=Center, EAST=East, BEFORE_FIRST_LINE=First, PAGE_START=First, AFTER_LAST_LINE=Last, PAGE_END=Last, NORTH=North, SOUTH=South, WEST=West}' - - name: BoxLayout - type: 4 - typeName: map - value: '{X_AXIS=0, Y_AXIS=1, LINE_AXIS=2, PAGE_AXIS=3}' - - name: DISPOSE_ON_CLOSE - type: 1 - typeName: number - value: '2' - - name: DO_NOTHING_ON_CLOSE - type: 1 - typeName: number - value: '0' - - name: EXIT_ON_CLOSE - type: 1 - typeName: number - value: '3' - - name: HIDE_ON_CLOSE - type: 1 - typeName: number - value: '1' - - name: SwingConstants - type: 4 - typeName: map - value: '{BOTTOM=3, CENTER=0, EAST=3, HORIZONTAL=0, LEADING=10, LEFT=2, NEXT=12, NORTH=1, NORTH_EAST=2, NORTH_WEST=8, PREVIOUS=13, RIGHT=4, SOUTH=5, SOUTH_EAST=4, SOUTH_WEST=6, TOP=1, TRAILING=11, VERTICAL=1, WEST=7}' - functions: - - name: borderLayout - args: 'hgap = 0, vgap = 0' - desc: 'creates BorderLayout' - desc_ru: 'создаёт BorderLayout' - - name: boxLayout - args: 'panel, axis = BoxLayout.PAGE_AXIS' - desc: 'creates BoxLayout' - desc_ru: 'создаёт BoxLayout' - - name: cardLayout - args: 'hgap = 0, vgap = 0' - desc: 'creates CardLayout' - desc_ru: 'создаёт CardLayout' - - name: flowLayout - args: 'align = FlowLayout.CENTER, hgap = 5, vgap = 5' - desc: 'creates FlowLayout' - desc_ru: 'создаёт FlowLayout' - - name: gridLayout - args: 'rows = 1, cols = 0, hgap = 0, vgap = 0' - desc: 'creates GridLayout' - desc_ru: 'создаёт GridLayout' - - name: newButton - args: 'text = ""' - desc: 'creates new button' - desc_ru: 'создаёт новую кнопку' - - name: newLabel - args: 'text = "", align = SwingConstants.LEADING' - desc: 'creates new label' - desc_ru: 'создаёт новую текстовую метку' - - name: newPanel - args: 'layoutManager = ...' - desc: 'creates new panel with optional layout manager' - desc_ru: 'создаёт новую панель с опциональным LayoutManager' - - name: newProgressBar - args: 'isVertical = false, min = 0, max = 100' - desc: 'creates new progress bar' - desc_ru: 'создаёт новый прогрессбар' - since: 1.5.0 - - name: newScrollPane - args: 'view, verticalPolicy = AS_NEEDED, horizontalPolicy = AS_NEEDED' - desc: 'creates new scroll pane' - desc_ru: 'создаёт новую область прокрутки' - since: 1.5.0 - - name: newTextArea - args: 'text = ""' - desc: 'creates new text area' - desc_ru: 'создаёт новую область ввода' - since: 1.5.0 - - name: newTextField - args: 'text = "", rows = 0, cols = 0' - desc: 'creates new text field' - desc_ru: 'создаёт новое поле ввода' - - name: newWindow - args: 'title = ""' - desc: 'creates new window and returns JFrameValue' - desc_ru: 'создаёт новое окно и возвращает JFrameValue' - - name: java - scope: both - constants: - - name: Object.class - type: 4 - typeName: map - value: 'java.lang.Object' - - name: Object[].class - type: 4 - typeName: map - value: 'java.lang.Object[]' - - name: Object[][].class - type: 4 - typeName: map - value: 'java.lang.Object[][]' - - name: String.class - type: 4 - typeName: map - value: 'java.lang.String' - - name: String[].class - type: 4 - typeName: map - value: 'java.lang.String[]' - - name: String[][].class - type: 4 - typeName: map - value: 'java.lang.String[][]' - - name: boolean.class - type: 4 - typeName: map - value: 'boolean' - - name: boolean[].class - type: 4 - typeName: map - value: 'boolean[]' - - name: boolean[][].class - type: 4 - typeName: map - value: 'boolean[][]' - - name: byte.class - type: 4 - typeName: map - value: 'byte' - - name: byte[].class - type: 4 - typeName: map - value: 'byte[]' - - name: byte[][].class - type: 4 - typeName: map - value: 'byte[][]' - - name: char.class - type: 4 - typeName: map - value: 'char' - - name: char[].class - type: 4 - typeName: map - value: 'char[]' - - name: char[][].class - type: 4 - typeName: map - value: 'char[][]' - - name: double.class - type: 4 - typeName: map - value: 'double' - - name: double[].class - type: 4 - typeName: map - value: 'double[]' - - name: double[][].class - type: 4 - typeName: map - value: 'double[][]' - - name: float.class - type: 4 - typeName: map - value: 'float' - - name: float[].class - type: 4 - typeName: map - value: 'float[]' - - name: float[][].class - type: 4 - typeName: map - value: 'float[][]' - - name: int.class - type: 4 - typeName: map - value: 'int' - - name: int[].class - type: 4 - typeName: map - value: 'int[]' - - name: int[][].class - type: 4 - typeName: map - value: 'int[][]' - - name: long.class - type: 4 - typeName: map - value: 'long' - - name: long[].class - type: 4 - typeName: map - value: 'long[]' - - name: long[][].class - type: 4 - typeName: map - value: 'long[][]' - - name: 'null' - type: 482862660 - typeName: unknown (482862660) - value: 'null' - - name: short.class - type: 4 - typeName: map - value: 'short' - - name: short[].class - type: 4 - typeName: map - value: 'short[]' - - name: short[][].class - type: 4 - typeName: map - value: 'short[][]' - functions: - - name: isNull - args: 'values...' - desc: 'checks if one or more values are null' - desc_ru: 'проверяет, является ли одно или несколько значений null' - - name: newClass - args: 'name' - desc: 'creates ClassValue' - desc_ru: 'создаёт ClassValue' - - name: toObject - args: 'ownlangValue' - desc: 'converts ownlangValue to Java object' - desc_ru: 'конвертирует ownlangValue в Java объект' - - name: toValue - args: 'javaObject' - desc: 'converts javaObject to OwnLang value' - desc_ru: 'конвертирует javaObject в OwnLang значение' - types: - - name: ClassValue - functions: - - name: "asSubclass" - args: "" - desc: "" - desc_ru: "" - - name: "canonicalName" - args: "" - desc: "" - desc_ru: "" - - name: "cast" - args: "" - desc: "" - desc_ru: "" - - name: "genericString" - args: "" - desc: "" - desc_ru: "" - - name: "getComponentType" - args: "" - desc: "" - desc_ru: "" - - name: "getDeclaringClass" - args: "" - desc: "" - desc_ru: "" - - name: "getEnclosingClass" - args: "" - desc: "" - desc_ru: "" - - name: "getSuperclass" - args: "" - desc: "" - desc_ru: "" - - name: "getClasses" - args: "" - desc: "" - desc_ru: "" - - name: "getDeclaredClasses" - args: "" - desc: "" - desc_ru: "" - - name: "getInterfaces" - args: "" - desc: "" - desc_ru: "" - - name: "isAnnotation" - args: "" - desc: "" - desc_ru: "" - - name: "isAnonymousClass" - args: "" - desc: "" - desc_ru: "" - - name: "isArray" - args: "" - desc: "" - desc_ru: "" - - name: "isAssignableFrom" - args: "" - desc: "" - desc_ru: "" - - name: "isEnum" - args: "" - desc: "" - desc_ru: "" - - name: "isInterface" - args: "" - desc: "" - desc_ru: "" - - name: "isLocalClass" - args: "" - desc: "" - desc_ru: "" - - name: "isMemberClass" - args: "" - desc: "" - desc_ru: "" - - name: "isPrimitive" - args: "" - desc: "" - desc_ru: "" - - name: "isSynthetic" - args: "" - desc: "" - desc_ru: "" - - name: "modifiers" - args: "" - desc: "" - desc_ru: "" - - name: "name" - args: "" - desc: "" - desc_ru: "" - - name: "new" - args: "" - desc: "creates new instance of class" - desc_ru: "создаёт новый экземпляр класса" - - name: "simpleName" - args: "" - desc: "" - desc_ru: "" - - name: "typeName" - args: "" - desc: "" - desc_ru: "" - - name: NullValue - - name: ObjectValue - - name: jdbc - scope: desktop - constants: - - name: CLOSE_ALL_RESULTS - type: 1 - typeName: number - value: '3' - - name: CLOSE_CURRENT_RESULT - type: 1 - typeName: number - value: '1' - - name: CLOSE_CURSORS_AT_COMMIT - type: 1 - typeName: number - value: '2' - - name: CONCUR_READ_ONLY - type: 1 - typeName: number - value: '1007' - - name: CONCUR_UPDATABLE - type: 1 - typeName: number - value: '1008' - - name: EXECUTE_FAILED - type: 1 - typeName: number - value: '-3' - - name: FETCH_FORWARD - type: 1 - typeName: number - value: '1000' - - name: FETCH_REVERSE - type: 1 - typeName: number - value: '1001' - - name: FETCH_UNKNOWN - type: 1 - typeName: number - value: '1002' - - name: HOLD_CURSORS_OVER_COMMIT - type: 1 - typeName: number - value: '1' - - name: KEEP_CURRENT_RESULT - type: 1 - typeName: number - value: '2' - - name: NO_GENERATED_KEYS - type: 1 - typeName: number - value: '2' - - name: RETURN_GENERATED_KEYS - type: 1 - typeName: number - value: '1' - - name: SUCCESS_NO_INFO - type: 1 - typeName: number - value: '-2' - - name: TRANSACTION_NONE - type: 1 - typeName: number - value: '0' - - name: TRANSACTION_READ_COMMITTED - type: 1 - typeName: number - value: '2' - - name: TRANSACTION_READ_UNCOMMITTED - type: 1 - typeName: number - value: '1' - - name: TRANSACTION_REPEATABLE_READ - type: 1 - typeName: number - value: '4' - - name: TRANSACTION_SERIALIZABLE - type: 1 - typeName: number - value: '8' - - name: TYPE_FORWARD_ONLY - type: 1 - typeName: number - value: '1003' - - name: TYPE_SCROLL_INSENSITIVE - type: 1 - typeName: number - value: '1004' - - name: TYPE_SCROLL_SENSITIVE - type: 1 - typeName: number - value: '1005' - functions: - - name: getConnection - args: '...' - desc: |- - `getConnection(connectionUrl)` - - `getConnection(connectionUrl, driverClassName)` - - `getConnection(connectionUrl, user, password)` - - `getConnection(connectionUrl, user, password, driverClassName)` - - Creates connection and returns ConnectionValue. - desc_ru: |- - `getConnection(connectionUrl)` - - `getConnection(connectionUrl, driverClassName)` - - `getConnection(connectionUrl, user, password)` - - `getConnection(connectionUrl, user, password, driverClassName)` - - Создаёт подключение и возвращает ConnectionValue. - - name: mysql - args: 'connectionUrl' - desc: 'creates mysql connection' - desc_ru: 'создаёт mysql подключение' - - name: sqlite - args: 'connectionUrl' - desc: 'creates sqlite connection' - desc_ru: 'создаёт sqlite подключение' - types: - - name: ConnectionValue - functions: - - name: "clearWarnings" - args: "" - desc: "" - desc_ru: "" - - name: "close" - args: "" - desc: "" - desc_ru: "" - - name: "commit" - args: "" - desc: "" - desc_ru: "" - - name: "createStatement" - args: "" - desc: "" - desc_ru: "" - - name: "getAutoCommit" - args: "" - desc: "" - desc_ru: "" - - name: "getCatalog" - args: "" - desc: "" - desc_ru: "" - - name: "getHoldability" - args: "" - desc: "" - desc_ru: "" - - name: "getNetworkTimeout" - args: "" - desc: "" - desc_ru: "" - - name: "getSchema" - args: "" - desc: "" - desc_ru: "" - - name: "getTransactionIsolation" - args: "" - desc: "" - desc_ru: "" - - name: "getUpdateCount" - args: "" - desc: "" - desc_ru: "" - - name: "isClosed" - args: "" - desc: "" - desc_ru: "" - - name: "isReadOnly" - args: "" - desc: "" - desc_ru: "" - - name: "prepareStatement" - args: "" - desc: "" - desc_ru: "" - - name: "rollback" - args: "" - desc: "" - desc_ru: "" - - name: "setHoldability" - args: "" - desc: "" - desc_ru: "" - - name: "setTransactionIsolation" - args: "" - desc: "" - desc_ru: "" - - name: ResultSetValue - functions: - - name: "absolute" - args: "" - desc: "" - desc_ru: "" - - name: "afterLast" - args: "" - desc: "" - desc_ru: "" - - name: "beforeFirst" - args: "" - desc: "" - desc_ru: "" - - name: "cancelRowUpdates" - args: "" - desc: "" - desc_ru: "" - - name: "clearWarnings" - args: "" - desc: "" - desc_ru: "" - - name: "close" - args: "" - desc: "" - desc_ru: "" - - name: "deleteRow" - args: "" - desc: "" - desc_ru: "" - - name: "findColumn" - args: "" - desc: "" - desc_ru: "" - - name: "first" - args: "" - desc: "" - desc_ru: "" - - name: "getArray" - args: "" - desc: "" - desc_ru: "" - - name: "getBigDecimal" - args: "" - desc: "" - desc_ru: "" - - name: "getBoolean" - args: "" - desc: "" - desc_ru: "" - - name: "getByte" - args: "" - desc: "" - desc_ru: "" - - name: "getBytes" - args: "" - desc: "" - desc_ru: "" - - name: "getConcurrency" - args: "" - desc: "" - desc_ru: "" - - name: "getCursorName" - args: "" - desc: "" - desc_ru: "" - - name: "getDate" - args: "" - desc: "" - desc_ru: "" - - name: "getDouble" - args: "" - desc: "" - desc_ru: "" - - name: "getFetchDirection" - args: "" - desc: "" - desc_ru: "" - - name: "getFetchSize" - args: "" - desc: "" - desc_ru: "" - - name: "getFloat" - args: "" - desc: "" - desc_ru: "" - - name: "getHoldability" - args: "" - desc: "" - desc_ru: "" - - name: "getInt" - args: "" - desc: "" - desc_ru: "" - - name: "getLong" - args: "" - desc: "" - desc_ru: "" - - name: "getNString" - args: "" - desc: "" - desc_ru: "" - - name: "getRow" - args: "" - desc: "" - desc_ru: "" - - name: "getRowId" - args: "" - desc: "" - desc_ru: "" - - name: "getShort" - args: "" - desc: "" - desc_ru: "" - - name: "getStatement" - args: "" - desc: "" - desc_ru: "" - - name: "getString" - args: "" - desc: "" - desc_ru: "" - - name: "getTime" - args: "" - desc: "" - desc_ru: "" - - name: "getTimestamp" - args: "" - desc: "" - desc_ru: "" - - name: "getType" - args: "" - desc: "" - desc_ru: "" - - name: "getURL" - args: "" - desc: "" - desc_ru: "" - - name: "insertRow" - args: "" - desc: "" - desc_ru: "" - - name: "isAfterLast" - args: "" - desc: "" - desc_ru: "" - - name: "isBeforeFirst" - args: "" - desc: "" - desc_ru: "" - - name: "isClosed" - args: "" - desc: "" - desc_ru: "" - - name: "isFirst" - args: "" - desc: "" - desc_ru: "" - - name: "isLast" - args: "" - desc: "" - desc_ru: "" - - name: "last" - args: "" - desc: "" - desc_ru: "" - - name: "moveToCurrentRow" - args: "" - desc: "" - desc_ru: "" - - name: "moveToInsertRow" - args: "" - desc: "" - desc_ru: "" - - name: "next" - args: "" - desc: "" - desc_ru: "" - - name: "previous" - args: "" - desc: "" - desc_ru: "" - - name: "refreshRow" - args: "" - desc: "" - desc_ru: "" - - name: "relative" - args: "" - desc: "" - desc_ru: "" - - name: "rowDeleted" - args: "" - desc: "" - desc_ru: "" - - name: "rowInserted" - args: "" - desc: "" - desc_ru: "" - - name: "rowUpdated" - args: "" - desc: "" - desc_ru: "" - - name: "setFetchDirection" - args: "" - desc: "" - desc_ru: "" - - name: "setFetchSize" - args: "" - desc: "" - desc_ru: "" - - name: "updateBigDecimal" - args: "" - desc: "" - desc_ru: "" - - name: "updateBoolean" - args: "" - desc: "" - desc_ru: "" - - name: "updateByte" - args: "" - desc: "" - desc_ru: "" - - name: "updateBytes" - args: "" - desc: "" - desc_ru: "" - - name: "updateDate" - args: "" - desc: "" - desc_ru: "" - - name: "updateDouble" - args: "" - desc: "" - desc_ru: "" - - name: "updateFloat" - args: "" - desc: "" - desc_ru: "" - - name: "updateInt" - args: "" - desc: "" - desc_ru: "" - - name: "updateLong" - args: "" - desc: "" - desc_ru: "" - - name: "updateNString" - args: "" - desc: "" - desc_ru: "" - - name: "updateNull" - args: "" - desc: "" - desc_ru: "" - - name: "updateRow" - args: "" - desc: "" - desc_ru: "" - - name: "updateShort" - args: "" - desc: "" - desc_ru: "" - - name: "updateString" - args: "" - desc: "" - desc_ru: "" - - name: "updateTime" - args: "" - desc: "" - desc_ru: "" - - name: "updateTimestamp" - args: "" - desc: "" - desc_ru: "" - - name: "wasNull" - args: "" - desc: "" - desc_ru: "" - - name: StatementValue - functions: - - name: "addBatch" - args: "" - desc: "" - desc_ru: "" - - name: "cancel" - args: "" - desc: "" - desc_ru: "" - - name: "clearBatch" - args: "" - desc: "" - desc_ru: "" - - name: "clearParameters" - args: "" - desc: "" - desc_ru: "" - - name: "clearWarnings" - args: "" - desc: "" - desc_ru: "" - - name: "close" - args: "" - desc: "" - desc_ru: "" - - name: "closeOnCompletion" - args: "" - desc: "" - desc_ru: "" - - name: "execute" - args: "" - desc: "" - desc_ru: "" - - name: "executeBatch" - args: "" - desc: "" - desc_ru: "" - - name: "executeLargeBatch" - args: "" - desc: "" - desc_ru: "" - - name: "executeLargeUpdate" - args: "" - desc: "" - desc_ru: "" - - name: "executeQuery" - args: "" - desc: "" - desc_ru: "" - - name: "executeUpdate" - args: "" - desc: "" - desc_ru: "" - - name: "getFetchDirection" - args: "" - desc: "" - desc_ru: "" - - name: "getFetchSize" - args: "" - desc: "" - desc_ru: "" - - name: "getGeneratedKeys" - args: "" - desc: "" - desc_ru: "" - - name: "getMaxFieldSize" - args: "" - desc: "" - desc_ru: "" - - name: "getMaxRows" - args: "" - desc: "" - desc_ru: "" - - name: "getMoreResults" - args: "" - desc: "" - desc_ru: "" - - name: "getQueryTimeout" - args: "" - desc: "" - desc_ru: "" - - name: "getResultSet" - args: "" - desc: "" - desc_ru: "" - - name: "getResultSetConcurrency" - args: "" - desc: "" - desc_ru: "" - - name: "getResultSetHoldability" - args: "" - desc: "" - desc_ru: "" - - name: "getResultSetType" - args: "" - desc: "" - desc_ru: "" - - name: "getUpdateCount" - args: "" - desc: "" - desc_ru: "" - - name: "isCloseOnCompletion" - args: "" - desc: "" - desc_ru: "" - - name: "isClosed" - args: "" - desc: "" - desc_ru: "" - - name: "isPoolable" - args: "" - desc: "" - desc_ru: "" - - name: "setBigDecimal" - args: "" - desc: "" - desc_ru: "" - - name: "setBoolean" - args: "" - desc: "" - desc_ru: "" - - name: "setByte" - args: "" - desc: "" - desc_ru: "" - - name: "setBytes" - args: "" - desc: "" - desc_ru: "" - - name: "setCursorName" - args: "" - desc: "" - desc_ru: "" - - name: "setDate" - args: "" - desc: "" - desc_ru: "" - - name: "setDouble" - args: "" - desc: "" - desc_ru: "" - - name: "setEscapeProcessing" - args: "" - desc: "" - desc_ru: "" - - name: "setFetchDirection" - args: "" - desc: "" - desc_ru: "" - - name: "setFetchSize" - args: "" - desc: "" - desc_ru: "" - - name: "setFloat" - args: "" - desc: "" - desc_ru: "" - - name: "setInt" - args: "" - desc: "" - desc_ru: "" - - name: "setLargeMaxRows" - args: "" - desc: "" - desc_ru: "" - - name: "setLong" - args: "" - desc: "" - desc_ru: "" - - name: "setMaxFieldSize" - args: "" - desc: "" - desc_ru: "" - - name: "setMaxRows" - args: "" - desc: "" - desc_ru: "" - - name: "setNString" - args: "" - desc: "" - desc_ru: "" - - name: "setNull" - args: "" - desc: "" - desc_ru: "" - - name: "setPoolable" - args: "" - desc: "" - desc_ru: "" - - name: "setQueryTimeout" - args: "" - desc: "" - desc_ru: "" - - name: "setShort" - args: "" - desc: "" - desc_ru: "" - - name: "setString" - args: "" - desc: "" - desc_ru: "" - - name: "setTime" - args: "" - desc: "" - desc_ru: "" - - name: "setTimestamp" - args: "" - desc: "" - desc_ru: "" - - name: "setURL" - args: "" - desc: "" - desc_ru: "" - - name: regex - since: 1.4.0 - constants: - - name: Pattern - type: 4 - typeName: map - value: "{UNIX_LINES=1, CASE_INSENSITIVE=2, I=2, COMMENTS=4, MULTILINE=8, M=8, LITERAL=16, S=32, DOTALL=32, UNICODE_CASE=64, CANON_EQ=128, UNICODE_CHARACTER_CLASS=256, U=256, quote=def(s) { return string }, matches=def(str,pattern) { return boolean }, split=def(str,pattern,limit = 0) { return array }, compile=def(pattern,flags = 0) { return PatternValue }}" - functions: - - name: regex - args: 'pattern, flags = 0' - desc: 'creates pattern and returns PatternValue' - desc_ru: 'создаёт шаблон регулярного выражения и возвращает PatternValue' - types: - - name: PatternValue - functions: - - name: "flags" - args: "" - desc: "returns pattern flags" - desc_ru: "возвращает флаги шаблона" - - name: "pattern" - args: "" - desc: "returns pattern as string" - desc_ru: "возвращает шаблон в виде строки" - - name: "matcher" - args: "input" - desc: "returns MatcherValue" - desc_ru: "возвращает MatcherValue" - - name: "matches" - args: "input" - desc: "checks if input matches the pattern" - desc_ru: "проверяет, соответствует ли входная строка шаблону" - - name: "split" - args: "input, limit = 0" - desc: "splits input around matches of this pattern" - desc_ru: "разбивает строку на основе совпадений шаблона" - - name: "replaceCallback" - args: "input, callback" - desc: "replaces input with the result of the given callback" - desc_ru: "заменяет строку результатом заданной функции" - example: |- - use regex - in = "dog cat" - pattern = regex("(\w+)\s(\w+)", Pattern.I) - println pattern.replaceCallback(in, def(m) = m.group(2) + "" + m.group(1)) - example_ru: |- - use regex - in = "пёс кот" - pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) - println pattern.replaceCallback(in, def(m) = m.group(2) + "о" + m.group(1)) - - name: MatcherValue - functions: - - name: "start" - args: "group = ..." - desc: "returns the start index of matched subsequence" - desc_ru: "возвращает начальную позицию найденного совпадения" - - name: "end" - args: "group = ..." - desc: "returns the offset after last character of matched subsequence" - desc_ru: "возвращает позицию, следующую за последним символов найденного совпадения" - - name: "find" - args: "start = 0" - desc: "resets this matcher and attempts to find the next matched subsequence" - desc_ru: "сбрасывает состояние матчера и пытается найти следующее совпадение" - - name: "group" - args: "group = 0" - desc: "returns matched group" - desc_ru: "возвращает найденную группу" - - name: "pattern" - args: "" - desc: "returns the pattern" - desc_ru: "возвращает шаблон" - - name: "region" - args: "start, end" - desc: "sets the limits of this matcher's region" - desc_ru: "задаёт ограничения для текущего региона" - - name: "replaceFirst" - args: "replacement" - desc: "replaces first matched subsequence with the given replacement string" - desc_ru: "заменяет первое совпадение на заданную строку" - - name: "replaceAll" - args: "replacement" - desc: "replaces all matched subsequences with the given replacement string" - desc_ru: "заменяет все совпадения на заданную строку" - - name: "replaceCallback" - args: "callback" - desc: "replaces input with the result of the given callback" - desc_ru: "заменяет строку результатом заданной функции" - example: |- - use regex - in = "dog cat" - pattern = regex("(\w+)\s(\w+)", Pattern.I) - matcher = pattern.matcher(in) - println matcher.replaceCallback(def(m) = m.group(2) + m.group(1)) - example_ru: |- - use regex - in = "пёс кот" - pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) - matcher = pattern.matcher(in) - println matcher.replaceCallback(def(m) = m.group(2) + "о" + m.group(1)) - - name: "reset" - args: input = "" - desc: "" - desc_ru: "" - - name: "usePattern" - args: "patternvalue" - desc: "" - desc_ru: "" - - name: "useAnchoringBounds" - args: "status" - desc: "" - desc_ru: "" - - name: "hasAnchoringBounds" - args: "" - desc: "" - desc_ru: "" - - name: "useTransparentBounds" - args: "status" - desc: "" - desc_ru: "" - - name: "hasTransparentBounds" - args: "" - desc: "" - desc_ru: "" - - name: "hitEnd" - args: "" - desc: "" - desc_ru: "" - - name: "lookingAt" - args: "" - desc: "" - desc_ru: "" - - name: "matches" - args: "" - desc: "" - desc_ru: "" - - name: "groupCount" - args: "" - desc: "" - desc_ru: "" - - name: "regionStart" - args: "" - desc: "" - desc_ru: "" - - name: "regionEnd" - args: "" - desc: "" - desc_ru: "" - - name: android - scope: "android" - desc: "Contains common Android functions" - desc_ru: "Содержит вспомогательные функции для Android" - constants: - - name: "Intent" - typeName: map - type: 4 - value: "{ACTION_BOOT_COMPLETED=android.intent.action.BOOT_COMPLETED, ACTION_DEFAULT=android.intent.action.VIEW, ACTION_DELETE=android.intent.action.DELETE, ACTION_EDIT=android.intent.action.EDIT, ACTION_INSTALL_PACKAGE=android.intent.action.INSTALL_PACKAGE, ACTION_LOCATION_SOURCE_SETTINGS=android.settings.LOCATION_SOURCE_SETTINGS, ACTION_MAIN=android.intent.action.MAIN, ACTION_MEDIA_MOUNTED=android.intent.action.MEDIA_MOUNTED, ACTION_REBOOT=android.intent.action.REBOOT, ACTION_RUN=android.intent.action.RUN, ACTION_SEARCH=android.intent.action.SEARCH, ACTION_SEND=android.intent.action.SEND, ACTION_VIEW=android.intent.action.VIEW, ACTION_WEB_SEARCH=android.intent.action.WEB_SEARCH}" - - name: R - type: 4 - typeName: map - value: '{array={}, attr={}, color={}, drawable={}, id={}, integer={}, layout={}, string={}}' - desc: "Resource constants from android.R.xxx class" - desc_ru: "Константы ресурсов класса android.R.xxx" - - name: SDK_INT - type: 1 - typeName: number - value: 'Android version SDK' - desc: "Android version SDK" - desc_ru: "Версия SDK Android" - - name: "Span" - typeName: map - type: 4 - value: "{COLOR=0, ABSOLUTE_SIZE=1, RELATIVE_SIZE=2, URL=3, STYLE=4, CLICKABLE=5, TYPEFACE=6, HTML=7}" - functions: - - - name: "assetBitmap" - args: "path" - desc: "loads bitmap from the file in apk's assets folder" - desc_ru: "загружает изображение из файла в папке assets внутри apk" - - - name: "assetBytes" - args: "path" - desc: "reads bytes of the file in apk's assets folder" - desc_ru: "читает массив байт из файла в папке assets внутри apk" - - - name: "assetText" - args: "path" - desc: "reads text content of the file in apk's assets folder" - desc_ru: "читает текст файла в папке assets внутри apk" - - - name: "chooser" - args: "" - desc: "" - desc_ru: "" - - - name: "listAssets" - args: "path" - desc: "returns list of files in apk's assets folder" - desc_ru: "возвращает список файлов в папке assets внутри apk" - - - name: "spannable" - args: "type, text, ..." - desc: "" - desc_ru: "" - - - name: "startActivity" - args: "intent, uri = \"\", bundle = []" - desc: "starts an activity" - desc_ru: "запускает Activity" - - - name: "toast" - args: "text, duration = 0" - desc: "shows toast notification" - desc_ru: "показывает всплывающее уведомление (toast)" - - - name: "uithread" - args: "function, ..." - desc: "runs function in main UI-thread" - desc_ru: "выполняет функцию в главном UI-потоке" - - name: canvas - scope: "android" - desc: "Contains functions for working with graphics on Android" - desc_ru: "Содержит функции для работы с графикой в Android" - constants: - - name: "VertexMode" - typeName: map - type: 4 - value: "{TRIANGLES=0, TRIANGLE_STRIP=1, TRIANGLE_FAN=2}" - - name: "Action" - typeName: map - type: 4 - value: "{DOWN=0, UP=1, MOVE=2, MULTIPLE=2, CANCEL=3, OUTSIDE=4, POINTER_DOWN=5, POINTER_UP=6, POINTER_INDEX_SHIFT=8, MASK=255, POINTER_INDEX_MASK=65280}" - - name: "BitmapCompressFormat" - typeName: map - type: 4 - value: "{JPEG=0, PNG=1, WEBP=2}" - - name: "EdgeType" - typeName: map - type: 4 - value: "{BW=0, AA=1}" - - name: "Cap" - typeName: map - type: 4 - value: "{BUTT=0, ROUND=1, SQUARE=2}" - - name: "Style" - typeName: map - type: 4 - value: "{FILL=0, STROKE=1, FILL_AND_STROKE=2}" - - name: "BitmapConfig" - typeName: map - type: 4 - value: "{ALPHA_8=0, RGB_565=1, ARGB_4444=2, ARGB_8888=3}" - - name: "Join" - typeName: map - type: 4 - value: "{MITER=0, ROUND=1, BEVEL=2}" - - name: "Align" - typeName: map - type: 4 - value: "{LEFT=0, CENTER=1, RIGHT=2}" - - name: "DisplayMetrics" - typeName: map - type: 4 - value: "{density=, densityDpi=, scaledDensity=, widthPixels=, heightPixels=, xdpi=, ydpi=}" - since: 1.5.1 - functions: - - name: "createBitmap" - args: "..." - desc: |- - `createBitmap(bitmap)` - creates a copy of the bitmap. - - `createBitmap(bytes)` - creates bitmap from byte array. - - `createBitmap(w, h)` - creates new bitmap with the given size. - - `createBitmap(w, h, config)` - creates new bitmap with the given size and config. - - `createBitmap(bytes, offset, length)` - creates bitmap from byte array starting from offset. - - `createBitmap(pixels, w, h, config)` - creates new bitmap from pixels array. - - `createBitmap(bitmap, x, y, w, h)` - creates new bitmap from the part of the `bitmap`. - - `createBitmap(pixels, offset, stride, w, h, config)` - creates new bitmap from pixels array starting from offset. - - Returns BitmapValue. - desc_ru: |- - `createBitmap(bitmap)` - создаёт копию изображения. - - `createBitmap(bytes)` - создаёт изображение из массива байт. - - `createBitmap(w, h)` - создаёт новое изображение с заданным размером. - - `createBitmap(w, h, config)` - создаёт новое изображение с заданным размером и конфигурацией. - - `createBitmap(bytes, offset, length)` - создаёт изображение из массива байт, начиная с offset. - - `createBitmap(pixels, w, h, config)` - создаёт изображение из массива пикселей. - - `createBitmap(bitmap, x, y, w, h)` - создаёт изображение из части другого изображения. - - `createBitmap(pixels, offset, stride, w, h, config)` - создаёт изображение из массива пикселей, начиная с offset. - - Возвращает BitmapValue. - example: |- - use http, canvas - - g = showcanvas() - url = "http://lorempixel.com/640/480/nature" - imageBytes = download(url) - bitmap = createBitmap(imageBytes) - g.drawBitmap(bitmap, 0, 0) - repaint() - - name: "createScaledBitmap" - args: "srcBitmap, width, height, filter" - desc: "scales bitmap to size and returns new BitmapValue" - desc_ru: "возвращает BitmapValue с изменённым размером заданного изображения" - - name: "getScreenBitmap" - args: "" - desc: "returns current screen as bitmap" - desc_ru: "возвращает содержимое экрана в виде изображения" - - name: "hidecanvas" - args: "" - desc: "closes canvas screen and releases resources" - desc_ru: "закрывает экран канваса и освобождает ресурсы" - - name: "newPath" - args: "path = null" - desc: "creates new PathValue" - desc_ru: "создаёт новый PathValue" - since: 1.5.1 - - name: "newLinearGradient" - args: "x0, y0, x1, y1, colors|color1, positions|color2, tileMode" - desc: "creates new shader" - desc_ru: "создаёт новый шейдер" - since: 1.5.1 - - name: "newRadialGradient" - args: "cx, cy, radius, colors|color1, positions|color2, tileMode" - desc: "creates new shader" - desc_ru: "создаёт новый шейдер" - since: 1.5.1 - - name: "newSweepGradient" - args: "cx, cy, colors|color1, positions|color2" - desc: "creates new shader" - desc_ru: "создаёт новый шейдер" - since: 1.5.1 - - name: "newBitmapShader" - args: "bitmap, modeX, modeY" - desc: "creates new bitmap shader" - desc_ru: "создаёт новый шейдер из картинки" - since: 1.5.1 - - name: "newComposeShader" - args: "shader1, shader2, mode = SRC_OVER" - desc: "creates new composition shader" - desc_ru: "создаёт новый композитный шейдер" - since: 1.5.1 - - name: "repaint" - args: "" - desc: "" - desc_ru: "" - - name: "setOnKeyDownEvent" - args: "" - desc: "" - desc_ru: "" - - name: "setOnKeyUpEvent" - args: "" - desc: "" - desc_ru: "" - - name: "setOnLongPressEvent" - args: "" - desc: "" - desc_ru: "" - - name: "setOnTouchEvent" - args: "" - desc: "" - desc_ru: "" - - name: "setGestureDetectorListener" - args: "listener" - desc: "sets gesture detector listener" - desc_ru: "устанавливает обработчик детектора жестов" - since: 1.5.1 - - name: "showcanvas" - args: "" - desc: "shows canvas screen and returns GraphicsValue" - desc_ru: "показывает экран канваса и возвращает GraphicsValue" - example: |- - use canvas - g = showcanvas() - types: - - name: "BitmapValue" - functions: - - - name: "compress" - args: "" - desc: "" - desc_ru: "" - - - name: "copy" - args: "" - desc: "" - desc_ru: "" - - - name: "eraseColor" - args: "" - desc: "" - desc_ru: "" - - - name: "extractAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "getAllocationByteCount" - args: "" - desc: "" - desc_ru: "" - - - name: "getByteCount" - args: "" - desc: "" - desc_ru: "" - - - name: "getDensity" - args: "" - desc: "" - desc_ru: "" - - - name: "getGraphics" - args: "" - desc: "" - desc_ru: "" - - - name: "getWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "getHeight" - args: "" - desc: "" - desc_ru: "" - - - name: "getRowBytes" - args: "" - desc: "" - desc_ru: "" - - - name: "getPixel" - args: "" - desc: "" - desc_ru: "" - - - name: "getPixels" - args: "" - desc: "" - desc_ru: "" - - - name: "getScaledWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "getScaledHeight" - args: "" - desc: "" - desc_ru: "" - - - name: "hasAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "hasMipMap" - args: "" - desc: "" - desc_ru: "" - - - name: "isMutable" - args: "" - desc: "" - desc_ru: "" - - - name: "isPremultiplied" - args: "" - desc: "" - desc_ru: "" - - - name: "isRecycled" - args: "" - desc: "" - desc_ru: "" - - - name: "prepareToDraw" - args: "" - desc: "" - desc_ru: "" - - - name: "recycle" - args: "" - desc: "" - desc_ru: "" - - - name: "setPixel" - args: "" - desc: "" - desc_ru: "" - - - name: "setPixels" - args: "" - desc: "" - desc_ru: "" - - - name: "GraphicsValue" - functions: - - - name: "ascent" - args: "" - desc: "" - desc_ru: "" - - - name: "breakText" - args: "" - desc: "" - desc_ru: "" - - - name: "clearShadowLayer" - args: "" - desc: "" - desc_ru: "" - - name: "clipPath" - args: "path" - desc: "" - desc_ru: "" - since: 1.5.1 - - name: "clipRect" - args: "x, y, w, h, op = 0" - desc: "" - desc_ru: "" - - - name: "descent" - args: "" - desc: "" - desc_ru: "" - - - name: "drawARGB" - args: "" - desc: "" - desc_ru: "" - - - name: "drawArc" - args: "" - desc: "" - desc_ru: "" - - - name: "drawBitmap" - args: "" - desc: "" - desc_ru: "" - - - name: "drawCircle" - args: "" - desc: "" - desc_ru: "" - - - name: "drawColor" - args: "" - desc: "" - desc_ru: "" - - - name: "drawLine" - args: "" - desc: "" - desc_ru: "" - - - name: "drawOval" - args: "" - desc: "" - desc_ru: "" - - name: "drawPath" - args: "path" - desc: "" - desc_ru: "" - since: 1.5.1 - - name: "drawPoint" - args: "" - desc: "" - desc_ru: "" - - name: "drawRGB" - args: "" - desc: "" - desc_ru: "" - - - name: "drawRect" - args: "" - desc: "" - desc_ru: "" - - - name: "drawRoundRect" - args: "" - desc: "" - desc_ru: "" - - name: "drawText" - args: "" - desc: "" - desc_ru: "" - - name: "drawTextOnPath" - args: "text, path, hOffset, vOffset" - desc: "" - desc_ru: "" - since: 1.5.1 - - name: "fillCircle" - args: "" - desc: "" - desc_ru: "" - - - name: "fillOval" - args: "" - desc: "" - desc_ru: "" - - - name: "fillRect" - args: "" - desc: "" - desc_ru: "" - - - name: "fillRoundRect" - args: "" - desc: "" - desc_ru: "" - - - name: "getAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "getClipBounds" - args: "" - desc: "" - desc_ru: "" - - name: "getColor" - args: "" - desc: "" - desc_ru: "" - - name: "getFillPath" - args: "src, dst" - desc: "" - desc_ru: "" - since: 1.5.1 - - name: "getDensity" - args: "" - desc: "" - desc_ru: "" - - - name: "getFlags" - args: "" - desc: "" - desc_ru: "" - - - name: "getFontSpacing" - args: "" - desc: "" - desc_ru: "" - - - name: "getHeight" - args: "" - desc: "" - desc_ru: "" - - - name: "getSaveCount" - args: "" - desc: "" - desc_ru: "" - - - name: "getStrokeCap" - args: "" - desc: "" - desc_ru: "" - - - name: "getStrokeJoin" - args: "" - desc: "" - desc_ru: "" - - - name: "getStrokeMiter" - args: "" - desc: "" - desc_ru: "" - - - name: "getStrokeWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "getStyle" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextAlign" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextBounds" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextScaleX" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextSize" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextSkewX" - args: "" - desc: "" - desc_ru: "" - - - name: "getTextWidths" - args: "" - desc: "" - desc_ru: "" - - - name: "getTypeface" - args: "" - desc: "" - desc_ru: "" - - - name: "getWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "isAntiAlias" - args: "" - desc: "" - desc_ru: "" - - - name: "isDither" - args: "" - desc: "" - desc_ru: "" - - - name: "isFakeBoldText" - args: "" - desc: "" - desc_ru: "" - - - name: "isFilterBitmap" - args: "" - desc: "" - desc_ru: "" - - - name: "isLinearText" - args: "" - desc: "" - desc_ru: "" - - - name: "isOpaque" - args: "" - desc: "" - desc_ru: "" - - - name: "isStrikeThruText" - args: "" - desc: "" - desc_ru: "" - - - name: "isSubpixelText" - args: "" - desc: "" - desc_ru: "" - - - name: "isUnderlineText" - args: "" - desc: "" - desc_ru: "" - - - name: "measureText" - args: "" - desc: "" - desc_ru: "" - - - name: "quickReject" - args: "" - desc: "" - desc_ru: "" - - - name: "reset" - args: "" - desc: "" - desc_ru: "" - - - name: "restore" - args: "" - desc: "" - desc_ru: "" - - - name: "restoreToCount" - args: "" - desc: "" - desc_ru: "" - - - name: "rotate" - args: "" - desc: "" - desc_ru: "" - - - name: "save" - args: "" - desc: "" - desc_ru: "" - - - name: "saveLayer" - args: "" - desc: "" - desc_ru: "" - - - name: "saveLayerAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "scale" - args: "" - desc: "" - desc_ru: "" - - - name: "setAlpha" - args: "" - desc: "" - desc_ru: "" - - - name: "setAntiAlias" - args: "" - desc: "" - desc_ru: "" - - - name: "setBitmap" - args: "" - desc: "" - desc_ru: "" - - - name: "setColor" - args: "" - desc: "" - desc_ru: "" - - - name: "setDensity" - args: "" - desc: "" - desc_ru: "" - - - name: "setDither" - args: "" - desc: "" - desc_ru: "" - - - name: "setFakeBoldText" - args: "" - desc: "" - desc_ru: "" - - - name: "setFilterBitmap" - args: "" - desc: "" - desc_ru: "" - - - name: "setFlags" - args: "" - desc: "" - desc_ru: "" - - - name: "setLinearText" - args: "" - desc: "" - desc_ru: "" - - name: "setShader" - args: "shader" - desc: "" - desc_ru: "" - since: 1.5.1 - - name: "setShadowLayer" - args: "radius, dx, dy, shadowColor" - desc: "" - desc_ru: "" - - name: "setStrikeThruText" - args: "isEnabled" - desc: "" - desc_ru: "" - - name: "setStrokeCap" - args: "cap" - desc: "" - desc_ru: "" - - name: "setStrokeJoin" - args: "join" - desc: "" - desc_ru: "" - - name: "setStrokeMiter" - args: "miter" - desc: "" - desc_ru: "" - - - name: "setStrokeWidth" - args: "" - desc: "" - desc_ru: "" - - - name: "setStyle" - args: "" - desc: "" - desc_ru: "" - - - name: "setSubpixelText" - args: "" - desc: "" - desc_ru: "" - - - name: "setTextAlign" - args: "" - desc: "" - desc_ru: "" - - - name: "setTextScaleX" - args: "" - desc: "" - desc_ru: "" - - - name: "setTextSize" - args: "" - desc: "" - desc_ru: "" - - - name: "setTextSkewX" - args: "" - desc: "" - desc_ru: "" - - - name: "setTypeface" - args: "" - desc: "" - desc_ru: "" - - - name: "setUnderlineText" - args: "" - desc: "" - desc_ru: "" - - - name: "skew" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeCircle" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeOval" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeRect" - args: "" - desc: "" - desc_ru: "" - - - name: "strokeRoundRect" - args: "" - desc: "" - desc_ru: "" - - - name: "translate" - args: "" - desc: "" - desc_ru: "" - - name: forms - scope: android - desc: "Contains functions for working with forms" - desc_ru: "Содержит функции для работы с формами" - constants: - - name: Gravity - type: 4 - typeName: map - value: '{NONE=0, NO_GRAVITY=0, CENTER_HORIZONTAL=1, LEFT=3, RIGHT=5, FILL_HORIZONTAL=7, CLIP_HORIZONTAL=8, CENTER_VERTICAL=16, CENTER=17, TOP=48, BOTTOM=80, FILL_VERTICAL=112, FILL=119, CLIP_VERTICAL=128}' - - name: InputType - type: 4 - typeName: map - value: '{TYPE_CLASS_DATETIME=4, TYPE_CLASS_NUMBER=2, TYPE_CLASS_PHONE=3, TYPE_CLASS_TEXT=1, TYPE_DATETIME_VARIATION_DATE=16, TYPE_DATETIME_VARIATION_NORMAL=0, TYPE_DATETIME_VARIATION_TIME=32, TYPE_MASK_CLASS=15, TYPE_MASK_FLAGS=16773120, TYPE_MASK_VARIATION=4080, TYPE_NULL=0, TYPE_NUMBER_FLAG_DECIMAL=8192, TYPE_NUMBER_FLAG_SIGNED=4096, TYPE_NUMBER_VARIATION_NORMAL=0, TYPE_NUMBER_VARIATION_PASSWORD=16, TYPE_TEXT_FLAG_AUTO_COMPLETE=65536, TYPE_TEXT_FLAG_AUTO_CORRECT=32768, TYPE_TEXT_FLAG_CAP_CHARACTERS=4096, TYPE_TEXT_FLAG_CAP_SENTENCES=16384, TYPE_TEXT_FLAG_CAP_WORDS=8192, TYPE_TEXT_FLAG_IME_MULTI_LINE=262144, TYPE_TEXT_FLAG_MULTI_LINE=131072, TYPE_TEXT_FLAG_NO_SUGGESTIONS=524288, TYPE_TEXT_VARIATION_EMAIL_ADDRESS=32, TYPE_TEXT_VARIATION_EMAIL_SUBJECT=48, TYPE_TEXT_VARIATION_FILTER=176, TYPE_TEXT_VARIATION_LONG_MESSAGE=80, TYPE_TEXT_VARIATION_NORMAL=0, TYPE_TEXT_VARIATION_PASSWORD=128, TYPE_TEXT_VARIATION_PERSON_NAME=96, TYPE_TEXT_VARIATION_PHONETIC=192, TYPE_TEXT_VARIATION_POSTAL_ADDRESS=112, TYPE_TEXT_VARIATION_SHORT_MESSAGE=64, TYPE_TEXT_VARIATION_URI=16, TYPE_TEXT_VARIATION_VISIBLE_PASSWORD=144, TYPE_TEXT_VARIATION_WEB_EDIT_TEXT=160, TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS=208, TYPE_TEXT_VARIATION_WEB_PASSWORD=224}' - - name: LinearLayout - type: 4 - typeName: map - value: '{HORIZONTAL=0, VERTICAL=1}' - - name: MATCH_PARENT - type: 1 - typeName: number - value: '-1' - - name: PorterDuff - type: 4 - typeName: map - value: '{ADD=16, CLEAR=0, DARKEN=12, DST=2, DST_ATOP=10, DST_IN=6, DST_OUT=8, DST_OVER=4, LIGHTEN=13, MULTIPLY=14, OVERLAY=17, SCREEN=15, SRC=1, SRC_ATOP=9, SRC_IN=5, SRC_OUT=7, SRC_OVER=3, XOR=11}' - - name: ScaleType - type: 4 - typeName: map - value: '{MATRIX=0, FIT_XY=1, FIT_START=2, FIT_CENTER=3, FIT_END=4, CENTER=5, CENTER_CROP=6, CENTER_INSIDE=7}' - - name: WRAP_CONTENT - type: 1 - typeName: number - value: '-2' - functions: - - name: showForm - args: 'view, layoutParams = {}' - desc: 'shows view' - desc_ru: 'показывает форму' - - name: inflate - args: 'resourceId, rootView = null, attachToRoot = false' - desc: 'Inflates view from resource xml' - desc_ru: 'Создаёт view из xml-ресурса' - - name: newArrayAdapter - args: 'resourceId = R.layout.simple_list_item_1, elements = []' - desc: 'Creates ArrayAdapter to use in ListView' - desc_ru: 'Создаёт ArrayAdapter для использования в ListView' - - name: newBaseAdapter - args: 'mapWithFunctions' - desc: '' - desc_ru: '' - example: |- - use std, android, forms - - img1 = assetBitmap("ownlang.png") - img2 = img1 - - items = [ - {"img" : img1, "text" : "Item 1"}, - {"img" : img2, "text" : "Item 2"} - ] - adapter = newBaseAdapter({ - "getCount": def() = length(items) - "getItem": def(pos) = items[pos] - "getItemId": def(pos) = pos - "getView": def(pos, view, parent) { - if (view == 0) { - view = newLinearLayout() - view.setOrientation(LinearLayout.HORIZONTAL) - imageView = newImageView() - view.addView(imageView) - textView = newTextView() - view.addView(textView) - view.setTag([imageView, textView]) - } else { - extract(imageView, textView) = view.getTag() - } - - imageView.setImageBitmap(items[pos].img); - textView.setText(items[pos].text); - return view - } - }); - - listView = newListView() - listView.setAdapter(adapter) - listView.onItemClick(def(v, pos, id) { - toast(adapter.getItem(pos).text + " selected") - }) - - panel = newLinearLayout() - panel.addView(newTextView("ListView with BaseAdapter demo")) - panel.addView(listView) - - showForm(panel) - - name: newButton - args: 'text = ""' - desc: 'creates Button' - desc_ru: 'создаёт Button' - - name: newCheckBox - args: '' - desc: 'creates CheckBox' - desc_ru: 'создаёт CheckBox' - - name: newEditText - args: '' - desc: 'creates EditText' - desc_ru: 'создаёт EditText' - - name: newFrameLayout - args: '' - desc: 'creates FrameLayout container' - desc_ru: 'создаёт контейнер FrameLayout' - - name: newImageButton - args: '' - desc: 'creates ImageButton' - desc_ru: 'создаёт ImageButton' - - name: newImageView - args: '' - desc: 'creates ImageView' - desc_ru: 'создаёт ImageView' - - name: newLinearLayout - args: '' - desc: 'creates LinearLayout container' - desc_ru: 'создаёт контейнер LinearLayout' - - name: newListView - args: '' - desc: 'creates ListView' - desc_ru: 'создаёт ListView' - - name: newProgressBar - args: 'style = R.attr.progressBarStyle' - desc: 'creates ProgressBar' - desc_ru: 'создаёт ProgressBar' - example: |- - use android, forms - pb1 = newProgressBar(R.attr.progressBarStyleHorizontal) - pb1.setMax(100) - pb1.setProgress(10) - pb2 = newProgressBar() - pb2.setIndeterminate(true) - - panel = newLinearLayout() - panel.addView(pb1) - panel.addView(pb2) - showForm(panel) - - name: newRadioButton - args: '' - desc: 'creates RadioButton' - desc_ru: 'создаёт RadioButton' - - name: newRadioGroup - args: '' - desc: 'creates RadioGroup container' - desc_ru: 'создаёт контейнер RadioGroup' - - name: newRelativeLayout - args: '' - desc: 'creates RelativeLayout container' - desc_ru: 'создаёт контейнер RelativeLayout' - - name: newScrollView - args: '' - desc: 'creates ScrollView container' - desc_ru: 'создаёт контейнер ScrollView' - - name: newSeekBar - args: '' - desc: 'creates SeekBar' - desc_ru: 'создаёт SeekBar' - - name: newSwitch - args: '' - desc: 'creates Switch (available for SDK_INT >= 14)' - desc_ru: 'создаёт Switch (доступен для SDK_INT >= 14)' - - name: newTextView - args: 'text = ""' - desc: 'creates TextView' - desc_ru: 'создаёт TextView' - - name: newToggleButton - args: '' - desc: 'creates ToggleButton' - desc_ru: 'создаёт ToggleButton' - types: - - name: ViewValue - functions: - - name: bringToFront - args: '' - desc: '' - desc_ru: '' - - name: buildDrawingCache - args: '' - desc: '' - desc_ru: '' - - name: callOnClick - args: '' - desc: 'available for SDK_INT >= 15' - desc_ru: 'доступно для SDK_INT >= 15' - - name: cancelLongPress - args: '' - desc: '' - desc_ru: '' - - name: clearAnimation - args: '' - desc: '' - desc_ru: '' - - name: clearFocus - args: '' - desc: '' - desc_ru: '' - - name: computeScroll - args: '' - desc: '' - desc_ru: '' - - name: destroyDrawingCache - args: '' - desc: '' - desc_ru: '' - - name: dispatchDisplayHint - args: '' - desc: '' - desc_ru: '' - - name: findFocus - args: '' - desc: '' - desc_ru: '' - - name: findViewById - args: '' - desc: '' - desc_ru: '' - - name: focusSearch - args: '' - desc: '' - desc_ru: '' - - name: forceLayout - args: '' - desc: '' - desc_ru: '' - - name: getAlpha - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getBaseline - args: '' - desc: '' - desc_ru: '' - - name: getBottom - args: '' - desc: '' - desc_ru: '' - - name: getContentDescription - args: '' - desc: '' - desc_ru: '' - - name: getDrawingCacheBackgroundColor - args: '' - desc: '' - desc_ru: '' - - name: getDrawingCacheQuality - args: '' - desc: '' - desc_ru: '' - - name: getDrawingTime - args: '' - desc: '' - desc_ru: '' - - name: getHeight - args: '' - desc: '' - desc_ru: '' - - name: getHorizontalFadingEdgeLength - args: '' - desc: '' - desc_ru: '' - - name: getId - args: '' - desc: '' - desc_ru: '' - - name: getKeepScreenOn - args: '' - desc: '' - desc_ru: '' - - name: getLeft - args: '' - desc: '' - desc_ru: '' - - name: getMeasuredHeight - args: '' - desc: '' - desc_ru: '' - - name: getMeasuredHeightAndState - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getMeasuredState - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getMeasuredWidth - args: '' - desc: '' - desc_ru: '' - - name: getMeasuredWidthAndState - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getNextFocusDownId - args: '' - desc: '' - desc_ru: '' - - name: getNextFocusForwardId - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getNextFocusLeftId - args: '' - desc: '' - desc_ru: '' - - name: getNextFocusRightId - args: '' - desc: '' - desc_ru: '' - - name: getNextFocusUpId - args: '' - desc: '' - desc_ru: '' - - name: getOverScrollMode - args: '' - desc: '' - desc_ru: '' - - name: getPaddingBottom - args: '' - desc: '' - desc_ru: '' - - name: getPaddingEnd - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: getPaddingLeft - args: '' - desc: '' - desc_ru: '' - - name: getPaddingRight - args: '' - desc: '' - desc_ru: '' - - name: getPaddingStart - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: getPaddingTop - args: '' - desc: '' - desc_ru: '' - - name: getPivotX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getPivotY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getRight - args: '' - desc: '' - desc_ru: '' - - name: getRootView - args: '' - desc: '' - desc_ru: '' - - name: getRotation - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getRotationX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getRotationY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getScaleX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getScaleY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getScrollBarFadeDuration - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: getScrollBarSize - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: getScrollBarStyle - args: '' - desc: '' - desc_ru: '' - - name: getScrollX - args: '' - desc: '' - desc_ru: '' - - name: getScrollY - args: '' - desc: '' - desc_ru: '' - - name: getSolidColor - args: '' - desc: '' - desc_ru: '' - - name: getSystemUiVisibility - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getTag - args: '' - desc: '' - desc_ru: '' - - name: getTextAlignment - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: getTextDirection - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: getTop - args: '' - desc: '' - desc_ru: '' - - name: getTranslationX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getTranslationY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getTranslationZ - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: getVerticalFadingEdgeLength - args: '' - desc: '' - desc_ru: '' - - name: getVerticalScrollbarPosition - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getVerticalScrollbarWidth - args: '' - desc: '' - desc_ru: '' - - name: getVisibility - args: '' - desc: '' - desc_ru: '' - - name: getWidth - args: '' - desc: '' - desc_ru: '' - - name: getWindowSystemUiVisibility - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: getWindowVisibility - args: '' - desc: '' - desc_ru: '' - - name: getX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: getZ - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: hasFocus - args: '' - desc: '' - desc_ru: '' - - name: hasFocusable - args: '' - desc: '' - desc_ru: '' - - name: hasNestedScrollingParent - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: hasOnClickListeners - args: '' - desc: 'available for SDK_INT >= 15' - desc_ru: 'доступно для SDK_INT >= 15' - - name: hasOverlappingRendering - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: hasTransientState - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: hasWindowFocus - args: '' - desc: '' - desc_ru: '' - - name: invalidate - args: '' - desc: '' - desc_ru: '' - - name: invalidateDrawable - args: '' - desc: '' - desc_ru: '' - - name: invalidateOutline - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: isAccessibilityFocused - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: isActivated - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: isAttachedToWindow - args: '' - desc: 'available for SDK_INT >= 19' - desc_ru: 'доступно для SDK_INT >= 19' - - name: isClickable - args: '' - desc: '' - desc_ru: '' - - name: isContextClickable - args: '' - desc: 'available for SDK_INT >= 23' - desc_ru: 'доступно для SDK_INT >= 23' - - name: isDirty - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: isDrawingCacheEnabled - args: '' - desc: '' - desc_ru: '' - - name: isDuplicateParentStateEnabled - args: '' - desc: '' - desc_ru: '' - - name: isEnabled - args: '' - desc: '' - desc_ru: '' - - name: isFocusable - args: '' - desc: '' - desc_ru: '' - - name: isFocusableInTouchMode - args: '' - desc: '' - desc_ru: '' - - name: isFocused - args: '' - desc: '' - desc_ru: '' - - name: isHapticFeedbackEnabled - args: '' - desc: '' - desc_ru: '' - - name: isHardwareAccelerated - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: isHorizontalFadingEdgeEnabled - args: '' - desc: '' - desc_ru: '' - - name: isHorizontalScrollBarEnabled - args: '' - desc: '' - desc_ru: '' - - name: isHovered - args: '' - desc: 'available for SDK_INT >= 14' - desc_ru: 'доступно для SDK_INT >= 14' - - name: isImportantForAccessibility - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: isInEditMode - args: '' - desc: '' - desc_ru: '' - - name: isInLayout - args: '' - desc: 'available for SDK_INT >= 18' - desc_ru: 'доступно для SDK_INT >= 18' - - name: isInTouchMode - args: '' - desc: '' - desc_ru: '' - - name: isLaidOut - args: '' - desc: 'available for SDK_INT >= 19' - desc_ru: 'доступно для SDK_INT >= 19' - - name: isLayoutDirectionResolved - args: '' - desc: 'available for SDK_INT >= 19' - desc_ru: 'доступно для SDK_INT >= 19' - - name: isLayoutRequested - args: '' - desc: '' - desc_ru: '' - - name: isLongClickable - args: '' - desc: '' - desc_ru: '' - - name: isNestedScrollingEnabled - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: isOpaque - args: '' - desc: '' - desc_ru: '' - - name: isPaddingRelative - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: isPressed - args: '' - desc: '' - desc_ru: '' - - name: isSaveEnabled - args: '' - desc: '' - desc_ru: '' - - name: isSaveFromParentEnabled - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: isScrollContainer - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: isScrollbarFadingEnabled - args: '' - desc: '' - desc_ru: '' - - name: isSelected - args: '' - desc: '' - desc_ru: '' - - name: isShown - args: '' - desc: '' - desc_ru: '' - - name: isSoundEffectsEnabled - args: '' - desc: '' - desc_ru: '' - - name: isTextAlignmentResolved - args: '' - desc: 'available for SDK_INT >= 19' - desc_ru: 'доступно для SDK_INT >= 19' - - name: isTextDirectionResolved - args: '' - desc: 'available for SDK_INT >= 19' - desc_ru: 'доступно для SDK_INT >= 19' - - name: isVerticalFadingEdgeEnabled - args: '' - desc: '' - desc_ru: '' - - name: isVerticalScrollBarEnabled - args: '' - desc: '' - desc_ru: '' - - name: jumpDrawablesToCurrentState - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: offsetLeftAndRight - args: '' - desc: '' - desc_ru: '' - - name: offsetTopAndBottom - args: '' - desc: '' - desc_ru: '' - - name: onClick - args: '' - desc: '' - desc_ru: '' - - name: onFocusChange - args: '' - desc: '' - desc_ru: '' - - name: onKey - args: '' - desc: '' - desc_ru: '' - - name: onLongClick - args: '' - desc: '' - desc_ru: '' - - name: performClick - args: '' - desc: '' - desc_ru: '' - - name: performHapticFeedback - args: '' - desc: '' - desc_ru: '' - - name: performLongClick - args: '' - desc: '' - desc_ru: '' - - name: playSoundEffect - args: '' - desc: '' - desc_ru: '' - - name: post - args: '' - desc: '' - desc_ru: '' - - name: postDelayed - args: '' - desc: '' - desc_ru: '' - - name: postInvalidate - args: '' - desc: '' - desc_ru: '' - - name: refreshDrawableState - args: '' - desc: '' - desc_ru: '' - - name: requestFocus - args: '' - desc: '' - desc_ru: '' - - name: requestFocusFromTouch - args: '' - desc: '' - desc_ru: '' - - name: requestLayout - args: '' - desc: '' - desc_ru: '' - - name: scrollBy - args: '' - desc: '' - desc_ru: '' - - name: scrollTo - args: '' - desc: '' - desc_ru: '' - - name: sendAccessibilityEvent - args: '' - desc: '' - desc_ru: '' - - name: setActivated - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setAlpha - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setBackground - args: '' - desc: '' - desc_ru: '' - - name: setBackgroundColor - args: '' - desc: '' - desc_ru: '' - - name: setBackgroundDrawable - args: '' - desc: '' - desc_ru: '' - - name: setBackgroundResource - args: '' - desc: '' - desc_ru: '' - - name: setBottom - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setCameraDistance - args: '' - desc: 'available for SDK_INT >= 12' - desc_ru: 'доступно для SDK_INT >= 12' - - name: setClickable - args: '' - desc: '' - desc_ru: '' - - name: setClipToOutline - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: setContentDescription - args: '' - desc: '' - desc_ru: '' - - name: setContextClickable - args: '' - desc: 'available for SDK_INT >= 23' - desc_ru: 'доступно для SDK_INT >= 23' - - name: setDrawingCacheBackgroundColor - args: '' - desc: '' - desc_ru: '' - - name: setDrawingCacheEnabled - args: '' - desc: '' - desc_ru: '' - - name: setDrawingCacheQuality - args: '' - desc: '' - desc_ru: '' - - name: setDuplicateParentStateEnabled - args: '' - desc: '' - desc_ru: '' - - name: setEnabled - args: '' - desc: '' - desc_ru: '' - - name: setFadingEdgeLength - args: '' - desc: '' - desc_ru: '' - - name: setFilterTouchesWhenObscured - args: '' - desc: '' - desc_ru: '' - - name: setFitsSystemWindows - args: '' - desc: 'available for SDK_INT >= 14' - desc_ru: 'доступно для SDK_INT >= 14' - - name: setFocusable - args: '' - desc: '' - desc_ru: '' - - name: setFocusableInTouchMode - args: '' - desc: '' - desc_ru: '' - - name: setForeground - args: '' - desc: '' - desc_ru: '' - - name: setHapticFeedbackEnabled - args: '' - desc: '' - desc_ru: '' - - name: setHorizontalFadingEdgeEnabled - args: '' - desc: '' - desc_ru: '' - - name: setHorizontalScrollBarEnabled - args: '' - desc: '' - desc_ru: '' - - name: setHovered - args: '' - desc: 'available for SDK_INT >= 14' - desc_ru: 'доступно для SDK_INT >= 14' - - name: setId - args: '' - desc: '' - desc_ru: '' - - name: setImportantForAccessibility - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: setKeepScreenOn - args: '' - desc: '' - desc_ru: '' - - name: setLabelFor - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: setLayoutDirection - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: setLeft - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setLongClickable - args: '' - desc: '' - desc_ru: '' - - name: setMinimumHeight - args: '' - desc: '' - desc_ru: '' - - name: setMinimumWidth - args: '' - desc: '' - desc_ru: '' - - name: setNestedScrollingEnabled - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: setNextFocusDownId - args: '' - desc: '' - desc_ru: '' - - name: setNextFocusForwardId - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setNextFocusLeftId - args: '' - desc: '' - desc_ru: '' - - name: setNextFocusRightId - args: '' - desc: '' - desc_ru: '' - - name: setNextFocusUpId - args: '' - desc: '' - desc_ru: '' - - name: setOnClickListener - args: '' - desc: '' - desc_ru: '' - - name: setOnFocusChangeListener - args: '' - desc: '' - desc_ru: '' - - name: setOnKeyListener - args: '' - desc: '' - desc_ru: '' - - name: setOnLongClickListener - args: '' - desc: '' - desc_ru: '' - - name: setOverScrollMode - args: '' - desc: '' - desc_ru: '' - - name: setPadding - args: '' - desc: '' - desc_ru: '' - - name: setPaddingRelative - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: setPivotX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setPivotY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setPressed - args: '' - desc: '' - desc_ru: '' - - name: setRight - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setRotation - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setRotationX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setRotationY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setSaveEnabled - args: '' - desc: '' - desc_ru: '' - - name: setSaveFromParentEnabled - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setScaleX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setScaleY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setScrollBarDefaultDelayBeforeFade - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: setScrollBarFadeDuration - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: setScrollBarSize - args: '' - desc: 'available for SDK_INT >= 16' - desc_ru: 'доступно для SDK_INT >= 16' - - name: setScrollBarStyle - args: '' - desc: '' - desc_ru: '' - - name: setScrollContainer - args: '' - desc: '' - desc_ru: '' - - name: setScrollX - args: '' - desc: 'available for SDK_INT >= 14' - desc_ru: 'доступно для SDK_INT >= 14' - - name: setScrollY - args: '' - desc: 'available for SDK_INT >= 14' - desc_ru: 'доступно для SDK_INT >= 14' - - name: setSelected - args: '' - desc: '' - desc_ru: '' - - name: setSoundEffectsEnabled - args: '' - desc: '' - desc_ru: '' - - name: setSystemUiVisibility - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setTag - args: '' - desc: '' - desc_ru: '' - - name: setTextAlignment - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: setTextDirection - args: '' - desc: 'available for SDK_INT >= 17' - desc_ru: 'доступно для SDK_INT >= 17' - - name: setTop - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setTranslationX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setTranslationY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setTranslationZ - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: setVerticalFadingEdgeEnabled - args: '' - desc: '' - desc_ru: '' - - name: setVerticalScrollbarPosition - args: '' - desc: '' - desc_ru: '' - - name: setVisibility - args: '' - desc: '' - desc_ru: '' - - name: setWillNotCacheDrawing - args: '' - desc: '' - desc_ru: '' - - name: setWillNotDraw - args: '' - desc: '' - desc_ru: '' - - name: setX - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setY - args: '' - desc: 'available for SDK_INT >= 11' - desc_ru: 'доступно для SDK_INT >= 11' - - name: setZ - args: '' - desc: 'available for SDK_INT >= 21' - desc_ru: 'доступно для SDK_INT >= 21' - - name: showContextMenu - args: '' - desc: '' - desc_ru: '' - - name: willNotCacheDrawing - args: '' - desc: '' - desc_ru: '' - - name: willNotDraw - args: '' - desc: '' - desc_ru: '' - - name: TextViewValue - desc: 'Inheritance hierarchy: ViewValue' - desc_ru: 'Иерархия наследования: ViewValue' - functions: - - name: beginBatchEdit - args: '' - desc: '' - desc_ru: '' - - name: endBatchEdit - args: '' - desc: '' - desc_ru: '' - - name: getAutoLinkMask - args: '' - desc: '' - desc_ru: '' - - name: getCompoundDrawablePadding - args: '' - desc: '' - desc_ru: '' - - name: getCompoundPaddingBottom - args: '' - desc: '' - desc_ru: '' - - name: getCompoundPaddingLeft - args: '' - desc: '' - desc_ru: '' - - name: getCompoundPaddingRight - args: '' - desc: '' - desc_ru: '' - - name: getCompoundPaddingTop - args: '' - desc: '' - desc_ru: '' - - name: getCurrentHintTextColor - args: '' - desc: '' - desc_ru: '' - - name: getCurrentTextColor - args: '' - desc: '' - desc_ru: '' - - name: getEditableText - args: '' - desc: '' - desc_ru: '' - - name: getEllipsize - args: '' - desc: '' - desc_ru: '' - - name: getError - args: '' - desc: '' - desc_ru: '' - - name: getExtendedPaddingBottom - args: '' - desc: '' - desc_ru: '' - - name: getExtendedPaddingTop - args: '' - desc: '' - desc_ru: '' - - name: getFreezesText - args: '' - desc: '' - desc_ru: '' - - name: getGravity - args: '' - desc: '' - desc_ru: '' - - name: getHighlightColor - args: '' - desc: '' - desc_ru: '' - - name: getHint - args: '' - desc: '' - desc_ru: '' - - name: getImeActionId - args: '' - desc: '' - desc_ru: '' - - name: getImeActionLabel - args: '' - desc: '' - desc_ru: '' - - name: getImeOptions - args: '' - desc: '' - desc_ru: '' - - name: getInputType - args: '' - desc: '' - desc_ru: '' - - name: getLineCount - args: '' - desc: '' - desc_ru: '' - - name: getLineHeight - args: '' - desc: '' - desc_ru: '' - - name: getLinksClickable - args: '' - desc: '' - desc_ru: '' - - name: getSelectionEnd - args: '' - desc: '' - desc_ru: '' - - name: getSelectionStart - args: '' - desc: '' - desc_ru: '' - - name: getText - args: '' - desc: '' - desc_ru: '' - - name: getTextScaleX - args: '' - desc: '' - desc_ru: '' - - name: getTextSize - args: '' - desc: '' - desc_ru: '' - - name: getTotalPaddingBottom - args: '' - desc: '' - desc_ru: '' - - name: getTotalPaddingLeft - args: '' - desc: '' - desc_ru: '' - - name: getTotalPaddingRight - args: '' - desc: '' - desc_ru: '' - - name: getTotalPaddingTop - args: '' - desc: '' - desc_ru: '' - - name: hasSelection - args: '' - desc: '' - desc_ru: '' - - name: isCursorVisible - args: '' - desc: '' - desc_ru: '' - - name: isInputMethodTarget - args: '' - desc: '' - desc_ru: '' - - name: isSuggestionsEnabled - args: '' - desc: '' - desc_ru: '' - - name: isTextSelectable - args: '' - desc: '' - desc_ru: '' - - name: length - args: '' - desc: '' - desc_ru: '' - - name: moveCursorToVisibleOffset - args: '' - desc: '' - desc_ru: '' - - name: setAllCaps - args: '' - desc: '' - desc_ru: '' - - name: setAutoLinkMask - args: '' - desc: '' - desc_ru: '' - - name: setBreakStrategy - args: '' - desc: '' - desc_ru: '' - - name: setCompoundDrawablePadding - args: '' - desc: '' - desc_ru: '' - - name: setCompoundDrawables - args: '' - desc: '' - desc_ru: '' - - name: setCursorVisible - args: '' - desc: '' - desc_ru: '' - - name: setEllipsize - args: '' - desc: '' - desc_ru: '' - - name: setEms - args: '' - desc: '' - desc_ru: '' - - name: setError - args: '' - desc: '' - desc_ru: '' - - name: setFreezesText - args: '' - desc: '' - desc_ru: '' - - name: setGravity - args: '' - desc: '' - desc_ru: '' - - name: setHeight - args: '' - desc: '' - desc_ru: '' - - name: setHighlightColor - args: '' - desc: '' - desc_ru: '' - - name: setHint - args: '' - desc: '' - desc_ru: '' - - name: setHintTextColor - args: '' - desc: '' - desc_ru: '' - - name: setHorizontallyScrolling - args: '' - desc: '' - desc_ru: '' - - name: setImeOptions - args: '' - desc: '' - desc_ru: '' - - name: setInputType - args: '' - desc: '' - desc_ru: '' - - name: setLines - args: '' - desc: '' - desc_ru: '' - - name: setLinkTextColor - args: '' - desc: '' - desc_ru: '' - - name: setLinksClickable - args: '' - desc: '' - desc_ru: '' - - name: setMaxEms - args: '' - desc: '' - desc_ru: '' - - name: setMaxHeight - args: '' - desc: '' - desc_ru: '' - - name: setMaxLines - args: '' - desc: '' - desc_ru: '' - - name: setMaxWidth - args: '' - desc: '' - desc_ru: '' - - name: setMinEms - args: '' - desc: '' - desc_ru: '' - - name: setMinHeight - args: '' - desc: '' - desc_ru: '' - - name: setMinLines - args: '' - desc: '' - desc_ru: '' - - name: setMinWidth - args: '' - desc: '' - desc_ru: '' - - name: setPaintFlags - args: '' - desc: '' - desc_ru: '' - - name: setRawInputType - args: '' - desc: '' - desc_ru: '' - - name: setSelectAllOnFocus - args: '' - desc: '' - desc_ru: '' - - name: setSingleLine - args: '' - desc: '' - desc_ru: '' - - name: setText - args: '' - desc: '' - desc_ru: '' - - name: setTextColor - args: '' - desc: '' - desc_ru: '' - - name: setTextIsSelectable - args: '' - desc: '' - desc_ru: '' - - name: setTextScaleX - args: '' - desc: '' - desc_ru: '' - - name: setTextSize - args: '' - desc: '' - desc_ru: '' - - name: setWidth - args: '' - desc: '' - desc_ru: '' - - name: EditTextValue - desc: 'Inheritance hierarchy: TextViewValue < ViewValue' - desc_ru: 'Иерархия наследования: TextViewValue < ViewValue' - functions: - - name: extendSelection - args: '' - desc: '' - desc_ru: '' - - name: selectAll - args: '' - desc: '' - desc_ru: '' - - name: setSelection - args: '' - desc: '' - desc_ru: '' - - name: ButtonValue - desc: 'Inheritance hierarchy: TextViewValue < ViewValue' - desc_ru: 'Иерархия наследования: TextViewValue < ViewValue' - functions: [] - - name: CompoundButtonValue - desc: 'Inheritance hierarchy: ButtonValue < TextViewValue < ViewValue' - desc_ru: 'Иерархия наследования: ButtonValue < TextViewValue < ViewValue' - functions: - - name: isChecked - args: '' - desc: '' - desc_ru: '' - - name: onCheck - args: '' - desc: '' - desc_ru: '' - - name: setButtonDrawable - args: '' - desc: '' - desc_ru: '' - - name: setChecked - args: '' - desc: '' - desc_ru: '' - - name: toggle - args: '' - desc: '' - desc_ru: '' - - name: ToggleButtonValue - desc: 'Inheritance hierarchy: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' - desc_ru: 'Иерархия наследования: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' - functions: - - name: getTextOff - args: '' - desc: '' - desc_ru: '' - - name: getTextOn - args: '' - desc: '' - desc_ru: '' - - name: setTextOff - args: '' - desc: '' - desc_ru: '' - - name: setTextOn - args: '' - desc: '' - desc_ru: '' - - name: SwitchValue - desc: 'Inheritance hierarchy: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' - desc_ru: 'Иерархия наследования: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' - functions: - - name: getTextOff - args: '' - desc: '' - desc_ru: '' - - name: getTextOn - args: '' - desc: '' - desc_ru: '' - - name: setTextOff - args: '' - desc: '' - desc_ru: '' - - name: setTextOn - args: '' - desc: '' - desc_ru: '' - - name: ImageViewValue - desc: 'Inheritance hierarchy: ViewValue' - desc_ru: 'Иерархия наследования: ViewValue' - functions: - - name: clearColorFilter - args: '' - desc: '' - desc_ru: '' - - name: getScaleType - args: '' - desc: '' - desc_ru: '' - - name: setAdjustViewBounds - args: '' - desc: '' - desc_ru: '' - - name: setColorFilter - args: '' - desc: '' - desc_ru: '' - - name: setImageAlpha - args: '' - desc: '' - desc_ru: '' - - name: setImageBitmap - args: '' - desc: '' - desc_ru: '' - - name: setImageDrawable - args: '' - desc: '' - desc_ru: '' - - name: setImageLevel - args: '' - desc: '' - desc_ru: '' - - name: setImageResource - args: '' - desc: '' - desc_ru: '' - - name: setImageURI - args: '' - desc: '' - desc_ru: '' - - name: setMaxHeight - args: '' - desc: '' - desc_ru: '' - - name: setMaxWidth - args: '' - desc: '' - desc_ru: '' - - name: setScaleType - args: '' - desc: '' - desc_ru: '' - - name: ImageButtonValue - desc: 'Inheritance hierarchy: ImageViewValue < ViewValue' - desc_ru: 'Иерархия наследования: ImageViewValue < ViewValue' - functions: [] - - name: ViewGroupValue - desc: 'Inheritance hierarchy: ViewValue' - desc_ru: 'Иерархия наследования: ViewValue' - functions: - - name: addView - args: '' - desc: '' - desc_ru: '' - - name: bringChildToFront - args: '' - desc: '' - desc_ru: '' - - name: clearChildFocus - args: '' - desc: '' - desc_ru: '' - - name: getChildAt - args: '' - desc: '' - desc_ru: '' - - name: getChildCount - args: '' - desc: '' - desc_ru: '' - - name: indexOfChild - args: '' - desc: '' - desc_ru: '' - - name: recomputeViewAttributes - args: '' - desc: '' - desc_ru: '' - - name: removeAllViews - args: '' - desc: '' - desc_ru: '' - - name: removeAllViewsInLayout - args: '' - desc: '' - desc_ru: '' - - name: removeView - args: '' - desc: '' - desc_ru: '' - - name: removeViewAt - args: '' - desc: '' - desc_ru: '' - - name: removeViewInLayout - args: '' - desc: '' - desc_ru: '' - - name: LinearLayoutValue - desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' - functions: - - name: getOrientation - args: '' - desc: '' - desc_ru: '' - - name: getWeightSum - args: '' - desc: '' - desc_ru: '' - - name: setGravity - args: '' - desc: '' - desc_ru: '' - - name: setHorizontalGravity - args: '' - desc: '' - desc_ru: '' - - name: setOrientation - args: '' - desc: '' - desc_ru: '' - - name: setVerticalGravity - args: '' - desc: '' - desc_ru: '' - - name: setWeightSum - args: '' - desc: '' - desc_ru: '' - - name: RelativeLayoutValue - desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' - functions: - - name: getGravity - args: '' - desc: '' - desc_ru: '' - - name: setGravity - args: '' - desc: '' - desc_ru: '' - - name: setHorizontalGravity - args: '' - desc: '' - desc_ru: '' - - name: setIgnoreGravity - args: '' - desc: '' - desc_ru: '' - - name: setVerticalGravity - args: '' - desc: '' - desc_ru: '' - - name: FrameLayoutValue - desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' - functions: [] - - name: ScrollViewValue - desc: 'Inheritance hierarchy: FrameLayoutValue < ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: FrameLayoutValue < ViewGroupValue < ViewValue' - functions: - - name: isFillViewport - args: '' - desc: '' - desc_ru: '' - - name: isSmoothScrollingEnabled - args: '' - desc: '' - desc_ru: '' - - name: setFillViewport - args: '' - desc: '' - desc_ru: '' - - name: setSmoothScrollingEnabled - args: '' - desc: '' - desc_ru: '' - - name: AdapterViewValue - desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' - functions: - - name: getAdapter - args: '' - desc: '' - desc_ru: '' - - name: getCount - args: '' - desc: '' - desc_ru: '' - - name: getEmptyView - args: '' - desc: '' - desc_ru: '' - - name: getFirstVisiblePosition - args: '' - desc: '' - desc_ru: '' - - name: getItemAtPosition - args: '' - desc: '' - desc_ru: '' - - name: getItemIdAtPosition - args: '' - desc: '' - desc_ru: '' - - name: getLastVisiblePosition - args: '' - desc: '' - desc_ru: '' - - name: getPositionForView - args: '' - desc: '' - desc_ru: '' - - name: getSelectedItem - args: '' - desc: '' - desc_ru: '' - - name: getSelectedItemId - args: '' - desc: '' - desc_ru: '' - - name: getSelectedItemPosition - args: '' - desc: '' - desc_ru: '' - - name: getSelectedView - args: '' - desc: '' - desc_ru: '' - - name: onItemClick - args: '' - desc: '' - desc_ru: '' - - name: onItemLongClick - args: '' - desc: '' - desc_ru: '' - - name: onItemSelected - args: '' - desc: '' - desc_ru: '' - - name: performItemClick - args: '' - desc: '' - desc_ru: '' - - name: setAdapter - args: '' - desc: '' - desc_ru: '' - - name: setEmptyView - args: '' - desc: '' - desc_ru: '' - - name: ListViewValue - desc: 'Inheritance hierarchy: AdapterViewValue < ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: AdapterViewValue < ViewGroupValue < ViewValue' - functions: - - name: addFooterView - args: '' - desc: '' - desc_ru: '' - - name: addHeaderView - args: '' - desc: '' - desc_ru: '' - - name: getDividerHeight - args: '' - desc: '' - desc_ru: '' - - name: getFooterViewsCount - args: '' - desc: '' - desc_ru: '' - - name: getHeaderViewsCount - args: '' - desc: '' - desc_ru: '' - - name: getItemsCanFocus - args: '' - desc: '' - desc_ru: '' - - name: getMaxScrollAmount - args: '' - desc: '' - desc_ru: '' - - name: removeFooterView - args: '' - desc: '' - desc_ru: '' - - name: removeHeaderView - args: '' - desc: '' - desc_ru: '' - - name: setCacheColorHint - args: '' - desc: '' - desc_ru: '' - - name: setDividerHeight - args: '' - desc: '' - desc_ru: '' - - name: setFooterDividersEnabled - args: '' - desc: '' - desc_ru: '' - - name: setHeaderDividersEnabled - args: '' - desc: '' - desc_ru: '' - - name: setItemsCanFocus - args: '' - desc: '' - desc_ru: '' - - name: setSelection - args: '' - desc: '' - desc_ru: '' - - name: setSelectionAfterHeaderView - args: '' - desc: '' - desc_ru: '' - - name: smoothScrollToPosition - args: '' - desc: '' - desc_ru: '' - - name: RadioGroupValue - desc: 'Inheritance hierarchy: LinearLayoutValue < ViewGroupValue < ViewValue' - desc_ru: 'Иерархия наследования: LinearLayoutValue < ViewGroupValue < ViewValue' - functions: - - name: check - args: '' - desc: '' - desc_ru: '' - - name: clearCheck - args: '' - desc: '' - desc_ru: '' - - name: getCheckedRadioButtonId - args: '' - desc: '' - desc_ru: '' - - name: onCheck - args: '' - desc: '' - desc_ru: '' - - name: setOnCheckedChangeListener - args: '' - desc: '' - desc_ru: '' - - name: ProgressBarValue - desc: 'Inheritance hierarchy: ViewValue' - desc_ru: 'Иерархия наследования: ViewValue' - functions: - - name: getMax - args: '' - desc: '' - desc_ru: '' - - name: getProgress - args: '' - desc: '' - desc_ru: '' - - name: getSecondaryProgress - args: '' - desc: '' - desc_ru: '' - - name: incrementProgressBy - args: '' - desc: '' - desc_ru: '' - - name: incrementSecondaryProgressBy - args: '' - desc: '' - desc_ru: '' - - name: setIndeterminate - args: '' - desc: '' - desc_ru: '' - - name: setIndeterminateDrawable - args: '' - desc: '' - desc_ru: '' - - name: setMax - args: '' - desc: '' - desc_ru: '' - - name: setProgress - args: '' - desc: '' - desc_ru: '' - - name: setProgressDrawable - args: '' - desc: '' - desc_ru: '' - - name: setSecondaryProgress - args: '' - desc: '' - desc_ru: '' - - name: SeekBarValue - desc: 'Inheritance hierarchy: ProgressBarValue < ViewValue' - desc_ru: 'Иерархия наследования: ProgressBarValue < ViewValue' - functions: - - name: getKeyProgressIncrement - args: '' - desc: '' - desc_ru: '' - - name: getThumbOffset - args: '' - desc: '' - desc_ru: '' - - name: onSeekBarChange - args: '' - desc: '' - desc_ru: '' - - name: setKeyProgressIncrement - args: '' - desc: '' - desc_ru: '' - - name: setOnSeekBarChangeListener - args: '' - desc: '' - desc_ru: '' - - name: setThumb - args: '' - desc: '' - desc_ru: '' - - name: setThumbOffset - args: '' - desc: '' - desc_ru: '' - - name: AdapterValue - functions: - - name: getCount - args: '' - desc: '' - desc_ru: '' - - name: getItem - args: '' - desc: '' - desc_ru: '' - - name: getItemId - args: '' - desc: '' - desc_ru: '' - - name: getItemViewType - args: '' - desc: '' - desc_ru: '' - - name: getView - args: '' - desc: '' - desc_ru: '' - - name: getViewTypeCount - args: '' - desc: '' - desc_ru: '' - - name: hasStableIds - args: '' - desc: '' - desc_ru: '' - - name: isEmpty - args: '' - desc: '' - desc_ru: '' - - name: ListAdapterValue - desc: 'Inheritance hierarchy: AdapterValue' - desc_ru: 'Иерархия наследования: AdapterValue' - functions: - - name: areAllItemsEnabled - args: '' - desc: '' - desc_ru: '' - - name: isEnabled - args: '' - desc: '' - desc_ru: '' - - name: imageprocessing - scope: "android" - desc: |- - Contains functions for image processing. - - You can apply effect in two ways: - - 1. Pass BitmapValue and parameters array. The result will be a BitmapValue. `bitmap = boxBlur(bitmap, [20, 40])` - 2. Pass width, height, pixels array and parameters array. The result will be an array [width, height, pixels]. `extract(width, height, pixels) = boxBlur(w, h, pixels, [20, 40])` - desc_ru: |- - Содержит функции для обработки изображений. - - Применить эффект можно двумя способами: - - 1. Передать BitmapValue и массив параметров. Результатом будет BitmapValue. `bitmap = boxBlur(bitmap, [20, 40])` - 2. Передать ширину, высоту, массив пикселей и массив параметров. Результатом будет массив [ширина, высота, пиксели]. `extract(width, height, pixels) = boxBlur(w, h, pixels, [20, 40])` - functions: - - - name: "boxBlur" - args: "horizontalBlur = 10 (min 1, max 100), verticalBlur = 10 (min 1, max 100)" - desc: "applies quick blur effect" - desc_ru: "применяет быстрый эффект размытия" - - - name: "contrast" - args: "level = 40 (min -100, max 100)" - desc: "changes contrast of the image" - desc_ru: "изменяет контрастность изображения" - - - name: "decolour" - args: "" - desc: "converts color image to grayscale" - desc_ru: "преобразует цветное изображение в оттенки серого" - - - name: "edgeDetection" - args: "operator = 1, mode = 0" - desc: |- - applies edge detection effect. - - `operator` 0 - Roberts, 1 - Prewitt, 2 - Sobel, 3 - Scharr - `mode` 0 - color edges, 1 - gray edges, 2 - subtract edges - desc_ru: |- - применяет эффект выделения границ. - - `operator` 0 - оператор Робертса, 1 - Прюитт, 2 - Собеля, 3 - Шарра - `mode` 0 - цветные грани, 1 - чёрно-белые грани, 2 - вычитание границ - - - name: "emboss" - args: "azimuth = 45 (min 0, max 360), elevation = 45 (min 0, max 90), edgeHeight = 140 (min 0, max 256), edgeThickness = 80 (min 2, max 100), emboss = 0 (min 0, max 1)" - desc: "applies emboss effect" - desc_ru: "применяет эффект выдавливания" - - - name: "extractChannel" - args: "channel = 0, monochrome = 0" - desc: |- - extracts given channel from image. - - `channel` 0 - red, 1 - green, 2 - blue - `monochrome` 0 - off, 1 - on - desc_ru: |- - извлекает заданный канал из изображения. - - `channel` 0 - красный, 1 - зелёный, 2 - синий - `monochrome` конвертировать полученную маску в чёрно-белый, 0 - нет, 1 - да - - - name: "gamma" - args: "level = 20 (min -50, max 50)" - desc: "changes gamma of the image" - desc_ru: "изменяет гамму изображения" - - - name: "hsbCorrection" - args: "hue = 45 (min 0, max 360), saturation = 0 (min -100, max 100), brightness = 0 (min -100, max 100), tone = 0 (min 0, max 1)" - desc: "changes hue, saturation and brightness of the image" - desc_ru: "изменяет оттенок, насыщенность и яркость изображения, тонирует при `tone` = 1" - - - name: "invert" - args: "invertAlpha = 0, invertRed = 1, invertGreen = 2, invertBlue = 3" - desc: "inverts channels of the image" - desc_ru: "инвертирует заданные каналы изображения" - - - name: "monochrome" - args: "level = 128 (min 0, max 255)" - desc: "converts color image to monochrome" - desc_ru: "преобразует цветное изображение в монохромное" - - - name: "mosaic" - args: "size = 4 (min 1, max 50)" - desc: "applies mosaic effect" - desc_ru: "применяет эффект мозайки" - - - name: "noiseGeneration" - args: "amount = 50 (min 0, max 255), monochrome = 0" - desc: "adds noise to images" - desc_ru: "добавляет шум к изображению" - - - name: "posterization" - args: "level = 64 (min 1, max 255)" - desc: "applies posterization effect" - desc_ru: "применяет эффект постеризации" - - - name: "rgbCorrection" - args: "alpha = 0 (min -255, max 255), red = 0 (min -255, max 255), green = 0 (min -255, max 255), blue = 0 (min -255, max 255)" - desc: "changes alpha, red, green and blue channels of the image" - desc_ru: "изменяет прозрачность, красный, зелёный, синий каналы изображения" - - - name: "rotate" - args: "angle = 45 (min 0, max 360)" - desc: "rotates image" - desc_ru: "поворачивает изображение" - - - name: "saturation" - args: "level = 64 (min -255, max 255)" - desc: "changes saturation of the image" - desc_ru: "изменяет насыщенность изображения" - - - name: "scatter" - args: "horizontalScatter = 10 (min 1, max 100), verticalScatter = 10 (min 1, max 100)" - desc: "applies pixel scatter effect" - desc_ru: "применяет эффект рассеивания пикселей" - - - name: "smooth" - args: "level = 3 (min 1, max 25)" - desc: "applies smooth effect" - desc_ru: "применяет эффект сглаживания" - - - name: "xor" - args: "level = 64 (min 0, max 255)" - desc: "applies xor operation for each pixel of the image" - desc_ru: "применяет операцию ИСКЛЮЧАЮЩЕЕ ИЛИ для каждого пикселя изображения" - - name: gps - scope: "android" - desc: |- - Contains functions for working with GPS. - desc_ru: |- - Содержит функции для работы с GPS. - constants: - - name: GPS_PROVIDER - type: 2 - typeName: string - value: gps - - name: NETWORK_PROVIDER - type: 2 - typeName: string - value: network - functions: - - - name: "isEnabled" - args: "provider" - desc: "checks if the given location service provider is enabled" - desc_ru: "проверяет доступность указанного провайдера местоположения" - - - name: "lastKnownLocation" - args: "provider" - desc: "gets last known location with the given location provider, or zero if it is unable to get location" - desc_ru: "получает последнее сохранённое местоположение для указанного провайдера, либо 0, если получить местоположение не удалось" - - - name: "getProviders" - args: "enabledOnly = false" - desc: "returns an array of location providers" - desc_ru: "возвращает массив провайдеров местоположения" - - - name: "requestUpdates" - args: "provider, minTime, minDistance, callback" - desc: |- - subscribes to the location listener - desc_ru: |- - подписывается на обработчик получения местоположения - example: |- - use std, gps - - provider = "gps" // or passive, network if exists - // requestUpdates(provider, 0, 25, def(loc) = echo("location changed: ", loc)) - requestUpdates(provider, 10 * 1000, 25, { - "onLocationChanged" : def(loc) = echo("location changed: ", loc) - "onStatusChanged" : def(p, status) = echo("status changed: ", p, " is ", getStatus(status)) - "onProviderEnabled" : def(p) = echo("provider ", p, " is now enabled") - "onProviderDisabled" : def(p) = echo("provider ", p, " is now disabled") - }) diff --git a/docs/src/modules/android.yml b/docs/src/modules/android.yml new file mode 100644 index 00000000..8880db8b --- /dev/null +++ b/docs/src/modules/android.yml @@ -0,0 +1,62 @@ +name: android +scope: "android" +desc: "Contains common Android functions" +desc_ru: "Содержит вспомогательные функции для Android" +constants: + - name: "Intent" + typeName: map + type: 4 + value: "{ACTION_BOOT_COMPLETED=android.intent.action.BOOT_COMPLETED, ACTION_DEFAULT=android.intent.action.VIEW, ACTION_DELETE=android.intent.action.DELETE, ACTION_EDIT=android.intent.action.EDIT, ACTION_INSTALL_PACKAGE=android.intent.action.INSTALL_PACKAGE, ACTION_LOCATION_SOURCE_SETTINGS=android.settings.LOCATION_SOURCE_SETTINGS, ACTION_MAIN=android.intent.action.MAIN, ACTION_MEDIA_MOUNTED=android.intent.action.MEDIA_MOUNTED, ACTION_REBOOT=android.intent.action.REBOOT, ACTION_RUN=android.intent.action.RUN, ACTION_SEARCH=android.intent.action.SEARCH, ACTION_SEND=android.intent.action.SEND, ACTION_VIEW=android.intent.action.VIEW, ACTION_WEB_SEARCH=android.intent.action.WEB_SEARCH}" + - name: R + type: 4 + typeName: map + value: '{array={}, attr={}, color={}, drawable={}, id={}, integer={}, layout={}, string={}}' + desc: "Resource constants from android.R.xxx class" + desc_ru: "Константы ресурсов класса android.R.xxx" + - name: SDK_INT + type: 1 + typeName: number + value: 'Android version SDK' + desc: "Android version SDK" + desc_ru: "Версия SDK Android" + - name: "Span" + typeName: map + type: 4 + value: "{COLOR=0, ABSOLUTE_SIZE=1, RELATIVE_SIZE=2, URL=3, STYLE=4, CLICKABLE=5, TYPEFACE=6, HTML=7}" +functions: + - name: "assetBitmap" + args: "path" + desc: "loads bitmap from the file in apk's assets folder" + desc_ru: "загружает изображение из файла в папке assets внутри apk" + - name: "assetBytes" + args: "path" + desc: "reads bytes of the file in apk's assets folder" + desc_ru: "читает массив байт из файла в папке assets внутри apk" + - name: "assetText" + args: "path" + desc: "reads text content of the file in apk's assets folder" + desc_ru: "читает текст файла в папке assets внутри apk" + - name: "chooser" + args: "" + desc: "" + desc_ru: "" + - name: "listAssets" + args: "path" + desc: "returns list of files in apk's assets folder" + desc_ru: "возвращает список файлов в папке assets внутри apk" + - name: "spannable" + args: "type, text, ..." + desc: "" + desc_ru: "" + - name: "startActivity" + args: "intent, uri = \"\", bundle = []" + desc: "starts an activity" + desc_ru: "запускает Activity" + - name: "toast" + args: "text, duration = 0" + desc: "shows toast notification" + desc_ru: "показывает всплывающее уведомление (toast)" + - name: "uithread" + args: "function, ..." + desc: "runs function in main UI-thread" + desc_ru: "выполняет функцию в главном UI-потоке" \ No newline at end of file diff --git a/docs/src/modules/base64.yml b/docs/src/modules/base64.yml new file mode 100644 index 00000000..ab771c50 --- /dev/null +++ b/docs/src/modules/base64.yml @@ -0,0 +1,24 @@ +name: base64 +scope: both +desc: "Contains base64 encoding and decoding functions" +desc_ru: "Содержит функции кодирования данных в base64 и наоборот" +constants: + - name: BASE64_URL_SAFE + type: 1 + typeName: number + value: '8' + desc: 'Url safe encoding output' + desc_ru: 'Вывод данных в безопасном для ссылок формате' +functions: + - name: base64decode + args: 'data, type = 0' + desc: 'decodes base64-encoded byte array or string into byte array' + desc_ru: 'декодирует массив байт или строку, закодированную в base64, в массив байт' + - name: base64encode + args: 'data, type = 0' + desc: 'encodes byte array or string into base64-encoded byte array' + desc_ru: 'кодирует массив байт или строку в закодированный base64 массив байт' + - name: base64encodeToString + args: 'data, type = 0' + desc: 'encodes byte array or string into base64-encoded string' + desc_ru: 'кодирует массив байт или строку в закодированную base64 строку' \ No newline at end of file diff --git a/docs/src/modules/canvas.yml b/docs/src/modules/canvas.yml new file mode 100644 index 00000000..101f829a --- /dev/null +++ b/docs/src/modules/canvas.yml @@ -0,0 +1,98 @@ +name: canvas +scope: "desktop" +desc: "Contains functions for working with graphics" +desc_ru: "Содержит функции для работы с графикой" +constants: + - name: "VK_DOWN" + typeName: number + type: 1 + value: "40" + desc: "arrow down key code" + desc_ru: "код клавиши стрелка вниз" + - name: "VK_ESCAPE" + typeName: number + type: 1 + value: "27" + desc: "Esc key code" + desc_ru: "код клавиши Esc" + - name: "VK_FIRE" + typeName: number + type: 1 + value: "10" + desc: "Enter key code" + desc_ru: "код клавиши Enter" + - name: "VK_LEFT" + typeName: number + type: 1 + value: "37" + desc: "arrow left key code" + desc_ru: "код клавиши стрелка влево" + - name: "VK_RIGHT" + typeName: number + type: 1 + value: "39" + desc: "arrow left key code" + desc_ru: "код клавиши стрелка вправо" + - name: "VK_UP" + typeName: number + type: 1 + value: "38" + desc: "arrow up key code" + desc_ru: "код клавиши стрелка вверх" +functions: + - name: "clip" + args: "x, y, w, h" + desc: "sets the current clip to the rectangle specified by the given coordinates" + desc_ru: "устанавливает текущий клип в прямоугольник, заданный данными координатами" + - name: "color" + args: "rgb" + desc: "sets color drawing. `rgb` - color with the specified combined RGB value" + desc_ru: "устанвливает цвет рисования. `rgb` - целое, комбинация цветов RGB, например `#FFGGFF`" + - name: "color" + args: "red, green, blue" + desc: "sets color with the specified red, green, and blue values in the range (0 - 255)" + desc_ru: "устанвливает цвет рисования c отдельными уровнями красного, зеленого и синего в диапазоне (0 - 255)" + - name: "drawstring" + args: "text, x, y" + desc: "draws string `text` at position `x`, `y`" + desc_ru: "рисует строку `text` с координатами `x`, `y`" + - name: "foval" + args: "x, y, w, h" + desc: "draws a filled oval at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует закрашенный овал на позиции `x`, `y`, размером `w`, `h`" + - name: "frect" + args: "x, y, w, h" + desc: "draws a filled rectangle at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует закрашенный прямоугольник на позиции `x`, `y`, размером `w`, `h`" + - name: "keypressed" + args: "" + desc: "returns the code of the pressed key (see the constant section)" + desc_ru: "возрвращает код нажатой клавиши (см. раздел константы)" + - name: "line" + args: "x1, y1, x2, y2" + desc: "draws line from point (`x1`;y1`) to (`x2`;y2`)" + desc_ru: "рисует линию от позиции (`x1`;y1`) до (`x2`;y2`)" + - name: "mousehover" + args: "" + desc: "returns array with current mouse pointer coordinates" + desc_ru: "возвращает массив с текущими координатами указателя мыши" + - name: "oval" + args: "x, y, w, h" + desc: "draws a oval at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует овал на позиции `x`, `y`, размером `w`, `h`" + - name: "prompt" + args: "message" + desc: "displays a dialog box that prompts the visitor for input" + desc_ru: "показывает диалог для ввода значения от пользователя" + - name: "rect" + args: "x, y, w, h" + desc: "draws a rectangle at position `x`,` y`, size `w`,` h`" + desc_ru: "рисует прямоугольник на позиции `x`, `y`, размером `w`, `h`" + - name: "repaint" + args: "" + desc: "draws elements from graphics buffer on canvas" + desc_ru: "прорисовывает элементы из буфера на холсте" + - name: "window" + args: "name, width, hight" + desc: "creates a new window with the specified `name` and size `width`x`height`" + desc_ru: "создает новое окно с именем `name` и размером `width`x`height`" \ No newline at end of file diff --git a/docs/src/modules/canvas_android.yml b/docs/src/modules/canvas_android.yml new file mode 100644 index 00000000..a224c722 --- /dev/null +++ b/docs/src/modules/canvas_android.yml @@ -0,0 +1,658 @@ +name: canvas +scope: "android" +desc: "Contains functions for working with graphics on Android" +desc_ru: "Содержит функции для работы с графикой в Android" +constants: + - name: "VertexMode" + typeName: map + type: 4 + value: "{TRIANGLES=0, TRIANGLE_STRIP=1, TRIANGLE_FAN=2}" + - name: "Action" + typeName: map + type: 4 + value: "{DOWN=0, UP=1, MOVE=2, MULTIPLE=2, CANCEL=3, OUTSIDE=4, POINTER_DOWN=5, POINTER_UP=6, POINTER_INDEX_SHIFT=8, MASK=255, POINTER_INDEX_MASK=65280}" + - name: "BitmapCompressFormat" + typeName: map + type: 4 + value: "{JPEG=0, PNG=1, WEBP=2}" + - name: "EdgeType" + typeName: map + type: 4 + value: "{BW=0, AA=1}" + - name: "Cap" + typeName: map + type: 4 + value: "{BUTT=0, ROUND=1, SQUARE=2}" + - name: "Style" + typeName: map + type: 4 + value: "{FILL=0, STROKE=1, FILL_AND_STROKE=2}" + - name: "BitmapConfig" + typeName: map + type: 4 + value: "{ALPHA_8=0, RGB_565=1, ARGB_4444=2, ARGB_8888=3}" + - name: "Join" + typeName: map + type: 4 + value: "{MITER=0, ROUND=1, BEVEL=2}" + - name: "Align" + typeName: map + type: 4 + value: "{LEFT=0, CENTER=1, RIGHT=2}" + - name: "DisplayMetrics" + typeName: map + type: 4 + value: "{density=, densityDpi=, scaledDensity=, widthPixels=, heightPixels=, xdpi=, ydpi=}" + since: 1.5.1 +functions: + - name: "createBitmap" + args: "..." + desc: |- + `createBitmap(bitmap)` - creates a copy of the bitmap. + + `createBitmap(bytes)` - creates bitmap from byte array. + + `createBitmap(w, h)` - creates new bitmap with the given size. + + `createBitmap(w, h, config)` - creates new bitmap with the given size and config. + + `createBitmap(bytes, offset, length)` - creates bitmap from byte array starting from offset. + + `createBitmap(pixels, w, h, config)` - creates new bitmap from pixels array. + + `createBitmap(bitmap, x, y, w, h)` - creates new bitmap from the part of the `bitmap`. + + `createBitmap(pixels, offset, stride, w, h, config)` - creates new bitmap from pixels array starting from offset. + + Returns BitmapValue. + desc_ru: |- + `createBitmap(bitmap)` - создаёт копию изображения. + + `createBitmap(bytes)` - создаёт изображение из массива байт. + + `createBitmap(w, h)` - создаёт новое изображение с заданным размером. + + `createBitmap(w, h, config)` - создаёт новое изображение с заданным размером и конфигурацией. + + `createBitmap(bytes, offset, length)` - создаёт изображение из массива байт, начиная с offset. + + `createBitmap(pixels, w, h, config)` - создаёт изображение из массива пикселей. + + `createBitmap(bitmap, x, y, w, h)` - создаёт изображение из части другого изображения. + + `createBitmap(pixels, offset, stride, w, h, config)` - создаёт изображение из массива пикселей, начиная с offset. + + Возвращает BitmapValue. + example: |- + use http, canvas + + g = showcanvas() + url = "http://lorempixel.com/640/480/nature" + imageBytes = download(url) + bitmap = createBitmap(imageBytes) + g.drawBitmap(bitmap, 0, 0) + repaint() + - name: "createScaledBitmap" + args: "srcBitmap, width, height, filter" + desc: "scales bitmap to size and returns new BitmapValue" + desc_ru: "возвращает BitmapValue с изменённым размером заданного изображения" + - name: "getScreenBitmap" + args: "" + desc: "returns current screen as bitmap" + desc_ru: "возвращает содержимое экрана в виде изображения" + - name: "hidecanvas" + args: "" + desc: "closes canvas screen and releases resources" + desc_ru: "закрывает экран канваса и освобождает ресурсы" + - name: "newPath" + args: "path = null" + desc: "creates new PathValue" + desc_ru: "создаёт новый PathValue" + since: 1.5.1 + - name: "newLinearGradient" + args: "x0, y0, x1, y1, colors|color1, positions|color2, tileMode" + desc: "creates new shader" + desc_ru: "создаёт новый шейдер" + since: 1.5.1 + - name: "newRadialGradient" + args: "cx, cy, radius, colors|color1, positions|color2, tileMode" + desc: "creates new shader" + desc_ru: "создаёт новый шейдер" + since: 1.5.1 + - name: "newSweepGradient" + args: "cx, cy, colors|color1, positions|color2" + desc: "creates new shader" + desc_ru: "создаёт новый шейдер" + since: 1.5.1 + - name: "newBitmapShader" + args: "bitmap, modeX, modeY" + desc: "creates new bitmap shader" + desc_ru: "создаёт новый шейдер из картинки" + since: 1.5.1 + - name: "newComposeShader" + args: "shader1, shader2, mode = SRC_OVER" + desc: "creates new composition shader" + desc_ru: "создаёт новый композитный шейдер" + since: 1.5.1 + - name: "repaint" + args: "" + desc: "" + desc_ru: "" + - name: "setOnKeyDownEvent" + args: "" + desc: "" + desc_ru: "" + - name: "setOnKeyUpEvent" + args: "" + desc: "" + desc_ru: "" + - name: "setOnLongPressEvent" + args: "" + desc: "" + desc_ru: "" + - name: "setOnTouchEvent" + args: "" + desc: "" + desc_ru: "" + - name: "setGestureDetectorListener" + args: "listener" + desc: "sets gesture detector listener" + desc_ru: "устанавливает обработчик детектора жестов" + since: 1.5.1 + - name: "showcanvas" + args: "" + desc: "shows canvas screen and returns GraphicsValue" + desc_ru: "показывает экран канваса и возвращает GraphicsValue" + example: |- + use canvas + g = showcanvas() +types: + - name: "BitmapValue" + functions: + - name: "compress" + args: "" + desc: "" + desc_ru: "" + - name: "copy" + args: "" + desc: "" + desc_ru: "" + - name: "eraseColor" + args: "" + desc: "" + desc_ru: "" + - name: "extractAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "getAllocationByteCount" + args: "" + desc: "" + desc_ru: "" + - name: "getByteCount" + args: "" + desc: "" + desc_ru: "" + - name: "getDensity" + args: "" + desc: "" + desc_ru: "" + - name: "getGraphics" + args: "" + desc: "" + desc_ru: "" + - name: "getWidth" + args: "" + desc: "" + desc_ru: "" + - name: "getHeight" + args: "" + desc: "" + desc_ru: "" + - name: "getRowBytes" + args: "" + desc: "" + desc_ru: "" + - name: "getPixel" + args: "" + desc: "" + desc_ru: "" + - name: "getPixels" + args: "" + desc: "" + desc_ru: "" + - name: "getScaledWidth" + args: "" + desc: "" + desc_ru: "" + - name: "getScaledHeight" + args: "" + desc: "" + desc_ru: "" + - name: "hasAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "hasMipMap" + args: "" + desc: "" + desc_ru: "" + - name: "isMutable" + args: "" + desc: "" + desc_ru: "" + - name: "isPremultiplied" + args: "" + desc: "" + desc_ru: "" + - name: "isRecycled" + args: "" + desc: "" + desc_ru: "" + - name: "prepareToDraw" + args: "" + desc: "" + desc_ru: "" + - name: "recycle" + args: "" + desc: "" + desc_ru: "" + - name: "setPixel" + args: "" + desc: "" + desc_ru: "" + - name: "setPixels" + args: "" + desc: "" + desc_ru: "" + - name: "GraphicsValue" + functions: + - name: "ascent" + args: "" + desc: "" + desc_ru: "" + - name: "breakText" + args: "" + desc: "" + desc_ru: "" + - name: "clearShadowLayer" + args: "" + desc: "" + desc_ru: "" + - name: "clipPath" + args: "path" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "clipRect" + args: "x, y, w, h, op = 0" + desc: "" + desc_ru: "" + - name: "descent" + args: "" + desc: "" + desc_ru: "" + - name: "drawARGB" + args: "" + desc: "" + desc_ru: "" + - name: "drawArc" + args: "" + desc: "" + desc_ru: "" + - name: "drawBitmap" + args: "" + desc: "" + desc_ru: "" + - name: "drawCircle" + args: "" + desc: "" + desc_ru: "" + - name: "drawColor" + args: "" + desc: "" + desc_ru: "" + - name: "drawLine" + args: "" + desc: "" + desc_ru: "" + - name: "drawOval" + args: "" + desc: "" + desc_ru: "" + - name: "drawPath" + args: "path" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "drawPoint" + args: "" + desc: "" + desc_ru: "" + - name: "drawRGB" + args: "" + desc: "" + desc_ru: "" + - name: "drawRect" + args: "" + desc: "" + desc_ru: "" + - name: "drawRoundRect" + args: "" + desc: "" + desc_ru: "" + - name: "drawText" + args: "" + desc: "" + desc_ru: "" + - name: "drawTextOnPath" + args: "text, path, hOffset, vOffset" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "fillCircle" + args: "" + desc: "" + desc_ru: "" + - name: "fillOval" + args: "" + desc: "" + desc_ru: "" + - name: "fillRect" + args: "" + desc: "" + desc_ru: "" + - name: "fillRoundRect" + args: "" + desc: "" + desc_ru: "" + - name: "getAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "getClipBounds" + args: "" + desc: "" + desc_ru: "" + - name: "getColor" + args: "" + desc: "" + desc_ru: "" + - name: "getFillPath" + args: "src, dst" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "getDensity" + args: "" + desc: "" + desc_ru: "" + - name: "getFlags" + args: "" + desc: "" + desc_ru: "" + - name: "getFontSpacing" + args: "" + desc: "" + desc_ru: "" + - name: "getHeight" + args: "" + desc: "" + desc_ru: "" + - name: "getSaveCount" + args: "" + desc: "" + desc_ru: "" + - name: "getStrokeCap" + args: "" + desc: "" + desc_ru: "" + - name: "getStrokeJoin" + args: "" + desc: "" + desc_ru: "" + - name: "getStrokeMiter" + args: "" + desc: "" + desc_ru: "" + - name: "getStrokeWidth" + args: "" + desc: "" + desc_ru: "" + - name: "getStyle" + args: "" + desc: "" + desc_ru: "" + - name: "getTextAlign" + args: "" + desc: "" + desc_ru: "" + - name: "getTextBounds" + args: "" + desc: "" + desc_ru: "" + - name: "getTextScaleX" + args: "" + desc: "" + desc_ru: "" + - name: "getTextSize" + args: "" + desc: "" + desc_ru: "" + - name: "getTextSkewX" + args: "" + desc: "" + desc_ru: "" + - name: "getTextWidths" + args: "" + desc: "" + desc_ru: "" + - name: "getTypeface" + args: "" + desc: "" + desc_ru: "" + - name: "getWidth" + args: "" + desc: "" + desc_ru: "" + - name: "isAntiAlias" + args: "" + desc: "" + desc_ru: "" + - name: "isDither" + args: "" + desc: "" + desc_ru: "" + - name: "isFakeBoldText" + args: "" + desc: "" + desc_ru: "" + - name: "isFilterBitmap" + args: "" + desc: "" + desc_ru: "" + - name: "isLinearText" + args: "" + desc: "" + desc_ru: "" + - name: "isOpaque" + args: "" + desc: "" + desc_ru: "" + - name: "isStrikeThruText" + args: "" + desc: "" + desc_ru: "" + - name: "isSubpixelText" + args: "" + desc: "" + desc_ru: "" + - name: "isUnderlineText" + args: "" + desc: "" + desc_ru: "" + - name: "measureText" + args: "" + desc: "" + desc_ru: "" + - name: "quickReject" + args: "" + desc: "" + desc_ru: "" + - name: "reset" + args: "" + desc: "" + desc_ru: "" + - name: "restore" + args: "" + desc: "" + desc_ru: "" + - name: "restoreToCount" + args: "" + desc: "" + desc_ru: "" + - name: "rotate" + args: "" + desc: "" + desc_ru: "" + - name: "save" + args: "" + desc: "" + desc_ru: "" + - name: "saveLayer" + args: "" + desc: "" + desc_ru: "" + - name: "saveLayerAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "scale" + args: "" + desc: "" + desc_ru: "" + - name: "setAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "setAntiAlias" + args: "" + desc: "" + desc_ru: "" + - name: "setBitmap" + args: "" + desc: "" + desc_ru: "" + - name: "setColor" + args: "" + desc: "" + desc_ru: "" + - name: "setDensity" + args: "" + desc: "" + desc_ru: "" + - name: "setDither" + args: "" + desc: "" + desc_ru: "" + - name: "setFakeBoldText" + args: "" + desc: "" + desc_ru: "" + - name: "setFilterBitmap" + args: "" + desc: "" + desc_ru: "" + - name: "setFlags" + args: "" + desc: "" + desc_ru: "" + - name: "setLinearText" + args: "" + desc: "" + desc_ru: "" + - name: "setShader" + args: "shader" + desc: "" + desc_ru: "" + since: 1.5.1 + - name: "setShadowLayer" + args: "radius, dx, dy, shadowColor" + desc: "" + desc_ru: "" + - name: "setStrikeThruText" + args: "isEnabled" + desc: "" + desc_ru: "" + - name: "setStrokeCap" + args: "cap" + desc: "" + desc_ru: "" + - name: "setStrokeJoin" + args: "join" + desc: "" + desc_ru: "" + - name: "setStrokeMiter" + args: "miter" + desc: "" + desc_ru: "" + - name: "setStrokeWidth" + args: "" + desc: "" + desc_ru: "" + - name: "setStyle" + args: "" + desc: "" + desc_ru: "" + - name: "setSubpixelText" + args: "" + desc: "" + desc_ru: "" + - name: "setTextAlign" + args: "" + desc: "" + desc_ru: "" + - name: "setTextScaleX" + args: "" + desc: "" + desc_ru: "" + - name: "setTextSize" + args: "" + desc: "" + desc_ru: "" + - name: "setTextSkewX" + args: "" + desc: "" + desc_ru: "" + - name: "setTypeface" + args: "" + desc: "" + desc_ru: "" + - name: "setUnderlineText" + args: "" + desc: "" + desc_ru: "" + - name: "skew" + args: "" + desc: "" + desc_ru: "" + - name: "strokeCircle" + args: "" + desc: "" + desc_ru: "" + - name: "strokeOval" + args: "" + desc: "" + desc_ru: "" + - name: "strokeRect" + args: "" + desc: "" + desc_ru: "" + - name: "strokeRoundRect" + args: "" + desc: "" + desc_ru: "" + - name: "translate" + args: "" + desc: "" + desc_ru: "" \ No newline at end of file diff --git a/docs/src/modules/canvasfx.yml b/docs/src/modules/canvasfx.yml new file mode 100644 index 00000000..4e97a63d --- /dev/null +++ b/docs/src/modules/canvasfx.yml @@ -0,0 +1,417 @@ +name: canvasfx +scope: "desktop" +desc: "Contains functions for working with Java FX graphics" +desc_ru: "Содержит функции для работы с графикой Java FX" +constants: + - name: "ArcType" + typeName: map + type: 4 + value: "{OPEN=0, CHORD=1, ROUND=2}" + - name: "BlendMode" + typeName: map + type: 4 + value: "{SRC_OVER=0, SRC_ATOP=1, ADD=2, MULTIPLY=3, SCREEN=4, OVERLAY=5, DARKEN=6, LIGHTEN=7, COLOR_DODGE=8, COLOR_BURN=9, HARD_LIGHT=10, SOFT_LIGHT=11, DIFFERENCE=12, EXCLUSION=13, RED=14, GREEN=15, BLUE=16}" + - name: "Color" + typeName: map + type: 4 + value: "{hsb=def(hue,saturation,brightness,opacity=1.0), new=def(rgb) def(r,g,b,opacity=1.0), rgb=def(r,g,b,opacity=1.0), web=def(name,opacity=1.0, ALICEBLUE=ColorValue 0xf0f8ffff, ANTIQUEWHITE=ColorValue 0xfaebd7ff, AQUA=ColorValue 0x00ffffff, AQUAMARINE=ColorValue 0x7fffd4ff, AZURE=ColorValue 0xf0ffffff, BEIGE=ColorValue 0xf5f5dcff, BISQUE=ColorValue 0xffe4c4ff, BLACK=ColorValue 0x000000ff, BLANCHEDALMOND=ColorValue 0xffebcdff, BLUE=ColorValue 0x0000ffff, BLUEVIOLET=ColorValue 0x8a2be2ff, BROWN=ColorValue 0xa52a2aff, BURLYWOOD=ColorValue 0xdeb887ff, CADETBLUE=ColorValue 0x5f9ea0ff, CHARTREUSE=ColorValue 0x7fff00ff, CHOCOLATE=ColorValue 0xd2691eff, CORAL=ColorValue 0xff7f50ff, CORNFLOWERBLUE=ColorValue 0x6495edff, CORNSILK=ColorValue 0xfff8dcff, CRIMSON=ColorValue 0xdc143cff, CYAN=ColorValue 0x00ffffff, DARKBLUE=ColorValue 0x00008bff, DARKCYAN=ColorValue 0x008b8bff, DARKGOLDENROD=ColorValue 0xb8860bff, DARKGRAY=ColorValue 0xa9a9a9ff, DARKGREEN=ColorValue 0x006400ff, DARKGREY=ColorValue 0xa9a9a9ff, DARKKHAKI=ColorValue 0xbdb76bff, DARKMAGENTA=ColorValue 0x8b008bff, DARKOLIVEGREEN=ColorValue 0x556b2fff, DARKORANGE=ColorValue 0xff8c00ff, DARKORCHID=ColorValue 0x9932ccff, DARKRED=ColorValue 0x8b0000ff, DARKSALMON=ColorValue 0xe9967aff, DARKSEAGREEN=ColorValue 0x8fbc8fff, DARKSLATEBLUE=ColorValue 0x483d8bff, DARKSLATEGRAY=ColorValue 0x2f4f4fff, DARKSLATEGREY=ColorValue 0x2f4f4fff, DARKTURQUOISE=ColorValue 0x00ced1ff, DARKVIOLET=ColorValue 0x9400d3ff, DEEPPINK=ColorValue 0xff1493ff, DEEPSKYBLUE=ColorValue 0x00bfffff, DIMGRAY=ColorValue 0x696969ff, DIMGREY=ColorValue 0x696969ff, DODGERBLUE=ColorValue 0x1e90ffff, FIREBRICK=ColorValue 0xb22222ff, FLORALWHITE=ColorValue 0xfffaf0ff, FORESTGREEN=ColorValue 0x228b22ff, FUCHSIA=ColorValue 0xff00ffff, GAINSBORO=ColorValue 0xdcdcdcff, GHOSTWHITE=ColorValue 0xf8f8ffff, GOLD=ColorValue 0xffd700ff, GOLDENROD=ColorValue 0xdaa520ff, GRAY=ColorValue 0x808080ff, GREEN=ColorValue 0x008000ff, GREENYELLOW=ColorValue 0xadff2fff, GREY=ColorValue 0x808080ff, HONEYDEW=ColorValue 0xf0fff0ff, HOTPINK=ColorValue 0xff69b4ff, INDIANRED=ColorValue 0xcd5c5cff, INDIGO=ColorValue 0x4b0082ff, IVORY=ColorValue 0xfffff0ff, KHAKI=ColorValue 0xf0e68cff, LAVENDER=ColorValue 0xe6e6faff, LAVENDERBLUSH=ColorValue 0xfff0f5ff, LAWNGREEN=ColorValue 0x7cfc00ff, LEMONCHIFFON=ColorValue 0xfffacdff, LIGHTBLUE=ColorValue 0xadd8e6ff, LIGHTCORAL=ColorValue 0xf08080ff, LIGHTCYAN=ColorValue 0xe0ffffff, LIGHTGOLDENRODYELLOW=ColorValue 0xfafad2ff, LIGHTGRAY=ColorValue 0xd3d3d3ff, LIGHTGREEN=ColorValue 0x90ee90ff, LIGHTGREY=ColorValue 0xd3d3d3ff, LIGHTPINK=ColorValue 0xffb6c1ff, LIGHTSALMON=ColorValue 0xffa07aff, LIGHTSEAGREEN=ColorValue 0x20b2aaff, LIGHTSKYBLUE=ColorValue 0x87cefaff, LIGHTSLATEGRAY=ColorValue 0x778899ff, LIGHTSLATEGREY=ColorValue 0x778899ff, LIGHTSTEELBLUE=ColorValue 0xb0c4deff, LIGHTYELLOW=ColorValue 0xffffe0ff, LIME=ColorValue 0x00ff00ff, LIMEGREEN=ColorValue 0x32cd32ff, LINEN=ColorValue 0xfaf0e6ff, MAGENTA=ColorValue 0xff00ffff, MAROON=ColorValue 0x800000ff, MEDIUMAQUAMARINE=ColorValue 0x66cdaaff, MEDIUMBLUE=ColorValue 0x0000cdff, MEDIUMORCHID=ColorValue 0xba55d3ff, MEDIUMPURPLE=ColorValue 0x9370dbff, MEDIUMSEAGREEN=ColorValue 0x3cb371ff, MEDIUMSLATEBLUE=ColorValue 0x7b68eeff, MEDIUMSPRINGGREEN=ColorValue 0x00fa9aff, MEDIUMTURQUOISE=ColorValue 0x48d1ccff, MEDIUMVIOLETRED=ColorValue 0xc71585ff, MIDNIGHTBLUE=ColorValue 0x191970ff, MINTCREAM=ColorValue 0xf5fffaff, MISTYROSE=ColorValue 0xffe4e1ff, MOCCASIN=ColorValue 0xffe4b5ff, NAVAJOWHITE=ColorValue 0xffdeadff, NAVY=ColorValue 0x000080ff, OLDLACE=ColorValue 0xfdf5e6ff, OLIVE=ColorValue 0x808000ff, OLIVEDRAB=ColorValue 0x6b8e23ff, ORANGE=ColorValue 0xffa500ff, ORANGERED=ColorValue 0xff4500ff, ORCHID=ColorValue 0xda70d6ff, PALEGOLDENROD=ColorValue 0xeee8aaff, PALEGREEN=ColorValue 0x98fb98ff, PALETURQUOISE=ColorValue 0xafeeeeff, PALEVIOLETRED=ColorValue 0xdb7093ff, PAPAYAWHIP=ColorValue 0xffefd5ff, PEACHPUFF=ColorValue 0xffdab9ff, PERU=ColorValue 0xcd853fff, PINK=ColorValue 0xffc0cbff, PLUM=ColorValue 0xdda0ddff, POWDERBLUE=ColorValue 0xb0e0e6ff, PURPLE=ColorValue 0x800080ff, RED=ColorValue 0xff0000ff, ROSYBROWN=ColorValue 0xbc8f8fff, ROYALBLUE=ColorValue 0x4169e1ff, SADDLEBROWN=ColorValue 0x8b4513ff, SALMON=ColorValue 0xfa8072ff, SANDYBROWN=ColorValue 0xf4a460ff, SEAGREEN=ColorValue 0x2e8b57ff, SEASHELL=ColorValue 0xfff5eeff, SIENNA=ColorValue 0xa0522dff, SILVER=ColorValue 0xc0c0c0ff, SKYBLUE=ColorValue 0x87ceebff, SLATEBLUE=ColorValue 0x6a5acdff, SLATEGRAY=ColorValue 0x708090ff, SLATEGREY=ColorValue 0x708090ff, SNOW=ColorValue 0xfffafaff, SPRINGGREEN=ColorValue 0x00ff7fff, STEELBLUE=ColorValue 0x4682b4ff, TAN=ColorValue 0xd2b48cff, TEAL=ColorValue 0x008080ff, THISTLE=ColorValue 0xd8bfd8ff, TOMATO=ColorValue 0xff6347ff, TRANSPARENT=ColorValue 0x00000000, TURQUOISE=ColorValue 0x40e0d0ff, VIOLET=ColorValue 0xee82eeff, WHEAT=ColorValue 0xf5deb3ff, WHITE=ColorValue 0xffffffff, WHITESMOKE=ColorValue 0xf5f5f5ff, YELLOW=ColorValue 0xffff00ff, YELLOWGREEN=ColorValue 0x9acd32ff}" + - name: "Events" + typeName: map + type: 4 + value: "{DRAG_DETECTED=0, MOUSE_CLICKED=1, MOUSE_DRAGGED=2, MOUSE_ENTERED=3, MOUSE_ENTERED_TARGET=4, MOUSE_EXITED=5, MOUSE_EXITED_TARGET=6, MOUSE_MOVED=7, MOUSE_PRESSED=8, MOUSE_RELEASED=9, KEY_PRESSED=10, KEY_RELEASED=11, KEY_TYPED=12, SWIPE_DOWN=13, SWIPE_LEFT=14, SWIPE_RIGHT=15, SWIPE_UP=16}" + - name: "FillRule" + typeName: map + type: 4 + value: "{EVEN_ODD=0, NON_ZERO=1}" + - name: "KeyCode" + typeName: map + type: 4 + value: "{A=36, ACCEPT=158, ADD=76, AGAIN=180, ALL_CANDIDATES=168, ALPHANUMERIC=162, ALT=7, ALT_GRAPH=185, AMPERSAND=134, ASTERISK=135, AT=141, B=37, BACK_QUOTE=112, BACK_SLASH=63, BACK_SPACE=1, BEGIN=186, BRACELEFT=139, BRACERIGHT=140, C=38, CANCEL=3, CAPS=9, CHANNEL_DOWN=218, CHANNEL_UP=217, CIRCUMFLEX=143, CLEAR=4, CLOSE_BRACKET=64, CODE_INPUT=170, COLON=142, COLORED_KEY_0=206, COLORED_KEY_1=207, COLORED_KEY_2=208, COLORED_KEY_3=209, COMMA=20, COMMAND=222, COMPOSE=184, CONTEXT_MENU=154, CONTROL=6, CONVERT=156, COPY=177, CUT=176, D=39, DEAD_ABOVEDOT=124, DEAD_ABOVERING=126, DEAD_ACUTE=119, DEAD_BREVE=123, DEAD_CARON=128, DEAD_CEDILLA=129, DEAD_CIRCUMFLEX=120, DEAD_DIAERESIS=125, DEAD_DOUBLEACUTE=127, DEAD_GRAVE=118, DEAD_IOTA=131, DEAD_MACRON=122, DEAD_OGONEK=130, DEAD_SEMIVOICED_SOUND=133, DEAD_TILDE=121, DEAD_VOICED_SOUND=132, DECIMAL=79, DELETE=81, DIGIT0=24, DIGIT1=25, DIGIT2=26, DIGIT3=27, DIGIT4=28, DIGIT5=29, DIGIT6=30, DIGIT7=31, DIGIT8=32, DIGIT9=33, DIVIDE=80, DOLLAR=144, DOWN=19, E=40, EJECT_TOGGLE=210, END=14, ENTER=0, EQUALS=35, ESCAPE=10, EURO_SIGN=145, EXCLAMATION_MARK=146, F=41, F1=84, F10=93, F11=94, F12=95, F13=96, F14=97, F15=98, F16=99, F17=100, F18=101, F19=102, F2=85, F20=103, F21=104, F22=105, F23=106, F24=107, F3=86, F4=87, F5=88, F6=89, F7=90, F8=91, F9=92, FAST_FWD=213, FINAL=155, FIND=181, FULL_WIDTH=165, G=42, GAME_A=198, GAME_B=199, GAME_C=200, GAME_D=201, GREATER=138, H=43, HALF_WIDTH=166, HELP=110, HIRAGANA=164, HOME=15, I=44, INFO=205, INPUT_METHOD_ON_OFF=175, INSERT=109, INVERTED_EXCLAMATION_MARK=147, J=45, JAPANESE_HIRAGANA=172, JAPANESE_KATAKANA=171, JAPANESE_ROMAN=173, K=46, KANA=160, KANA_LOCK=174, KANJI=161, KATAKANA=163, KP_DOWN=115, KP_LEFT=116, KP_RIGHT=117, KP_UP=114, L=47, LEFT=16, LEFT_PARENTHESIS=148, LESS=137, M=48, META=111, MINUS=21, MODECHANGE=159, MULTIPLY=75, MUTE=221, N=49, NONCONVERT=157, NUMBER_SIGN=149, NUMPAD0=65, NUMPAD1=66, NUMPAD2=67, NUMPAD3=68, NUMPAD4=69, NUMPAD5=70, NUMPAD6=71, NUMPAD7=72, NUMPAD8=73, NUMPAD9=74, NUM_LOCK=82, O=50, OPEN_BRACKET=62, P=51, PAGE_DOWN=13, PAGE_UP=12, PASTE=178, PAUSE=8, PERIOD=22, PLAY=211, PLUS=150, POUND=203, POWER=204, PREVIOUS_CANDIDATE=169, PRINTSCREEN=108, PROPS=182, Q=52, QUOTE=113, QUOTEDBL=136, R=53, RECORD=212, REWIND=214, RIGHT=18, RIGHT_PARENTHESIS=151, ROMAN_CHARACTERS=167, S=54, SCROLL_LOCK=83, SEMICOLON=34, SEPARATOR=77, SHIFT=5, SHORTCUT=223, SLASH=23, SOFTKEY_0=188, SOFTKEY_1=189, SOFTKEY_2=190, SOFTKEY_3=191, SOFTKEY_4=192, SOFTKEY_5=193, SOFTKEY_6=194, SOFTKEY_7=195, SOFTKEY_8=196, SOFTKEY_9=197, SPACE=11, STAR=202, STOP=183, SUBTRACT=78, T=55, TAB=2, TRACK_NEXT=216, TRACK_PREV=215, U=56, UNDEFINED=187, UNDERSCORE=152, UNDO=179, UP=17, V=57, VOLUME_DOWN=220, VOLUME_UP=219, W=58, WINDOWS=153, X=59, Y=60, Z=61}" + - name: "MouseButton" + typeName: map + type: 4 + value: "{NONE=0, PRIMARY=1, MIDDLE=2, SECONDARY=3}" + - name: "StrokeLineCap" + typeName: map + type: 4 + value: "{SQUARE=0, BUTT=1, ROUND=2}" + - name: "StrokeLineJoin" + typeName: map + type: 4 + value: "{MITER=0, BEVEL=1, ROUND=2}" + - name: "TextAlignment" + typeName: map + type: 4 + value: "{LEFT=0, CENTER=1, RIGHT=2, JUSTIFY=3}" + - name: "VPos" + typeName: map + type: 4 + value: "{TOP=0, CENTER=1, BASELINE=2, BOTTOM=3}" +functions: + - name: "BlendEffect" + args: "" + desc: "" + desc_ru: "" + - name: "BloomEffect" + args: "" + desc: "" + desc_ru: "" + - name: "BoxBlurEffect" + args: "" + desc: "" + desc_ru: "" + - name: "ColorAdjustEffect" + args: "" + desc: "" + desc_ru: "" + - name: "ColorInputEffect" + args: "" + desc: "" + desc_ru: "" + - name: "DropShadowEffect" + args: "" + desc: "" + desc_ru: "" + - name: "GaussianBlurEffect" + args: "" + desc: "" + desc_ru: "" + - name: "GlowEffect" + args: "" + desc: "" + desc_ru: "" + - name: "InnerShadowEffect" + args: "" + desc: "" + desc_ru: "" + - name: "LightingEffect" + args: "" + desc: "" + desc_ru: "" + - name: "MotionBlurEffect" + args: "" + desc: "" + desc_ru: "" + - name: "PerspectiveTransformEffect" + args: "" + desc: "" + desc_ru: "" + - name: "ReflectionEffect" + args: "" + desc: "" + desc_ru: "" + - name: "SepiaToneEffect" + args: "" + desc: "" + desc_ru: "" + - name: "ShadowEffect" + args: "" + desc: "" + desc_ru: "" + - name: "addEventFilter" + args: "" + desc: "" + desc_ru: "" + - name: "addEventHandler" + args: "" + desc: "" + desc_ru: "" + - name: "createImage" + args: "..." + desc: |- + `createImage(url)` - creates an image from url. + + `createImage(w, h)` - creates an image with the given size. + + `createBitmap(w, h, pixels)` - creates an image from pixels array. + + Returns ImageFXValue. + desc_ru: |- + `createImage(url)` - создаёт изображение из пути к ресурсу. + + `createImage(w, h)` - создаёт новое изображение с заданным размером. + + `createBitmap(w, h, pixels)` - создаёт изображение из массива пикселей. + + Возвращает ImageFXValue. + example: |- + use canvasfx + + g = showcanvas() + url = "http://lorempixel.com/640/480/nature" + bitmap = createImage(url) + g.drawBitmap(bitmap, 0, 0) + bitmap = createImage("file:image.png") + g.drawBitmap(bitmap, 200, 0) + - name: "repaint" + args: "" + desc: "" + desc_ru: "" + - name: "window" + args: "" + desc: "" + desc_ru: "" +types: + - name: "ColorValue" + - name: "EffectValue" + - name: "GraphicsFXValue" + functions: + - name: "appendSVGPath" + args: "" + desc: "" + desc_ru: "" + - name: "applyEffect" + args: "" + desc: "" + desc_ru: "" + - name: "arc" + args: "" + desc: "" + desc_ru: "" + - name: "arcTo" + args: "" + desc: "" + desc_ru: "" + - name: "beginPath" + args: "" + desc: "" + desc_ru: "" + - name: "bezierCurveTo" + args: "" + desc: "" + desc_ru: "" + - name: "clearRect" + args: "" + desc: "" + desc_ru: "" + - name: "clip" + args: "" + desc: "" + desc_ru: "" + - name: "closePath" + args: "" + desc: "" + desc_ru: "" + - name: "fill" + args: "" + desc: "" + desc_ru: "" + - name: "fillArc" + args: "" + desc: "" + desc_ru: "" + - name: "fillOval" + args: "" + desc: "" + desc_ru: "" + - name: "fillPolygon" + args: "" + desc: "" + desc_ru: "" + - name: "fillRect" + args: "" + desc: "" + desc_ru: "" + - name: "fillRoundRect" + args: "" + desc: "" + desc_ru: "" + - name: "fillText" + args: "" + desc: "" + desc_ru: "" + - name: "getFill" + args: "" + desc: "" + desc_ru: "" + - name: "getFillRule" + args: "" + desc: "" + desc_ru: "" + - name: "getGlobalAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "getGlobalBlendMode" + args: "" + desc: "" + desc_ru: "" + - name: "getLineCap" + args: "" + desc: "" + desc_ru: "" + - name: "getLineJoin" + args: "" + desc: "" + desc_ru: "" + - name: "getLineWidth" + args: "" + desc: "" + desc_ru: "" + - name: "getMiterLimit" + args: "" + desc: "" + desc_ru: "" + - name: "getStroke" + args: "" + desc: "" + desc_ru: "" + - name: "getTextAlign" + args: "" + desc: "" + desc_ru: "" + - name: "getTextBaseline" + args: "" + desc: "" + desc_ru: "" + - name: "isPointInPath" + args: "" + desc: "" + desc_ru: "" + - name: "lineTo" + args: "" + desc: "" + desc_ru: "" + - name: "moveTo" + args: "" + desc: "" + desc_ru: "" + - name: "quadraticCurveTo" + args: "" + desc: "" + desc_ru: "" + - name: "rect" + args: "" + desc: "" + desc_ru: "" + - name: "restore" + args: "" + desc: "" + desc_ru: "" + - name: "rotate" + args: "" + desc: "" + desc_ru: "" + - name: "save" + args: "" + desc: "" + desc_ru: "" + - name: "scale" + args: "" + desc: "" + desc_ru: "" + - name: "setEffect" + args: "" + desc: "" + desc_ru: "" + - name: "setFill" + args: "" + desc: "" + desc_ru: "" + - name: "setFillRule" + args: "" + desc: "" + desc_ru: "" + - name: "setGlobalAlpha" + args: "" + desc: "" + desc_ru: "" + - name: "setGlobalBlendMode" + args: "" + desc: "" + desc_ru: "" + - name: "setLineCap" + args: "" + desc: "" + desc_ru: "" + - name: "setLineJoin" + args: "" + desc: "" + desc_ru: "" + - name: "setLineWidth" + args: "" + desc: "" + desc_ru: "" + - name: "setMiterLimit" + args: "" + desc: "" + desc_ru: "" + - name: "setStroke" + args: "" + desc: "" + desc_ru: "" + - name: "setTextAlign" + args: "" + desc: "" + desc_ru: "" + - name: "setTextBaseline" + args: "" + desc: "" + desc_ru: "" + - name: "stroke" + args: "" + desc: "" + desc_ru: "" + - name: "strokeArc" + args: "" + desc: "" + desc_ru: "" + - name: "strokeLine" + args: "x1, y1, x2, y2" + desc: "" + desc_ru: "" + - name: "strokeOval" + args: "" + desc: "" + desc_ru: "" + - name: "strokePolygon" + args: "" + desc: "" + desc_ru: "" + - name: "strokePolyline" + args: "" + desc: "" + desc_ru: "" + - name: "strokeRect" + args: "" + desc: "" + desc_ru: "" + - name: "strokeRoundRect" + args: "" + desc: "" + desc_ru: "" + - name: "strokeText" + args: "" + desc: "" + desc_ru: "" + - name: "transform" + args: "" + desc: "" + desc_ru: "" + - name: "translate" + args: "" + desc: "" + desc_ru: "" + - name: "ImageFXValue" + constants: + - name: "width" + typeName: number + type: 1 + value: "/*width of the image*/" + - name: "height" + typeName: number + type: 1 + value: "/*height of the image*/" + - name: "preserveRatio" + typeName: number + type: 1 + value: "/*is preserve ratio*/" + - name: "smooth" + typeName: number + type: 1 + value: "/*is smooth*/" + functions: + - name: "getPixels" + args: "" + desc: "returns pixels array of the image" + desc_ru: "возвращает массив пикселей изображения" \ No newline at end of file diff --git a/docs/src/modules/date.yml b/docs/src/modules/date.yml new file mode 100644 index 00000000..7614ffc5 --- /dev/null +++ b/docs/src/modules/date.yml @@ -0,0 +1,107 @@ +name: date +scope: "both" +desc: "Contains functions for working with date and time" +desc_ru: "Содержит функции для работы с датой и временем" +constants: + - name: "STYLE_FULL" + typeName: number + type: 1 + value: "0" + - name: "STYLE_LONG" + typeName: number + type: 1 + value: "1" + - name: "STYLE_MEDIUM" + typeName: number + type: 1 + value: "2" + - name: "STYLE_SHORT" + typeName: number + type: 1 + value: "3" +functions: + - name: "newDate" + args: "..." + desc: |- + `newDate()` - returns current date. + + `newDate(timestamp)` - returns date by given timestamp. + + `newDate(dateString)` - parses and returns date by given string. + + `newDate(pattern, dateString)` - parses and returns date by given string in `pattern` format. + + `newDate(year, month, day)` - returns date by year, month and day. + + `newDate(year, month, day, hour, minute)` - returns date by year, month, day, hour and minute. + + `newDate(year, month, day, hour, minute, second)` - returns date by year, month, day, hour, minute and second. + + Returns DateValue. + desc_ru: |- + `newDate()` - возвращает текущую дату. + + `newDate(timestamp)` - возвращает дату для указанной метки времени. + + `newDate(dateString)` - парсит и возвращает дату, записанную в виде строки. + + `newDate(pattern, dateString)` - парсит и возвращает дату, записанную в виде строки в формате `pattern`. + + `newDate(year, month, day)` - возвращает дату для указанных года, месяца и дня. + + `newDate(year, month, day, hour, minute)` - возвращает дату для указанных года, месяца, дня, часа и минуты. + + `newDate(year, month, day, hour, minute, second)` - возвращает дату для указанных года, месяца, дня, часа, минуты и секунды. + + Возвращает DateValue. + - name: "newFormat" + args: "..." + desc: |- + `newFormat()` - returns default date format. + + `newFormat(pattern)` - returns date format by given pattern. + + `newFormat(type)` - returns format: 0 - default, 1 - date, 2 - time, 3 - date and time. + + `newFormat(pattern, locale)` - returns date format by given pattern and locale. + + `newFormat(type, style)` - returns format: 0 - default, 1 - date, 2 - time, 3 - date and time. `style`: 0 - full, 1 - long, 2 - medium, 3 - short. + + Returns DateFormatValue. + desc_ru: |- + `newFormat()` - возвращает формат даты по умолчанию. + + `newFormat(pattern)` - возвращает формат с указанным шаблоном. + + `newFormat(type)` - возвращает формат: 0 - по умолчанию, 1 - для даты, 2 - для времени, 3 - для времени и даты. + + `newFormat(pattern, locale)` - возвращает формат для указанного шаблона в заданной локализации. + + `newFormat(type, style)` - возвращает формат: 0 - по умолчанию, 1 - для даты, 2 - для времени, 3 - для времени и даты. `style`: 0 - полный, 1 - длинный, 2 - средний, 3 - короткий. + + Возвращает DateFormatValue. + - name: "formatDate" + args: "date, format = default" + desc: formats date by given format and returns string + desc_ru: форматирует дату в указанном формате и возвращает строку + example: |- + use date + + d = newDate(2016, 4, 8) + println formatDate(d, newFormat("yyyy/MM/dd")) // "2016/05/08" + - name: "parseDate" + args: "dateString, format = default" + desc: parses date from string by given pattern. Returns DateValue + desc_ru: парсит дату из строки в указанном шаблоне. Возвращает DateValue + example: |- + use date + + println parseDate("2016/05/08", newFormat("yyyy/MM/dd")) + - name: "toTimestamp" + args: "date" + desc: returns timestamp in milliseconds + desc_ru: возвращает время в миллисекундах +types: + - name: "DateValue" + value: "year, month, day, hour, minute, second, millisecond" + - name: "DateFormatValue" \ No newline at end of file diff --git a/docs/src/modules/downloader.yml b/docs/src/modules/downloader.yml new file mode 100644 index 00000000..64410d3b --- /dev/null +++ b/docs/src/modules/downloader.yml @@ -0,0 +1,24 @@ +name: downloader +scope: both +desc: "Contains functions for downloading large files" +desc_ru: "Содержит функции для скачивания больших файлов" +functions: + - name: getContentLength + args: 'url' + desc: 'gets content length by sending HEAD request to the given url' + desc_ru: 'получает значение заголовка Content-Length путём отправки HEAD-запроса на указанный url' + - name: downloader + args: 'downloadUrl, filePath, progressCallback = def() {}, bufferSize = 16384' + desc: 'downloads file from `downloadUrl` to `filePath`' + desc_ru: 'скачивает файл по адресу `downloadUrl` и сохраняет в `filePath`' + example: |- + use downloader, std + + MBYTES = 1048576.0 // 1024*1024 + url = "http://www.ovh.net/files/10Mb.dat" + file = "10Mb.dat" + + downloader(url, file, def(progress, bytesDownloaded, bytesMax) { + bar = "#" * (progress / 2) + print sprintf("%-50s %d%% %.2f / %.2f MiB\\r", bar, progress, bytesDownloaded / MBYTES, bytesMax / MBYTES) + }) \ No newline at end of file diff --git a/docs/src/modules/files.yml b/docs/src/modules/files.yml new file mode 100644 index 00000000..684e9d8d --- /dev/null +++ b/docs/src/modules/files.yml @@ -0,0 +1,288 @@ +name: files +scope: "both" +desc: "Contains functions for working with files" +desc_ru: "Содержит функции для работы с файлами" +constants: + - name: "FILES_COMPARATOR" + typeName: "function" + scope: "both" + type: 5 + value: "def(f1, f2) = compare(f1, f2)" + desc: "function which compares two file descriptors" + desc_ru: "функция, которая сравнивает два файловых дескриптора" + - name: "SDCARD" + typeName: string + scope: "android" + type: 2 + value: "path to SDCARD" + desc: "path to SDCARD" + desc_ru: "путь к внешнему хранилищу" +functions: + - name: "canExecute" + args: "f" + desc: "checks execute permission of the descriptor `f`" + desc_ru: "проверяет права на выполнение дескриптора `f`" + - name: "canRead" + args: "f" + desc: "checks read permission of the descriptor `f`" + desc_ru: "проверяет права на чтение дескриптора `f`" + - name: "canWrite" + args: "f" + desc: "checks write permission of the descriptor `f`" + desc_ru: "проверяет права на запись дескриптора `f`" + - name: copy + args: 'src, dst' + desc: 'copies file src to dst location' + desc_ru: 'копирует файл src в dst' + - name: "delete" + args: "f" + desc: "removes file or directory. Returns 1 if delete was successfull, 0 otherwise" + desc_ru: "удаляет файл или папку. Возвращает 1, если удаление прошло успешно, иначе - 0" + - name: "exists" + args: "f" + desc: "checks file or directory existing. Returns 1 if exists, 0 otherwise" + desc_ru: "проверяет, существует ли файл или папка. Возвращает 1, если существует, иначе - 0" + - name: "fclose" + args: "f" + desc: "closes file" + desc_ru: "закрывает файл" + - name: "fileSize" + args: "f" + desc: "returns file size in bytes" + desc_ru: "возвращает размер файла в байтах" + - name: "flush" + args: "f" + desc: "flushes write buffer into file" + desc_ru: "сбрасывает буфер записи в файл" + - name: "fopen" + args: "path, mode = \"r\"" + desc: |- + opens file файл with `path` in given `mode`: + + - "" - opens file or directory for getting info; + - "r" - opens file for read in text mode; + - "rb" - opens file for read in binary mode; + - "w" - opens file for write in text mode; + - "w+" - opens file for append in text mode; + - "wb" - opens file for write in binary mode; + - "wb+" - opens file for append in binary mode. + + Returns a file descriptor for using in other functions. + desc_ru: |- + открывает файл по пути `path` в заданном режиме `mode`: + + - "" - открывает файл или папку для получения информации; + - "r" - открывает файл для чтения в текстовом режиме; + - "rb" - открывает файл для чтения в бинарном режиме; + - "w" - открывает файл для записи в текстовом режиме; + - "w+" - открывает файл для дозаписи в текстовом режиме; + - "wb" - открывает файл для записи в бинарном режиме; + - "wb+" - открывает файл для дозаписи в бинарном режиме. + + Возвращает дескриптор файла, который необходим для остальных функций. + example: |- + use files + + f1 = fopen("text.txt") // opens file text.txt for read in text mode + f2 = fopen("E:/1.dat", "rbwb") // opens file 1.dat on drive E for binary read and write" + example_ru: |- + use files + + f1 = fopen("text.txt") // открывает файл text.txt для текстового чтения + f2 = fopen("E:/1.dat", "rbwb") // открывает файл 1.dat на диске E для бинарного чтения и записи" + - name: "getParent" + args: "f" + desc: "returns parent path of the given descriptor `f`" + desc_ru: "возвращает родительский путь для заданного дескриптора `f`" + - name: "isDirectory" + args: "f" + desc: "checks if descriptor `f` is directory" + desc_ru: "проверяет, является ли дескриптор `f` папкой. 1 - является, 0 - нет" + - name: "isFile" + args: "f" + desc: "checks if descriptor `f` is file" + desc_ru: "проверяет, является ли дескриптор f файлом. 1 - является, 0 - нет" + - name: "isHidden" + args: "f" + desc: "checks if descriptor `f` is hidden" + desc_ru: "проверяет, скрыт ли дескриптор f. 1 - скрыт, 0 - нет" + - name: "lastModified" + args: "f" + desc: "returns last modification time" + desc_ru: "возвращает время последнего изменения" + - name: "listFiles" + args: "f" + desc: "returns array with filenames in given directory.\n\n f - directory descriptor" + desc_ru: "возвращает массив с именами файлов в указанной директории.\n\n f - дескриптор папки" + example: |- + use files + + f1 = fopen("E:/examples", "") // opens directory examples for getting information + list = listFiles(f1) // gets array with filenames in directory + example_ru: |- + use files + + f1 = fopen("E:/examples", "") // открыть папку examples для получения информации + list = listFiles(f1) // получить массив с именами файлов в этой папке + - name: "mkdir" + args: "f" + desc: "creates the directory. Returns 1 if operation was successfull, 0 otherwise" + desc_ru: "создаёт папку. Возвращает 1, если создание прошло успешно, иначе - 0" + - name: "mkdirs" + args: "f" + desc: "creates the directories. Returns 1 if operation was successfull, 0 otherwise" + desc_ru: "создаёт папки. Возвращает 1, если создание прошло успешно, иначе - 0" + - name: "readAllBytes" + args: "f" + desc: "reads all bytes from file. Returns array with bytes" + desc_ru: "чтение всех байт файла. Возвращает массив байт файла" + example: |- + use std, files + + f1 = fopen("file.bin", "rb") + array = readAllBytes(f1) + println length(array) + - name: "readBoolean" + args: "f" + desc: "reads boolean (1 byte). Returns 0 if byte was 0, 1 otherwise" + desc_ru: "чтение boolean-значения (1 байт). Возвращает 0, если байт имеет значение 0, 1 - если значение не равно 0" + - name: "readByte" + args: "f" + desc: "reads one byte" + desc_ru: "чтение одного байта" + - name: "readBytes" + args: "f, array, offset = 0, length = length(array)" + desc: "reads `length` bytes of file `f` and stores to `array` starting from `offset+1` byte. Returns number of read bytes" + desc_ru: "чтение заданного количества байт в массив `array`. Возвращает число прочитанных байт. \nЕсли offset и length не указаны, то читается количество байт равное длине массива. \nЕсли offset и length указаны, то читается length байт в массив array, начиная с `offset+1` байта" + example: |- + use files + + f1 = fopen("file.bin", "rb") // file.bin must contain more than 5000 bytes + array = newarray(2048) + readCount = readBytes(f1, array) // reads 2048 bytes + readCount = readBytes(f1, array, 10) // reads 2048 bytes starting from 11 byte + readCount = readBytes(f1, array, 20, 10) // reads 10 bytes, starting from 21 byte + example_ru: |- + use files + + f1 = fopen("file.bin", "rb") // file.bin должен иметь больше 5000 байтов + array = newarray(2048) + readCount = readBytes(f1, array) // читает 2048 байт из файла + readCount = readBytes(f1, array, 10) // читает 2048 байт, начиная с 11 байта + readCount = readBytes(f1, array, 20, 10) // читает 10 байт, начиная с 21 байта + - name: "readChar" + args: "f" + desc: "reads one char (2 bytes). Returns number char's code" + desc_ru: "чтение одного символа (2 байта). Возвращает число - код символа" + - name: "readDouble" + args: "f" + desc: "reads 8 bytes double number" + desc_ru: "чтение 8 байт (вещественное число двойной точности)" + - name: "readFloat" + args: "f" + desc: "reads 4 bytes float number" + desc_ru: "чтение 4 байт (вещественное число)" + - name: "readInt" + args: "f" + desc: "reads 4 bytes integer number" + desc_ru: "чтение 4 байт (целое число)" + - name: "readLine" + args: "f" + desc: "reads line from file opened in text mode" + desc_ru: "чтение строки в текстовом режиме" + - name: "readLong" + args: "f" + desc: "reads 8 bytes long number" + desc_ru: "чтение 8 байт (длинное целое число)" + - name: "readShort" + args: "f" + desc: "reads 2 bytes short number" + desc_ru: "чтение 2 байт (короткое целое число)" + - name: "readText" + args: "f" + desc: "reads all file's content as string" + desc_ru: "чтение всего файла в текстовом режиме в строку" + - name: "readUTF" + args: "f" + desc: "reads string in binary mode" + desc_ru: "чтение строки в бинарном режиме" + - name: "rename" + args: "from, to" + desc: "renames (or moves) file" + desc_ru: "переименование (или перемещение) файла" + example: |- + use files + + f1 = fopen("C:/file1", "i") + f2 = fopen("E:/file2", "i") + rename(f1, f2) + fclose(f1) + fclose(f2) + - name: "setLastModified" + args: "f, time" + desc: "sets last modified time" + desc_ru: "устанавливает время изменения" + - name: "setReadOnly" + args: "f" + desc: "marks descriptor read only" + desc_ru: "помечает дескриптор только для чтения" + - name: "setExecutable" + args: "f, executable, ownerOnly = true" + desc: "sets execute permission" + desc_ru: "устанавливает права на выполнение" + - name: "setReadable" + args: "f, readable, ownerOnly = true" + desc: "sets read permission" + desc_ru: "устанавливает права на чтение" + - name: "setWritable" + args: "f, writable, ownerOnly = true" + desc: "sets write permission" + desc_ru: "устанавливает права на запись" + - name: "writeBoolean" + args: "f, v" + desc: "writes boolean (0 or 1) to file" + desc_ru: "запись одного байта boolean (0 или 1) в файл" + - name: "writeByte" + args: "f, v" + desc: "writes one byte to file" + desc_ru: "запись одного байта в файл" + - name: "writeBytes" + args: "f, array, offset = 0, length = length(array)" + desc: "writes `length` bytes to file `f` from byte `array` starting from `offset`" + desc_ru: "запись заданного количества байт в файл `f` из массива байт `array`. \nЕсли offset и length не указаны, то записывается количество байт равное длине массива. \nЕсли offset и length указаны, то пропускается offset байт и записывается length байт" + - name: "writeChar" + args: "f, v" + desc: "writes one char (2 bytes) to file. `v` can be number - writes number, or string - writes code of first symbol" + desc_ru: "запись одного символа (2 байта) в файл. `v` может быть как числом (пишется это число), так и строкой (пишется код первого символа)" + - name: "writeDouble" + args: "f, v" + desc: "writes 8 bytes double number to file" + desc_ru: "запись 8 байт (вещественное число двойной точности)" + - name: "writeFloat" + args: "f, v" + desc: "writes 4 bytes float number to file" + desc_ru: "запись 4 байт (вещественное число)" + - name: "writeInt" + args: "f, v" + desc: "writes 4 bytes integer number to file" + desc_ru: "запись 4 байт (целое число)" + - name: "writeLine" + args: "f, v" + desc: "writes string to file in text mode **adds line break at the end of the string**" + desc_ru: "запись строки в текстовом режиме **Добавляет в конец символ переноса строки**" + - name: "writeLong" + args: "f, v" + desc: "writes 8 bytes long number to file" + desc_ru: "запись 8 байт (длинное целое число)" + - name: "writeShort" + args: "f, v" + desc: "writes 2 bytes short number to file" + desc_ru: "запись двух байт (короткое целое число)" + - name: "writeText" + args: "f, v" + desc: "writes string to file in text mode. Unlike `writeLine` does not add line break" + desc_ru: "запись всего текста в текстовом режиме. В отличие от `writeLine`, не добавляет символ переноса строки" + - name: "writeUTF" + args: "f, v" + desc: "writes string to file in binary mode" + desc_ru: "запись строки в бинарном режиме" \ No newline at end of file diff --git a/docs/src/modules/forms.yml b/docs/src/modules/forms.yml new file mode 100644 index 00000000..cc6d6640 --- /dev/null +++ b/docs/src/modules/forms.yml @@ -0,0 +1,89 @@ +name: forms +scope: desktop +desc: "Contains functions for working with forms" +desc_ru: "Содержит функции для работы с формами" +constants: + - name: BorderLayout + type: 4 + typeName: map + value: '{AFTER_LINE_ENDS=After, LINE_END=After, LINE_START=Before, BEFORE_LINE_BEGINS=Before, CENTER=Center, EAST=East, BEFORE_FIRST_LINE=First, PAGE_START=First, AFTER_LAST_LINE=Last, PAGE_END=Last, NORTH=North, SOUTH=South, WEST=West}' + - name: BoxLayout + type: 4 + typeName: map + value: '{X_AXIS=0, Y_AXIS=1, LINE_AXIS=2, PAGE_AXIS=3}' + - name: DISPOSE_ON_CLOSE + type: 1 + typeName: number + value: '2' + - name: DO_NOTHING_ON_CLOSE + type: 1 + typeName: number + value: '0' + - name: EXIT_ON_CLOSE + type: 1 + typeName: number + value: '3' + - name: HIDE_ON_CLOSE + type: 1 + typeName: number + value: '1' + - name: SwingConstants + type: 4 + typeName: map + value: '{BOTTOM=3, CENTER=0, EAST=3, HORIZONTAL=0, LEADING=10, LEFT=2, NEXT=12, NORTH=1, NORTH_EAST=2, NORTH_WEST=8, PREVIOUS=13, RIGHT=4, SOUTH=5, SOUTH_EAST=4, SOUTH_WEST=6, TOP=1, TRAILING=11, VERTICAL=1, WEST=7}' +functions: + - name: borderLayout + args: 'hgap = 0, vgap = 0' + desc: 'creates BorderLayout' + desc_ru: 'создаёт BorderLayout' + - name: boxLayout + args: 'panel, axis = BoxLayout.PAGE_AXIS' + desc: 'creates BoxLayout' + desc_ru: 'создаёт BoxLayout' + - name: cardLayout + args: 'hgap = 0, vgap = 0' + desc: 'creates CardLayout' + desc_ru: 'создаёт CardLayout' + - name: flowLayout + args: 'align = FlowLayout.CENTER, hgap = 5, vgap = 5' + desc: 'creates FlowLayout' + desc_ru: 'создаёт FlowLayout' + - name: gridLayout + args: 'rows = 1, cols = 0, hgap = 0, vgap = 0' + desc: 'creates GridLayout' + desc_ru: 'создаёт GridLayout' + - name: newButton + args: 'text = ""' + desc: 'creates new button' + desc_ru: 'создаёт новую кнопку' + - name: newLabel + args: 'text = "", align = SwingConstants.LEADING' + desc: 'creates new label' + desc_ru: 'создаёт новую текстовую метку' + - name: newPanel + args: 'layoutManager = ...' + desc: 'creates new panel with optional layout manager' + desc_ru: 'создаёт новую панель с опциональным LayoutManager' + - name: newProgressBar + args: 'isVertical = false, min = 0, max = 100' + desc: 'creates new progress bar' + desc_ru: 'создаёт новый прогрессбар' + since: 1.5.0 + - name: newScrollPane + args: 'view, verticalPolicy = AS_NEEDED, horizontalPolicy = AS_NEEDED' + desc: 'creates new scroll pane' + desc_ru: 'создаёт новую область прокрутки' + since: 1.5.0 + - name: newTextArea + args: 'text = ""' + desc: 'creates new text area' + desc_ru: 'создаёт новую область ввода' + since: 1.5.0 + - name: newTextField + args: 'text = "", rows = 0, cols = 0' + desc: 'creates new text field' + desc_ru: 'создаёт новое поле ввода' + - name: newWindow + args: 'title = ""' + desc: 'creates new window and returns JFrameValue' + desc_ru: 'создаёт новое окно и возвращает JFrameValue' \ No newline at end of file diff --git a/docs/src/modules/forms_android.yml b/docs/src/modules/forms_android.yml new file mode 100644 index 00000000..a5f3385a --- /dev/null +++ b/docs/src/modules/forms_android.yml @@ -0,0 +1,2015 @@ +name: forms +scope: android +desc: "Contains functions for working with forms" +desc_ru: "Содержит функции для работы с формами" +constants: + - name: Gravity + type: 4 + typeName: map + value: '{NONE=0, NO_GRAVITY=0, CENTER_HORIZONTAL=1, LEFT=3, RIGHT=5, FILL_HORIZONTAL=7, CLIP_HORIZONTAL=8, CENTER_VERTICAL=16, CENTER=17, TOP=48, BOTTOM=80, FILL_VERTICAL=112, FILL=119, CLIP_VERTICAL=128}' + - name: InputType + type: 4 + typeName: map + value: '{TYPE_CLASS_DATETIME=4, TYPE_CLASS_NUMBER=2, TYPE_CLASS_PHONE=3, TYPE_CLASS_TEXT=1, TYPE_DATETIME_VARIATION_DATE=16, TYPE_DATETIME_VARIATION_NORMAL=0, TYPE_DATETIME_VARIATION_TIME=32, TYPE_MASK_CLASS=15, TYPE_MASK_FLAGS=16773120, TYPE_MASK_VARIATION=4080, TYPE_NULL=0, TYPE_NUMBER_FLAG_DECIMAL=8192, TYPE_NUMBER_FLAG_SIGNED=4096, TYPE_NUMBER_VARIATION_NORMAL=0, TYPE_NUMBER_VARIATION_PASSWORD=16, TYPE_TEXT_FLAG_AUTO_COMPLETE=65536, TYPE_TEXT_FLAG_AUTO_CORRECT=32768, TYPE_TEXT_FLAG_CAP_CHARACTERS=4096, TYPE_TEXT_FLAG_CAP_SENTENCES=16384, TYPE_TEXT_FLAG_CAP_WORDS=8192, TYPE_TEXT_FLAG_IME_MULTI_LINE=262144, TYPE_TEXT_FLAG_MULTI_LINE=131072, TYPE_TEXT_FLAG_NO_SUGGESTIONS=524288, TYPE_TEXT_VARIATION_EMAIL_ADDRESS=32, TYPE_TEXT_VARIATION_EMAIL_SUBJECT=48, TYPE_TEXT_VARIATION_FILTER=176, TYPE_TEXT_VARIATION_LONG_MESSAGE=80, TYPE_TEXT_VARIATION_NORMAL=0, TYPE_TEXT_VARIATION_PASSWORD=128, TYPE_TEXT_VARIATION_PERSON_NAME=96, TYPE_TEXT_VARIATION_PHONETIC=192, TYPE_TEXT_VARIATION_POSTAL_ADDRESS=112, TYPE_TEXT_VARIATION_SHORT_MESSAGE=64, TYPE_TEXT_VARIATION_URI=16, TYPE_TEXT_VARIATION_VISIBLE_PASSWORD=144, TYPE_TEXT_VARIATION_WEB_EDIT_TEXT=160, TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS=208, TYPE_TEXT_VARIATION_WEB_PASSWORD=224}' + - name: LinearLayout + type: 4 + typeName: map + value: '{HORIZONTAL=0, VERTICAL=1}' + - name: MATCH_PARENT + type: 1 + typeName: number + value: '-1' + - name: PorterDuff + type: 4 + typeName: map + value: '{ADD=16, CLEAR=0, DARKEN=12, DST=2, DST_ATOP=10, DST_IN=6, DST_OUT=8, DST_OVER=4, LIGHTEN=13, MULTIPLY=14, OVERLAY=17, SCREEN=15, SRC=1, SRC_ATOP=9, SRC_IN=5, SRC_OUT=7, SRC_OVER=3, XOR=11}' + - name: ScaleType + type: 4 + typeName: map + value: '{MATRIX=0, FIT_XY=1, FIT_START=2, FIT_CENTER=3, FIT_END=4, CENTER=5, CENTER_CROP=6, CENTER_INSIDE=7}' + - name: WRAP_CONTENT + type: 1 + typeName: number + value: '-2' +functions: + - name: showForm + args: 'view, layoutParams = {}' + desc: 'shows view' + desc_ru: 'показывает форму' + - name: inflate + args: 'resourceId, rootView = null, attachToRoot = false' + desc: 'Inflates view from resource xml' + desc_ru: 'Создаёт view из xml-ресурса' + - name: newArrayAdapter + args: 'resourceId = R.layout.simple_list_item_1, elements = []' + desc: 'Creates ArrayAdapter to use in ListView' + desc_ru: 'Создаёт ArrayAdapter для использования в ListView' + - name: newBaseAdapter + args: 'mapWithFunctions' + desc: '' + desc_ru: '' + example: |- + use std, android, forms + + img1 = assetBitmap("ownlang.png") + img2 = img1 + + items = [ + {"img" : img1, "text" : "Item 1"}, + {"img" : img2, "text" : "Item 2"} + ] + adapter = newBaseAdapter({ + "getCount": def() = length(items) + "getItem": def(pos) = items[pos] + "getItemId": def(pos) = pos + "getView": def(pos, view, parent) { + if (view == 0) { + view = newLinearLayout() + view.setOrientation(LinearLayout.HORIZONTAL) + imageView = newImageView() + view.addView(imageView) + textView = newTextView() + view.addView(textView) + view.setTag([imageView, textView]) + } else { + extract(imageView, textView) = view.getTag() + } + + imageView.setImageBitmap(items[pos].img); + textView.setText(items[pos].text); + return view + } + }); + + listView = newListView() + listView.setAdapter(adapter) + listView.onItemClick(def(v, pos, id) { + toast(adapter.getItem(pos).text + " selected") + }) + + panel = newLinearLayout() + panel.addView(newTextView("ListView with BaseAdapter demo")) + panel.addView(listView) + + showForm(panel) + - name: newButton + args: 'text = ""' + desc: 'creates Button' + desc_ru: 'создаёт Button' + - name: newCheckBox + args: '' + desc: 'creates CheckBox' + desc_ru: 'создаёт CheckBox' + - name: newEditText + args: '' + desc: 'creates EditText' + desc_ru: 'создаёт EditText' + - name: newFrameLayout + args: '' + desc: 'creates FrameLayout container' + desc_ru: 'создаёт контейнер FrameLayout' + - name: newImageButton + args: '' + desc: 'creates ImageButton' + desc_ru: 'создаёт ImageButton' + - name: newImageView + args: '' + desc: 'creates ImageView' + desc_ru: 'создаёт ImageView' + - name: newLinearLayout + args: '' + desc: 'creates LinearLayout container' + desc_ru: 'создаёт контейнер LinearLayout' + - name: newListView + args: '' + desc: 'creates ListView' + desc_ru: 'создаёт ListView' + - name: newProgressBar + args: 'style = R.attr.progressBarStyle' + desc: 'creates ProgressBar' + desc_ru: 'создаёт ProgressBar' + example: |- + use android, forms + pb1 = newProgressBar(R.attr.progressBarStyleHorizontal) + pb1.setMax(100) + pb1.setProgress(10) + pb2 = newProgressBar() + pb2.setIndeterminate(true) + + panel = newLinearLayout() + panel.addView(pb1) + panel.addView(pb2) + showForm(panel) + - name: newRadioButton + args: '' + desc: 'creates RadioButton' + desc_ru: 'создаёт RadioButton' + - name: newRadioGroup + args: '' + desc: 'creates RadioGroup container' + desc_ru: 'создаёт контейнер RadioGroup' + - name: newRelativeLayout + args: '' + desc: 'creates RelativeLayout container' + desc_ru: 'создаёт контейнер RelativeLayout' + - name: newScrollView + args: '' + desc: 'creates ScrollView container' + desc_ru: 'создаёт контейнер ScrollView' + - name: newSeekBar + args: '' + desc: 'creates SeekBar' + desc_ru: 'создаёт SeekBar' + - name: newSwitch + args: '' + desc: 'creates Switch (available for SDK_INT >= 14)' + desc_ru: 'создаёт Switch (доступен для SDK_INT >= 14)' + - name: newTextView + args: 'text = ""' + desc: 'creates TextView' + desc_ru: 'создаёт TextView' + - name: newToggleButton + args: '' + desc: 'creates ToggleButton' + desc_ru: 'создаёт ToggleButton' +types: + - name: ViewValue + functions: + - name: bringToFront + args: '' + desc: '' + desc_ru: '' + - name: buildDrawingCache + args: '' + desc: '' + desc_ru: '' + - name: callOnClick + args: '' + desc: 'available for SDK_INT >= 15' + desc_ru: 'доступно для SDK_INT >= 15' + - name: cancelLongPress + args: '' + desc: '' + desc_ru: '' + - name: clearAnimation + args: '' + desc: '' + desc_ru: '' + - name: clearFocus + args: '' + desc: '' + desc_ru: '' + - name: computeScroll + args: '' + desc: '' + desc_ru: '' + - name: destroyDrawingCache + args: '' + desc: '' + desc_ru: '' + - name: dispatchDisplayHint + args: '' + desc: '' + desc_ru: '' + - name: findFocus + args: '' + desc: '' + desc_ru: '' + - name: findViewById + args: '' + desc: '' + desc_ru: '' + - name: focusSearch + args: '' + desc: '' + desc_ru: '' + - name: forceLayout + args: '' + desc: '' + desc_ru: '' + - name: getAlpha + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getBaseline + args: '' + desc: '' + desc_ru: '' + - name: getBottom + args: '' + desc: '' + desc_ru: '' + - name: getContentDescription + args: '' + desc: '' + desc_ru: '' + - name: getDrawingCacheBackgroundColor + args: '' + desc: '' + desc_ru: '' + - name: getDrawingCacheQuality + args: '' + desc: '' + desc_ru: '' + - name: getDrawingTime + args: '' + desc: '' + desc_ru: '' + - name: getHeight + args: '' + desc: '' + desc_ru: '' + - name: getHorizontalFadingEdgeLength + args: '' + desc: '' + desc_ru: '' + - name: getId + args: '' + desc: '' + desc_ru: '' + - name: getKeepScreenOn + args: '' + desc: '' + desc_ru: '' + - name: getLeft + args: '' + desc: '' + desc_ru: '' + - name: getMeasuredHeight + args: '' + desc: '' + desc_ru: '' + - name: getMeasuredHeightAndState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getMeasuredState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getMeasuredWidth + args: '' + desc: '' + desc_ru: '' + - name: getMeasuredWidthAndState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getNextFocusDownId + args: '' + desc: '' + desc_ru: '' + - name: getNextFocusForwardId + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getNextFocusLeftId + args: '' + desc: '' + desc_ru: '' + - name: getNextFocusRightId + args: '' + desc: '' + desc_ru: '' + - name: getNextFocusUpId + args: '' + desc: '' + desc_ru: '' + - name: getOverScrollMode + args: '' + desc: '' + desc_ru: '' + - name: getPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getPaddingEnd + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getPaddingLeft + args: '' + desc: '' + desc_ru: '' + - name: getPaddingRight + args: '' + desc: '' + desc_ru: '' + - name: getPaddingStart + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: getPivotX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getPivotY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getRight + args: '' + desc: '' + desc_ru: '' + - name: getRootView + args: '' + desc: '' + desc_ru: '' + - name: getRotation + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getRotationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getRotationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getScaleX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getScaleY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getScrollBarFadeDuration + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: getScrollBarSize + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: getScrollBarStyle + args: '' + desc: '' + desc_ru: '' + - name: getScrollX + args: '' + desc: '' + desc_ru: '' + - name: getScrollY + args: '' + desc: '' + desc_ru: '' + - name: getSolidColor + args: '' + desc: '' + desc_ru: '' + - name: getSystemUiVisibility + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getTag + args: '' + desc: '' + desc_ru: '' + - name: getTextAlignment + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getTextDirection + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: getTop + args: '' + desc: '' + desc_ru: '' + - name: getTranslationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getTranslationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getTranslationZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: getVerticalFadingEdgeLength + args: '' + desc: '' + desc_ru: '' + - name: getVerticalScrollbarPosition + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getVerticalScrollbarWidth + args: '' + desc: '' + desc_ru: '' + - name: getVisibility + args: '' + desc: '' + desc_ru: '' + - name: getWidth + args: '' + desc: '' + desc_ru: '' + - name: getWindowSystemUiVisibility + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: getWindowVisibility + args: '' + desc: '' + desc_ru: '' + - name: getX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: getZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: hasFocus + args: '' + desc: '' + desc_ru: '' + - name: hasFocusable + args: '' + desc: '' + desc_ru: '' + - name: hasNestedScrollingParent + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: hasOnClickListeners + args: '' + desc: 'available for SDK_INT >= 15' + desc_ru: 'доступно для SDK_INT >= 15' + - name: hasOverlappingRendering + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: hasTransientState + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: hasWindowFocus + args: '' + desc: '' + desc_ru: '' + - name: invalidate + args: '' + desc: '' + desc_ru: '' + - name: invalidateDrawable + args: '' + desc: '' + desc_ru: '' + - name: invalidateOutline + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isAccessibilityFocused + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isActivated + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isAttachedToWindow + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isClickable + args: '' + desc: '' + desc_ru: '' + - name: isContextClickable + args: '' + desc: 'available for SDK_INT >= 23' + desc_ru: 'доступно для SDK_INT >= 23' + - name: isDirty + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isDrawingCacheEnabled + args: '' + desc: '' + desc_ru: '' + - name: isDuplicateParentStateEnabled + args: '' + desc: '' + desc_ru: '' + - name: isEnabled + args: '' + desc: '' + desc_ru: '' + - name: isFocusable + args: '' + desc: '' + desc_ru: '' + - name: isFocusableInTouchMode + args: '' + desc: '' + desc_ru: '' + - name: isFocused + args: '' + desc: '' + desc_ru: '' + - name: isHapticFeedbackEnabled + args: '' + desc: '' + desc_ru: '' + - name: isHardwareAccelerated + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isHorizontalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: isHorizontalScrollBarEnabled + args: '' + desc: '' + desc_ru: '' + - name: isHovered + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: isImportantForAccessibility + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isInEditMode + args: '' + desc: '' + desc_ru: '' + - name: isInLayout + args: '' + desc: 'available for SDK_INT >= 18' + desc_ru: 'доступно для SDK_INT >= 18' + - name: isInTouchMode + args: '' + desc: '' + desc_ru: '' + - name: isLaidOut + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isLayoutDirectionResolved + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isLayoutRequested + args: '' + desc: '' + desc_ru: '' + - name: isLongClickable + args: '' + desc: '' + desc_ru: '' + - name: isNestedScrollingEnabled + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: isOpaque + args: '' + desc: '' + desc_ru: '' + - name: isPaddingRelative + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: isPressed + args: '' + desc: '' + desc_ru: '' + - name: isSaveEnabled + args: '' + desc: '' + desc_ru: '' + - name: isSaveFromParentEnabled + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: isScrollContainer + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: isScrollbarFadingEnabled + args: '' + desc: '' + desc_ru: '' + - name: isSelected + args: '' + desc: '' + desc_ru: '' + - name: isShown + args: '' + desc: '' + desc_ru: '' + - name: isSoundEffectsEnabled + args: '' + desc: '' + desc_ru: '' + - name: isTextAlignmentResolved + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isTextDirectionResolved + args: '' + desc: 'available for SDK_INT >= 19' + desc_ru: 'доступно для SDK_INT >= 19' + - name: isVerticalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: isVerticalScrollBarEnabled + args: '' + desc: '' + desc_ru: '' + - name: jumpDrawablesToCurrentState + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: offsetLeftAndRight + args: '' + desc: '' + desc_ru: '' + - name: offsetTopAndBottom + args: '' + desc: '' + desc_ru: '' + - name: onClick + args: '' + desc: '' + desc_ru: '' + - name: onFocusChange + args: '' + desc: '' + desc_ru: '' + - name: onKey + args: '' + desc: '' + desc_ru: '' + - name: onLongClick + args: '' + desc: '' + desc_ru: '' + - name: performClick + args: '' + desc: '' + desc_ru: '' + - name: performHapticFeedback + args: '' + desc: '' + desc_ru: '' + - name: performLongClick + args: '' + desc: '' + desc_ru: '' + - name: playSoundEffect + args: '' + desc: '' + desc_ru: '' + - name: post + args: '' + desc: '' + desc_ru: '' + - name: postDelayed + args: '' + desc: '' + desc_ru: '' + - name: postInvalidate + args: '' + desc: '' + desc_ru: '' + - name: refreshDrawableState + args: '' + desc: '' + desc_ru: '' + - name: requestFocus + args: '' + desc: '' + desc_ru: '' + - name: requestFocusFromTouch + args: '' + desc: '' + desc_ru: '' + - name: requestLayout + args: '' + desc: '' + desc_ru: '' + - name: scrollBy + args: '' + desc: '' + desc_ru: '' + - name: scrollTo + args: '' + desc: '' + desc_ru: '' + - name: sendAccessibilityEvent + args: '' + desc: '' + desc_ru: '' + - name: setActivated + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setAlpha + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setBackground + args: '' + desc: '' + desc_ru: '' + - name: setBackgroundColor + args: '' + desc: '' + desc_ru: '' + - name: setBackgroundDrawable + args: '' + desc: '' + desc_ru: '' + - name: setBackgroundResource + args: '' + desc: '' + desc_ru: '' + - name: setBottom + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setCameraDistance + args: '' + desc: 'available for SDK_INT >= 12' + desc_ru: 'доступно для SDK_INT >= 12' + - name: setClickable + args: '' + desc: '' + desc_ru: '' + - name: setClipToOutline + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: setContentDescription + args: '' + desc: '' + desc_ru: '' + - name: setContextClickable + args: '' + desc: 'available for SDK_INT >= 23' + desc_ru: 'доступно для SDK_INT >= 23' + - name: setDrawingCacheBackgroundColor + args: '' + desc: '' + desc_ru: '' + - name: setDrawingCacheEnabled + args: '' + desc: '' + desc_ru: '' + - name: setDrawingCacheQuality + args: '' + desc: '' + desc_ru: '' + - name: setDuplicateParentStateEnabled + args: '' + desc: '' + desc_ru: '' + - name: setEnabled + args: '' + desc: '' + desc_ru: '' + - name: setFadingEdgeLength + args: '' + desc: '' + desc_ru: '' + - name: setFilterTouchesWhenObscured + args: '' + desc: '' + desc_ru: '' + - name: setFitsSystemWindows + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setFocusable + args: '' + desc: '' + desc_ru: '' + - name: setFocusableInTouchMode + args: '' + desc: '' + desc_ru: '' + - name: setForeground + args: '' + desc: '' + desc_ru: '' + - name: setHapticFeedbackEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalScrollBarEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHovered + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setId + args: '' + desc: '' + desc_ru: '' + - name: setImportantForAccessibility + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setKeepScreenOn + args: '' + desc: '' + desc_ru: '' + - name: setLabelFor + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setLayoutDirection + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setLeft + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setLongClickable + args: '' + desc: '' + desc_ru: '' + - name: setMinimumHeight + args: '' + desc: '' + desc_ru: '' + - name: setMinimumWidth + args: '' + desc: '' + desc_ru: '' + - name: setNestedScrollingEnabled + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: setNextFocusDownId + args: '' + desc: '' + desc_ru: '' + - name: setNextFocusForwardId + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setNextFocusLeftId + args: '' + desc: '' + desc_ru: '' + - name: setNextFocusRightId + args: '' + desc: '' + desc_ru: '' + - name: setNextFocusUpId + args: '' + desc: '' + desc_ru: '' + - name: setOnClickListener + args: '' + desc: '' + desc_ru: '' + - name: setOnFocusChangeListener + args: '' + desc: '' + desc_ru: '' + - name: setOnKeyListener + args: '' + desc: '' + desc_ru: '' + - name: setOnLongClickListener + args: '' + desc: '' + desc_ru: '' + - name: setOverScrollMode + args: '' + desc: '' + desc_ru: '' + - name: setPadding + args: '' + desc: '' + desc_ru: '' + - name: setPaddingRelative + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setPivotX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setPivotY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setPressed + args: '' + desc: '' + desc_ru: '' + - name: setRight + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setRotation + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setRotationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setRotationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setSaveEnabled + args: '' + desc: '' + desc_ru: '' + - name: setSaveFromParentEnabled + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setScaleX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setScaleY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setScrollBarDefaultDelayBeforeFade + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setScrollBarFadeDuration + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setScrollBarSize + args: '' + desc: 'available for SDK_INT >= 16' + desc_ru: 'доступно для SDK_INT >= 16' + - name: setScrollBarStyle + args: '' + desc: '' + desc_ru: '' + - name: setScrollContainer + args: '' + desc: '' + desc_ru: '' + - name: setScrollX + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setScrollY + args: '' + desc: 'available for SDK_INT >= 14' + desc_ru: 'доступно для SDK_INT >= 14' + - name: setSelected + args: '' + desc: '' + desc_ru: '' + - name: setSoundEffectsEnabled + args: '' + desc: '' + desc_ru: '' + - name: setSystemUiVisibility + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTag + args: '' + desc: '' + desc_ru: '' + - name: setTextAlignment + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setTextDirection + args: '' + desc: 'available for SDK_INT >= 17' + desc_ru: 'доступно для SDK_INT >= 17' + - name: setTop + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTranslationX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTranslationY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setTranslationZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: setVerticalFadingEdgeEnabled + args: '' + desc: '' + desc_ru: '' + - name: setVerticalScrollbarPosition + args: '' + desc: '' + desc_ru: '' + - name: setVisibility + args: '' + desc: '' + desc_ru: '' + - name: setWillNotCacheDrawing + args: '' + desc: '' + desc_ru: '' + - name: setWillNotDraw + args: '' + desc: '' + desc_ru: '' + - name: setX + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setY + args: '' + desc: 'available for SDK_INT >= 11' + desc_ru: 'доступно для SDK_INT >= 11' + - name: setZ + args: '' + desc: 'available for SDK_INT >= 21' + desc_ru: 'доступно для SDK_INT >= 21' + - name: showContextMenu + args: '' + desc: '' + desc_ru: '' + - name: willNotCacheDrawing + args: '' + desc: '' + desc_ru: '' + - name: willNotDraw + args: '' + desc: '' + desc_ru: '' + - name: TextViewValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: beginBatchEdit + args: '' + desc: '' + desc_ru: '' + - name: endBatchEdit + args: '' + desc: '' + desc_ru: '' + - name: getAutoLinkMask + args: '' + desc: '' + desc_ru: '' + - name: getCompoundDrawablePadding + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingLeft + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingRight + args: '' + desc: '' + desc_ru: '' + - name: getCompoundPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: getCurrentHintTextColor + args: '' + desc: '' + desc_ru: '' + - name: getCurrentTextColor + args: '' + desc: '' + desc_ru: '' + - name: getEditableText + args: '' + desc: '' + desc_ru: '' + - name: getEllipsize + args: '' + desc: '' + desc_ru: '' + - name: getError + args: '' + desc: '' + desc_ru: '' + - name: getExtendedPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getExtendedPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: getFreezesText + args: '' + desc: '' + desc_ru: '' + - name: getGravity + args: '' + desc: '' + desc_ru: '' + - name: getHighlightColor + args: '' + desc: '' + desc_ru: '' + - name: getHint + args: '' + desc: '' + desc_ru: '' + - name: getImeActionId + args: '' + desc: '' + desc_ru: '' + - name: getImeActionLabel + args: '' + desc: '' + desc_ru: '' + - name: getImeOptions + args: '' + desc: '' + desc_ru: '' + - name: getInputType + args: '' + desc: '' + desc_ru: '' + - name: getLineCount + args: '' + desc: '' + desc_ru: '' + - name: getLineHeight + args: '' + desc: '' + desc_ru: '' + - name: getLinksClickable + args: '' + desc: '' + desc_ru: '' + - name: getSelectionEnd + args: '' + desc: '' + desc_ru: '' + - name: getSelectionStart + args: '' + desc: '' + desc_ru: '' + - name: getText + args: '' + desc: '' + desc_ru: '' + - name: getTextScaleX + args: '' + desc: '' + desc_ru: '' + - name: getTextSize + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingBottom + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingLeft + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingRight + args: '' + desc: '' + desc_ru: '' + - name: getTotalPaddingTop + args: '' + desc: '' + desc_ru: '' + - name: hasSelection + args: '' + desc: '' + desc_ru: '' + - name: isCursorVisible + args: '' + desc: '' + desc_ru: '' + - name: isInputMethodTarget + args: '' + desc: '' + desc_ru: '' + - name: isSuggestionsEnabled + args: '' + desc: '' + desc_ru: '' + - name: isTextSelectable + args: '' + desc: '' + desc_ru: '' + - name: length + args: '' + desc: '' + desc_ru: '' + - name: moveCursorToVisibleOffset + args: '' + desc: '' + desc_ru: '' + - name: setAllCaps + args: '' + desc: '' + desc_ru: '' + - name: setAutoLinkMask + args: '' + desc: '' + desc_ru: '' + - name: setBreakStrategy + args: '' + desc: '' + desc_ru: '' + - name: setCompoundDrawablePadding + args: '' + desc: '' + desc_ru: '' + - name: setCompoundDrawables + args: '' + desc: '' + desc_ru: '' + - name: setCursorVisible + args: '' + desc: '' + desc_ru: '' + - name: setEllipsize + args: '' + desc: '' + desc_ru: '' + - name: setEms + args: '' + desc: '' + desc_ru: '' + - name: setError + args: '' + desc: '' + desc_ru: '' + - name: setFreezesText + args: '' + desc: '' + desc_ru: '' + - name: setGravity + args: '' + desc: '' + desc_ru: '' + - name: setHeight + args: '' + desc: '' + desc_ru: '' + - name: setHighlightColor + args: '' + desc: '' + desc_ru: '' + - name: setHint + args: '' + desc: '' + desc_ru: '' + - name: setHintTextColor + args: '' + desc: '' + desc_ru: '' + - name: setHorizontallyScrolling + args: '' + desc: '' + desc_ru: '' + - name: setImeOptions + args: '' + desc: '' + desc_ru: '' + - name: setInputType + args: '' + desc: '' + desc_ru: '' + - name: setLines + args: '' + desc: '' + desc_ru: '' + - name: setLinkTextColor + args: '' + desc: '' + desc_ru: '' + - name: setLinksClickable + args: '' + desc: '' + desc_ru: '' + - name: setMaxEms + args: '' + desc: '' + desc_ru: '' + - name: setMaxHeight + args: '' + desc: '' + desc_ru: '' + - name: setMaxLines + args: '' + desc: '' + desc_ru: '' + - name: setMaxWidth + args: '' + desc: '' + desc_ru: '' + - name: setMinEms + args: '' + desc: '' + desc_ru: '' + - name: setMinHeight + args: '' + desc: '' + desc_ru: '' + - name: setMinLines + args: '' + desc: '' + desc_ru: '' + - name: setMinWidth + args: '' + desc: '' + desc_ru: '' + - name: setPaintFlags + args: '' + desc: '' + desc_ru: '' + - name: setRawInputType + args: '' + desc: '' + desc_ru: '' + - name: setSelectAllOnFocus + args: '' + desc: '' + desc_ru: '' + - name: setSingleLine + args: '' + desc: '' + desc_ru: '' + - name: setText + args: '' + desc: '' + desc_ru: '' + - name: setTextColor + args: '' + desc: '' + desc_ru: '' + - name: setTextIsSelectable + args: '' + desc: '' + desc_ru: '' + - name: setTextScaleX + args: '' + desc: '' + desc_ru: '' + - name: setTextSize + args: '' + desc: '' + desc_ru: '' + - name: setWidth + args: '' + desc: '' + desc_ru: '' + - name: EditTextValue + desc: 'Inheritance hierarchy: TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: TextViewValue < ViewValue' + functions: + - name: extendSelection + args: '' + desc: '' + desc_ru: '' + - name: selectAll + args: '' + desc: '' + desc_ru: '' + - name: setSelection + args: '' + desc: '' + desc_ru: '' + - name: ButtonValue + desc: 'Inheritance hierarchy: TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: TextViewValue < ViewValue' + functions: [] + - name: CompoundButtonValue + desc: 'Inheritance hierarchy: ButtonValue < TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: ButtonValue < TextViewValue < ViewValue' + functions: + - name: isChecked + args: '' + desc: '' + desc_ru: '' + - name: onCheck + args: '' + desc: '' + desc_ru: '' + - name: setButtonDrawable + args: '' + desc: '' + desc_ru: '' + - name: setChecked + args: '' + desc: '' + desc_ru: '' + - name: toggle + args: '' + desc: '' + desc_ru: '' + - name: ToggleButtonValue + desc: 'Inheritance hierarchy: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + functions: + - name: getTextOff + args: '' + desc: '' + desc_ru: '' + - name: getTextOn + args: '' + desc: '' + desc_ru: '' + - name: setTextOff + args: '' + desc: '' + desc_ru: '' + - name: setTextOn + args: '' + desc: '' + desc_ru: '' + - name: SwitchValue + desc: 'Inheritance hierarchy: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + desc_ru: 'Иерархия наследования: CompoundButtonValue < ButtonValue < TextViewValue < ViewValue' + functions: + - name: getTextOff + args: '' + desc: '' + desc_ru: '' + - name: getTextOn + args: '' + desc: '' + desc_ru: '' + - name: setTextOff + args: '' + desc: '' + desc_ru: '' + - name: setTextOn + args: '' + desc: '' + desc_ru: '' + - name: ImageViewValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: clearColorFilter + args: '' + desc: '' + desc_ru: '' + - name: getScaleType + args: '' + desc: '' + desc_ru: '' + - name: setAdjustViewBounds + args: '' + desc: '' + desc_ru: '' + - name: setColorFilter + args: '' + desc: '' + desc_ru: '' + - name: setImageAlpha + args: '' + desc: '' + desc_ru: '' + - name: setImageBitmap + args: '' + desc: '' + desc_ru: '' + - name: setImageDrawable + args: '' + desc: '' + desc_ru: '' + - name: setImageLevel + args: '' + desc: '' + desc_ru: '' + - name: setImageResource + args: '' + desc: '' + desc_ru: '' + - name: setImageURI + args: '' + desc: '' + desc_ru: '' + - name: setMaxHeight + args: '' + desc: '' + desc_ru: '' + - name: setMaxWidth + args: '' + desc: '' + desc_ru: '' + - name: setScaleType + args: '' + desc: '' + desc_ru: '' + - name: ImageButtonValue + desc: 'Inheritance hierarchy: ImageViewValue < ViewValue' + desc_ru: 'Иерархия наследования: ImageViewValue < ViewValue' + functions: [] + - name: ViewGroupValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: addView + args: '' + desc: '' + desc_ru: '' + - name: bringChildToFront + args: '' + desc: '' + desc_ru: '' + - name: clearChildFocus + args: '' + desc: '' + desc_ru: '' + - name: getChildAt + args: '' + desc: '' + desc_ru: '' + - name: getChildCount + args: '' + desc: '' + desc_ru: '' + - name: indexOfChild + args: '' + desc: '' + desc_ru: '' + - name: recomputeViewAttributes + args: '' + desc: '' + desc_ru: '' + - name: removeAllViews + args: '' + desc: '' + desc_ru: '' + - name: removeAllViewsInLayout + args: '' + desc: '' + desc_ru: '' + - name: removeView + args: '' + desc: '' + desc_ru: '' + - name: removeViewAt + args: '' + desc: '' + desc_ru: '' + - name: removeViewInLayout + args: '' + desc: '' + desc_ru: '' + - name: LinearLayoutValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: + - name: getOrientation + args: '' + desc: '' + desc_ru: '' + - name: getWeightSum + args: '' + desc: '' + desc_ru: '' + - name: setGravity + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalGravity + args: '' + desc: '' + desc_ru: '' + - name: setOrientation + args: '' + desc: '' + desc_ru: '' + - name: setVerticalGravity + args: '' + desc: '' + desc_ru: '' + - name: setWeightSum + args: '' + desc: '' + desc_ru: '' + - name: RelativeLayoutValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: + - name: getGravity + args: '' + desc: '' + desc_ru: '' + - name: setGravity + args: '' + desc: '' + desc_ru: '' + - name: setHorizontalGravity + args: '' + desc: '' + desc_ru: '' + - name: setIgnoreGravity + args: '' + desc: '' + desc_ru: '' + - name: setVerticalGravity + args: '' + desc: '' + desc_ru: '' + - name: FrameLayoutValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: [] + - name: ScrollViewValue + desc: 'Inheritance hierarchy: FrameLayoutValue < ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: FrameLayoutValue < ViewGroupValue < ViewValue' + functions: + - name: isFillViewport + args: '' + desc: '' + desc_ru: '' + - name: isSmoothScrollingEnabled + args: '' + desc: '' + desc_ru: '' + - name: setFillViewport + args: '' + desc: '' + desc_ru: '' + - name: setSmoothScrollingEnabled + args: '' + desc: '' + desc_ru: '' + - name: AdapterViewValue + desc: 'Inheritance hierarchy: ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: ViewGroupValue < ViewValue' + functions: + - name: getAdapter + args: '' + desc: '' + desc_ru: '' + - name: getCount + args: '' + desc: '' + desc_ru: '' + - name: getEmptyView + args: '' + desc: '' + desc_ru: '' + - name: getFirstVisiblePosition + args: '' + desc: '' + desc_ru: '' + - name: getItemAtPosition + args: '' + desc: '' + desc_ru: '' + - name: getItemIdAtPosition + args: '' + desc: '' + desc_ru: '' + - name: getLastVisiblePosition + args: '' + desc: '' + desc_ru: '' + - name: getPositionForView + args: '' + desc: '' + desc_ru: '' + - name: getSelectedItem + args: '' + desc: '' + desc_ru: '' + - name: getSelectedItemId + args: '' + desc: '' + desc_ru: '' + - name: getSelectedItemPosition + args: '' + desc: '' + desc_ru: '' + - name: getSelectedView + args: '' + desc: '' + desc_ru: '' + - name: onItemClick + args: '' + desc: '' + desc_ru: '' + - name: onItemLongClick + args: '' + desc: '' + desc_ru: '' + - name: onItemSelected + args: '' + desc: '' + desc_ru: '' + - name: performItemClick + args: '' + desc: '' + desc_ru: '' + - name: setAdapter + args: '' + desc: '' + desc_ru: '' + - name: setEmptyView + args: '' + desc: '' + desc_ru: '' + - name: ListViewValue + desc: 'Inheritance hierarchy: AdapterViewValue < ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: AdapterViewValue < ViewGroupValue < ViewValue' + functions: + - name: addFooterView + args: '' + desc: '' + desc_ru: '' + - name: addHeaderView + args: '' + desc: '' + desc_ru: '' + - name: getDividerHeight + args: '' + desc: '' + desc_ru: '' + - name: getFooterViewsCount + args: '' + desc: '' + desc_ru: '' + - name: getHeaderViewsCount + args: '' + desc: '' + desc_ru: '' + - name: getItemsCanFocus + args: '' + desc: '' + desc_ru: '' + - name: getMaxScrollAmount + args: '' + desc: '' + desc_ru: '' + - name: removeFooterView + args: '' + desc: '' + desc_ru: '' + - name: removeHeaderView + args: '' + desc: '' + desc_ru: '' + - name: setCacheColorHint + args: '' + desc: '' + desc_ru: '' + - name: setDividerHeight + args: '' + desc: '' + desc_ru: '' + - name: setFooterDividersEnabled + args: '' + desc: '' + desc_ru: '' + - name: setHeaderDividersEnabled + args: '' + desc: '' + desc_ru: '' + - name: setItemsCanFocus + args: '' + desc: '' + desc_ru: '' + - name: setSelection + args: '' + desc: '' + desc_ru: '' + - name: setSelectionAfterHeaderView + args: '' + desc: '' + desc_ru: '' + - name: smoothScrollToPosition + args: '' + desc: '' + desc_ru: '' + - name: RadioGroupValue + desc: 'Inheritance hierarchy: LinearLayoutValue < ViewGroupValue < ViewValue' + desc_ru: 'Иерархия наследования: LinearLayoutValue < ViewGroupValue < ViewValue' + functions: + - name: check + args: '' + desc: '' + desc_ru: '' + - name: clearCheck + args: '' + desc: '' + desc_ru: '' + - name: getCheckedRadioButtonId + args: '' + desc: '' + desc_ru: '' + - name: onCheck + args: '' + desc: '' + desc_ru: '' + - name: setOnCheckedChangeListener + args: '' + desc: '' + desc_ru: '' + - name: ProgressBarValue + desc: 'Inheritance hierarchy: ViewValue' + desc_ru: 'Иерархия наследования: ViewValue' + functions: + - name: getMax + args: '' + desc: '' + desc_ru: '' + - name: getProgress + args: '' + desc: '' + desc_ru: '' + - name: getSecondaryProgress + args: '' + desc: '' + desc_ru: '' + - name: incrementProgressBy + args: '' + desc: '' + desc_ru: '' + - name: incrementSecondaryProgressBy + args: '' + desc: '' + desc_ru: '' + - name: setIndeterminate + args: '' + desc: '' + desc_ru: '' + - name: setIndeterminateDrawable + args: '' + desc: '' + desc_ru: '' + - name: setMax + args: '' + desc: '' + desc_ru: '' + - name: setProgress + args: '' + desc: '' + desc_ru: '' + - name: setProgressDrawable + args: '' + desc: '' + desc_ru: '' + - name: setSecondaryProgress + args: '' + desc: '' + desc_ru: '' + - name: SeekBarValue + desc: 'Inheritance hierarchy: ProgressBarValue < ViewValue' + desc_ru: 'Иерархия наследования: ProgressBarValue < ViewValue' + functions: + - name: getKeyProgressIncrement + args: '' + desc: '' + desc_ru: '' + - name: getThumbOffset + args: '' + desc: '' + desc_ru: '' + - name: onSeekBarChange + args: '' + desc: '' + desc_ru: '' + - name: setKeyProgressIncrement + args: '' + desc: '' + desc_ru: '' + - name: setOnSeekBarChangeListener + args: '' + desc: '' + desc_ru: '' + - name: setThumb + args: '' + desc: '' + desc_ru: '' + - name: setThumbOffset + args: '' + desc: '' + desc_ru: '' + - name: AdapterValue + functions: + - name: getCount + args: '' + desc: '' + desc_ru: '' + - name: getItem + args: '' + desc: '' + desc_ru: '' + - name: getItemId + args: '' + desc: '' + desc_ru: '' + - name: getItemViewType + args: '' + desc: '' + desc_ru: '' + - name: getView + args: '' + desc: '' + desc_ru: '' + - name: getViewTypeCount + args: '' + desc: '' + desc_ru: '' + - name: hasStableIds + args: '' + desc: '' + desc_ru: '' + - name: isEmpty + args: '' + desc: '' + desc_ru: '' + - name: ListAdapterValue + desc: 'Inheritance hierarchy: AdapterValue' + desc_ru: 'Иерархия наследования: AdapterValue' + functions: + - name: areAllItemsEnabled + args: '' + desc: '' + desc_ru: '' + - name: isEnabled + args: '' + desc: '' + desc_ru: '' \ No newline at end of file diff --git a/docs/src/modules/functional.yml b/docs/src/modules/functional.yml new file mode 100644 index 00000000..4ee5f3ba --- /dev/null +++ b/docs/src/modules/functional.yml @@ -0,0 +1,161 @@ +name: functional +scope: "both" +desc: "Contains functions for operating data in functional style" +desc_ru: "Содержит функции для работы с данными в функциональном стиле" +constants: + - name: "IDENTITY" + typeName: "function" + type: 5 + value: "def(x) = x" + desc: "function which returns passed argument" + desc_ru: "функция, которая возвращает переданный в неё аргумент" +functions: + - name: "chain" + args: "data, functions..." + desc: "" + desc_ru: "" + - name: "combine" + args: "functions..." + desc: "combines functions" + desc_ru: "комбинирует функции (композиция)" + example: |- + use functional + + def f1() = 2 + def f2(a) = a*2 + def f3(a) = a/4 + + f = combine(::f1, ::f2, ::f3) + println f() // 1 + // same as + f = def() = f3(f2(f1())) + println f() // 1 + example_ru: |- + use functional + + def f1() = 2 + def f2(a) = a*2 + def f3(a) = a/4 + + f = combine(::f1, ::f2, ::f3) + println f() // 1 + // равносильно + f = def() = f3(f2(f1())) + println f() // 1 + - name: dropwhile + args: 'data, predicate' + desc: 'skips elements while predicate function returns true' + desc_ru: 'пропускает элементы пока функция-предикат возвращает true' + - name: "filter" + args: "data, predicate" + desc: "filters array or object.\n\n`predicate` is a function which takes one argument for arrays or two arguments for objects" + desc_ru: "фильтрует массив или объект и возвращает массив только с теми элементами, которые удовлетворяют предикату `predicate`.\n\n`predicate` - функция которая принимает один (для массивов) и два (для объектов) аргумента" + example: |- + use functional + + nums = [1,2,3,4,5] + print filter(nums, def(x) = x % 2 == 0) // [2, 4] + - name: "flatmap" + args: "array, mapper" + desc: "converts each element of an array to other array" + desc_ru: "преобразует каждый элемент массива в массив элементов" + example: |- + use functional + + nums = [1,2,3,4] + print flatmap(nums, def(x) { + arr = newarray(x) + for i = 0, i < x, i++ + arr[i] = x + return arr + }) // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] + - name: "foreach" + args: "data, consumer" + desc: "invokes function `consumer` for each element of array or map `data`\n\nIf `data` - массив, то в функции consumer необходим один параметр, если объект - два (ключ и значение)." + desc_ru: "для каждого элемента в массиве или объекте `data` вызывает функцию `consumer`\n\nЕсли `data` - массив, то в функции `consumer` необходим один параметр, если объект - два (ключ и значение)." + example: |- + use functional + + foreach([1, 2, 3], def(v) { print v }) + foreach({"key": 1, "key2": "text"}, def(key, value) { + print key + ": " + value + }) + - name: "map" + args: "data, mapper..." + desc: "converts elements of array or map. If `data` is array - `mapper` converts his elements, if `data` is object - you need to pass `keyMapper` - converts keys and `valueMapper` - converts values" + desc_ru: "преобразует элементы массива или объекта.\n\nЕсли `data` - массив, то функция `mapper` преобразует значения, если объект - необходимо передать две функции: `keyMapper` - преобразует ключи и `valueMapper` - преобразует значения" + example: |- + use functional + + nums = [3,4,5] + print map(nums, def(x) = x * x) // [9, 16, 25] + - name: "reduce" + args: "data, identity, accumulator" + desc: "converts elements of an array or a map to one value, e.g. sum of elements or concatenation string. `accumulator` takes one argument for array and two arguments for object (key and value)." + desc_ru: "преобразует элементы массива или объекта в одно значение, например сумма элементов или объединение в строку.\n\nЕсли `data` - массив, то в функции `accumulator` необходим один параметр, если объект - два (ключ и значение)" + example: |- + use functional + + nums = [1,2,3,4,5] + print reduce(nums, 0, def(x, y) = x + y) // 15 + - name: "sortby" + args: "array, function" + desc: "sorts elements of an array or an object by `function` result" + desc_ru: "сортирует элементы массива по данным в функции `function`" + example: |- + use functional + + data = [ + {"k1": 2, "k2": "x"}, + {"k1": 7, "k2": "d"}, + {"k1": 4, "k2": "z"}, + {"k1": 5, "k2": "p"}, + ] + print sortby(data, def(v) = v.k1) // [{k1=2, k2=x}, {k1=4, k2=z}, {k1=5, k2=p}, {k1=7, k2=d}] + print sortby(data, def(v) = v.k2) // [{k1=7, k2=d}, {k1=5, k2=p}, {k1=2, k2=x}, {k1=4, k2=z}] + - name: "stream" + args: "data" + desc: |- + creates stream from data and returns StreamValue + + StreamValue functions: + - `filter(func)` - filters elements + - `map(func)` - converts each element + - `flatMap(func)` - converts each element to array + - `sorted(func)` - sorts elements with comparator function + - `sortBy(func)` - applies function, then sorts elements + - `takeWhile(func)` - takes elements while predicate function returns true + - `dropWhile(func)` - skips elements while predicate function returns true + - `peek(func)` - executes function for each element and returns stream + - `skip(count)` - skips count elements + - `limit(count)` - limits elements size + - `custom(func)` - performs custom operation + - `reduce(func)` - converts elements to one value + - `forEach(func)` - executes function for each element + - `joining(delimiter = "", prefix = "", suffix = "")` - joins elements into a string + - `toArray()` - returns array of elements + - `count()` - returns count of elements + desc_ru: |- + создаёт stream из данных и возвращает StreamValue + + Функции StreamValue: + - `filter(func)` - фильтрует элементы + - `map(func)` - преобразует каждый элемент + - `flatMap(func)` - преобразует каждый элемент в массив + - `sorted(func)` - сортирует элементы в соответствии с функцией-компаратором + - `sortBy(func)` - применяет функцию, затем сортирует элементы + - `takeWhile(func)` - собирает элементы пока функция-предикат возвращает true + - `dropWhile(func)` - пропускает элементы пока функция-предикат возвращает true + - `peek(func)` - вызывает функцию для каждого элемента и возвращает stream + - `skip(count)` - пропускает указанное количество элементов + - `limit(count)` - ограничивает количество элементов + - `custom(func)` - выполняет пользовательскую операцию над данными + - `reduce(func)` - преобразует элементы в одно значение + - `forEach(func)` - вызывает функцию для каждого элемента + - `joining(delimiter = "", prefix = "", suffix = "")` - склеивает элементы в строку + - `toArray()` - возвращает массив элементов + - `count()` - возвращает количество элементов + - name: takewhile + args: 'data, predicate' + desc: 'takes elements while predicate function returns true' + desc_ru: 'собирает элементы пока функция-предикат возвращает true' \ No newline at end of file diff --git a/docs/src/modules/gps_android.yml b/docs/src/modules/gps_android.yml new file mode 100644 index 00000000..f1ae79d0 --- /dev/null +++ b/docs/src/modules/gps_android.yml @@ -0,0 +1,45 @@ +name: gps +scope: "android" +desc: |- + Contains functions for working with GPS. +desc_ru: |- + Содержит функции для работы с GPS. +constants: + - name: GPS_PROVIDER + type: 2 + typeName: string + value: gps + - name: NETWORK_PROVIDER + type: 2 + typeName: string + value: network +functions: + - name: isEnabled + args: "provider" + desc: "checks if the given location service provider is enabled" + desc_ru: "проверяет доступность указанного провайдера местоположения" + - name: lastKnownLocation + args: "provider" + desc: "gets last known location with the given location provider, or zero if it is unable to get location" + desc_ru: "получает последнее сохранённое местоположение для указанного провайдера, либо 0, если получить местоположение не удалось" + - name: getProviders + args: "enabledOnly = false" + desc: "returns an array of location providers" + desc_ru: "возвращает массив провайдеров местоположения" + - name: "requestUpdates" + args: "provider, minTime, minDistance, callback" + desc: |- + subscribes to the location listener + desc_ru: |- + подписывается на обработчик получения местоположения + example: |- + use std, gps + + provider = "gps" // or passive, network if exists + // requestUpdates(provider, 0, 25, def(loc) = echo("location changed: ", loc)) + requestUpdates(provider, 10 * 1000, 25, { + "onLocationChanged" : def(loc) = echo("location changed: ", loc) + "onStatusChanged" : def(p, status) = echo("status changed: ", p, " is ", getStatus(status)) + "onProviderEnabled" : def(p) = echo("provider ", p, " is now enabled") + "onProviderDisabled" : def(p) = echo("provider ", p, " is now disabled") + }) diff --git a/docs/src/modules/gzip.yml b/docs/src/modules/gzip.yml new file mode 100644 index 00000000..af290dd9 --- /dev/null +++ b/docs/src/modules/gzip.yml @@ -0,0 +1,43 @@ +name: gzip +since: 1.5.0 +scope: both +desc: "Contains functions for working with gzip compression" +desc_ru: "Содержит функции для работы с gzip компрессией" +constants: [] +functions: + - name: gzip + args: "inputFile, outputFile" + desc: |- + creates a gzip archive with the `inputFile` file and saves to `outputFile`. + Returns 1 if compression was successfull, -1 otherwise. + desc_ru: |- + создаёт gzip архив с файлом `inputFile` и сохраняет в `outputFile`. + Возвращает 1 если компрессия завершилась успешно, и -1 в противном случае. + example: |- + use gzip + gzip("/tmp/readme.md", "/tmp/readme.md.gz") + - name: gzipBytes + args: "bytes" + desc: returns gzip-compressed input bytes. + desc_ru: возвращает сжатый в gzip массив байт. + example: |- + use gzip + bytes = gzipBytes([0, 119, 87, 80/* ... */]) + - name: ungzip + args: "inputFile, outputFile" + desc: |- + unpacks a gzip archive to `outputFile` file. + Returns 1 if operation was successfull, -1 otherwise. + desc_ru: |- + распаковывает gzip архив в файл `outputFile`. + Возвращает 1 если операция завершилась успешно, и -1 в противном случае. + example: |- + use gzip + gzip("/tmp/readme.md.gz", "/tmp/readme.md") + - name: ungzipBytes + args: "bytes" + desc: returns uncompressed bytes. + desc_ru: возвращает распакованный gzip массив байт. + example: |- + use gzip + bytes = ungzipBytes([0, 119, 87, 80/* ... */]) \ No newline at end of file diff --git a/docs/src/modules/http.yml b/docs/src/modules/http.yml new file mode 100644 index 00000000..54c8bb71 --- /dev/null +++ b/docs/src/modules/http.yml @@ -0,0 +1,82 @@ +name: http +scope: "both" +desc: "Contains network functions" +desc_ru: "Содержит функции для взаимодействия с сетью" +constants: [] +functions: + - name: "http" + args: "url" + desc: |- + performs GET-request to `url`. + + `http(url, method)` - performs request with `method` (GET, POST, PUT, DELETE, PATCH, OPTIONS) to `url`. + + `http(url, callback)` - performs GET-request to `url`, response will be send to function `callback`. + + `http(url, method, params)` - performs request with given `method` and object `params` to `url`. + + `http(url, method, callback)` - performs request with given `method` to `url`, response will be send to function `callback`. + + `http(url, method, params, callback)` - performs request with given `method` and object `params` to `url`, response will be send to function `callback`. + + `http(url, method, params, options, callback)` - performs request with given `method`, object `params` and connection `options` to `url`, response will be send to function `callback`. + + Connection options is a object (map): + + - `header` - sets http-header (string or array). + - `encoded` - is `params` object already urlencoded. + - `content_type` - sets Content-Type. + - `extended_result` - marks that response should be extended and should contains: + - `text` - server response text + - `message` - server response message + - `code` - server response code + - `headers` - response http-header + - `content_length` - Content-Length + - `content_type` - Content-Type + desc_ru: |- + `http(url)` - выполняет GET-запрос на указанный адрес `url`. + + `http(url, method)` - выполняет запрос на указанный адрес `url` методом method (GET, POST, PUT, DELETE, PATCH, OPTIONS). + + `http(url, callback)` - выполняет GET-запрос на указанный адрес `url`, ответ сервера передаёт в функцию `callback`. + + `http(url, method, params)` - выполняет запрос на указанный адрес `url`, методом `method` c данными `params` (объект). + + `http(url, method, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, ответ сервера передаёт в функцию `callback`. + + `http(url, method, params, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, с данными `params`, ответ сервера передаёт в функцию `callback`. + + `http(url, method, params, options, callback)` - выполняет запрос на указанный адрес `url`, методом `method`, с данными `params`, параметрами подключения `options`, ответ сервера передаёт в функцию `callback`. + + Параметрами подключения выступает объект. Допустимы следующие параметры + + - `header` - задаёт http-заголовок, если передана строка или несколько заголовков, если массив. + - `encoded` - указывает, что данные `params` уже закодированы в URL-формате. + - `content_type` - указывает Content-Type. + - `extended_result` - указывает, что ответ сервера нужно вернуть в расширенном виде, а именно объектом с данными: + - `text` - текст ответа сервера + - `message` - сообщение сервера + - `code` - код ответа сервера + - `headers` - http-заголовки ответа + - `content_length` - Content-Length + - `content_type` - Content-Type + example: |- + use http + http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { + println "Added: " + v + }) + - name: "download" + args: "url" + desc: "downloads content by url as bytes array" + desc_ru: "получает содержимое по указанному адресу в виде массива байт" + example: |- + use http, files + bytes = download("http://url") + f = fopen("file", "wb") + writeBytes(f, bytes) + flush(f) + fclose(f) + - name: "urlencode" + args: "str" + desc: "converts string to URL-format" + desc_ru: "преобразует строку в URL-формат" \ No newline at end of file diff --git a/docs/src/modules/imageprocessing_android.yml b/docs/src/modules/imageprocessing_android.yml new file mode 100644 index 00000000..f0354425 --- /dev/null +++ b/docs/src/modules/imageprocessing_android.yml @@ -0,0 +1,109 @@ +name: imageprocessing +scope: "android" +desc: |- + Contains functions for image processing. + + You can apply effect in two ways: + + 1. Pass BitmapValue and parameters array. The result will be a BitmapValue. `bitmap = boxBlur(bitmap, [20, 40])` + 2. Pass width, height, pixels array and parameters array. The result will be an array [width, height, pixels]. `extract(width, height, pixels) = boxBlur(w, h, pixels, [20, 40])` +desc_ru: |- + Содержит функции для обработки изображений. + + Применить эффект можно двумя способами: + + 1. Передать BitmapValue и массив параметров. Результатом будет BitmapValue. `bitmap = boxBlur(bitmap, [20, 40])` + 2. Передать ширину, высоту, массив пикселей и массив параметров. Результатом будет массив [ширина, высота, пиксели]. `extract(width, height, pixels) = boxBlur(w, h, pixels, [20, 40])` +functions: + - name: "boxBlur" + args: "horizontalBlur = 10 (min 1, max 100), verticalBlur = 10 (min 1, max 100)" + desc: "applies quick blur effect" + desc_ru: "применяет быстрый эффект размытия" + - name: "contrast" + args: "level = 40 (min -100, max 100)" + desc: "changes contrast of the image" + desc_ru: "изменяет контрастность изображения" + - name: "decolour" + args: "" + desc: "converts color image to grayscale" + desc_ru: "преобразует цветное изображение в оттенки серого" + - name: "edgeDetection" + args: "operator = 1, mode = 0" + desc: |- + applies edge detection effect. + + `operator` 0 - Roberts, 1 - Prewitt, 2 - Sobel, 3 - Scharr + `mode` 0 - color edges, 1 - gray edges, 2 - subtract edges + desc_ru: |- + применяет эффект выделения границ. + + `operator` 0 - оператор Робертса, 1 - Прюитт, 2 - Собеля, 3 - Шарра + `mode` 0 - цветные грани, 1 - чёрно-белые грани, 2 - вычитание границ + - name: "emboss" + args: "azimuth = 45 (min 0, max 360), elevation = 45 (min 0, max 90), edgeHeight = 140 (min 0, max 256), edgeThickness = 80 (min 2, max 100), emboss = 0 (min 0, max 1)" + desc: "applies emboss effect" + desc_ru: "применяет эффект выдавливания" + - name: "extractChannel" + args: "channel = 0, monochrome = 0" + desc: |- + extracts given channel from image. + + `channel` 0 - red, 1 - green, 2 - blue + `monochrome` 0 - off, 1 - on + desc_ru: |- + извлекает заданный канал из изображения. + + `channel` 0 - красный, 1 - зелёный, 2 - синий + `monochrome` конвертировать полученную маску в чёрно-белый, 0 - нет, 1 - да + - name: "gamma" + args: "level = 20 (min -50, max 50)" + desc: "changes gamma of the image" + desc_ru: "изменяет гамму изображения" + - name: "hsbCorrection" + args: "hue = 45 (min 0, max 360), saturation = 0 (min -100, max 100), brightness = 0 (min -100, max 100), tone = 0 (min 0, max 1)" + desc: "changes hue, saturation and brightness of the image" + desc_ru: "изменяет оттенок, насыщенность и яркость изображения, тонирует при `tone` = 1" + - name: "invert" + args: "invertAlpha = 0, invertRed = 1, invertGreen = 2, invertBlue = 3" + desc: "inverts channels of the image" + desc_ru: "инвертирует заданные каналы изображения" + - name: "monochrome" + args: "level = 128 (min 0, max 255)" + desc: "converts color image to monochrome" + desc_ru: "преобразует цветное изображение в монохромное" + - name: "mosaic" + args: "size = 4 (min 1, max 50)" + desc: "applies mosaic effect" + desc_ru: "применяет эффект мозайки" + - name: "noiseGeneration" + args: "amount = 50 (min 0, max 255), monochrome = 0" + desc: "adds noise to images" + desc_ru: "добавляет шум к изображению" + - name: "posterization" + args: "level = 64 (min 1, max 255)" + desc: "applies posterization effect" + desc_ru: "применяет эффект постеризации" + - name: "rgbCorrection" + args: "alpha = 0 (min -255, max 255), red = 0 (min -255, max 255), green = 0 (min -255, max 255), blue = 0 (min -255, max 255)" + desc: "changes alpha, red, green and blue channels of the image" + desc_ru: "изменяет прозрачность, красный, зелёный, синий каналы изображения" + - name: "rotate" + args: "angle = 45 (min 0, max 360)" + desc: "rotates image" + desc_ru: "поворачивает изображение" + - name: "saturation" + args: "level = 64 (min -255, max 255)" + desc: "changes saturation of the image" + desc_ru: "изменяет насыщенность изображения" + - name: "scatter" + args: "horizontalScatter = 10 (min 1, max 100), verticalScatter = 10 (min 1, max 100)" + desc: "applies pixel scatter effect" + desc_ru: "применяет эффект рассеивания пикселей" + - name: "smooth" + args: "level = 3 (min 1, max 25)" + desc: "applies smooth effect" + desc_ru: "применяет эффект сглаживания" + - name: "xor" + args: "level = 64 (min 0, max 255)" + desc: "applies xor operation for each pixel of the image" + desc_ru: "применяет операцию ИСКЛЮЧАЮЩЕЕ ИЛИ для каждого пикселя изображения" \ No newline at end of file diff --git a/docs/src/modules/java.yml b/docs/src/modules/java.yml new file mode 100644 index 00000000..4488b821 --- /dev/null +++ b/docs/src/modules/java.yml @@ -0,0 +1,253 @@ +name: java +scope: both +constants: + - name: Object.class + type: 4 + typeName: map + value: 'java.lang.Object' + - name: Object[].class + type: 4 + typeName: map + value: 'java.lang.Object[]' + - name: Object[][].class + type: 4 + typeName: map + value: 'java.lang.Object[][]' + - name: String.class + type: 4 + typeName: map + value: 'java.lang.String' + - name: String[].class + type: 4 + typeName: map + value: 'java.lang.String[]' + - name: String[][].class + type: 4 + typeName: map + value: 'java.lang.String[][]' + - name: boolean.class + type: 4 + typeName: map + value: 'boolean' + - name: boolean[].class + type: 4 + typeName: map + value: 'boolean[]' + - name: boolean[][].class + type: 4 + typeName: map + value: 'boolean[][]' + - name: byte.class + type: 4 + typeName: map + value: 'byte' + - name: byte[].class + type: 4 + typeName: map + value: 'byte[]' + - name: byte[][].class + type: 4 + typeName: map + value: 'byte[][]' + - name: char.class + type: 4 + typeName: map + value: 'char' + - name: char[].class + type: 4 + typeName: map + value: 'char[]' + - name: char[][].class + type: 4 + typeName: map + value: 'char[][]' + - name: double.class + type: 4 + typeName: map + value: 'double' + - name: double[].class + type: 4 + typeName: map + value: 'double[]' + - name: double[][].class + type: 4 + typeName: map + value: 'double[][]' + - name: float.class + type: 4 + typeName: map + value: 'float' + - name: float[].class + type: 4 + typeName: map + value: 'float[]' + - name: float[][].class + type: 4 + typeName: map + value: 'float[][]' + - name: int.class + type: 4 + typeName: map + value: 'int' + - name: int[].class + type: 4 + typeName: map + value: 'int[]' + - name: int[][].class + type: 4 + typeName: map + value: 'int[][]' + - name: long.class + type: 4 + typeName: map + value: 'long' + - name: long[].class + type: 4 + typeName: map + value: 'long[]' + - name: long[][].class + type: 4 + typeName: map + value: 'long[][]' + - name: 'null' + type: 482862660 + typeName: unknown (482862660) + value: 'null' + - name: short.class + type: 4 + typeName: map + value: 'short' + - name: short[].class + type: 4 + typeName: map + value: 'short[]' + - name: short[][].class + type: 4 + typeName: map + value: 'short[][]' +functions: + - name: isNull + args: 'values...' + desc: 'checks if one or more values are null' + desc_ru: 'проверяет, является ли одно или несколько значений null' + - name: newClass + args: 'name' + desc: 'creates ClassValue' + desc_ru: 'создаёт ClassValue' + - name: toObject + args: 'ownlangValue' + desc: 'converts ownlangValue to Java object' + desc_ru: 'конвертирует ownlangValue в Java объект' + - name: toValue + args: 'javaObject' + desc: 'converts javaObject to OwnLang value' + desc_ru: 'конвертирует javaObject в OwnLang значение' +types: + - name: ClassValue + functions: + - name: "asSubclass" + args: "" + desc: "" + desc_ru: "" + - name: "canonicalName" + args: "" + desc: "" + desc_ru: "" + - name: "cast" + args: "" + desc: "" + desc_ru: "" + - name: "genericString" + args: "" + desc: "" + desc_ru: "" + - name: "getComponentType" + args: "" + desc: "" + desc_ru: "" + - name: "getDeclaringClass" + args: "" + desc: "" + desc_ru: "" + - name: "getEnclosingClass" + args: "" + desc: "" + desc_ru: "" + - name: "getSuperclass" + args: "" + desc: "" + desc_ru: "" + - name: "getClasses" + args: "" + desc: "" + desc_ru: "" + - name: "getDeclaredClasses" + args: "" + desc: "" + desc_ru: "" + - name: "getInterfaces" + args: "" + desc: "" + desc_ru: "" + - name: "isAnnotation" + args: "" + desc: "" + desc_ru: "" + - name: "isAnonymousClass" + args: "" + desc: "" + desc_ru: "" + - name: "isArray" + args: "" + desc: "" + desc_ru: "" + - name: "isAssignableFrom" + args: "" + desc: "" + desc_ru: "" + - name: "isEnum" + args: "" + desc: "" + desc_ru: "" + - name: "isInterface" + args: "" + desc: "" + desc_ru: "" + - name: "isLocalClass" + args: "" + desc: "" + desc_ru: "" + - name: "isMemberClass" + args: "" + desc: "" + desc_ru: "" + - name: "isPrimitive" + args: "" + desc: "" + desc_ru: "" + - name: "isSynthetic" + args: "" + desc: "" + desc_ru: "" + - name: "modifiers" + args: "" + desc: "" + desc_ru: "" + - name: "name" + args: "" + desc: "" + desc_ru: "" + - name: "new" + args: "" + desc: "creates new instance of class" + desc_ru: "создаёт новый экземпляр класса" + - name: "simpleName" + args: "" + desc: "" + desc_ru: "" + - name: "typeName" + args: "" + desc: "" + desc_ru: "" + - name: NullValue + - name: ObjectValue \ No newline at end of file diff --git a/docs/src/modules/jdbc.yml b/docs/src/modules/jdbc.yml new file mode 100644 index 00000000..04e79833 --- /dev/null +++ b/docs/src/modules/jdbc.yml @@ -0,0 +1,685 @@ +name: jdbc +scope: desktop +constants: + - name: CLOSE_ALL_RESULTS + type: 1 + typeName: number + value: '3' + - name: CLOSE_CURRENT_RESULT + type: 1 + typeName: number + value: '1' + - name: CLOSE_CURSORS_AT_COMMIT + type: 1 + typeName: number + value: '2' + - name: CONCUR_READ_ONLY + type: 1 + typeName: number + value: '1007' + - name: CONCUR_UPDATABLE + type: 1 + typeName: number + value: '1008' + - name: EXECUTE_FAILED + type: 1 + typeName: number + value: '-3' + - name: FETCH_FORWARD + type: 1 + typeName: number + value: '1000' + - name: FETCH_REVERSE + type: 1 + typeName: number + value: '1001' + - name: FETCH_UNKNOWN + type: 1 + typeName: number + value: '1002' + - name: HOLD_CURSORS_OVER_COMMIT + type: 1 + typeName: number + value: '1' + - name: KEEP_CURRENT_RESULT + type: 1 + typeName: number + value: '2' + - name: NO_GENERATED_KEYS + type: 1 + typeName: number + value: '2' + - name: RETURN_GENERATED_KEYS + type: 1 + typeName: number + value: '1' + - name: SUCCESS_NO_INFO + type: 1 + typeName: number + value: '-2' + - name: TRANSACTION_NONE + type: 1 + typeName: number + value: '0' + - name: TRANSACTION_READ_COMMITTED + type: 1 + typeName: number + value: '2' + - name: TRANSACTION_READ_UNCOMMITTED + type: 1 + typeName: number + value: '1' + - name: TRANSACTION_REPEATABLE_READ + type: 1 + typeName: number + value: '4' + - name: TRANSACTION_SERIALIZABLE + type: 1 + typeName: number + value: '8' + - name: TYPE_FORWARD_ONLY + type: 1 + typeName: number + value: '1003' + - name: TYPE_SCROLL_INSENSITIVE + type: 1 + typeName: number + value: '1004' + - name: TYPE_SCROLL_SENSITIVE + type: 1 + typeName: number + value: '1005' +functions: + - name: getConnection + args: '...' + desc: |- + `getConnection(connectionUrl)` + + `getConnection(connectionUrl, driverClassName)` + + `getConnection(connectionUrl, user, password)` + + `getConnection(connectionUrl, user, password, driverClassName)` + + Creates connection and returns ConnectionValue. + desc_ru: |- + `getConnection(connectionUrl)` + + `getConnection(connectionUrl, driverClassName)` + + `getConnection(connectionUrl, user, password)` + + `getConnection(connectionUrl, user, password, driverClassName)` + + Создаёт подключение и возвращает ConnectionValue. + - name: mysql + args: 'connectionUrl' + desc: 'creates mysql connection' + desc_ru: 'создаёт mysql подключение' + - name: sqlite + args: 'connectionUrl' + desc: 'creates sqlite connection' + desc_ru: 'создаёт sqlite подключение' +types: + - name: ConnectionValue + functions: + - name: "clearWarnings" + args: "" + desc: "" + desc_ru: "" + - name: "close" + args: "" + desc: "" + desc_ru: "" + - name: "commit" + args: "" + desc: "" + desc_ru: "" + - name: "createStatement" + args: "" + desc: "" + desc_ru: "" + - name: "getAutoCommit" + args: "" + desc: "" + desc_ru: "" + - name: "getCatalog" + args: "" + desc: "" + desc_ru: "" + - name: "getHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "getNetworkTimeout" + args: "" + desc: "" + desc_ru: "" + - name: "getSchema" + args: "" + desc: "" + desc_ru: "" + - name: "getTransactionIsolation" + args: "" + desc: "" + desc_ru: "" + - name: "getUpdateCount" + args: "" + desc: "" + desc_ru: "" + - name: "isClosed" + args: "" + desc: "" + desc_ru: "" + - name: "isReadOnly" + args: "" + desc: "" + desc_ru: "" + - name: "prepareStatement" + args: "" + desc: "" + desc_ru: "" + - name: "rollback" + args: "" + desc: "" + desc_ru: "" + - name: "setHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "setTransactionIsolation" + args: "" + desc: "" + desc_ru: "" + - name: ResultSetValue + functions: + - name: "absolute" + args: "" + desc: "" + desc_ru: "" + - name: "afterLast" + args: "" + desc: "" + desc_ru: "" + - name: "beforeFirst" + args: "" + desc: "" + desc_ru: "" + - name: "cancelRowUpdates" + args: "" + desc: "" + desc_ru: "" + - name: "clearWarnings" + args: "" + desc: "" + desc_ru: "" + - name: "close" + args: "" + desc: "" + desc_ru: "" + - name: "deleteRow" + args: "" + desc: "" + desc_ru: "" + - name: "findColumn" + args: "" + desc: "" + desc_ru: "" + - name: "first" + args: "" + desc: "" + desc_ru: "" + - name: "getArray" + args: "" + desc: "" + desc_ru: "" + - name: "getBigDecimal" + args: "" + desc: "" + desc_ru: "" + - name: "getBoolean" + args: "" + desc: "" + desc_ru: "" + - name: "getByte" + args: "" + desc: "" + desc_ru: "" + - name: "getBytes" + args: "" + desc: "" + desc_ru: "" + - name: "getConcurrency" + args: "" + desc: "" + desc_ru: "" + - name: "getCursorName" + args: "" + desc: "" + desc_ru: "" + - name: "getDate" + args: "" + desc: "" + desc_ru: "" + - name: "getDouble" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "getFloat" + args: "" + desc: "" + desc_ru: "" + - name: "getHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "getInt" + args: "" + desc: "" + desc_ru: "" + - name: "getLong" + args: "" + desc: "" + desc_ru: "" + - name: "getNString" + args: "" + desc: "" + desc_ru: "" + - name: "getRow" + args: "" + desc: "" + desc_ru: "" + - name: "getRowId" + args: "" + desc: "" + desc_ru: "" + - name: "getShort" + args: "" + desc: "" + desc_ru: "" + - name: "getStatement" + args: "" + desc: "" + desc_ru: "" + - name: "getString" + args: "" + desc: "" + desc_ru: "" + - name: "getTime" + args: "" + desc: "" + desc_ru: "" + - name: "getTimestamp" + args: "" + desc: "" + desc_ru: "" + - name: "getType" + args: "" + desc: "" + desc_ru: "" + - name: "getURL" + args: "" + desc: "" + desc_ru: "" + - name: "insertRow" + args: "" + desc: "" + desc_ru: "" + - name: "isAfterLast" + args: "" + desc: "" + desc_ru: "" + - name: "isBeforeFirst" + args: "" + desc: "" + desc_ru: "" + - name: "isClosed" + args: "" + desc: "" + desc_ru: "" + - name: "isFirst" + args: "" + desc: "" + desc_ru: "" + - name: "isLast" + args: "" + desc: "" + desc_ru: "" + - name: "last" + args: "" + desc: "" + desc_ru: "" + - name: "moveToCurrentRow" + args: "" + desc: "" + desc_ru: "" + - name: "moveToInsertRow" + args: "" + desc: "" + desc_ru: "" + - name: "next" + args: "" + desc: "" + desc_ru: "" + - name: "previous" + args: "" + desc: "" + desc_ru: "" + - name: "refreshRow" + args: "" + desc: "" + desc_ru: "" + - name: "relative" + args: "" + desc: "" + desc_ru: "" + - name: "rowDeleted" + args: "" + desc: "" + desc_ru: "" + - name: "rowInserted" + args: "" + desc: "" + desc_ru: "" + - name: "rowUpdated" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "updateBigDecimal" + args: "" + desc: "" + desc_ru: "" + - name: "updateBoolean" + args: "" + desc: "" + desc_ru: "" + - name: "updateByte" + args: "" + desc: "" + desc_ru: "" + - name: "updateBytes" + args: "" + desc: "" + desc_ru: "" + - name: "updateDate" + args: "" + desc: "" + desc_ru: "" + - name: "updateDouble" + args: "" + desc: "" + desc_ru: "" + - name: "updateFloat" + args: "" + desc: "" + desc_ru: "" + - name: "updateInt" + args: "" + desc: "" + desc_ru: "" + - name: "updateLong" + args: "" + desc: "" + desc_ru: "" + - name: "updateNString" + args: "" + desc: "" + desc_ru: "" + - name: "updateNull" + args: "" + desc: "" + desc_ru: "" + - name: "updateRow" + args: "" + desc: "" + desc_ru: "" + - name: "updateShort" + args: "" + desc: "" + desc_ru: "" + - name: "updateString" + args: "" + desc: "" + desc_ru: "" + - name: "updateTime" + args: "" + desc: "" + desc_ru: "" + - name: "updateTimestamp" + args: "" + desc: "" + desc_ru: "" + - name: "wasNull" + args: "" + desc: "" + desc_ru: "" + - name: StatementValue + functions: + - name: "addBatch" + args: "" + desc: "" + desc_ru: "" + - name: "cancel" + args: "" + desc: "" + desc_ru: "" + - name: "clearBatch" + args: "" + desc: "" + desc_ru: "" + - name: "clearParameters" + args: "" + desc: "" + desc_ru: "" + - name: "clearWarnings" + args: "" + desc: "" + desc_ru: "" + - name: "close" + args: "" + desc: "" + desc_ru: "" + - name: "closeOnCompletion" + args: "" + desc: "" + desc_ru: "" + - name: "execute" + args: "" + desc: "" + desc_ru: "" + - name: "executeBatch" + args: "" + desc: "" + desc_ru: "" + - name: "executeLargeBatch" + args: "" + desc: "" + desc_ru: "" + - name: "executeLargeUpdate" + args: "" + desc: "" + desc_ru: "" + - name: "executeQuery" + args: "" + desc: "" + desc_ru: "" + - name: "executeUpdate" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "getFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "getGeneratedKeys" + args: "" + desc: "" + desc_ru: "" + - name: "getMaxFieldSize" + args: "" + desc: "" + desc_ru: "" + - name: "getMaxRows" + args: "" + desc: "" + desc_ru: "" + - name: "getMoreResults" + args: "" + desc: "" + desc_ru: "" + - name: "getQueryTimeout" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSet" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSetConcurrency" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSetHoldability" + args: "" + desc: "" + desc_ru: "" + - name: "getResultSetType" + args: "" + desc: "" + desc_ru: "" + - name: "getUpdateCount" + args: "" + desc: "" + desc_ru: "" + - name: "isCloseOnCompletion" + args: "" + desc: "" + desc_ru: "" + - name: "isClosed" + args: "" + desc: "" + desc_ru: "" + - name: "isPoolable" + args: "" + desc: "" + desc_ru: "" + - name: "setBigDecimal" + args: "" + desc: "" + desc_ru: "" + - name: "setBoolean" + args: "" + desc: "" + desc_ru: "" + - name: "setByte" + args: "" + desc: "" + desc_ru: "" + - name: "setBytes" + args: "" + desc: "" + desc_ru: "" + - name: "setCursorName" + args: "" + desc: "" + desc_ru: "" + - name: "setDate" + args: "" + desc: "" + desc_ru: "" + - name: "setDouble" + args: "" + desc: "" + desc_ru: "" + - name: "setEscapeProcessing" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchDirection" + args: "" + desc: "" + desc_ru: "" + - name: "setFetchSize" + args: "" + desc: "" + desc_ru: "" + - name: "setFloat" + args: "" + desc: "" + desc_ru: "" + - name: "setInt" + args: "" + desc: "" + desc_ru: "" + - name: "setLargeMaxRows" + args: "" + desc: "" + desc_ru: "" + - name: "setLong" + args: "" + desc: "" + desc_ru: "" + - name: "setMaxFieldSize" + args: "" + desc: "" + desc_ru: "" + - name: "setMaxRows" + args: "" + desc: "" + desc_ru: "" + - name: "setNString" + args: "" + desc: "" + desc_ru: "" + - name: "setNull" + args: "" + desc: "" + desc_ru: "" + - name: "setPoolable" + args: "" + desc: "" + desc_ru: "" + - name: "setQueryTimeout" + args: "" + desc: "" + desc_ru: "" + - name: "setShort" + args: "" + desc: "" + desc_ru: "" + - name: "setString" + args: "" + desc: "" + desc_ru: "" + - name: "setTime" + args: "" + desc: "" + desc_ru: "" + - name: "setTimestamp" + args: "" + desc: "" + desc_ru: "" + - name: "setURL" + args: "" + desc: "" + desc_ru: "" \ No newline at end of file diff --git a/docs/src/modules/json.yml b/docs/src/modules/json.yml new file mode 100644 index 00000000..3bd555db --- /dev/null +++ b/docs/src/modules/json.yml @@ -0,0 +1,25 @@ +name: json +scope: "both" +desc: "Contains functions for working with the json format" +desc_ru: "Содержит функции преобразования данных в формат json и наоборот" +constants: [] +functions: + - name: "jsondecode" + args: "data" + desc: "converts data to json string" + desc_ru: "преобразует переданные данные в строку в формате json" + example: |- + use json + print jsondecode("{\"key1\":1,\"key2\":[1,2,3],\"key3\":\"text\"}") // {key2=[1, 2, 3], key3=text, key1=1} + - name: "jsonencode" + args: "jsonString, indent = 0" + desc: "converts string to data" + desc_ru: "преобразует строку в формате json в данные" + example: |- + use json + data = { + "key1": 1, + "key2": [1, 2, 3], + "key3": "text" + } + print jsonencode(data) // {"key1":1,"key2":[1,2,3],"key3":"text"} \ No newline at end of file diff --git a/docs/src/modules/math.yml b/docs/src/modules/math.yml new file mode 100644 index 00000000..643a8f2b --- /dev/null +++ b/docs/src/modules/math.yml @@ -0,0 +1,162 @@ +name: math +scope: "both" +desc: "Contains math functions and constants" +desc_ru: "Содержит математические функции и константы" +constants: + - name: "E" + typeName: number + type: 1 + value: "2.718281828459045" + - name: "PI" + typeName: number + type: 1 + value: "3.141592653589793" +functions: + - name: "abs" + args: "x" + desc: "absolute value of `x`" + desc_ru: "модуль числа `x`" + - name: "acos" + args: "x" + desc: "arc cosine" + desc_ru: "арккосинус" + - name: "asin" + args: "x" + desc: "arc sine" + desc_ru: "арксинус" + - name: "atan" + args: "x" + desc: "arc tangent" + desc_ru: "арктангенс" + - name: "atan2" + args: "y, x" + desc: "returns angle θ whose tangent is the ratio of two numbers" + desc_ru: "угол θ, тангенс которого равен отношению двух указанных чисел" + - name: "cbrt" + args: "x" + desc: "cube root" + desc_ru: "кубический корень числа x" + - name: "ceil" + args: "x" + desc: "returns the ceiling of `x`" + desc_ru: "округляет вещественное число в большую сторону" + example: |- + use math + + ceil(6.4) // 7 + - name: "copySign" + args: "magnitude, sign" + desc: "returns a value with the magnitude of x and the sign of y" + desc_ru: "возвращает значение с величиной x и знаком y" + - name: "cos" + args: "x" + desc: "trigonometric cosine" + desc_ru: "косинус" + - name: "cosh" + args: "x" + desc: "hyperbolic cosine" + desc_ru: "гиперболический косинус" + - name: "exp" + args: "x" + desc: "ex" + desc_ru: "ex" + - name: "expm1" + args: "x" + desc: "ex-1" + desc_ru: "ex-1" + - name: "floor" + args: "x" + desc: "returns floor of `x`" + desc_ru: "округляет вещественное число в меньшую сторону" + example: |- + use math + + floor(3.8) // 3 + - name: "getExponent" + args: "x" + desc: "returns the unbiased exponent used in the representation of a double or float" + desc_ru: "возвращают несмещенное значение экспоненты числа" + - name: "hypot" + args: "x, y" + desc: "returns the square root of the sum of squares of its arguments" + desc_ru: "расчёт гипотенузы sqrt(x2 + y2) без переполнения" + - name: "IEEEremainder" + args: "x, y" + desc: "returns the remainder resulting from the division of a specified number by another specified number. This operation complies with the remainder operation defined in Section 5.1 of ANSI/IEEE Std 754-1985; IEEE Standard for Binary Floating-Point Arithmetic; Institute of Electrical and Electronics Engineers, Inc; 1985." + desc_ru: "возвращает остаток от деления x на y по стандарту ANSI/IEEE Std 754-1985, раздел 5.1" + - name: "log" + args: "x" + desc: "returns the logarithm of a specified number" + desc_ru: "логарифм" + - name: "log1p" + args: "x" + desc: "" + desc_ru: "натуральный логарифм от x + 1 (`ln(x + 1)`)" + - name: "log10" + args: "x" + desc: "returns the base 10 logarithm of a specified number" + desc_ru: "десятичный логарифм" + - name: "max" + args: "x, y" + desc: "returns the larger of two specified numbers" + desc_ru: "максимальное из двух чисел" + - name: "min" + args: "x, y" + desc: "returns the smaller of two numbers" + desc_ru: "минимальное из двух чисел" + - name: "nextAfter" + args: "x, y" + desc: "" + desc_ru: "" + - name: "nextUp" + args: "x" + desc: "" + desc_ru: "" + - name: "pow" + args: "x, y" + desc: "returns a specified number raised to the specified power" + desc_ru: "возведение x в степень y" + - name: "rint" + args: "x" + desc: "" + desc_ru: "" + - name: "round" + args: "x" + desc: "rounds a value to the nearest integer or to the specified number of fractional digits" + desc_ru: "округляет вещественное число до ближайшего целого" + - name: "signum" + args: "x" + desc: "returns an integer that indicates the sign of a number" + desc_ru: "возвращает целое число, указывающее знак числа" + - name: "sin" + args: "x" + desc: "" + desc_ru: "синус" + - name: "sinh" + args: "x" + desc: "" + desc_ru: "гиперболический синус" + - name: "sqrt" + args: "x" + desc: "" + desc_ru: "квадратный корень" + - name: "tan" + args: "x" + desc: "" + desc_ru: "тангенс" + - name: "tanh" + args: "x" + desc: "" + desc_ru: "гиперболический тангенс" + - name: "toDegrees" + args: "x" + desc: "" + desc_ru: "перевод радиан в градусы" + - name: "toRadians" + args: "x" + desc: "" + desc_ru: "перевод градусов в радианы" + - name: "ulp" + args: "x" + desc: "" + desc_ru: "" \ No newline at end of file diff --git a/docs/src/modules/ounit.yml b/docs/src/modules/ounit.yml new file mode 100644 index 00000000..85f49d9a --- /dev/null +++ b/docs/src/modules/ounit.yml @@ -0,0 +1,60 @@ +name: ounit +scope: "both" +desc: "Contains functions for testing. Invokes all functions with prefix `test` and checks expected and actual values, counts execution time" +desc_ru: "Содержит функции для тестирования. Поочерёдно вызывает все функции программы, которые имеют приставку `test` и подсчитывает время выполнение и расхождения с ожидаемыми значениями" +constants: [] +functions: + - name: "assertEquals" + args: "expected, actual" + desc: "checks that two values are equal" + desc_ru: "проверяет, равны ли два значения" + - name: "assertFalse" + args: "actual" + desc: "checks that value is false (equals 0)" + desc_ru: "проверяет, является ли значение ложным (равным нулю)" + - name: "assertNotEquals" + args: "expected, actual" + desc: "checks that two values are not equal" + desc_ru: "проверяет, отличаются ли два значения" + - name: "assertSameType" + args: "expected, actual" + desc: "checks that types of two values are equal" + desc_ru: "проверяет, одинаковы ли типы у двух значений" + - name: "assertTrue" + args: "actual" + desc: "checks that value is true (not equals 0)" + desc_ru: "проверяет, является ли значение истинным (не равным нулю)" + - name: "runTests" + args: "" + desc: "executes tests and returns information about it's results" + desc_ru: "запускает тесты и возвращает информацию о них по завершению работы в виде строки" + example: |- + use ounit + + def testAdditionOnNumbers() { + assertEquals(6, 0 + 1 + 2 + 3) + } + + def testTypes() { + assertSameType(0, 0.0) + } + + def testFail() { + assertTrue(false) + } + + println runTests() + + /* + testTypes [passed] + Elapsed: 0,0189 sec + + testAdditionOnNumbers [passed] + Elapsed: 0,0008 sec + + testFail [FAILED] + Expected true, but found false. + Elapsed: 0,0001 sec + + Tests run: 3, Failures: 1, Time elapsed: 0,0198 sec + */ \ No newline at end of file diff --git a/docs/src/modules/regex.yml b/docs/src/modules/regex.yml new file mode 100644 index 00000000..057d5e15 --- /dev/null +++ b/docs/src/modules/regex.yml @@ -0,0 +1,147 @@ +name: regex +since: 1.4.0 +constants: + - name: Pattern + type: 4 + typeName: map + value: "{UNIX_LINES=1, CASE_INSENSITIVE=2, I=2, COMMENTS=4, MULTILINE=8, M=8, LITERAL=16, S=32, DOTALL=32, UNICODE_CASE=64, CANON_EQ=128, UNICODE_CHARACTER_CLASS=256, U=256, quote=def(s) { return string }, matches=def(str,pattern) { return boolean }, split=def(str,pattern,limit = 0) { return array }, compile=def(pattern,flags = 0) { return PatternValue }}" +functions: + - name: regex + args: 'pattern, flags = 0' + desc: 'creates pattern and returns PatternValue' + desc_ru: 'создаёт шаблон регулярного выражения и возвращает PatternValue' +types: + - name: PatternValue + functions: + - name: "flags" + args: "" + desc: "returns pattern flags" + desc_ru: "возвращает флаги шаблона" + - name: "pattern" + args: "" + desc: "returns pattern as string" + desc_ru: "возвращает шаблон в виде строки" + - name: "matcher" + args: "input" + desc: "returns MatcherValue" + desc_ru: "возвращает MatcherValue" + - name: "matches" + args: "input" + desc: "checks if input matches the pattern" + desc_ru: "проверяет, соответствует ли входная строка шаблону" + - name: "split" + args: "input, limit = 0" + desc: "splits input around matches of this pattern" + desc_ru: "разбивает строку на основе совпадений шаблона" + - name: "replaceCallback" + args: "input, callback" + desc: "replaces input with the result of the given callback" + desc_ru: "заменяет строку результатом заданной функции" + example: |- + use regex + in = "dog cat" + pattern = regex("(\w+)\s(\w+)", Pattern.I) + println pattern.replaceCallback(in, def(m) = m.group(2) + "" + m.group(1)) + example_ru: |- + use regex + in = "пёс кот" + pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) + println pattern.replaceCallback(in, def(m) = m.group(2) + "о" + m.group(1)) + - name: MatcherValue + functions: + - name: "start" + args: "group = ..." + desc: "returns the start index of matched subsequence" + desc_ru: "возвращает начальную позицию найденного совпадения" + - name: "end" + args: "group = ..." + desc: "returns the offset after last character of matched subsequence" + desc_ru: "возвращает позицию, следующую за последним символов найденного совпадения" + - name: "find" + args: "start = 0" + desc: "resets this matcher and attempts to find the next matched subsequence" + desc_ru: "сбрасывает состояние матчера и пытается найти следующее совпадение" + - name: "group" + args: "group = 0" + desc: "returns matched group" + desc_ru: "возвращает найденную группу" + - name: "pattern" + args: "" + desc: "returns the pattern" + desc_ru: "возвращает шаблон" + - name: "region" + args: "start, end" + desc: "sets the limits of this matcher's region" + desc_ru: "задаёт ограничения для текущего региона" + - name: "replaceFirst" + args: "replacement" + desc: "replaces first matched subsequence with the given replacement string" + desc_ru: "заменяет первое совпадение на заданную строку" + - name: "replaceAll" + args: "replacement" + desc: "replaces all matched subsequences with the given replacement string" + desc_ru: "заменяет все совпадения на заданную строку" + - name: "replaceCallback" + args: "callback" + desc: "replaces input with the result of the given callback" + desc_ru: "заменяет строку результатом заданной функции" + example: |- + use regex + in = "dog cat" + pattern = regex("(\w+)\s(\w+)", Pattern.I) + matcher = pattern.matcher(in) + println matcher.replaceCallback(def(m) = m.group(2) + m.group(1)) + example_ru: |- + use regex + in = "пёс кот" + pattern = regex("(\w+)\s(\w+)", Pattern.U | Pattern.I) + matcher = pattern.matcher(in) + println matcher.replaceCallback(def(m) = m.group(2) + "о" + m.group(1)) + - name: "reset" + args: input = "" + desc: "" + desc_ru: "" + - name: "usePattern" + args: "patternvalue" + desc: "" + desc_ru: "" + - name: "useAnchoringBounds" + args: "status" + desc: "" + desc_ru: "" + - name: "hasAnchoringBounds" + args: "" + desc: "" + desc_ru: "" + - name: "useTransparentBounds" + args: "status" + desc: "" + desc_ru: "" + - name: "hasTransparentBounds" + args: "" + desc: "" + desc_ru: "" + - name: "hitEnd" + args: "" + desc: "" + desc_ru: "" + - name: "lookingAt" + args: "" + desc: "" + desc_ru: "" + - name: "matches" + args: "" + desc: "" + desc_ru: "" + - name: "groupCount" + args: "" + desc: "" + desc_ru: "" + - name: "regionStart" + args: "" + desc: "" + desc_ru: "" + - name: "regionEnd" + args: "" + desc: "" + desc_ru: "" \ No newline at end of file diff --git a/docs/src/modules/robot.yml b/docs/src/modules/robot.yml new file mode 100644 index 00000000..9cf8e764 --- /dev/null +++ b/docs/src/modules/robot.yml @@ -0,0 +1,139 @@ +name: robot +scope: "both" +desc: "Contains functions for working with clipboard, processes, automation" +desc_ru: "Содержит функции для работы с буфером обмена, процессами, автоматизацией" +constants: + - name: "BUTTON1" + typeName: number + type: 1 + value: "16" + desc: "left mouse button code" + desc_ru: "код левой кнопки мыши" + - name: "BUTTON2" + typeName: number + type: 1 + value: "8" + desc: "middle mouse button code" + desc_ru: "код средней кнопки мыши" + - name: "BUTTON3" + typeName: number + type: 1 + value: "4" + desc: "right mouse button code" + desc_ru: "код правой кнопки мыши" + - name: "VK_DOWN" + typeName: number + type: 1 + value: "40" + desc: "key down code" + desc_ru: "код клавиши вниз" + - name: "VK_ESCAPE" + typeName: number + type: 1 + value: "27" + desc: "Escape key code" + desc_ru: "код клавиши Escape" + - name: "VK_FIRE" + typeName: number + type: 1 + value: "10" + desc: "Enter key code" + desc_ru: "код клавиши Enter" + - name: "VK_LEFT" + typeName: number + type: 1 + value: "37" + desc: "key left code" + desc_ru: "код клавиши влево" + - name: "VK_RIGHT" + typeName: number + type: 1 + value: "39" + desc: "key right code" + desc_ru: "код клавиши вправо" +functions: + - name: "click" + args: "buttons" + scope: "desktop" + desc: "performs click with given mouse buttons" + desc_ru: "осуществляет клик мышью с заданными клавишами" + example: |- + use robot + + click(BUTTON3) // right mouse button click + example_ru: |- + use robot + + click(BUTTON3) // клик правой кнопкой мыши + - name: "delay" + args: "ms" + scope: "desktop" + desc: "delay by given milliseconds" + desc_ru: "задержка на заданной количество миллисекунд" + - name: "execProcess" + args: "args..." + desc: "executes the process with parameters" + desc_ru: "запускает процесс с параметрами\n\n Если функции переданы несколько аргументов, то они все передаются как параметры.\n Если функции передан только один параметр - массив, то его элементы передаются как параметры.\n Если функции передан только один параметр, то он служит единственным параметром." + example: |- + use robot + + execProcess("mkdir", "Test") + execProcess("mkdir Test") + execProcess(["mkdir", "Test"]) + - name: "execProcessAndWait" + args: "args..." + desc: "same as `execProcess`, but waits until process completes, returns it's exit code" + desc_ru: "аналогичен функции `execProcess`, но ожидает завершение порождаемого процесса и возвращает его статус" + - name: "fromClipboard" + args: "" + desc: "gets text from clipboard" + desc_ru: "получает строку из буфера обмена" + - name: "keyPress" + args: "key" + scope: "desktop" + desc: "performs pressing key" + desc_ru: "осуществляет зажатие клавиши с кодом key" + - name: "keyRelease" + args: "key" + scope: "desktop" + desc: "performs releasing key" + desc_ru: "осуществляет отпускание клавиши с кодом key" + - name: "mouseMove" + args: "x, y" + scope: "desktop" + desc: "moves mouse pointer to given point" + desc_ru: "перемещает указатель мыши в заданную координату" + - name: "mousePress" + args: "buttons" + scope: "desktop" + desc: "performs pressing the given mouse button" + desc_ru: "осуществляет зажатие заданной кнопки мыши" + - name: "mouseRelease" + args: "buttons" + scope: "desktop" + desc: "performs releasing the given mouse button" + desc_ru: "осуществляет отпускание заданной кнопки мыши" + - name: "mouseWheel" + args: "value" + scope: "desktop" + desc: "performs scrolling (< 0 - up, > 0 - down)" + desc_ru: "осуществляет прокрутку колеса мыши (отрицательное значение - вверх, положительное - вниз)" + - name: "setAutoDelay" + args: "ms" + scope: "desktop" + desc: "sets delay after each automation event" + desc_ru: "установка длительности автоматической задержки после каждого события автоматизации" + - name: "toClipboard" + args: "text" + desc: "adds text to clipboards" + desc_ru: "копирует строку в буфер обмена" + - name: "typeText" + args: "text" + scope: "desktop" + desc: "performs typing text by pressing keys for each character" + desc_ru: "осуществляет последовательное нажатие клавиш для набора заданного текста" + - name: "sudo" + args: "args..." + scope: "android" + desc: "same as `execProcess`, but executes command as root (requires rooted device)" + desc_ru: "аналогичен функции `execProcess`, но выполняет команду от имени администратора (нужен Root)" \ No newline at end of file diff --git a/docs/src/modules/socket.yml b/docs/src/modules/socket.yml new file mode 100644 index 00000000..4a12016e --- /dev/null +++ b/docs/src/modules/socket.yml @@ -0,0 +1,161 @@ +name: socket +scope: both +constants: + - name: EVENT_CONNECT + type: 2 + typeName: string + value: connect + - name: EVENT_CONNECTING + type: 2 + typeName: string + value: connecting + - name: EVENT_CONNECT_ERROR + type: 2 + typeName: string + value: connect_error + - name: EVENT_CONNECT_TIMEOUT + type: 2 + typeName: string + value: connect_timeout + - name: EVENT_DISCONNECT + type: 2 + typeName: string + value: disconnect + - name: EVENT_ERROR + type: 2 + typeName: string + value: error + - name: EVENT_MESSAGE + type: 2 + typeName: string + value: message + - name: EVENT_PING + type: 2 + typeName: string + value: ping + - name: EVENT_PONG + type: 2 + typeName: string + value: pong + - name: EVENT_RECONNECT + type: 2 + typeName: string + value: reconnect + - name: EVENT_RECONNECTING + type: 2 + typeName: string + value: reconnecting + - name: EVENT_RECONNECT_ATTEMPT + type: 2 + typeName: string + value: reconnect_attempt + - name: EVENT_RECONNECT_ERROR + type: 2 + typeName: string + value: reconnect_error + - name: EVENT_RECONNECT_FAILED + type: 2 + typeName: string + value: reconnect_failed +functions: + - name: newSocket + args: 'url, options = {}' + desc: |- + creates new SocketValue + + options (map with keys): + - forceNew (boolean) + - multiplex (boolean) + - reconnection (boolean) + - rememberUpgrade (boolean) + - secure (boolean) + - timestampRequests (boolean) + - upgrade (boolean) + - policyPort (integer) + - port (integer) + - reconnectionAttempts (integer) + - reconnectionDelay (timestamp - long) + - reconnectionDelayMax (timestamp - long) + - timeout (timestamp - long) - set -1 to disable + - randomizationFactor (double) + - host (string) + - hostname (string) + - path (string) + - query (string) + - timestampParam (string) + - transports (array of strings) + desc_ru: |- + создаёт новый SocketValue + + options (map с ключами): + - forceNew (boolean) + - multiplex (boolean) + - reconnection (boolean) + - rememberUpgrade (boolean) + - secure (boolean) + - timestampRequests (boolean) + - upgrade (boolean) + - policyPort (integer) + - port (integer) + - reconnectionAttempts (integer) + - reconnectionDelay (timestamp - long) + - reconnectionDelayMax (timestamp - long) + - timeout (timestamp - long) - -1 для отключения + - randomizationFactor (double) + - host (string) + - hostname (string) + - path (string) + - query (string) + - timestampParam (string) + - transports (array of strings) +types: + - name: SocketValue + functions: + - name: "close" + args: "" + desc: "disconnects the socket" + desc_ru: "закрывает соединение сокета" + - name: "connect" + args: "" + desc: "connects the socket" + desc_ru: "подключает сокет" + - name: "connected" + args: "" + desc: "returns connected status (1 - connected, 0 - no)" + desc_ru: "возвращает состояние подключения (1 - подключен, 0 - нет)" + - name: "disconnect" + args: "" + desc: "disconnects the socket" + desc_ru: "закрывает соединение сокета" + - name: "emit" + args: "event, data" + desc: "emits an event" + desc_ru: "посылает событие" + - name: "hasListeners" + args: "event" + desc: "returns true if there is listeners for specified event" + desc_ru: "возвращает true, если для указанного события есть обработчики" + - name: "id" + args: "" + desc: "returns socket id" + desc_ru: "возвращает id сокета" + - name: "off" + args: "event = .." + desc: "removes specified event handler, or removes all if no arguments were passed" + desc_ru: "удаляет обработчик указанного события или удаляет все обработчики, если не было передано ни одного аргумента" + - name: "on" + args: "event, listener" + desc: "adds event listener" + desc_ru: "добавляет обработчик указанного события" + - name: "once" + args: "event, listener" + desc: "adds one time event listener" + desc_ru: "добавляет одноразовый обработчик указанного события" + - name: "open" + args: "" + desc: "connects the socket" + desc_ru: "подключает сокет" + - name: "send" + args: "data" + desc: "send messages" + desc_ru: "отправляет сообщения" \ No newline at end of file diff --git a/docs/src/modules/std.yml b/docs/src/modules/std.yml new file mode 100644 index 00000000..18afef73 --- /dev/null +++ b/docs/src/modules/std.yml @@ -0,0 +1,290 @@ +name: std +scope: "both" +desc: "Contains common functions" +desc_ru: "Содержит вспомогательные функции общего назначения" +constants: + - name: "ARGS" + typeName: string + scope: "desktop" + type: 2 + value: "command-line arguments" + - name: OwnLang + typeName: map + type: 4 + value: "{PLATFORM=android/desktop, VERSION=1.5.0_000000, VERSION_MAJOR=1, VERSION_MINOR=5, VERSION_PATCH=0}" + since: 1.4.0 +functions: + - name: arrayCombine + args: "keys, values" + desc: "creates map by combining two arrays" + desc_ru: "создаёт объект на основе двух массивов" + - name: arrayKeyExists + args: "key, map" + desc: "checks existing key in map. 1 - exists, 0 - no" + desc_ru: "проверяет, содержится ли ключ key в объекте map. 1 - содержится, 0 - нет" + - name: arrayKeys + args: "map" + desc: "returns array of map keys" + desc_ru: "возвращает массив ключей карты" + - name: arraySplice + args: "array, start, deleteCount = length(array) - start, additions = []" + desc: "returns new array with removed `deleteCount` elements starting from `start` and/or added new elements from `start` index" + desc_ru: "возвращает новый массив с удалёнными `deleteCount` элементами, начиная с позиции `start` и/или добавляет новые элементы с позиции `start`" + - name: arrayValues + args: "map" + desc: "returns array of map values" + desc_ru: "возвращает массив значений карты" + - name: charAt + args: "input, index" + desc: returns char code in position `index` of string `input` + desc_ru: возвращает код символа в позиции `index` строки `input` + - name: clearConsole + scope: "android" + args: "" + desc: "clears console" + desc_ru: "очищает консоль" + - name: default + args: a, b + desc: returns value `a` if it it non empty, returns `b` otherwise + desc_ru: возвращает значение `a`, если оно не пустое, иначе возвращается значение `b` + since: 1.4.0 + example: |- + use std + + user = {"name": "", "lastname": "Doe"} + name = default(user.name, "Unknown") + lastname = default(user.lastname, "Unknown") + println name + " " + lastname // Unknown Doe + example_ru: |- + use std + + user = {"name": "", "lastname": "Иванов"} + name = default(user.name, "Имя неизвестно") + lastname = default(user.lastname, "фамилия неизвестна") + println name + " " + lastname // Имя неизвестно Иванов + - name: echo + args: "arg..." + desc: "prints values to console, separate them by space and puts newline at the end. Takes variable number of arguments" + desc_ru: "выводит значения в консоль, разделяя их пробелом, а потом ставит перенос строки. Может принимать переменное значение аргументов" + example: |- + use std + + echo(1, "abc") // prints "1 abc" to console + echo(1, 2, 3, 4, 5, "a", "b") // prints "1 2 3 4 5 a b" + example_ru: |- + use std + + echo(1, "abc") // выведет строку "1 abc" в консоль + echo(1, 2, 3, 4, 5, "a", "b") // выведет строку "1 2 3 4 5 a b" в консоль" + - name: getBytes + args: input, charset = "UTF-8" + desc: returns byte array of the string in the given charset + desc_ru: возвращает массив байт строки в заданной кодировке + since: 1.5.0 + - name: indexOf + args: "input, what, index = 0" + desc: "finds first occurrence of `what` in string `input`, starting at position `index`" + desc_ru: "поиск первого вхождения подстроки `what` в строке `input`, начиная с позиции `index`" + - name: join + args: "array, delimiter = \"\", prefix = \"\", suffix = \"\"" + desc: "join array to string with `delimiter`, `prefix` and `suffix`" + desc_ru: "объединяет массив в строку с разделителем `delimiter`, префиксом `prefix` и суффиксом `suffix`" + - name: lastIndexOf + args: "input, what, index = 0" + desc: "finds last occurrence of `what` in string `input`, starting at position `index`" + desc_ru: "поиск последнего вхождения подстроки `what` в строке `input`, начиная с позиции `index`" + - name: length + args: "x" + desc: "returns length of string, array/map size or number of function arguments" + desc_ru: "возвращает длину строки, размер массива/объекта или количество аргументов функции в зависимости от типа аргумента x" + - name: newarray + args: "size..." + desc: "creates array with `size`.\n`newarray(x)` - creates 1D array, `newarray(x,y)` - creates 2D array" + desc_ru: "оздаёт массив с размером size. Если указать 1 аргумент - создаётся одномерный массив, если 2 аргумента - двухмерный и т.д" + example: |- + use std + + println newarray(4) // [0, 0, 0, 0] + println newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] + - name: parseInt + args: "str, radix" + desc: parses string into integer in the `radix` + desc_ru: парсит строку в целое число с указанным основанием + - name: parseLong + args: "str, radix" + desc: parses string into long in the `radix` + desc_ru: парсит строку в длинное целое число с указанным основанием + - name: rand + args: "from = 0, to = .." + desc: |- + returns pseudo-random number. + `rand()` - returns float number from 0 to 1 + `rand(max)` - returns random number from 0 to max + `rand(from, to)` - return random number from `from` to `to` + desc_ru: "возвращает псевдослучайное число. Если вызвать функцию без аргументов, возвращается вещественное число от 0 до 1. Если указан один аргумент - возвращается целое число в диапазоне [0...значение). Если указаны два аргумента - возвращается псевдослучайное число в промежутке [from...to)" + - name: range + args: "from = 0, to, step = 1" + desc: |- + creates lazy array by number range. + `range(to)` - creates range from 0 to `to` (exclusive) with step 1 + `range(from, to)` - creates range from `from` to `to` (exclusive) with step 1 + `range(from, to, step)` - creates range from `from` to `to` (exclusive) with step `step` + desc_ru: |- + создаёт массив с элементами числового промежутка. Элементы вычисляются по мере их использования, так что в цикле foreach можно использовать любые промежутки. + `range(to)` - создаёт промежуток от 0 до `to` (не включительно) с шагом 1 + `range(from, to)` - создаёт промежуток от `from` до `to` (не включительно) с шагом 1 + `range(from, to, step)` - создаёт промежуток от `from` до `to` (не включительно) с шагом `step` + example: |- + use std + + println range(3) // [0, 1, 2] + r = range(-5, 0) // [-5, -4, -3, -2, -1] + println r[0] // -5 + println r[2] // -3 + for x : range(20, 9, -5) { + println x + } // 20 15 10 + - name: readln + scope: "desktop" + args: "x" + desc: "reads a line from console" + desc_ru: "чтение строки из консоли" + - name: replace + args: "str, target, replacement" + desc: "replaces all occurrences of string `target` with string `replacement`" + desc_ru: "заменяет все вхождения подстроки `target` на строку `replacement`" + - name: replaceAll + args: "str, regex, replacement" + desc: "replaces all occurrences of regular expression `regex` with string `replacement`" + desc_ru: "заменяет все вхождения регулярного выражения `regex` на строку `replacement`" + - name: replaceFirst + args: "str, regex, replacement" + desc: "replaces first occurrence of regular expression `regex` with string `replacement`" + desc_ru: "заменяет первое вхождение регулярного выражения `regex` на строку `replacement`" + - name: sleep + args: "time" + desc: "causes current thread to sleep for `time` milliseconds" + desc_ru: "приостановка текущего потока на time миллисекунд" + - name: sort + args: "array, comparator = .." + desc: "sorts array by natural order or by `comparator` function" + desc_ru: "сортирует массив. Если задана функция `comparator`, то сортировка будет производится на основе результата функции сравнения" + - name: split + args: "str, regex, limit = 0" + desc: "splits string `str` with regular expression `regex` into array. `limit` parameter affects the length of resulting array" + desc_ru: "разделяет строку `str` по шаблону `regex` и помещает элементы в массив. Если указан параметр `limit`, то будет произведено не более limit разбиений, соответственно размер результирующего массива будет ограничен этим значением limit" + example: |- + use std + + println split("a5b5c5d5e", "5") // ["a", "b", "c", "d", "e"] + println split("a5b5c5d5e", "5", 3) // ["a", "b", "c5d5e"] + - name: sprintf + args: "format, args..." + desc: "formats string by arguments" + desc_ru: "форматирует строку" + - name: stringFromBytes + args: input, charset = "UTF-8" + desc: returns a string from byte array in the given charset + desc_ru: возвращает строку из массива байт в заданной кодировке + since: 1.5.0 + - name: stripMargin + args: input, marginPrefix = "|" + desc: trims leading whitespaces followed by `marginPrefix` on each line and removes the first and the last lines if they are blank + desc_ru: обрезает начальные пробелы, сопровождаемые `marginPrefix` в каждой строке, и удаляет первую и последнюю строки, если они пустые + since: 1.5.0 + example: |- + use std + + println " + |123 + |456 + ".stripMargin() // "123\n456" + - name: substring + args: "str, startIndex, endIndex = .." + desc: "returns string from `startIndex` to `endIndex` or to end of string if `endIndex` is not set" + desc_ru: "обрезает строку `str`, начиная от символа после позиции `startIndex` и по `endIndex`. Если `endIndex` не указан, обрезается до конца строки" + example: |- + use std + + println substring("abcde", 1) // bcde + println substring("abcde", 2, 4) // cd + - name: sync + args: "callback" + desc: calls an asynchronous function synchronously + desc_ru: делает асинхронный вызов синхронным + example: |- + use std, http + + url = "https://whatthecommit.com/index.txt" + result = sync(def(ret) { + http(url, def(t) = ret(t)) + }) + println result + - name: thread + args: "func, args..." + desc: "creates new thread with parameters if passed" + desc_ru: |- + создаёт новый поток и передаёт параметры, если есть. + + `func` - функция, ссылка на функцию (`::function`) или имя функции (`"function"`) + + `args` - 0 или более аргументов, которые необходимо передать в функцию func + example: |- + use std + + thread(def() { + println "New Thread" + }) + + thread(::newthread, 10) + thread("newthread", 20) + + def newthread(x) { + println "New Thread. x = " + x + } + - name: time + args: "" + desc: "returns current time in milliseconds from 01.01.1970" + desc_ru: "возвращает текущее время в миллисекундах начиная с 1970 года" + - name: toChar + args: "code" + desc: "converts char code to string" + desc_ru: "переводит код символа в строку" + example: |- + use std + + println toChar(48) // "0" + - name: toHexString + args: 'number' + desc: 'converts number into hex string' + desc_ru: 'конвертирует число в строку с шестнадцатиричным представлением' + - name: toLowerCase + args: "str" + desc: "converts all symbols to lower case" + desc_ru: "переводит все символы строки в нижний регистр" + - name: toUpperCase + args: "str" + desc: "converts all symbols to upper case" + desc_ru: "переводит все символы строки в верхний регистр" + - name: trim + args: "str" + desc: "removes any leading and trailing whitespaces in string" + desc_ru: "обрезает пробельные невидимые символы по обоим концам строки" + - name: try + args: "unsafeFunction, catchFunction = def(type, message) = -1" + desc: suppress any error in `unsafeFunction` and returns the result of the `catchFunction` if any error occurs + desc_ru: подавляет любые ошибки в `unsafeFunction` и возрвщает результат функции `catchFunction`, если была ошибка + example: |- + use std + + println try(def() = "success") // success + println try(def() = try + 2) // -1 + println try(def() = try(), def(type, message) = sprintf("Error handled:\ntype: %s\nmessage: %s", type, message)) + println try(def() = try(), 42) // 42 + example_ru: |- + use std + + println try(def() = "успех") // успех + println try(def() = try + 2) // -1 + println try(def() = try(), def(type, message) = sprintf("Обработана ошибка:\nтип: %s\nсообщение: %s", type, message)) + println try(def() = try(), 42) // 42 \ No newline at end of file diff --git a/docs/src/modules/types.yml b/docs/src/modules/types.yml new file mode 100644 index 00000000..8b06f088 --- /dev/null +++ b/docs/src/modules/types.yml @@ -0,0 +1,80 @@ +name: types +scope: "both" +desc: "Contains functions for type checking and conversion" +desc_ru: "Содержит функции для проверки и преобразования типов" +constants: + - name: "OBJECT" + typeName: number + type: 1 + value: "0" + - name: "NUMBER" + typeName: number + type: 1 + value: "1" + - name: "STRING" + typeName: number + type: 1 + value: "2" + - name: "ARRAY" + typeName: number + type: 1 + value: "3" + - name: "MAP" + typeName: number + type: 1 + value: "4" + - name: "FUNCTION" + typeName: number + type: 1 + value: "5" +functions: + - name: "byte" + args: "value" + desc: "converts value to byte" + desc_ru: "преобразует значение к типу byte" + - name: "double" + args: "value" + desc: "converts value to double" + desc_ru: "преобразует значение к типу double" + - name: "float" + args: "value" + desc: "converts value to float" + desc_ru: "преобразует значение к типу float" + - name: "int" + args: "value" + desc: "converts value to int" + desc_ru: "преобразует значение к типу int" + - name: "long" + args: "value" + desc: "converts value to long" + desc_ru: "преобразует значение к типу long" + - name: "number" + args: "value" + desc: "converts value to number if possible" + desc_ru: "преобразует значение к числу, если это возможно" + example: |- + use types + + println typeof(number("2.3")) // 1 (NUMBER) + - name: "short" + args: "value" + desc: "converts value to short" + desc_ru: "преобразует значение к типу short" + - name: "string" + args: "value" + desc: "converts value to string" + desc_ru: "преобразует значение в строку" + example: |- + use types + + println typeof(string(1)) // 2 (STRING) + - name: "typeof" + args: "value" + desc: "returns the type of value" + desc_ru: "возвращает тип переданного значения" + example: |- + use types + + println typeof(1) // 1 (NUMBER) + println typeof("text") // 2 (STRING) + println typeof([]) // 3 (ARRAY) \ No newline at end of file diff --git a/docs/src/modules/yaml.yml b/docs/src/modules/yaml.yml new file mode 100644 index 00000000..7960bffa --- /dev/null +++ b/docs/src/modules/yaml.yml @@ -0,0 +1,14 @@ +name: yaml +scope: desktop +desc: "Contains functions for working with the yaml format" +desc_ru: "Содержит функции преобразования данных в формат yaml и наоборот" +constants: [] +functions: + - name: yamldecode + args: "data" + desc: "converts data to yaml string" + desc_ru: "преобразует переданные данные в строку в формате yaml" + - name: yamlencode + args: "yamlString" + desc: "converts yaml string to data" + desc_ru: "преобразует строку в формате yaml в данные" \ No newline at end of file diff --git a/docs/src/modules/zip.yml b/docs/src/modules/zip.yml new file mode 100644 index 00000000..e34f5376 --- /dev/null +++ b/docs/src/modules/zip.yml @@ -0,0 +1,98 @@ +name: zip +since: 1.5.0 +scope: both +desc: "Contains functions for working with zip archives" +desc_ru: "Содержит функции для работы с zip архивами" +constants: [] +functions: + - + name: zip + args: "inputPath, outputFile, mapper = def(entryPath) = entryPath" + desc: |- + creates a zip archive with the contents of `inputPath` and saves to `outputFile`. + `mapper` is used to set the name of the final file inside the archive and for filtering. If the mapper returns an empty string, the file will be skipped. + Returns the number of archived files, or -1 if the archive could not be created. + desc_ru: |- + создаёт zip архив с содержимым `inputPath` и сохраняет в `outputFile`. + `mapper` используется для задания имени конечного файла внутри архива, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. + Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. + example: |- + use zip + // Zip all files in directory + zip("/tmp/dir", "/tmp/1.zip") + // Zip .txt files + zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") + example_ru: |- + use zip + // Архивировать все файлы в директории + zip("/tmp/dir", "/tmp/1.zip") + // Архивировать .txt файлы + zip("/tmp/dir", "/tmp/2.zip", def(p) = p.endsWith(".txt") ? p : "") + - + name: zipFiles + args: "input, outputFile" + desc: |- + creates a zip archive with the contents of `inputPath` and saves to `outputFile`. + If `input` is a string, then a single file or the contents of a folder is archived. + If `input` is an array, then the files and folders listed in it are archived. + If `input` is an associative array, then the files and folders listed in the keys are archived and the names inside the archive will be the values of an array. + Returns the number of archived files, or -1 if the archive could not be created. + desc_ru: |- + создаёт zip архив с содержимым `input` и сохраняет в `outputFile`. + Если `input` — строка, то архивируется один файл или содержимое папки. + Если `input` — массив, то архивируются файлы и папки, перечисленные в нём. + Если `input` — ассоциативный массив, то архивируются файлы и папки перечисленные в ключах, а именами внутри архива будут служить значения. + Возвращает количество заархивированных файлов, либо -1, если создать архив не удалось. + example: |- + use zip + zipFiles("/tmp/dir/file.txt", "/tmp/1.zip") + zipFiles(["/tmp/dir/file.txt", "/tmp/dir/readme.md"], "/tmp/2.zip") + zipFiles({"/tmp/dir/file.txt" : "docs/1.md", "/tmp/dir/readme.md" : "docs/2.md"}, "/tmp/3.zip") + - + name: unzip + args: "input, output, mapper = def(entryName) = entryPath" + desc: |- + unpacks a zip archive to `output` directory. + `mapper` is used to set the name of the final file and for filtering. If the mapper returns an empty string, the file will be skipped. + Returns the number of unzipped files, or -1 if unzipping the archive was failed. + desc_ru: |- + распаковывает zip архив `input` в папку `output`. + `mapper` используется для задания имени конечного файла, а также для фильтрации. Если в mapper вернуть пустую строку, то файл будет пропущен. + Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. + example: |- + use zip + // Unzip all files in directory + unzip("/tmp/1.zip", "/tmp/dir") + // Unzip .txt files + unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") + example_ru: |- + use zip + // Распаковать все файлы в директории + unzip("/tmp/1.zip", "/tmp/dir") + // Распаковать .txt файлы + unzip("/tmp/2.zip", "/tmp/dir", def(p) = p.endsWith(".txt") ? p : "") + - + name: unzipFiles + args: "input, output" + desc: |- + unpacks a `output` files from zip archive . + If `output` is a string, then a single file is unzipped. + If `output` is an array, then the files listed in it are unzipped. + If `output` is an associative array, the files listed in the keys are unzipped and the values will be file names. + Returns the number of unzipped files, or -1 if unzipping the archive was failed. + desc_ru: |- + распаковывает `output` файлы из zip архива. + Если `output` — строка, то разархивируется один файл. + Если `output` — массив, то разархивируются файлы, перечисленные в нём. + Если `output` — ассоциативный массив, то разархивируются файлы перечисленные в ключах, а именами файлов будут служить значения. + Возвращает количество разархивированных файлов, либо -1, если разархивировать архив не удалось. + example: |- + use zip + unzipFiles("/tmp/1.zip", "file.txt") + unzipFiles("/tmp/2.zip", ["file.txt", "readme.md"]) + unzipFiles("/tmp/3.zip", {"docs/1.md" : "/tmp/dir/file.txt", "docs/2.md" : "/tmp/dir/readme.md"}) + - + name: listZipEntries + args: "input" + desc: returns an array of zip archive filenames + desc_ru: возвращает массив с именами файлов zip архива \ No newline at end of file From d1c7a678177a8ca67477110e035bdd5acf12b0e8 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 19 Nov 2023 18:57:33 +0200 Subject: [PATCH 368/448] Add modules documentation generator --- docs/.gitignore | 3 +- docs/docs/.vuepress/configs/pages.js | 4 +- docs/src/docgen-md.own | 180 +++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 docs/src/docgen-md.own diff --git a/docs/.gitignore b/docs/.gitignore index 7d2109ab..b25f8534 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,4 +1,5 @@ node_modules .temp .cache -/docs/*/modules/ +docs/.vuepress/configs/modules.js +docs/*/modules/ diff --git a/docs/docs/.vuepress/configs/pages.js b/docs/docs/.vuepress/configs/pages.js index 55fed5be..ff894be8 100644 --- a/docs/docs/.vuepress/configs/pages.js +++ b/docs/docs/.vuepress/configs/pages.js @@ -1,3 +1,4 @@ +import modules from './modules' export default { '/': { text: {'en': 'OwnLang', 'ru': 'OwnLang'}, @@ -24,7 +25,6 @@ export default { '/modules/': { text: {'en': 'Modules', 'ru': 'Модули'}, - pages: [ - ] + pages: modules } } \ No newline at end of file diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own new file mode 100644 index 00000000..d5ad5249 --- /dev/null +++ b/docs/src/docgen-md.own @@ -0,0 +1,180 @@ +use std, types, files, yaml, functional + +INPUT_PATH_FMT = "./modules/%s.yml" +OUTPUT_DIR_FMT = "../docs/%s/modules" +OUTPUT_PATH_FMT = OUTPUT_DIR_FMT + "/%s.md" + +LANGS = ["en", "ru"] +MODULES = [ + "std", + "types", + "math", + "date", + "files", + "http", + "socket", + "downloader", + "base64", + "json", + "yaml", + "zip", + "gzip", + "functional", + "robot", + "ounit", + "canvas", + "canvasfx", + "forms", + "java", + "jdbc", + "regex", + "android", + "canvas_android", + "forms_android", + "imageprocessing_android", + "gps_android" +] +messages = { + "constants": {"en": "Constants", "ru": "Константы"}, + "functions": {"en": "Functions", "ru": "Функции"}, + "types": {"en": "Types", "ru": "Типы"}, + "example": {"en": "Example", "ru": "Пример"}, + "since": {"en": "since", "ru": "начиная с"} +} + +// Write modules pages to vuepress config +f = fopen("../docs/.vuepress/configs/modules.js", "w") +writeLine(f, "export default [") +writeLine(f, stream(MODULES).map(def(m) = " \"%s.md\"".sprintf(m)).joining(",\n")) +writeLine(f, "]") +flush(f) +fclose(f) + +// Create output dirs +for lang : LANGS { + mkdirs(sprintf(OUTPUT_DIR_FMT, lang)) +} + +for moduleName : MODULES { + module = readYml(sprintf(INPUT_PATH_FMT, moduleName)) + for lang : LANGS { + println "" + module.name + " / " + lang + file = sprintf(OUTPUT_PATH_FMT, lang, moduleName) + f = fopen(file, "w") + + writeHeader(f, module, lang) + writeConstants(f, module.constants ?? [], lang) + writeFunctions(f, module.functions ?? [], lang, 2); + writeTypes(f, module.types ?? [], lang); + + flush(f) + fclose(f) + } +} + +// -- write +def writeHeader(f, module, lang) { + writeText(f, header(module.name, 1)) + if length(module.scope ?? "") && (module.scope != "both") { + writeText(f, " (" + module.scope + ")") + } + writeLine(f, "\n") + if length(module.since ?? "") { + writeSince(f, module.since, lang) + } + writeDescription(f, module, lang, "\n%s\n") +} + +def writeConstants(f, constants, lang) { + if (constants.isEmpty()) return 0 + + writeLine(f, "\n\n## " + messages.constants[lang]) + for info : constants { + writeText(f, "\n`%s` : *%s*".sprintf(info.name, info.typeName)) + writeScope(f, info.scope ?? "") + writeText(f, " = ") + constValue = getValue(info, "value", lang) + if (info.type != MAP && info.type != CLASS) { + writeText(f, "`%s`".sprintf(constValue)) + } else { + mapValues = constValue.substring(1, constValue.length - 1).split(", ") + if (mapValues.length >= 7) { + writeText(f, "\n\n```own\n{\n "); + writeText(f, mapValues.joinToString(",\n ")); + writeText(f, "\n}\n```"); + } else { + writeText(f, "`%s`".sprintf(constValue)); + } + } + writeLine(f, "") + writeDescription(f, info, lang, "\n%s\n") + } + +} + +def writeFunctions(f, functions, lang, level = 2) { + if (functions.isEmpty()) return 0 + + writeLine(f, "\n\n" + header(messages.functions[lang], level)) + for info : functions { + writeText(f, "\n`%s(%s)`".sprintf(info.name, info.args)) + writeScope(f, info.scope ?? "") + if length(info.since ?? "") { + writeSince(f, info.since, lang) + } + writeDescription(f, info, lang, " — %s") + writeLine(f, "") + + example = getValue(info, "example", lang) + if (length(example ?? "")) { + writeLine(f, "\n```own") + writeLine(f, example) + writeLine(f, "```") + } + } +} + +def writeTypes(f, types, lang) { + if (types.isEmpty()) return 0 + + writeLine(f, "\n\n" + header(messages.types[lang])) + for info : types { + writeText(f, "\n\n" + header("`%s`".sprintf(info.name), 3)) + writeScope(f, info.scope ?? "") + writeDescription(f, info, lang, "%s\n") + writeFunctions(f, info.functions ?? [], lang, 4); + writeLine(f, "") + } +} + +def writeScope(f, scope) = match(scope) { + case "android": writeText(f, " ") + case "desktop": writeText(f, " ") + case _: { } +} + +def writeDescription(f, obj, lang, format = "%s") { + str = getValue(obj, "desc", lang) + if (str != "") { + writeText(f, sprintf(format, str)) + } +} + +def writeSince(f, since, lang) { + writeText(f, "".sprintf(messages.since[lang], since)) +} + +// -- utils +def getValue(object, key, lang = "en") { + newKey = (lang != "en") ? (key + "_" + lang) : key + return object[newKey] ?? object[key] ?? "" +} + +def header(text, level = 2) = ("#" * level) + " " + text + +def readYml(filename) { + f = fopen(filename, "r") + s = readText(f) + fclose(f) + return yamldecode(s) +} \ No newline at end of file From 99a95a044f4be8308ca8f26468bc829155d847b8 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 20 Nov 2023 21:20:11 +0200 Subject: [PATCH 369/448] Update README --- README.md | 4 +- examples.own | 167 --------------------------------------------------- tests.own | 128 --------------------------------------- 3 files changed, 2 insertions(+), 297 deletions(-) delete mode 100644 examples.own delete mode 100644 tests.own diff --git a/README.md b/README.md index 313ccaab..5c1b938b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ OwnLang - dynamic functional programming language inspired by Scala and Python. | Free | Pro | Desktop | | :--: | :-: | :-----: | -| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.5.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.5.0) +| [![Free](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) | [![Pro](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.annimon.ownlang) | [v1.5.0](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/tag/v1.5.0) | Also available as AUR package: @@ -139,7 +139,7 @@ def patch_callback(v) { ## Build -Build using Gradle `./gradlew dist` +Build using Gradle `./gradlew shadowJar` or take a look to [latest release](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases/latest) for binaries. diff --git a/examples.own b/examples.own deleted file mode 100644 index d55a4297..00000000 --- a/examples.own +++ /dev/null @@ -1,167 +0,0 @@ -/* - * - * Automatic run examples for testing. - * Have functions for special launch own scripts if need - * - */ - -use date, files, robot, std - -DEBUG = true -EXAMPLES_DIR = "examples" -REPORT_PATH = "F:/report.txt" -EXEC_TEMPLATE = "cmd /U /C \"ownlang -f %s %s >> %s 2>&1\"" - -// Main list of examples. Contains predefined examples with custom executing params -listExamples = { - /* template - "program_name": { - "isRun": false, - "path": "" // relative path to program - "args": [], // additional args for run - "prelaunch": "", // pre-launch other application, e.g. start server - }, - */ - "fx_basic_shapes.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "fx_event_handlers.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "fx_global_alpha.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "fx_image_negate.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "fx_image.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "fx_koch_snowflake.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "fx_rotation.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "okhttp_telegram_sendvoice.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "telegram_api.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "okhttp_imgur_upload.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "okhttp_websocket.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, - "pipes_online.own": { - "isRun": false, - "path": "" - "args": [], - "prelaunch": "", - }, -} - -def debug(something) { - if !DEBUG return 0 - - println sprintf("[%s] %s", newDate(), something) -} - -// Algorithm: get list of examples, filter and add to main list of examples -def readExamples() { - examplesDir = fopen(EXAMPLES_DIR, "") - dirs = listFiles(examplesDir) - - for dir : dirs { - relativeDirPath = EXAMPLES_DIR + "/" + dir - subDir = fopen(relativeDirPath, "") - if (!isDirectory(subDir) || dir == "." || dir == "..") continue - files = listFiles(subDir) - for file : files { - if (indexOf(file, ".own") < 0 || dir == "." || dir == "..") { - debug(file + "not ownlang application or sub directory") - continue - } - if (arrayKeyExists(file, listExamples)) { - debug(file + " exists in main list") - continue - } - program = { - "isRun": true, - "path": relativeDirPath + "/" + file - "args": [], - "prelaunch": "", - } - listExamples[file] = program - } - } - return listExamples -} - -readExamples() - -// remove old report -if exists(REPORT_PATH) { - delete(REPORT_PATH) -} - -// main task -for name, program : listExamples { - if !program.isRun { - println "Skip: " + name - continue - } - - println "Executing: " + name - - reportBeforeExec = sprintf("cmd /U /C \"echo %s\n >> %s", program.path, REPORT_PATH) - execProcessAndWait(reportBeforeExec) - - if length(trim(program.prelaunch)) > 0 { - println "Pre-launch: " + program.pre-launch - execProcessAndWait(program.pre-launch) - } - - execString = sprintf(EXEC_TEMPLATE, program.path, join(program.args, " "), REPORT_PATH) - debug(execString) - exitCode = execProcessAndWait(execString) - println "Exit code: " + exitCode - - reportAfterExec = sprintf("cmd /U /C \"echo %s\n >> %s", "*"*19, REPORT_PATH) - execProcessAndWait(reportAfterExec) -} \ No newline at end of file diff --git a/tests.own b/tests.own deleted file mode 100644 index 2eddadef..00000000 --- a/tests.own +++ /dev/null @@ -1,128 +0,0 @@ -use ounit, types, functional, date, files - -def testAdditionOnNumbers() { - assertEquals(6, 0 + 1 + 2 + 3) -} - -def testSubtractionOnNumbers() { - assertEquals(-6, 0 - 1 - 2 - 3) -} - -def testPrefixIncrement() { - a = 8 - assertEquals(9, ++a) - assertEquals(9, a) -} - -def testPostfixIncrement() { - a = 8 - assertEquals(8, a++) - assertEquals(9, a) -} - -def testStringReversing() { - assertEquals("tset", -"test") -} - -def testStringMultiplication() { - assertEquals("******", "*" * 6) -} - -def testTypes() { - assertSameType(0, 0.0) -} - -/*def testFail() { - assertTrue(false) -}*/ - -def testScope() { - x = 5 - def func() { - assertEquals(5, x) - x += 10 - assertEquals(15, x) - } - func(); - assertEquals(15, x) -} - -def testFibonacci() { - def fib(n) { - if n < 2 return n - return fib(n-2) + fib(n-1) - } - assertEquals(3, fib(4)) - assertEquals(21, fib(8)) -} - -def testFunctionalChain() { - data = [1,2,3,4,5,6,7] - result = chain(data, - ::filter, def(x) = x <= 4, - ::sortby, def(x) = -x, - ::map, def(x) = x * 2, - ) - assertEquals([8,6,4,2], result) -} - - -// --- Date -def testNewDate() { - d = newDate(2016, 04, 10) - assertEquals(2016, d.year) - assertEquals(4, d.month) - assertEquals(10, d.day) - assertEquals(0, d.hour) - assertEquals(0, d.minute) - assertEquals(0, d.second) - assertEquals(0, d.millisecond) -} - -def testCompareDates() { - assertTrue(newDate(2016, 04, 10) > newDate(2015, 03, 11)) - assertTrue(newDate(2012, 04, 10) < newDate(2015, 03, 11)) - assertTrue(newDate(2015, 03, 11, 0, 0, 0) == newDate(2015, 03, 11)) -} - -def testDateFormat() { - d = formatDate(newDate(2016, 04, 10), newFormat("yyyy/MM/dd HH:mm:ss")) - assertEquals("2016/05/10 00:00:00", d) -} - -def testDateParse() { - d = parseDate("2016/05/10", newFormat("yyyy/MM/dd")) - assertEquals(2016, d.year) - assertEquals(4, d.month) - assertEquals(10, d.day) - assertEquals(0, d.hour) -} - -// --- Files -def testFiles() { - // writeLong - f = fopen("test.file", "wb") - writeLong(f, 1002003004005006007) - flush(f) - fclose(f) - - // append & writeFloat - fpNumber = 100200.3004005006007 - f = fopen("test.file", "wb+") - writeFloat(f, fpNumber) - flush(f) - fclose(f) - - f = fopen("test.file", "rb") - assertEquals(1002003004005006007, readLong(f)) - assertEquals(float(fpNumber), readFloat(f)) - assertEquals(-1, readInt(f)) // EOF - assertEquals(0, FILES_COMPARATOR(f, f)) - fclose(f) - - f = fopen("test.file", "i") - delete(f) - fclose(f) -} - -println runTests() \ No newline at end of file From b2982b3ad1224fb79ebcec90539bdfcbcc80667c Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 23 Nov 2023 22:48:23 +0200 Subject: [PATCH 370/448] Adjust docs styles --- docs/docs/.vuepress/config.js | 7 ++++++- docs/docs/.vuepress/styles/palette.scss | 12 ++++++++++++ docs/src/docgen-md.own | 6 +++--- 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 docs/docs/.vuepress/styles/palette.scss diff --git a/docs/docs/.vuepress/config.js b/docs/docs/.vuepress/config.js index ffc9adb7..95a4a552 100644 --- a/docs/docs/.vuepress/config.js +++ b/docs/docs/.vuepress/config.js @@ -21,6 +21,11 @@ export default defineUserConfig({ }, theme: defaultTheme({ + repo: 'aNNiMON/Own-Programming-Language-Tutorial', + docsBranch: 'next', + editLinkPattern: ':repo/blob/:branch/docs/docs/:path', + editLinkText: 'View source', + contributors: false, locales: { '/en/': { selectLanguageName: 'English', @@ -40,4 +45,4 @@ export default defineUserConfig({ preloadLanguages: ['own', 'json'] }), ], -}) \ No newline at end of file +}) diff --git a/docs/docs/.vuepress/styles/palette.scss b/docs/docs/.vuepress/styles/palette.scss new file mode 100644 index 00000000..69693c0d --- /dev/null +++ b/docs/docs/.vuepress/styles/palette.scss @@ -0,0 +1,12 @@ +:root { + --c-brand: #f15d15; + --c-brand-light: #ff9562; + --c-badge-danger: #f63f3f; + --c-badge-warning: #d0af01; +} +html.dark { + --c-brand: #e1792d; + --c-brand-light: #ff8e3d; + --c-badge-danger: #d94657; + --c-badge-danger-text: #160304; +} \ No newline at end of file diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index d5ad5249..1470c992 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -148,8 +148,8 @@ def writeTypes(f, types, lang) { } def writeScope(f, scope) = match(scope) { - case "android": writeText(f, " ") - case "desktop": writeText(f, " ") + case "android": writeText(f, " ") + case "desktop": writeText(f, " ") case _: { } } @@ -161,7 +161,7 @@ def writeDescription(f, obj, lang, format = "%s") { } def writeSince(f, since, lang) { - writeText(f, "".sprintf(messages.since[lang], since)) + writeText(f, "".sprintf(messages.since[lang], since)) } // -- utils From 68570f10f1ba0fb00f1aae651a25c9fa4b954eb2 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 24 Nov 2023 00:13:13 +0200 Subject: [PATCH 371/448] Add custom components, update vuepress version --- docs/docs/.vuepress/components/Scope.vue | 30 ++ docs/docs/.vuepress/components/Since.vue | 32 ++ docs/docs/.vuepress/config.js | 7 + docs/docs/.vuepress/styles/palette.scss | 3 + docs/package.json | 8 +- docs/pnpm-lock.yaml | 548 ++++++++++++++--------- docs/src/docgen-md.own | 16 +- 7 files changed, 424 insertions(+), 220 deletions(-) create mode 100644 docs/docs/.vuepress/components/Scope.vue create mode 100644 docs/docs/.vuepress/components/Since.vue diff --git a/docs/docs/.vuepress/components/Scope.vue b/docs/docs/.vuepress/components/Scope.vue new file mode 100644 index 00000000..0d5cd18e --- /dev/null +++ b/docs/docs/.vuepress/components/Scope.vue @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/docs/docs/.vuepress/components/Since.vue b/docs/docs/.vuepress/components/Since.vue new file mode 100644 index 00000000..62232ab3 --- /dev/null +++ b/docs/docs/.vuepress/components/Since.vue @@ -0,0 +1,32 @@ + + + + + \ No newline at end of file diff --git a/docs/docs/.vuepress/config.js b/docs/docs/.vuepress/config.js index 95a4a552..f660608c 100644 --- a/docs/docs/.vuepress/config.js +++ b/docs/docs/.vuepress/config.js @@ -1,10 +1,14 @@ import { defineUserConfig, defaultTheme } from 'vuepress' +import { getDirname, path } from '@vuepress/utils' +import { registerComponentsPlugin } from '@vuepress/plugin-register-components' import { prismjsPlugin } from '@vuepress/plugin-prismjs' import { sidebarConfig } from './configs/sidebar' import { navbarConfig } from './configs/navbar' import Prism from 'prismjs'; import definePrismOwnLang from '../../../editors/prismjs/own-language.js' + definePrismOwnLang(Prism) +const __dirname = getDirname(import.meta.url) export default defineUserConfig({ locales: { @@ -44,5 +48,8 @@ export default defineUserConfig({ prismjsPlugin({ preloadLanguages: ['own', 'json'] }), + registerComponentsPlugin({ + componentsDir: path.resolve(__dirname, './components'), + }) ], }) diff --git a/docs/docs/.vuepress/styles/palette.scss b/docs/docs/.vuepress/styles/palette.scss index 69693c0d..c4f3dd38 100644 --- a/docs/docs/.vuepress/styles/palette.scss +++ b/docs/docs/.vuepress/styles/palette.scss @@ -3,6 +3,9 @@ --c-brand-light: #ff9562; --c-badge-danger: #f63f3f; --c-badge-warning: #d0af01; + + --usc-since: var(--c-badge-warning); + --usc-scope: var(--c-brand); } html.dark { --c-brand: #e1792d; diff --git a/docs/package.json b/docs/package.json index a0c46b2a..f9ff7181 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,10 +11,12 @@ "author": "aNNiMON", "license": "MIT", "devDependencies": { + "@vuepress/client": "2.0.0-rc.0", + "@vuepress/plugin-prismjs": "2.0.0-rc.0", + "@vuepress/plugin-register-components": "2.0.0-rc.0", + "@vuepress/utils": "2.0.0-rc.0", "prismjs": "^1.29.0", - "@vuepress/client": "2.0.0-beta.68", - "@vuepress/plugin-prismjs": "2.0.0-beta.68", "vue": "^3.3.8", - "vuepress": "2.0.0-beta.68" + "vuepress": "2.0.0-rc.0" } } diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index ab624755..92a719de 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -6,11 +6,17 @@ settings: devDependencies: '@vuepress/client': - specifier: 2.0.0-beta.68 - version: 2.0.0-beta.68 + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 '@vuepress/plugin-prismjs': - specifier: 2.0.0-beta.68 - version: 2.0.0-beta.68 + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 + '@vuepress/plugin-register-components': + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 + '@vuepress/utils': + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 prismjs: specifier: ^1.29.0 version: 1.29.0 @@ -18,8 +24,8 @@ devDependencies: specifier: ^3.3.8 version: 3.3.8 vuepress: - specifier: 2.0.0-beta.68 - version: 2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8) + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0(@vuepress/client@2.0.0-rc.0)(vue@3.3.8) packages: @@ -50,8 +56,8 @@ packages: to-fast-properties: 2.0.0 dev: true - /@esbuild/android-arm64@0.18.20: - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + /@esbuild/android-arm64@0.19.7: + resolution: {integrity: sha512-YEDcw5IT7hW3sFKZBkCAQaOCJQLONVcD4bOyTXMZz5fr66pTHnAet46XAtbXAkJRfIn2YVhdC6R9g4xa27jQ1w==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -59,8 +65,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.18.20: - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + /@esbuild/android-arm@0.19.7: + resolution: {integrity: sha512-YGSPnndkcLo4PmVl2tKatEn+0mlVMr3yEpOOT0BeMria87PhvoJb5dg5f5Ft9fbCVgtAz4pWMzZVgSEGpDAlww==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -68,8 +74,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.18.20: - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + /@esbuild/android-x64@0.19.7: + resolution: {integrity: sha512-jhINx8DEjz68cChFvM72YzrqfwJuFbfvSxZAk4bebpngGfNNRm+zRl4rtT9oAX6N9b6gBcFaJHFew5Blf6CvUw==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -77,8 +83,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.18.20: - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + /@esbuild/darwin-arm64@0.19.7: + resolution: {integrity: sha512-dr81gbmWN//3ZnBIm6YNCl4p3pjnabg1/ZVOgz2fJoUO1a3mq9WQ/1iuEluMs7mCL+Zwv7AY5e3g1hjXqQZ9Iw==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -86,8 +92,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.18.20: - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + /@esbuild/darwin-x64@0.19.7: + resolution: {integrity: sha512-Lc0q5HouGlzQEwLkgEKnWcSazqr9l9OdV2HhVasWJzLKeOt0PLhHaUHuzb8s/UIya38DJDoUm74GToZ6Wc7NGQ==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -95,8 +101,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.18.20: - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + /@esbuild/freebsd-arm64@0.19.7: + resolution: {integrity: sha512-+y2YsUr0CxDFF7GWiegWjGtTUF6gac2zFasfFkRJPkMAuMy9O7+2EH550VlqVdpEEchWMynkdhC9ZjtnMiHImQ==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -104,8 +110,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.18.20: - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + /@esbuild/freebsd-x64@0.19.7: + resolution: {integrity: sha512-CdXOxIbIzPJmJhrpmJTLx+o35NoiKBIgOvmvT+jeSadYiWJn0vFKsl+0bSG/5lwjNHoIDEyMYc/GAPR9jxusTA==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -113,8 +119,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.18.20: - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + /@esbuild/linux-arm64@0.19.7: + resolution: {integrity: sha512-inHqdOVCkUhHNvuQPT1oCB7cWz9qQ/Cz46xmVe0b7UXcuIJU3166aqSunsqkgSGMtUCWOZw3+KMwI6otINuC9g==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -122,8 +128,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.18.20: - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + /@esbuild/linux-arm@0.19.7: + resolution: {integrity: sha512-Y+SCmWxsJOdQtjcBxoacn/pGW9HDZpwsoof0ttL+2vGcHokFlfqV666JpfLCSP2xLxFpF1lj7T3Ox3sr95YXww==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -131,8 +137,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.18.20: - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + /@esbuild/linux-ia32@0.19.7: + resolution: {integrity: sha512-2BbiL7nLS5ZO96bxTQkdO0euGZIUQEUXMTrqLxKUmk/Y5pmrWU84f+CMJpM8+EHaBPfFSPnomEaQiG/+Gmh61g==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -140,8 +146,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.18.20: - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + /@esbuild/linux-loong64@0.19.7: + resolution: {integrity: sha512-BVFQla72KXv3yyTFCQXF7MORvpTo4uTA8FVFgmwVrqbB/4DsBFWilUm1i2Oq6zN36DOZKSVUTb16jbjedhfSHw==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -149,8 +155,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.18.20: - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + /@esbuild/linux-mips64el@0.19.7: + resolution: {integrity: sha512-DzAYckIaK+pS31Q/rGpvUKu7M+5/t+jI+cdleDgUwbU7KdG2eC3SUbZHlo6Q4P1CfVKZ1lUERRFP8+q0ob9i2w==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -158,8 +164,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.18.20: - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + /@esbuild/linux-ppc64@0.19.7: + resolution: {integrity: sha512-JQ1p0SmUteNdUaaiRtyS59GkkfTW0Edo+e0O2sihnY4FoZLz5glpWUQEKMSzMhA430ctkylkS7+vn8ziuhUugQ==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -167,8 +173,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.18.20: - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + /@esbuild/linux-riscv64@0.19.7: + resolution: {integrity: sha512-xGwVJ7eGhkprY/nB7L7MXysHduqjpzUl40+XoYDGC4UPLbnG+gsyS1wQPJ9lFPcxYAaDXbdRXd1ACs9AE9lxuw==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -176,8 +182,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.18.20: - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + /@esbuild/linux-s390x@0.19.7: + resolution: {integrity: sha512-U8Rhki5PVU0L0nvk+E8FjkV8r4Lh4hVEb9duR6Zl21eIEYEwXz8RScj4LZWA2i3V70V4UHVgiqMpszXvG0Yqhg==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -185,8 +191,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.18.20: - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + /@esbuild/linux-x64@0.19.7: + resolution: {integrity: sha512-ZYZopyLhm4mcoZXjFt25itRlocKlcazDVkB4AhioiL9hOWhDldU9n38g62fhOI4Pth6vp+Mrd5rFKxD0/S+7aQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -194,8 +200,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.18.20: - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + /@esbuild/netbsd-x64@0.19.7: + resolution: {integrity: sha512-/yfjlsYmT1O3cum3J6cmGG16Fd5tqKMcg5D+sBYLaOQExheAJhqr8xOAEIuLo8JYkevmjM5zFD9rVs3VBcsjtQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -203,8 +209,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.18.20: - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + /@esbuild/openbsd-x64@0.19.7: + resolution: {integrity: sha512-MYDFyV0EW1cTP46IgUJ38OnEY5TaXxjoDmwiTXPjezahQgZd+j3T55Ht8/Q9YXBM0+T9HJygrSRGV5QNF/YVDQ==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -212,8 +218,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.18.20: - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + /@esbuild/sunos-x64@0.19.7: + resolution: {integrity: sha512-JcPvgzf2NN/y6X3UUSqP6jSS06V0DZAV/8q0PjsZyGSXsIGcG110XsdmuWiHM+pno7/mJF6fjH5/vhUz/vA9fw==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -221,8 +227,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.18.20: - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + /@esbuild/win32-arm64@0.19.7: + resolution: {integrity: sha512-ZA0KSYti5w5toax5FpmfcAgu3ZNJxYSRm0AW/Dao5up0YV1hDVof1NvwLomjEN+3/GMtaWDI+CIyJOMTRSTdMw==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -230,8 +236,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.18.20: - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + /@esbuild/win32-ia32@0.19.7: + resolution: {integrity: sha512-CTOnijBKc5Jpk6/W9hQMMvJnsSYRYgveN6O75DTACCY18RA2nqka8dTZR+x/JqXCRiKk84+5+bRKXUSbbwsS0A==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -239,8 +245,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.18.20: - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + /@esbuild/win32-x64@0.19.7: + resolution: {integrity: sha512-gRaP2sk6hc98N734luX4VpF318l3w+ofrtTu9j5L8EQXF+FzQKV6alCOHMVoJJHvVK/mGbwBXfOL1HETQu9IGQ==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -336,6 +342,102 @@ packages: fastq: 1.15.0 dev: true + /@rollup/rollup-android-arm-eabi@4.5.1: + resolution: {integrity: sha512-YaN43wTyEBaMqLDYeze+gQ4ZrW5RbTEGtT5o1GVDkhpdNcsLTnLRcLccvwy3E9wiDKWg9RIhuoy3JQKDRBfaZA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.5.1: + resolution: {integrity: sha512-n1bX+LCGlQVuPlCofO0zOKe1b2XkFozAVRoczT+yxWZPGnkEAKTTYVOGZz8N4sKuBnKMxDbfhUsB1uwYdup/sw==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.5.1: + resolution: {integrity: sha512-QqJBumdvfBqBBmyGHlKxje+iowZwrHna7pokj/Go3dV1PJekSKfmjKrjKQ/e6ESTGhkfPNLq3VXdYLAc+UtAQw==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.5.1: + resolution: {integrity: sha512-RrkDNkR/P5AEQSPkxQPmd2ri8WTjSl0RYmuFOiEABkEY/FSg0a4riihWQGKDJ4LnV9gigWZlTMx2DtFGzUrYQw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.5.1: + resolution: {integrity: sha512-ZFPxvUZmE+fkB/8D9y/SWl/XaDzNSaxd1TJUSE27XAKlRpQ2VNce/86bGd9mEUgL3qrvjJ9XTGwoX0BrJkYK/A==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.5.1: + resolution: {integrity: sha512-FEuAjzVIld5WVhu+M2OewLmjmbXWd3q7Zcx+Rwy4QObQCqfblriDMMS7p7+pwgjZoo9BLkP3wa9uglQXzsB9ww==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.5.1: + resolution: {integrity: sha512-f5Gs8WQixqGRtI0Iq/cMqvFYmgFzMinuJO24KRfnv7Ohi/HQclwrBCYkzQu1XfLEEt3DZyvveq9HWo4bLJf1Lw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.5.1: + resolution: {integrity: sha512-CWPkPGrFfN2vj3mw+S7A/4ZaU3rTV7AkXUr08W9lNP+UzOvKLVf34tWCqrKrfwQ0NTk5GFqUr2XGpeR2p6R4gw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.5.1: + resolution: {integrity: sha512-ZRETMFA0uVukUC9u31Ed1nx++29073goCxZtmZARwk5aF/ltuENaeTtRVsSQzFlzdd4J6L3qUm+EW8cbGt0CKQ==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.5.1: + resolution: {integrity: sha512-ihqfNJNb2XtoZMSCPeoo0cYMgU04ksyFIoOw5S0JUVbOhafLot+KD82vpKXOurE2+9o/awrqIxku9MRR9hozHQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.5.1: + resolution: {integrity: sha512-zK9MRpC8946lQ9ypFn4gLpdwr5a01aQ/odiIJeL9EbgZDMgbZjjT/XzTqJvDfTmnE1kHdbG20sAeNlpc91/wbg==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.5.1: + resolution: {integrity: sha512-5I3Nz4Sb9TYOtkRwlH0ow+BhMH2vnh38tZ4J4mggE48M/YyJyp/0sPSxhw1UeS1+oBgQ8q7maFtSeKpeRJu41Q==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@sindresorhus/merge-streams@1.0.0: resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==} engines: {node: '>=18'} @@ -399,14 +501,14 @@ packages: resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} dev: true - /@vitejs/plugin-vue@4.4.1(vite@4.5.0)(vue@3.3.8): - resolution: {integrity: sha512-HCQG8VDFDM7YDAdcj5QI5DvUi+r6xvo9LgvYdk7LSkUNwdpempdB5horkMSZsbdey9Ywsf5aaU8kEPw9M5kREA==} + /@vitejs/plugin-vue@4.5.0(vite@5.0.2)(vue@3.3.8): + resolution: {integrity: sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.0.0 + vite: ^4.0.0 || ^5.0.0 vue: ^3.2.25 dependencies: - vite: 4.5.0 + vite: 5.0.2 vue: 3.3.8 dev: true @@ -497,20 +599,20 @@ packages: resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==} dev: true - /@vuepress/bundler-vite@2.0.0-beta.68: - resolution: {integrity: sha512-N2grrjKIQEZJcb+JaG7ZimCoUH3bRK4zwHjPOLpBpplTQ/V5l99I90FMswpaCs7bKBiXTO0fiEUYn4Nw8V/qkQ==} + /@vuepress/bundler-vite@2.0.0-rc.0: + resolution: {integrity: sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A==} dependencies: - '@vitejs/plugin-vue': 4.4.1(vite@4.5.0)(vue@3.3.8) - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vitejs/plugin-vue': 4.5.0(vite@5.0.2)(vue@3.3.8) + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 autoprefixer: 10.4.16(postcss@8.4.31) connect-history-api-fallback: 2.0.0 postcss: 8.4.31 postcss-load-config: 4.0.1(postcss@8.4.31) - rollup: 3.29.4 - vite: 4.5.0 + rollup: 4.5.1 + vite: 5.0.2 vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) transitivePeerDependencies: @@ -527,29 +629,29 @@ packages: - typescript dev: true - /@vuepress/cli@2.0.0-beta.68: - resolution: {integrity: sha512-Br0aaJIWBtKjXBMmulLcN5hFOx8kbVHgs8K+EOASC9fLrq6LolsUJIdAiR+KyeMwQMyRInKrF3SF7k7AJetVeQ==} + /@vuepress/cli@2.0.0-rc.0: + resolution: {integrity: sha512-XWSIFO9iOR7N4O2lXIwS5vZuLjU9WU/aGAtmhMWEMxrdMx7TQaJbgrfpTUEbHMf+cPI1DXBbUbtmkqIvtfOV0w==} hasBin: true dependencies: - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 cac: 6.7.14 chokidar: 3.5.3 envinfo: 7.11.0 - esbuild: 0.18.20 + esbuild: 0.19.7 transitivePeerDependencies: - '@vue/composition-api' - supports-color - typescript dev: true - /@vuepress/client@2.0.0-beta.68: - resolution: {integrity: sha512-Y6amMnkPxpmn51vcgy5yzm3gpIaqZo4Pa8ItPFd7MW6GQy6HVZRNaV9ufzWRPOAedLHgpT4aVXomidvTMEKHVw==} + /@vuepress/client@2.0.0-rc.0: + resolution: {integrity: sha512-TwQx8hJgYONYxX+QltZ2aw9O5Ym6SKelfiUduuIRb555B1gece/jSVap3H/ZwyBhpgJMtG4+/Mrmf8nlDSHjvw==} dependencies: '@vue/devtools-api': 6.5.1 - '@vuepress/shared': 2.0.0-beta.68 - '@vueuse/core': 10.6.0(vue@3.3.8) + '@vuepress/shared': 2.0.0-rc.0 + '@vueuse/core': 10.6.1(vue@3.3.8) vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) transitivePeerDependencies: @@ -557,13 +659,13 @@ packages: - typescript dev: true - /@vuepress/core@2.0.0-beta.68: - resolution: {integrity: sha512-/c+3gdduDyiyeGARzui6Z5ZeZurRGcbVSmqcUfb8SjB7sHojDt+bq/7gYeXKXrJ4R0zPpmqshlZdNGOSY4+uGQ==} + /@vuepress/core@2.0.0-rc.0: + resolution: {integrity: sha512-uoOaZP1MdxZYJIAJcRcmYKKeCIVnxZeOuLMOOB9CPuAKSalT1RvJ1lztw6RX3q9SPnlqtSZPQXDncPAZivw4pA==} dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/markdown': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/markdown': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 vue: 3.3.8 transitivePeerDependencies: - '@vue/composition-api' @@ -571,8 +673,8 @@ packages: - typescript dev: true - /@vuepress/markdown@2.0.0-beta.68: - resolution: {integrity: sha512-wQOVw1QQSnkdKClTnv3dHw1A7Y+XF2eu2hJmhTf9XOnEMxQ9taacIq5iRuQdcfR+Y8rjWmrzrqWZL+MiJbxKMQ==} + /@vuepress/markdown@2.0.0-rc.0: + resolution: {integrity: sha512-USmqdKKMT6ZFHYRztTjKUlO8qgGfnEygMAAq4AzC/uYXiEfrbMBLAWJhteyGS56P3rGLj0OPAhksE681bX/wOg==} dependencies: '@mdit-vue/plugin-component': 1.0.0 '@mdit-vue/plugin-frontmatter': 1.0.0 @@ -584,8 +686,8 @@ packages: '@mdit-vue/types': 1.0.0 '@types/markdown-it': 13.0.6 '@types/markdown-it-emoji': 2.0.4 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 markdown-it: 13.0.2 markdown-it-anchor: 8.6.7(@types/markdown-it@13.0.6)(markdown-it@13.0.2) markdown-it-emoji: 2.0.2 @@ -594,12 +696,12 @@ packages: - supports-color dev: true - /@vuepress/plugin-active-header-links@2.0.0-beta.68: - resolution: {integrity: sha512-yMOvnzYrzZ70hCPWXlPrm6nU8q8MvrfhLf3R007ino7TWhlumTioYEnXKX3TH5+us1QM3W/CI+LUyr1si6leGg==} + /@vuepress/plugin-active-header-links@2.0.0-rc.0: + resolution: {integrity: sha512-UJdXLYNGL5Wjy5YGY8M2QgqT75bZ95EHebbqGi8twBdIJE9O+bM+dPJyYtAk2PIVqFORiw3Hj+PchsNSxdn9+g==} dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 ts-debounce: 4.0.0 vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) @@ -609,12 +711,12 @@ packages: - typescript dev: true - /@vuepress/plugin-back-to-top@2.0.0-beta.68: - resolution: {integrity: sha512-YobSlJUltm+zzTgJttmU1iDI0qUotRMl7TXnutAqJ7FTsPBUVrLQsXpfSEJnwwBbZ99VHTAF5FvXwtlZRuoLNg==} + /@vuepress/plugin-back-to-top@2.0.0-rc.0: + resolution: {integrity: sha512-6GPfuzV5lkAnR00BxRUhqMXwMWt741alkq2R6bln4N8BneSOwEpX/7vi19MGf232aKdS/Va4pF5p0/nJ8Sed/g==} dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 ts-debounce: 4.0.0 vue: 3.3.8 transitivePeerDependencies: @@ -623,14 +725,14 @@ packages: - typescript dev: true - /@vuepress/plugin-container@2.0.0-beta.68: - resolution: {integrity: sha512-oRGO9B9KgT9ZqjOcxBdTZI7eeI80qzYOYy8BGA+tYeKVy2AaLQk7GsUm3mMQn6Z82AdqBtRag/eUUiMo3p6toA==} + /@vuepress/plugin-container@2.0.0-rc.0: + resolution: {integrity: sha512-b7vrLN11YE7qiUDPfA3N9P7Z8fupe9Wbcr9KAE/bmfZ9VT4d6kzpVyoU7XHi99XngitsmnkaXP4aBvBF1c2AnA==} dependencies: '@types/markdown-it': 13.0.6 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/markdown': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/markdown': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 markdown-it: 13.0.2 markdown-it-container: 3.0.0 transitivePeerDependencies: @@ -639,14 +741,14 @@ packages: - typescript dev: true - /@vuepress/plugin-external-link-icon@2.0.0-beta.68: - resolution: {integrity: sha512-6oHeD0HT8SsFMxaKYEfc35Qx4vlJivJXbdZr/pcYbAEDSv3eORKrVnY9yZk5i6aTI/sxeTyEmoahqdcx+6uV6w==} + /@vuepress/plugin-external-link-icon@2.0.0-rc.0: + resolution: {integrity: sha512-o8bk0oIlj/BkKc02mq91XLDloq1VOz/8iNcRwKAeqBE6svXzdYiyoTGet0J/4iPuAetsCn75S57W6RioDJHMnQ==} dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/markdown': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/markdown': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 vue: 3.3.8 transitivePeerDependencies: - '@vue/composition-api' @@ -654,11 +756,11 @@ packages: - typescript dev: true - /@vuepress/plugin-git@2.0.0-beta.68: - resolution: {integrity: sha512-L3F5fMu0zVzl90xlZBjoHJSCHdGFfWGs624xcC66QKeFXU6xVt7lMB4wyuPYfi6opCSfDwigmVYcJOsMmbCdBg==} + /@vuepress/plugin-git@2.0.0-rc.0: + resolution: {integrity: sha512-r7UF77vZxaYeJQLygzodKv+15z3/dTLuGp4VcYO21W6BlJZvd4u9zqgiV7A//bZQvK4+3Hprylr0G3KgXqMewA==} dependencies: - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 execa: 8.0.1 transitivePeerDependencies: - '@vue/composition-api' @@ -666,13 +768,13 @@ packages: - typescript dev: true - /@vuepress/plugin-medium-zoom@2.0.0-beta.68: - resolution: {integrity: sha512-2bnxcvNQM+i9b5cDDgzitfyLawssPzcxVVOcscUozhvNSkiVre6aCVjStmLk9uWpsFPhtkWawdXCFYpCdBF7Ug==} + /@vuepress/plugin-medium-zoom@2.0.0-rc.0: + resolution: {integrity: sha512-peU1lYKsmKikIe/0pkJuHzD/k6xW2TuqdvKVhV4I//aOE1WxsREKJ4ACcldmoIsnysoDydAUqKT6xDPGyDsH2g==} dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 - medium-zoom: 1.0.8 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 + medium-zoom: 1.1.0 vue: 3.3.8 transitivePeerDependencies: - '@vue/composition-api' @@ -680,12 +782,12 @@ packages: - typescript dev: true - /@vuepress/plugin-nprogress@2.0.0-beta.68: - resolution: {integrity: sha512-72yFcUIaON4YUwjf/6qK1DkZcGnZAST/At2t2/esUVm3XOEPxqz2HwKYfU89Rp/+VZfTp0Nn8W4kXuLcC+V0KA==} + /@vuepress/plugin-nprogress@2.0.0-rc.0: + resolution: {integrity: sha512-rI+eK0Pg1KiZE+7hGmDUeSbgdWCid8Vnw0hFKNmjinDzGVmx4m03M6qfvclsI0SryH+lR7itZGLaR4gbTlrz/w==} dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) transitivePeerDependencies: @@ -694,11 +796,11 @@ packages: - typescript dev: true - /@vuepress/plugin-palette@2.0.0-beta.68: - resolution: {integrity: sha512-LILoXCY9NMi+doNz09HiUeNiElJy6ECbR/yodOBp+jcwGZ4RVPFp8PEeK3jCZ8+UuJxa1mmmi6dqTxp02xrAFQ==} + /@vuepress/plugin-palette@2.0.0-rc.0: + resolution: {integrity: sha512-wW70SCp3/K7s1lln5YQsBGTog2WXaQv5piva5zhXcQ47YGf4aAJpThDa5C/ot4HhkPOKn8Iz5s0ckxXZzW8DIg==} dependencies: - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 chokidar: 3.5.3 transitivePeerDependencies: - '@vue/composition-api' @@ -706,10 +808,10 @@ packages: - typescript dev: true - /@vuepress/plugin-prismjs@2.0.0-beta.68: - resolution: {integrity: sha512-IARRHzZ2XeLQPfelimqU/eexoItnwnz6z4tSkTIrV4PQeWg6EjMc92TxHyE+EEWAcka/DZxd42+xq0QV7FSJJQ==} + /@vuepress/plugin-prismjs@2.0.0-rc.0: + resolution: {integrity: sha512-c5WRI7+FhVjdbymOKQ8F2KY/Bnv7aQtWScVk8vCMUimNi7v7Wff/A/i3KSFNz/tge3LxiAeH/Dc2WS/OnQXwCg==} dependencies: - '@vuepress/core': 2.0.0-beta.68 + '@vuepress/core': 2.0.0-rc.0 prismjs: 1.29.0 transitivePeerDependencies: - '@vue/composition-api' @@ -717,14 +819,26 @@ packages: - typescript dev: true - /@vuepress/plugin-theme-data@2.0.0-beta.68: - resolution: {integrity: sha512-UFiMxJAD20mOK29P1H8zoHFNeDVer+2goQ9qy/VjDAbzE2I2yOa6TbJ7fWhSO8Vq0dCy7cX92wZMhXQIyUeNgQ==} + /@vuepress/plugin-register-components@2.0.0-rc.0: + resolution: {integrity: sha512-yN71x93j8ce99bqOwHn3lVfgiwsfhv21ByW/3em1kGXANjzOOoXOvt7ITbXNa5g6bsfjdJpoeUkUtFPwfK8dNA==} + dependencies: + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 + chokidar: 3.5.3 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-theme-data@2.0.0-rc.0: + resolution: {integrity: sha512-FXY3/Ml+rM6gNKvwdBF6vKAcwnSvtXCzKgQwJAw3ppQTKUkLcbOxqM+h4d8bzHWAAvdnEvQFug5uEZgWllBQbA==} dependencies: '@vue/devtools-api': 6.5.1 - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 vue: 3.3.8 transitivePeerDependencies: - '@vue/composition-api' @@ -732,36 +846,36 @@ packages: - typescript dev: true - /@vuepress/shared@2.0.0-beta.68: - resolution: {integrity: sha512-vnlOOchZ7ZHeTQuFDKcTC1AKF5zl4+XKwZZdpX9cUkIl3rYbM4y80yoWvfG5SQnPjjoYG57g4Qz21Fa8u/CnCQ==} + /@vuepress/shared@2.0.0-rc.0: + resolution: {integrity: sha512-ikdSfjRv5LGM1iv4HHwF9P6gqTjaFCXKPK+hzlkHFHNZO1GLqk7/BPc4F51tAG1s8TcLhUZc+54LrfgS7PkXXA==} dependencies: '@mdit-vue/types': 1.0.0 '@vue/shared': 3.3.8 dev: true - /@vuepress/theme-default@2.0.0-beta.68: - resolution: {integrity: sha512-qsIaM3ZVjJb6KeuScxVRLfLylBa3kK7IriZ9YlmkHl2NwzspsqVMTh4Ozd2MlkhzMH4TnB1XLybTQKGCZnQBVw==} + /@vuepress/theme-default@2.0.0-rc.0: + resolution: {integrity: sha512-I8Y08evDmMuD1jh3NftPpFFSlCWOizQDJLjN7EQwcg7jiAP4A7c2REo6nBN2EmP24Mi7UrRM+RnytHR5V+pElA==} peerDependencies: - sass-loader: ^13.2.1 + sass-loader: ^13.3.2 peerDependenciesMeta: sass-loader: optional: true dependencies: - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/plugin-active-header-links': 2.0.0-beta.68 - '@vuepress/plugin-back-to-top': 2.0.0-beta.68 - '@vuepress/plugin-container': 2.0.0-beta.68 - '@vuepress/plugin-external-link-icon': 2.0.0-beta.68 - '@vuepress/plugin-git': 2.0.0-beta.68 - '@vuepress/plugin-medium-zoom': 2.0.0-beta.68 - '@vuepress/plugin-nprogress': 2.0.0-beta.68 - '@vuepress/plugin-palette': 2.0.0-beta.68 - '@vuepress/plugin-prismjs': 2.0.0-beta.68 - '@vuepress/plugin-theme-data': 2.0.0-beta.68 - '@vuepress/shared': 2.0.0-beta.68 - '@vuepress/utils': 2.0.0-beta.68 - '@vueuse/core': 10.6.0(vue@3.3.8) + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/plugin-active-header-links': 2.0.0-rc.0 + '@vuepress/plugin-back-to-top': 2.0.0-rc.0 + '@vuepress/plugin-container': 2.0.0-rc.0 + '@vuepress/plugin-external-link-icon': 2.0.0-rc.0 + '@vuepress/plugin-git': 2.0.0-rc.0 + '@vuepress/plugin-medium-zoom': 2.0.0-rc.0 + '@vuepress/plugin-nprogress': 2.0.0-rc.0 + '@vuepress/plugin-palette': 2.0.0-rc.0 + '@vuepress/plugin-prismjs': 2.0.0-rc.0 + '@vuepress/plugin-theme-data': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 + '@vueuse/core': 10.6.1(vue@3.3.8) sass: 1.69.5 vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) @@ -771,13 +885,13 @@ packages: - typescript dev: true - /@vuepress/utils@2.0.0-beta.68: - resolution: {integrity: sha512-asRN+c8JCIVJWusP/V0FY8rgArGwuKXarEIKwFHcaR7x9IeB3Iww4p8raQHb1xYJADM7QFXx1gs2oM6Fx4XsUw==} + /@vuepress/utils@2.0.0-rc.0: + resolution: {integrity: sha512-Q1ay/woClDHcW0Qe91KsnHoupdNN0tp/vhjvVLuAYxlv/1Obii7hz9WFcajyyGEhmsYxdvG2sGmcxFA02tuKkw==} dependencies: '@types/debug': 4.1.12 '@types/fs-extra': 11.0.4 '@types/hash-sum': 1.0.2 - '@vuepress/shared': 2.0.0-beta.68 + '@vuepress/shared': 2.0.0-rc.0 debug: 4.3.4 fs-extra: 11.1.1 globby: 14.0.0 @@ -789,24 +903,24 @@ packages: - supports-color dev: true - /@vueuse/core@10.6.0(vue@3.3.8): - resolution: {integrity: sha512-+Yee+g9+9BEbvkyGdn4Bf4yZx9EfocAytpV2ZlrlP7xcz+qznLmZIDqDroTvc5vtMkWZicisgEv8dt3+jL+HQg==} + /@vueuse/core@10.6.1(vue@3.3.8): + resolution: {integrity: sha512-Pc26IJbqgC9VG1u6VY/xrXXfxD33hnvxBnKrLlA2LJlyHII+BSrRoTPJgGYq7qZOu61itITFUnm6QbacwZ4H8Q==} dependencies: '@types/web-bluetooth': 0.0.20 - '@vueuse/metadata': 10.6.0 - '@vueuse/shared': 10.6.0(vue@3.3.8) + '@vueuse/metadata': 10.6.1 + '@vueuse/shared': 10.6.1(vue@3.3.8) vue-demi: 0.14.6(vue@3.3.8) transitivePeerDependencies: - '@vue/composition-api' - vue dev: true - /@vueuse/metadata@10.6.0: - resolution: {integrity: sha512-mzKHkHoiK6xVz01VzQjM2l6ofUanEaofgEGPgDHcAzlvOTccPRTIdEuzneOUTYxgfm1vkDikS6rtrEw/NYlaTQ==} + /@vueuse/metadata@10.6.1: + resolution: {integrity: sha512-qhdwPI65Bgcj23e5lpGfQsxcy0bMjCAsUGoXkJ7DsoeDUdasbZ2DBa4dinFCOER3lF4gwUv+UD2AlA11zdzMFw==} dev: true - /@vueuse/shared@10.6.0(vue@3.3.8): - resolution: {integrity: sha512-0t4MVE18sO+/4Gh0jfeOXBTjKeV4606N9kIrDOLPjFl8Rwnlodn+QC5A4LfJuysK7aOsTMjF3KnzNeueaI0xlQ==} + /@vueuse/shared@10.6.1(vue@3.3.8): + resolution: {integrity: sha512-TECVDTIedFlL0NUfHWncf3zF9Gc4VfdxfQc8JFwoVZQmxpONhLxFrlm0eHQeidHj4rdTPL3KXJa0TZCk1wnc5Q==} dependencies: vue-demi: 0.14.6(vue@3.3.8) transitivePeerDependencies: @@ -989,34 +1103,34 @@ packages: hasBin: true dev: true - /esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + /esbuild@0.19.7: + resolution: {integrity: sha512-6brbTZVqxhqgbpqBR5MzErImcpA0SQdoKOkcWK/U30HtQxnokIpG3TX2r0IJqbFUzqLjhU/zC1S5ndgakObVCQ==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 + '@esbuild/android-arm': 0.19.7 + '@esbuild/android-arm64': 0.19.7 + '@esbuild/android-x64': 0.19.7 + '@esbuild/darwin-arm64': 0.19.7 + '@esbuild/darwin-x64': 0.19.7 + '@esbuild/freebsd-arm64': 0.19.7 + '@esbuild/freebsd-x64': 0.19.7 + '@esbuild/linux-arm': 0.19.7 + '@esbuild/linux-arm64': 0.19.7 + '@esbuild/linux-ia32': 0.19.7 + '@esbuild/linux-loong64': 0.19.7 + '@esbuild/linux-mips64el': 0.19.7 + '@esbuild/linux-ppc64': 0.19.7 + '@esbuild/linux-riscv64': 0.19.7 + '@esbuild/linux-s390x': 0.19.7 + '@esbuild/linux-x64': 0.19.7 + '@esbuild/netbsd-x64': 0.19.7 + '@esbuild/openbsd-x64': 0.19.7 + '@esbuild/sunos-x64': 0.19.7 + '@esbuild/win32-arm64': 0.19.7 + '@esbuild/win32-ia32': 0.19.7 + '@esbuild/win32-x64': 0.19.7 dev: true /escalade@3.1.1: @@ -1293,8 +1407,8 @@ packages: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} dev: true - /medium-zoom@1.0.8: - resolution: {integrity: sha512-CjFVuFq/IfrdqesAXfg+hzlDKu6A2n80ZIq0Kl9kWjoHh9j1N9Uvk5X0/MmN0hOfm5F9YBswlClhcwnmtwz7gA==} + /medium-zoom@1.1.0: + resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} dev: true /merge-stream@2.0.0: @@ -1476,11 +1590,23 @@ packages: engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true - /rollup@3.29.4: - resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} + /rollup@4.5.1: + resolution: {integrity: sha512-0EQribZoPKpb5z1NW/QYm3XSR//Xr8BeEXU49Lc/mQmpmVVG5jPUVrpc2iptup/0WMrY9mzas0fxH+TjYvG2CA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.5.1 + '@rollup/rollup-android-arm64': 4.5.1 + '@rollup/rollup-darwin-arm64': 4.5.1 + '@rollup/rollup-darwin-x64': 4.5.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.5.1 + '@rollup/rollup-linux-arm64-gnu': 4.5.1 + '@rollup/rollup-linux-arm64-musl': 4.5.1 + '@rollup/rollup-linux-x64-gnu': 4.5.1 + '@rollup/rollup-linux-x64-musl': 4.5.1 + '@rollup/rollup-win32-arm64-msvc': 4.5.1 + '@rollup/rollup-win32-ia32-msvc': 4.5.1 + '@rollup/rollup-win32-x64-msvc': 4.5.1 fsevents: 2.3.3 dev: true @@ -1640,12 +1766,12 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /vite@4.5.0: - resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} - engines: {node: ^14.18.0 || >=16.0.0} + /vite@5.0.2: + resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@types/node': '>= 14' + '@types/node': ^18.0.0 || >=20.0.0 less: '*' lightningcss: ^1.21.0 sass: '*' @@ -1668,9 +1794,9 @@ packages: terser: optional: true dependencies: - esbuild: 0.18.20 + esbuild: 0.19.7 postcss: 8.4.31 - rollup: 3.29.4 + rollup: 4.5.1 optionalDependencies: fsevents: 2.3.3 dev: true @@ -1714,19 +1840,19 @@ packages: '@vue/shared': 3.3.8 dev: true - /vuepress-vite@2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8): - resolution: {integrity: sha512-/+qO1uO8EX6ERFFgaWqKNW/nD89JrmS9sgMYxREPM0lsWj1E+bgkvc8KJ6DIgWRc1lRs0kMrEhK3UHC0md8ESQ==} + /vuepress-vite@2.0.0-rc.0(@vuepress/client@2.0.0-rc.0)(vue@3.3.8): + resolution: {integrity: sha512-+2XBejeiskPyr2raBeA2o4uDFDsjtadpUVmtio3qqFtQpOhidz/ORuiTLr2UfLtFn1ASIHP6Vy2YjQ0e/TeUVw==} engines: {node: '>=18.16.0'} hasBin: true peerDependencies: - '@vuepress/client': 2.0.0-beta.68 + '@vuepress/client': 2.0.0-rc.0 vue: ^3.3.4 dependencies: - '@vuepress/bundler-vite': 2.0.0-beta.68 - '@vuepress/cli': 2.0.0-beta.68 - '@vuepress/client': 2.0.0-beta.68 - '@vuepress/core': 2.0.0-beta.68 - '@vuepress/theme-default': 2.0.0-beta.68 + '@vuepress/bundler-vite': 2.0.0-rc.0 + '@vuepress/cli': 2.0.0-rc.0 + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/theme-default': 2.0.0-rc.0 vue: 3.3.8 transitivePeerDependencies: - '@types/node' @@ -1743,12 +1869,12 @@ packages: - typescript dev: true - /vuepress@2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8): - resolution: {integrity: sha512-75naWJMIwyD1WiVswN01Am4JwcRxlUPLTkxN/345dVaVAgKyzIDKKJDgmUN6MKZkDN2z2dDKLMcquI6VUrlCRg==} + /vuepress@2.0.0-rc.0(@vuepress/client@2.0.0-rc.0)(vue@3.3.8): + resolution: {integrity: sha512-sydt/B7+pIw926G5PntYmptLkC5o2buXKh+WR1+P2KnsvkXU+UGnQrJJ0FBvu/4RNuY99tkUZd59nyPhEmRrCg==} engines: {node: '>=18.16.0'} hasBin: true dependencies: - vuepress-vite: 2.0.0-beta.68(@vuepress/client@2.0.0-beta.68)(vue@3.3.8) + vuepress-vite: 2.0.0-rc.0(@vuepress/client@2.0.0-rc.0)(vue@3.3.8) transitivePeerDependencies: - '@types/node' - '@vue/composition-api' diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index 1470c992..587c886b 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -39,7 +39,7 @@ messages = { "functions": {"en": "Functions", "ru": "Функции"}, "types": {"en": "Types", "ru": "Типы"}, "example": {"en": "Example", "ru": "Пример"}, - "since": {"en": "since", "ru": "начиная с"} + "since": {"en": "Since", "ru": "Начиная с"} } // Write modules pages to vuepress config @@ -120,7 +120,7 @@ def writeFunctions(f, functions, lang, level = 2) { writeText(f, "\n`%s(%s)`".sprintf(info.name, info.args)) writeScope(f, info.scope ?? "") if length(info.since ?? "") { - writeSince(f, info.since, lang) + writeSince(f, info.since, lang, true) } writeDescription(f, info, lang, " — %s") writeLine(f, "") @@ -148,8 +148,8 @@ def writeTypes(f, types, lang) { } def writeScope(f, scope) = match(scope) { - case "android": writeText(f, " ") - case "desktop": writeText(f, " ") + case "android": writeText(f, " ") + case "desktop": writeText(f, " ") case _: { } } @@ -160,8 +160,12 @@ def writeDescription(f, obj, lang, format = "%s") { } } -def writeSince(f, since, lang) { - writeText(f, "".sprintf(messages.since[lang], since)) +def writeSince(f, version, lang, isInline = false) { + if (isInline) { + writeText(f, "".sprintf(version)) + } else { + writeText(f, "".sprintf(messages.since[lang], version)) + } } // -- utils From 90314e0cb910e362fc49b8ed4e163261d2f656da Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 25 Nov 2023 17:38:27 +0200 Subject: [PATCH 372/448] Add Gradle ownlangExec and generateMarkdownModules tasks --- docs/build.gradle | 11 +++++++++++ modules/server/build.gradle | 2 +- ownlang-desktop/build.gradle | 36 +++++++++++++++++++++++++----------- settings.gradle | 1 + 4 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 docs/build.gradle diff --git a/docs/build.gradle b/docs/build.gradle new file mode 100644 index 00000000..9b9b9051 --- /dev/null +++ b/docs/build.gradle @@ -0,0 +1,11 @@ +tasks.register('generateMarkdownModules') { + group = "documentation" + def ownlangExec = tasks.getByPath(':ownlang-desktop:ownlangExec') + doFirst { + ownlangExec.configure { + workingDir '../docs/src' + args '-f', 'docgen-md.own' + } + } + finalizedBy ownlangExec +} \ No newline at end of file diff --git a/modules/server/build.gradle b/modules/server/build.gradle index 80f05708..3ad23262 100644 --- a/modules/server/build.gradle +++ b/modules/server/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation "org.slf4j:slf4j-simple:${versions.slf4j}" implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" - testImplementation platform("org.junit:junit-bom:") + testImplementation platform("org.junit:junit-bom:${versions.junit}") testImplementation 'org.junit.jupiter:junit-jupiter' } diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index 04c30364..bb0defd3 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -12,6 +12,12 @@ application { mainClass = project.mainClassName } +jar { + manifest { + attributes 'Main-Class': project.mainClassName + } +} + dependencies { implementation project(":ownlang-core") implementation project(":ownlang-parser") @@ -26,25 +32,33 @@ test { useJUnitPlatform() } -tasks.register('runProgram', JavaExec) { - group = "application" - description = "Run sample program" +def ownlangExec = tasks.register('ownlangExec', JavaExec) { dependsOn classes mainClass = project.mainClassName classpath = sourceSets.main.runtimeClasspath standardInput = System.in - ignoreExitValue true - args '-f ../program.own'.split(' ') } -tasks.register('runOptimizing', JavaExec) { +tasks.register('runProgram') { + group = "application" + description = "Run sample program" + doFirst { + ownlangExec.configure { + args '-f ../program.own'.split(' ') + } + } + finalizedBy ownlangExec +} + +tasks.register('runOptimizing') { group = "application" description = "Run sample program with optimizations and measurements" - dependsOn classes - mainClass = project.mainClassName - classpath = sourceSets.main.runtimeClasspath - ignoreExitValue true - args '-o 9 -m -a -f ../program.own'.split(' ') + doFirst { + ownlangExec.configure { + args '-o 9 -m -a -f ../program.own'.split(' ') + } + } + finalizedBy ownlangExec } tasks.register('runOptimizationDumper', JavaExec) { diff --git a/settings.gradle b/settings.gradle index 72a528c1..68c60705 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,6 +4,7 @@ include 'ownlang-core' include 'ownlang-parser' include 'ownlang-desktop' include 'ownlang-utils' +include 'docs' final def modules = ['main', 'canvasfx', 'server'] From 2fbd578b771d158083c4b1aa5390be0916cb7870 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 27 Nov 2023 19:14:01 +0200 Subject: [PATCH 373/448] Preserve the order of Map elements by default --- .../src/main/java/com/annimon/ownlang/parser/Parser.java | 2 +- .../ownlang/parser/optimization/OptimizationVisitor.java | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index cf8522f1..271c3ee9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -388,7 +388,7 @@ private Node array() { private Node map() { // {key1 : value1, key2 : value2, ...} consume(TokenType.LBRACE); - final Map elements = new HashMap<>(); + final Map elements = new LinkedHashMap<>(); while (!match(TokenType.RBRACE)) { final Node key = primary(); consume(TokenType.COLON); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 0f15305c..29a90d35 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -5,10 +5,7 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.parser.ast.*; import static com.annimon.ownlang.parser.visitors.VisitorUtils.isValue; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public abstract class OptimizationVisitor implements ResultVisitor { @@ -264,7 +261,7 @@ public Node visit(IncludeStatement s, T t) { @Override public Node visit(MapExpression s, T t) { - final Map elements = new HashMap<>(s.elements.size()); + final Map elements = new LinkedHashMap<>(s.elements.size()); boolean changed = false; for (Map.Entry entry : s.elements.entrySet()) { final Node key = entry.getKey().accept(this, t); From 3cafd2922128eb72c07643cff5cfdc76e19bb198 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 27 Nov 2023 19:52:54 +0200 Subject: [PATCH 374/448] Rearrange module pages --- docs/src/docgen-md.own | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index 587c886b..622f2e18 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -1,4 +1,4 @@ -use std, types, files, yaml, functional +use std, types, files, json, yaml, functional INPUT_PATH_FMT = "./modules/%s.yml" OUTPUT_DIR_FMT = "../docs/%s/modules" @@ -7,33 +7,39 @@ OUTPUT_PATH_FMT = OUTPUT_DIR_FMT + "/%s.md" LANGS = ["en", "ru"] MODULES = [ "std", - "types", - "math", "date", + "downloader", "files", + "functional", "http", + "java", + "math", + "ounit", + "regex", + "robot", "socket", - "downloader", + "types", + // formats "base64", "json", "yaml", - "zip", "gzip", - "functional", - "robot", - "ounit", + "zip" + + // Desktop-only "canvas", "canvasfx", "forms", - "java", - "jdbc", - "regex", + "jdbc" + + // Android-only "android", "canvas_android", "forms_android", - "imageprocessing_android", - "gps_android" + "gps_android", + "imageprocessing_android" ] + messages = { "constants": {"en": "Constants", "ru": "Константы"}, "functions": {"en": "Functions", "ru": "Функции"}, @@ -43,10 +49,9 @@ messages = { } // Write modules pages to vuepress config +modulesPages = jsonencode(map(MODULES, def(m) = m + ".md")) f = fopen("../docs/.vuepress/configs/modules.js", "w") -writeLine(f, "export default [") -writeLine(f, stream(MODULES).map(def(m) = " \"%s.md\"".sprintf(m)).joining(",\n")) -writeLine(f, "]") +writeLine(f, "export default " + modulesPages) flush(f) fclose(f) From 779f4f936817e5eeb0bce3c58088ed69b19671e2 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 27 Nov 2023 19:54:01 +0200 Subject: [PATCH 375/448] Collapse long Constants block by default --- docs/src/docgen-md.own | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index 622f2e18..aff6ccce 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -45,7 +45,8 @@ messages = { "functions": {"en": "Functions", "ru": "Функции"}, "types": {"en": "Types", "ru": "Типы"}, "example": {"en": "Example", "ru": "Пример"}, - "since": {"en": "Since", "ru": "Начиная с"} + "since": {"en": "Since", "ru": "Начиная с"}, + "elements": {"en": " elements", "ru": " элементов"} } // Write modules pages to vuepress config @@ -104,11 +105,13 @@ def writeConstants(f, constants, lang) { } else { mapValues = constValue.substring(1, constValue.length - 1).split(", ") if (mapValues.length >= 7) { - writeText(f, "\n\n```own\n{\n "); - writeText(f, mapValues.joinToString(",\n ")); - writeText(f, "\n}\n```"); + writeText(f, "\n\n::: details %d %s".sprintf(mapValues.length, messages.elements[lang])); + writeText(f, "\n\n```own:no-line-numbers\n{\n "); + writeText(f, mapValues.joinToString(",\n ")); + writeText(f, "\n}\n```"); + writeText(f, "\n:::"); } else { - writeText(f, "`%s`".sprintf(constValue)); + writeText(f, "`%s`".sprintf(constValue)); } } writeLine(f, "") From 02fd959e58d7b249b7bec7fc7859c8b860166972 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 27 Nov 2023 19:55:11 +0200 Subject: [PATCH 376/448] Add new functions documentation --- docs/src/modules/functional.yml | 180 ++++++++++++------ docs/src/modules/std.yml | 52 ++++- .../resources/modules/functional/groupby.own | 13 ++ 3 files changed, 185 insertions(+), 60 deletions(-) diff --git a/docs/src/modules/functional.yml b/docs/src/modules/functional.yml index 4ee5f3ba..c0b554fd 100644 --- a/docs/src/modules/functional.yml +++ b/docs/src/modules/functional.yml @@ -10,11 +10,11 @@ constants: desc: "function which returns passed argument" desc_ru: "функция, которая возвращает переданный в неё аргумент" functions: - - name: "chain" + - name: chain args: "data, functions..." desc: "" desc_ru: "" - - name: "combine" + - name: combine args: "functions..." desc: "combines functions" desc_ru: "комбинирует функции (композиция)" @@ -55,7 +55,7 @@ functions: nums = [1,2,3,4,5] print filter(nums, def(x) = x % 2 == 0) // [2, 4] - - name: "flatmap" + - name: flatmap args: "array, mapper" desc: "converts each element of an array to other array" desc_ru: "преобразует каждый элемент массива в массив элементов" @@ -69,7 +69,7 @@ functions: arr[i] = x return arr }) // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] - - name: "foreach" + - name: foreach args: "data, consumer" desc: "invokes function `consumer` for each element of array or map `data`\n\nIf `data` - массив, то в функции consumer необходим один параметр, если объект - два (ключ и значение)." desc_ru: "для каждого элемента в массиве или объекте `data` вызывает функцию `consumer`\n\nЕсли `data` - массив, то в функции `consumer` необходим один параметр, если объект - два (ключ и значение)." @@ -80,7 +80,7 @@ functions: foreach({"key": 1, "key2": "text"}, def(key, value) { print key + ": " + value }) - - name: "map" + - name: map args: "data, mapper..." desc: "converts elements of array or map. If `data` is array - `mapper` converts his elements, if `data` is object - you need to pass `keyMapper` - converts keys and `valueMapper` - converts values" desc_ru: "преобразует элементы массива или объекта.\n\nЕсли `data` - массив, то функция `mapper` преобразует значения, если объект - необходимо передать две функции: `keyMapper` - преобразует ключи и `valueMapper` - преобразует значения" @@ -89,7 +89,7 @@ functions: nums = [3,4,5] print map(nums, def(x) = x * x) // [9, 16, 25] - - name: "reduce" + - name: reduce args: "data, identity, accumulator" desc: "converts elements of an array or a map to one value, e.g. sum of elements or concatenation string. `accumulator` takes one argument for array and two arguments for object (key and value)." desc_ru: "преобразует элементы массива или объекта в одно значение, например сумма элементов или объединение в строку.\n\nЕсли `data` - массив, то в функции `accumulator` необходим один параметр, если объект - два (ключ и значение)" @@ -98,10 +98,10 @@ functions: nums = [1,2,3,4,5] print reduce(nums, 0, def(x, y) = x + y) // 15 - - name: "sortby" + - name: sortby args: "array, function" - desc: "sorts elements of an array or an object by `function` result" - desc_ru: "сортирует элементы массива по данным в функции `function`" + desc: "sorts elements of an array or a map by `function` result" + desc_ru: "сортирует элементы массива или объекта по данным в функции `function`" example: |- use functional @@ -111,51 +111,125 @@ functions: {"k1": 4, "k2": "z"}, {"k1": 5, "k2": "p"}, ] - print sortby(data, def(v) = v.k1) // [{k1=2, k2=x}, {k1=4, k2=z}, {k1=5, k2=p}, {k1=7, k2=d}] - print sortby(data, def(v) = v.k2) // [{k1=7, k2=d}, {k1=5, k2=p}, {k1=2, k2=x}, {k1=4, k2=z}] - - name: "stream" - args: "data" - desc: |- - creates stream from data and returns StreamValue - - StreamValue functions: - - `filter(func)` - filters elements - - `map(func)` - converts each element - - `flatMap(func)` - converts each element to array - - `sorted(func)` - sorts elements with comparator function - - `sortBy(func)` - applies function, then sorts elements - - `takeWhile(func)` - takes elements while predicate function returns true - - `dropWhile(func)` - skips elements while predicate function returns true - - `peek(func)` - executes function for each element and returns stream - - `skip(count)` - skips count elements - - `limit(count)` - limits elements size - - `custom(func)` - performs custom operation - - `reduce(func)` - converts elements to one value - - `forEach(func)` - executes function for each element - - `joining(delimiter = "", prefix = "", suffix = "")` - joins elements into a string - - `toArray()` - returns array of elements - - `count()` - returns count of elements - desc_ru: |- - создаёт stream из данных и возвращает StreamValue + println sortby(data, def(v) = v.k1) // [{k1=2, k2=x}, {k1=4, k2=z}, {k1=5, k2=p}, {k1=7, k2=d}] + println sortby(data, def(v) = v.k2) // [{k1=7, k2=d}, {k1=5, k2=p}, {k1=2, k2=x}, {k1=4, k2=z}] + - name: groupby + args: "data, function" + desc: "groups elements of an array or a map by `function` result" + desc_ru: "группирует элементы массива или объекта на основе результата функции `function`" + since: 2.0.0 + example: |- + use functional - Функции StreamValue: - - `filter(func)` - фильтрует элементы - - `map(func)` - преобразует каждый элемент - - `flatMap(func)` - преобразует каждый элемент в массив - - `sorted(func)` - сортирует элементы в соответствии с функцией-компаратором - - `sortBy(func)` - применяет функцию, затем сортирует элементы - - `takeWhile(func)` - собирает элементы пока функция-предикат возвращает true - - `dropWhile(func)` - пропускает элементы пока функция-предикат возвращает true - - `peek(func)` - вызывает функцию для каждого элемента и возвращает stream - - `skip(count)` - пропускает указанное количество элементов - - `limit(count)` - ограничивает количество элементов - - `custom(func)` - выполняет пользовательскую операцию над данными - - `reduce(func)` - преобразует элементы в одно значение - - `forEach(func)` - вызывает функцию для каждого элемента - - `joining(delimiter = "", prefix = "", suffix = "")` - склеивает элементы в строку - - `toArray()` - возвращает массив элементов - - `count()` - возвращает количество элементов + data = [ + {"k1": 2, "k2": "x"}, + {"k1": 4, "k2": "z"}, + {"k1": 5, "k2": "p"}, + ] + println groupby(data, def(e) = e.k1) // {"2"=[{k1=2, k2=x}], "4"=[{k1=4, k2=z}], "5"=[{k2=p, k1=5}]} + println groupby(data, def(e) = e.k2) // {"x"=[{k1=2, k2=x}], "z"=[{k1=4, k2=z}], "p"=[{k2=p, k1=5}]} + - name: stream + args: data + desc: creates stream from data and returns `StreamValue` + desc_ru: создаёт stream из данных и возвращает `StreamValue` - name: takewhile args: 'data, predicate' desc: 'takes elements while predicate function returns true' - desc_ru: 'собирает элементы пока функция-предикат возвращает true' \ No newline at end of file + desc_ru: 'собирает элементы пока функция-предикат возвращает true' +types: + - name: StreamValue + functions: + - name: filter + args: func + desc: filters elements based on predicate function result (true - remain, false - drop) + desc_ru: фильтрует элементы на основе результата функции-предиката (true - оставить, false - убрать) + - name: filterNot + args: func + desc: filters elements based on negated predicate function result (false - remain, true - drop) + desc_ru: фильтрует элементы на основе обратного результата функции-предиката (false - оставить, true - убрать) + since: 2.0.0 + - name: map + args: func + desc: converts each element + desc_ru: преобразует каждый элемент + - name: flatMap + args: func + desc: converts each element to array + desc_ru: преобразует каждый элемент в массив + - name: sorted + args: func + desc: sorts elements with comparator function + desc_ru: сортирует элементы в соответствии с функцией-компаратором + - name: sortBy + args: func + desc: applies function, then sorts elements + desc_ru: применяет функцию, затем сортирует элементы + - name: groupBy + args: func + desc: groups elements based on function result + desc_ru: группирует элементы на основе результата выполнения функции + since: 2.0.0 + - name: takeWhile + args: func + desc: takes elements while predicate function returns true + desc_ru: собирает элементы пока функция-предикат возвращает true + - name: dropWhile + args: func + desc: skips elements while predicate function returns true, returns remaining elements + desc_ru: пропускает элементы пока функция-предикат возвращает true + - name: peek + args: func + desc: executes function for each element and returns stream + desc_ru: вызывает функцию для каждого элемента и возвращает stream + - name: skip + args: count + desc: skips `count` elements + desc_ru: пропускает указанное количество элементов + - name: limit + args: count + desc: limits elements size + desc_ru: ограничивает количество элементов + - name: custom + args: func + desc: performs custom operation + desc_ru: выполняет пользовательскую операцию над данными + example: |- + use std, functional + + println stream([1, 2, 3, 4]) + .custom(::reverse) + .toArray() + + def reverse(container) { + size = length(container) + result = newarray(size) + for i : range(size) { + result[size - i - 1] = container[i] + } + return result + } + - name: reduce + args: func + desc: converts elements to one value + desc_ru: преобразует элементы в одно значение + - name: forEach + args: func + desc: executes function for each element + desc_ru: вызывает функцию для каждого элемента + - name: forEachIndexed + args: func + desc: executes function for each element and its index + desc_ru: вызывает функцию для каждого элемента и его порядкового номера + since: 2.0.0 + - name: joining + args: delimiter = "", prefix = "", suffix = "" + desc: joins elements into a string + desc_ru: склеивает элементы в строку + - name: toArray + args: "" + desc: returns array of elements + desc_ru: возвращает массив элементов + - name: count + args: "" + desc: returns the elements count + desc_ru: возвращает количество элементов diff --git a/docs/src/modules/std.yml b/docs/src/modules/std.yml index 18afef73..92abeba2 100644 --- a/docs/src/modules/std.yml +++ b/docs/src/modules/std.yml @@ -11,7 +11,7 @@ constants: - name: OwnLang typeName: map type: 4 - value: "{PLATFORM=android/desktop, VERSION=1.5.0_000000, VERSION_MAJOR=1, VERSION_MINOR=5, VERSION_PATCH=0}" + value: "{PLATFORM=android/desktop, VERSION=2.0.1_000000, VERSION_MAJOR=2, VERSION_MINOR=0, VERSION_PATCH=1}" since: 1.4.0 functions: - name: arrayCombine @@ -76,11 +76,39 @@ functions: echo(1, "abc") // выведет строку "1 abc" в консоль echo(1, 2, 3, 4, 5, "a", "b") // выведет строку "1 2 3 4 5 a b" в консоль" + - name: exit + args: status + desc: terminates an application with provided status code. Non-zero values indicates abnormal termination + desc_ru: завершает работу приложения с заданным кодом. Ненулевое значение означает завершение с ошибкой + since: 2.0.0 + example: |- + use std + + println "Bye!" + exit(0) + example_ru: |- + use std + + println "До свидания!" + exit(0) - name: getBytes args: input, charset = "UTF-8" desc: returns byte array of the string in the given charset desc_ru: возвращает массив байт строки в заданной кодировке since: 1.5.0 + - name: getenv + args: key, defaultValue = "" + desc: returns the value of the specified environment variable if it's exists, returns `defaultValue` otherwise + desc_ru: возвращает значение указанной переменной среды, если такова существует. В противном случае возвращает `defaultValue` + since: 2.0.0 + example: |- + use std + println getenv("JAVA_HOME") + - name: getprop + args: key, defaultValue = "" + desc: returns the value of the system property if it's exists, returns `defaultValue` otherwise + desc_ru: возвращает значение системного свойства, если оно существует. В противном случае возвращает `defaultValue` + since: 2.0.0 - name: indexOf args: "input, what, index = 0" desc: "finds first occurrence of `what` in string `input`, starting at position `index`" @@ -97,6 +125,11 @@ functions: args: "x" desc: "returns length of string, array/map size or number of function arguments" desc_ru: "возвращает длину строки, размер массива/объекта или количество аргументов функции в зависимости от типа аргумента x" + - name: nanotime + args: "" + desc: returns VM time source in nanoseconds for elapsed time purposes + desc_ru: возвращает время виртуальной машины в наносекундах, для отсчёта затраченного времени + since: 2.0.0 - name: newarray args: "size..." desc: "creates array with `size`.\n`newarray(x)` - creates 1D array, `newarray(x,y)` - creates 2D array" @@ -107,13 +140,18 @@ functions: println newarray(4) // [0, 0, 0, 0] println newarray(2, 3) // [[0, 0, 0], [0, 0, 0]] - name: parseInt - args: "str, radix" - desc: parses string into integer in the `radix` - desc_ru: парсит строку в целое число с указанным основанием + args: str, radix + desc: parses the input string into an integer with `radix` base + desc_ru: преобразует строку в целое число с указанным основанием - name: parseLong - args: "str, radix" - desc: parses string into long in the `radix` - desc_ru: парсит строку в длинное целое число с указанным основанием + args: str, radix + desc: parses the input string into a long integer with `radix` base + desc_ru: преобразует строку в длинное целое число с указанным основанием + - name: parseDouble + args: str + desc: parses the input string into a double + desc_ru: преобразует строку в вещественное число типа double + since: 2.0.0 - name: rand args: "from = 0, to = .." desc: |- diff --git a/ownlang-parser/src/test/resources/modules/functional/groupby.own b/ownlang-parser/src/test/resources/modules/functional/groupby.own index 49a3237a..6330312b 100644 --- a/ownlang-parser/src/test/resources/modules/functional/groupby.own +++ b/ownlang-parser/src/test/resources/modules/functional/groupby.own @@ -1,5 +1,17 @@ use std, functional +def testGroupByKeys() { + data = [ + {"k1": 1, "k2": "a"}, + {"k1": 2, "k2": "b"}, + {"k1": 3, "k2": "c"}, + ] + result = groupby(data, def(e) = e.k2) + assertEquals([{"k1": 1, "k2": "a"}], result.a) + assertEquals([{"k1": 2, "k2": "b"}], result.b) + assertEquals([{"k1": 3, "k2": "c"}], result.c) +} + def testArraysGroupBy() { arr = [1, 2, 3, 4, 1, 2, 3, 1, 2, 3] result = groupby(arr, def(v) = v % 2 == 0) @@ -13,3 +25,4 @@ def testMapsGroupBy() { assertEquals({"test1": 234, "test2": 345, "test3": 456}, result[true]) assertEquals({"abc": 123, "def": 567}, result[false]) } + From 73662814cab18e0eff51f2e8212a8310628c2fe8 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 8 Dec 2023 22:32:06 +0200 Subject: [PATCH 377/448] Add collections documentation --- docs/src/docgen-md.own | 1 + docs/src/modules/collections.yml | 26 +++++++++++++++++++ .../modules/collections/collections.java | 18 +++---------- 3 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 docs/src/modules/collections.yml diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index aff6ccce..adcc827e 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -7,6 +7,7 @@ OUTPUT_PATH_FMT = OUTPUT_DIR_FMT + "/%s.md" LANGS = ["en", "ru"] MODULES = [ "std", + "collections", "date", "downloader", "files", diff --git a/docs/src/modules/collections.yml b/docs/src/modules/collections.yml new file mode 100644 index 00000000..07f37667 --- /dev/null +++ b/docs/src/modules/collections.yml @@ -0,0 +1,26 @@ +name: collections +scope: both +desc: Contains functions for working with collections +desc_ru: Содержит функции для работы с коллекциями +since: 2.0.0 +functions: + - name: hashMap + args: "fromMap = {}" + desc: creates a new HashMap based on `fromMap` values + desc_ru: создаёт новый HashMap из значений `fromMap` + - name: linkedHashMap + args: "fromMap = {}" + desc: creates a new LinkedHashMap based on `fromMap` values + desc_ru: создаёт новый LinkedHashMap из значений `fromMap` + - name: concurrentHashMap + args: "fromMap = {}" + desc: creates a new ConcurrentHashMap based on `fromMap` values + desc_ru: создаёт новый ConcurrentHashMap из значений `fromMap` + - name: treeMap + args: "fromMap = {}, comparator = def(a, b) = 0" + desc: creates a new TreeMap based on `fromMap` values and `comparator` + desc_ru: создаёт новый TreeMap из значений `fromMap` и компаратора `comparator` + - name: concurrentSkipListMap + args: "fromMap = {}, comparator = def(a, b) = 0" + desc: creates a new ConcurrentSkipListMap based on `fromMap` values and `comparator` + desc_ru: создаёт новый ConcurrentSkipListMap из значений `fromMap` и компаратора `comparator` diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java index b01fa027..b49ff8e2 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java @@ -53,24 +53,14 @@ private Function sortedMapFunction(final Supplier> mapSu case 0: // treeMap() map = mapSupplier.get(); break; - case 1: // treeMap(map) || treeMap(comparator) - if (args[0].type() == Types.MAP) { - map = mapSupplier.get(); - map.putAll(((MapValue) args[0]).getMap()); - } else if (args[0].type() == Types.FUNCTION) { - final Function comparator = ValueUtils.consumeFunction(args[0], 0); - map = comparatorToMapFunction.apply((o1, o2) -> comparator.execute(o1, o2).asInt()); - } else { - throw new TypeException("Map or comparator function expected in first argument"); - } + case 1: // treeMap(map) + map = mapSupplier.get(); + map.putAll(ValueUtils.consumeMap(args[0], 0).getMap()); break; case 2: // treeMap(map, comparator) - if (args[0].type() != Types.MAP) { - throw new TypeException("Map expected in first argument"); - } final Function comparator = ValueUtils.consumeFunction(args[1], 1); map = comparatorToMapFunction.apply((o1, o2) -> comparator.execute(o1, o2).asInt()); - map.putAll(((MapValue) args[0]).getMap()); + map.putAll(ValueUtils.consumeMap(args[0], 0).getMap()); break; default: throw new IllegalStateException(); From 189c3674cc56c2f355f00e1f2fb497e91cc61675 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 8 Dec 2023 22:33:04 +0200 Subject: [PATCH 378/448] Add server documentation --- docs/src/docgen-md.own | 3 +- docs/src/modules/server.yml | 222 ++++++++++++++++++ .../ownlang/modules/server/ServerValue.java | 9 +- 3 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 docs/src/modules/server.yml diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index adcc827e..972cc1be 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -31,7 +31,8 @@ MODULES = [ "canvas", "canvasfx", "forms", - "jdbc" + "jdbc", + "server", // Android-only "android", diff --git a/docs/src/modules/server.yml b/docs/src/modules/server.yml new file mode 100644 index 00000000..47652531 --- /dev/null +++ b/docs/src/modules/server.yml @@ -0,0 +1,222 @@ +name: server +since: 2.0.0 +scope: desktop +functions: + - name: newServer + args: 'config = {}' + desc: Initializes server using provided config. Returns ServerValue. + desc_ru: Инициализирует сервер, используя заданный конфиг. Возвращает ServerValue. + example: |- + use std, server + + newServer() + .get("/", def(ctx) = ctx.json({"message": "Hello, world!"})) + .start(8081) + - name: serve + args: 'port = 8080, dir = "."' + desc: Starts a server on the `port` and hosts the directory `dir` + desc_ru: Запускает сервер на указанном порту и хостит директорию `dir` + example: |- + use server + serve(8083, "./public_html") +types: + - name: ServerValue + functions: + - name: get + args: 'path, handler, roles...' + desc: adds a GET request handler + desc_ru: добавляет обработчик GET запросов + - name: post + args: 'path, handler, roles...' + desc: adds a POST request handler + desc_ru: добавляет обработчик POST запросов + - name: put + args: 'path, handler, roles...' + desc: adds a PUT request handler + desc_ru: добавляет обработчик PUT запросов + - name: patch + args: 'path, handler, roles...' + desc: adds a PATCH request handler + desc_ru: добавляет обработчик PATCH запросов + - name: head + args: 'path, handler, roles...' + desc: adds a HEAD request handler + desc_ru: добавляет обработчик HEAD запросов + - name: delete + args: 'path, handler, roles...' + desc: adds a DELETE request handler + desc_ru: добавляет обработчик DELETE запросов + - name: options + args: 'path, handler, roles...' + desc: adds a OPTIONS request handler + desc_ru: добавляет обработчик OPTIONS запросов + - name: error + args: 'status, handler, contentType = "*"' + desc: adds an error handler + desc_ru: добавляет обработчик ошибок + - name: exception + args: 'className, handler' + desc: adds an exception handler + desc_ru: добавляет обработчик исключений + - name: start + args: 'port = 8080, host = ""' + desc: Starts a server. Use `port` 0 to start a server on a random port. + desc_ru: Запускает сервер. Укажите `port` 0, чтобы запустить сервер на случайном порте + - name: stop + args: '' + desc: Stops a server + desc_ru: Останавливает работу сервера + - name: ContextValue + functions: + - name: attribute + args: 'key, value = ""' + desc: gets or sets an attribute by key + desc_ru: получает или устанавливает аттрибут по ключу `key` + - name: basicAuthCredentials + args: '' + desc: returns a basic authorization credentials, an array with two elements — username and password + desc_ru: возвращает простые данные авторизации, массив с двумя элементами — имя пользователя и пароль + example: |- + extract(username, password) = ctx.basicAuthCredentials() + - name: body + args: '' + desc: returns a response body as a string + desc_ru: возвращает тело ответа в виде строки + - name: bodyAsBytes + args: '' + desc: returns a response body as a byte array + desc_ru: возвращает тело ответа в виде массива байт + - name: characterEncoding + args: '' + desc: returns a character encoding from Content-Type if possible + desc_ru: возвращает кодировку символов из заголовка Content-Type, если возможно + - name: cookie + args: 'name, value = "", maxAge = -1' + desc: gets or sets a cookie + desc_ru: получает или устанавливает значение куки + - name: contentLength + args: '' + desc: returns a content length in bytes + desc_ru: возвращает длину контента в байтах + - name: contentType + args: 'contentType = ""' + desc: gets or sets a Content-Type header + desc_ru: получает или устанавливает заголовок Content-Type + - name: contextPath + args: '' + desc: returns a request context path + desc_ru: возвращает путь контекста запроса + - name: endpointHandlerPath + args: '' + desc: returns a matched endpoint handler path + desc_ru: возвращает путь обработчика совпавшего эндпоинта + - name: formParam + args: 'key' + desc: returns a form parameter + desc_ru: возвращает параметр формы + - name: fullUrl + args: '' + desc: returns a full url + desc_ru: возвращает полный адрес + - name: handlerType + args: '' + desc: returns a current handler type + desc_ru: возвращает тип текущего обработчика + - name: header + args: 'name, value =""' + desc: gets or sets header + desc_ru: получает или устанавливает заголовок по названию `name` + - name: host + args: '' + desc: returns a host + desc_ru: возвращает имя хоста + - name: html + args: 'html' + desc: sets result to the specified html string. Also sets Content-Type header to text/html + desc_ru: устанавливает указанныую html-строку в качестве результата. Также устанавливает заголовок Content-Type в text/html + - name: ip + args: '' + desc: returns an IP address + desc_ru: возвращает IP адрес + - name: json + args: 'obj' + desc: serializes an object to json string and sets it as the result + desc_ru: сериализует объект в json строку и устанавливает в качестве результата + - name: jsonStream + args: 'obj' + desc: serializes an object to json stream and sets it as the result + desc_ru: сериализует объект в json потом и устанавливает в качестве результата + - name: matchedPath + args: '' + desc: returns a matched request path + desc_ru: возвращает совпавший путь запроса + - name: path + args: '' + desc: returns a request path + desc_ru: возвращает путь запроса + - name: port + args: '' + desc: returns a port number + desc_ru: возвращает номер порта + - name: protocol + args: '' + desc: returns a protocol + desc_ru: возвращает протокол + - name: queryString + args: '' + desc: returns a query string + desc_ru: возвращает строку запроса + - name: redirect + args: 'location, statusCode = 302' + desc: redirects to a location with the given status. By default, the status is 302 FOUND + desc_ru: редиректит на указанный путь с указанным статусом. По умолчанию, статус — 302 FOUND + - name: removeCookie + args: 'name, path = "/"' + desc: removes a cookie by name and path + desc_ru: удаляет куки по имени и пути + - name: render + args: 'filePath, data = {}' + desc: renders a file with specified data and sets it as the result + desc_ru: рендерит файл с указанными данными и устанавливает в качестве результата + - name: result + args: 'value = ""' + desc: gets or sets a result. `value` can be a string or a byte array + desc_ru: получает или устанавливает результат. `value` может быть строкой или массивом байт + - name: statusCode + args: '' + desc: returns a response status code + desc_ru: возвращает код статуса ответа + - name: scheme + args: '' + desc: returns a request scheme + desc_ru: возвращает схему запроса + - name: url + args: '' + desc: returns a request url + desc_ru: возвращает адрес запроса + - name: userAgent + args: '' + desc: returns an User-Agent header + desc_ru: возвращает заголовок User-Agent + - name: Config + desc: |- + { + "webjars": true, + "classpathDirs": ["dir1", "dir2"], + "externalDirs": ["dir1", "dir2"], + + "asyncTimeout": 6_000, + "defaultContentType": "text/plain", + "etags": true, + "maxRequestSize": 1_000_000, + + "caseInsensitiveRoutes": true, + "ignoreTrailingSlashes": true, + "multipleSlashesAsSingle": true, + "contextPath": "/", + + "basicAuth": ["user", "password"], + "dev": true, + "showBanner": false, + "sslRedirects": true + } \ No newline at end of file diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java index 05dbcd7a..5d401cd9 100644 --- a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java @@ -33,17 +33,14 @@ private void init() { private Value error(Value[] args) { Arguments.checkOrOr(2, 3, args.length); - final int handlerIndex; final String contentType; if (args.length == 2) { contentType = "*"; - handlerIndex = 1; } else { - contentType = args[1].asString(); - handlerIndex = 2; + contentType = args[2].asString(); } int status = args[0].asInt(); - final Handler handler = toHandler(ValueUtils.consumeFunction(args[handlerIndex], handlerIndex)); + final Handler handler = toHandler(ValueUtils.consumeFunction(args[1], 1)); server.error(status, contentType, handler); return this; } @@ -70,7 +67,7 @@ private Value start(Value[] args) { switch (args.length) { case 0 -> server.start(); case 1 -> server.start(args[0].asInt()); - case 2 -> server.start(args[0].asString(), args[1].asInt()); + case 2 -> server.start(args[1].asString(), args[0].asInt()); } return this; } From b016096161e1d340eee7f4d46e0048c2c05fc083 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 10 Dec 2023 18:01:37 +0200 Subject: [PATCH 379/448] [server] Add Header constants --- docs/src/modules/server.yml | 28 +++++++++++++++++++ .../ownlang/modules/server/server.java | 3 +- .../com/annimon/ownlang/lib/ValueUtils.java | 16 +++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/src/modules/server.yml b/docs/src/modules/server.yml index 47652531..d8f4a380 100644 --- a/docs/src/modules/server.yml +++ b/docs/src/modules/server.yml @@ -1,6 +1,34 @@ name: server since: 2.0.0 scope: desktop +constants: + - name: Header + type: 4 + typeName: map + value: '{ACCEPT=Accept, ACCEPT_CHARSET=Accept-Charset, ACCEPT_ENCODING=Accept-Encoding, + ACCEPT_LANGUAGE=Accept-Language, ACCEPT_RANGES=Accept-Ranges, ACCESS_CONTROL_ALLOW_CREDENTIALS=Access-Control-Allow-Credentials, + ACCESS_CONTROL_ALLOW_HEADERS=Access-Control-Allow-Headers, ACCESS_CONTROL_ALLOW_METHODS=Access-Control-Allow-Methods, + ACCESS_CONTROL_ALLOW_ORIGIN=Access-Control-Allow-Origin, ACCESS_CONTROL_EXPOSE_HEADERS=Access-Control-Expose-Headers, + ACCESS_CONTROL_MAX_AGE=Access-Control-Max-Age, ACCESS_CONTROL_REQUEST_HEADERS=Access-Control-Request-Headers, + ACCESS_CONTROL_REQUEST_METHOD=Access-Control-Request-Method, AGE=Age, ALLOW=Allow, + AUTHORIZATION=Authorization, CACHE_CONTROL=Cache-Control, CLEAR_SITE_DATA=Clear-Site-Data, + CONNECTION=Connection, CONTENT_DISPOSITION=Content-Disposition, CONTENT_ENCODING=Content-Encoding, + CONTENT_LANGUAGE=Content-Language, CONTENT_LENGTH=Content-Length, CONTENT_LOCATION=Content-Location, + CONTENT_RANGE=Content-Range, CONTENT_SECURITY_POLICY=Content-Security-Policy, + CONTENT_TYPE=Content-Type, COOKIE=Cookie, CROSS_ORIGIN_EMBEDDER_POLICY=Cross-Origin-Embedder-Policy, + CROSS_ORIGIN_OPENER_POLICY=Cross-Origin-Opener-Policy, CROSS_ORIGIN_RESOURCE_POLICY=Cross-Origin-Resource-Policy, + DATE=Date, ETAG=ETag, EXPECT=Expect, EXPIRES=Expires, FROM=From, HOST=Host, + IF_MATCH=If-Match, IF_MODIFIED_SINCE=If-Modified-Since, IF_NONE_MATCH=If-None-Match, + IF_RANGE=If-Range, IF_UNMODIFIED_SINCE=If-Unmodified-Since, LAST_MODIFIED=Last-Modified, + LINK=Link, LOCATION=Location, MAX_FORWARDS=Max-Forwards, ORIGIN=Origin, PRAGMA=Pragma, + PROXY_AUTHENTICATE=Proxy-Authenticate, PROXY_AUTHORIZATION=Proxy-Authorization, + RANGE=Range, REFERER=Referer, REFERRER_POLICY=Referrer-Policy, RETRY_AFTER=Retry-After, + SEC_WEBSOCKET_KEY=Sec-WebSocket-Key, SERVER=Server, SET_COOKIE=Set-Cookie, STRICT_TRANSPORT_SECURITY=Strict-Transport-Security, + TE=TE, TRAILER=Trailer, TRANSFER_ENCODING=Transfer-Encoding, UPGRADE=Upgrade, + USER_AGENT=User-Agent, VARY=Vary, VIA=Via, WARNING=Warning, WWW_AUTHENTICATE=WWW-Authenticate, + X_ACCEL_BUFFERING=X-Accel-Buffering, X_CONTENT_TYPE_OPTIONS=X-Content-Type-Options, + X_FORWARDED_FOR=X-Forwarded-For, X_FORWARDED_PROTO=X-Forwarded-Proto, X_FRAME_OPTIONS=X-Frame-Options, + X_HTTP_METHOD_OVERRIDE=X-HTTP-Method-Override, X_PERMITTED_CROSS_DOMAIN_POLICIES=X-Permitted-Cross-Domain-Policies}' functions: - name: newServer args: 'config = {}' diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java index 514369b2..e412a85f 100644 --- a/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java @@ -3,6 +3,7 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import io.javalin.Javalin; +import io.javalin.http.Header; import io.javalin.http.staticfiles.Location; import java.util.Map; import static java.util.Map.entry; @@ -11,7 +12,7 @@ public final class server implements Module { @Override public Map constants() { - return Map.of(); + return Map.of("Header", ValueUtils.collectStringConstants(Header.class)); } @Override diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index 3b74a10f..e9764ea9 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -123,17 +123,29 @@ public static Function consumeFunction(Value value, String errorMessage) { return ((FunctionValue) value).getValue(); } - @SuppressWarnings("unchecked") public static MapValue collectNumberConstants(Class clazz, Class type) { + return collectConstants(clazz, type, NumberValue::of); + } + + public static MapValue collectStringConstants(Class clazz) { + return collectConstants(clazz, String.class, StringValue::new); + } + + @SuppressWarnings("unchecked") + private static MapValue collectConstants(Class clazz, Class type, FieldConverter converter) { 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))); + result.set(field.getName(), converter.convert((T) field.get(type))); } catch (IllegalAccessException ignore) { } } return result; } + + private interface FieldConverter { + V convert(T input) throws IllegalAccessException; + } } From bfb21ecdbd65a70f48d7208b3c67c775643385f4 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 10 Dec 2023 18:16:42 +0200 Subject: [PATCH 380/448] Move ModulesInfoCreator to docs module --- docs/build.gradle | 27 +++ .../com/annimon/ownlang/docs/ModuleInfo.java | 80 +++++++++ .../ownlang/docs/ModulesInfoCreator.java | 73 ++++++++ ownlang-utils/build.gradle | 2 - .../ownlang/utils/ModulesInfoCreator.java | 159 ------------------ .../com/annimon/ownlang/utils/Sandbox.java | 1 - 6 files changed, 180 insertions(+), 162 deletions(-) create mode 100644 docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java create mode 100644 docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java delete mode 100644 ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java diff --git a/docs/build.gradle b/docs/build.gradle index 9b9b9051..f5f694da 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -1,3 +1,21 @@ +plugins { + id 'java' +} + +group = 'com.annimon' +version = '2.0-SNAPSHOT' + +dependencies { + implementation project(":ownlang-core") + implementation project(":ownlang-parser") + implementation project(":ownlang-utils") + implementation project(":modules:main") + implementation project(":modules:canvasfx") + implementation project(":modules:server") + + implementation "org.yaml:snakeyaml:${versions.snakeyaml}" +} + tasks.register('generateMarkdownModules') { group = "documentation" def ownlangExec = tasks.getByPath(':ownlang-desktop:ownlangExec') @@ -8,4 +26,13 @@ tasks.register('generateMarkdownModules') { } } finalizedBy ownlangExec +} + +tasks.register('generateModuleInfo', JavaExec) { + group = "documentation" + description = "Run sample program" + dependsOn classes + mainClass = 'com.annimon.ownlang.docs.ModulesInfoCreator' + classpath = sourceSets.main.runtimeClasspath + args 'server', 'okhttp' } \ No newline at end of file diff --git a/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java b/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java new file mode 100644 index 00000000..bc3f5229 --- /dev/null +++ b/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java @@ -0,0 +1,80 @@ +package com.annimon.ownlang.docs; + +import com.annimon.ownlang.Version; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.util.*; +import java.util.stream.Collectors; + +public class ModuleInfo { + private final String name; + final List functions; + final Map constants; + final List types; + + public ModuleInfo(String name) { + this.name = name; + functions = new ArrayList<>(); + constants = new HashMap<>(); + types = new ArrayList<>(); + } + + public List> functions() { + return functions.stream().sorted() + .map(f -> { + final Map function = new LinkedHashMap<>(); + function.put("name", f); + function.put("args", ""); + function.put("desc", ""); + function.put("desc_ru", ""); + return function; + }) + .collect(Collectors.toList()); + } + + public List> constants() { + final List> result = new ArrayList<>(); + constants.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(entry -> { + final Value value = entry.getValue(); + + final Map constant = new LinkedHashMap<>(); + constant.put("name", entry.getKey()); + constant.put("type", value.type()); + constant.put("typeName", Types.typeToString(value.type())); + if (value.type() == Types.MAP) { + String text = ((MapValue) value).getMap().entrySet().stream() + .sorted(Comparator.comparing( + e -> ((MapValue) value).size() > 16 ? e.getKey() : e.getValue())) + .map(Object::toString) + .collect(Collectors.joining(", ", "{", "}")); + constant.put("value", text); + } else { + constant.put("value", value.asString()); + } + result.add(constant); + }); + return result; + } + + public Map info() { + final Map result = new LinkedHashMap<>(); + result.put("name", name); + result.put("scope", "both"); + result.put("since", "%d.%d.%d".formatted(Version.VERSION_MAJOR, Version.VERSION_MINOR, Version.VERSION_PATCH)); + result.put("constants", constants()); + result.put("functions", functions()); + if (!types.isEmpty()) { + result.put("types", types.stream().sorted() + .map(s -> { + final Map type = new HashMap<>(); + type.put("name", s); + return type; + }) + .toArray()); + } + return result; + } +} \ No newline at end of file diff --git a/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java b/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java new file mode 100644 index 00000000..d54982e8 --- /dev/null +++ b/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java @@ -0,0 +1,73 @@ +package com.annimon.ownlang.docs; + +import com.annimon.ownlang.lib.ModuleLoader; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.modules.Module; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class ModulesInfoCreator { + + public static void main(String[] args) { + if (args.length == 0) { + System.err.println("No modules provided.\nUsage: ModulesInfoCreator ..."); + System.exit(1); + } + + final Class clazz = Module.class; // get classloader for package + + final List moduleInfos = new ArrayList<>(); + + for (String moduleName : args) { + final Module module = ModuleLoader.load(moduleName); + + final ModuleInfo moduleInfo = new ModuleInfo(moduleName); + moduleInfo.functions.addAll(module.functions().keySet()); + moduleInfo.constants.putAll(module.constants()); + moduleInfo.types.addAll(listValues(module.getClass())); + moduleInfos.add(moduleInfo); + } + + printAsYaml(moduleInfos); + + System.out.println("Total functions: " + moduleInfos.stream() + .mapToLong(m -> m.functions.size()) + .sum() + ); + System.out.println("Total constants: " + moduleInfos.stream() + .mapToLong(m -> m.constants.keySet().size()) + .sum() + ); + } + + private static void printAsYaml(List moduleInfos) { + DumperOptions options = new DumperOptions(); + options.setIndent(2); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + final List> infos = new ArrayList<>(); + for (ModuleInfo moduleInfo : moduleInfos) { + infos.add(moduleInfo.info()); + } + System.out.println(new Yaml(options).dump(infos)); + } + + private static List listValues(Class moduleClass) { + return Arrays.stream(moduleClass.getDeclaredClasses()) + .filter(clazz -> getAllInterfaces(clazz).stream().anyMatch(i -> i.equals(Value.class))) + .map(Class::getSimpleName) + .collect(Collectors.toList()); + } + + private static Set> getAllInterfaces(Class clazz) { + if (clazz.getSuperclass() == null) { + return Collections.emptySet(); + } + return Stream.concat(Arrays.stream(clazz.getInterfaces()), getAllInterfaces(clazz.getSuperclass()).stream()) + .collect(Collectors.toSet()); + } + +} diff --git a/ownlang-utils/build.gradle b/ownlang-utils/build.gradle index 8a12b43a..205d0bf7 100644 --- a/ownlang-utils/build.gradle +++ b/ownlang-utils/build.gradle @@ -7,8 +7,6 @@ version = '2.0-SNAPSHOT' dependencies { api project(":ownlang-parser") - implementation "org.json:json:${versions.json}" - implementation "org.yaml:snakeyaml:${versions.snakeyaml}" implementation "jline:jline:${versions.jline}" testImplementation platform("org.junit:junit-bom:${versions.junit}") diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java deleted file mode 100644 index de6447ad..00000000 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.annimon.ownlang.utils; - -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.modules.Module; -import java.io.File; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.json.JSONArray; -import org.json.JSONObject; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; - -public final class ModulesInfoCreator { - - private static final String MODULES_PATH = "src/main/java/com/annimon/ownlang/modules"; - - public static void main(String[] args) - throws ReflectiveOperationException { - final Class clazz = Module.class; // get classloader for package - - final List moduleInfos = new ArrayList<>(); - - String[] moduleNames = Optional.ofNullable(new File(MODULES_PATH).listFiles()) - .stream() - .flatMap(Arrays::stream) - .filter(File::isDirectory) - .map(File::getName) - .toArray(String[]::new); - for (String moduleName : moduleNames) { - final Module module = ModuleLoader.load(moduleName); - - final ModuleInfo moduleInfo = new ModuleInfo(moduleName); - moduleInfo.functions.addAll(module.functions().keySet()); - moduleInfo.constants.putAll(module.constants()); - moduleInfo.types.addAll(listValues(module.getClass())); - moduleInfos.add(moduleInfo); - } - - // printAsJson(moduleInfos); - printAsYaml(moduleInfos); - - System.out.println("Total modules: " + moduleInfos.size()); - System.out.println("Total functions: " + moduleInfos.stream() - .mapToLong(m -> m.functions.size()) - .sum() - ); - System.out.println("Total constants: " + moduleInfos.stream() - .mapToLong(m -> m.constants.keySet().size()) - .sum() - ); - } - - private static void printAsJson(List moduleInfos) { - final JSONArray modulesJson = new JSONArray(); - for (ModuleInfo moduleInfo : moduleInfos) { - modulesJson.put(new JSONObject(moduleInfo.info())); - } - System.out.println(modulesJson.toString(2)); - } - - private static void printAsYaml(List moduleInfos) { - DumperOptions options = new DumperOptions(); - options.setIndent(2); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - - final List> infos = new ArrayList<>(); - for (ModuleInfo moduleInfo : moduleInfos) { - infos.add(moduleInfo.info()); - } - System.out.println(new Yaml(options).dump(infos)); - } - - private static List listValues(Class moduleClass) { - return Arrays.stream(moduleClass.getDeclaredClasses()) - .filter(clazz -> getAllInterfaces(clazz).stream().anyMatch(i -> i.equals(Value.class))) - .map(Class::getSimpleName) - .collect(Collectors.toList()); - } - - private static Set> getAllInterfaces(Class clazz) { - if (clazz.getSuperclass() == null) { - return Collections.emptySet(); - } - return Stream.concat(Arrays.stream(clazz.getInterfaces()), getAllInterfaces(clazz.getSuperclass()).stream()) - .collect(Collectors.toSet()); - } - - static class ModuleInfo { - private final String name; - final List functions; - final Map constants; - final List types; - - public ModuleInfo(String name) { - this.name = name; - functions = new ArrayList<>(); - constants = new HashMap<>(); - types = new ArrayList<>(); - } - - public List> functions() { - return functions.stream().sorted() - .map(f -> { - final Map function = new LinkedHashMap<>(); - function.put("name", f); - function.put("args", ""); - function.put("desc", ""); - function.put("desc_ru", ""); - return function; - }) - .collect(Collectors.toList()); - } - - public List> constants() { - final List> result = new ArrayList<>(); - constants.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .forEach(entry -> { - final Value value = entry.getValue(); - - final Map constant = new LinkedHashMap<>(); - constant.put("name", entry.getKey()); - constant.put("type", value.type()); - constant.put("typeName", Types.typeToString(value.type())); - if (value.type() == Types.MAP) { - String text = ((MapValue) value).getMap().entrySet().stream() - .sorted(Comparator.comparing( - e -> ((MapValue)value).size() > 16 ? e.getKey() : e.getValue())) - .map(Object::toString) - .collect(Collectors.joining(", ", "{", "}")); - constant.put("value", text); - } else { - constant.put("value", value.asString()); - } - result.add(constant); - }); - return result; - } - - public Map info() { - final Map result = new LinkedHashMap<>(); - result.put("name", name); - result.put("scope", "both"); - result.put("constants", constants()); - result.put("functions", functions()); - if (!types.isEmpty()) { - result.put("types", types.stream().sorted() - .map(s -> { - final Map type = new HashMap<>(); - type.put("name", s); - return type; - }) - .toArray()); - } - return result; - } - } -} diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java index b36659de..773882b5 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -9,7 +9,6 @@ import com.annimon.ownlang.parser.SourceLoader; import com.annimon.ownlang.parser.Token; import com.annimon.ownlang.parser.ast.Node; -import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.visitors.FunctionAdder; import java.io.File; import java.io.IOException; From 9d9fdd15742b8c52717bb61191ae0613d722e3a5 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 10 Dec 2023 18:55:49 +0200 Subject: [PATCH 381/448] [okhttp] Add docs --- docs/src/docgen-md.own | 1 + docs/src/modules/okhttp.yml | 210 ++++++++++++++++++ .../okhttp/MultipartBodyBuilderValue.java | 11 +- 3 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 docs/src/modules/okhttp.yml diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index 972cc1be..4c33e54c 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -13,6 +13,7 @@ MODULES = [ "files", "functional", "http", + "okhttp", "java", "math", "ounit", diff --git a/docs/src/modules/okhttp.yml b/docs/src/modules/okhttp.yml new file mode 100644 index 00000000..ef13ea8d --- /dev/null +++ b/docs/src/modules/okhttp.yml @@ -0,0 +1,210 @@ +name: okhttp +since: 2.0.0 +scope: both +constants: + - name: MultipartBody + type: 4 + typeName: map + value: MultipartBodyValue + - name: RequestBody + type: 4 + typeName: map + value: RequestBodyValue + - name: okhttp + type: 4 + typeName: map + value: OkHttpValue +functions: [] +types: + - name: CallValue + functions: + - name: cancel + args: '' + desc: '' + desc_ru: '' + - name: enqueue + args: 'onResponse, onFailure=def(call, errorMessage)' + desc: '' + desc_ru: '' + - name: execute + args: '' + desc: '' + desc_ru: '' + - name: isCanceled + args: '' + desc: '' + desc_ru: '' + - name: isExecuted + args: '' + desc: '' + desc_ru: '' + - name: MultipartBodyValue + constants: + - name: ALTERNATIVE + type: 2 + typeName: string + value: multipart/alternative + - name: DIGEST + type: 2 + typeName: string + value: multipart/digest + - name: FORM + type: 2 + typeName: string + value: multipart/form-data + - name: MIXED + type: 2 + typeName: string + value: multipart/mixed + - name: PARALLEL + type: 2 + typeName: string + value: multipart/parallel + functions: + - name: builder + args: '' + desc: returns MultipartBodyBuilderValue + desc_ru: dозвращает MultipartBodyBuilderValue + - name: MultipartBodyBuilderValue + functions: + - name: addFormData + args: 'data' + desc: '' + desc_ru: '' + - name: addFormDataPart + args: 'name, value, requestBody = empty' + desc: '' + desc_ru: '' + - name: addPart + args: 'requestBody, headers = {}' + desc: '' + desc_ru: '' + - name: build + args: '' + desc: creates and returns MultipartBodyValue + desc_ru: создаёт и возвращает MultipartBodyValue + - name: setType + args: 'type' + desc: '' + desc_ru: '' + - name: RequestBuilderValue + functions: + - name: addHeader + args: 'name, value' + desc: '' + desc_ru: '' + - name: cacheControl + args: '' + desc: '' + desc_ru: '' + - name: delete + args: 'requestBody = empty' + desc: '' + desc_ru: '' + - name: get + args: '' + desc: '' + desc_ru: '' + - name: head + args: '' + desc: '' + desc_ru: '' + - name: header + args: 'name, value' + desc: '' + desc_ru: '' + - name: headers + args: 'headersMap' + desc: '' + desc_ru: '' + - name: method + args: 'method, requestBody = empty' + desc: '' + desc_ru: '' + - name: newCall + args: 'client' + desc: creates new call, returns CallValue + desc_ru: создаёт новый вызов, возвращает CallValue + - name: patch + args: 'requestBody = empty' + desc: '' + desc_ru: '' + - name: post + args: 'requestBody = empty' + desc: '' + desc_ru: '' + - name: put + args: 'requestBody = empty' + desc: '' + desc_ru: '' + - name: removeHeader + args: 'name' + desc: '' + desc_ru: '' + - name: url + args: 'url' + desc: '' + desc_ru: '' + - name: RequestBodyValue + functions: + - name: bytes + args: 'contentType, bytes, offset = 0, bytesCount = bytes.length' + desc: '' + desc_ru: '' + - name: file + args: 'contentType, filePath' + desc: '' + desc_ru: '' + - name: string + args: 'contentType, content' + desc: '' + desc_ru: '' + - name: OkHttpValue + constants: + - name: client + type: 4 + typeName: map + value: HttpClientValue + functions: + - name: request + args: '' + desc: returns RequestBuilderValue + desc_ru: возвращает RequestBuilderValue + - name: HttpClientValue + functions: + - name: connectTimeoutMillis + args: '' + desc: '' + desc_ru: '' + - name: followRedirects + args: '' + desc: '' + desc_ru: '' + - name: followSslRedirects + args: '' + desc: '' + desc_ru: '' + - name: newCall + args: 'request' + desc: creates new call, returns CallValue + desc_ru: создаёт новый вызов, возвращает CallValue + - name: newWebSocket + args: 'request, callbacks' + desc: '' + desc_ru: '' + - name: pingIntervalMillis + args: '' + desc: '' + desc_ru: '' + - name: readTimeoutMillis + args: '' + desc: '' + desc_ru: '' + - name: retryOnConnectionFailure + args: '' + desc: '' + desc_ru: '' + - name: writeTimeoutMillis + args: '' + desc: '' + desc_ru: '' diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java index b1ee7bc5..9b7af755 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyBuilderValue.java @@ -8,6 +8,7 @@ import java.util.Map; import okhttp3.MediaType; import okhttp3.MultipartBody; +import okhttp3.RequestBody; public class MultipartBodyBuilderValue extends MapValue { @@ -56,14 +57,14 @@ private Value addFormData(Value[] args) { } private Value addPart(Value[] args) { - Arguments.checkOrOr(2, 3, args.length); + Arguments.checkOrOr(1, 2, args.length); + RequestBody requestBody = Values.getRequestBody(args[0], " at first argument"); if (args.length == 1) { - builder.addPart( - Values.getRequestBody(args[0], " at first argument")); + builder.addPart(requestBody); } else { builder.addPart( - Values.getHeaders(args[0], " at first argument"), - Values.getRequestBody(args[1], " at second argument")); + Values.getHeaders(args[1], " at second argument"), + requestBody); } return this; } From f772173df1cf9bb6b27105c89020b7e26309029c Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 11 Dec 2023 18:04:32 +0200 Subject: [PATCH 382/448] Improve docs generators --- docs/docs/en/basics/types.md | 1 + docs/docs/ru/basics/types.md | 1 + docs/src/docgen-md.own | 17 +++++++++++------ .../com/annimon/ownlang/docs/ModuleInfo.java | 6 +++++- .../ownlang/docs/ModulesInfoCreator.java | 11 ++++++++--- docs/src/modules/server.yml | 2 +- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/docs/docs/en/basics/types.md b/docs/docs/en/basics/types.md index ec4e3b29..c7b5b116 100644 --- a/docs/docs/en/basics/types.md +++ b/docs/docs/en/basics/types.md @@ -7,6 +7,7 @@ OwnLang types are: * Array - arrays * Map - objects (an associative arrays) * Function - functions + * Class Since OwnLang is dynamic programming language, which means that explicitly declare the types is not necessary. diff --git a/docs/docs/ru/basics/types.md b/docs/docs/ru/basics/types.md index 088844dc..0d0f55ca 100644 --- a/docs/docs/ru/basics/types.md +++ b/docs/docs/ru/basics/types.md @@ -7,6 +7,7 @@ * Array - массивы * Map - объекты (ассоциативные массивы) * Function - функции + * Class - классы Поскольку OwnLang - динамически типизируемый язык программирования, это значит, что явно объявлять типы не нужно. diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own index 4c33e54c..53e61abe 100644 --- a/docs/src/docgen-md.own +++ b/docs/src/docgen-md.own @@ -136,12 +136,7 @@ def writeFunctions(f, functions, lang, level = 2) { writeDescription(f, info, lang, " — %s") writeLine(f, "") - example = getValue(info, "example", lang) - if (length(example ?? "")) { - writeLine(f, "\n```own") - writeLine(f, example) - writeLine(f, "```") - } + writeExample(f, info, lang) } } @@ -155,6 +150,7 @@ def writeTypes(f, types, lang) { writeDescription(f, info, lang, "%s\n") writeFunctions(f, info.functions ?? [], lang, 4); writeLine(f, "") + writeExample(f, info, lang) } } @@ -179,6 +175,15 @@ def writeSince(f, version, lang, isInline = false) { } } +def writeExample(f, info, lang) { + example = getValue(info, "example", lang) + if (length(example ?? "")) { + writeLine(f, "\n```own") + writeLine(f, example) + writeLine(f, "```") + } +} + // -- utils def getValue(object, key, lang = "en") { newKey = (lang != "en") ? (key + "_" + lang) : key diff --git a/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java b/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java index bc3f5229..d47f0fec 100644 --- a/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java +++ b/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java @@ -20,6 +20,10 @@ public ModuleInfo(String name) { types = new ArrayList<>(); } + public String name() { + return name; + } + public List> functions() { return functions.stream().sorted() .map(f -> { @@ -62,8 +66,8 @@ public List> constants() { public Map info() { final Map result = new LinkedHashMap<>(); result.put("name", name); - result.put("scope", "both"); result.put("since", "%d.%d.%d".formatted(Version.VERSION_MAJOR, Version.VERSION_MINOR, Version.VERSION_PATCH)); + result.put("scope", "both"); result.put("constants", constants()); result.put("functions", functions()); if (!types.isEmpty()) { diff --git a/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java b/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java index d54982e8..8c069911 100644 --- a/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java +++ b/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java @@ -48,11 +48,16 @@ private static void printAsYaml(List moduleInfos) { options.setIndent(2); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - final List> infos = new ArrayList<>(); + final Yaml yaml = new Yaml(options); for (ModuleInfo moduleInfo : moduleInfos) { - infos.add(moduleInfo.info()); + final String separator = "-".repeat(moduleInfo.name().length() + 12); + System.out.println(separator); + System.out.print("--- "); + System.out.print(moduleInfo.name() + ".yml"); + System.out.println(" ---"); + System.out.println(separator); + System.out.println(yaml.dump(moduleInfo.info())); } - System.out.println(new Yaml(options).dump(infos)); } private static List listValues(Class moduleClass) { diff --git a/docs/src/modules/server.yml b/docs/src/modules/server.yml index d8f4a380..cb317e13 100644 --- a/docs/src/modules/server.yml +++ b/docs/src/modules/server.yml @@ -227,7 +227,7 @@ types: desc: returns an User-Agent header desc_ru: возвращает заголовок User-Agent - name: Config - desc: |- + example: |- { "webjars": true, "classpathDirs": ["dir1", "dir2"], From b11761a99ae83e35074787b5582e1642066f5aae Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 11 Dec 2023 21:15:26 +0200 Subject: [PATCH 383/448] Small fixes --- .../ownlang/modules/http/http_http.java | 47 ++++++++----------- .../ownlang/modules/robot/robot_exec.java | 24 ++++------ .../com/annimon/ownlang/lib/NumberValue.java | 4 +- .../main/java/com/annimon/ownlang/Main.java | 2 +- 4 files changed, 32 insertions(+), 45 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java index 336b9a7f..23048d65 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.modules.http; import com.annimon.ownlang.exceptions.ArgumentsMismatchException; -import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -31,65 +30,59 @@ public final class http_http implements Function { @Override public Value execute(Value[] args) { String url, method; + Function function; switch (args.length) { case 1: // http(url) url = args[0].asString(); return process(url, "GET"); - + case 2: // http(url, method) || http(url, callback) url = args[0].asString(); if (args[1].type() == Types.FUNCTION) { - return process(url, "GET", (FunctionValue) args[1]); + return process(url, "GET", ValueUtils.consumeFunction(args[1], 1)); } return process(url, args[1].asString()); - + case 3: // http(url, method, params) || http(url, method, callback) url = args[0].asString(); method = args[1].asString(); if (args[2].type() == Types.FUNCTION) { - return process(url, method, (FunctionValue) args[2]); + return process(url, method, ValueUtils.consumeFunction(args[2], 2)); } - return process(url, method, args[2], FunctionValue.EMPTY); - + return process(url, method, args[2], FunctionValue.EMPTY.getValue()); + case 4: // http(url, method, params, callback) - if (args[3].type() != Types.FUNCTION) { - throw new TypeException("Fourth arg must be a function callback"); - } url = args[0].asString(); method = args[1].asString(); - return process(url, method, args[2], (FunctionValue) args[3]); - + function = ValueUtils.consumeFunction(args[3], 3); + return process(url, method, args[2], function); + case 5: // http(url, method, params, headerParams, callback) - if (args[3].type() != Types.MAP) { - throw new TypeException("Third arg must be a map"); - } - if (args[4].type() != Types.FUNCTION) { - throw new TypeException("Fifth arg must be a function callback"); - } url = args[0].asString(); method = args[1].asString(); - return process(url, method, args[2], (MapValue) args[3], (FunctionValue) args[4]); - + MapValue options = ValueUtils.consumeMap(args[3], 3); + function = ValueUtils.consumeFunction(args[4], 4); + return process(url, method, args[2], options, function); + default: throw new ArgumentsMismatchException("From 1 to 5 arguments expected, got " + args.length); } } private Value process(String url, String method) { - return process(url, method, FunctionValue.EMPTY); + return process(url, method, FunctionValue.EMPTY.getValue()); } - private Value process(String url, String method, FunctionValue function) { - return process(url, method, MapValue.EMPTY, function); + private Value process(String url, String method, Function callback) { + return process(url, method, MapValue.EMPTY, callback); } - private Value process(String url, String method, Value params, FunctionValue function) { - return process(url, method, params, MapValue.EMPTY, function); + private Value process(String url, String method, Value params, Function callback) { + return process(url, method, params, MapValue.EMPTY, callback); } - private Value process(String url, String methodStr, Value requestParams, MapValue options, FunctionValue function) { + private Value process(String url, String methodStr, Value requestParams, MapValue options, Function callback) { final String method = methodStr.toUpperCase(); - final Function callback = function.getValue(); try { final Request.Builder builder = new Request.Builder() .url(url) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java index ae2ad8d0..d953dc87 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_exec.java @@ -25,23 +25,17 @@ public Value execute(Value[] args) { final Process process; if (args.length > 1) { process = Runtime.getRuntime().exec(toStringArray(args)); - } else switch (args[0].type()) { - case Types.ARRAY: - final ArrayValue array = (ArrayValue) args[0]; - process = Runtime.getRuntime().exec(toStringArray(array.getCopyElements())); - break; - - default: - process = Runtime.getRuntime().exec(args[0].asString()); + } else if (args[0].type() == Types.ARRAY) { + final ArrayValue array = (ArrayValue) args[0]; + process = Runtime.getRuntime().exec(toStringArray(array.getCopyElements())); + } else { + process = Runtime.getRuntime().exec(args[0].asString()); } - - switch (mode) { - case EXEC_AND_WAIT: - return NumberValue.of(process.waitFor()); - case EXEC: - default: - return NumberValue.ZERO; + + if (mode == Mode.EXEC_AND_WAIT) { + return NumberValue.of(process.waitFor()); } + return NumberValue.ZERO; } catch (Exception ex) { return NumberValue.ZERO; } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java index 68e005dd..e3e9e370 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java @@ -120,9 +120,9 @@ public boolean equals(Object obj) { return Float.compare(value.floatValue(), other.floatValue()) == 0; } if (value instanceof Long || other instanceof Long) { - return Long.compare(value.longValue(), other.longValue()) == 0; + return value.longValue() == other.longValue(); } - return Integer.compare(value.intValue(), other.intValue()) == 0; + return value.intValue() == other.intValue(); } @Override diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 78460e13..86efaf45 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -178,7 +178,7 @@ private static void run(RunOptions options) { System.out.println(stagesData.getOrDefault(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, "")); } if (options.showMeasurements) { - System.out.println("======================"); + System.out.println("=".repeat(25)); System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); } } From c84bf9a4e52948aa122861c4a058c0d671f64ee3 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 11 Dec 2023 22:26:17 +0200 Subject: [PATCH 384/448] [http] Add synchronous httpSync --- docs/src/modules/http.yml | 16 ++- .../{http_http.java => HttpFunctions.java} | 109 ++++++++++-------- .../annimon/ownlang/modules/http/http.java | 4 +- 3 files changed, 79 insertions(+), 50 deletions(-) rename modules/main/src/main/java/com/annimon/ownlang/modules/http/{http_http.java => HttpFunctions.java} (66%) diff --git a/docs/src/modules/http.yml b/docs/src/modules/http.yml index 54c8bb71..0fc448cb 100644 --- a/docs/src/modules/http.yml +++ b/docs/src/modules/http.yml @@ -4,8 +4,8 @@ desc: "Contains network functions" desc_ru: "Содержит функции для взаимодействия с сетью" constants: [] functions: - - name: "http" - args: "url" + - name: http + args: "url, ..." desc: |- performs GET-request to `url`. @@ -65,6 +65,18 @@ functions: http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) { println "Added: " + v }) + - name: httpSync + args: 'url, method = "GET", requestParams = {}, options = {}' + desc: Synchronous version of `http` function. See above for parameters description. + desc_ru: Синхронная версия функции `http`. См. выше описание параметров. + example: |- + use http + extract(isOk, content) = httpSync("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}) + if (isOk) { + println "Added: " + content + } else { + println "Failure" + } - name: "download" args: "url" desc: "downloads content by url as bytes array" diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/HttpFunctions.java similarity index 66% rename from modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java rename to modules/main/src/main/java/com/annimon/ownlang/modules/http/HttpFunctions.java index 23048d65..02c6887a 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http_http.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/HttpFunctions.java @@ -1,6 +1,5 @@ package com.annimon.ownlang.modules.http; -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -14,7 +13,7 @@ import okhttp3.Response; import okhttp3.internal.http.HttpMethod; -public final class http_http implements Function { +public final class HttpFunctions { private static final Value HEADER_KEY = new StringValue("header"), @@ -27,71 +26,76 @@ public final class http_http implements Function { private final OkHttpClient client = new OkHttpClient(); - @Override - public Value execute(Value[] args) { - String url, method; - Function function; + public Value httpSync(Value[] args) { + Arguments.checkRange(1, 4, args.length); + + String url = args[0].asString(); + String method = (args.length >= 2) ? args[1].asString() : "GET"; + Value requestParams = (args.length >= 3) ? args[2] : MapValue.EMPTY; + MapValue options = (args.length >= 4) ? ValueUtils.consumeMap(args[3], 3) : MapValue.EMPTY; + + boolean isSuccessful; + Value result = options.containsKey(EXTENDED_RESULT) ? MapValue.EMPTY : StringValue.EMPTY; + try (Response response = executeRequest(url, method, requestParams, options)) { + isSuccessful = response.isSuccessful(); + if (isSuccessful) { + result = getResult(response, options); + } + } catch (IOException ioe) { + isSuccessful = false; + } + return new ArrayValue(new Value[] { + NumberValue.fromBoolean(isSuccessful), + result + }); + } + + public Value http(Value[] args) { + Arguments.checkRange(1, 5, args.length); + + String url = args[0].asString(); + String method = "GET"; + Value requestParams = MapValue.EMPTY; + MapValue options = MapValue.EMPTY; + Function callback = FunctionValue.EMPTY.getValue(); + switch (args.length) { case 1: // http(url) - url = args[0].asString(); - return process(url, "GET"); + break; case 2: // http(url, method) || http(url, callback) - url = args[0].asString(); if (args[1].type() == Types.FUNCTION) { - return process(url, "GET", ValueUtils.consumeFunction(args[1], 1)); + callback = ValueUtils.consumeFunction(args[1], 1); + } else { + method = args[1].asString(); } - return process(url, args[1].asString()); + break; case 3: // http(url, method, params) || http(url, method, callback) - url = args[0].asString(); method = args[1].asString(); if (args[2].type() == Types.FUNCTION) { - return process(url, method, ValueUtils.consumeFunction(args[2], 2)); + callback = ValueUtils.consumeFunction(args[2], 2); + } else { + requestParams = args[2]; } - return process(url, method, args[2], FunctionValue.EMPTY.getValue()); + break; case 4: // http(url, method, params, callback) - url = args[0].asString(); method = args[1].asString(); - function = ValueUtils.consumeFunction(args[3], 3); - return process(url, method, args[2], function); + requestParams = args[2]; + callback = ValueUtils.consumeFunction(args[3], 3); + break; case 5: // http(url, method, params, headerParams, callback) - url = args[0].asString(); method = args[1].asString(); - MapValue options = ValueUtils.consumeMap(args[3], 3); - function = ValueUtils.consumeFunction(args[4], 4); - return process(url, method, args[2], options, function); - - default: - throw new ArgumentsMismatchException("From 1 to 5 arguments expected, got " + args.length); + requestParams = args[2]; + options = ValueUtils.consumeMap(args[3], 3); + callback = ValueUtils.consumeFunction(args[4], 4); + break; } - } - - private Value process(String url, String method) { - return process(url, method, FunctionValue.EMPTY.getValue()); - } - - private Value process(String url, String method, Function callback) { - return process(url, method, MapValue.EMPTY, callback); - } - private Value process(String url, String method, Value params, Function callback) { - return process(url, method, params, MapValue.EMPTY, callback); - } - - private Value process(String url, String methodStr, Value requestParams, MapValue options, Function callback) { - final String method = methodStr.toUpperCase(); try { - final Request.Builder builder = new Request.Builder() - .url(url) - .method(method, getRequestBody(method, requestParams, options)); - if (options.containsKey(HEADER_KEY)) { - applyHeaderParams((MapValue) options.get(HEADER_KEY), builder); - } - - final Response response = client.newCall(builder.build()).execute(); + final Response response = executeRequest(url, method, requestParams, options); callback.execute(getResult(response, options)); return NumberValue.fromBoolean(response.isSuccessful()); } catch (IOException ex) { @@ -99,6 +103,17 @@ private Value process(String url, String methodStr, Value requestParams, MapValu } } + private Response executeRequest(String url, String methodStr, Value requestParams, MapValue options) throws IOException { + final String method = methodStr.toUpperCase(); + final Request.Builder builder = new Request.Builder() + .url(url) + .method(method, getRequestBody(method, requestParams, options)); + if (options.containsKey(HEADER_KEY)) { + applyHeaderParams((MapValue) options.get(HEADER_KEY), builder); + } + return client.newCall(builder.build()).execute(); + } + private Value getResult(Response response, MapValue options) throws IOException { if (options.containsKey(EXTENDED_RESULT)) { final MapValue map = new MapValue(10); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java index 4c5fe91f..5a203f59 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java @@ -19,9 +19,11 @@ public Map constants() { @Override public Map functions() { + final var httpFunctions = new HttpFunctions(); return Map.of( "urlencode", new http_urlencode(), - "http", new http_http(), + "http", httpFunctions::http, + "httpSync", httpFunctions::httpSync, "download", new http_download() ); } From f1ccc2c8af2950ee761bcfc806a9c5d299d19968 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 13 Dec 2023 22:22:49 +0200 Subject: [PATCH 385/448] Add gradle task for running npm --- docs/build.gradle | 8 ++++++++ docs/src/modules/okhttp.yml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/build.gradle b/docs/build.gradle index f5f694da..a2ed5e19 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -28,6 +28,14 @@ tasks.register('generateMarkdownModules') { finalizedBy ownlangExec } +tasks.register('runDocsDev', Exec) { + group = "documentation" + description = "Run sample program" + dependsOn generateMarkdownModules + workingDir '../docs/docs' + commandLine 'pnpm', 'docs:dev' +} + tasks.register('generateModuleInfo', JavaExec) { group = "documentation" description = "Run sample program" diff --git a/docs/src/modules/okhttp.yml b/docs/src/modules/okhttp.yml index ef13ea8d..d53eaaa0 100644 --- a/docs/src/modules/okhttp.yml +++ b/docs/src/modules/okhttp.yml @@ -1,5 +1,5 @@ name: okhttp -since: 2.0.0 +since: 1.5.0 scope: both constants: - name: MultipartBody From 6cdfd5173f4b1b68ea229436fad325ae93fc16f6 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 15 Dec 2023 22:18:29 +0200 Subject: [PATCH 386/448] Redeclare module versions --- build.gradle | 2 ++ docs/build.gradle | 2 +- modules/canvasfx/build.gradle | 2 +- modules/main/build.gradle | 3 +-- modules/server/build.gradle | 2 +- ownlang-core/build.gradle | 2 +- ownlang-desktop/build.gradle | 2 +- ownlang-parser/build.gradle | 2 +- ownlang-utils/build.gradle | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 386aa773..f60aaea3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,7 @@ ext { versions = [ + project: '2.0.0', + json: '20230227', // org.json:json snakeyaml: '1.20', // org.yaml:snakeyaml okhttp: '3.8.1', // com.squareup.okhttp3:okhttp diff --git a/docs/build.gradle b/docs/build.gradle index a2ed5e19..7e25b7cd 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.annimon' -version = '2.0-SNAPSHOT' +version = versions.project dependencies { implementation project(":ownlang-core") diff --git a/modules/canvasfx/build.gradle b/modules/canvasfx/build.gradle index aab78fa0..025d36d5 100644 --- a/modules/canvasfx/build.gradle +++ b/modules/canvasfx/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.annimon.module' -version = '2.0-SNAPSHOT' +version = '1.0.0' javafx { version = "17" diff --git a/modules/main/build.gradle b/modules/main/build.gradle index 1e8e777d..9bba8290 100644 --- a/modules/main/build.gradle +++ b/modules/main/build.gradle @@ -3,8 +3,7 @@ plugins { } group = 'com.annimon.module' -version = '2.0-SNAPSHOT' - +version = versions.project dependencies { compileOnlyApi project(":ownlang-core") diff --git a/modules/server/build.gradle b/modules/server/build.gradle index 3ad23262..b395a2b3 100644 --- a/modules/server/build.gradle +++ b/modules/server/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'com.annimon.module' -version = '2.0-SNAPSHOT' +version = '1.0.0' dependencies { compileOnlyApi project(":ownlang-core") diff --git a/ownlang-core/build.gradle b/ownlang-core/build.gradle index 96e8bfbe..ac6bf822 100644 --- a/ownlang-core/build.gradle +++ b/ownlang-core/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.annimon' -version = '2.0-SNAPSHOT' +version = versions.project dependencies { implementation "org.json:json:${versions.json}" diff --git a/ownlang-desktop/build.gradle b/ownlang-desktop/build.gradle index bb0defd3..719f15a8 100644 --- a/ownlang-desktop/build.gradle +++ b/ownlang-desktop/build.gradle @@ -5,7 +5,7 @@ plugins { } group = 'com.annimon' -version = '2.0-SNAPSHOT' +version = versions.project ext.mainClassName = 'com.annimon.ownlang.Main' application { diff --git a/ownlang-parser/build.gradle b/ownlang-parser/build.gradle index b411a92c..c34acf27 100644 --- a/ownlang-parser/build.gradle +++ b/ownlang-parser/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.annimon' -version = '2.0-SNAPSHOT' +version = versions.project dependencies { api project(':ownlang-core') diff --git a/ownlang-utils/build.gradle b/ownlang-utils/build.gradle index 205d0bf7..f8e2dbbf 100644 --- a/ownlang-utils/build.gradle +++ b/ownlang-utils/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.annimon' -version = '2.0-SNAPSHOT' +version = versions.project dependencies { api project(":ownlang-parser") From cea49f3c9d54dbe26952a500cb510d9caa9daf6f Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 15 Dec 2023 23:05:20 +0200 Subject: [PATCH 387/448] Add changelog page --- docs/docs/.vuepress/configs/pages.js | 3 +- docs/docs/en/changelog.md | 66 +++++++++++++++++++++++++++ docs/docs/ru/changelog.md | 68 ++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 docs/docs/en/changelog.md create mode 100644 docs/docs/ru/changelog.md diff --git a/docs/docs/.vuepress/configs/pages.js b/docs/docs/.vuepress/configs/pages.js index ff894be8..d60daba0 100644 --- a/docs/docs/.vuepress/configs/pages.js +++ b/docs/docs/.vuepress/configs/pages.js @@ -4,7 +4,8 @@ export default { text: {'en': 'OwnLang', 'ru': 'OwnLang'}, pages: [ 'README.md', - 'links.md' + 'links.md', + 'changelog.md', ] }, diff --git a/docs/docs/en/changelog.md b/docs/docs/en/changelog.md new file mode 100644 index 00000000..b3f0a0a3 --- /dev/null +++ b/docs/docs/en/changelog.md @@ -0,0 +1,66 @@ +# Changelog + +## 1.5.0 + +- Added modules `zip`, `gzip`, `okhttp` +- Added functions `std::getBytes`, `std::stringFromBytes`, `std::stripMargin` +- Added JProgressBar, JTextArea, JScrollPane to `forms`, methods for JButton, JTextField and WindowListener +- Added function `joining` to `functional::stream` +- Added array properties: `arr.length`, `arr.isEmpty()`, `arr.joinToString(...)` +- Added null coalesce operator `??` +- Added basic support for classes +- Strict string to number conversion +- `for` supports iterating strings and arrays with index: + `for ch : "test"` + `for ch, code : "test"` + `for el : arr` + `for el, index : arr` +- Pretty-print for `jsonencode`: + `jsonencode(obj)` — minified json + `jsonencode(obj, 2)` — pretty-print json with 2 spaces indent +- Ability to set options for yaml parser/dumper +- Fixed mysql connection in `jdbc` +- Fixed `str::range` for reversed ranges +- Fixed files::readBytes with offset and length +- Fixed matching class constructor in `java::new`. Ability to instantiate classes with `new` operator +- Other minor changes + + +## 1.4.0 + +- Added modules `downloader`, `regex` +- Added functions `std::arraySplice`, `std::default` +- Added constant `std::OwnLang` which stores language version and platform metadata +- Added `peek`, `sorted` to StreamValue +- An ability to import several modules `use ["std", "types", "math"]` +- String internal fields support (length, lower, upper, chars, trim(), startsWith(s), endsWith(s), matches(s), contains(s), equalsIgnoreCase(s), isEmpty()). Also support for extensions: `"%d. %s".sprintf(1, "OwnLang")` -> `sprintf("%d. %s", 1, "OwnLang")` +- Added kawaii-operator `^^` +- Improved REPL mode. Now command history (up key) supported on all platforms. Added autocompletion by Tab key. +- Improved error output +- Updated examples + + +## 1.3.0 + +- Function and function call chaining support (`func().func()` and `func()()`) +- Added `takewhile`, `dropwhile`, `stream` functions to `functional` module +- Added `parseInt`, `parseLong`, `toHexString` functions to `std` module +- Added `copy` function to `files` module +- Added `socket`, `base64`, `java`, `forms`, `jdbc` modules +- Improved optimization +- Updated examples +- Minor fixes and improvements + +## 1.2.0 + +- Added `canvasfx`, `date`, `yml`, `aimp` modules +- Updated `std`, `math`, `files`, `functional` modules +- Added `std::ARGS` constant for accessing command-line arguments +- Added REPL mode, Beautifier, Linter, Optimizer +- Fixed error recovering in parser and deadlock in lexer +- Added merging objects operation `map1 + map2` +- Fixed variables scope +- Speed up files reading +- Added NumberValue cache +- Updated Netbeans plugin +- Added examples and help diff --git a/docs/docs/ru/changelog.md b/docs/docs/ru/changelog.md new file mode 100644 index 00000000..82056eba --- /dev/null +++ b/docs/docs/ru/changelog.md @@ -0,0 +1,68 @@ +# История изменений + +## 1.5.0 + +- Добавлены модули `zip`, `gzip`, `okhttp` +- Добавлены функции `std::getBytes`, `std::stringFromBytes`, `std::stripMargin` +- В `forms` добавлены JProgressBar, JTextArea, JScrollPane, методы для JButton, JTextField и WindowListener +- В `functional::stream` добавлена функция `joining` +- Добавлены свойства и функции для массивов: `arr.length`, `arr.isEmpty()`, `arr.joinToString(...)` +- Добавлен оператор объединения с null `??` (null coalesce) +- Добавлены классы (пока без наследования, как структура) +- Строгое преобразование строк в числа (раньше int("test") выдавало 0, а теперь ошибку) +- В `for` теперь можно итерировать строки и массивы с индексом: + `for ch : "test"` + `for ch, code : "test"` + `for el : arr` + `for el, index : arr` +- В jsonencode можно задать отступ для читабельного форматирования: + `jsonencode(obj)` — минифицированный json + `jsonencode(obj, 2)` — pretty-print с отступом в 2 пробела +- Возможность задать параметры парсера/дампера yaml +- Исправлено подключение к mysql в модуле `jdbc` +- Исправлен `str::range` для реверсивных промежутков +- Исправлена функция files::readBytes с заданными offset и length +- Исправлен поиск подходящего конструктора класса в `java::new`, так же можно инстанцировать класс через оператор new +- Другие мелкие изменения + + +## 1.4.0 + +- Добавлены модули `downloader`, `regex` +- Добавлены функции `std::arraySplice`, `std::default` +- Добавлена константа `OwnLang` в модуль `std`, содержащая метаинформацию о версии языка и платформы +- В StreamValue добавлены функции `peek`, `sorted` +- Возможность импортировать сразу несколько модулей `use ["std", "types", "math"]` +- Поддержка внутренних полей и функций у строк (length, lower, upper, chars, trim(), startsWith(s), endsWith(s), matches(s), contains(s), equalsIgnoreCase(s), isEmpty()). Также доступны автоматические функции расширения: `"%d. %s".sprintf(1, "OwnLang")` -> `sprintf("%d. %s", 1, "OwnLang")` +- Добавлен kawaii-оператор `^^` для возможного переопределения +- Улучшен режим REPL, теперь история команд (клавиша вверх) поддерживается на всех платформах, а по табу теперь всплывают подсказки автодополнения +- Немного улучшен вывод ошибок +- Обновлены примеры + + +## 1.3.0 + +- Поддержка цепочек функций и функциональных вызовов (`func().func()` и `func()()`) +- Добавлены функции `takewhile`, `dropwhile`, `stream` в модуль `functional` +- Добавлены функции `parseInt`, `parseLong`, `toHexString` в модуль `std` +- Добавлена функция `copy` в модуль `files` +- Добавлены модули `socket`, `base64`, `java`, `forms`, `jdbc` +- Улучшена оптимизация +- Обновлены примеры +- Мелкие исправления и улучшения + + +## 1.2.0 + +- Добавлены модули `canvasfx`, `date`, `yml`, `aimp` +- Обновлены модули `std`, `math`, `files`, `functional` +- Добавлена константа `std::ARGS` для доступа к аргументам командной строки +- Добавлен режим REPL, Beautifier, линтер, оптимизатор +- Добавлена операция слияния объектов `map1 + map2` +- Исправлено восстановление при ошибках парсинга и зависание в лексере +- Исправлена область видимости переменных +- Ускорено чтение файлов +- Добавлен кэш числовых значений +- Обновлён плагин для Netbeans +- Добавлены примеры и помощь + From 7e9dc9038d20331c61301842b7f1cab49c35fda2 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 18 Dec 2023 21:44:48 +0200 Subject: [PATCH 388/448] [server] Add more methods to ContextValue --- docs/src/modules/server.yml | 30 ++++++++++++++++++- .../ownlang/modules/server/ContextValue.java | 22 ++++++++++++++ .../com/annimon/ownlang/lib/MapValue.java | 2 +- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/docs/src/modules/server.yml b/docs/src/modules/server.yml index cb317e13..4cc394cc 100644 --- a/docs/src/modules/server.yml +++ b/docs/src/modules/server.yml @@ -161,11 +161,23 @@ types: - name: html args: 'html' desc: sets result to the specified html string. Also sets Content-Type header to text/html - desc_ru: устанавливает указанныую html-строку в качестве результата. Также устанавливает заголовок Content-Type в text/html + desc_ru: устанавливает указанную html-строку в качестве результата. Также устанавливает заголовок Content-Type в text/html - name: ip args: '' desc: returns an IP address desc_ru: возвращает IP адрес + - name: isHttpMethod + args: '' + desc: returns true if the request is http method + desc_ru: возвращает true, если запрос — http метод + - name: isMultipartFormData + args: '' + desc: returns true if the request is multipart/formdata + desc_ru: возвращает true, если запрос — multipart/formdata + - name: isMultipart + args: '' + desc: returns true if the request is multipart + desc_ru: возвращает true, если запрос — multipart - name: json args: 'obj' desc: serializes an object to json string and sets it as the result @@ -178,10 +190,18 @@ types: args: '' desc: returns a matched request path desc_ru: возвращает совпавший путь запроса + - name: method + args: '' + desc: returns a method (GET, POST, ...) + desc_ru: возвращает метод (GET, POST, ...) - name: path args: '' desc: returns a request path desc_ru: возвращает путь запроса + - name: pathParam + args: 'key' + desc: returns a request path parameter + desc_ru: возвращает параметр пути запроса - name: port args: '' desc: returns a port number @@ -190,6 +210,10 @@ types: args: '' desc: returns a protocol desc_ru: возвращает протокол + - name: queryParam + args: 'key' + desc: returns a query parameter + desc_ru: возвращает параметр запроса - name: queryString args: '' desc: returns a query string @@ -210,6 +234,10 @@ types: args: 'value = ""' desc: gets or sets a result. `value` can be a string or a byte array desc_ru: получает или устанавливает результат. `value` может быть строкой или массивом байт + - name: status + args: 'status = ...' + desc: gets or sets a status code. `status` can be an integer status code (404, 500) or a string status name ("NOT_FOUND", "INTERNAL_SERVER_ERROR"). + desc_ru: получает или устанавливает код статуса. `status` может быть числовым кодом (404, 500) или строкой имени статуса ("NOT_FOUND", "INTERNAL_SERVER_ERROR") - name: statusCode args: '' desc: returns a response status code diff --git a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java index fe9d2ca4..81c6040b 100644 --- a/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java @@ -35,17 +35,24 @@ private void init() { set("host", Converters.voidToString(ctx::host)); set("html", stringToContext(ctx::html)); set("ip", Converters.voidToString(ctx::ip)); + set("isHttpMethod", Converters.voidToBoolean(() -> ctx.method().isHttpMethod())); + set("isMultipart", Converters.voidToBoolean(ctx::isMultipart)); + set("isMultipartFormData", Converters.voidToBoolean(ctx::isMultipartFormData)); set("json", objectToContext(ctx::json)); set("jsonStream", objectToContext(ctx::jsonStream)); set("matchedPath", Converters.voidToString(ctx::matchedPath)); + set("method", Converters.voidToString(() -> ctx.method().name())); set("path", Converters.voidToString(ctx::path)); + set("pathParam", Converters.stringToString(ctx::pathParam)); set("port", Converters.voidToInt(ctx::port)); set("protocol", Converters.voidToString(ctx::protocol)); + set("queryParam", Converters.stringToString(ctx::queryParam)); set("queryString", Converters.voidToString(ctx::queryString)); set("redirect", this::redirect); set("removeCookie", this::removeCookie); set("render", this::render); set("result", this::result); + set("status", this::status); set("statusCode", Converters.voidToInt(ctx::statusCode)); set("scheme", Converters.voidToString(ctx::scheme)); set("url", Converters.voidToString(ctx::url)); @@ -151,6 +158,21 @@ private Value result(Value[] args) { } } + private Value status(Value[] args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 0) { + return new StringValue(ctx.status().name()); + } else { + final var arg = args[0]; + if (arg.type() == Types.NUMBER) { + ctx.status(arg.asInt()); + } else { + ctx.status(HttpStatus.valueOf(arg.asString())); + } + return this; + } + } + private Value stringToContext(Consumer consumer) { return new FunctionValue(args -> { Arguments.check(1, args.length); diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java index eedac0dc..01b7728e 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -104,7 +104,7 @@ public Object raw() { @Override public Object asJavaObject() { - Map result = new HashMap<>(map.size()); + Map result = new LinkedHashMap<>(map.size()); map.forEach((k, v) -> result.put(k.asJavaObject(), v.asJavaObject())); return result; } From 8ca3672204996a7c151fdeb380bbfc36582274a1 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 30 Dec 2023 13:12:05 +0200 Subject: [PATCH 389/448] Add server examples --- examples/server/notes_public/index.html | 27 ++++++++++++++++++ examples/server/notes_public/notes.js | 33 ++++++++++++++++++++++ examples/server/notes_public/styles.css | 35 +++++++++++++++++++++++ examples/server/server_spa.own | 37 +++++++++++++++++++++++++ examples/server/server_spa_simple.own | 20 +++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 examples/server/notes_public/index.html create mode 100644 examples/server/notes_public/notes.js create mode 100644 examples/server/notes_public/styles.css create mode 100644 examples/server/server_spa.own create mode 100644 examples/server/server_spa_simple.own diff --git a/examples/server/notes_public/index.html b/examples/server/notes_public/index.html new file mode 100644 index 00000000..a81d83d1 --- /dev/null +++ b/examples/server/notes_public/index.html @@ -0,0 +1,27 @@ + + + + Notes + + + +
+

Notes

+
+ + +
+
    +
    + + +
      +
    • +

      Note 1

      +

      Content

      +
    • +
    +
    + + + \ No newline at end of file diff --git a/examples/server/notes_public/notes.js b/examples/server/notes_public/notes.js new file mode 100644 index 00000000..dc53ddf5 --- /dev/null +++ b/examples/server/notes_public/notes.js @@ -0,0 +1,33 @@ +const elNotes = document.querySelector('#notes'); +const elNoteTemplate = document.querySelector('templates ul[name="note"] li'); + +function renderNote(note) { + const el = elNoteTemplate.cloneNode(true); + el.querySelector('h4').innerText = 'Note #' + note.id; + el.querySelector('p').innerText = note.content; + return el; +} + +async function addNote() { + const inpEl = document.querySelector('.new-note input'); + const content = inpEl.value; + const resp = await fetch('/notes/', { + method: "POST", + body: content + }); + const note = await resp.json(); + inpEl.value = ''; + console.log(note); + elNotes.prepend(renderNote(note)); +} + +async function getNotes() { + const resp = await fetch("/notes"); + const notes = await resp.json(); + elNotes.innerHTML = ''; + for (const note of notes) { + elNotes.prepend(renderNote(note)); + } +} + +getNotes(); \ No newline at end of file diff --git a/examples/server/notes_public/styles.css b/examples/server/notes_public/styles.css new file mode 100644 index 00000000..73f35472 --- /dev/null +++ b/examples/server/notes_public/styles.css @@ -0,0 +1,35 @@ +templates { + display: none; +} + +#notes { + list-style-type: none; + padding: 0; +} +.note { + margin: 0; + padding: 0rem 0.3rem; +} +.note h4 { + margin: 0; +} +.note p { + color: #333; + margin-top: 0; +} + +.new-note { + margin-bottom: 2rem; +} +.new-note input { + width: 15rem; + padding: 0.3rem; +} +.new-note button { + padding: 0.3rem 1rem; + background-color: #4caf50; + color: #fff; + border: none; + cursor: pointer; + font-size: 1rem; +} \ No newline at end of file diff --git a/examples/server/server_spa.own b/examples/server/server_spa.own new file mode 100644 index 00000000..16cb9bec --- /dev/null +++ b/examples/server/server_spa.own @@ -0,0 +1,37 @@ +use std, jdbc, server + +// curl -X POST http://localhost:8084/notes/ -d "New note 2" + +conn = getConnection("jdbc:sqlite::memory:") +st = conn.createStatement() +st.executeUpdate("CREATE TABLE IF NOT EXISTS notes(id integer primary key, content string)") +stAddNote = conn.prepareStatement("INSERT INTO notes(content) VALUES(?)", RETURN_GENERATED_KEYS) +stGetNote = conn.prepareStatement("SELECT id, content FROM notes WHERE id = ?") + +createNote("This is your first note.") + +def getNotes() { + notes = [] + rs = st.executeQuery("SELECT id, content FROM notes") + while (rs.next()) { + notes += {"id": rs.getInt(1), "content": rs.getString(2)} + } + rs.close() + return notes +} + +def createNote(content) { + stAddNote.setString(1, content) + stAddNote.executeUpdate() + rs = stAddNote.getGeneratedKeys() + rs.next() + return {"id": rs.getLong(1) ?? -1, "content": content} +} + +newServer({"dev": true, "externalDirs": ["notes_public"]}) + .get("/notes", def(ctx) = ctx.json( getNotes() )) + .post("/notes", def(ctx) { + ctx.status(201) + ctx.json( createNote(ctx.body()) ) + }) + .start(8084) \ No newline at end of file diff --git a/examples/server/server_spa_simple.own b/examples/server/server_spa_simple.own new file mode 100644 index 00000000..8f1d3c86 --- /dev/null +++ b/examples/server/server_spa_simple.own @@ -0,0 +1,20 @@ +use std, jdbc, server + +// curl -X POST http://localhost:8084/notes/ -d "New note 2" + +notes = [] +createNote("This is your first note.") + +def createNote(content) { + note = {"id": notes.length + 1, "content": content}; + notes += note + return note +} + +newServer({"externalDirs": ["notes_public"]}) + .get("/notes", def(ctx) = ctx.json(notes)) + .post("/notes", def(ctx) { + ctx.status(201) + ctx.json( createNote(ctx.body()) ) + }) + .start(8084) \ No newline at end of file From 5a533cc6e1d8a5ae5b0a5783071323b6c7ac3772 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 1 Jan 2024 14:10:08 +0200 Subject: [PATCH 390/448] [functional] Add tomap, Stream::toMap, Stream::anyMatch, Stream::allMatch, Stream::noneMatch --- docs/src/modules/functional.yml | 31 +++++++++ .../modules/functional/StreamValue.java | 5 ++ .../modules/functional/functional.java | 1 + .../modules/functional/functional_chain.java | 2 +- .../functional/functional_combine.java | 2 +- .../functional/functional_dropWhile.java | 2 +- .../modules/functional/functional_filter.java | 2 +- .../functional/functional_filterNot.java | 2 +- .../functional/functional_flatmap.java | 2 +- .../functional/functional_forEach.java | 2 +- .../functional/functional_forEachIndexed.java | 2 +- .../functional/functional_groupBy.java | 2 +- .../modules/functional/functional_map.java | 2 +- .../modules/functional/functional_match.java | 52 +++++++++++++++ .../modules/functional/functional_reduce.java | 2 +- .../modules/functional/functional_sortBy.java | 2 +- .../modules/functional/functional_stream.java | 2 +- .../functional/functional_takeWhile.java | 2 +- .../modules/functional/functional_toMap.java | 63 +++++++++++++++++++ .../parser/ast/FunctionalExpression.java | 3 + .../resources/modules/functional/stream.own | 29 +++++++++ .../resources/modules/functional/tomap.own | 39 ++++++++++++ 22 files changed, 237 insertions(+), 14 deletions(-) create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_match.java create mode 100644 modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_toMap.java create mode 100644 ownlang-parser/src/test/resources/modules/functional/tomap.own diff --git a/docs/src/modules/functional.yml b/docs/src/modules/functional.yml index c0b554fd..3ab6bba8 100644 --- a/docs/src/modules/functional.yml +++ b/docs/src/modules/functional.yml @@ -128,6 +128,17 @@ functions: ] println groupby(data, def(e) = e.k1) // {"2"=[{k1=2, k2=x}], "4"=[{k1=4, k2=z}], "5"=[{k2=p, k1=5}]} println groupby(data, def(e) = e.k2) // {"x"=[{k1=2, k2=x}], "z"=[{k1=4, k2=z}], "p"=[{k2=p, k1=5}]} + - name: tomap + args: "data, keyMapper, valueMapper = def(v) = v, merger = def(oldValue, newValue) = newValue" + desc: "converts elements of an array or a map to a map based on `keyMapper` and `valueMapper` functions result. `merger` function resolves collisions" + desc_ru: "преобразует элементы массива или объекта в объект, основываясь на результате функций `keyMapper` и `valueMapper`. Функция `merger` используется для разрешения коллизий" + since: 2.0.0 + example: |- + use functional + + data = ["apple", "banana"] + println tomap(data, def(str) = str.substring(0, 1)) // {"a": "apple", "b": "banana"} + println tomap(data, def(str) = str.substring(0, 1), ::toUpperCase) // {"a": "APPLE", "b": "BANANA"} - name: stream args: data desc: creates stream from data and returns `StreamValue` @@ -229,6 +240,26 @@ types: args: "" desc: returns array of elements desc_ru: возвращает массив элементов + - name: toMap + args: "keyMapper, valueMapper = def(v) = v, merger = def(oldValue, newValue) = newValue" + desc: "converts elements to a map based on `keyMapper` and `valueMapper` functions result. `merger` function resolves collisions" + desc_ru: "преобразует элементы в объект, основываясь на результате функций `keyMapper` и `valueMapper`. Функция `merger` используется для разрешения коллизий" + since: 2.0.0 + - name: anyMatch + args: predicate + desc: "returns `true` if there is any element matching the given `predicate`, otherwise returns `false`" + desc_ru: "возвращает `true`, если хотя бы один элемент удовлетворяет функции `predicate`, иначе возвращает `false`" + since: 2.0.0 + - name: allMatch + args: predicate + desc: "returns `true` if all elements match the given `predicate`, otherwise returns `false`" + desc_ru: "возвращает `true`, если все элементы удовлетворяют функции `predicate`, иначе возвращает `false`" + since: 2.0.0 + - name: noneMatch + args: predicate + desc: "returns `true` if no elements match the given `predicate`, otherwise returns `false`" + desc_ru: "возвращает `true`, если нет элементов, удовлетворяющих функции `predicate`, иначе возвращает `false`" + since: 2.0.0 - name: count args: "" desc: returns the elements count diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java index ff26031e..07defb53 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -2,6 +2,7 @@ import com.annimon.ownlang.exceptions.ArgumentsMismatchException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.functional.functional_match.MatchType; import java.util.Arrays; class StreamValue extends MapValue { @@ -33,6 +34,10 @@ private void init() { set("forEachIndexed", wrapTerminal(new functional_forEachIndexed())); set("groupBy", wrapTerminal(new functional_groupBy())); set("toArray", args -> container); + set("toMap", wrapTerminal(new functional_toMap())); + set("anyMatch", wrapTerminal(functional_match.match(MatchType.ANY))); + set("allMatch", wrapTerminal(functional_match.match(MatchType.ALL))); + set("noneMatch", wrapTerminal(functional_match.match(MatchType.NONE))); set("joining", container::joinToString); set("count", args -> NumberValue.of(container.size())); } diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java index 711b170f..d51bfe33 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -28,6 +28,7 @@ public Map functions() { result.put("takewhile", new functional_takeWhile()); result.put("dropwhile", new functional_dropWhile()); result.put("groupby", new functional_groupBy()); + result.put("tomap", new functional_toMap()); result.put("chain", new functional_chain()); result.put("stream", new functional_stream()); diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java index 9fd1f29c..602999b3 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java @@ -5,7 +5,7 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.ValueUtils; -public final class functional_chain implements Function { +final class functional_chain implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java index e5c9e28d..d37432bd 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java @@ -7,7 +7,7 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class functional_combine implements Function { +final class functional_combine implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java index 11cbff37..5bd469b2 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_dropWhile.java @@ -9,7 +9,7 @@ import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.ValueUtils; -public final class functional_dropWhile implements Function { +final class functional_dropWhile implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java index 6f1c1ca6..0600b436 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filter.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.Map; -public final class functional_filter implements Function { +final class functional_filter implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java index 7ab6e204..1987c9b6 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java @@ -2,7 +2,7 @@ import com.annimon.ownlang.lib.*; -public final class functional_filterNot implements Function { +final class functional_filterNot implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java index 775be2b4..e59302de 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_flatmap.java @@ -10,7 +10,7 @@ import java.util.ArrayList; import java.util.List; -public final class functional_flatmap implements Function { +final class functional_flatmap implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java index a9b9b7a9..d25daa10 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java @@ -4,7 +4,7 @@ import com.annimon.ownlang.lib.*; import java.util.Map; -public final class functional_forEach implements Function { +final class functional_forEach implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java index 4ab3ee76..bd86eb4f 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java @@ -3,7 +3,7 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; -public final class functional_forEachIndexed implements Function { +final class functional_forEachIndexed implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java index 8b8c484a..f90a2f1e 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_groupBy.java @@ -7,7 +7,7 @@ import java.util.List; import java.util.Map; -public final class functional_groupBy implements Function { +final class functional_groupBy implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java index 5ca801cc..5d43a146 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_map.java @@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils; import java.util.Map; -public final class functional_map implements Function { +final class functional_map implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_match.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_match.java new file mode 100644 index 00000000..d76995a7 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_match.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; + +final class functional_match { + + enum MatchType { ALL, ANY, NONE } + + static Function match(MatchType matchType) { + return args -> { + Arguments.check(2, args.length); + final Value container = args[0]; + if (container.type() != Types.ARRAY) { + throw new TypeException("Invalid first argument. Array expected"); + } + final Function predicate = ValueUtils.consumeFunction(args[1], 1); + return switch (matchType) { + case ALL -> allMatch((ArrayValue) container, predicate); + case ANY -> anyMatch((ArrayValue) container, predicate); + case NONE -> noneMatch((ArrayValue) container, predicate); + }; + }; + } + + private static NumberValue allMatch(ArrayValue array, Function predicate) { + for (Value value : array) { + if (predicate.execute(value) == NumberValue.ZERO) { + return NumberValue.ZERO; + } + } + return NumberValue.ONE; + } + + private static NumberValue anyMatch(ArrayValue array, Function predicate) { + for (Value value : array) { + if (predicate.execute(value) != NumberValue.ZERO) { + return NumberValue.ONE; + } + } + return NumberValue.ZERO; + } + + private static NumberValue noneMatch(ArrayValue array, Function predicate) { + for (Value value : array) { + if (predicate.execute(value) != NumberValue.ZERO) { + return NumberValue.ZERO; + } + } + return NumberValue.ONE; + } +} diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java index cc03dfd9..b14b82b9 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -10,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils; import java.util.Map; -public final class functional_reduce implements Function { +final class functional_reduce implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java index a395ee93..2292aecf 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java @@ -10,7 +10,7 @@ import java.util.Arrays; import java.util.Comparator; -public final class functional_sortBy implements Function { +final class functional_sortBy implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java index 20173266..5ae744b0 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java @@ -3,7 +3,7 @@ import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; -public final class functional_stream implements Function { +final class functional_stream implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java index 3a0569d3..33f72618 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_takeWhile.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.Map; -public final class functional_takeWhile implements Function { +final class functional_takeWhile implements Function { @Override public Value execute(Value[] args) { diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_toMap.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_toMap.java new file mode 100644 index 00000000..cb7c1381 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_toMap.java @@ -0,0 +1,63 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.LinkedHashMap; +import java.util.Map; + +final class functional_toMap implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.checkRange(2, 4, args.length); + final Value container = args[0]; + final Function keyMapper = ValueUtils.consumeFunction(args[1], 1); + final Function valueMapper = args.length >= 3 + ? ValueUtils.consumeFunction(args[2], 2) + : null; + final Function merger = args.length >= 4 + ? ValueUtils.consumeFunction(args[3], 3) + : null; + return toMap(container, keyMapper, valueMapper, merger); + } + + static MapValue toMap(Value container, Function keyMapper, Function valueMapper, Function merger) { + return switch (container.type()) { + case Types.ARRAY -> toMap((ArrayValue) container, keyMapper, valueMapper, merger); + case Types.MAP -> toMap((MapValue) container, keyMapper, valueMapper, merger); + default -> throw new TypeException("Cannot iterate " + Types.typeToString(container.type())); + }; + } + + static MapValue toMap(ArrayValue array, Function keyMapper, Function valueMapper, Function merger) { + final Map result = new LinkedHashMap<>(array.size()); + for (Value element : array) { + final Value key = keyMapper.execute(element); + final Value value = valueMapper != null + ? valueMapper.execute(element) + : element; + final Value oldValue = result.get(key); + final Value newValue = (oldValue == null || merger == null) + ? value + : merger.execute(oldValue, value); + result.put(key, newValue); + } + return new MapValue(result); + } + + static MapValue toMap(MapValue map, Function keyMapper, Function valueMapper, Function merger) { + final Map result = new LinkedHashMap<>(map.size()); + for (Map.Entry element : map) { + final Value key = keyMapper.execute(element.getKey(), element.getValue()); + final Value value = valueMapper != null + ? valueMapper.execute(element.getKey(), element.getValue()) + : element.getValue(); + final Value oldValue = result.get(key); + final Value newValue = (oldValue == null || merger == null) + ? value + : merger.execute(oldValue, value); + result.put(key, newValue); + } + return new MapValue(result); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 1475ef47..a97edfc8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -54,6 +54,9 @@ public Value eval() { private Function consumeFunction(Node expr) { final Value value = expr.eval(); + if (value == null) { + throw new UnknownFunctionException(expr.toString(), getRange()); + } if (value.type() == Types.FUNCTION) { return ((FunctionValue) value).getValue(); } diff --git a/ownlang-parser/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own index 394edc98..da98a294 100644 --- a/ownlang-parser/src/test/resources/modules/functional/stream.own +++ b/ownlang-parser/src/test/resources/modules/functional/stream.own @@ -125,3 +125,32 @@ def testMapsGroupBy() { assertEquals([["test1", 234], ["test2", 345], ["test3", 456]], sort(result[true])) assertEquals([["abc", 123], ["def", 567]], sort(result[false])) } + +def testToMap() { + data = ["apple", "banana", "cherry"] + result = stream(data) + .toMap(def(str) = str.substring(0, 1), ::toUpperCase) + assertEquals("APPLE", result.a) + assertEquals("BANANA", result.b) + assertEquals("CHERRY", result.c) +} + +def testAllMatch() { + data = [2, 4, 8, 20] + assertTrue(stream(data).allMatch(def(v) = v % 2 == 0)) + assertFalse(stream(data).allMatch(def(v) = v < 10)) +} + +def testAnyMatch() { + data = [2, 4, 8, 20] + assertTrue(stream(data).anyMatch(def(v) = v > 10)) + assertFalse(stream(data).anyMatch(def(v) = v % 2 == 1)) +} + +def testNoneMatch() { + data = [2, 4, 8, 20] + assertTrue(stream(data).noneMatch(def(v) = v % 2 == 1)) + assertFalse(stream(data).noneMatch(def(v) = v > 10)) +} + + diff --git a/ownlang-parser/src/test/resources/modules/functional/tomap.own b/ownlang-parser/src/test/resources/modules/functional/tomap.own new file mode 100644 index 00000000..a834571e --- /dev/null +++ b/ownlang-parser/src/test/resources/modules/functional/tomap.own @@ -0,0 +1,39 @@ +use std, functional + +def testArrayToMapByKeyMapper() { + data = ["apple", "banana", "cherry"] + result = tomap(data, def(str) = str.substring(0, 1)) + assertEquals("apple", result.a) + assertEquals("banana", result.b) + assertEquals("cherry", result.c) +} + +def testArrayToMapByKeyValueMapper() { + data = ["apple", "banana", "cherry"] + result = tomap(data, def(str) = str.substring(0, 1), ::toUpperCase) + assertEquals("APPLE", result.a) + assertEquals("BANANA", result.b) + assertEquals("CHERRY", result.c) +} + +def testArrayToMapByKeyValueMapperAndMerger() { + data = ["apple", "banana", "cherry", "apricot", "coconut"] + result = tomap(data, def(str) = str.substring(0, 1), ::toUpperCase, def(oldValue, newValue) = oldValue + ", " + newValue) + assertEquals("APPLE, APRICOT", result.a) + assertEquals("BANANA", result.b) + assertEquals("CHERRY, COCONUT", result.c) +} + +def testMapToMapByKeyMapper() { + data = {"k1": 1, "k2": 2} + result = tomap(data, def(k, v) = k + "" + v) + assertEquals(1, result.k11) + assertEquals(2, result.k22) +} + +def testMapToMapByKeyValueMapper() { + data = {"k1": 1, "k2": 2} + result = tomap(data, def(k, v) = k + "" + v, def(k, v) = v + 10) + assertEquals(11, result.k11) + assertEquals(12, result.k22) +} From 9581c09e797cfd1a36a5432847d848f0022113ba Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Wed, 10 Jan 2024 21:45:38 +0200 Subject: [PATCH 391/448] Add changelog in docs --- docs/docs/README.md | 2 +- docs/docs/en/changelog.md | 25 +++++++++++++++++++++++++ docs/docs/ru/changelog.md | 25 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/docs/README.md b/docs/docs/README.md index 7c274b94..53360047 100644 --- a/docs/docs/README.md +++ b/docs/docs/README.md @@ -10,6 +10,6 @@ actions: - text: 🇷🇺 Русский link: /ru/ type: primary -footer: © 2023 aNNiMON +footer: © 2024 aNNiMON --- \ No newline at end of file diff --git a/docs/docs/en/changelog.md b/docs/docs/en/changelog.md index b3f0a0a3..ba4145e7 100644 --- a/docs/docs/en/changelog.md +++ b/docs/docs/en/changelog.md @@ -1,5 +1,30 @@ # Changelog +## 2.0.0 + +### Breaking changes +- Minimal Java version is 17. +- Simplified use statement. `use std, math` instead of `use ["std", "math"]`. +- Change `case [x]` behavior in list pattern matching to match single element. +- More strict lexer. Fixed escaping backslash in strings. Fixed HEX numbers println 0x0123456789, 0x०१२३४५६७८९. + +### Changes +- Introducing Constants. Constant can be imported only when using a module. +- Fixed variables scope in shadowing. +- Better error visualizing. Parse errors shows exact line in which an error occurs. Same for Linter and Runtime errors. +- Semantic linter as a required stage. +- Preserve the order of Map elements by default. +- Ability to run programs from resources by adding "resource:" prefix to path. +- Updated documentation. New documentation engine. + +### Modules +- [std] Added parseDouble, nanotime, exit, getenv, getprop functions. +- [http] Added httpSync function. +- [functional] Added groupby, tomap, Stream.groupBy, Stream.filterNot, Stream.forEachIndexed, Stream::toMap, Stream.anyMatch, Stream.allMatch, Stream.noneMatch operators. +- [canvasfx] Works for Java 17+ with Java FX 17 (Windows only). +- [server] New server module. + + ## 1.5.0 - Added modules `zip`, `gzip`, `okhttp` diff --git a/docs/docs/ru/changelog.md b/docs/docs/ru/changelog.md index 82056eba..038b82db 100644 --- a/docs/docs/ru/changelog.md +++ b/docs/docs/ru/changelog.md @@ -1,5 +1,30 @@ # История изменений +## 2.0.0 + +### Критические изменения +- Минимальная версия Java — 17. +- Упрощён оператор use. `use std, math` вместо `use ["std", "math"]`. +- `case [x]` при сопоставлении списков теперь соответствует лишь одному элементу. +- Более строгий лексер. Исправлено экранирование обратного слэша в строках. Исправлены HEX числа println 0x0123456789, 0x०१२३४५६७८९. + +### Изменения +- Добавлены константы. Константа может быть импортирована только при подключении модуля. +- Исправлена область видимости переменных при шедоуинге. +- Улучшена визуализация ошибок. Ошибки парсинга показывают конкретное место, где возникла ошибка. То же самое с линтером и ошибками времени исполнения. +- Семантический линтер как обязательный этап работы интерпретатора. +- Сохранение порядка элементов в Map по умолчанию. +- Возможность запускать программы из ресурсов, указав "resource:" в качестве префикса пути. +- Обновлена документация. Новый движок документации. + +### Модули +- [std] Добавлены функции parseDouble, nanotime, exit, getenv, getprop. +- [http] Добавлена функция httpSync. +- [functional] Добавлены функции groupby, tomap и операторы Stream.groupBy, Stream.filterNot, Stream.forEachIndexed, Stream::toMap, Stream.anyMatch, Stream.allMatch, Stream.noneMatch +- [canvasfx] Исправлено для Java 17+ с Java FX 17 (только Windows) +- [server] Новый модуль сервера + + ## 1.5.0 - Добавлены модули `zip`, `gzip`, `okhttp` From b7194207709454156848ad2fc9ee5a7b18d831b5 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Thu, 11 Jan 2024 22:12:14 +0200 Subject: [PATCH 392/448] Add search plugin, build docs task --- docs/.gitignore | 1 + docs/build.gradle | 10 +++++++++- docs/docs/.vuepress/config.js | 10 +++++++++- docs/docs/README.md | 2 +- docs/package.json | 7 ++++++- docs/pnpm-lock.yaml | 19 +++++++++++++++++++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/docs/.gitignore b/docs/.gitignore index b25f8534..c561d8e5 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -2,4 +2,5 @@ node_modules .temp .cache docs/.vuepress/configs/modules.js +docs/.vuepress/dist/ docs/*/modules/ diff --git a/docs/build.gradle b/docs/build.gradle index 7e25b7cd..72811d40 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -30,12 +30,20 @@ tasks.register('generateMarkdownModules') { tasks.register('runDocsDev', Exec) { group = "documentation" - description = "Run sample program" + description = "Start docs dev server" dependsOn generateMarkdownModules workingDir '../docs/docs' commandLine 'pnpm', 'docs:dev' } +tasks.register('buildDocs', Exec) { + group = "documentation" + description = "Build docs to static site" + dependsOn generateMarkdownModules + workingDir '../docs/docs' + commandLine 'pnpm', 'docs:build' +} + tasks.register('generateModuleInfo', JavaExec) { group = "documentation" description = "Run sample program" diff --git a/docs/docs/.vuepress/config.js b/docs/docs/.vuepress/config.js index f660608c..78845b53 100644 --- a/docs/docs/.vuepress/config.js +++ b/docs/docs/.vuepress/config.js @@ -2,6 +2,7 @@ import { defineUserConfig, defaultTheme } from 'vuepress' import { getDirname, path } from '@vuepress/utils' import { registerComponentsPlugin } from '@vuepress/plugin-register-components' import { prismjsPlugin } from '@vuepress/plugin-prismjs' +import { searchPlugin } from '@vuepress/plugin-search' import { sidebarConfig } from './configs/sidebar' import { navbarConfig } from './configs/navbar' import Prism from 'prismjs'; @@ -11,6 +12,7 @@ definePrismOwnLang(Prism) const __dirname = getDirname(import.meta.url) export default defineUserConfig({ + base: "/docs/ownlang/", locales: { '/en/': { lang: 'en-US', @@ -50,6 +52,12 @@ export default defineUserConfig({ }), registerComponentsPlugin({ componentsDir: path.resolve(__dirname, './components'), - }) + }), + searchPlugin({ + locales: { + '/en/': { placeholder: 'Search' }, + '/ru/': { placeholder: 'Поиск' }, + }, + }), ], }) diff --git a/docs/docs/README.md b/docs/docs/README.md index 53360047..1c14e3bd 100644 --- a/docs/docs/README.md +++ b/docs/docs/README.md @@ -7,7 +7,7 @@ actions: - text: 🇺🇸 English link: /en/ type: primary - - text: 🇷🇺 Русский + - text: 🇪🇷 Русский link: /ru/ type: primary footer: © 2024 aNNiMON diff --git a/docs/package.json b/docs/package.json index f9ff7181..4d455a4b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -7,13 +7,18 @@ "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" }, - "keywords": ["documentation", "ownlang", "programming-language"], + "keywords": [ + "documentation", + "ownlang", + "programming-language" + ], "author": "aNNiMON", "license": "MIT", "devDependencies": { "@vuepress/client": "2.0.0-rc.0", "@vuepress/plugin-prismjs": "2.0.0-rc.0", "@vuepress/plugin-register-components": "2.0.0-rc.0", + "@vuepress/plugin-search": "2.0.0-rc.0", "@vuepress/utils": "2.0.0-rc.0", "prismjs": "^1.29.0", "vue": "^3.3.8", diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 92a719de..d659b8ee 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -14,6 +14,9 @@ devDependencies: '@vuepress/plugin-register-components': specifier: 2.0.0-rc.0 version: 2.0.0-rc.0 + '@vuepress/plugin-search': + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 '@vuepress/utils': specifier: 2.0.0-rc.0 version: 2.0.0-rc.0 @@ -831,6 +834,22 @@ packages: - typescript dev: true + /@vuepress/plugin-search@2.0.0-rc.0: + resolution: {integrity: sha512-1ikJUgIN+7QrcAftxpWUKTrNVHEN2+k/az0Sjz7Ok7EthMHcG6qQsIb+AoK4WIQMsJkwVPLxwym/M1FbBTZDWQ==} + dependencies: + '@vuepress/client': 2.0.0-rc.0 + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/shared': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 + chokidar: 3.5.3 + vue: 3.3.8 + vue-router: 4.2.5(vue@3.3.8) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + /@vuepress/plugin-theme-data@2.0.0-rc.0: resolution: {integrity: sha512-FXY3/Ml+rM6gNKvwdBF6vKAcwnSvtXCzKgQwJAw3ppQTKUkLcbOxqM+h4d8bzHWAAvdnEvQFug5uEZgWllBQbA==} dependencies: From 3a766141175c3462c99803b10d9ef4d4ddce1297 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Fri, 12 Jan 2024 22:04:31 +0200 Subject: [PATCH 393/448] Fix CI --- .github/workflows/gradle.yml | 38 ++++++++++++++++++++++++++++++++++++ README.md | 4 +--- gradlew | 0 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/gradle.yml mode change 100644 => 100755 gradlew diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 00000000..20a209ff --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,38 @@ +name: build + +on: + push: + branches: [ "latest" ] + pull_request: + branches: [ "latest" ] + +env: + GRADLE_CACHE_KEY: ${{ github.run_id }}-gradle-${{ github.run_number }}-${{ github.run_number }}-${{ github.sha }} + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + name: Build & Test + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Gradle cache + uses: actions/cache@v3 + with: + path: ~/.gradle/caches + key: ${{ env.GRADLE_CACHE_KEY }} + restore-keys: ${{ env.GRADLE_CACHE_KEY }} + + - name: Run tests + uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 + with: + arguments: test diff --git a/README.md b/README.md index 5c1b938b..4d34d772 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # OwnLang -[![Build Status](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial.svg?branch=latest)](https://travis-ci.org/aNNiMON/Own-Programming-Language-Tutorial) - OwnLang - dynamic functional programming language inspired by Scala and Python. Available for PC, Android and Java ME devices. ## Installing @@ -157,4 +155,4 @@ or ## License -MIT - see [MIT licence information](LICENSE) +MIT - see [MIT license information](LICENSE) diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From 2f1e4bfb0c56cfd7ee16b31e67aa363c06f160f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 19:53:37 +0000 Subject: [PATCH 394/448] Bump vite from 5.0.2 to 5.0.11 in /docs Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.2 to 5.0.11. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.0.11/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/pnpm-lock.yaml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index d659b8ee..e928f63b 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -504,14 +504,14 @@ packages: resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} dev: true - /@vitejs/plugin-vue@4.5.0(vite@5.0.2)(vue@3.3.8): + /@vitejs/plugin-vue@4.5.0(vite@5.0.11)(vue@3.3.8): resolution: {integrity: sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 || ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.2 + vite: 5.0.11 vue: 3.3.8 dev: true @@ -605,7 +605,7 @@ packages: /@vuepress/bundler-vite@2.0.0-rc.0: resolution: {integrity: sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A==} dependencies: - '@vitejs/plugin-vue': 4.5.0(vite@5.0.2)(vue@3.3.8) + '@vitejs/plugin-vue': 4.5.0(vite@5.0.11)(vue@3.3.8) '@vuepress/client': 2.0.0-rc.0 '@vuepress/core': 2.0.0-rc.0 '@vuepress/shared': 2.0.0-rc.0 @@ -615,7 +615,7 @@ packages: postcss: 8.4.31 postcss-load-config: 4.0.1(postcss@8.4.31) rollup: 4.5.1 - vite: 5.0.2 + vite: 5.0.11 vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) transitivePeerDependencies: @@ -1571,6 +1571,15 @@ packages: source-map-js: 1.0.2 dev: true + /postcss@8.4.33: + resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} @@ -1785,8 +1794,8 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /vite@5.0.2: - resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==} + /vite@5.0.11: + resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1814,7 +1823,7 @@ packages: optional: true dependencies: esbuild: 0.19.7 - postcss: 8.4.31 + postcss: 8.4.33 rollup: 4.5.1 optionalDependencies: fsevents: 2.3.3 From d223bbbd0bc88ec32bf3545e789b9b731e4b8000 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:40:28 +0000 Subject: [PATCH 395/448] Bump vite from 5.0.11 to 5.0.12 in /docs Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.11 to 5.0.12. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.0.12/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.0.12/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/pnpm-lock.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index e928f63b..6d89c999 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -504,14 +504,14 @@ packages: resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} dev: true - /@vitejs/plugin-vue@4.5.0(vite@5.0.11)(vue@3.3.8): + /@vitejs/plugin-vue@4.5.0(vite@5.0.12)(vue@3.3.8): resolution: {integrity: sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 || ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.11 + vite: 5.0.12 vue: 3.3.8 dev: true @@ -605,17 +605,17 @@ packages: /@vuepress/bundler-vite@2.0.0-rc.0: resolution: {integrity: sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A==} dependencies: - '@vitejs/plugin-vue': 4.5.0(vite@5.0.11)(vue@3.3.8) + '@vitejs/plugin-vue': 4.5.0(vite@5.0.12)(vue@3.3.8) '@vuepress/client': 2.0.0-rc.0 '@vuepress/core': 2.0.0-rc.0 '@vuepress/shared': 2.0.0-rc.0 '@vuepress/utils': 2.0.0-rc.0 - autoprefixer: 10.4.16(postcss@8.4.31) + autoprefixer: 10.4.16(postcss@8.4.33) connect-history-api-fallback: 2.0.0 - postcss: 8.4.31 - postcss-load-config: 4.0.1(postcss@8.4.31) + postcss: 8.4.33 + postcss-load-config: 4.0.1(postcss@8.4.33) rollup: 4.5.1 - vite: 5.0.11 + vite: 5.0.12 vue: 3.3.8 vue-router: 4.2.5(vue@3.3.8) transitivePeerDependencies: @@ -970,7 +970,7 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true - /autoprefixer@10.4.16(postcss@8.4.31): + /autoprefixer@10.4.16(postcss@8.4.33): resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -982,7 +982,7 @@ packages: fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.31 + postcss: 8.4.33 postcss-value-parser: 4.2.0 dev: true @@ -1541,7 +1541,7 @@ packages: engines: {node: '>=8.6'} dev: true - /postcss-load-config@4.0.1(postcss@8.4.31): + /postcss-load-config@4.0.1(postcss@8.4.33): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -1554,7 +1554,7 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.31 + postcss: 8.4.33 yaml: 2.3.4 dev: true @@ -1794,8 +1794,8 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /vite@5.0.11: - resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} + /vite@5.0.12: + resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: From f1772746cc51121e06291a3312db62000515359b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 27 Jan 2024 20:39:05 +0200 Subject: [PATCH 396/448] Improve errors displaying for container expressions --- .../ownlang/modules/std/std_range.java | 19 ++++-- .../ownlang/exceptions/TypeException.java | 6 ++ .../com/annimon/ownlang/parser/Parser.java | 26 ++++---- .../parser/ast/AssignmentExpression.java | 32 +++++++--- .../parser/ast/ContainerAccessExpression.java | 63 +++++++++++++++---- .../parser/ast/ObjectCreationExpression.java | 7 +-- .../optimization/OptimizationVisitor.java | 8 +-- .../annimon/ownlang/parser/ast/ASTHelper.java | 2 +- 8 files changed, 115 insertions(+), 48 deletions(-) diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java index 1feb1b9d..67fc30b8 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_range.java @@ -9,9 +9,9 @@ final class std_range implements Function { public Value execute(Value[] args) { Arguments.checkRange(1, 3, args.length); return switch (args.length) { - default -> RangeValue.of(0, getLong(args[0]), 1); case 2 -> RangeValue.of(getLong(args[0]), getLong(args[1]), 1); case 3 -> RangeValue.of(getLong(args[0]), getLong(args[1]), getLong(args[2])); + default -> RangeValue.of(0, getLong(args[0]), 1); }; } @@ -41,9 +41,13 @@ public RangeValue(long from, long to, long step) { this.from = from; this.to = to; this.step = step; - final long base = (from < to) ? (to - from) : (from - to); + + final long base = (from < to) + ? Math.subtractExact(to, from) + : Math.subtractExact(from, to); final long absStep = (step < 0) ? -step : step; - this.size = (int) (base / absStep + (base % absStep == 0 ? 0 : 1)); + final long longSize = (base / absStep + (base % absStep == 0 ? 0 : 1)); + this.size = longSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) longSize; } @Override @@ -67,8 +71,9 @@ public Value[] getCopyElements() { private boolean isIntegerRange() { if (to > 0) { return (from > Integer.MIN_VALUE && to < Integer.MAX_VALUE); + } else { + return (to > Integer.MIN_VALUE && from < Integer.MAX_VALUE); } - return (to > Integer.MIN_VALUE && from < Integer.MAX_VALUE); } @Override @@ -78,10 +83,12 @@ public int size() { @Override public Value get(int index) { + long value = from + index * step; if (isIntegerRange()) { - return NumberValue.of((int) (from + index * step)); + return NumberValue.of((int) (value)); + } else { + return NumberValue.of(value); } - return NumberValue.of(from + (long) index * step); } @Override diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java index e5323079..4b4b5627 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java @@ -1,8 +1,14 @@ package com.annimon.ownlang.exceptions; +import com.annimon.ownlang.util.Range; + public final class TypeException extends OwnLangRuntimeException { public TypeException(String message) { super(message); } + + public TypeException(String message, Range range) { + super(message, range); + } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 271c3ee9..45bc8a78 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -353,10 +353,10 @@ private Node functionChain(Node qualifiedNameExpr) { if (lookMatch(0, TokenType.LPAREN)) { // next function call - return functionChain(new ContainerAccessExpression(expr, indices)); + return functionChain(new ContainerAccessExpression(expr, indices, getRange())); } // container access - return new ContainerAccessExpression(expr, indices); + return new ContainerAccessExpression(expr, indices, getRange()); } return expr; } @@ -532,7 +532,7 @@ private AssignmentExpression assignmentStrict() { final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType); final Node expression = expression(); - return new AssignmentExpression(op, (Accessible) targetExpr, expression); + return new AssignmentExpression(op, (Accessible) targetExpr, expression, getRange(position, index)); } private Node ternary() { @@ -765,9 +765,7 @@ private Node objectCreation() { args.add(expression()); match(TokenType.COMMA); } - final var expr = new ObjectCreationExpression(className, args); - expr.setRange(getRange(startTokenIndex, index - 1)); - return expr; + return new ObjectCreationExpression(className, args, getRange(startTokenIndex, index - 1)); } return unary(); @@ -859,12 +857,13 @@ private Node qualifiedName() { if (!match(TokenType.WORD)) return null; final List indices = variableSuffix(); + final var variable = new VariableExpression(current.text()); + variable.setRange(getRange(startTokenIndex, index - 1)); if (indices == null || indices.isEmpty()) { - final var variable = new VariableExpression(current.text()); - variable.setRange(getRange(startTokenIndex, index - 1)); return variable; + } else { + return new ContainerAccessExpression(variable, indices, variable.getRange()); } - return new ContainerAccessExpression(current.text(), indices); } private List variableSuffix() { @@ -905,15 +904,16 @@ private Node value() { if (lookMatch(1, TokenType.WORD) && lookMatch(2, TokenType.LPAREN)) { match(TokenType.DOT); return functionChain(new ContainerAccessExpression( - strExpr, Collections.singletonList( - new ValueExpression(consume(TokenType.WORD).text()) - ))); + strExpr, + Collections.singletonList(new ValueExpression(consume(TokenType.WORD).text())), + getRange() + )); } final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { return strExpr; } - return new ContainerAccessExpression(strExpr, indices); + return new ContainerAccessExpression(strExpr, indices, getRange()); } return strExpr; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java index e76c0afb..ae2892ca 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java @@ -1,22 +1,32 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.lib.EvaluableValue; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue { +public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue, SourceLocation { public final Accessible target; public final BinaryExpression.Operator operation; public final Node expression; + private final Range range; - public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Node expr) { + public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Node expr, Range range) { this.operation = operation; this.target = target; this.expression = expr; + this.range = range; + } + + @Override + public Range getRange() { + return range; } @Override @@ -24,13 +34,21 @@ public Value eval() { super.interruptionCheck(); if (operation == null) { // Simple assignment - return target.set(expression.eval()); + return target.set(checkNonNull(expression.eval(), "Assignment expression")); } - final Node expr1 = new ValueExpression(target.get()); - final Node expr2 = new ValueExpression(expression.eval()); - return target.set(new BinaryExpression(operation, expr1, expr2).eval()); + final Node expr1 = new ValueExpression(checkNonNull(target.get(), "Assignment target")); + final Node expr2 = new ValueExpression(checkNonNull(expression.eval(), "Assignment expression")); + final Value result = new BinaryExpression(operation, expr1, expr2).eval(); + return target.set(result); } - + + private Value checkNonNull(Value value, String message) { + if (value == null) { + throw new OwnLangRuntimeException(message + " evaluates to null", range); + } + return value; + } + @Override public void accept(Visitor visitor) { visitor.visit(this); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 09b3f993..f0ddac14 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -1,7 +1,10 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.exceptions.OwnLangRuntimeException; import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; import java.util.List; import java.util.regex.Pattern; @@ -9,7 +12,7 @@ * * @author aNNiMON */ -public final class ContainerAccessExpression implements Node, Accessible { +public final class ContainerAccessExpression implements Node, Accessible, SourceLocation { private static final Pattern PATTERN_SIMPLE_INDEX = Pattern.compile("^\"[a-zA-Z$_]\\w*\""); @@ -17,15 +20,13 @@ public final class ContainerAccessExpression implements Node, Accessible { public final List indices; private final boolean[] simpleIndices; private final boolean rootIsVariable; + private final Range range; - public ContainerAccessExpression(String variable, List indices) { - this(new VariableExpression(variable), indices); - } - - public ContainerAccessExpression(Node root, List indices) { + public ContainerAccessExpression(Node root, List indices, Range range) { rootIsVariable = root instanceof VariableExpression; this.root = root; this.indices = indices; + this.range = range; simpleIndices = precomputeSimpleIndices(); } @@ -33,6 +34,11 @@ public boolean rootIsVariable() { return rootIsVariable; } + @Override + public Range getRange() { + return range; + } + public Node getRoot() { return root; } @@ -47,11 +53,24 @@ public Value get() { final Value container = getContainer(); final Value lastIndex = lastIndex(); return switch (container.type()) { - case Types.ARRAY -> ((ArrayValue) container).get(lastIndex); + case Types.ARRAY -> { + final ArrayValue arr = (ArrayValue) container; + final int size = arr.size(); + if (lastIndex.type() != Types.NUMBER) { + yield arr.get(lastIndex); + } else { + final int index = lastIndex.asInt(); + if (0 <= index && index < size) { + yield arr.get(index); + } else { + throw outOfBounds(index, size); + } + } + } case Types.MAP -> ((MapValue) container).get(lastIndex); case Types.STRING -> ((StringValue) container).access(lastIndex); case Types.CLASS -> ((ClassInstance) container).access(lastIndex); - default -> throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); + default -> throw arrayOrMapExpected(container, " while accessing a container"); }; } @@ -60,14 +79,23 @@ public Value set(Value value) { final Value container = getContainer(); final Value lastIndex = lastIndex(); switch (container.type()) { - case Types.ARRAY -> ((ArrayValue) container).set(lastIndex.asInt(), value); + case Types.ARRAY -> { + final ArrayValue arr = (ArrayValue) container; + final int size = arr.size(); + final int index = lastIndex.asInt(); + if (0 <= index && index < size) { + arr.set(index, value); + } else { + throw outOfBounds(index, size); + } + } case Types.MAP -> ((MapValue) container).set(lastIndex, value); case Types.CLASS -> ((ClassInstance) container).set(lastIndex, value); - default -> throw new TypeException("Array or map expected. Got " + container.type()); + default -> throw arrayOrMapExpected(container, " while setting a value to container"); } return value; } - + public Value getContainer() { Value container = root.eval(); final int last = indices.size() - 1; @@ -76,7 +104,7 @@ public Value getContainer() { container = switch (container.type()) { case Types.ARRAY -> ((ArrayValue) container).get(index.asInt()); case Types.MAP -> ((MapValue) container).get(index); - default -> throw new TypeException("Array or map expected"); + default -> throw arrayOrMapExpected(container, " while resolving a container"); }; } return container; @@ -96,6 +124,17 @@ public MapValue consumeMap(Value value) { } return (MapValue) value; } + + private OwnLangRuntimeException outOfBounds(int index, int size) { + return new OwnLangRuntimeException( + "Index %d is out of bounds for array length %d".formatted(index, size), range); + } + + private TypeException arrayOrMapExpected(Value v, String message) { + return new TypeException("Array or map expected" + + (message == null ? "" : message) + + ". Got " + Types.typeToString(v.type()), range); + } @Override public void accept(Visitor visitor) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java index a19bbf6b..82d0e930 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -11,14 +11,11 @@ public final class ObjectCreationExpression implements Node, SourceLocation { public final String className; public final List constructorArguments; - private Range range; + private final Range range; - public ObjectCreationExpression(String className, List constructorArguments) { + public ObjectCreationExpression(String className, List constructorArguments, Range range) { this.className = className; this.constructorArguments = constructorArguments; - } - - public void setRange(Range range) { this.range = range; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 29a90d35..d2c29476 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -31,7 +31,7 @@ public Node visit(AssignmentExpression s, T t) { final Node exprNode = s.expression.accept(this, t); final Node targetNode = s.target.accept(this, t); if ( (exprNode != s.expression || targetNode != s.target) && (targetNode instanceof Accessible) ) { - return new AssignmentExpression(s.operation, (Accessible) targetNode, exprNode); + return new AssignmentExpression(s.operation, (Accessible) targetNode, exprNode, s.getRange()); } return s; } @@ -79,7 +79,7 @@ public Node visit(ClassDeclarationStatement s, T t) { final AssignmentExpression newField; if (fieldExpr != field.expression) { changed = true; - newField = new AssignmentExpression(field.operation, field.target, fieldExpr); + newField = new AssignmentExpression(field.operation, field.target, fieldExpr, field.getRange()); } else { newField = field; } @@ -126,7 +126,7 @@ public Node visit(ContainerAccessExpression s, T t) { indices.add(node); } if (changed) { - return new ContainerAccessExpression(root, indices); + return new ContainerAccessExpression(root, indices, s.getRange()); } return s; } @@ -356,7 +356,7 @@ public Node visit(ObjectCreationExpression s, T t) { } if (changed) { - return new ObjectCreationExpression(s.className, args); + return new ObjectCreationExpression(s.className, args, s.getRange()); } return s; } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java index ef65b332..d0787489 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ASTHelper.java @@ -44,7 +44,7 @@ public static AssignmentExpression assign(Accessible accessible, Node expr) { } public static AssignmentExpression assign(BinaryExpression.Operator op, Accessible accessible, Node expr) { - return new AssignmentExpression(op, accessible, expr); + return new AssignmentExpression(op, accessible, expr, null); } public static BinaryExpression operator(BinaryExpression.Operator op, Node left, Node right) { From 03d890df7c6c20dcdc119c5337b7690b191af70d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Mon, 29 Jan 2024 18:38:51 +0200 Subject: [PATCH 397/448] Disable jline expansion --- build.gradle | 2 +- .../main/java/com/annimon/ownlang/utils/repl/JLineConsole.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f60aaea3..02eada79 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ ext { snakeyaml: '1.20', // org.yaml:snakeyaml okhttp: '3.8.1', // com.squareup.okhttp3:okhttp socket: '1.0.2', // io.socket:socket.io-client - jline: '2.14.5', // jline:jline + jline: '2.14.6', // jline:jline javalin: '5.6.3', // io.javalin:javalin slf4j: '2.0.9', // org.slf4j:slf4j-simple diff --git a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java index 04b06119..a312b572 100644 --- a/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java @@ -9,6 +9,7 @@ public class JLineConsole implements ReplConsole { private final ConsoleReader console; public JLineConsole() throws IOException { + System.setProperty(ConsoleReader.JLINE_EXPAND_EVENTS, "false"); console = new ConsoleReader(); } From 31945471dd0deea44ddc1915db6c0da81f3ca1cf Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 10 Feb 2024 22:30:10 +0200 Subject: [PATCH 398/448] Refactor ounit functions --- .../annimon/ownlang/modules/ounit/ounit.java | 154 ++++++++---------- .../ownlang/parser/MockOUnitStage.java | 52 ++++++ .../annimon/ownlang/parser/ProgramsTest.java | 45 +---- 3 files changed, 121 insertions(+), 130 deletions(-) create mode 100644 ownlang-parser/src/test/java/com/annimon/ownlang/parser/MockOUnitStage.java diff --git a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java index c087230f..39088724 100644 --- a/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -5,7 +5,6 @@ import com.annimon.ownlang.modules.Module; import java.text.DecimalFormat; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -22,110 +21,91 @@ public Map constants() { @Override public Map functions() { - final var result = new LinkedHashMap(16); - result.put("assertEquals", new assertEquals()); - result.put("assertNotEquals", new assertNotEquals()); - result.put("assertSameType", new assertSameType()); - result.put("assertTrue", new assertTrue()); - result.put("assertFalse", new assertFalse()); - result.put("runTests", new runTests()); - return result; + return Map.of( + "assertEquals", this::assertEquals, + "assertNotEquals", this::assertNotEquals, + "assertSameType", this::assertSameType, + "assertTrue", this::assertTrue, + "assertFalse", this::assertFalse, + "runTests", this::runTests + ); } private static String microsToSeconds(long micros) { return new DecimalFormat("#0.0000").format(micros / 1000d / 1000d) + " sec"; } - - private static class assertEquals implements Function { - @Override - public Value execute(Value[] args) { - Arguments.check(2, args.length); - if (args[0].equals(args[1])) return NumberValue.ONE; - throw new OUnitAssertionException("Values are not equal: " - + "1: " + args[0] + ", 2: " + args[1]); - } + + private Value assertEquals(Value[] args) { + Arguments.check(2, args.length); + if (args[0].equals(args[1])) return NumberValue.ONE; + throw new OUnitAssertionException("Values are not equal: " + + "1: " + args[0] + ", 2: " + args[1]); } - private static class assertNotEquals implements Function { - @Override - public Value execute(Value[] args) { - Arguments.check(2, args.length); - if (!args[0].equals(args[1])) return NumberValue.ONE; - throw new OUnitAssertionException("Values are equal: " + args[0]); - } + private Value assertNotEquals(Value[] args) { + Arguments.check(2, args.length); + if (!args[0].equals(args[1])) return NumberValue.ONE; + throw new OUnitAssertionException("Values are equal: " + args[0]); } - private static class assertSameType implements Function { - @Override - public Value execute(Value[] args) { - Arguments.check(2, args.length); - if (args[0].type() == args[1].type()) return NumberValue.ONE; - throw new OUnitAssertionException("Types mismatch. " - + "1: " + Types.typeToString(args[0].type()) - + ", 2: " + Types.typeToString(args[1].type())); - } + private Value assertSameType(Value[] args) { + Arguments.check(2, args.length); + if (args[0].type() == args[1].type()) return NumberValue.ONE; + throw new OUnitAssertionException("Types mismatch. " + + "1: " + Types.typeToString(args[0].type()) + + ", 2: " + Types.typeToString(args[1].type())); } - private static class assertTrue implements Function { - @Override - public Value execute(Value[] args) { - Arguments.check(1, args.length); - if (args[0].asInt() != 0) return NumberValue.ONE; - throw new OUnitAssertionException("Expected true, but found false."); - } + private Value assertTrue(Value[] args) { + Arguments.check(1, args.length); + if (args[0].asInt() != 0) return NumberValue.ONE; + throw new OUnitAssertionException("Expected true, but found false."); } - private static class assertFalse implements Function { - @Override - public Value execute(Value[] args) { - Arguments.check(1, args.length); - if (args[0].asInt() == 0) return NumberValue.ONE; - throw new OUnitAssertionException("Expected false, but found true."); - } + private Value assertFalse(Value[] args) { + Arguments.check(1, args.length); + if (args[0].asInt() == 0) return NumberValue.ONE; + throw new OUnitAssertionException("Expected false, but found true."); } - - private static class runTests implements Function { - - @Override - public Value execute(Value[] args) { - final var testFunctions = ScopeHandler.functions().entrySet().stream() - .filter(e -> e.getKey().toLowerCase().startsWith("test")) - .toList(); - List tests = testFunctions.stream() - .map(e -> runTest(e.getKey(), e.getValue())) - .toList(); - int failures = 0; - long summaryTime = 0; - final StringBuilder result = new StringBuilder(); - for (TestInfo test : tests) { - if (!test.isPassed) failures++; - summaryTime += test.elapsedTimeInMicros; - result.append(Console.newline()); - result.append(test.info()); - } + private Value runTests(Value[] args) { + final var testFunctions = ScopeHandler.functions().entrySet().stream() + .filter(e -> e.getKey().toLowerCase().startsWith("test")) + .toList(); + List tests = testFunctions.stream() + .map(e -> runTest(e.getKey(), e.getValue())) + .toList(); + + int failures = 0; + long summaryTime = 0; + final StringBuilder result = new StringBuilder(); + for (TestInfo test : tests) { + if (!test.isPassed) failures++; + summaryTime += test.elapsedTimeInMicros; result.append(Console.newline()); - result.append(String.format("Tests run: %d, Failures: %d, Time elapsed: %s", - tests.size(), failures, - microsToSeconds(summaryTime))); - return new StringValue(result.toString()); + result.append(test.info()); } + result.append(Console.newline()); + result.append(String.format("Tests run: %d, Failures: %d, Time elapsed: %s", + tests.size(), failures, + microsToSeconds(summaryTime))); + return new StringValue(result.toString()); + } - private TestInfo runTest(String name, Function f) { - final long startTime = System.nanoTime(); - boolean isSuccessfull; - String failureDescription; - try { - f.execute(); - isSuccessfull = true; - failureDescription = ""; - } catch (OUnitAssertionException oae) { - isSuccessfull = false; - failureDescription = oae.getMessage(); - } - final long elapsedTime = System.nanoTime() - startTime; - return new TestInfo(name, isSuccessfull, failureDescription, elapsedTime / 1000); + private TestInfo runTest(String name, Function f) { + final long startTime = System.nanoTime(); + boolean isSuccessfull; + String failureDescription; + try { + f.execute(); + isSuccessfull = true; + failureDescription = ""; + } catch (OUnitAssertionException oae) { + isSuccessfull = false; + failureDescription = oae.getMessage(); } + final long elapsedTime = System.nanoTime() - startTime; + return new TestInfo(name, isSuccessfull, failureDescription, elapsedTime / 1000); } private static class OUnitAssertionException extends RuntimeException { @@ -142,7 +122,7 @@ private record TestInfo( long elapsedTimeInMicros ) { public String info() { - return String.format("%s [%s]\n%sElapsed: %s\n", + return "%s [%s]\n%sElapsed: %s\n".formatted( name, isPassed ? "passed" : "FAILED", isPassed ? "" : (failureDescription + "\n"), diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/MockOUnitStage.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/MockOUnitStage.java new file mode 100644 index 00000000..3ac9db47 --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/MockOUnitStage.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.parser; + +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.ScopeHandler; +import com.annimon.ownlang.parser.ast.Node; +import com.annimon.ownlang.stages.Stage; +import com.annimon.ownlang.stages.StagesData; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.fail; + +public class MockOUnitStage implements Stage { + + @Override + public Node perform(StagesData stagesData, Node input) { + ScopeHandler.resetScope(); + ScopeHandler.setFunction("assertEquals", (args) -> { + assertEquals(args[0], args[1]); + return NumberValue.ONE; + }); + ScopeHandler.setFunction("assertNotEquals", (args) -> { + assertNotEquals(args[0], args[1]); + return NumberValue.ONE; + }); + ScopeHandler.setFunction("assertSameType", (args) -> { + assertEquals(args[0].type(), args[1].type()); + return NumberValue.ONE; + }); + ScopeHandler.setFunction("assertTrue", (args) -> { + assertTrue(args[0].asInt() != 0); + return NumberValue.ONE; + }); + ScopeHandler.setFunction("assertFalse", (args) -> { + assertFalse(args[0].asInt() != 0); + return NumberValue.ONE; + }); + ScopeHandler.setFunction("assertFail", (args) -> { + assertThrows(Throwable.class, + () -> ((FunctionValue) args[0]).getValue().execute()); + return NumberValue.ONE; + }); + ScopeHandler.setFunction("fail", (args) -> { + if (args.length > 0) { + fail(args[0].asString()); + } else { + fail(); + } + return NumberValue.ONE; + }); + return input; + } +} diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index d08787d4..2de00393 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -2,8 +2,6 @@ import com.annimon.ownlang.Console; import com.annimon.ownlang.exceptions.OwnLangParserException; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.ClassDeclarationStatement; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; @@ -42,7 +40,7 @@ public static void createStage() { .then(new ParserStage()) .then(new LinterStage(LinterStage.Mode.SEMANTIC)) .thenConditional(true, new OptimizationStage(9)) - .then(ProgramsTest::mockOUnit) + .then(new MockOUnitStage()) .then(new ExecutionStage()) .then((stagesData, input) -> { input.accept(testFunctionsExecutor); @@ -50,45 +48,6 @@ public static void createStage() { }); } - private static Node mockOUnit(StagesData stagesData, Node input) { - ScopeHandler.resetScope(); - // Let's mock junit methods as ounit functions - ScopeHandler.setFunction("assertEquals", (args) -> { - assertEquals(args[0], args[1]); - return NumberValue.ONE; - }); - ScopeHandler.setFunction("assertNotEquals", (args) -> { - assertNotEquals(args[0], args[1]); - return NumberValue.ONE; - }); - ScopeHandler.setFunction("assertSameType", (args) -> { - assertEquals(args[0].type(), args[1].type()); - return NumberValue.ONE; - }); - ScopeHandler.setFunction("assertTrue", (args) -> { - assertTrue(args[0].asInt() != 0); - return NumberValue.ONE; - }); - ScopeHandler.setFunction("assertFalse", (args) -> { - assertFalse(args[0].asInt() != 0); - return NumberValue.ONE; - }); - ScopeHandler.setFunction("assertFail", (args) -> { - assertThrows(Throwable.class, - () -> ((FunctionValue) args[0]).getValue().execute()); - return NumberValue.ONE; - }); - ScopeHandler.setFunction("fail", (args) -> { - if (args.length > 0) { - fail(args[0].asString()); - } else { - fail(); - } - return NumberValue.ONE; - }); - return input; - } - @ParameterizedTest @MethodSource("data") public void testProgram(InputSource inputSource) { @@ -119,7 +78,7 @@ public void visit(FunctionDefineStatement s) { @Override public void visit(ClassDeclarationStatement s) { - + // skip for tests } }; } From 7acad442111f641117e878586b0cb0e37394c493 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 10 Feb 2024 23:30:37 +0200 Subject: [PATCH 399/448] Support for long number declaration --- docs/docs/en/changelog.md | 1 + docs/docs/ru/changelog.md | 1 + .../com/annimon/ownlang/parser/Lexer.java | 13 ++- .../com/annimon/ownlang/parser/Parser.java | 99 ++++++++++++------- .../com/annimon/ownlang/parser/TokenType.java | 2 + .../com/annimon/ownlang/parser/LexerTest.java | 22 ++--- .../parser/LexerValidDataProvider.java | 5 +- .../annimon/ownlang/parser/ProgramsTest.java | 2 +- .../expressions/binaryExpressionOnNumbers.own | 10 ++ 9 files changed, 102 insertions(+), 53 deletions(-) diff --git a/docs/docs/en/changelog.md b/docs/docs/en/changelog.md index ba4145e7..bff44f1d 100644 --- a/docs/docs/en/changelog.md +++ b/docs/docs/en/changelog.md @@ -10,6 +10,7 @@ ### Changes - Introducing Constants. Constant can be imported only when using a module. +- Support for long number declaration: `700L`, `0xABL` - Fixed variables scope in shadowing. - Better error visualizing. Parse errors shows exact line in which an error occurs. Same for Linter and Runtime errors. - Semantic linter as a required stage. diff --git a/docs/docs/ru/changelog.md b/docs/docs/ru/changelog.md index 038b82db..01c7b25f 100644 --- a/docs/docs/ru/changelog.md +++ b/docs/docs/ru/changelog.md @@ -10,6 +10,7 @@ ### Изменения - Добавлены константы. Константа может быть импортирована только при подключении модуля. +- Возможность задать числа типа long: `700L`, `0xABL` - Исправлена область видимости переменных при шедоуинге. - Улучшена визуализация ошибок. Ошибки парсинга показывают конкретное место, где возникла ошибка. То же самое с линтером и ошибками времени исполнения. - Семантический линтер как обязательный этап работы интерпретатора. diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 72663ef3..aea3a268 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -167,7 +167,8 @@ private void tokenizeNumber() { while (true) { if (current == '.') { decimal = true; - if (hasDot) throw error("Invalid float number " + buffer, startPos); + if (hasDot) + throw error("Invalid float number " + buffer, startPos); hasDot = true; } else if (current == 'e' || current == 'E') { decimal = true; @@ -182,6 +183,9 @@ private void tokenizeNumber() { } if (decimal) { addToken(TokenType.DECIMAL_NUMBER, buffer.toString(), startPos); + } else if (current == 'L') { + next(); + addToken(TokenType.LONG_NUMBER, buffer.toString(), startPos); } else { addToken(TokenType.NUMBER, buffer.toString(), startPos); } @@ -232,7 +236,12 @@ private void tokenizeHexNumber(int skipChars) { if (buffer.isEmpty()) throw error("Empty HEX value", startPos); if (peek(-1) == '_') throw error("HEX value cannot end with _", startPos, markEndPos()); - addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); + if (current == 'L') { + next(); + addToken(TokenType.HEX_LONG_NUMBER, buffer.toString(), startPos); + } else { + addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos); + } } private static boolean isNumber(char current) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 45bc8a78..689f19c9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -50,6 +50,14 @@ public static Node parse(List tokens) { ASSIGN_OPERATORS.put(TokenType.ATEQ, BinaryExpression.Operator.AT); } + private static final EnumSet NUMBER_TOKEN_TYPES = EnumSet.of( + TokenType.NUMBER, + TokenType.LONG_NUMBER, + TokenType.DECIMAL_NUMBER, + TokenType.HEX_NUMBER, + TokenType.HEX_LONG_NUMBER + ); + private final List tokens; private final int size; private final ParseErrors parseErrors; @@ -347,7 +355,7 @@ private Node functionChain(Node qualifiedNameExpr) { } if (lookMatch(0, TokenType.DOT)) { final List indices = variableSuffix(); - if (indices == null || indices.isEmpty()) { + if (indices.isEmpty()) { return expr; } @@ -411,20 +419,10 @@ private MatchExpression match() { consume(TokenType.CASE); MatchExpression.Pattern pattern = null; final Token current = get(0); - if (match(TokenType.NUMBER)) { - // case 20: - pattern = new MatchExpression.ConstantPattern( - NumberValue.of(createNumber(current.text(), 10)) - ); - } else if (match(TokenType.DECIMAL_NUMBER)) { - // case 0.5: - pattern = new MatchExpression.ConstantPattern( - NumberValue.of(createDecimalNumber(current.text())) - ); - } else if (match(TokenType.HEX_NUMBER)) { - // case #FF: + if (isNumberToken(current.type())) { + // case 20: / case 0.5: / case #FF: pattern = new MatchExpression.ConstantPattern( - NumberValue.of(createNumber(current.text(), 16)) + NumberValue.of(getAsNumber(current)) ); } else if (match(TokenType.TEXT)) { // case "text": @@ -859,7 +857,7 @@ private Node qualifiedName() { final List indices = variableSuffix(); final var variable = new VariableExpression(current.text()); variable.setRange(getRange(startTokenIndex, index - 1)); - if (indices == null || indices.isEmpty()) { + if (indices.isEmpty()) { return variable; } else { return new ContainerAccessExpression(variable, indices, variable.getRange()); @@ -869,7 +867,7 @@ private Node qualifiedName() { private List variableSuffix() { // .key1.arr1[expr1][expr2].key2 if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) { - return null; + return Collections.emptyList(); } final List indices = new ArrayList<>(); while (lookMatch(0, TokenType.DOT) || lookMatch(0, TokenType.LBRACKET)) { @@ -888,47 +886,72 @@ private List variableSuffix() { private Node value() { final Token current = get(0); - if (match(TokenType.NUMBER)) { - return new ValueExpression(createNumber(current.text(), 10)); - } - if (match(TokenType.DECIMAL_NUMBER)) { - return new ValueExpression(createDecimalNumber(current.text())); - } - if (match(TokenType.HEX_NUMBER)) { - return new ValueExpression(createNumber(current.text(), 16)); + if (isNumberToken(current.type())) { + return new ValueExpression(getAsNumber(current)); } if (match(TokenType.TEXT)) { final ValueExpression strExpr = new ValueExpression(current.text()); // "text".property || "text".func() if (lookMatch(0, TokenType.DOT)) { - if (lookMatch(1, TokenType.WORD) && lookMatch(2, TokenType.LPAREN)) { - match(TokenType.DOT); - return functionChain(new ContainerAccessExpression( - strExpr, - Collections.singletonList(new ValueExpression(consume(TokenType.WORD).text())), - getRange() - )); - } - final List indices = variableSuffix(); - if (indices == null || indices.isEmpty()) { - return strExpr; - } - return new ContainerAccessExpression(strExpr, indices, getRange()); + return stringProperty(strExpr); } return strExpr; } throw error("Unknown expression: " + current); } + private Node stringProperty(ValueExpression strExpr) { + if (lookMatch(1, TokenType.WORD) && lookMatch(2, TokenType.LPAREN)) { + match(TokenType.DOT); + return functionChain(new ContainerAccessExpression( + strExpr, + Collections.singletonList(new ValueExpression(consume(TokenType.WORD).text())), + getRange() + )); + } + final List indices = variableSuffix(); + if (indices.isEmpty()) { + return strExpr; + } + return new ContainerAccessExpression(strExpr, indices, getRange()); + } + + private boolean isNumberToken(TokenType type) { + return NUMBER_TOKEN_TYPES.contains(type); + } + + private Number getAsNumber(Token current) { + if (match(TokenType.NUMBER)) { + return createNumber(current.text(), 10); + } + if (match(TokenType.LONG_NUMBER)) { + return createLongNumber(current.text(), 10); + } + if (match(TokenType.DECIMAL_NUMBER)) { + return createDecimalNumber(current.text()); + } + if (match(TokenType.HEX_NUMBER)) { + return createNumber(current.text(), 16); + } + if (match(TokenType.HEX_LONG_NUMBER)) { + return createLongNumber(current.text(), 16); + } + throw error("Unknown number expression: " + current); + } + private Number createNumber(String text, int radix) { // Integer try { return Integer.parseInt(text, radix); } catch (NumberFormatException nfe) { - return Long.parseLong(text, radix); + return createLongNumber(text, radix); } } + private Number createLongNumber(String text, int radix) { + return Long.parseLong(text, radix); + } + private Number createDecimalNumber(String text) { // Double return Double.parseDouble(text); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java index cf6a71db..7c7f2c06 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java @@ -7,8 +7,10 @@ public enum TokenType { NUMBER, + LONG_NUMBER, DECIMAL_NUMBER, HEX_NUMBER, + HEX_LONG_NUMBER, WORD, TEXT, diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index 062cfc8e..22bbba93 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -41,17 +41,17 @@ public static Stream invalidData() { } @Test - public void testNumbers() { - String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF"; + void testNumbers() { + String input = "0 800L 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF 0x7FL"; List result = Lexer.tokenize(input); - assertTokens(result, NUMBER, DECIMAL_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); + assertTokens(result, NUMBER, LONG_NUMBER, DECIMAL_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_LONG_NUMBER); assertThat(result) .extracting(Token::text) - .containsExactly("0", "3.1415", "CAFEBABE", "f7d6c5", "FFFF"); + .containsExactly("0", "800", "3.1415", "CAFEBABE", "f7d6c5", "FFFF", "7F"); } @Test - public void testDecimalNumbersExponent() { + void testDecimalNumbersExponent() { String input = "4e+7 0.3E-19 2e0 5e0000000000000200 5E-000000089"; List result = Lexer.tokenize(input); assertThat(result) @@ -61,7 +61,7 @@ public void testDecimalNumbersExponent() { } @Test - public void testString() { + void testString() { String input = "\"1\\\"2\""; List result = Lexer.tokenize(input); assertTokens(result, TEXT); @@ -69,7 +69,7 @@ public void testString() { } @Test - public void testEscapeString() { + void testEscapeString() { String input = """ "\\\\/\\\\" """.stripIndent(); @@ -79,7 +79,7 @@ public void testEscapeString() { } @Test - public void testEmptyString() { + void testEmptyString() { String input = "\"\""; List result = Lexer.tokenize(input); assertTokens(result, TEXT); @@ -87,7 +87,7 @@ public void testEmptyString() { } @Test - public void testComments() { + void testComments() { String input = "// 1234 \n /* */ 123 /* \n 12345 \n\n\n */"; List result = Lexer.tokenize(input); assertTokens(result, NUMBER); @@ -96,7 +96,7 @@ public void testComments() { @ParameterizedTest @MethodSource("validData") - public void testValidInput(String name, String input, List tokenTypes) throws IOException { + void testValidInput(String name, String input, List tokenTypes) throws IOException { List result = Lexer.tokenize(input); assertThat(result) .hasSize(tokenTypes.size()) @@ -106,7 +106,7 @@ public void testValidInput(String name, String input, List tokenTypes @ParameterizedTest @MethodSource("invalidData") - public void testInvalidInput(String name, String input) throws IOException { + void testInvalidInput(String name, String input) throws IOException { assertThatThrownBy(() -> Lexer.tokenize(input)) .isInstanceOf(OwnLangParserException.class); } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java index 891eab2e..a22e99ed 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java @@ -33,7 +33,10 @@ private static List numbers() { List.of(DECIMAL_NUMBER)), Arguments.of("Hex numbers", "#FF 0xCA 0x12fb 0xFF", - List.of(HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER)) + List.of(HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER)), + Arguments.of("Long numbers", + "680L #80L 0x700L", + List.of(LONG_NUMBER, HEX_LONG_NUMBER, HEX_LONG_NUMBER)) ); } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 2de00393..eb32171b 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -50,7 +50,7 @@ public static void createStage() { @ParameterizedTest @MethodSource("data") - public void testProgram(InputSource inputSource) { + void testProgram(InputSource inputSource) { final StagesDataMap stagesData = new StagesDataMap(); try { testPipeline.perform(stagesData, inputSource); diff --git a/ownlang-parser/src/test/resources/expressions/binaryExpressionOnNumbers.own b/ownlang-parser/src/test/resources/expressions/binaryExpressionOnNumbers.own index 0b6be707..c7341593 100644 --- a/ownlang-parser/src/test/resources/expressions/binaryExpressionOnNumbers.own +++ b/ownlang-parser/src/test/resources/expressions/binaryExpressionOnNumbers.own @@ -14,6 +14,16 @@ def testMultiplicationOnNumbers() { assertEquals(30, 5 * (-2 * -3)) } +def testMultiplicationOverflowOnNumbers() { + assertNotEquals(1234567890000L, 1234567890 * 1000) + assertNotEquals(0xFFFFFF00, 0x100 * 0xFFFFFF) +} + +def testMultiplicationOnLongNumbers() { + assertEquals(1234567890000L, 1234567890 * 1000L) + assertEquals(0xFFFFFF00L, 0x100L * 0xFFFFFF) +} + def testDivisionOnNumbers() { assertEquals(3, 6 / 2) assertEquals(30, -900 / (60 / -2)) From 14e165edf74686178cc37732491007274081c9c8 Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 17 Feb 2024 21:48:09 +0200 Subject: [PATCH 400/448] Add GTKSourceView syntax --- .gitignore | 3 +- editors/README.md | 4 ++ editors/gtksourceview/ownlang.lang | 89 ++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 editors/gtksourceview/ownlang.lang diff --git a/.gitignore b/.gitignore index 8fda7643..598e8c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ /out/ /store/ /optimizations/ -OwnLang.iml \ No newline at end of file +OwnLang.iml +*.sh \ No newline at end of file diff --git a/editors/README.md b/editors/README.md index 9d41cb8e..d58f6aa6 100644 --- a/editors/README.md +++ b/editors/README.md @@ -13,3 +13,7 @@ import Prism from 'prismjs'; import definePrismOwnLang from './prismjs/own-language.js' definePrismOwnLang(Prism) ``` + +## GTKSourceView + +Place `ownlang.lang` in `/usr/share/gtksourceview-3.0/language-specs/ownlang.lang` \ No newline at end of file diff --git a/editors/gtksourceview/ownlang.lang b/editors/gtksourceview/ownlang.lang new file mode 100644 index 00000000..7d852cfa --- /dev/null +++ b/editors/gtksourceview/ownlang.lang @@ -0,0 +1,89 @@ + + + + text/x-own + *.own + // + /* + */ + + + +