From c1b3ec4c119fb6d95dd40bd4dfddad3224ff68a9 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 24 Sep 2019 21:55:12 +0300 Subject: [PATCH 001/189] =?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 002/189] =?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 003/189] =?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 004/189] =?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 005/189] =?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 006/189] =?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 007/189] =?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 008/189] =?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 009/189] =?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 010/189] =?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 011/189] =?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 012/189] =?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 013/189] =?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 014/189] =?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 015/189] =?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 016/189] =?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 017/189] =?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 018/189] =?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 019/189] =?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 020/189] =?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 021/189] =?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 022/189] =?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 023/189] =?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 024/189] 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 025/189] =?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 026/189] =?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 027/189] 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 028/189] 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 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^y&(UJw6GFCwYZE3Eii!GzbJwQ6GlMey#wI?@jA$V`!0~bud{V9{g+Srcb#AV)G>9?H?lJR z|5Qc%S5;RBeLFj2hyT|QGk+tKg1@Rue}(Wr4-v9;wXw3*HzJ~^F|^Wmbo7pthU%w- z3)(Sb)}VBu_5ZaJoZW|Ohfl-BZzX62DK1{#mGKL9H*XNh{(|e68)wq1=H&nqPq4oi z%|O7bnKfm?yNp=By{T$W1?fU!6I8#Mv8}nA>6|R1f*Oq^FvvNak`#*C{X$4va>UoS zA`(Erflj173T0bTR*Vy4rJu~FU5UXK;(<5T2_25xs{}W2mH=8n1Pu%~Bx(T0nHt;s z-&T2OJ7^i{@856tcZr4mf99y@?&xG}E$3kScd?wzjUE3!xw-Q@JDC~VIGG#jJJ~w? zV-boJt!)wb;e1fYLPqBH%k-*})|Wk$j>2u{^e`Z!!XW9T%cZ4wt@VLTt6hz38}UJg!HZUDyJEC{0fA%B4aTas_G)I~=ju_&r7 zUt=R`wptSW9_elN^MoEl)!8l64sKQCG7?+tFV<5l_w;jH;ATg;r{;YoH&__}dx33x zeDpz*Ds4ukuf%;MB$jzLUWHe1Cm^_K)V(TihDco5rAUNczQBX4KYk!X7<5;MHJ-2* z-+m0*Naz$)a;3cl^%>2`c=)A)maHjorP!uJmSLER3I>fSQ}^xXduW4~$jM!1u*(B1 z*3GCW*_IEE$hoCYHYsjI2isq56{?zzBYO-)VNQ<1pjL?CXhcudoOGVZ@jiM(fDgk} zE9WoidJEpVYhg6Px7IJnHII#h>DFKS;X7bF`lZ4SSUH^uAn3yP=sxQZ;*B={o*lgP z4y`HUO(iT&Yo;9T8-kWCE&eHL;ldz7prmH$sGby`5E`h+RZf3c(#TeRcA=AIFI73G zYr^kqKloTRPpFZfC7G;)gwi|%_aP+%t*(&}fHz{SQKb)LrA3&*_xlaLO+r5Es0aUh zTPD-6PiB3XT|w9G4Enev%)y{i%SSD`7uqIroSPIA(_DX{=`a|Qka}ISZwk=bIo9`= z>e%{Wk^CTXYO4&&+9K`$gp&XA+mlN*$MV0{w((a8{>ig?h(7`{G zXU9nJolrVY26vqmP{90hk2)<3EE1gOPCOalxV<3=oJr^qV=13+4_;fi04S%PrydXx zKKYcy%(4&(XCx=8(}`qj`lvy=<4l^S3V{uT_-b1Q@`-6Grm)--p5F9zr7wZ}ji2gM z7lQq28Hq)~qzbj;xA}0v%ozQ*hO})GYtM-htwfRE1;>gZe0Fl+ZGk9S6V{T>SF4X! zH@&{V|2k8UGLJ2-zy2lv*T1O$^GrqmcfeA1GsOv z;+NNB)9gim`Z+LlqfYkcS{pBae-12wHv&BQnA@p=av|hvDL~8N&+Wcbyy5KzI zMHI}W`z0YIp%XOUpWpc@bl1nKZHpe~`DJF3T^4ejg6+;%*_fFoYAZCR9i=UViZ~wVJFKzr^M7W|Pr@uw+3IM;1zD z+^|}PY))Z@prCrQ84pmPRg-_Z(CuQU!2}D9+gE5TF;k$d@N|fDO>0}19N{pvc3dpF zjoZtlJ6m|SuEU$6MUj3|r$;wiYh=>hYphwg79D05YaSc;;jc$9lE*6x(eZ2XxYvt^ z9>Vhzbt=?FB7;4dzySJ6-(J_1x&#R7M}?GbywO-<>Fmb%d(F>ZS|H2 zHk+!ZquLJpn;z}?vJXPgu17o*aYJf zkmke~=YfBr>gj66l8xz6vPFXvDdYYj=OV)HXToVpkkv4HWE${JIiyBY7rXIPa-WA=mU$RE0pM%?$)E z`(|Ifg$r|p_6?zW?zg!l7H}w5c6t6chs4^~-WUP}0C@k43mE^inF_lZS~)wKyBLd@ zTN(2k8X7w~O6%L`n;QQ!>L;m4+94Wa{aB}yn73Qw^Wn=`0R%P5`IDh6_$RL#m}%s~ z6oDeQjIn69Z$)KDOM2t+oPRjqo@Ny=5K^mw52K5Ujs$QV_}%pnq0?rg(c%p5v}7cA zWB-1``8m1yd1vAM{#b$mfIUdSYtCx`f-fALKN59?)4_T<5Q5`z3ZD?SKZnd!y)@@% zCr<9hlPTDV@dKC!ktYmgX2Tq0bYl@yoB_4}J@b(VLPv(g2xt_Pjv+)HOc6I=2Zu4O zY5>xXTi}D{lZvoh7){DC<4mM@b>boG>_qfI9H?-TL{D5yDMGVsshJ*U87G%S7v*1t z=8}_-stk$T%u=2%+);tYFCkGnozb4nWVM8$=*0inWD#tFn=FSTO@jGOm}voDDr*mcu%2&&m5z?+Kz&_hX6Zp?h>@0WTo#NiN!Cuo)yy;* z@&3B&&TP1lnuD+Dk}-uA1D{}HB0{v-77qqv8jL(3_vC-zrym(ARrat)&-hC}bT$!a zYVija4-#;1hPi%NA+nPF9PA>VWoGS4eGsu%a`bqUia*1SHnB=O^(XAp3I<0DTi=pn z%OUlhe_3#90|PVAd#>ULdWc42@y0@WB*oWJkh0E^AIW;0yYOn{8FVq@b{#DsRt=kGsk!^t#kmHOiJ-ZI^|>u z*(e=C17Wu{OT2Qh*F`zdWQ4VJVdlw|A97U^POCfL!oVf`ad~HM1;xch6b@qCl5j$W zae46W2H3A+oyH}^aPCQTZJHJDhEi1z%+naylqY9F-q{6ZQ7t@4Y!mN zwe1sKIW2UmH(G5(L19!EZgCU{sxi`QQSD^i+|FO~QUJ#ofp2=R z$rERKS?OSSWBkaK0{yj$<=A1`I>I)|m9moeb;xymV3wwM$Z;URyG6lio4SW-_tKPj zzM!WVOVQ1ss?vtnTUjr&1jux7iqAPj->+x%DQaLn+vJL@?lD-jx;Y6inWl1GazXGK zLI~X?*h1rURkSfKi+K5 z;i2O={6}I%8FvN)S_4(2_Tjjj=2U@n3$S-`fp_-Fe0moiSHg77_E6kg#y$c%dB;8? zIyn!&1hY#WV1XLF0cKBU;dk z(&J_e>L_4R@hjr4m`tXPrX9$_WQL{94fN8DLQ!-Idc3n%u4mkT1uv5@IwEm@!OI)i z{}sHb{-bshw6!rYH+6Q-2C0K2jOn4N%sm*++Xih+X7lhjjYn<7onOnIr$jaEj_>l8;rSGR4LE(&pYfC4doO&Sfs1~tgf3Dykr(?TuwG`)C0&*a+01Cn1#j=8!X=1( zS0WofL!_d9<~PbXZ34DPycH;9xI-ejUSd9dq?}3wn7m0O*8s8>athj^J9U|_=<&r` zZ6aJ|M1twQy%yp=@p<%}jrTi9nq#6?Y8KwqlwH5wA~DIW*sq;&J8V`YJbQE_1xN<| z1LVI?g(4VTun<3VpZl5;v4zkK1t4uzVB+I=j)iGAzzT492@Z3SRs<9IRR z4~4K|@_(er`4t#O9f`%1VdCTYlf@h6!3&A_EF@wZp%qm9Pc8o5>t)hcy!pm~j5roI zzkdCzZ5w$^?!^BE<=lVwJm~&2;`#S_S4`jL@6N(M;ZBr_rlO`Y(l?7Z8$Q-}7n7J~ zVN;-{0<9QvBLxx>G7vFDk=XFbO&#R`MrWKj*_m3D}z|K%x@6(||e{$S&y0ZaiDazElKEf#5w_H6H z83Kilyj^QhN2p_Ov;IOcsg;A+qDu;53L|Ow#Hm z!*f!m!ji_$e(#V2OqrHI)xEvpe>}(6bDP|!>7LA7EVWxwnw}DA0@UrPoATF!Gf|^# zNX?Bvf={S8;U!krMI>OYH#9h^Hu6?&hUZ#PtRoOdW*HmO#apJ3))Ctk&yd-0$qFsi z^3Vy3LcpOGDh&$-9yHP~I)ldyPuG+G^gv_MFQ}L75=hb2O%wVW>3fh?mtYStoH=eS zxT1?SAg)nwIgPVxsO>Bs{FZkf7WRvd|00aGv5Y28;7#HgSGSQCbYBOG5+0;!NS0E; z8AzdFe>y{Wp~uueBRlY9{lYydI07UskI=Gi8~y`BPpEGpvuqN1X6op@pW2<8)O6tC z7n)t7#6^};-WrMuq7n0ww!|QQU4&O{0Ianm9|7rCU81BR(pf>^R|q9IY*Qoe;CFp6 zm{MPCXmv(BT|KTSZ4$K@Z1YPiwb^>&dQ0Zq#CCk1<@AEPTJuKx*g<)S#hiDpeQWu!kv?ZQh(eOPY=->m}3@*c;ln4*p zkzbiheKR$&u)s&e8Uk3LqBFZZgE#JCyvE+!r=oupr~&By@JGX-_0!2~QFRAoi0!rr zE>>L)Fterxe2BUQgc>aZ>e z`h83nSN-C|G_(+=xSX|4Xk;e%E`H)8c z5zaMjUC;?}P1M7>Gd$&%fqcm>fKv2~xT!JP{&C+_tIv`u2zSSEg-()Ao=T?AHEF%c z3sAS@SwzS4LHA$dTai0myUO3(4e+}*?NCmE%_KWK{XucLi^;gQzjDg5OrArIPvIH0mU52d96q8hR&_MK_CzAdI! zJd~@|n1j5(H?*J|Mm{at(Joo0ncEJY6Yy0TVES!05jMIfrH3kyGO$|)|Kr!`CRWw}vcz@41fWI%jp5_; z$7v*AimR!bW{@hR4x!jqz=Y2#RyORez(&zFL3XpK#-gMfb!W;v^t=T}&^$9)A^N;z z5C?MC=I#FT58%I=q`|8><>_B2iSZi%faE`$q@2E!8NZ{Wv9-Z}C)y;HH(ksX_#YZE z4fRTEDnm{^F=Hu2e8BRpVQcCAWXfg)kVMKM83B|=l#9@$`i}ZMRgX658%pl^_80Gj z<+#mR*$2;`(&n8tZOPnFk~jXFDbIA)hpd~)jFzA8nTsDFyWc;Ndt8x%iPa-=y&{qE zi6?Emhw?bnMT3Ze& zPXB(n03bWZ*S}Jhq zWJhH#PV0@4Y2(M~`n2bk!h)Z_UX8a{jIphPH(?S=KT0HB@DDo1H|w7q)@m6Y+dJro zOIgay7v|~?eOC6b%=+wJ9_rGqj4#N2O&V9G1csJ{U7c>JyMA|u+3i_**C2yZPc=G~ z;DKe6VAM^Dcux6&@D~2#0@T(}i%Vv~>(pwiMY7`Qtz)fiY++Kc&5`*Mc z5N74JF}Q@T0zblB=ddf8`4hsGi3>bSwH0tvWH1z z@VO!~wSVW<6~^^0J-A%ROLfzkg_RG6dDHMdV0t)0Ri6=aETcKx*UU{Dfi7HoIos&l zz`rPoE=y?0W1C`&AazhvUMwd{&t%00?V=MNwr6T$Y+$VK*n(?&acQ^<<3ggj^4#Qz zy(XS;e|(%0%}3LfgN*!4&c+F3XSZ0yeV9DnN(W)^RqlS_n#6B}FrBXrYOWv6Uiy{pq~rF1`e{B~0XI0@{K7YhSGr-g2*11D z-h)M?tyDCzB3(hvfpPeLAl@Q@KzE3*?4pEj7d>$zKVm!*I`q{~TJEw;+mdEVldjAPj((~d#Ofb0c;W?viQ=of~)t?IGX}POIFE zLblu;Y+VQh`P&%p9N^_{cBCy4gA$+6j7vYkrf<-S-__omQTAA(;D*;m^&e+%RNlY3 zU+BLfJm^DWZiT?#(nf&(?uK@T64R!~alFG*d7f?@62r#wNLrJ(R6BiIAp^%eZS%8r zCD`0l?Qg;8?CUVeGAJ%IW)dDWWd8*EHecuc!hPZ@T~zB+t{HthgL|znqjvEa9T9B9 z7w_vW;^DwrM?e3?tvWOS6GMuQjwYFEZx&gYuzJwAJt`r)WeJ3Q-nnX81YE24tkG5+&!eOb2c<}J*> zedFB6$1`NJa!c> z_LdIs+{iUP@{;g+I$o$sBSK=STTXLMr835VT3KFvmTc9+yZJeFj*g*C$nZlAX2%jDQI^W-P<#!FY{>tjJQ%naWbE|+IIWtcRIAWApgABYLi ze0Zz`BbNcE<`x9@E@K9itQXPPDxN6;SZh?VFb!juAR8r@vsEqq3OV&f8kX>=_4KRJ+09b3>7_j`n;jJ>ZSRuXKUTcaOiuU$F zAP99VatJVeMzYYiEGK2mu`SdyIWh}7*P#080m{9aYS+Y-M|VEkL^D(K zN}z7PY?WULf;Noin*pj$t^h6eB9OP?b5-^>`cq!t6y92;(kX(T0GjMO`tty+Ph5CI zzN}u`1P`yMc4=6ID<-}=6|>>tNy_c0_^@k<(qGxGk0}eq$ugm5Wo#0MTEe7Z&g}Q*t2DKp#|q)CV<3*&Y<{sE zPWR<6L~hFwB{8|8TTX_`qe7vN9dd9NZ`3cf%A0ZR0mVL4F&P#&g`dUG$IM+EFtfL< z8f&I@KHb&!G1aX_qEnZdb;PX}8p?6O!JfrYd-NyXIF+oNGbBhcYO_b!62Ob$LJ&i5 zFur5 zJ6t|k+3Tt-`ZvGN_VW@%_cPBQ{uZZVAUbCvy>uRl@}*~r+0-?2HRrlp6heKM$D?%% zL$2Rq)M$A-W=|scWo#=;Fd__zbRF2R9s?#o=TZ(TdRz(%R_h)zm^gsmTWMsoB9q$e znHv=99TRcf*pW}#B4(xvUJZ>-jg6#BVD{xg*tEUD9-|Ux@EZ%DV{R1i3|4M2j2<0P zvBrT{@VDye z6?Le&^@HJgsswl`DgY@>}(n zklPRn7^hAxgxn`+&VmFqV=m6)k!*>zd2@+#h(?2G!4FSsyP9#JeqH(GV98-htdTjK z#JfcPO?PCck*+-F2Xm!3f{A5n@UoQ?9!pX-%!aGQxlJXFR+vbUq?%6Z>ToOs!G#Nf z5k++J;>DL&!1wzTxaa-`kifIq^;^uh0|I2c$Q|>6`;JJOvVu+q zWZPRQ2?43)lG=_59ZJ8K^{8W_NMwbmP-m?prZsEz02Lc9ekZS84`+tod!ULn$fXMl zR-!;rzDzL;j5~i!EVH2tLBfm1QL-D)pDAz5u#r3Sc(3g5Q114#ReB@YF1S58 zJTOVJ-P2V5=GqCrdK;9O0%SOt{?Y&V*zow4$QOz zh4+>DoZsMiL&Z9X}|Q+B&BXqnLSP+I7HE%Oq`zm$LuT+EOPa7exfN_h^zc8JxPpsNJj=nnL6CO zZKyc7zFdV;Jb92IO+F!9E;#eLa!By(zIxdOY1GWwC5pv@??@ChDyGaU6j${XGARdX z1oznIa#=8~fhKPDgUGv_i;q|F4T87me&L=4B4;kc|B$Z(T@pO6_XOQ)mbBbHxQ|BB z=Om;(-+mE4`$#gS{FCYioG1@I( zCE?UlXAf2Bn};_sY+XJGOL5k?!ev;=Cr%fkOegs`Ngrh##e{7 zr?%`9IF04wz>=l-{@slNp;?gI9RajX(>4^%L&2_itWC`TK}K{i4Vwkb^D&ipF0~)4 zPnW}hg%uy3?9Rv;`Y3Ch_izRIJ8qo!IH&Ye(FfR&TZXvwJ_9PO{h z=kAH3XU3JFCEHDt?=9mjE>?7^#q1LNDALsW<>(dqs6Mf*NLuGidgbd4m981Pm z!F+9$)BlW+X>5u!`M9@}F>pi+n zlcLIW7tzDn*@0Bn#oC|<%X7aR6gscT(xM<+*sT5v*7PwHsHxYaHrVu}+|DvBivRa7 z?dfA<(l+R{{rK+K=v#Gmi{7T*R?j{Zvnr-i@WVKKy1y^wBn_3vePa-2kce6 zu4cW(<;@c)x4qcvoHVpuupnsb8nEb06PIJMbGi)5xaz8H7QR%t2uA|=nCn0ydhFKA50AEQm}>bUWn%FY56H+YP3y0R zeYZawamCj|hn4JQ7~xU?zs?0v6TCp_0T-fkOv~7x1+%vwQ4*+1iqx2UuHLbAUoNWR zsWJkYeH<59EoM!yF|Nguuj2XR1T)UCy(OWlN%_k>c~Id9lB3!urmLJgKA=O+>UM5fylZ!BoVr5=^2L@$Uq~X7**`4MlNj4yyPz> z=H)#~$34CiV`W@jK(v-2ZnEaf? zG1m4^15VxH5Xm562y!``wBF0f@uPKJaLT~RNIyTR&D-}}P|Mdct$+;J8i#9v!zpNc zIB0X}Gl@i!F)#u!(wIDIoXx~xny{E4r_QyV-3z;NwAA(Cvqra9mW?&_)kc&e?irV3 zQkVT9w5PZ5fo166FHyuzf|ut3J(Fk;PpuwS#qmyuI&zD85n#96kj;$0B8{GOlj+;U zJR@oJymiJVbGyq_<>3Q83P3WW#9~d;!NGf?i=wSzlag>h(!Wnq#V&>nvHG1O=!x+* zJ3S;3RXmR#tB*5PjL?}S&T3e=nJ3;dTP5_IF*^91A(mv?6Q+gp=#$<32Pf_r0#vNe zQCXN*S}VjvLGmqu36M6yvWwrA7kT-3!cd|L_Uj;^n?HSB1?Lg;fs(Quth6+zm|Jux zCMvc8nj<;Df!L@jA6*G%40Y9^+PT&ENK06^kd{B+izB03%9Ed%Px6#ybtRzb$cb|c za>|5n#@h+iWU465iFMoSk-75O;Ao`|>_k}<*G51WfRGhQhF74^IlxIna|mF{?2hU| zCR=Fc)$$>t)BVHTM47H9$Asnq#r=l;J7rw2y97dFn#1lhVB9BN`xo^|BTTGHg^S%LSQ;eeBv|w z%3FVtz;0pKfy#>BrwzA|of)JL_JK9Wm{P9y`Y3*hEH zn)+og>J*j_O3gU>25xA?hCI6l~$bA7BGe#`&%odWZmI*22ty*ZP{bOfc=@EB6K?z=3 zysSxFs%wWz4TgteL#^@i5+C<$`-ZX{!7*5gj7PElRx1ewXufc-U;AmZ< z1rxk7%f@CvK|mj>#`P;dCj`w3;NG^`us4J!2@KDN$0R$dv~yggfxg0oklXkK%N_Ca zWX)D~!#=)Z5fAH->-v8Qwy z_3>#T+`CW(%v*MDoNK+E6IaZq#bK1S!P>utziMMIgR?ZT+rRdk0;D@&I!G-IfEIN9 zrX|3MLb2p6q<<5ICi;TO*#nmaiL^z&h1grk++JI&l0Sx$U1hpW$Y6M*l7>II#Fsa z95llMnSSTES>q={2}=p8g-s6jUGu~ILgf%y90IioE7$z@hP4~^NvF;x&}z~V!w!9X z8#IcJe~RF27sTBsoI@yA4&QJ4UKdE@f-TsKonH}KA<`#4p2G%0-qia(%*&00{hn|q zEBM{E{8BffgIu9xZV=BtXpJ}nABeS&`kydB(IWtZt^l1o2a;YJFm}&)7(KGI{pTzC zAMRl~U?bd25jucKU%Sb>%yn*1HmrYS|&xT)7GyDt2rueXYlQp_VXWQU2XYvi?Vy2;AA_VvyOC_9ziTI z1-&!$>0pi0;1)sw=D&lOY?DZ4HC@z>#)90_X98jsYTG*dqeCpXBAv698z|}^Gj(hR zDjb#xb}j#O*8Ayc-eYZE#i{iz1_=tV-Te?iKO(4gMe4bMl6WGMUosPYrkKMoBIPCj z(S|hXlI{syMTEnNpXF9_B>95+4HuVUI@OfvW1T@MYxA+tu`Rqy#9!+g%VE@W;S{?> ze72VOXtjUj5RC7_VHa~*U@%vxz>_~)lw-hmh8chaKG?Al90fCr44lXZ2=^$V%5aK_ zC%K!=!FPbYTjD=n2RvenTHH~%VA})wHS(Lk0NaUOkN;KunemU78)7zVp9E{vD#1?w z=>`*|2YB8a*QpvL^-SJNEd366(N4fJE}6^^fP^of%@?7WcOb_FF8>*!5}fZeNuK+v z#ZJLae=}$8)c5ZS;-QsQa?r~3zeY>pN})S*P*MS>^NLW_fS@5 z-+2myrihvPjEkA%kF@5&P+ykoBv3+$Q%oH#e_nOZb{6mz0!k*wQw9%ZG@MD;3hQ2Z zb1zPZx)n7)S_^{~a6 zeNxe%YENP*iA&7xOv&H)$JVC4Y8x6dKF)3iTpe%Orw`Akxm;OrZ>BpOHX$qN9J4d% zSF@fWBl+E_xE@v`IQZ^uaJKq{OMlr_)}PG%{2L+r#zQ0J<}dGK=`Zi&|3b(Xu(fq^ zboxtdlGZo3QFPLGaQYw8hq~*63fwo+L^7ceiYXwt7&QLiw1J|8xwsirD^3rKz9I0MlZYWoZ9?RrXgGHOP$qR0EX?;NiHr)oWdtzCMiW6D}j8Ykh;*XN5V zfKHz*gMgdnu>Pc^TC5%aFdogg+8{A{O5FZLJTz{yu~wgQcPHW?R7qh#E6HAaAUXP$ zT9TdMaL1@vYa95NT7n&A=u2zchL?K|t*gJBaU~%oJ}St;NN1!Vnb;~E99sc;IyY%A zYE%^zT!Kk7(25ma*eg8IH+ zk&O)lrTsS3RlIZxu`=U)v&GtEI`S^d3>`b!J6Nf|9& z@uj*}hq!zfF(8i%FHWNC^oNwxF8yN==p{%ss+xw%EIW51_SMwZD`{HyuPKumsY&~Z z2Tk>6bIW4+_*{AN`}8=;GGoGyJ}U4@yGC-^snMa%VU}%^EUpjT^<-Hi{uqP zQyQ&<5#O$E&Gg6A`K+U@d+1@-o@FCEb@+#3M=q3GUtF^eRwfF$Bg^V&e&=$!n z;^q|j(nE(FvsuN6GYN?bMjIWHcUXr^)^t-J9g2091T}!=Y^SsG51xH#+Z}w;WiY9QQ_?B29l6 zKbIdNM zgjC-_-=bPKtk4i{mmo6*oWU|0e_6nQKn`#Tk4L;=`dYmZD)4>QKog+@1wE%CY7yBv zB=kpk5`vjlF$7@;kD4MxmZYaY$^ui?*@Kou&gIF!QeHUjw(-Kn5*Lhu zy78J4RmKeeJWt5dr=~$)RT%h!?iH1pI(94W|8YAtjg*23C3OR%K!d_A-Q6Vw>HpTn z4ezJ@`F=nOVaU^`g_WgK5I&sA>W7Zk%>Dxbm`)-#a^@9|XJ6`g$l{NaiBIR_1pgwP z@0^>$w9~H+v?`m#D@qy{(vlEAAw%%W$#(N9{tf=G?R(Nu+K^!g0DzdkZ3(jf+>-bw z8&ufM*wFdEkAo$thIu0XZQxf?tKZk7#nS5;A^?H~5*c3G1ue1^w?5@*uq+lwH6$-T zBdAlVQ1+V72R2U4bu^j_dgL@pZ=|A7VX)?rHlBI!tnkj)FxsM;6VoR8e1C3dus--W zcBZ*ktb9M*R{*%|?f`OO^cwPaYKy?&!0tk#`(&oz?}=}_ivrw0?`s2c5g(Xy5ffmgTfbYxKVN>1%3^V>~afRb7Y`7$bf#QMpv~{9_9+?*Gic6Dr9BnTHIh}*yLoR<6&52 z%|^qJdW43Fk$`y0QkW^lMrY<+iffeO=5&_ppSK~*Xj!au)|x_Mf}}c+G#VradRlt?LV*E9&~eXvnwsZm>VkdPjD=bTac1mxkpf0D@LW_ zUWg;RN_c}YE-UZ|zO=0+b}k4ok1v%(UlaG1=wId;$UIMFSaK4%V6!Y|=UB1t&+Z74 z>QkcL8lBG@79SwuE@@137GgDLnpB7EAWYhI6}V(CDS~o}?Dg6bNvG0WE-`KL>z@oX z`CWl%Wm!5SR+e^9UdDK3RlgIh6HdOi2S8GeRmE9o>U>cfNUf~m9%6A}4~c+n=|Ids z)0UX*$n~tgzyaERb*-h5#MqQ+VIlg+MLaL$$1ftK-G4u-qRFq)z#$Us@dk7+(kGQv zQ#=_b33dql%5s!nR%Q-p9+`^H5lg@5)Sm>#&n+2NQN~EjJ9@TlRjs$S0S@ez2E<*Y zZZj}Sv0m0{09iNslK=}S{VF4q8JVf2C88tNrKOSX>7x&L(qoOl^Il=D%PSV-(=g>4Nc`1N~h>s z%f+oUw&@YQN=YAKKU#W^!Obl`64G`paR)&LQ^*8{vNEe+eocf~aTp_WHyEkc8FXjp zMQ!h;>}u2aiOdanyL6XKr)C$;1DR{^INCs}5B64YKEWl)A|-tV=@Wt#>5%Vx%Saj- z0dgr_<<>Cy6_PPybMmlJ!d9l9u3(oLvmkf3gsPY;|0LcCKD}zsbn?p`bO7udl+kA_ zQY3~)od1#qDy+2DYBua$7FYBw*|^)q+%x^-d4Rm-`iw$ zcLB=8{#~V;tt)<8-1WVc1E=COz@K+t7VuNOPnQjM9_`m|4b*pV5BM!C=+9sek<)K9 z{kV(0hIVFbAGM688}6J1h4;ehq5+TPg$zw}0rI+KYefeZ%d!)#Jaa1ML;jU(k(rgU z{Qa_QNphLWPiu9CEQ|%mW)Ain602yKYdb3fkCSQ+ zE^7?aH$-8fyllPrGV>_R4+S5bQ$sw$Bcu_RDCQKOR)cq|0KW6aG!XU>Wn|M*pyCy_t zN|%Ce34i{QrXX+mK|pA6vP5q|E7keF%*39%{D}*i<_?+3gsHlw$MbbKFytf+6X^`h zggYcvH|>ExY1Z2d1&K}yvf9kxVFFtsZv+Y3G_qg$})hYWg9fBgCfnK(hSQ>_3U>_6JMzcs;7j z4>cth+Az{L$oT4b!ZkigNI99`z zS&|DjVm$2;Z1J~jiN{4B0tRtu&t$^6Lwkb-HcsjeNDj@+JmEQIsq|J#)vjp_WS!F= z6XpS#;>R7*D_s+lmB&7f_e(u8r|ZTpP-?_zC99Lam%MD2 zrDZWS-0^ez{#IJq6r=$Uhz>wtlHxew%zW_S(e-v4cV5-y;0iJ)B|&FcpGiS)X~N~& zwTxk2P{wW7LcR$hPe!lI1u+`jdM;D&56V4AoJAlQixl&N8#6hplrq6YLeeD%$b5ZN zK4h~S74OkwB6%wvFZUj8o2O8lM++q9z#%-sE-VOCvLqbpiltf+rWV;x60X4TQ@5j| zg*!qW;)j$-sy+Bqv*rryJk{Oy3iEp4ctMlTgHhm>l`#I!0*7K3?Uhp$?-OWnN9KNu zwk(Izybrn0dlqh}IhNcUPi-Ad-N_NqKoCtG`1&Vw*^1l)(jtIriK2b#%co=`^1ao~ zwrR7Rjq57h%u?L7qCk_tQ~lfe2lQXDP)nHJMgHHjk`!ov+@-i(yj|m@r_AaY>;PC8P`rXUGrTpuRR?NRFWZgHN3lL+b`W;-ZvlJBMCq5uk-*J zgDA+Hb}ivkZedzF6e%g>Yz6sZ{t>qhpf$G#Nj{wt*E&`E%&j9ao?mWN{wrmrv1-U} zU0j{ALzuTBptcI~SATXY4M?{M+`E-&Y!fCnls98s$=vw*IKSLdK)N)CpgKkSJe4bl zKa{9O)Inj()hOFGV?vNRcVb{mONYRfjp*=uNRICD+qf=A5^-ZnZx7_#e5Lx>kz)=9 zD0uv1%3slVs`nAy1o}vky(ETMxXShyUL$dHl9+NH4j!Po@pya4U~}R_bmJql?++&8 z=Ttvm%l&J_HLsH=R=!#VzkLQ`Y|CF!x~q0MeY{i=d}W7T?tt4q<%VKz4Uu{KWRX9m zh5&qMaty3w#_cvc2$>c+qT_h+qP}vUHd%e+`G?y&VA#2m=P;t&6u%P z%p7B6{xkEJi^gOMNm(^P_iC$%kf<@uF0c*G&Q1*`FG{TxZxCEu;C0gn^*LZd18e!A zC5?i*dfFc`zSR>rxeZ}eroG4FL(v!`-#)~~VJH|HgY@IjUnfdcQ?LMKYOSzOx>u9uPqvC!g4%Pae+HdBQgN@=w zlwfXRMq+Z);LE0QH{^*(2!JLOm}y+d@1jMYjU@C$v$VR4=+D@uV@98aBAK1@Vh2Y^ z5E<`+Vv74o-a);}7E=><(fyzb=3isRbfY+IK{a~k7Fx9zu|E#cNgXwiMCW)ctTd(O z21$12>;Nx4w`P*z3O6`BE>U_Us-|#U2`(tNCB!X`5L;yo{j&)3)on?A@))IvWU!h+ zbpHsORW6Aye>orXT6#gY5CX3YL%B;FHf6$i|s z6@JDXv8w{tylo6OWXn`O6G$5u^lRI!jcO}10_#hevjBUpf1Q1>VES6}U81L&7?E7yuFhW{%orkzN(y{t(_;VPhUQ&=lCGLJvynfRG3Ch*+{3eJ*>~LKW5KdpSgsA zTr3%bOe|_Gl0AGZ?=W9zYKJ>rGU~|&3_9%5ea?4=M8>DY72hUD#Nnm}E@s2OQZJg! z!o1p87Skj!?NsIq`rqi#+khJJhE?l}3aPCPJzr@ySXCfveM^(l@tBu#Ez>B&<1Pe* zpPA)J!dPji1g3) zOVmn8z?$hdqM*aBvAG${wvN_&Hi&4APd}y*Vw3LY1r(KoDvObeP!z6~7g*?5suhPm zz3<;eASnmCOn8R2jHEQqV5o`pK1A&Yabw?wE-akHnlGw@r=acMKFs4UNx z-J@aE_M&^jK{(W%;nEg8qLA#Qy_;p=SxCc?9*PWbB3!8RJdmv; zYqH>~>8ro2GJP+o^Rh$Pd%~4vqT|(*oH*#rI&s>404IivAixGWdPa$69T2pDQqj!(BW_~0pareVG$EwbbopqKo zywVpnXTx!m#-hkZGptrpq;hV@6DLfYgDq$e;$_r6h>mv}x@9sWZfo`~voK5G7f-vK+_#ncQvc32Oo?(6o2Wh?~ETSn1j;vF&wYi!W+D4z{~%G zb`-}&(@^+HfaH3x$GPVkC`u3SHth;#Ukg#`6?_g_H<)4jfC_u?pyPOiIqx+&-PAC7 zrzPKc%nJ?^G%cK5exU*sRUo`rPC8)#lX@hFY&gDf;xor* zkHpWuzM|EzkA&7-#oxRSB?$pSvZ%(-Lid0~MVf#)aG{U?3v^LxdzZ3;wAcx=BD>ZD z0a$BkX5!4ujAlbQ8jD#M467Cuy9Qf&S+-c)U;2Im4I?0{*Qf<<%@!iq!FKNS8V!-?K)bVc3|HY zaws-HK)n)&cWAq~q0%#>aO48^f%A8K#1N%s)K9iQFYXR~IE2+;@0Eq*#d2Khh$ZPi zKS+)@vC!vJK+^2QI8Z?V^(63ZhQ(I%$ib-AdB`sm<1@)iclYf&e4LB6bDnZbH7wHSX(znr%@EH4O*m*0A_XGPPJ)St9@o{nzFb{dAcvZ zD$*qV0PYm}b@HNd%H4IsV=DHIs$sfkcEswD&K5QPPx_X}`LCg@iF@*AfCMaRH7c<-maJniwiMc%zI+w9c(T>u>o{ZB&N1ic}-5C%ww1|dY~zcB@24H#YJ++QdL z3mb))2zNsuHTw-=^KJ8NjpSl_p7O8z+c5m2<4lWIZBd5$w_9NG&HE6p`&i#0`Ot1` zQKCa$XE40|hV)&vb|ZE}DY7DVDNnKxZyLsZ$V3{ZM6R_!$%$Qca=k`txUASLmh)A1 zWX4!gRSd}@D2c6F%`l%R8$zWgsa`>nifz@c_SFqYx9l@PvUu1=7(VWQd--QHNQ~E2 z-Q^_Gxdkm#IvQ!wWlwsDOtQ|2^5o0WcixLlKQ5))d*?BXU$@M(o88%(DG=g;*29r! z;}!jmKMGLsS*LQgmOC~@Gn%G+4YCT~U>&P{$Ayk2PiE9g2{6uI)u3~i50`hrRhoX? zz^U(IG~hUtGqWGRf5bLW2zC`2wV%GG##BvAt5Em`{hG5!?`MS)PR6oCU7Io)snslE zLapRcS6Sr6S_C< zEPr_P2azwG>zXtT^`25bTgDu=i#ff6(48!MRB*9t&`PyPM$e*W^Q0QM%^D;L>;BZS`eyFTybrVT^0K^^E|MxC;~j7 zgO1Lg3rlN~c{aDR~bxj1zMD{=yaW2ASLp{r{TT>M=G&d-oJB+r69*=Gk( z{Ie7!KBMy^J$l{MB03o{Y_j=>sJRWyaS>aUA#!%~o?njds7I--Ad`YBxdcrl-JDy6 zJH^jZLr2d7LtFg^zSM07lz8#dkt|AT^@d1L_THm7W++u%wm5zh?nOK4Ap2RpNktIC zb2MG1Hi2<p*rZE)_+NDlWCr<5b@$9RAZaSaD zKv-bcT>*3HeuhLI9=2J;!>P{rML<_kh>PZ3xVRCsUGr0E`+JRj1#Qr~-Q;%Z=LXeQ zo)-4R^R6tsGltSF+IvJ`4-npAXq(CumiBZg>pK5}ma4ib3SaN|wgGXPh2zwG@ZKj< zjXx#0MlyZ*2h#Lmyfp<*1ExkD`2J(dCdpm3S=%1#02U^ypYX1vq$Ubs1dms6*3`-- zxgAb-P1DM)Pgz69J~8P@tMEX0_{cHj%WHXXWo>0G98G9>Gev_BB(cp6oTl^=Ge7~# z3S5HP7$$?4&S~dn8ygYqAf*dyj~S6 z|6x9+-UAOE{9063G0II(2QH!co$tzs5rp-jf{SRZs{Ps0jh*tRQiHUCH5|pE!C40jq@yq!-Ju&W?+~14N_o{QgpKpj41+hEuT+Qu zNblfzE3;QP@95~8>2>(%Ap{}j9Vxd&|6*~6$H4Gx&-Q+j&zEOf?~3<+g3#L6kw?u6 zQZ!okbcZ4eE(bbXm%}Sr#_ty^{6K?O?uy)9lLC5nh~>gc{8Rmprc`qR04d@d5ReK8 z5D@$StmOQ+rc@Fs8v{K{Au~XMfSJD2|HYjoDrib#16Xa7#v2Qc<#vrttC|gNAr@z= zyPA^x$e@G`foS-i6jE`7GHokx@zUX65Ahqm{Dhw~oWIiB!}(s*zv3*UNrLU*8(Al$;~7 zVx?a8JoTN2$>JM;VYHhMhA4B-rtDNj9A{qY%kU|kx(-$ zQSrffNSFSB0!Qu@SwtSmogUra%d?0;MzgA(d~7s_cStM@*d~xJtRnR*bTf1*YaFFP z_SRgEefc77&r)!@JG>0z9@1pNB>z>PYdvyCl7YCw+5#lZ4T-4B(~V;c@|^Ne%kS#q z6Ma6YAuhBU%E#7Tm-ro8xqkGPnYH3Bd*_Bv@uw-bEucK}XQ?6eD!dIc!b`@{ITucg zC!MG!vD`hj%)NVnz`Zf(Q^XlO8g+20{P?`lJOVW#f9MY*V*_fm7yrnJBm?4n>jpeM zqYBhJY0oL4BZ`bq;wMXa&E9QyT`4hFPx9qXDBf0(^X*U`)fJlOi~daXcjPwU|E}r9 z8AxDb0`i-Z2tYuD|Fb3hcP3$=YN!v238uGkeLE8uEC(908bwSIoaH4EbX>zcNsRLv za}PC?wwzrZ*9!Ho^pW>s%CUjlO@IUuCfxhMx~18JNi5N{89SG zIg-ja-AmNd+vc7}_L0ZYSfWq14_LSJyP}anU=0Yz%sL&GrqLdSt@6H|)L>b*S;hb4(N zW0GglN|X(@NE0aoqCfN&%MkY~73eXE^Yu(^nMikW(^r!wDMQi^I9H8m6BUKU7*BBG zV;N%wcTKg7J)2NidA;>avBFeYsbItCd28 zM(oyu)GO8%3yC?GTv^Qa`ZKXN-=QYPtPP4RmW#5CxZSwgd#~A9uf~u;f zl97<4Ni2k3qb?SkjyX_*3BK6pjvT1$$Yd5Oa}!O%oTfWBT@JT{Yu@9*3S!99Q(|^8 z$Oz4J+0gQlkrM=^+bhQM4l*Gg2**~(E5#|0Fsl>wCUyvrSAlcg^JkvqFhYFW`?Epu4eO$&anjP#H@yqm?)VpwJ z$yIsK2<}ghjnTVIAL_eKAHEPhem8#VTf}x)=2WYQ#9%gaN6->!1!W(PI$2e~uszx; zx>IqdMC~sjL6*{AgV`+aU^c_g&>yoeY$F%2$q7rpsQ2JV<*NtS%`h)01u73WveaFC~pEhkjHBC&4916a@HM)HW$m zs+em@-mhw4hb~sDCr(Sec8o~nsZ(kovsT#3D^9PTR@bC3uqh6HxS`!b)2^LvD|}%u z4udKyi$a~1F)C@YF3ls=qpj)SA_yTwI}VsrIOuk@G;pRig8`4-tx9Mn%)XySd@t9U zJU#8qo>_#-myr76V8~bD5rNkIJUYsPMO2KZwJBA>%Urr>5vxdLHW%YLzd*xx5~**( zTZc87nQYllA8+W_C-MdFvZjzVrWq>fRM&}#(4VYBcf`|k$t1?X`=yR?y1}$Q{IuuX zwXqor#Hz~%&VjevJ{_#d@u$+f3qtS4YloehmqCZ`^x@m(O1PKh5W7R`(v-K?6LP_2 zR{gcpQr4j^uxw zCUQ%(Kzv`Pv6OLWUf!D5>@EXt@ZaF+lvL}S)k2tfuwOq)wV*3YK$s2?KY!9<-sw!q zGtMyJBZXkk(s3sH2&dGZNGzWXV8%G6%(0_){U#j>V=6OkF^1gxJy!Rd1-P)t68tEV zPYVkX`ZU@NdW|*xbY1039%D&>b7|~PAxd2@eUsrQ60#{mh3+8o=~r&nAR7wZIWS9^ zfM)944~6tqEO)i7!lqISyFG#98%5I#Z=|kkX|Q$A>F-$W^Iajc(^ynFclSP7k1EY* zH8jXAu%yTo1gkEX8(v_JnS;{)8Xr#T6`~E2Ca&{9GPi+1pW64Y`b78y*!@0Iyo^k~ zE_$fW`ozw$->=9d+FKciXAoO$v17OT0yd*S-E!l}fBJVPqJQ0TFX8*>X= za|<$OlLFDn?Qt7bD>w(%Em3&**EK^00nvy?mtSKT+s3>{#7#ZTMoZCM6=w^Ax@NQ^ zFohu=1Yh%xDt}YKJS;a#sZP>;+@awWjEaHBb%nw=tnkjdkRB%*=}H6j+)hxV7R#ww zN)`KZZAn+^B46)wCR!hJp2olYu1&B`*QV?G)6xDp$@sv?QkGe+oG|P9ssH6=a|eqb8zO?Nye7=+fuq4PaLp|GN` z--+-z+ow2+J+eGbbDIN}dccRB;gnT2LBxEO5!)1Bzzr-yB_b)Cyl7b!$vXIG9qdv9 zG!o&_(^o)gsIRO1-3wp;@GJHLr+?uA{0SVu^%q9`Ux0ENmw%D-X#Rs6ZVTX^(AxeV zvNj+>n39mDrEHR>laLw_Uyz<0*{7nK_%Sjr-3a!#PVqN41aTsxGJQwDV}k(~AR7s! z?__3aNMmngU}R?N__t@WgfPJO5x@eubSae9)n-ipF4Rn>zJP$mK&2#+C-Cx_%WclM z?3F*fr&88TZgYcS_Z1Wo0PpAy4YjB&v+|={c7uCo30(QEkEJRA`SMdI@dL0%^QVq#HXs< zs|hp5XcLesff1R*hfe?Ftc+i;`e5~ILA|T>vf@>3yG*U(nfMY0CF?R=;PQzC(+>;l(YEpq@!k*yWQ< zi3+E2{@z0U^#{pMf#WSCLdl6-7V&m0brDvT7N9qN859@ONC;i59}Q$f-_(S|&Nn2* z(x~$%E9JBD-b7T0+h1T}qtQdMP$Y;=0~PE7mNy}9uI8YB81lP8Rm^!4mndNz$xu<+ zWNy}Ux68@~1T+GM*T#hV-zmo zNvwdlcIaN=P9AZ=mzek|2Z*Q1G1wMxgeN$LNA_#vJKO_Js^>rU69oY%+!DaDdjg2Q z-2cAp{{6p7n>jd`S)0h({uK=K+nWF?<{gdxv)Ca~TXs$tW$0^)wXO2ZFo&Rv5j~-k zz#zoem&}ijL58_U*H0CpB9&!BaTaZhuH$A9`-4D7ERXo67hyY?F{_xy0b6n~iR^+y zcIqW_so_81VmSe*s0{nc{qiC4%%ltDRLChwCc=~xLJZggEZ_sHPH>V!3`6wy%kkN^ zYcm&c$?cr}k3S(dbeLNAj^X>XR_e+J$|imk>8vwE?xrc1+sRX63p{<0Mg2^o91SCc zeM0LKXu|(#9Zy(itW1&3Z`RVKy0&;x?73DDzf;%PHz93}t$+Yed8GRb0fl(+~e0!ciqlrVhyp{=2-(6SG=0@>8 zjmYstL`Nb9S=3%{j||PEo(LZ02CYy##~JZHrC`$M-XX* zD1XJv=VORoSuz^a_~Yi!AgL#3dMP{ucJF+HAcq#gGPY}N#biC>Iv%=+(?Lp-u67YyC2+&Tny zag+Qm4w+a`**Gy=|Z5geHbU9E*4kleFY!OT?)7;KPL7wJ5x#ENx?8#OoG&} z-?q3Qfu)=YS5_^uc(fPTthOUS`K}X=)oj&()O<7<>aZy=inK z#p?*GPcezIfM5!lvXh!3y?p~iwkNoYN`u7#^FVj~9C_>gIfNyQ}036)^8itXnGzGxmqI?>+8R=Q&-sbBz`f23K z8B!96NrV)HLe(ODhYj5p^s52Hsrp*U8S*!E#FAVs9|T(%Zr$$^hQwv7CdVrc9jV_>+}dB%Nbec;Yq}e z)Pg6dzhp;UZ3(m04B4y>=yq7S7TRbUPot6U#e*rXO6x?+vTS_ljCLeGiUAw+r?T!Mid+Wgq8(6VSy<*760FXhU^x_KH? z^$_AnBrIIQKukJ&dl`sp3t0aG!VG#e>OhE1USadWcWj+!nZ8q%hdEc5l82 zQ)2HxWzs5Fc9AptzHFgPjjL{;|A-d-*n0aP@3U-S!j1R>e?4=xhAHEYuc|lQeBO_^8 zw8lbG*d!?uh~sJz#3Q#aAKs>&86yhz*f%T1CCZ8n`BNYjVQHUxjr&UUK})}U`trbJ zrCY2XM-wN6Ovh`zPxLZ+x{3!{r_y57k`kSo-c6KK#z@!(z8~~&L*y{ha z#V5v2NPsY)1j@cL|cthB~o8!o@n8)um*GCq=6kQ(4{X@wP z$vHX*G*FVm7*shM#yNd}xCq=4#jNmf%vVIPtYzd#pD^<}V7ot=>Rv#22)3MXw*>hB1A)MtyJ%IUbMJ{iV?v__O)Ww&ZXYnl2S3L_akVoa9JA)y z=PsrAbg&M9GyBF%!{99J=A4&!|0YWR^;Y=MO}~a906smS)#87(14&u~1`+*h8~T?A^0z~H zL(Re!bj<72ffKZe>^Um>4_Pr*G7R^1U6U18|D# zT@G)Pmjho}KHq+FZ6?-&xm4wl66Sw5K$gNJRErS5y>-*E)WOlwDv}k)Krj&KMZ#R# zE`bGeVYm;Z?^63sw=*W?*etdCr+3YR#8Y|D-IFK6!^pDFixB`UxgBXX1dtN-dar_R zcm~&h{l40R=y;dwjedS+$LAy1!@x_pHo$bM>3xRsA$N15h{(Qu(!-42Hj#R}gMJ5o zl6)pDcT?)E1}M~W6#(Y&p|1t@VMsuHz)Espu2r?!sk5wr1I`AL=|%l{>>`q8IQjje zTCeFv?cg9Y)22zvtM`PnV>?;8Pw>yyYX0q0KwCJskTz1fD4On#Qhz;0XyLdWi)ylM zSc}(pa16p}h4n>hcUC7Y$%5ykM6cjRyGoV=tWZE(h24qefG>l-x%DVn4+~6`%j;W! zaa05N)1|)MWy#KbgZAfP7noG%96emK0CHin&Q%7UVw%i4CSBlK> zQ6e?D4wzIVvU|0nwm9n*C7A3U=I_jn zqOxexjeJa2Cjp1~0;@=DJD#ddy%lpyqy-venIG)_TU0GzY(HGl1feJO#d;IEoAPM4 zEZE_V60Y*nMWO^K2MSAa`b-Kd={R=(EtLYLg z;0xv=ZTT|CO7Yk;@?4=4GcS%(9i&l;7|p#yDL^`;gH@KR)zHCF<#s z6qfWiMxXhHEaN(1`qhwsbCT34S)sQ>PmgV^aht)nk33WkS$yY8$9i?eZWMd1I2S0q z*$(t|V$xY2ve*!d3{Q5zr+Um{>(b-Zq$VBfr=ULfJdm+~X0*YS6 zz^B5V-nUuH9WS%XoR=$iKgpW*y0~D}==uN?bj}x&3p^o8J>MeJJrs#Neel8=j({K& zIo7~i(>as^(>s*jnbT<$6`^vdAK5n~n?h%aF{b_8-_*IosBSP=!{S>cG6X7JaUyr2 z?vbR)N7Z;A_3^j)EnPw(Y7YwW`kR8eLn`Tr&mQ*_F|kRbyZAUG!-8wf;74r@jgH+a zuxKN*0-0^$RmXE~2L{b5Wbj3AqoHVRG6vF+VPgTrET-n=VLU`xeq`DBhvHiG4E|(S zw9efM7k{U&$8osV8#CA#D_{s)2i-kHb=f^ub8$&mEamtTW3PHOa{ACj2Q|L&$qidh z)d#AsF5R*_xdDeP&H;3k5&+==T!NFkFvs*+B5Qn@(xk(cGMq1C=MSk>fvYc$xD1-n z`LOuBk@~SmWn0dY%JnA>>#GLa!;2+;-i#9#{-f_ypiF^{w-G0>Dr!_W^~QIPbmJQ& z6;0vp1wZ3zi&yOQVtI$t51$u(bGjV=oH&PN?qE)dp;xg!<~(XEtjJh0d}9H>BK{7V z&n_o4D|Kkr0@Q}5JL!G!1!G&E#&0uELVsxq$>sL&r6Pu@O7UId*_t8Jd`Kh@74qSaUXbKJ4`x3U;b1B zL$P=M-HtOD&+6;o@=#@>G?2B@btLfm4Yj4MoJ!iPAa=(5Dfl-Hmp3Pj>ZRL=3(eO# zYI1teyZnKa)Bezkw!x};u#vj+XAnWfJM_G#r0N_^I~Y}g5z%u`-w8jl&oPX=psb7O zsQT1ox2k`CnQ;XT3Ecjj5BZmefMbDHI|1<7)&NmD+y6dB`Db*JsB9%WCx_x~y)+}w ziD9F74JHJOZDZt10E?8NkA_a4N_b;{IYE7*G3(r)y@Rk5{;OL||M@(cC~J+?p+;gy z&|`|{h-0etsiVQC%KHOct~)A%`OxtGRu$oplzJGkmcjsP3|U7)EjD)d4Mj&>ZSUF% zN*D?oS%=Bd3L|O9ijlk1x`KZdMx`{0XZB$BaD9++0Pw(mhIV zA^dkFfnqD`-eym%&Rtk0mNzs2^ypMJJxBuvr3C-%SgZa6#chG?3fS3IE{!`s z@e8-{1heS18W#ITeU-$#)toIet;^uLY1la+`)D4T@mTd5TobtoQ{`$InLlYQ{Rg(y z_PZkTCKbgFuG7JU0E6W~5VcvAP7?su3^&C-!(|XXK!6gl&C};Z39E4t^MRjz+Cdt>ZysX)r{4@H#1f1L#6Y!>lSMgE#ovAM~65{pGHNb0A?{ zB9N~hH)!@xD*5C0%;C6(s__g$yKgrzT%xz+ZM1|Jlg=fJ126^8T^`m#-2R@cVT<9Q z=nNFonV>z@+fd@`cN|&z5uU}z`g_vQt`l zCP`UL6veTsI0(L#x@J;{9CwA{aRIQ;7=is34bXa%YL1h2-*SXgNP2Nrz7M}Wn~gu8 zQR7W>^1DeXQr0D`pf?c36Gck!)yco|m#PgM{|$iu*9zI!Um)KBtPpE}AIprR9L8tq7hi9yF{Y6cWfAxCYA8( z`j?g%YBUwPx9`{X;8JfSHd|Xw2Tv+Ak^rgQ&f(_e+EYfC*X6|i$5rzc(7v4}KkObf zC;be6c?Nxa@BTnff}h#AkR3~y1+4wbUKZW}j^I0z%UD}G88GZA$lBtDQF!v0d#axP zfL&z9&TU@d5p+_jrn3a8HM**lX7#Sf>GmBg;UyOANTSI**p&J@tGz{*#VR=N08Fr2 z&`$n1uWW5pHbE@d9BZdAIFDCGEeF5HfXO0e@0d(%*clpSdE#u*CGTN+60OcYN=xIU zw&Jq@linhU<#{pJel+};p_S_TWdIS=P|Fk0aE{lz`i!@a z%NY|xlhHRQ}ncF^;Py;k`wReNb zU1nvsP;O*>OJh5piuZp+phWH?8gT&qD;4hFSpEM{y#Ez-{-@rnqUrD#A0+`}tX3Eq zwtokYz}MjWIvQ|7fgEJ>Pch#DalstnT4hnCSS|I#*|*LQn2!6(gF=J`#omH($Jc&A zlUMRr!BuZj6~mP}$)fns$*hH}4I7s~Jh%8hU$5A{$v0LwT=b*{oKdV&PP$y1$K9~T zf%iqORCq7++^J&0wb zc8e$ok|N@R9>|8}`^QP@Nz*LeqMhZ3R8iLZMa(8@0z(Np%*w_37RZl_e{f5!;TEV5 zi*PjA!u!bG1mrLDjl`KUPasI~RuOBkSmy0h$y)u$zz zl2ijnD$LX8B|^@OyXt;sE{m~2wwY=s&Q@GfOR%p)uGWRO=2fD>(j>Fpua`776r=^( zZOoHx3|k}5AZ^TN#v?1707Wo})-QkwV&kR6B4Rc|r%_-kXJPP*I&5Eh!-DW!uyZGEE(ghC5!hqB2jY zGoLY{3~VKa5J1sTEx$x$sV`5H{n$dRM}bQ;*{yME&+RA^V<9d2HfO=82+W>pPkzUT zO>$YZ;CWVx-PmY9plhrtU^AuUn4bd$?l|j`S)YGWQ_Yeqg}i9iS91wg+f)p}j;Hyd zstPGahpEv`(#5H#!QX4l)_mPhIsdCQ^yO|=#2Yl8u2j$4^G^X6 z16f1Ql5Jwoari~8=rf}xu7$ic=tsRjezMo4ejoy`u-V}k==Ti2ECjZ6@#z{hp=U94 zcaAJvaGieXEA^;8YxJ-YId6qiDF=Jn??ffJXeo?W>^lD%SL5`+Pt9s~kK%#;1%|BO z=3-Vm?bL~&Wjs=ol#O-kA<#Zmf;6Xn8e9k+8oab5?~AeEmt(+b`2!MHS(0?3phtJk z%#6ocUXUr=ucx9XCE(&@=BqA>Lq(aC2n`yC5Z)oCGCxTVF+QgNW^6I3q8-cm?ylW` z>y;wT&qz1F#Uj5;8gXLl=`K6N_5fsaw8}vmn)cOM-FuJ}*)8Ul(A-;;i%5&kC`(|J zTX1b%v4M}DnIZZ!v z1i7rS-yDs-M4DAa3d4f?2;_8ki9 z$CjUQcULaEj4ab8$k@VNdMQqzNARXt9}qhun>wpT7@OvSvIt0fR0fy(X)oPZg0-oq zy`A-AF!A)Vs*w)WKeY!rw8-5KZDaok(1DFsyFm@uhC3f=0cQ+>(KRae=r{+LG4<%k zG#wYFQ0<%(y=XW&_x^BZbBM5)=?cbi3lMvYh7(GMo^M~PekwflUT((McuuuCJ+i;s zkIUUZOkJNq8^OJflYEoHy8StlI{dvrVA6Un6|0;0&2`WV53HDOh22Xd{sg3o8CSdY zre@RLk$m05aMmHu4OJY9yrZS*)*M;i7;KGVv8qI-svDUOKYpPWSXts_Sz&WP6Wb(r z3+p!|7&B+Q$;|en ztT7&!&-afH*lomLo`y9ieFH_oaluwW=cP)s84QMH9#-JZNKc@GU6hF}nD<-)TX!-- zsRPFA2lD9_W>(kZijS2#6L|G($6hjkg!Tcp|bjbW{ zae%>RPpzjby!maTT(O*eo)r}Hha#{Ot?)bvn1`G9rOHoal7CPi41_iOyX1m)@>V_f zx7-lzP{C>P3!%>xe@q7VYTfKBCyslHVap#Vl0;nB^Z^BJoEl#AwQU42RWK-h21`e3 z-28MIC~T0V?ApUwhH^*&OhpacF@060N72!4yWkF^g?n+rO2!zC7uBPXCTb;h@1;FY z4m2i5&!MKl3?id^&0`@NCfox8M38;mAarMM3$0Pk7FQj0-f5y zh}v7=IAUPs&pY{E1=#~9Wz@p2UP@k?M4eZ8&JkEMScU^g*X(>*p;$fOGi2#m2BwA@!J~1 z&QrMKSJWQrjkmIC2bqjFOK9~@otn2ckf-3_nO#ThPlT@2{&ZK#V^2x$E*d{v@ z3*(hV>3n-bx5XyM{Nc>f@Y6U>wZ@0p?FJ3J*lEUcbhw2ojkJLH$X}uxM&c}C{8q7R3%N6B+)Hw>IV)o$N~i7rJ)GDsskQ5 zuW$^S%x=;ZoSz**5@R-~HX{1&C(l=0$;7zy>5dbDHpo0rM~x#N9|?j;9GPcpw5sIo z*nj%mUtWebM1UF@NSX)_?l!6acz(3}C5N0KsXVth7};A;3X|s_kMGE+auZ{uS^{}l z?%ZlFd2G&UTTqq^ohDXUp&E6f5~wlD5xbIe4gAB=ajfn4XA^Zvq6W{!FssG=O!^|& zLV4?)b#W{K9yVK&1$ld&6Bw6XlEi99Uo(4Wry*K#18L>{lZj|eU4Yhhurx3}WD_RN zYb%@(;B0q?XhdasI}U1%t5TNzFkD$`XcY%qn-}Qe=gva)qM^PWn5PT$ zmdDw2nZnNAy}AQx&zt-^IvNwdd*D3yiNDfzY4ontGj;6%1<`58o^evT&lHIs+rH$g8QKZnt$0LN7lS&!o#2Q1 z?x#9Mrs(e?%$vWR{EQkbQtd|x82AYE^1-5F^e)mv&QQGF{ERE=wh^IQjIy9GQJ$}Q z$Pyi#StXmgnI&N}NPi*4-`;%;^Cmn&Z z(bwkESgVAUR&+Tk$#!M0X8$@KqY05&iOY~7yhra6N+RT!c{Y{R1TJ_v6=4O34QHW3 z8p%HQX4{0>;YL2~zYI2~p%lu1GTkKWO+WlPk(V@6%S5C@ngVj_Pe(VC; zPXK9&kX8WOL2%+bmf5Y5DyI!_qTtpX3=&bK7NnTM^sQiy#ato(UJdX80URA>Cz^dh2qsKz3JLA_K=WPQcSoB|yffjrft=aWc>$Lb59`v)%!TkPMfBs=o#C z%eTq`J_2%D}C9CHGA`-qb<*={Casb^UsmZ?xzLs##`p-u^u36K2I@KED};dBgP9aNbZ@38&JgE1NY{6(@h6NOl8iT?Wmo!h z5eTH2Z)h)p6E&N6iZej4~DEY-bZX z(kQ<3>6=_TPL;e0XJ1&SgMLJF|BCxnz1r|y@3(Z3eAiEMbR2RfTHQ=RT0Y9A3e!%+ zgS(Wc6fDOSki0x`)+V9SD$+c>9Bkp=vXLSi6nGDHC6I_Bu|^MCTQS{?l5Yyyz^GgN zV8TQE^gtVe%pIVY*4suR3EG>fre4epHJ3J{P#RJhRR3RNR{@pPwsjHd?r!OjZs~53 zPNloMyGy#eq`SMjyIUGW8WjG|cfG%gzWeSO;~NLZaL>7W@3Z#WbLCG)rPC1I*xPeX ziCX7C_U)pm;)-`KgxnMx+{2Dtz4kD(zq^bsDZ+1LDEC(nT%wSzQ@;r42Kl<{yk}0& z)kSzqz2X#J*M6RIPkVE6yh-jhPmu@W(xxaW&^ja#o=qe`0&a8W@vFN^2p_YlD_~Ox z4cOFi{BG!aZEaz!r(+9vSps|`jr44OTH>ELOr}Oj$aM0e_>F;r2)gpT?#eo92f;$N z+j=1zN|i;7aV@|ZM{gDY^BnR~T#5AMmuC;;TPTI}^MYH{C;KVvYZvx;7N@jjKvxxN zylB`?rXMR}MJNJ}aqJ-$kP)HWghc@tgMB6C8dJ)bkqF!Hz%)wDRpwYnRV6rv+jPVQ z&*z8t(l8LhRo^((<|iE5ES>qSD1P?hTog^GqPfYS@bUCBuQrkMf1zV-C#igSV_@hy zHOKGo8)jT`*)BYMrLwnxTOzoZxHlTHM=~dQvrH0$JPQ_%bQbOxjzbynHt54n3(w_j zAO|^7z$>psUu_TZnXoHJbllRC`C!}6`iGj764&)JxKL{~d9ca~tDmqGTW~|OmyPJ~ z=so&PU^_cJ;KD4~d{Q02RV&umEUuflxCMTN3gpM9_`J@dCK!M6tA=}_W z=b`04%ML+yg&d++kJz|SJ+K0!aTAz&yC)8ulqNJ3v}X*Qlqf_6`Qg@qtl;vA3lf8= zQmr_^cnJb9!3h7}rav{|_l>%MmW>`DAe5fDjghU9z22XFk#gn!a)@PgrC!&Lti4g` z367&}%DvMj2ou-lCpPAvx_$9O7upLFxi^-2Wulp0$S8Vp$=!DV-} zVRw|v;cB;%(vXCQQvjgwsMogQ$C&z&2rcB@9Q@g3$x|uvv$Qae5{S?D!-W1Ka z5!8N(F&w@nT4n~l79aDe@z7bvMShaaw@~Vk}uwS58A+VBywa&G*^KAL?uH#Kl5G%m^vK3pehq93`2Pr>i+(r00(4bCs2Pk9quKv zh{0WsR=YN@XkG2Cu-sVI1ud_?txOm$qGSzHNt=cp7WvZMi?=2X_b;*rFO~~f-&@4s znQIj+w@Ncab}%D@8z!)UP$V{q>uDpafu+$me_5k{tDVl;U0zf8!hhw`nBG)4;^X{r zDDGTzBX`$TFnA7ll4b^G@Zp{qk`FiQU=}Q7rlJGw4nbMDCn(410kuFJk!bF<3jf*#F*~P=N(;A1>FEiFF5^r+70srm_uN)>@SIsQ!zxUA$T3s8rdc!)&7@f{|GWoE!mjbPKH7N$ z*4%+@1)X}YjjKADBD;)!+`VDGDEnJ(^gUO?viGY(SZ`BA4jpqN4w=p0pHLz;EcTfQ zo=Uhblef(oyB0_*L2TKn6SQ1z276urW4-;jMY=Etma6KMeZg|;Sf#vcom%$^l_R-% zrf(z*^2^irjaI)MQxvZ5ZNayq|&o$12hOLgEhPGV?k6oqkV=%I;f0%+7H z8YnQ}TM`NCB)NwP%XX1y5-iX)SdARDsuMN*5VBM+M)VC+F<}Q!yE8af@qDr5xV0>5 z4Fp}q#LIleiD-FT-VaPsNF;oWHN`RQnKYFR_K-;8wd^;+yMgS0GX+W=a$r>(@a)F> z7@s3s;z?TBwGqS^(+TN8KY}@lH3g;zU+-9A3C#d@qUkjrOuVH;eVWng>8p=C(zz(g zUps^X$KGMoUtS$3f6q5dD+z00j%+oT*g_9p$6=z%2o`>Ot2&A=GzC~wDO8cLMJ)h(SlV=1p9#vLQ9uzx ziqD3{)YbZqB!dK%8W_t*xtn34WFlCngFW#@h=0Ia=lpl0x%5@A(iizId9_u@h~@^U zNNa(*1Q&av)jh$a(fxR+H9!_6gQx?MUzlnX!E~|;Oqyn3=jV<58wsX${I+CoTb9j3 z7$TjLu;LUVOY1>0vil;Zfql_h<3koY z#AUhYiWsU&y>?W3DusT>I{TX1V3}T6q-x2LzXZ|6Bx;hf64#d%2%hUOz zd-9YA`ZiUlAyUblHl$M*QJH)hD4@KfCyCFgc83OS{Hv^APekcf@G5VUxDUT6q{iUx z;_LCVK-@d#=eG#86-t)vf((zq?9O_FfnkfjVm8j#IF&&=ZU(l(=fDsq^I3BS_4FvX zD=%(AD`cF_d>p=hD5I+x8Q=Le_cag#IE!N@rb!>E)h6d2IkbaO^U}I`>tsg2K7HP1 zX1EXEQDiUHTfI+st5h&NFVc$=3jWL09u}LW=4`ok?POo=m zUCei=V8hgi@-tr9!Fvp>yWDd7oT3Z7YInf+LcpW@smry0opy=~jHffg*tL7T45H2y z9Bz=s2Y;)K^f?i78;N^s>c)DF?2xxlqCRYuCw9GbkX;*TdLOL2cU#)B0q}I!Qc0Y= zeSFM+=NDKy_jLl?y`Gr zUPL4%!p|3L6A4k8G;rC9<^0%;X>Bo#^#)c}mwzBH*M-GSQvn-ztp!`IC7338NF3HZ8nIcSWe3U)Q+)2my4%NWk=y-!+&8pe zoA<+eO2YZzA+M~OEi;P8I6f$-@Hmgtc=$AaG{{67`0RUFO@JuNo}6>b*zQl8FA54>96l=hhN zEt?}s{jzy4R`H6}Wrk1V1g;{FaC{60>e?-?{?O>HvL#fxek7)I|6)tEW(_}pyc0Skt6--X_(!V2 zZI-wY_fnRE;ZgfwuKafC=gDLtY2p&(aYn2=MU^#i_%8C#+ADVK#nVtZc)S;Cg%*Ll$}q$AXy`+q_g_ zu9p`1Jo++Ex$ng@$hMnlli;i~zAqg2VLc(GxZGD%1MSd!&JZxq4)C;pB9Mu5{nGd&}gS=)dO7dZAugbikk=ps0G5e&*nZQYr-SIrWsqrR)`6 zFG4l1WiZvHEvrMPv0YbB-)5x@Oa3wfkua&<<1Jf6Vh4{5ve)T58)~UgcP=C99MBd# zu2Suj6xc7ZmFp->D`I}yeIYc{hWlt0sr1#SkS4~3TlRsifok&_Ajq&7ej5MDguY>- z%TR^KDJeXvF1}jxGgk@5a!6TtoFPS6j?F&z1x#|vNj`WWN)b46F-x?_gf#VGu6p@Y zvNOdgM#DIB(%?!9(et$y@5$; z_6O^s!HB7TJgiIk;1Z?%I?6Bw^I(OST>KH*4-j{i$7Sn}T^AS)z6FN_7h}({9e8$F zFXi~;7OJ)nvib8gIkMx0=dR^sp0C)n}?T;Djm=UI&3V9EHc zO$iv_ZIXRS>xATDIz!mGp2{Ir;b_=^SPR+M#N#+b2m{2YdA>=(#Z=RKczrd_gmCn% z!&d0^{`EHPNoG}13yU5ixjsb6$GI;zJFWaBG#y#cR>)X4=wv!6?ljYP156;CThrmngT9 z4-qN^*H=|p0UyDM)6^-` z!ruU`g)xP*OvraT(r8GdPoSwvD7_EzSTdrrlVjA7qOnC*5v@=ZRKezwIKDsv-EXQ6 za|bKDCKtqi1D={i7m)!Gkt>}h%2}U~r7mujCZe${%IWmtc++fpB-NJWG`Hvm=y*fK zh?XaOtpA|u;se!6FaFdqd(;EcJ(p;?xo(%zzRAt1 zSoFS?GjNN@dsBuyKE|#zzn3hjU`BF#hZn^AI0Bq9Rb?AS+lp3s zdAD5~Kk6$i$aWp6WImhP%%Y_3S_UIqS z@4n)7pV9)mRResYSL&^?1{H;i+10Pv7f3r)LM9B_&9NuT2DL8WvQ8r7iZt zpY0mMUT2+fK>orEY4y%UNQBq}AWhKz8R-tqa9N&rY9pPjyo))*F;TN)UYob{W;mCP zxWWdxrLcS|px*;_bxh1@5YI^f^Gxcl)(mlu^3(IA&uU+*s|%XrM?qa@ffeJKpMf4a z?>GAgqqJU4SSMtqj|S|&{9vU-ZZZwjg>=P6(c;V3%#sZ4Hv&~ID;gNuMn15uO{Wqx zJZt*0;b9ph-fmKYxB4Z)n`3v+yc;)Zj46@JclN%d@RBkZuiOq~+seu}*h;)nK9sA@ zw~9LjUu#W5&An?s9=kkrvkj8iGaw?^&M=JO3bEIO1j}6^-Y2P^3V!7+Gt*~eRs+64 zDDe`vIft=EH&r<^Vzy{U{3g+>b7-0NwOll?;dEmdS2ZHGDuU>V0dnmdkntsT$A%UP z88D}4&W;I9KuCArjktU{LPru{30_ik$RiP`c>bmZ(e)V!Xk;U7;iMD(B=NWv_!4P? ziwDqWS2n&A_Ym=GgudMT2@p-WNNA8x83g-0>y~H1)jl5O?@y8%;)!h@_ z1$l&#Zf;fXAek&DOk2ShSB1@wjzI4U#2Lks*bU9NJmMzLcYzetW@+j4&mCX%Y;on} zzN8%ry4#I6%mmk%mQiX?)NgSSyEWOSQLm~jj;PH;CuQQ2O_+8h#mid;8vXL|@)`S) zYR)cj zn}4PHihcD_gy-2E8>L5>U5@im0w3-D#XvDAHl9;k=Utp6Q6P^HLz9prnn z06pC~vcbdWWO4O{D?%qh@DruHkuLUOB7{X`vLQG31vLfbG?74Qy<5|(5`6M(QQJ#V z=Sx+)5pP7PrzQk8x*(H8Sw`Ghf$n?VRhNl#4QTCV!BK8t%|ZES@a1GG`eTQz_2?(!NBpl7TJ-P&3`dvw z(JwVm^InvT{ zww3piv4kxLY9O?tU3d0X`XTwvAEWf@H|HAEb~+=SbtS>oq(cZjcJKB;7ouIMj}AD_g*RhR(OtPsgC))U#$iwWHa!4B@-Qtf*7WR%3wgY>E0un1}V+8$X#zqrFl8#4SNpxISp zC>uX1n8bfa@Q(4cX1DF!Ic0TTOOBz}4wdz@a<7zsgU%&E*O66iy4Kmv3Lh(*lM-fL zqx41jIVH(0z3bl0;bW%OX30(2K0vq`dzj|%L7KoZwrS~#5Z{YZ{Gw-=zxJ{Gh$8AP zqo4c55RehPn4ID8zA1dLxhtP>ygaDS1)gBA;_P_e!FYln@PhEt3Hc@nf;iI99(zzE zM5AE##hW+yoW(HPB+J3{F>nHeLj~{Y{i_hS5KA)l$X!M58ZteE#r5Z}_kqeWfhEl5 z;K~u6<=Tc5`)!}sV`QERGn+&iy9x=f)*tcY;M=?*1A*I9Aw`Esn7a{}Qvz4ZVfr*?m!Wzrjfgf)N#gbskO33xd z@M1S?d*aI}GNFGI1?clBfWw4;)#v}}?th&jeD?y8JC^?D{X7L<8&jh(7*C$$t*}U= zN3ls3*o%ey;u$gw*dy$*a-69{@=DKM_6^8GtRTTeH~6Q_P=`D!{w0tbo847Tn-i|x z(cx1b9`|P-HWvs=Gh#?}@*??E{B0=YCldm4wFqHh^^6K9sq-wA(ljP5-*!FsXS+^@ zX{h0Ph*X1fNS@W-TQavv)M_^gsNIdK(r&V^AEZ+|;+jjQFrz0n))b)AoikM`KCQF& zeT+M0y^_b z3y)zqg@@63NQm7$I~jK$j{hW}gOhVv5983LA@}-X(5Z=L8EoR%A%hInD6in-*p~mR zuk|orXECH=dc`!Qr4wg!2E)dav2zWRv)D>h&M~a2TmyaC9U$y8GIXHgGOpQuL8j>Y zKadZ-OZj{Y2ZLM>MlMsUH5eVHy**_nXvX~kLz%wqMWh6t);e^aJO2{5u(-cZj6pRH z;aAk?M;8B4Q&-LnCIXWRtsa5X=`csSTa>Icv=VDtBRsxSu!wYEGR}7b!6PE;uu&qN znTb0MI^A%M>q*|psV~SF$LVlKc)LQAye`Z$J?kSo&6fAIgf|-#jSLc`iYDoYDb>1j z8lx~)PPo*Cuvm@!BJZGoJ%Ml}-IRX^i0X(14Ftsb`?UVIR?NRS1O;gEIbbQEJix(7 zG9-TV&SWMn5raVmhApWzqG1xBntnGRR1joDW$y`@h@x+)A1L_fb6UFN^7atgOkF}L z{VVPRoL#yXfo^%OO6R8f)q=sPg~xr0+s#(lTMuwcP##gXfF+_hl9V3Y)nd{55E+tU zqLKXcvk5Lp%wjR+zFq{Dvs;8#-Z<84@K3oQ@U>v&T)tMWJ!G8CP6V5TYmcJcb41oK z4>@@zS4cjrI1Abcaba15bWszwb}fnnMIYTr-ja$D=%B=Wj?*@FT}6VrO4FxTAH&e6 z&}4|!RtZBNRDBg&XDUZApPVPFAf+Z(qL=+f_JWAD$#f5#SbhYgOIeIdkz@J8Vp1k! zXuyj^w;kS~c+?h@vBkW+cu~8~TxXFQ)RJN}%sl5}6;L@76&z}eyHdr%L=biqZphl_ zi+S3rz9GmPWgI&G3v-9jwG-Btl*gnDlW5RVP#ES-<7}iMxJ^h>492yJ;bjyvg4^9G z3`)%8kknFQ&RKZo6gx?cZ_1Ji_1ND7x6EG{+H~6n6fltpR>0zg&cpN`AckS%`pBCZ zB;uz;?~U5yr*s{hZ8Xf5B-wV^O2pN-#X9qu5uu#H>aiBZ+VTH;()36D^Ja9~dBH1t4s^ID2J zVt!xEVR=3iXpdrK6nV_JGFlZx$fH3OX zAYDgUI}Ij_G0b}_&r{uLtN!Fu%u(AOsu$i)9A5d9YA9FObjPzNHMaZSsB}&`;5O-2-;C0#T(OR_;J$i2ZC6)yQPK8G|3|XG(!Zdtr>(x5xGHL)ZAzDgdac7v<+Xg ze~N(Uwcx5b9jBJ&T9LZ>nC|nH|2aHq!6j!WL0lSJRBVj-hdC1<;F@R<+u+t+M4||s z4%;bJWG_ajiHOKm2!#x~WBq2w_h{D-WKP#|=^@r_urMqHxLSc)UndcD{nN|&eHdYA zHyS7;A-p!ggwfpi*2XYGCTuStbTmzQdWc7w`QASFf+XBS#$)}YXtIBbUSY7^ahTD{ z8{<6>frt#<-5Jm=5d7&Fp;E**M5J6W5Dg*MB6Qr(5;flNkEtX z=K+^EDox^N8Ya755@=%9slC8r=Ibv4+LE&=KF4Mt?!JqqwoaaOWx7Jzf(1#x95#zq zDL7W)uiR1Tq~TxL@0I)JlmjuX5-rVfC|bfs*eb4b@%&RF}5q&<2!# zqkFt})d00XnR9q~=ng|Jv3MtvrV1a^+j)5ewVK12WhF#3j|pQ_n_bi;7K*5nd1ifc z29bUnj8G>|@0e|>TAe-rt^?9Jlf3b_41GJ73QZI56gA$MF>z_B$-gwRw2;GkO_xBM z5-*4+jUbr97vqPQq}~O%Y?qU)!F(n zp+HY2rMiaXDpT6Jt}DlIm3#I_2I_u(IZ8ZZN06vjSe;@{r4tNX6HHE?wveiwI*qSZ zq?u#R1iR!Y46h!0o&5E5T;k_G1jLVq`=10-t%A0w<;VI{iBznz-x0*xiWt4q@PT?9 zXf5j0FkxzOlblQ*828EY8hAPB;3&l$C) zWj+2Lr-zgtRn<`#(MTzp_*`T!Y~9X*>f?J-_7|KImEOf)!+_60%UbU*2biDq zE=m*tdsSHkt~!9*wS5I@ru#a$Hew?R6mx$*6cRl#Y|=DShezG9DtcYh$CKFzku%6I zTkukXVZ_{?@Omj~ajKI^LYwKMqr-_dc@7^>9==?D1^09+CVSrv3(HaY*@!+≺xP6>xgOOiY)rttk{qsA7{Wbuujxr^65$uRcM}1X8x7pQ*3r*Qf5N?{*Ha zA4~X=r>^-(9p4tcRD+z@dBmFf@nu(6fu&=;YiVbOX``Jn3(0fN68#wz8ONEt{?`K~ zR!yCLBwqkh_1!=Mr{abCuX~-G+^czt z_G(%B<|>~Z8MyXT3Nl{!Rfkt8kJAS-a+vGL_hf~WP!}mrR0K2o`@P-?Giar#rQW#R zQDhcngt>;6sNsZRB-?uR3Lh(B^;jHkv8G3E^gZDttwF&i-g6AnE+tORHO-a!9b8y@ zYSTGPFsGJ>^)OmTza^S;+9CP<+ymMC#BX`;WB`~cE}}ToOb%c^#)w@2(0v~BD+8gtTvEM z?O>9|-ZsR`9As{Zd8?jxmSBh2?d>QPBF7L;DaB{1Xn`~Y$V$-779q6#8;f5jelieI z7)*fIp20U`#P1XTPaa-Robyz)+B@b^DE` zVyu-bF%K;84?rF<^-`H2(fsIfsZLd=fMD9Y*N52cT%)+QxG6{}#B$K3u$gPn`KBFT zVkkD+FiIELcK9G&aAlmdfy#d za2&6Y(p}p_rwFbbHskr2kkbLs%k+# znb+{0T@npHGEMUFLb2W%3l27O`CIwrB=J5)I7{VjlWmB;9+%HQMJxVx{a0WD?c)K! zBhnS{Mewg=?D+NcEv)r~jjU~Kz=6Tk@5bR#k?gu(7rCqCUKu z5PU_v2+)Y{k%G)(Smx`bl&5BN=N3#0Ju-PRA3H~@ec}qP)C}%&AG3L~rfeK^AV|wQ ztn%KT3^f2Q%{Pptxm-P5o?6fX#s#^pc*UR^Twe_wNQOV zPNhmwE^H;m+^|les8j`$pB8Y5fR?^k#<}aQ2;0XM7Il5&WWK?qCaf+@t$E{V@gzGD z8ifI*!9=~9#uC-W1lF*qj3ETgiIe2G+B`M8rg3s+HwJQS|4fyILe(-8kmPe>%;SSV zX)JPl-lo7QCp3S)Df0P3y!~+%nAx&;)UOmMg6FjY9mPJZ^6kJ!_i);U^LCt zrRnx&HYxpqe@ZVW5p95SgUEbTh_v?*c?zb(=gV+Bo}pgy7AGjBIFWYZM&WKGO9b1v zWF^*y!6yajFQWe6gbRBP@gkRj3dkUXE1Oz}_10qo%y6%8Q~t*Kl0r)GSb}fpb`(+WdMBrZ>MexBeCWrmb5l zrJIWAA_HoQGZfS(t9hy)KK;Yh#kF&UKC975zGhI5haWAP%u&Z9c3?sx^yQY$u8X>rt9nns zblH1*gMD__G-}y{K9VzaP2LpE9*P4*98brW284KB(Dg#~beHL3+^$kzS);z-|2juU z192vP^Q`^?n4{T$pQGiRY;5(+{*6r`HEKw_ixrgqkNMrfItA6c;55B)tF z`WxEU`|e42Q<22Tq*MH>;!57o`0W8mWJU-DeBCN3jOSyIBPk8d9?h-K+Mk)m6TpWN znWAK>_>KUZqGkvYcnrQG9fQ8#|WNOq9cxOxbi90%T_lrW3Qw5!k-; zF&8XpWV{siLYazg(9c0uCC@+N{J%q(qLjxu@j?oH#&=Q-0K`fYU>zvhf+D zqHl$o0XZSI%xk@<_GD?xOr*7?0Ue>v;w&%(ykBOiLKSkG9H~Bn{Mw{6sD|F)faYuh z7>gKwZ_=NZ-S3XozilsL<<=}FU!y!oQ=mZGv@gpuA+zGpu^hNEVn`7uCA>F-)Q5Lz z;_YgTQL|a1x#PLr3?b#d0lxu!ahWaX`hXZsrr}?woVxC&EUkICKLA?-^$BAwu`tY! zW*Ki`+EY){FhL|LrCnsr`O3Fg@zZg3jFS}GbM514hTfOnk>7ELQRSMaX(19DH~ir#nmMo7jaj83RcM=`#2}d`sSP$EsJv46tI8$F zN$+4+^KF8MDVpk2F-UfVfy{EHCI#){bd+tFDKxK^$8~bD<{5ssdqP0e ztnB{Tx1?ueg*?vG#|0zA&@zwKQV-EvWuuMi`B!DS3kXL@hk0w|&%*ClzdqZ-rUEm4 z(65dj?5{|Z0ah*rCS~NK2cxWzf9#i3_j2 z9zZVeHe6)xD5+xP)J&gKZkXJQ+OU5_Y*QkxH>V_V`!h=V1#>!6S_V=+SJ+maWxO6H z1$Ti~Pc?hC|2;K+l_23g`mfzexEA7?3$WW5g#4rZ@%L`^pJS!}ve`I%GxZwbL0SzW z=b1QYH>b8<22C|6V!0!Q!pk@0%0d%wGrO_KA)~?0P+fu6o*US{PPF>68yc}Gz;+@A zg(8vMNw<|=QxE97V;;~RJnj0Yh~H=S%T%}+2mo;n$(PHfO$lh5`cURaqDfN`0??dxjJhlA9Pb1@SHq?XK9koRi0)3Gtg?0&W07yA zh2mP)@UQJQk)tP7$bP3^s~G$qW->I7LYRRT9STY%jO`AC4K85wLLZ(cLQKku7)Giw zj$W@z(juv_6jGF-da>CJl|ri1c_CRfdTlVWxp;>NbLw@Cdb9fE?vWEF%k6qx7>?)Y&fIuNQBb*z<8;S(g$L?4fRt- zQnl+| zjGLUjXxIF%7T8sUM93qIS#J);PHsQ0iFquZsq0h0Vqsju5jOHtWhPEoL6r8VZ8!d% z6Llelkam_Vj_4|t+}9AH!Uf_1#)hHXO^kJt%=n4Xthdu_L>X|S6r=(&`|m}|iYZiV z9!(2G+kwLhUu3rMm^Y{JamI}%swO+s=E*8iEuPB3q#dAYjx@7fJ}4b@lJNFU|V9yw%Ll$u(Vl85r=?m~s}bJ%zgbwOV=GW*C;8_015q8Y~rKENR= za>W)A;@LByON7~l+BhQo)f3DykkmVU{SH{>hU!55#_R6(A^p=SpE6uz9$~-zM12*w zRpQcdM-vWIwCKT_63a18{pPOc5xeRFbaj;;$UN0hYKGf{7bm2;2x~(}19o=<`5rlN zJ!D-(_63@Tq@3ZGoOeCLa5|;C0So+)^_D;{Ga}X#dO%A)u%q+O4;r`m(R)G*l94|j zx!61)9#F`ddqw0N*g3~brj7B;!?*{5tR;R=hs-rxeZKQ+yXS{-=Zh0n(om?*5wadkBNym<_#k^|Jy0bq4Tz z@jdysSG5-wU^n%XobulQf5%n&TQ~h_j(S%8W>EmEwk4qCg1-Ph{13pVdo;jq&C!X^ z&ejm1WNW1JL#FvDmmft_%iCAmtn(8S4#NFDU$*hp!aYZ?3#{t;c$!r;Hwg7%FO)gGV*umENLcFF1Qr`pRH5O(7a zweU;WxIY)4ZMAj<8!*I<0J8wW-++L3wO1SFP#00hnZ z1N8SUAmpg0WB31B=uc7Wg5Diw0eUSZpaLoXh6KE;y_f;h=^s%48Wi8Lzh(N*74bA? z?cdPVUigxK#Qk2a|84qt8YA!r-s77;;{DR}|1DzR)7p3%f9?khq{1Ir{&~iE8g}Lf zoR-G_FNNPH;6E;hKj-h8MeS*znIC}d0KoqicIGL{w^ZMTo>=R*y!{0G{Zotb|KKnCG}BMr5q}VDX8sF;pJ%B*m*A;0*bjo9oZkrkUM2pG8TV;Po;q**AaXDG zjp(=T`cK2{>4EqUWZ&Z7kbmz?e?kBGc>HN0o*qR0pmHetC#wIkmOedy`vE&w{!g&q zCyakMjeA;vr&jtOOxQKQF+Kf$_^IyxM}eMNj(^ac)c!{E6YTc_{q_2Xx$mh7@dv(8 u!@t1)?*_%E_4U*$@`GpzU>NuxHj>v8pnz|nZ?R(Nfe-*fa?~x~{`G$$1)d`S 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 029/189] 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 030/189] 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 031/189] 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 032/189] 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 033/189] 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 034/189] 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 035/189] 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 036/189] 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 037/189] 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 038/189] 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 039/189] 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 040/189] 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 041/189] 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 042/189] 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 043/189] 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 044/189] 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 045/189] 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 046/189] 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 047/189] 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 048/189] 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 049/189] 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 050/189] 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 051/189] 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 052/189] 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 053/189] 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 054/189] 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 055/189] 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 056/189] 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 057/189] 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 058/189] 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 059/189] 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 060/189] 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 061/189] 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 062/189] 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 063/189] 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 064/189] 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 065/189] 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 066/189] 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 067/189] 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 068/189] 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 069/189] 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 070/189] 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 071/189] 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 072/189] [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 073/189] 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 074/189] 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 075/189] 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 076/189] 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 077/189] 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 078/189] 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 079/189] 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 080/189] 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 081/189] 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 082/189] [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 083/189] [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 084/189] [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 085/189] [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 086/189] 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 087/189] 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 088/189] 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 089/189] 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 090/189] 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 091/189] [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 092/189] 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 093/189] 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 094/189] 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 095/189] 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 096/189] 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 097/189] 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 098/189] 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 099/189] 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 100/189] 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 101/189] [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 102/189] [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 103/189] 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 104/189] [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 105/189] [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 106/189] 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 107/189] 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 108/189] 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 109/189] 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 110/189] 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 111/189] 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 112/189] 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 113/189] 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 114/189] 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 115/189] 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 116/189] 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 117/189] 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 118/189] 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 119/189] 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 120/189] [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 121/189] 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 122/189] [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 123/189] 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 124/189] 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 125/189] [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 126/189] 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 127/189] 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 128/189] 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 129/189] [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 130/189] 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 131/189] [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 132/189] 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 133/189] 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 134/189] 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 135/189] 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 136/189] 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 137/189] 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 138/189] 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 139/189] 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 140/189] 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 141/189] 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 + // + /* + */ + + + +