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/README.md b/README.md index 2b6669c3..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: @@ -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] }) @@ -141,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/build.gradle b/build.gradle index fadfc0d7..f60aaea3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,70 +1,34 @@ -plugins { - id 'java' +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 + 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 + ] } -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).tap { + configureEach { + [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/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..c561d8e5 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +node_modules +.temp +.cache +docs/.vuepress/configs/modules.js +docs/.vuepress/dist/ +docs/*/modules/ diff --git a/docs/build.gradle b/docs/build.gradle new file mode 100644 index 00000000..72811d40 --- /dev/null +++ b/docs/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'java' +} + +group = 'com.annimon' +version = versions.project + +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') + doFirst { + ownlangExec.configure { + workingDir '../docs/src' + args '-f', 'docgen-md.own' + } + } + finalizedBy ownlangExec +} + +tasks.register('runDocsDev', Exec) { + group = "documentation" + 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" + 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/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 new file mode 100644 index 00000000..78845b53 --- /dev/null +++ b/docs/docs/.vuepress/config.js @@ -0,0 +1,63 @@ +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'; +import definePrismOwnLang from '../../../editors/prismjs/own-language.js' + +definePrismOwnLang(Prism) +const __dirname = getDirname(import.meta.url) + +export default defineUserConfig({ + base: "/docs/ownlang/", + locales: { + '/en/': { + lang: 'en-US', + title: 'OwnLang', + description: 'OwnLang documentation', + }, + '/ru/': { + lang: 'ru-RU', + title: 'OwnLang', + description: 'Документация OwnLang', + } + }, + + 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', + sidebar: sidebarConfig.en, + navbar: navbarConfig.en + }, + '/ru/': { + selectLanguageName: 'Русский', + sidebar: sidebarConfig.ru, + navbar: navbarConfig.ru + } + } + }), + + plugins: [ + prismjsPlugin({ + preloadLanguages: ['own', 'json'] + }), + registerComponentsPlugin({ + componentsDir: path.resolve(__dirname, './components'), + }), + searchPlugin({ + locales: { + '/en/': { placeholder: 'Search' }, + '/ru/': { placeholder: 'Поиск' }, + }, + }), + ], +}) 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..d60daba0 --- /dev/null +++ b/docs/docs/.vuepress/configs/pages.js @@ -0,0 +1,31 @@ +import modules from './modules' +export default { + '/': { + text: {'en': 'OwnLang', 'ru': 'OwnLang'}, + pages: [ + 'README.md', + 'links.md', + 'changelog.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: modules + } +} \ 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/.vuepress/styles/palette.scss b/docs/docs/.vuepress/styles/palette.scss new file mode 100644 index 00000000..c4f3dd38 --- /dev/null +++ b/docs/docs/.vuepress/styles/palette.scss @@ -0,0 +1,15 @@ +:root { + --c-brand: #f15d15; + --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; + --c-brand-light: #ff8e3d; + --c-badge-danger: #d94657; + --c-badge-danger-text: #160304; +} \ No newline at end of file diff --git a/docs/docs/README.md b/docs/docs/README.md new file mode 100644 index 00000000..1c14e3bd --- /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: © 2024 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..c7b5b116 --- /dev/null +++ b/docs/docs/en/basics/types.md @@ -0,0 +1,25 @@ +# Types + +OwnLang types are: + + * Number - numbers (integer, float) + * String - strings + * 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. + +```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/changelog.md b/docs/docs/en/changelog.md new file mode 100644 index 00000000..ba4145e7 --- /dev/null +++ b/docs/docs/en/changelog.md @@ -0,0 +1,91 @@ +# 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` +- 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/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..0d0f55ca --- /dev/null +++ b/docs/docs/ru/basics/types.md @@ -0,0 +1,25 @@ +# Типы + +В OwnLang есть такие типы: + + * Number - числа (охватывает как целые, так и вещественные числа) + * String - строки + * Array - массивы + * Map - объекты (ассоциативные массивы) + * Function - функции + * Class - классы + +Поскольку 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/changelog.md b/docs/docs/ru/changelog.md new file mode 100644 index 00000000..038b82db --- /dev/null +++ b/docs/docs/ru/changelog.md @@ -0,0 +1,93 @@ +# История изменений + +## 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` +- Добавлены функции `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 +- Добавлены примеры и помощь + 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/modules.yml b/docs/modules.yml deleted file mode 100644 index d9fdbc77..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/package.json b/docs/package.json new file mode 100644 index 00000000..4d455a4b --- /dev/null +++ b/docs/package.json @@ -0,0 +1,27 @@ +{ + "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": { + "@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", + "vuepress": "2.0.0-rc.0" + } +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 00000000..d659b8ee --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,1925 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@vuepress/client': + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 + '@vuepress/plugin-prismjs': + 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/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 + prismjs: + specifier: ^1.29.0 + version: 1.29.0 + vue: + specifier: ^3.3.8 + version: 3.3.8 + vuepress: + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0(@vuepress/client@2.0.0-rc.0)(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.19.7: + resolution: {integrity: sha512-YEDcw5IT7hW3sFKZBkCAQaOCJQLONVcD4bOyTXMZz5fr66pTHnAet46XAtbXAkJRfIn2YVhdC6R9g4xa27jQ1w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.19.7: + resolution: {integrity: sha512-YGSPnndkcLo4PmVl2tKatEn+0mlVMr3yEpOOT0BeMria87PhvoJb5dg5f5Ft9fbCVgtAz4pWMzZVgSEGpDAlww==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.19.7: + resolution: {integrity: sha512-jhINx8DEjz68cChFvM72YzrqfwJuFbfvSxZAk4bebpngGfNNRm+zRl4rtT9oAX6N9b6gBcFaJHFew5Blf6CvUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.19.7: + resolution: {integrity: sha512-dr81gbmWN//3ZnBIm6YNCl4p3pjnabg1/ZVOgz2fJoUO1a3mq9WQ/1iuEluMs7mCL+Zwv7AY5e3g1hjXqQZ9Iw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.19.7: + resolution: {integrity: sha512-Lc0q5HouGlzQEwLkgEKnWcSazqr9l9OdV2HhVasWJzLKeOt0PLhHaUHuzb8s/UIya38DJDoUm74GToZ6Wc7NGQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.19.7: + resolution: {integrity: sha512-+y2YsUr0CxDFF7GWiegWjGtTUF6gac2zFasfFkRJPkMAuMy9O7+2EH550VlqVdpEEchWMynkdhC9ZjtnMiHImQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.19.7: + resolution: {integrity: sha512-CdXOxIbIzPJmJhrpmJTLx+o35NoiKBIgOvmvT+jeSadYiWJn0vFKsl+0bSG/5lwjNHoIDEyMYc/GAPR9jxusTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.19.7: + resolution: {integrity: sha512-inHqdOVCkUhHNvuQPT1oCB7cWz9qQ/Cz46xmVe0b7UXcuIJU3166aqSunsqkgSGMtUCWOZw3+KMwI6otINuC9g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.19.7: + resolution: {integrity: sha512-Y+SCmWxsJOdQtjcBxoacn/pGW9HDZpwsoof0ttL+2vGcHokFlfqV666JpfLCSP2xLxFpF1lj7T3Ox3sr95YXww==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.19.7: + resolution: {integrity: sha512-2BbiL7nLS5ZO96bxTQkdO0euGZIUQEUXMTrqLxKUmk/Y5pmrWU84f+CMJpM8+EHaBPfFSPnomEaQiG/+Gmh61g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.19.7: + resolution: {integrity: sha512-BVFQla72KXv3yyTFCQXF7MORvpTo4uTA8FVFgmwVrqbB/4DsBFWilUm1i2Oq6zN36DOZKSVUTb16jbjedhfSHw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.19.7: + resolution: {integrity: sha512-DzAYckIaK+pS31Q/rGpvUKu7M+5/t+jI+cdleDgUwbU7KdG2eC3SUbZHlo6Q4P1CfVKZ1lUERRFP8+q0ob9i2w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.19.7: + resolution: {integrity: sha512-JQ1p0SmUteNdUaaiRtyS59GkkfTW0Edo+e0O2sihnY4FoZLz5glpWUQEKMSzMhA430ctkylkS7+vn8ziuhUugQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.19.7: + resolution: {integrity: sha512-xGwVJ7eGhkprY/nB7L7MXysHduqjpzUl40+XoYDGC4UPLbnG+gsyS1wQPJ9lFPcxYAaDXbdRXd1ACs9AE9lxuw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.19.7: + resolution: {integrity: sha512-U8Rhki5PVU0L0nvk+E8FjkV8r4Lh4hVEb9duR6Zl21eIEYEwXz8RScj4LZWA2i3V70V4UHVgiqMpszXvG0Yqhg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.19.7: + resolution: {integrity: sha512-ZYZopyLhm4mcoZXjFt25itRlocKlcazDVkB4AhioiL9hOWhDldU9n38g62fhOI4Pth6vp+Mrd5rFKxD0/S+7aQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.19.7: + resolution: {integrity: sha512-/yfjlsYmT1O3cum3J6cmGG16Fd5tqKMcg5D+sBYLaOQExheAJhqr8xOAEIuLo8JYkevmjM5zFD9rVs3VBcsjtQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.19.7: + resolution: {integrity: sha512-MYDFyV0EW1cTP46IgUJ38OnEY5TaXxjoDmwiTXPjezahQgZd+j3T55Ht8/Q9YXBM0+T9HJygrSRGV5QNF/YVDQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.19.7: + resolution: {integrity: sha512-JcPvgzf2NN/y6X3UUSqP6jSS06V0DZAV/8q0PjsZyGSXsIGcG110XsdmuWiHM+pno7/mJF6fjH5/vhUz/vA9fw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.19.7: + resolution: {integrity: sha512-ZA0KSYti5w5toax5FpmfcAgu3ZNJxYSRm0AW/Dao5up0YV1hDVof1NvwLomjEN+3/GMtaWDI+CIyJOMTRSTdMw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.19.7: + resolution: {integrity: sha512-CTOnijBKc5Jpk6/W9hQMMvJnsSYRYgveN6O75DTACCY18RA2nqka8dTZR+x/JqXCRiKk84+5+bRKXUSbbwsS0A==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.19.7: + resolution: {integrity: sha512-gRaP2sk6hc98N734luX4VpF318l3w+ofrtTu9j5L8EQXF+FzQKV6alCOHMVoJJHvVK/mGbwBXfOL1HETQu9IGQ==} + 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 + + /@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'} + 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.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 || ^5.0.0 + vue: ^3.2.25 + dependencies: + vite: 5.0.2 + 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-rc.0: + resolution: {integrity: sha512-rX8S8IYpqqlJfNPstS/joorpxXx/4WuE7+gDM31i2HUrxOKGZVzq8ZsRRRU2UdoTwHZSd3LpUS4sMtxE5xLK1A==} + dependencies: + '@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: 4.5.1 + vite: 5.0.2 + 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-rc.0: + resolution: {integrity: sha512-XWSIFO9iOR7N4O2lXIwS5vZuLjU9WU/aGAtmhMWEMxrdMx7TQaJbgrfpTUEbHMf+cPI1DXBbUbtmkqIvtfOV0w==} + hasBin: true + dependencies: + '@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.19.7 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@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-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: + - '@vue/composition-api' + - typescript + dev: true + + /@vuepress/core@2.0.0-rc.0: + resolution: {integrity: sha512-uoOaZP1MdxZYJIAJcRcmYKKeCIVnxZeOuLMOOB9CPuAKSalT1RvJ1lztw6RX3q9SPnlqtSZPQXDncPAZivw4pA==} + dependencies: + '@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' + - supports-color + - typescript + dev: true + + /@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 + '@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-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 + mdurl: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@vuepress/plugin-active-header-links@2.0.0-rc.0: + resolution: {integrity: sha512-UJdXLYNGL5Wjy5YGY8M2QgqT75bZ95EHebbqGi8twBdIJE9O+bM+dPJyYtAk2PIVqFORiw3Hj+PchsNSxdn9+g==} + dependencies: + '@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) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-back-to-top@2.0.0-rc.0: + resolution: {integrity: sha512-6GPfuzV5lkAnR00BxRUhqMXwMWt741alkq2R6bln4N8BneSOwEpX/7vi19MGf232aKdS/Va4pF5p0/nJ8Sed/g==} + dependencies: + '@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: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@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-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: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-external-link-icon@2.0.0-rc.0: + resolution: {integrity: sha512-o8bk0oIlj/BkKc02mq91XLDloq1VOz/8iNcRwKAeqBE6svXzdYiyoTGet0J/4iPuAetsCn75S57W6RioDJHMnQ==} + dependencies: + '@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' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-git@2.0.0-rc.0: + resolution: {integrity: sha512-r7UF77vZxaYeJQLygzodKv+15z3/dTLuGp4VcYO21W6BlJZvd4u9zqgiV7A//bZQvK4+3Hprylr0G3KgXqMewA==} + dependencies: + '@vuepress/core': 2.0.0-rc.0 + '@vuepress/utils': 2.0.0-rc.0 + execa: 8.0.1 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-medium-zoom@2.0.0-rc.0: + resolution: {integrity: sha512-peU1lYKsmKikIe/0pkJuHzD/k6xW2TuqdvKVhV4I//aOE1WxsREKJ4ACcldmoIsnysoDydAUqKT6xDPGyDsH2g==} + dependencies: + '@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' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-nprogress@2.0.0-rc.0: + resolution: {integrity: sha512-rI+eK0Pg1KiZE+7hGmDUeSbgdWCid8Vnw0hFKNmjinDzGVmx4m03M6qfvclsI0SryH+lR7itZGLaR4gbTlrz/w==} + dependencies: + '@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: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/plugin-palette@2.0.0-rc.0: + resolution: {integrity: sha512-wW70SCp3/K7s1lln5YQsBGTog2WXaQv5piva5zhXcQ47YGf4aAJpThDa5C/ot4HhkPOKn8Iz5s0ckxXZzW8DIg==} + 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-prismjs@2.0.0-rc.0: + resolution: {integrity: sha512-c5WRI7+FhVjdbymOKQ8F2KY/Bnv7aQtWScVk8vCMUimNi7v7Wff/A/i3KSFNz/tge3LxiAeH/Dc2WS/OnQXwCg==} + dependencies: + '@vuepress/core': 2.0.0-rc.0 + prismjs: 1.29.0 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@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-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: + '@vue/devtools-api': 6.5.1 + '@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' + - supports-color + - typescript + dev: true + + /@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-rc.0: + resolution: {integrity: sha512-I8Y08evDmMuD1jh3NftPpFFSlCWOizQDJLjN7EQwcg7jiAP4A7c2REo6nBN2EmP24Mi7UrRM+RnytHR5V+pElA==} + peerDependencies: + sass-loader: ^13.3.2 + peerDependenciesMeta: + sass-loader: + optional: true + dependencies: + '@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) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@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-rc.0 + 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.1(vue@3.3.8): + resolution: {integrity: sha512-Pc26IJbqgC9VG1u6VY/xrXXfxD33hnvxBnKrLlA2LJlyHII+BSrRoTPJgGYq7qZOu61itITFUnm6QbacwZ4H8Q==} + dependencies: + '@types/web-bluetooth': 0.0.20 + '@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.1: + resolution: {integrity: sha512-qhdwPI65Bgcj23e5lpGfQsxcy0bMjCAsUGoXkJ7DsoeDUdasbZ2DBa4dinFCOER3lF4gwUv+UD2AlA11zdzMFw==} + dev: true + + /@vueuse/shared@10.6.1(vue@3.3.8): + resolution: {integrity: sha512-TECVDTIedFlL0NUfHWncf3zF9Gc4VfdxfQc8JFwoVZQmxpONhLxFrlm0eHQeidHj4rdTPL3KXJa0TZCk1wnc5Q==} + 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.19.7: + resolution: {integrity: sha512-6brbTZVqxhqgbpqBR5MzErImcpA0SQdoKOkcWK/U30HtQxnokIpG3TX2r0IJqbFUzqLjhU/zC1S5ndgakObVCQ==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@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: + 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.1.0: + resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} + 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@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 + + /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@5.0.2: + resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + 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.19.7 + postcss: 8.4.31 + rollup: 4.5.1 + 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-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-rc.0 + vue: ^3.3.4 + dependencies: + '@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' + - '@vue/composition-api' + - less + - lightningcss + - sass + - sass-loader + - stylus + - sugarss + - supports-color + - terser + - ts-node + - typescript + dev: true + + /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-rc.0(@vuepress/client@2.0.0-rc.0)(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 diff --git a/docs/src/docgen-md.own b/docs/src/docgen-md.own new file mode 100644 index 00000000..53e61abe --- /dev/null +++ b/docs/src/docgen-md.own @@ -0,0 +1,200 @@ +use std, types, files, json, 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", + "collections", + "date", + "downloader", + "files", + "functional", + "http", + "okhttp", + "java", + "math", + "ounit", + "regex", + "robot", + "socket", + "types", + // formats + "base64", + "json", + "yaml", + "gzip", + "zip" + + // Desktop-only + "canvas", + "canvasfx", + "forms", + "jdbc", + "server", + + // Android-only + "android", + "canvas_android", + "forms_android", + "gps_android", + "imageprocessing_android" +] + +messages = { + "constants": {"en": "Constants", "ru": "Константы"}, + "functions": {"en": "Functions", "ru": "Функции"}, + "types": {"en": "Types", "ru": "Типы"}, + "example": {"en": "Example", "ru": "Пример"}, + "since": {"en": "Since", "ru": "Начиная с"}, + "elements": {"en": " elements", "ru": " элементов"} +} + +// 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 " + modulesPages) +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::: 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)); + } + } + 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, true) + } + writeDescription(f, info, lang, " — %s") + writeLine(f, "") + + writeExample(f, info, lang) + } +} + +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, "") + writeExample(f, info, lang) + } +} + +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, version, lang, isInline = false) { + if (isInline) { + writeText(f, "".sprintf(version)) + } else { + writeText(f, "".sprintf(messages.since[lang], version)) + } +} + +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 + 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 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..d47f0fec --- /dev/null +++ b/docs/src/main/java/com/annimon/ownlang/docs/ModuleInfo.java @@ -0,0 +1,84 @@ +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 String name() { + return name; + } + + 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("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()) { + 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..8c069911 --- /dev/null +++ b/docs/src/main/java/com/annimon/ownlang/docs/ModulesInfoCreator.java @@ -0,0 +1,78 @@ +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 Yaml yaml = new Yaml(options); + for (ModuleInfo moduleInfo : moduleInfos) { + 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())); + } + } + + 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/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/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/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..3ab6bba8 --- /dev/null +++ b/docs/src/modules/functional.yml @@ -0,0 +1,266 @@ +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 a map 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"}, + ] + 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 + + 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: 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` + desc_ru: создаёт stream из данных и возвращает `StreamValue` + - name: takewhile + args: 'data, predicate' + desc: 'takes elements while predicate function returns true' + 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: 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 + desc_ru: возвращает количество элементов 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..0fc448cb --- /dev/null +++ b/docs/src/modules/http.yml @@ -0,0 +1,94 @@ +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: 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" + 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/okhttp.yml b/docs/src/modules/okhttp.yml new file mode 100644 index 00000000..d53eaaa0 --- /dev/null +++ b/docs/src/modules/okhttp.yml @@ -0,0 +1,210 @@ +name: okhttp +since: 1.5.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/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/server.yml b/docs/src/modules/server.yml new file mode 100644 index 00000000..4cc394cc --- /dev/null +++ b/docs/src/modules/server.yml @@ -0,0 +1,278 @@ +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 = {}' + 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: 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 + 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: 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 + desc_ru: возвращает номер порта + - name: protocol + 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 + 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: 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 + 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 + example: |- + { + "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/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..92abeba2 --- /dev/null +++ b/docs/src/modules/std.yml @@ -0,0 +1,328 @@ +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=2.0.1_000000, VERSION_MAJOR=2, VERSION_MINOR=0, VERSION_PATCH=1}" + 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: 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`" + 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: 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" + 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 the input string into an integer with `radix` base + desc_ru: преобразует строку в целое число с указанным основанием + - name: parseLong + 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: |- + 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 diff --git a/editors/README.md b/editors/README.md new file mode 100644 index 00000000..9d41cb8e --- /dev/null +++ b/editors/README.md @@ -0,0 +1,15 @@ +# 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. + +## 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/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..af5ab474 --- /dev/null +++ b/editors/idea/filetypes/OwnLang.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + \ No newline at end of file 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 diff --git a/examples.own b/examples.own deleted file mode 100644 index ee107ddc..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/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 7a0ff506..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 { @@ -36,7 +35,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/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 a2f1cec5..efb4e310 100644 --- a/examples/canvas/fx_image.own +++ b/examples/canvas/fx_image.own @@ -1,6 +1,6 @@ -use "canvasfx" +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..fdcaddca 100644 --- a/examples/canvas/fx_image_negate.own +++ b/examples/canvas/fx_image_negate.own @@ -1,8 +1,7 @@ -use "std" -use "canvasfx" +use std, 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++ { 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/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/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/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 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/modules/canvasfx/build.gradle b/modules/canvasfx/build.gradle new file mode 100644 index 00000000..025d36d5 --- /dev/null +++ b/modules/canvasfx/build.gradle @@ -0,0 +1,24 @@ +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' +version = '1.0.0' + +javafx { + version = "17" + modules = [ 'javafx.controls', 'javafx.swing' ] +} + +dependencies { + compileOnlyApi 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/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 81% rename from 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..4f4ef655 100644 --- a/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.*; @@ -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; @@ -37,20 +39,18 @@ 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; + private static JFXPanel panel; - private static GraphicsContext graphics; private static Canvas canvas; private enum Events { @@ -76,7 +76,7 @@ private enum Events { private final EventType handler; - private Events(EventType handler) { + Events(EventType handler) { this.handler = handler; } @@ -85,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())) @@ -100,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())); - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("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())); } - Variables.define("KeyCode", keyCodes); + result.put("KeyCode", keyCodes); + return result; } @Override - public void init() { - initConstants(); - Functions.set("window", new CreateWindow()); - Functions.set("repaint", new Repaint()); - - Functions.set("BlendEffect", new BlendEffect()); - Functions.set("BloomEffect", new BloomEffect()); - Functions.set("BoxBlurEffect", new BoxBlurEffect()); - Functions.set("ColorAdjustEffect", new ColorAdjustEffect()); - Functions.set("ColorInputEffect", new ColorInputEffect()); - Functions.set("DropShadowEffect", new DropShadowEffect()); - Functions.set("GaussianBlurEffect", new GaussianBlurEffect()); - Functions.set("GlowEffect", new GlowEffect()); - Functions.set("InnerShadowEffect", new InnerShadowEffect()); - Functions.set("LightingEffect", new LightingEffect()); - Functions.set("MotionBlurEffect", new MotionBlurEffect()); - Functions.set("PerspectiveTransformEffect", new PerspectiveTransformEffect()); - Functions.set("ReflectionEffect", new ReflectionEffect()); - Functions.set("SepiaToneEffect", new SepiaToneEffect()); - Functions.set("ShadowEffect", new ShadowEffect()); - - Functions.set("addEventFilter", new addEventFilter()); - Functions.set("addEventHandler", new addEventHandler()); - Functions.set("createImage", new createImage()); + 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 { @@ -242,7 +246,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 +267,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 +279,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 +291,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 +345,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 +363,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 +377,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 +393,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 +401,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 +409,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 +438,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 +452,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 +500,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 +515,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 +524,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 +532,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 +546,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 +576,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 +601,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 +622,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 +704,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 +712,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 +768,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 +776,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 +792,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 +811,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 +831,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 +879,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 +895,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 +911,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 +930,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 +946,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 +970,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 +991,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 +1000,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 +1010,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)); @@ -1112,6 +1082,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/modules/main/build.gradle b/modules/main/build.gradle new file mode 100644 index 00000000..9bba8290 --- /dev/null +++ b/modules/main/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon.module' +version = versions.project + +dependencies { + compileOnlyApi project(":ownlang-core") + + 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:${versions.json}" + implementation "org.yaml:snakeyaml:${versions.snakeyaml}" + + 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/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 63% 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 index 328ffa93..15074a71 100644 --- a/src/main/java/com/annimon/ownlang/modules/base64/base64.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/base64/base64.java @@ -2,36 +2,39 @@ import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; -import java.io.UnsupportedEncodingException; +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() { - Variables.define("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(); - Functions.set("base64encode", this::base64encode); - Functions.set("base64decode", this::base64decode); - Functions.set("base64encodeToString", this::base64encodeToString); + public Map functions() { + return Map.of( + "base64encode", this::base64encode, + "base64decode", this::base64decode, + "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; @@ -45,17 +48,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/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 75% 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 index a6f0bc54..d4b5a6eb 100644 --- a/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/canvas/canvas.java @@ -12,17 +12,19 @@ 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; /** * * @author aNNiMON */ public final class canvas implements Module { - - private static JFrame frame; + private static CanvasPanel panel; private static Graphics2D graphics; private static BufferedImage img; @@ -30,36 +32,41 @@ public final class canvas implements Module { private static NumberValue lastKey; 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)); - } @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()); + 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); @@ -135,7 +142,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 +161,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 +174,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 +182,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 +190,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 +202,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 +211,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 +221,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/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 51% 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 index 3523b8d6..b49ff8e2 100644 --- a/src/main/java/com/annimon/ownlang/modules/collections/collections.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/collections/collections.java @@ -1,37 +1,30 @@ 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; -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(); - 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)); + 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) { @@ -60,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(); 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 90% 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 index 7f5e10da..95a18f65 100644 --- a/src/main/java/com/annimon/ownlang/modules/date/date.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/date/date.java @@ -1,14 +1,13 @@ 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; 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 +26,25 @@ public final class date implements Module { SECOND = new StringValue("second"), 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)); + @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(); - 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()); + 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() + ); } // @@ -152,7 +155,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 +216,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 +259,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; @@ -272,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); } } } @@ -280,7 +283,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 +303,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/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 85% 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 index 60110322..949fc335 100644 --- a/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; @@ -15,21 +9,30 @@ 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() { - Functions.set("getContentLength", this::getContentLength); - Functions.set("downloader", this::downloader); + public Map constants() { + return Collections.emptyMap(); } - private Value getContentLength(Value... args) { + @Override + public Map functions() { + return Map.of( + "getContentLength", this::getContentLength, + "downloader", this::downloader + ); + } + + private Value getContentLength(Value[] args) { Arguments.check(1, args.length); return NumberValue.of(getContentLength(args[0].asString())); } - private Value downloader(Value... args) { + 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/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 81% 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 index f65c9dd8..8cfd6f6e 100644 --- a/src/main/java/com/annimon/ownlang/modules/files/files.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/files/files.java @@ -16,7 +16,9 @@ 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.LinkedHashMap; import java.util.Map; /** @@ -27,77 +29,80 @@ public final class files implements Module { private static Map files; - public static void initConstants() { - Variables.define("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()); - Functions.set("fopen", new fopen()); - Functions.set("flush", new flush()); - Functions.set("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()); + 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 - 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()); + 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 - 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()); + 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 { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(2, args.length); final int fd1 = args[0].asInt(); @@ -118,17 +123,19 @@ public Value execute(Value... args) { private static class fopen implements Function { @Override - public Value execute(Value... args) { - Arguments.checkAtLeast(1, args.length); + public Value execute(Value[] args) { + 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); } } @@ -138,7 +145,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 +154,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(); @@ -159,11 +166,13 @@ 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(); + 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; } @@ -182,7 +191,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])); @@ -201,7 +210,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])) ); } @@ -573,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; 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 95% 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 index 5f909737..4d2fbf80 100644 --- a/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/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 95% 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 index 083ac4b6..b7917612 100644 --- a/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/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 96% 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 index b43f65b9..5d2edc66 100644 --- a/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/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 79% 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 index ca0c9b0e..b9dfbd60 100644 --- a/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 - 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)); + 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)); - Variables.define("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)); - Variables.define("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)); - Variables.define("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)); - Variables.define("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)); - Variables.define("WindowEvent", windowEvent); + result.put("WindowEvent", windowEvent); + return result; } @Override - public void init() { - initConstants(); + public Map functions() { + final var result = new LinkedHashMap(16); // 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); + 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 - 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); + 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/StreamValue.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java new file mode 100644 index 00000000..07defb53 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/StreamValue.java @@ -0,0 +1,123 @@ +package com.annimon.ownlang.modules.functional; + +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 { + + 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("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())); + } + + 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 new file mode 100644 index 00000000..d51bfe33 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional.java @@ -0,0 +1,38 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class functional implements Module { + + @Override + public Map constants() { + return Map.of("IDENTITY", new FunctionValue(args -> args[0])); + } + + @Override + 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()); + 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("tomap", new functional_toMap()); + + result.put("chain", new functional_chain()); + result.put("stream", new functional_stream()); + result.put("combine", new functional_combine()); + return result; + } +} 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 84% 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 index a4b685f8..602999b3 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.checkAtLeast(2, args.length); Value result = args[0]; 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 82% 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 index 33458de1..d37432bd 100644 --- 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 @@ -7,15 +7,15 @@ 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) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); 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/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 76% 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 index 254720fa..5bd469b2 100644 --- 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 @@ -9,20 +9,24 @@ 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) { + 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/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 63% 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 index 6752e805..0600b436 100644 --- 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 @@ -6,48 +6,43 @@ import java.util.List; import java.util.Map; -public final class functional_filter implements Function { - - private final boolean takeWhile; - - public functional_filter(boolean takeWhile) { - this.takeWhile = takeWhile; - } +final class functional_filter 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 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..1987c9b6 --- /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.*; + +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/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 72% 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 index 333e1333..e59302de 100644 --- 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 @@ -10,19 +10,24 @@ 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) { + 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..d25daa10 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEach.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import java.util.Map; + +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) { + return switch (container.type()) { + 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, Function consumer) { + if (consumer.getArgsCount() == 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, Function consumer) { + 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_forEachIndexed.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_forEachIndexed.java new file mode 100644 index 00000000..bd86eb4f --- /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.*; + +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/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..f90a2f1e --- /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; + +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/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 87% 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 index 50979548..5d43a146 100644 --- 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 @@ -10,10 +10,10 @@ 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) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final Value container = args[0]; @@ -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_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 new file mode 100644 index 00000000..b14b82b9 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java @@ -0,0 +1,50 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import java.util.Map; + +final class functional_reduce implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(3, args.length); + + 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) { + return reduceArray(identity, (ArrayValue) container, accumulator); + } + if (container.type() == Types.MAP) { + 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/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 79% 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 index 7ce988b2..2292aecf 100644 --- 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 @@ -8,11 +8,12 @@ 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 { +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"); @@ -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/functional/functional_stream.java b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java new file mode 100644 index 00000000..5ae744b0 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java @@ -0,0 +1,19 @@ +package com.annimon.ownlang.modules.functional; + +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; + +final class functional_stream implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.checkAtLeast(1, args.length); + + final Value value = args[0]; + 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"); + }; + } +} 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..33f72618 --- /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; + +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/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/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 81% 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 index 86523f68..df6aff46 100644 --- a/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/gzip/gzip.java @@ -1,27 +1,30 @@ 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; -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() { - Functions.set("gzip", this::gzipFile); - Functions.set("gzipBytes", this::gzipBytes); - Functions.set("ungzip", this::ungzipFile); - Functions.set("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) { @@ -43,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); } } @@ -61,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); } } @@ -83,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); } } @@ -100,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/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 65% 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/HttpFunctions.java index a247a41a..02c6887a 100644 --- a/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,7 +1,5 @@ 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; @@ -15,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"), @@ -28,77 +26,76 @@ public final class http_http implements Function { private final OkHttpClient client = new OkHttpClient(); - @Override - public Value execute(Value... args) { - String url, method; + 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", (FunctionValue) args[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, (FunctionValue) args[2]); + callback = ValueUtils.consumeFunction(args[2], 2); + } else { + requestParams = args[2]; } - return process(url, method, args[2], FunctionValue.EMPTY); - + break; + 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]); - + requestParams = args[2]; + callback = ValueUtils.consumeFunction(args[3], 3); + break; + 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]); - - 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); - } - - private Value process(String url, String method, FunctionValue function) { - return process(url, method, MapValue.EMPTY, function); - } - private Value process(String url, String method, Value params, FunctionValue function) { - return process(url, method, params, MapValue.EMPTY, function); - } - - private Value process(String url, String methodStr, Value requestParams, MapValue options, FunctionValue function) { - final String method = methodStr.toUpperCase(); - final Function callback = function.getValue(); 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) { @@ -106,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 new file mode 100644 index 00000000..5a203f59 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java @@ -0,0 +1,30 @@ +package com.annimon.ownlang.modules.http; + +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; + +/** + * + * @author aNNiMON + */ +public final class http implements Module { + + @Override + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + final var httpFunctions = new HttpFunctions(); + return Map.of( + "urlencode", new http_urlencode(), + "http", httpFunctions::http, + "httpSync", httpFunctions::httpSync, + "download", new http_download() + ); + } +} 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 93% 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 index 37de2115..35bcf697 100644 --- 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 @@ -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/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 94% 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 index c21c5f58..a62a9054 100644 --- 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 @@ -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/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 85% 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 index a64f19b1..55eca9d8 100644 --- a/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; @@ -7,9 +8,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 +20,53 @@ 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("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)); + 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(); - 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); + public Map functions() { + return Map.ofEntries( + entry("isNull", this::isNull), + entry("newClass", this::newClass), + entry("toObject", this::toObject), + entry("toValue", this::toValue) + ); } // @@ -243,7 +248,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); } } @@ -255,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; } @@ -305,7 +313,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"); } @@ -325,7 +333,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/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 87% 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 index e72cbdca..f2cef93a 100644 --- a/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,8 @@ 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.exceptions.OwnLangRuntimeException; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.modules.Module; import java.io.IOException; import java.math.BigDecimal; @@ -25,6 +17,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; @@ -34,39 +28,43 @@ */ 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)); + @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(); - Functions.set("getConnection", getConnectionFunction()); - Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); - Functions.set("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() { @@ -102,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); } }; } @@ -151,7 +149,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: @@ -164,11 +162,11 @@ private Value createStatement(Value... args) { throw new ArgumentsMismatchException("Wrong number of arguments"); } } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } - private Value prepareStatement(Value... args) { + private Value prepareStatement(Value[] args) { Arguments.checkRange(1, 4, args.length); try { final String sql = args[0].asString(); @@ -189,11 +187,11 @@ private Value prepareStatement(Value... args) { throw new ArgumentsMismatchException("Wrong number of arguments"); } } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } - private Value close(Value... args) { + private Value close(Value[] args) { try { if (connection != null) { connection.close(); @@ -287,13 +285,13 @@ private void init() { try { return new URL(args[1].asString()); } catch (IOException ioe) { - throw new RuntimeException(ioe); + throw new OwnLangRuntimeException(ioe); } })); } } - 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 { @@ -301,11 +299,11 @@ 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); } } - 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 { @@ -319,11 +317,11 @@ 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); } } - 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 +332,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 { @@ -348,11 +346,11 @@ 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); } } - 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 { @@ -366,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); } } } @@ -430,7 +428,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)); @@ -462,15 +460,15 @@ 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) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } } - private Value updateNull(Value... args) { + private Value updateNull(Value[] args) { Arguments.check(1, args.length); try { if (args[0].type() == Types.NUMBER) { @@ -479,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); } } } @@ -517,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); } } @@ -542,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); } }); } @@ -554,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); } }); } @@ -564,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); } }); } @@ -574,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); } }); } @@ -584,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); } }); } @@ -594,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); } }); } @@ -667,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); } }); } @@ -679,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); } }); } @@ -708,7 +706,7 @@ private static Value updateData(VoidColumnResultIntObject numberResult, V } return NumberValue.ONE; } catch (SQLException sqlex) { - throw new RuntimeException(sqlex); + throw new OwnLangRuntimeException(sqlex); } }); } @@ -731,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.java b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java new file mode 100644 index 00000000..97cce633 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.modules.json; + +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; + +/** + * + * @author aNNiMON + */ +public final class json implements Module { + + @Override + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + return Map.of( + "jsonencode", new json_encode(), + "jsondecode", new json_decode() + ); + } +} 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 77% 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 index c75e4c9c..d0f96ce9 100644 --- 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 @@ -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; @@ -10,14 +11,14 @@ 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(); 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/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 92% 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 index a7b4e649..53c7c8a3 100644 --- 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 @@ -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/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 59% 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 index ac3f42ea..e6b0e56a 100644 --- a/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,53 +17,57 @@ 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)); + @Override + public Map constants() { + return Map.of( + "PI", NumberValue.of(Math.PI), + "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)); + 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) { + private static Value abs(Value[] args) { Arguments.check(1, args.length); final Object raw = args[0].raw(); if (raw instanceof Double) { @@ -76,7 +82,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 +91,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 +100,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 +115,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 +130,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 +139,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/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 93% 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..95d30732 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,63 @@ -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.exceptions.OwnLangRuntimeException; +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) { + 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 OwnLangRuntimeException(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 84% 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..9b7af755 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,71 @@ -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; +import okhttp3.RequestBody; + +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(1, 2, args.length); + RequestBody requestBody = Values.getRequestBody(args[0], " at first argument"); + if (args.length == 1) { + builder.addPart(requestBody); + } else { + builder.addPart( + Values.getHeaders(args[1], " at second argument"), + requestBody); + } + 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 88% 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..495b4939 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,57 @@ -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.exceptions.OwnLangRuntimeException; +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 OwnLangRuntimeException(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 OwnLangRuntimeException(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 OwnLangRuntimeException(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 77% 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..c593d6d5 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.*; +import com.annimon.ownlang.modules.Module; +import okhttp3.MediaType; +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()); + + @Override + public Map constants() { + 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() + )); + }); + + + 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()); + + + MapValue okhttp = new MapValue(3); + okhttp.set("client", defaultClient); + okhttp.set("request", args -> new RequestBuilderValue()); + + return Map.of( + "RequestBody", requestBody, + "MultipartBody", multipartBody, + "okhttp", okhttp + ); + } + + @Override + public Map functions() { + return Collections.emptyMap(); + } +} 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 69% 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 index 1a129032..c087230f 100644 --- a/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/ounit/ounit.java @@ -1,17 +1,13 @@ 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.Collections; +import java.util.LinkedHashMap; import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; /** * @@ -19,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(); - 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()); + 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) { @@ -39,26 +38,26 @@ 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: " + 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) { + 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]); } } 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. " @@ -69,7 +68,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."); @@ -78,7 +77,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."); @@ -88,12 +87,14 @@ public Value execute(Value... args) { private static class runTests implements Function { @Override - public Value execute(Value... args) { - List tests = Functions.getFunctions().entrySet().stream() + 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())) - .collect(Collectors.toList()); - + .toList(); + int failures = 0; long summaryTime = 0; final StringBuilder result = new StringBuilder(); @@ -133,20 +134,13 @@ public OUnitAssertionException(String message) { super(message); } } - - private static class TestInfo { - String name; - boolean isPassed; - String failureDescription; - long elapsedTimeInMicros; - public TestInfo(String name, boolean isPassed, String failureDescription, long elapsedTimeInMicros) { - this.name = name; - this.isPassed = isPassed; - this.failureDescription = failureDescription; - this.elapsedTimeInMicros = elapsedTimeInMicros; - } - + private record TestInfo( + String name, + boolean isPassed, + String failureDescription, + long elapsedTimeInMicros + ) { public String info() { return String.format("%s [%s]\n%sElapsed: %s\n", name, 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 80% 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 index a7c01937..5ef96688 100644 --- a/src/main/java/com/annimon/ownlang/modules/regex/regex.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/regex/regex.java @@ -1,19 +1,14 @@ 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.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)); @@ -44,13 +39,12 @@ public static void initConstants() { return ArrayValue.of(pattern.split(args[1].asString(), limit)); }); map.set("compile", regex::compile); - Variables.define("Pattern", map); + return Map.of("Pattern", map); } @Override - public void init() { - initConstants(); - Functions.set("regex", regex::compile); + public Map functions() { + return Map.of("regex", regex::compile); } private static Value compile(Value[] args) { 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 54% 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 index 901c7bd2..b97603cb 100644 --- a/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() { - Variables.define("VK_DOWN", NumberValue.of(KeyEvent.VK_DOWN)); - Variables.define("VK_LEFT", NumberValue.of(KeyEvent.VK_LEFT)); - Variables.define("VK_RIGHT", NumberValue.of(KeyEvent.VK_RIGHT)); - Variables.define("VK_FIRE", NumberValue.of(KeyEvent.VK_ENTER)); - Variables.define("VK_ESCAPE", NumberValue.of(KeyEvent.VK_ESCAPE)); + @Override + 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)), - Variables.define("BUTTON1", NumberValue.of(InputEvent.BUTTON1_MASK)); - Variables.define("BUTTON2", NumberValue.of(InputEvent.BUTTON2_MASK)); - Variables.define("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) { - Functions.set("click", convertFunction(robot::click)); - Functions.set("delay", convertFunction(awtRobot::delay)); - Functions.set("setAutoDelay", convertFunction(awtRobot::setAutoDelay)); - Functions.set("keyPress", convertFunction(awtRobot::keyPress)); - Functions.set("keyRelease", convertFunction(awtRobot::keyRelease)); - Functions.set("mousePress", convertFunction(awtRobot::mousePress)); - Functions.set("mouseRelease", convertFunction(awtRobot::mouseRelease)); - Functions.set("mouseWheel", convertFunction(awtRobot::mouseWheel)); - Functions.set("mouseMove", (args) -> { - Arguments.check(2, args.length); - try { - awtRobot.mouseMove(args[0].asInt(), args[1].asInt()); - } catch (IllegalArgumentException iae) { } - return NumberValue.ZERO; - }); - Functions.set("typeText", (args) -> { - Arguments.check(1, args.length); - try { - typeText(args[0].asString()); - } catch (IllegalArgumentException iae) { } - return NumberValue.ZERO; - }); - Functions.set("toClipboard", new robot_toclipboard()); - Functions.set("fromClipboard", new robot_fromclipboard()); + 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()); } - Functions.set("execProcess", new robot_exec(robot_exec.Mode.EXEC)); - Functions.set("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() { @@ -79,10 +72,26 @@ 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; } } + + 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/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 62% 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 index 322dde16..d953dc87 100644 --- 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 @@ -18,30 +18,24 @@ public robot_exec(Mode mode) { } @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); try { final Process process; if (args.length > 1) { process = Runtime.getRuntime().exec(toStringArray(args)); - } else switch (args[0].type()) { - case Types.ARRAY: - final ArrayValue array = (ArrayValue) args[0]; - process = Runtime.getRuntime().exec(toStringArray(array.getCopyElements())); - break; - - default: - process = Runtime.getRuntime().exec(args[0].asString()); + } 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/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 93% 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 index 6e959d44..59260b7f 100644 --- 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 @@ -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/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 93% 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 index a1be3d16..0a31fc1e 100644 --- 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 @@ -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/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 75% 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 index 858bb4ad..78c5ca3f 100644 --- a/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,30 +16,32 @@ */ public final class socket implements Module { - public static void initConstants() { - Variables.define("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); - Variables.define("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); - Variables.define("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); - Variables.define("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); - Variables.define("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); - Variables.define("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); - Variables.define("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); - Variables.define("EVENT_PING", new StringValue(Socket.EVENT_PING)); - Variables.define("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); - Variables.define("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); - Variables.define("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); - Variables.define("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); - Variables.define("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); - Variables.define("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); + @Override + public 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(); - Functions.set("newSocket", socket::newSocket); + public Map functions() { + return Map.of("newSocket", socket::newSocket); } - private static Value newSocket(Value... args) { + private static Value newSocket(Value[] args) { Arguments.checkOrOr(1, 2, args.length); try { @@ -81,33 +85,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 +122,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 +136,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 +146,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 +156,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/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 87% 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 index b5692890..91b85987 100644 --- a/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; @@ -9,7 +10,7 @@ import com.annimon.ownlang.lib.ValueUtils; import java.io.UnsupportedEncodingException; -public final class ArrayFunctions { +final class ArrayFunctions { private ArrayFunctions() { } @@ -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/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 87% 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 index 743732ed..9bdbd248 100644 --- a/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java @@ -6,11 +6,11 @@ import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; -public final class NumberFunctions { +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/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 85% 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 index 774bd60b..c36ca1bf 100644 --- a/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; @@ -7,7 +8,7 @@ import com.annimon.ownlang.lib.Value; import java.io.UnsupportedEncodingException; -public final class StringFunctions { +final class StringFunctions { private StringFunctions() { } @@ -17,9 +18,14 @@ 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); } } + + 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); @@ -44,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) { @@ -53,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()); @@ -67,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 new file mode 100644 index 00000000..c3fafa0d --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std.java @@ -0,0 +1,94 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.Shared; +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; + +/** + * + * @author aNNiMON + */ +public final class std implements Module { + + @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)); + + return Map.of( + "OwnLang", ownlang, + "ARGS", ArrayValue.of(Shared.getOwnlangArgs()) + ); + } + + @Override + public Map functions() { + // 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", 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 + result.putAll(Map.ofEntries( + entry("toHexString", NumberFunctions::toHexString) + )); + + // String + result.putAll(Map.ofEntries( + 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("parseDouble", StringFunctions::parseDouble), + entry("stripMargin", StringFunctions::stripMargin) + )); + + // Arrays and map + result.putAll(Map.ofEntries( + 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) + )); + return Map.copyOf(result); + } +} 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 91% 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 index ac72c83f..78a48377 100644 --- 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 @@ -8,10 +8,10 @@ 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) { + 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/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 86% 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 index b54eb0c7..51eff703 100644 --- 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 @@ -8,10 +8,10 @@ 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) { + 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/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 89% 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 index 9a32de01..2bd2e477 100644 --- 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 @@ -11,10 +11,10 @@ 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) { + 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/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 94% 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 index f16f04ff..daf68707 100644 --- 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 @@ -7,10 +7,10 @@ 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) { + 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/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 89% 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 index a56ee5c7..1a7de755 100644 --- 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 @@ -11,10 +11,10 @@ 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) { + 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/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 82% 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 index e1b45a73..35b8ba14 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + 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 new file mode 100644 index 00000000..f2d3a81b --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.lib.*; + +final class std_default implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(2, args.length); + if (isEmpty(args[0])) { + return args[1]; + } + return args[0]; + } + + private boolean isEmpty(Value value) { + if (value == null || value.raw() == null) { + return true; + } + 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/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 78% 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 index c296e8ea..3c4e68b0 100644 --- 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 @@ -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) { + 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/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 84% 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 index cf905de5..6168042c 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + 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 new file mode 100644 index 00000000..033ac1c2 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_join.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; + +final class std_join implements Function { + + @Override + 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"); + } + + final ArrayValue array = (ArrayValue) args[0]; + 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/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 85% 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 index 54902092..ab60e3ac 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + 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 new file mode 100644 index 00000000..5e58b505 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java @@ -0,0 +1,25 @@ +package com.annimon.ownlang.modules.std; + +import com.annimon.ownlang.lib.*; + +final class std_length implements Function { + + @Override + public Value execute(Value[] args) { + Arguments.check(1, args.length); + + 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/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 89% 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 index 83839d7c..33c1b4de 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { return createArray(args, 0); } 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 92% 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 index 542b521c..0888abca 100644 --- 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 @@ -6,12 +6,12 @@ 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(); @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/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 86% 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 index fdad6822..1feb1b9d 100644 --- 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 @@ -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) { + 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) { @@ -128,7 +112,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 +129,11 @@ public Value next() { } @Override - public void remove() { } + public void remove() { + } }; } - return new Iterator() { + return new Iterator<>() { long value = from; @@ -165,7 +150,8 @@ public Value next() { } @Override - public void remove() { } + public void remove() { + } }; } @@ -197,8 +183,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/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 77% 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 index 082b7dbd..703f7890 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { try (Scanner sc = new Scanner(System.in)) { return new StringValue(sc.nextLine()); } 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 84% 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 index a52cf0bb..aee1dd5a 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final String input = args[0].asString(); 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 84% 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 index 4d6407e3..74e425dc 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final String input = args[0].asString(); 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 83% 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 index 060b8a1b..04ba3733 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(3, args.length); final String input = args[0].asString(); 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 83% 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 index 62566808..9d01de13 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(1, args.length); try { 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 74% 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 index 22608b4d..adb67285 100644 --- 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 @@ -10,26 +10,23 @@ 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) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); if (args[0].type() != Types.ARRAY) { throw new TypeException("Array expected in first argument"); } final Value[] elements = ((ArrayValue) args[0]).getCopyElements(); - + switch (args.length) { - case 1: - Arrays.sort(elements); - break; - case 2: + 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/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 85% 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 index 0c2c4688..73aa73f4 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String input = args[0].asString(); 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 88% 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 index 1a6dbcdd..944f640a 100644 --- 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 @@ -6,10 +6,10 @@ 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) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); final String format = args[0].asString(); 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 87% 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 index 8c65e58a..84f769e2 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.checkOrOr(2, 3, args.length); final String input = args[0].asString(); 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 84% 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 index 0cbef55c..04a4d31b 100644 --- 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 @@ -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; @@ -9,10 +10,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(1, args.length); final BlockingQueue queue = new LinkedBlockingQueue<>(2); @@ -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/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 58% 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 index e23bdef3..ca36114b 100644 --- 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 @@ -1,28 +1,22 @@ 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 { +final class std_thread implements Function { @Override - public Value execute(Value... args) { + public Value execute(Value[] args) { Arguments.checkAtLeast(1, args.length); Function body; 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/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 78% 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 index ef31d18e..d052cf82 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(String.valueOf((char) args[0].asInt())); } 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 77% 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 index a419719a..813f78d0 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().toLowerCase()); } 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 77% 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 index d6f102af..74c071c3 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().toUpperCase()); } 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 78% 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 index 2853142b..7ee1cbad 100644 --- 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 @@ -5,10 +5,10 @@ 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) { + public Value execute(Value[] args) { Arguments.check(1, args.length); return new StringValue(args[0].asString().trim()); } 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 90% 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 index fb3a4530..bf37ae44 100644 --- 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 @@ -2,10 +2,10 @@ import com.annimon.ownlang.lib.*; -public final class std_try implements Function { +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 new file mode 100644 index 00000000..5a845708 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/types/types.java @@ -0,0 +1,42 @@ +package com.annimon.ownlang.modules.types; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import java.util.Map; +import static java.util.Map.entry; + +/** + * + * @author aNNiMON + */ +public final class types implements Module { + + @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)), + entry("CLASS", NumberValue.of(Types.CLASS)) + ); + } + + @Override + 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 new file mode 100644 index 00000000..5a57cc67 --- /dev/null +++ b/modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java @@ -0,0 +1,26 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; +import java.util.Collections; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class yaml implements Module { + + @Override + public Map constants() { + return Collections.emptyMap(); + } + + @Override + public Map functions() { + return Map.of( + "yamlencode", new yaml_encode(), + "yamldecode", new yaml_decode() + ); + } +} 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 69% 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 index 472b7430..59529847 100644 --- 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 @@ -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; @@ -10,7 +11,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(); @@ -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); } } @@ -31,28 +32,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 +61,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/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 94% 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 index 5b30a22e..3005e9c7 100644 --- 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 @@ -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; @@ -11,7 +12,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]); @@ -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/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 92% 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 index 569979aa..5000d47e 100644 --- a/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; @@ -9,23 +10,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() { - 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); + 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) { @@ -93,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); } @@ -188,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); } @@ -256,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/modules/server/build.gradle b/modules/server/build.gradle new file mode 100644 index 00000000..b395a2b3 --- /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 = '1.0.0' + +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:${versions.junit}") + 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/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 new file mode 100644 index 00000000..81c6040b --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ContextValue.java @@ -0,0 +1,191 @@ +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.Map; +import java.util.function.Consumer; + +class ContextValue extends MapValue { + + private final Context ctx; + + public ContextValue(@NotNull Context ctx) { + 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("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("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)); + set("userAgent", Converters.voidToString(ctx::userAgent)); + } + + 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; + } + + private Value basicAuthCredentials(Value[] args) { + Arguments.check(0, args.length); + final var cred = ctx.basicAuthCredentials(); + return ArrayValue.of(new String[] { + cred.getUsername(), + cred.getPassword() + }); + } + + 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) { + Arguments.checkOrOr(1, 2, args.length); + String filePath = args[0].asString(); + if (args.length == 1) { + ctx.render(filePath); + } else { + MapValue map = ValueUtils.consumeMap(args[1], 1); + Map data = map.convertMap(Value::asString, Value::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 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) { + 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 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); + consumer.accept(args[0].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..5d401cd9 --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/ServerValue.java @@ -0,0 +1,109 @@ +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; + +class ServerValue extends MapValue { + + private final Javalin server; + + public ServerValue(Javalin server) { + super(12); + 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("exception", this::exception); + set("start", this::start); + set("stop", this::stop); + } + + private Value error(Value[] args) { + Arguments.checkOrOr(2, 3, args.length); + final String contentType; + if (args.length == 2) { + contentType = "*"; + } else { + contentType = args[2].asString(); + } + int status = args[0].asInt(); + final Handler handler = toHandler(ValueUtils.consumeFunction(args[1], 1)); + server.error(status, contentType, handler); + 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) { + case 0 -> server.start(); + case 1 -> server.start(args[0].asInt()); + case 2 -> server.start(args[1].asString(), args[0].asInt()); + } + 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); + 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..e412a85f --- /dev/null +++ b/modules/server/src/main/java/com/annimon/ownlang/modules/server/server.java @@ -0,0 +1,46 @@ +package com.annimon.ownlang.modules.server; + +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; + +public final class server implements Module { + + @Override + public Map constants() { + return Map.of("Header", ValueUtils.collectStringConstants(Header.class)); + } + + @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 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) { + 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/build.gradle b/ownlang-core/build.gradle new file mode 100644 index 00000000..ac6bf822 --- /dev/null +++ b/ownlang-core/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon' +version = versions.project + +dependencies { + implementation "org.json:json:${versions.json}" + + testImplementation platform("org.junit:junit-bom:${versions.junit}") + 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/src/main/java/com/annimon/ownlang/Console.java b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java similarity index 58% rename from src/main/java/com/annimon/ownlang/Console.java rename to ownlang-core/src/main/java/com/annimon/ownlang/Console.java index 888d9f75..3b7676e0 100644 --- a/src/main/java/com/annimon/ownlang/Console.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/Console.java @@ -3,10 +3,17 @@ 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.*; 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 { @@ -58,6 +65,32 @@ public static void error(CharSequence value) { outputSettings.error(value); } + public static void handleException(StagesData stagesData, Thread thread, Exception exception) { + final var joiner = new StringJoiner("\n"); + joiner.add(new ExceptionConverterStage() + .then((data, error) -> List.of(error)) + .then(new ErrorsLocationFormatterStage()) + .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) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try(final PrintStream ps = new PrintStream(baos)) { 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/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java new file mode 100644 index 00000000..24c05ff0 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.exceptions; + +import com.annimon.ownlang.util.Range; + +public final class ArgumentsMismatchException extends OwnLangRuntimeException { + + public ArgumentsMismatchException() { + } + + public ArgumentsMismatchException(String message) { + super(message); + } + + public ArgumentsMismatchException(String message, Range range) { + super(message, range); + } +} diff --git a/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 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/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..83159a63 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java @@ -0,0 +1,41 @@ +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 implements SourceLocatedError { + + private final Range range; + + public OwnLangRuntimeException() { + super(); + this.range = null; + } + + public OwnLangRuntimeException(Exception ex) { + super(ex); + 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/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 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/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/src/main/java/com/annimon/ownlang/exceptions/TypeException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java similarity index 63% rename from src/main/java/com/annimon/ownlang/exceptions/TypeException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java index c2e853b1..e5323079 100644 --- a/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 new file mode 100644 index 00000000..d963c25a --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.exceptions; + +import com.annimon.ownlang.util.Range; + +public final class UnknownClassException extends OwnLangRuntimeException { + + private final String className; + + public UnknownClassException(String name, Range range) { + super("Unknown class " + name, range); + this.className = name; + } + + public String getClassName() { + return className; + } +} 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 52% rename from src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java index 74ad7d3e..3d5328d4 100644 --- a/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java @@ -1,6 +1,8 @@ package com.annimon.ownlang.exceptions; -public final class UnknownFunctionException extends RuntimeException { +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/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java similarity index 78% rename from src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java rename to ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java index 4156c59e..bcfa22c3 100644 --- a/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-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java new file mode 100644 index 00000000..8ed000d9 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java @@ -0,0 +1,17 @@ +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, Range range) { + super("Variable " + variable + " does not exists", range); + this.variable = variable; + } + + public String getVariable() { + return variable; + } +} 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 80% rename from src/main/java/com/annimon/ownlang/lib/ArrayValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/ArrayValue.java index bc824fb1..44aa31ca 100644 --- a/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. @@ -52,7 +50,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 +98,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) { @@ -142,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/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/CallStack.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java new file mode 100644 index 00000000..bf016551 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/CallStack.java @@ -0,0 +1,62 @@ +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 { + + private static final int MAX_FUNCTION_LENGTH = 62; + private static final Deque calls = new ConcurrentLinkedDeque<>(); + + private CallStack() { } + + public static synchronized void clear() { + calls.clear(); + } + + 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( + "com.annimon.ownlang.modules.(\\w+)\\.?\\1?", "module $1"); + } + 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)); + } + + public static synchronized void exit() { + calls.pop(); + } + + 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, Range range) { + String format() { + return "\tat " + this; + } + + @Override + public String toString() { + if (range == null) { + return String.format("%s: %s", name, function); + } else { + return String.format("%s: %s %s", name, function, range.format()); + } + } + } +} 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/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java similarity index 55% rename from src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java index 9a2ff767..a5a0fe2f 100644 --- a/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,29 +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) { @@ -51,33 +57,39 @@ 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 + public Object asJavaObject() { + return thisMap.asJavaObject(); } @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().asString(); } - return className + "@" + thisMap; + return className + "@" + thisMap.asString(); } @Override @@ -98,7 +110,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/src/main/java/com/annimon/ownlang/lib/Converters.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/Converters.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Converters.java index 9415df00..32934ac3 100644 --- a/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/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/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/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java new file mode 100644 index 00000000..6d5c7524 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.lib; + +/** + * + * @author aNNiMON + */ +public final class Functions { + private Functions() { } + + /** + * @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/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 81% rename from src/main/java/com/annimon/ownlang/lib/MapValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/MapValue.java index 1d8c7d1e..01b7728e 100644 --- a/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(); } @@ -88,11 +85,29 @@ 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() { return map; } + + @Override + public Object asJavaObject() { + Map result = new LinkedHashMap<>(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/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/src/main/java/com/annimon/ownlang/lib/NumberValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java similarity index 96% rename from src/main/java/com/annimon/ownlang/lib/NumberValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/NumberValue.java index 68e005dd..e3e9e370 100644 --- a/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-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..19682bf8 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java @@ -0,0 +1,99 @@ +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 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<>(); + } + + @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; + } + + + 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; + } + + 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/Scope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java new file mode 100644 index 00000000..078d7d9e --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java @@ -0,0 +1,60 @@ +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 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..f82af080 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -0,0 +1,156 @@ +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(); + } + + public static Map classDeclarations() { + return rootScope.getClassDeclarations(); + } + + + static RootScope rootScope() { + return rootScope; + } + + /** + * Resets a scope for new program execution + */ + public static void resetScope() { + rootScope = new RootScope(); + scope = rootScope; + } + + public static AutoCloseableScope closeableScope() { + push(); + return new AutoCloseableScope(); + } + + 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 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; + } + 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) { + 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 boolean isConstantExists(String name) { + return rootScope.containsConstant(name); + } + + 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); + } + } + + 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/src/main/java/com/annimon/ownlang/lib/StringValue.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java similarity index 65% rename from src/main/java/com/annimon/ownlang/lib/StringValue.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/StringValue.java index d58f59f1..1e6bdee0 100644 --- a/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: - if (Functions.isExists(prop)) { - final Function f = Functions.get(prop); - return new FunctionValue(args -> { + default -> { + 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; 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/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 78% rename from src/main/java/com/annimon/ownlang/lib/Value.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java index 0bac3e4e..f334e6da 100644 --- a/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(); + } } 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 67% rename from src/main/java/com/annimon/ownlang/lib/ValueUtils.java rename to ownlang-core/src/main/java/com/annimon/ownlang/lib/ValueUtils.java index c8a2eab5..e9764ea9 100644 --- a/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; @@ -106,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)); } @@ -120,15 +124,28 @@ public static Function consumeFunction(Value value, String errorMessage) { } 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; + } } 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 new file mode 100644 index 00000000..25fe43b1 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.lib; + +/** + * + * @author aNNiMON + */ +public final class Variables { + private Variables() { } + + /** + * @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); + } + + /** + * @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-core/src/main/java/com/annimon/ownlang/modules/Module.java b/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java new file mode 100644 index 00000000..961a3157 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java @@ -0,0 +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 { + + Map constants(); + + Map functions(); +} 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 92% rename from src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java rename to ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/ConsoleOutputSettings.java index 87c409df..49227218 100644 --- a/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/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java similarity index 80% rename from src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java rename to ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java index 30427080..264714ad 100644 --- a/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/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java b/ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java similarity index 94% rename from src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java rename to ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/StringOutputSettings.java index 3ffc8f11..1c89163e 100644 --- a/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-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..4633a087 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.stages; + +import java.util.function.Supplier; + +public interface StagesData { + + T get(String tag); + + default T getOrDefault(String tag, T other) { + T value = get(tag); + 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/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-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..1ddc74e7 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java @@ -0,0 +1,34 @@ +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; +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 var lines = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE_LINES, new String[0]); + for (SourceLocatedError error : input) { + sb.append(Console.newline()); + sb.append(error); + final Range range = error.getRange(); + if (range != null) { + sb.append(' ').append(range.format()); + } + sb.append(Console.newline()); + 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(); + } +} \ 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/Pos.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/Pos.java new file mode 100644 index 00000000..6c72e523 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/Pos.java @@ -0,0 +1,14 @@ +package com.annimon.ownlang.util; + +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)); + } + + public String format() { + return "[" + row + ":" + col + "]"; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java new file mode 100644 index 00000000..b753d9d9 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.util; + +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 (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/SimpleError.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java new file mode 100644 index 00000000..16057c2d --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java @@ -0,0 +1,22 @@ +package com.annimon.ownlang.util; + +public record SimpleError(String message, Range range) implements SourceLocatedError { + public SimpleError(String message) { + this(message, null); + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String toString() { + return message; + } + + @Override + public Range getRange() { + return range; + } +} 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 new file mode 100644 index 00000000..af7aa003 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java @@ -0,0 +1,8 @@ +package com.annimon.ownlang.util; + +public interface SourceLocation { + + default Range getRange() { + return null; + } +} 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/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/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-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/input/SourceLoaderStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java new file mode 100644 index 00000000..40790546 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/SourceLoaderStage.java @@ -0,0 +1,39 @@ +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.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class SourceLoaderStage implements Stage { + + public static final String TAG_SOURCE_LINES = "sourceLines"; + + @Override + public String perform(StagesData stagesData, InputSource inputSource) { + try { + String result = inputSource.load(); + 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); + } + } + + 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-desktop/build.gradle b/ownlang-desktop/build.gradle new file mode 100644 index 00000000..719f15a8 --- /dev/null +++ b/ownlang-desktop/build.gradle @@ -0,0 +1,71 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'java' + id 'application' +} + +group = 'com.annimon' +version = versions.project + +ext.mainClassName = 'com.annimon.ownlang.Main' +application { + mainClass = project.mainClassName +} + +jar { + manifest { + attributes 'Main-Class': project.mainClassName + } +} + +dependencies { + implementation project(":ownlang-core") + implementation project(":ownlang-parser") + implementation project(":ownlang-utils") + implementation project(":modules:main") + + testImplementation platform("org.junit:junit-bom:${versions.junit}") + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} + +def ownlangExec = tasks.register('ownlangExec', JavaExec) { + dependsOn classes + mainClass = project.mainClassName + classpath = sourceSets.main.runtimeClasspath + standardInput = System.in +} + +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" + doFirst { + ownlangExec.configure { + args '-o 9 -m -a -f ../program.own'.split(' ') + } + } + finalizedBy ownlangExec +} + +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-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java new file mode 100644 index 00000000..86efaf45 --- /dev/null +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -0,0 +1,186 @@ +package com.annimon.ownlang; + +import com.annimon.ownlang.exceptions.OwnLangParserException; +import com.annimon.ownlang.exceptions.StoppedException; +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; +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; +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +/** + * @author aNNiMON + */ +public final class Main { + + public static void main(String[] args) throws IOException { + final RunOptions options = new RunOptions(); + if (args.length == 0 && (options.detectDefaultProgramPath() == null)) { + printUsage(); + return; + } + + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "-a": + case "--showast": + options.showAst = true; + break; + + case "-b": + case "--beautify": + options.beautifyMode = true; + break; + + case "-t": + case "--showtokens": + options.showTokens = true; + break; + + case "-m": + case "--showtime": + options.showMeasurements = true; + break; + + case "-o": + case "--optimize": + if (i + 1 < args.length) { + try { + options.optimizationLevel = Integer.parseInt(args[i + 1]); + } catch (NumberFormatException nfe) { + options.optimizationLevel = 2; + } + i++; + } else { + options.optimizationLevel = 2; + } + break; + + case "-r": + case "--repl": + Repl.main(new String[0]); + return; + + case "-l": + case "--lint": + 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": + if (i + 1 < args.length) { + options.programPath = args[i + 1]; + createOwnLangArgs(args, i + 2); + i++; + } + break; + + case "--sandbox": + Sandbox.main(createOwnLangArgs(args, i + 1)); + return; + + default: + if (options.programSource == null) { + options.programSource = args[i]; + createOwnLangArgs(args, i + 1); + } + break; + } + } + if (options.beautifyMode) { + String result = new SourceLoaderStage() + .then(new BeautifierStage()) + .perform(new StagesDataMap(), options.toInputSource()); + System.out.println(result); + return; + } + run(options); + } + + private static void printUsage() { + 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. 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 + -m, --showtime Show elapsed time of parsing and execution + """); + } + + 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(RunOptions options) { + final var measurement = new TimeMeasurement(); + final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop); + + final var stagesData = new StagesDataMap(); + try { + 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", + new OptimizationStage(options.optimizationLevel, options.showAst))) + .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()); + } catch (OwnLangParserException ex) { + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + System.err.println(error); + } catch (StoppedException ex) { + // skip + } catch (Exception ex) { + Console.handleException(stagesData, Thread.currentThread(), ex); + } finally { + 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); + System.out.println(stagesData.getOrDefault(OptimizationStage.TAG_OPTIMIZATION_SUMMARY, "")); + } + if (options.showMeasurements) { + System.out.println("=".repeat(25)); + System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); + } + } + } +} 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..2a81c4e6 --- /dev/null +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang; + +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 DEFAULT_PROGRAM = "program.own"; + + // input + String programPath; + String programSource; + // modes + LinterStage.Mode lintMode = LinterStage.Mode.SEMANTIC; + boolean beautifyMode; + int optimizationLevel; + // flags + boolean showTokens; + boolean showAst; + boolean showMeasurements; + + private final InputSourceDetector inputSourceDetector = new InputSourceDetector(); + + boolean linterEnabled() { + return lintMode != null && lintMode != LinterStage.Mode.NONE; + } + + String detectDefaultProgramPath() { + final String resourcePath = RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM; + if (inputSourceDetector.isReadable(resourcePath)) { + return resourcePath; + } + if (inputSourceDetector.isReadable(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"); + } + } + return inputSourceDetector.toInputSource(programPath); + } +} diff --git a/ownlang-parser/build.gradle b/ownlang-parser/build.gradle new file mode 100644 index 00000000..c34acf27 --- /dev/null +++ b/ownlang-parser/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon' +version = versions.project + +dependencies { + api project(':ownlang-core') + + 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.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}" +} + +test { + useJUnitPlatform() +} \ 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 new file mode 100644 index 00000000..1fb4cf81 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.exceptions; + +import com.annimon.ownlang.util.SourceLocatedError; +import java.util.Collection; +import java.util.List; + +/** + * Single Exception for Lexer, Parser and Linter errors + */ +public class OwnLangParserException extends RuntimeException { + + private final Collection errors; + + public OwnLangParserException(SourceLocatedError error) { + super(error.toString()); + errors = List.of(error);; + } + + public OwnLangParserException(Collection errors) { + super(errors.toString()); + this.errors = errors; + } + + 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 new file mode 100644 index 00000000..4a803e17 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -0,0 +1,21 @@ +package com.annimon.ownlang.exceptions; + +import com.annimon.ownlang.util.Range; + +/** + * + * @author aNNiMON + */ +public final class ParseException extends RuntimeException { + + private final Range range; + + public ParseException(String message, Range range) { + super(message); + this.range = range; + } + + public Range getRange() { + return range; + } +} \ No newline at end of file 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 59% 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..c62877aa 100644 --- a/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -5,67 +5,79 @@ 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(); } - + public String getArgsName(int index) { if (index < 0 || index >= getArgsCount()) return ""; - return arguments.get(index).getName(); + return arguments.get(index).name(); } @Override - public Value execute(Value... values) { + 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 { - 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.getName(), arg.getValueExpr().eval()); + ScopeHandler.defineVariableInCurrentScope(arg.name(), arg.valueExpr().eval()); } - body.execute(); + body.eval(); return NumberValue.ZERO; } catch (ReturnStatement rt) { return rt.getResult(); } finally { - Variables.pop(); + ScopeHandler.pop(); } } @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/src/main/java/com/annimon/ownlang/parser/Beautifier.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/BeautifierStage.java similarity index 94% rename from src/main/java/com/annimon/ownlang/parser/Beautifier.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/BeautifierStage.java index 064506eb..bf8453b9 100644 --- a/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() { @@ -246,9 +243,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 +257,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/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java new file mode 100644 index 00000000..72663ef3 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -0,0 +1,438 @@ +package com.annimon.ownlang.parser; + +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.*; + +/** + * + * @author aNNiMON + */ +public final class Lexer { + + public static List tokenize(String input) { + return new Lexer(input).tokenize(); + } + + private static final String OPERATOR_CHARS = "+-*/%()[]{}=<>!&|.,^~?:"; + + private static final Map OPERATORS; + static { + 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.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.PLUSPLUS); + operators.put("--", TokenType.MINUSMINUS); + + operators.put("::", TokenType.COLONCOLON); + + operators.put("&&", TokenType.AMPAMP); + operators.put("||", TokenType.BARBAR); + + 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 = Map.copyOf(operators); + } + + private static final Map KEYWORDS; + static { + 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() { + return KEYWORDS.keySet(); + } + + private final String input; + private final int length; + + private final List tokens; + private final StringBuilder buffer; + + private int pos; + private int row, col; + + public Lexer(String input) { + this.input = input; + length = input.length(); + + tokens = new ArrayList<>(); + buffer = new StringBuilder(40); + row = col = 1; + } + + public List tokenize() { + while (pos < length) { + // Fast path for skipping whitespaces + while (Character.isWhitespace(peek(0))) { + skip(); + } + + final char current = peek(0); + if (isNumber(current)) tokenizeNumber(); + else if (isOwnLangIdentifierStart(current)) tokenizeWord(); + 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 (current == ';') skip(); // ignore semicolon + else if (current == '\0') break; + else throw error("Unknown token " + current, markPos()); + } + return tokens; + } + + private void tokenizeNumber() { + final var buffer = createBuffer(); + final Pos startPos = markPos(); + char current = peek(0); + if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { + tokenizeHexNumber(2); + return; + } + boolean decimal = false; + boolean hasDot = false; + while (true) { + if (current == '.') { + decimal = true; + if (hasDot) throw error("Invalid float number " + buffer, startPos); + hasDot = true; + } else if (current == 'e' || current == 'E') { + decimal = true; + int exp = subTokenizeScientificNumber(startPos); + buffer.append(current).append(exp); + break; + } else if (!Character.isDigit(current)) { + break; + } + buffer.append(current); + current = next(); + } + if (decimal) { + addToken(TokenType.DECIMAL_NUMBER, buffer.toString(), startPos); + } else { + addToken(TokenType.NUMBER, buffer.toString(), startPos); + } + } + + private int subTokenizeScientificNumber(Pos startPos) { + 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", startPos, markEndPos()); + if (position >= 4) { + if (sign > 0) throw error("Float number too large", startPos, markEndPos()); + else throw error("Float number too small", startPos, markEndPos()); + } + return sign * result; + } + + private void tokenizeHexNumber(int skipChars) { + final var buffer = createBuffer(); + final Pos startPos = markPos(); + // Skip HEX prefix 0x or # + for (int i = 0; i < skipChars; i++) skip(); + + char current = peek(0); + while (isHexNumber(current) || (current == '_')) { + if (current != '_') { + // allow _ symbol + buffer.append(current); + } + current = next(); + } + + 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); + } + + private static boolean isNumber(char current) { + return ('0' <= current && current <= '9'); + } + + private static boolean isHexNumber(char current) { + return ('0' <= current && current <= '9') + || ('a' <= current && current <= 'f') + || ('A' <= current && current <= 'F'); + } + + private void tokenizeOperator() { + char current = peek(0); + if (current == '/') { + if (peek(1) == '/') { + tokenizeComment(); + return; + } else if (peek(1) == '*') { + tokenizeMultilineComment(); + return; + } + } + + final Pos startPos = markPos(); + final var buffer = createBuffer(); + while (true) { + if (!buffer.isEmpty() && !OPERATORS.containsKey(buffer.toString() + current)) { + addToken(OPERATORS.get(buffer.toString()), startPos); + return; + } + buffer.append(current); + current = next(); + } + } + + private void tokenizeWord() { + final var buffer = createBuffer(); + final Pos startPos = markPos(); + buffer.append(peek(0)); + char current = next(); + while (isOwnLangIdentifierPart(current)) { + buffer.append(current); + current = next(); + } + + final String word = buffer.toString(); + if (KEYWORDS.containsKey(word)) { + addToken(KEYWORDS.get(word), startPos); + } else { + addToken(TokenType.WORD, word, startPos); + } + } + + private void tokenizeExtendedWord() { + final Pos startPos = markPos(); + skip();// skip ` + final var buffer = createBuffer(); + char current = peek(0); + while (current != '`') { + if ("\r\n\0".indexOf(current) != -1) { + throw error("Reached end of line while parsing extended word.", startPos, markEndPos()); + } + buffer.append(current); + current = next(); + } + skip(); // skip closing ` + addToken(TokenType.WORD, buffer.toString(), startPos); + } + + private void tokenizeText() { + final Pos startPos = markPos(); + skip();// skip " + final var buffer = createBuffer(); + 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; + case 'f': current = next(); buffer.append('\f'); continue; + case 'n': current = next(); buffer.append('\n'); continue; + case 'r': current = next(); buffer.append('\r'); continue; + case 't': current = next(); buffer.append('\t'); continue; + case 'u': // http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.3 + int rollbackPosition = pos; + while (current == 'u') current = next(); + int escapedValue = 0; + for (int i = 12; i >= 0 && escapedValue != -1; i -= 4) { + if (isHexNumber(current)) { + escapedValue |= (Character.digit(current, 16) << i); + } else { + escapedValue = -1; + } + current = next(); + } + if (escapedValue >= 0) { + buffer.append((char) escapedValue); + } else { + // rollback + buffer.append("\\u"); + pos = rollbackPosition; + } + continue; + } + buffer.append('\\'); + continue; + } + if (current == '"') break; + if (current == '\0') throw error("Reached end of file while parsing text string.", startPos, markEndPos()); + buffer.append(current); + current = next(); + } + 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(); + } + } + + 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", startPos, markEndPos()); + } + current = next(); + } + skip(); // * + skip(); // / + } + + private boolean isOwnLangIdentifierStart(char current) { + return (Character.isLetter(current) || (current == '_') || (current == '$')); + } + + private boolean isOwnLangIdentifierPart(char current) { + return isOwnLangIdentifierStart(current) || isNumber(current); + } + + private StringBuilder createBuffer() { + buffer.setLength(0); + return buffer; + } + + 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); + if (result == '\n') { + row++; + col = 1; + } else col++; + pos++; + } + + private char next() { + skip(); + return peek(0); + } + + private char peek(int relativePosition) { + final int position = pos + relativePosition; + if (position >= length) return '\0'; + return input.charAt(position); + } + + private void addToken(TokenType type, Pos startPos) { + addToken(type, "", startPos); + } + + private void addToken(TokenType type, String text, Pos startRow) { + tokens.add(new Token(type, text, startRow)); + } + + 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/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java similarity index 69% rename from src/main/java/com/annimon/ownlang/parser/Parser.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 8ec59e0a..271c3ee9 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -1,16 +1,18 @@ 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 java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +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; /** * @@ -18,16 +20,16 @@ */ 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 ParseException(); + throw new OwnLangParserException(parser.getParseErrors()); } return program; } - private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); + private static final Token EOF = new Token(TokenType.EOF, "", Pos.UNKNOWN); private static final EnumMap ASSIGN_OPERATORS; static { @@ -53,7 +55,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; @@ -69,14 +71,17 @@ public ParseErrors getParseErrors() { return parseErrors; } - public Statement parse() { + public Node parse() { parseErrors.clear(); final BlockStatement result = new BlockStatement(); while (!match(TokenType.EOF)) { try { result.add(statement()); + } catch (ParseException ex) { + parseErrors.add(new ParseError(ex.getMessage(), ex.getRange())); + recover(); } catch (Exception ex) { - parseErrors.add(ex, getErrorLine()); + parseErrors.add(new ParseError(ex.getMessage(), getRange(), ex.getStackTrace())); recover(); } } @@ -84,20 +89,14 @@ public Statement parse() { return result; } - private int getErrorLine() { - if (size == 0) return 0; - if (pos >= size) return tokens.get(size - 1).getRow(); - return tokens.get(pos).getRow(); - } - private void recover() { - int preRecoverPosition = pos; - for (int i = preRecoverPosition; i <= size; i++) { - 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 @@ -136,19 +135,19 @@ 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()); } if (match(TokenType.USE)) { - return new UseStatement(expression()); + return useStatement(); } if (match(TokenType.INCLUDE)) { - return new IncludeStatement(expression()); + return includeStatement(); } if (match(TokenType.FOR)) { return forStatement(); @@ -163,40 +162,61 @@ private Statement statement() { return classDeclaration(); } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return new ExprStatement(functionChain(qualifiedName())); + return functionCallStatement(); } 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 { + modules.add(consumeOrExplainError(TokenType.WORD, + Parser::explainUseStatementError).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; + final Node expression = expression(); + 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 startTokenIndex = index; consume(TokenType.LPAREN); final List variables = new ArrayList<>(); while (!match(TokenType.RPAREN)) { - if (lookMatch(0, TokenType.WORD)) { - variables.add(consume(TokenType.WORD).getText()); - } 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(), startTokenIndex, index); + } consume(TokenType.EQ); return new DestructuringAssignmentStatement(variables, expression()); } private Statement ifElse() { - final Expression condition = expression(); + final Node condition = expression(); final Statement ifStatement = statementOrBlock(); final Statement elseStatement; if (match(TokenType.ELSE)) { @@ -208,7 +228,7 @@ private Statement ifElse() { } private Statement whileStatement() { - final Expression condition = expression(); + final Node condition = expression(); final Statement statement = statementOrBlock(); return new WhileStatement(condition, statement); } @@ -216,7 +236,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); } @@ -239,7 +259,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 @@ -250,9 +270,9 @@ && 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(); + final Node container = expression(); if (optParentheses) { consume(TokenType.RPAREN); // close opt parentheses } @@ -263,11 +283,11 @@ 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(); + final Node container = expression(); if (optParentheses) { consume(TokenType.RPAREN); // close opt parentheses } @@ -277,29 +297,32 @@ private ForeachMapStatement foreachMapStatement() { private FunctionDefineStatement functionDefine() { // def name(arg1, arg2 = value) { ... } || def name(args) = expr - final String name = consume(TokenType.WORD).getText(); + 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).getText(); + final String name = consume(TokenType.WORD).text(); if (match(TokenType.EQ)) { startsOptionalArgs = true; arguments.addOptional(name, variable()); } else if (!startsOptionalArgs) { arguments.addRequired(name); } else { - throw new ParseException("Required argument cannot be after optional"); + throw error(errorRequiredArgumentAfterOptional()); } match(TokenType.COMMA); } + arguments.setRange(getRange(startTokenIndex, index - 1)); return arguments; } @@ -310,14 +333,20 @@ private Statement statementBody() { return statementOrBlock(); } - private Expression functionChain(Expression qualifiedNameExpr) { + private ExprStatement functionCallStatement() { + return new ExprStatement( + functionChain(new ValueExpression(consume(TokenType.WORD).text())) + ); + } + + 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; } @@ -332,21 +361,23 @@ 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); final FunctionalExpression function = new FunctionalExpression(qualifiedNameExpr); while (!match(TokenType.RPAREN)) { function.addArgument(expression()); match(TokenType.COMMA); } + function.setRange(getRange(startTokenIndex, index - 1)); 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); @@ -354,14 +385,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 LinkedHashMap<>(); 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); } @@ -371,9 +402,9 @@ 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(); + final Node expression = expression(); consume(TokenType.LBRACE); final List patterns = new ArrayList<>(); do { @@ -381,28 +412,33 @@ private MatchExpression match() { 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(createNumber(current.getText(), 10)) + NumberValue.of(createDecimalNumber(current.text())) ); } 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 +446,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 { @@ -422,7 +458,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: @@ -447,7 +483,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 { @@ -458,19 +494,19 @@ 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)); 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; } @@ -479,33 +515,33 @@ private Expression assignment() { private AssignmentExpression assignmentStrict() { // x[0].prop += ... - final int position = pos; - final Expression targetExpr = qualifiedName(); - if ((targetExpr == null) || !(targetExpr instanceof Accessible)) { - pos = position; + final int position = index; + final Node targetExpr = qualifiedName(); + if (!(targetExpr instanceof Accessible)) { + index = position; return null; } - final TokenType currentType = get(0).getType(); + final TokenType currentType = get(0).type(); if (!ASSIGN_OPERATORS.containsKey(currentType)) { - pos = position; + index = position; return null; } 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)) { @@ -514,8 +550,8 @@ private Expression ternary() { return result; } - private Expression nullCoalesce() { - Expression result = logicalOr(); + private Node nullCoalesce() { + Node result = logicalOr(); while (true) { if (match(TokenType.QUESTIONQUESTION)) { @@ -528,8 +564,8 @@ private Expression nullCoalesce() { return result; } - private Expression logicalOr() { - Expression result = logicalAnd(); + private Node logicalOr() { + Node result = logicalAnd(); while (true) { if (match(TokenType.BARBAR)) { @@ -542,8 +578,8 @@ private Expression logicalOr() { return result; } - private Expression logicalAnd() { - Expression result = bitwiseOr(); + private Node logicalAnd() { + Node result = bitwiseOr(); while (true) { if (match(TokenType.AMPAMP)) { @@ -556,8 +592,8 @@ private Expression logicalAnd() { return result; } - private Expression bitwiseOr() { - Expression expression = bitwiseXor(); + private Node bitwiseOr() { + Node expression = bitwiseXor(); while (true) { if (match(TokenType.BAR)) { @@ -570,8 +606,8 @@ private Expression bitwiseOr() { return expression; } - private Expression bitwiseXor() { - Expression expression = bitwiseAnd(); + private Node bitwiseXor() { + Node expression = bitwiseAnd(); while (true) { if (match(TokenType.CARET)) { @@ -584,8 +620,8 @@ private Expression bitwiseXor() { return expression; } - private Expression bitwiseAnd() { - Expression expression = equality(); + private Node bitwiseAnd() { + Node expression = equality(); while (true) { if (match(TokenType.AMP)) { @@ -598,8 +634,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()); @@ -611,8 +647,8 @@ private Expression equality() { return result; } - private Expression conditional() { - Expression result = shift(); + private Node conditional() { + Node result = shift(); while (true) { if (match(TokenType.LT)) { @@ -637,8 +673,8 @@ private Expression conditional() { return result; } - private Expression shift() { - Expression expression = additive(); + private Node shift() { + Node expression = additive(); while (true) { if (match(TokenType.LTLT)) { @@ -663,8 +699,8 @@ private Expression shift() { return expression; } - private Expression additive() { - Expression result = multiplicative(); + private Node additive() { + Node result = multiplicative(); while (true) { if (match(TokenType.PLUS)) { @@ -693,8 +729,8 @@ private Expression additive() { return result; } - private Expression multiplicative() { - Expression result = objectCreation(); + private Node multiplicative() { + Node result = objectCreation(); while (true) { if (match(TokenType.STAR)) { @@ -719,22 +755,25 @@ private Expression multiplicative() { return result; } - private Expression objectCreation() { - if (match(TokenType.NEW)) { - final String className = consume(TokenType.WORD).getText(); - final List args = new ArrayList<>(); + private Node objectCreation() { + if (match(TokenType.NEW)) { + final var startTokenIndex = index - 1; + final String className = consume(TokenType.WORD).text(); + final List args = new ArrayList<>(); consume(TokenType.LPAREN); while (!match(TokenType.RPAREN)) { 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(); } - private Expression unary() { + private Node unary() { if (match(TokenType.PLUSPLUS)) { return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary()); } @@ -756,16 +795,16 @@ 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; } 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)) { @@ -773,20 +812,22 @@ 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)); + final Range range = getRange(startTokenIndex, index - 1); + return new ValueExpression(new UserDefinedFunction(arguments, statement, range)); } 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).getText())); + 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)) { @@ -811,28 +852,31 @@ private Expression variable() { return value(); } - private Expression qualifiedName() { + 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(); + final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { - return new VariableExpression(current.getText()); + final var variable = new VariableExpression(current.text()); + variable.setRange(getRange(startTokenIndex, index - 1)); + return variable; } - return new ContainerAccessExpression(current.getText(), indices); + 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).getText(); - final Expression key = new ValueExpression(fieldName); + final String fieldName = consume(TokenType.WORD).text(); + final Node key = new ValueExpression(fieldName); indices.add(key); } if (match(TokenType.LBRACKET)) { @@ -843,26 +887,29 @@ 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.getText(), 10)); + 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.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(); + final List indices = variableSuffix(); if (indices == null || indices.isEmpty()) { return strExpr; } @@ -870,14 +917,10 @@ 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(".")) { - return Double.parseDouble(text); - } // Integer try { return Integer.parseInt(text, radix); @@ -886,31 +929,96 @@ private Number createNumber(String text, int radix) { } } - private Token consume(TokenType type) { - final Token current = get(0); - if (type != current.getType()) { - throw new ParseException("Token " + current + " doesn't match " + type); + private Number createDecimalNumber(String text) { + // Double + return Double.parseDouble(text); + } + + private Token consume(TokenType expectedType) { + final Token actual = get(0); + if (expectedType != actual.type()) { + throw error(errorUnexpectedToken(actual, expectedType)); } - pos++; - return current; + index++; + 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)); + } + index++; + return actual; } private boolean match(TokenType type) { final Token current = get(0); - if (type != current.getType()) { + if (type != current.type()) { return false; } - pos++; + index++; return true; } private boolean lookMatch(int pos, TokenType type) { - return get(pos).getType() == type; + return get(pos).type() == type; } private Token get(int relativePosition) { - final int position = pos + relativePosition; + final int position = index + relativePosition; if (position >= 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, getRange()); + } + + private ParseException error(String message, int startIndex, int endIndex) { + return new ParseException(message, getRange(startIndex, endIndex)); + } + + 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/src/main/java/com/annimon/ownlang/parser/SourceLoader.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java similarity index 90% rename from src/main/java/com/annimon/ownlang/parser/SourceLoader.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java index 4621d138..ac8c5406 100644 --- a/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/Token.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java new file mode 100644 index 00000000..134681d1 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java @@ -0,0 +1,18 @@ +package com.annimon.ownlang.parser; + +import com.annimon.ownlang.util.Pos; + +/** + * @author aNNiMON + */ +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; + } +} 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 98% rename from src/main/java/com/annimon/ownlang/parser/TokenType.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/TokenType.java index 87d15eb5..cf6a71db 100644 --- a/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/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/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 new file mode 100644 index 00000000..9c923038 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java @@ -0,0 +1,13 @@ +package com.annimon.ownlang.parser.ast; + +public record Argument(String name, Node valueExpr) { + + public Argument(String name) { + this(name, null); + } + + @Override + public String toString() { + return name + (valueExpr == null ? "" : " = " + valueExpr); + } +} 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 76% 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 index e2cb0d57..c3cb2ccf 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Arguments.java @@ -1,25 +1,28 @@ 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++; } - public void addOptional(String name, Expression expr) { + public void addOptional(String name, Node expr) { arguments.add(new Argument(name, expr)); } @@ -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/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 83% 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 index 7feed693..11a740f6 100644 --- a/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/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 77% 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 index 170cab2f..e76c0afb 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/AssignmentExpression.java @@ -1,27 +1,23 @@ 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 Expression, Statement { +public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue { 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 +26,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/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 91% 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 index b6cdab0b..0524499c 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -2,19 +2,13 @@ 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.*; /** * * @author aNNiMON */ -public final class BinaryExpression implements Expression { +public final class BinaryExpression implements Node { public enum Operator { ADD("+"), @@ -51,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; @@ -66,30 +60,29 @@ 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; } } 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 +210,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/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 72% 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 index 17ddc23c..13d37b0a 100644 --- a/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/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 56% 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 index e27d350b..7813855c 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java @@ -1,13 +1,27 @@ 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 void execute() { + public Value eval() { throw this; } 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 52% 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 index 92a272c4..482cfba8 100644 --- a/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,6 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.ClassDeclarations; +import com.annimon.ownlang.lib.*; import java.util.ArrayList; import java.util.List; @@ -25,8 +25,27 @@ public void addMethod(FunctionDefineStatement statement) { } @Override - public void execute() { - ClassDeclarations.set(name, this); + public Value eval() { + 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 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 93% 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 index 01630014..e6f14e79 100644 --- a/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 new file mode 100644 index 00000000..09b3f993 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -0,0 +1,136 @@ +package com.annimon.ownlang.parser.ast; + +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 Node, Accessible { + + private static final Pattern PATTERN_SIMPLE_INDEX = Pattern.compile("^\"[a-zA-Z$_]\\w*\""); + + public final Node root; + public final List indices; + private final boolean[] simpleIndices; + private final boolean rootIsVariable; + + public ContainerAccessExpression(String variable, List indices) { + this(new VariableExpression(variable), indices); + } + + public ContainerAccessExpression(Node root, List indices) { + rootIsVariable = root instanceof VariableExpression; + this.root = root; + this.indices = indices; + simpleIndices = precomputeSimpleIndices(); + } + + public boolean rootIsVariable() { + return rootIsVariable; + } + + public Node getRoot() { + return root; + } + + @Override + public Value eval() { + return get(); + } + + @Override + public Value get() { + final Value container = getContainer(); + final Value lastIndex = lastIndex(); + 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 -> ((ClassInstance) container).access(lastIndex); + default -> throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type())); + }; + } + + @Override + 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.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()); + } + return value; + } + + public Value getContainer() { + Value container = root.eval(); + final int last = indices.size() - 1; + for (int i = 0; i < last; i++) { + final Value index = index(i); + 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; + } + + public Value lastIndex() { + return index(indices.size() - 1); + } + + private Value index(int index) { + return indices.get(index).eval(); + } + + public MapValue consumeMap(Value value) { + if (value.type() != Types.MAP) { + throw new TypeException("Map expected"); + } + return (MapValue) value; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + 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 (Node index : indices) { + String indexStr = index.toString(); + result[i] = PATTERN_SIMPLE_INDEX.matcher(indexStr).matches(); + i++; + } + return result; + } + + @Override + public String toString() { + final var sb = new StringBuilder(root.toString()); + int i = 0; + for (Node 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(); + } +} 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 56% 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 index 442ff090..311c97a3 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java @@ -1,13 +1,27 @@ 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 void execute() { + public Value eval() { throw this; } 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 76% 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 index 5848b35f..253328b2 100644 --- a/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; @@ -16,25 +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); - break; - case Types.MAP: - execute((MapValue) container); - break; + case Types.ARRAY -> execute((ArrayValue) container); + case Types.MAP -> execute((MapValue) container); } + return NumberValue.ZERO; } private void execute(ArrayValue array) { @@ -42,7 +36,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 +45,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/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 76% 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 index 5bbfa435..980212ef 100644 --- a/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/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 78% 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 index 7688e46a..5e849638 100644 --- a/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/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 71% 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 index e0553718..0648b42c 100644 --- a/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/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 58% 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 index fbc5aa6c..33200242 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -11,48 +11,35 @@ 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(); - final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; - - final Value containerValue = container.eval(); - switch (containerValue.type()) { - case Types.STRING: - iterateString(containerValue.asString()); - break; - case Types.ARRAY: - iterateArray((ArrayValue) containerValue); - break; - case Types.MAP: - iterateMap((MapValue) containerValue); - break; - default: - throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type())); - } - - // Restore variables - if (previousVariableValue != null) { - Variables.set(variable, previousVariableValue); - } else { - Variables.remove(variable); + 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())); + } } + return NumberValue.ZERO; } 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(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -63,9 +50,9 @@ 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(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -76,12 +63,12 @@ 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() })); try { - body.execute(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { 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 50% 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 index 2a24393f..a1199493 100644 --- a/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,45 +22,26 @@ public ForeachMapStatement(String key, String value, Expression container, State } @Override - public void execute() { + public Value eval() { super.interruptionCheck(); - final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; - final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; - - final Value containerValue = container.eval(); - switch (containerValue.type()) { - case Types.STRING: - iterateString(containerValue.asString()); - break; - case Types.ARRAY: - iterateArray((ArrayValue) containerValue); - break; - case Types.MAP: - iterateMap((MapValue) containerValue); - break; - default: - throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair"); - } - - // Restore variables - if (previousVariableValue1 != null) { - Variables.set(key, previousVariableValue1); - } else { - Variables.remove(key); - } - if (previousVariableValue2 != null) { - Variables.set(value, previousVariableValue2); - } else { - Variables.remove(value); + 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"); + } } + return NumberValue.ZERO; } 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(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -72,10 +53,10 @@ 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(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { @@ -86,10 +67,10 @@ 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(); + body.eval(); } catch (BreakStatement bs) { break; } catch (ContinueStatement cs) { 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 54% 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 index 460f8649..08768022 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -1,27 +1,39 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.Functions; +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; /** * * @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 void execute() { - Functions.set(name, new UserDefinedFunction(arguments, body)); + public Range getRange() { + return range; + } + + @Override + public Value eval() { + ScopeHandler.setFunction(name, new UserDefinedFunction(arguments, body, range)); + return NumberValue.ZERO; } @Override @@ -36,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/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 88% 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 index 702d2fa8..f6f09ce6 100644 --- a/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; @@ -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/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java new file mode 100644 index 00000000..a97edfc8 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -0,0 +1,107 @@ +package com.annimon.ownlang.parser.ast; + +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; + +/** + * + * @author aNNiMON + */ +public final class FunctionalExpression extends InterruptableNode + implements Statement, SourceLocation { + + public final Node functionExpr; + public final List arguments; + private Range range; + + public FunctionalExpression(Node functionExpr) { + this.functionExpr = functionExpr; + arguments = new ArrayList<>(); + } + + public void addArgument(Node arg) { + arguments.add(arg); + } + + public void setRange(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } + + @Override + public Value eval() { + super.interruptionCheck(); + final int size = arguments.size(); + final Value[] values = new Value[size]; + for (int i = 0; i < size; i++) { + values[i] = arguments.get(i).eval(); + } + final Function f = consumeFunction(functionExpr); + CallStack.enter(functionExpr.toString(), f, range); + final Value result = f.execute(values); + CallStack.exit(); + return result; + } + + 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(); + } + return getFunction(value.asString()); + } + + private Function getFunction(String key) { + if (ScopeHandler.isFunctionExists(key)) { + return ScopeHandler.getFunction(key); + } + if (ScopeHandler.isVariableOrConstantExists(key)) { + final Value variable = ScopeHandler.getVariableOrConstant(key); + if (variable.type() == Types.FUNCTION) { + return ((FunctionValue)variable).getValue(); + } + } + throw new UnknownFunctionException(key, getRange()); + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public R accept(ResultVisitor visitor, T t) { + return visitor.visit(this, t); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + if (functionExpr instanceof ValueExpression valueExpr && (valueExpr.value.type() == Types.STRING)) { + sb.append(valueExpr.value.asString()).append('('); + } else { + sb.append(functionExpr).append('('); + } + final Iterator it = arguments.iterator(); + if (it.hasNext()) { + sb.append(it.next()); + while (it.hasNext()) { + sb.append(", ").append(it.next()); + } + } + sb.append(')'); + return sb.toString(); + } +} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IfStatement.java similarity index 76% 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 index 985dbcdb..ae92e9f0 100644 --- a/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 new file mode 100644 index 00000000..45a95b3e --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java @@ -0,0 +1,72 @@ +package com.annimon.ownlang.parser.ast; + +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.Range; +import com.annimon.ownlang.util.SourceLocation; +import com.annimon.ownlang.util.input.InputSourceFile; +import com.annimon.ownlang.util.input.SourceLoaderStage; + +/** + * + * @author aNNiMON + */ +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(); + + final var stagesData = new StagesDataMap(); + try { + final String path = expression.eval().asString(); + 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)); + } catch (OwnLangParserException ex) { + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + throw new OwnLangRuntimeException(error, ex); + } + return NumberValue.ZERO; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public R accept(ResultVisitor visitor, T t) { + return visitor.visit(this, t); + } + + @Override + public String toString() { + return "include " + expression; + } +} 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 74% 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 index 988d3b6a..05c399db 100644 --- a/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/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 61% 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 index 4a334404..228fb5bb 100644 --- a/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; @@ -14,63 +11,50 @@ * * @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(); 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 (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); - if (optMatches(p)) { - final Value result = evalResult(p.result); - Variables.remove(pattern.variable); - return result; + try (final var ignored = ScopeHandler.closeableScope()) { + ScopeHandler.defineVariableInCurrentScope(pattern.variable, value); + if (optMatches(p)) { + return evalResult(p.result); + } } - Variables.remove(pattern.variable); } } - if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) { - final ListPattern pattern = (ListPattern) p; - if (matchListPattern((ArrayValue) value, pattern)) { - // Clean up variables if matched - final Value result = evalResult(p.result); - for (String var : pattern.parts) { - Variables.remove(var); + if ((value.type() == Types.ARRAY) && (p instanceof ListPattern pattern)) { + 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)) { - 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); } @@ -84,8 +68,8 @@ 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) ) { + final Node expr = p.values.get(i); + if ( (expr != ANY) && (expr.eval().compareTo(array.get(i)) != 0) ) { return false; } } @@ -98,18 +82,15 @@ 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; - - case 1: // match arr { case [x]: x = arr ... } - final String variable = parts.get(0); - Variables.define(variable, array); - if (optMatches(p)) { - return true; + return (arraySize == 0) && 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); } - Variables.remove(variable); return false; default: { // match arr { case [...]: .. } @@ -128,41 +109,24 @@ 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)); - } - if (optMatches(p)) { - // Clean up will be provided after evaluate result - return true; + ScopeHandler.defineVariableInCurrentScope(parts.get(i), array.get(i)); } - // Clean up variables if no match - for (String var : parts) { - Variables.remove(var); - } - return false; + return optMatches(p); } private boolean matchListPatternWithTail(ListPattern p, List parts, int partsSize, ArrayValue array, int arraySize) { // 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); - // Check optional condition - if (optMatches(p)) { - // Clean up will be provided after evaluate result - return true; - } - // Clean up variables - for (String var : parts) { - Variables.remove(var); - } - return false; + ScopeHandler.defineVariableInCurrentScope(parts.get(lastPart), tail); + return optMatches(p); } private boolean match(Value value, Value constant) { @@ -177,7 +141,7 @@ private boolean optMatches(Pattern pattern) { private Value evalResult(Statement s) { try { - s.execute(); + s.eval(); } catch (ReturnStatement ret) { return ret.getResult(); } @@ -205,9 +169,9 @@ public String toString() { return sb.toString(); } - public abstract static class Pattern { + public abstract static sealed class Pattern { public Statement result; - public Expression optCondition; + public Node optCondition; @Override public String toString() { @@ -220,8 +184,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; @@ -233,8 +197,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; @@ -246,8 +210,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<>()); @@ -277,14 +241,14 @@ 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) { + public TuplePattern(List parts) { this.values = parts; } @@ -292,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()); @@ -310,26 +274,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 Node ANY = new Node() { + @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/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 77% 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 index c9d2d834..a9c883d4 100644 --- a/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 new file mode 100644 index 00000000..a19bbf6b --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java @@ -0,0 +1,81 @@ +package com.annimon.ownlang.parser.ast; + +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 Node, 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() { + final ClassDeclaration cd = ScopeHandler.getClassDeclaration(className); + if (cd != null) { + return cd.newInstance(constructorArgs()); + } + + // Is Instantiable? + if (ScopeHandler.isVariableOrConstantExists(className)) { + final Value variable = ScopeHandler.getVariableOrConstant(className); + if (variable instanceof Instantiable instantiable) { + return instantiable.newInstance(constructorArgs()); + } + } + throw new UnknownClassException(className, range); + } + + private Value[] constructorArgs() { + final int argsSize = constructorArguments.size(); + final Value[] args = new Value[argsSize]; + int i = 0; + for (Node argument : constructorArguments) { + args[i++] = argument.eval(); + } + return args; + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public R accept(ResultVisitor visitor, T t) { + return visitor.visit(this, t); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("new ").append(className).append(' '); + final Iterator it = constructorArguments.iterator(); + if (it.hasNext()) { + sb.append(it.next()); + while (it.hasNext()) { + sb.append(", ").append(it.next()); + } + } + sb.append(')'); + return sb.toString(); + } +} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java similarity index 75% 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 index e4aa6dd2..27c3580c 100644 --- a/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/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 75% 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 index 20276db8..134747cd 100644 --- a/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/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 85% 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 index 8e6aaaad..3a62d193 100644 --- a/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/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 82% 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 index db7ce9c1..a09e0737 100644 --- a/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/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 75% 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 index 255f2b31..31af483b 100644 --- a/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/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 86% 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 index 551ba6ac..4892d85b 100644 --- a/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,45 +35,40 @@ 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(); 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/ast/UseStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java new file mode 100644 index 00000000..9869c71d --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java @@ -0,0 +1,64 @@ +package com.annimon.ownlang.parser.ast; + +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; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class UseStatement extends InterruptableNode implements Statement { + public final Collection modules; + + public UseStatement(Collection modules) { + this.modules = modules; + } + + @Override + public Value eval() { + super.interruptionCheck(); + for (String module : modules) { + ModuleLoader.loadAndUse(module); + } + return NumberValue.ZERO; + } + + public Map loadConstants() { + final var result = new LinkedHashMap(); + for (String moduleName : modules) { + final Module module = ModuleLoader.load(moduleName); + result.putAll(module.constants()); + } + 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) { + visitor.visit(this); + } + + @Override + public R accept(ResultVisitor visitor, T t) { + return visitor.visit(this, t); + } + + @Override + public String toString() { + return "use " + String.join(", ", modules); + } +} 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 93% 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 index 1b3c9ef4..02171b7a 100644 --- a/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/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 59% 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 index 35653a14..fa04ab71 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -1,21 +1,33 @@ 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; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class VariableExpression extends InterruptableNode implements Expression, 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(); @@ -24,13 +36,15 @@ 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, getRange()); + } + return ScopeHandler.getVariableOrConstant(name); } @Override public Value set(Value value) { - Variables.set(name, value); + ScopeHandler.setVariable(name, value); return value; } 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 76% 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 index 73256eb8..995a0f23 100644 --- a/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/error/ParseError.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java new file mode 100644 index 00000000..d67b1086 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java @@ -0,0 +1,35 @@ +package com.annimon.ownlang.parser.error; + +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocatedError; + +public record ParseError( + String message, + Range range, + StackTraceElement[] stackTraceElements +) implements SourceLocatedError { + + public ParseError(String message, Range range) { + this(message, range, new StackTraceElement[0]); + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Range getRange() { + return range; + } + + @Override + public StackTraceElement[] getStackTrace() { + return stackTraceElements; + } + + @Override + public String toString() { + return "Error on line " + range().start().row() + ": " + message; + } +} diff --git a/src/main/java/com/annimon/ownlang/parser/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java similarity index 64% rename from src/main/java/com/annimon/ownlang/parser/ParseErrors.java rename to ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java index 83873491..bab8e9df 100644 --- a/src/main/java/com/annimon/ownlang/parser/ParseErrors.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java @@ -1,11 +1,12 @@ -package com.annimon.ownlang.parser; +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(Exception ex, int line) { - errors.add(new ParseError(line, ex)); + + @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 new file mode 100644 index 00000000..89444485 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java @@ -0,0 +1,20 @@ +package com.annimon.ownlang.parser.error; + +import com.annimon.ownlang.stages.Stage; +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, String> { + + @Override + public String perform(StagesData stagesData, Collection input) { + 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/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java new file mode 100644 index 00000000..d5727f2b --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -0,0 +1,42 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.parser.ast.*; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author aNNiMON + */ +final class AssignValidator extends LintVisitor { + + private final Set moduleConstants = new HashSet<>(); + + AssignValidator(LinterResults results) { + super(results); + } + + @Override + public void visit(AssignmentExpression s) { + super.visit(s); + if (s.target instanceof VariableExpression varExpr) { + final String variable = varExpr.name; + if (moduleConstants.contains(variable)) { + results.add(LinterResult.warning( + "Variable \"%s\" overrides constant".formatted(variable))); + } + } + } + + @Override + public void visit(IncludeStatement st) { + super.visit(st); + applyVisitor(st, this); + } + + @Override + 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 new file mode 100644 index 00000000..129ea427 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -0,0 +1,35 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.parser.ast.*; +import java.util.HashSet; +import java.util.Set; + +final class DefaultFunctionsOverrideValidator extends LintVisitor { + + private final Set moduleFunctions = new HashSet<>(); + + DefaultFunctionsOverrideValidator(LinterResults results) { + super(results); + } + + @Override + public void visit(FunctionDefineStatement s) { + super.visit(s); + if (moduleFunctions.contains(s.name)) { + results.add(LinterResult.warning( + "Function \"%s\" overrides default module function".formatted(s.name))); + } + } + + @Override + public void visit(IncludeStatement s) { + super.visit(s); + applyVisitor(s, this); + } + + @Override + 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/IncludeSourceValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java new file mode 100644 index 00000000..c02995bf --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java @@ -0,0 +1,35 @@ +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), + s.getRange())); + } + } else { + results.add(LinterResult.warning( + "Include statement path \"%s\" is not a constant string".formatted(s.expression), + s.getRange())); + } + } +} 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 60% 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 index c6fa5a97..aba3a3a7 100644 --- a/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java @@ -1,15 +1,20 @@ 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; -public abstract class LintVisitor extends AbstractVisitor { +abstract class LintVisitor extends AbstractVisitor { + protected final LinterResults results; + + LintVisitor(LinterResults results) { + this.results = results; + } 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/LinterResult.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java new file mode 100644 index 00000000..f555456c --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java @@ -0,0 +1,52 @@ +package com.annimon.ownlang.parser.linters; + +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 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); + } + + 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 new file mode 100644 index 00000000..61044c6a --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -0,0 +1,60 @@ +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; +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 { + 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) { + if (mode == Mode.NONE) return 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); + 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(); // TODO special linter scope? + for (Visitor validator : validators) { + input.accept(validator); + ScopeHandler.resetScope(); + } + + results.sort(Comparator.comparing(LinterResult::severity)); + Console.println("Lint validation completed. %d results found!".formatted(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/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/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 91% 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 index b0f06af7..2982af28 100644 --- a/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; 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 98% 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 index 7a2b00c6..c69ad0b1 100644 --- a/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/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 90% 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 index 86c9e9ea..a2b20e92 100644 --- a/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; @@ -30,7 +29,7 @@ public class DeadCodeElimination extends OptimizationVisitor variableInfos = VariablesGrabber.getInfo(node); + final Map variableInfos = VariablesGrabber.getInfo(node, true); return node.accept(this, variableInfos); } @@ -99,7 +98,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); @@ -123,21 +122,20 @@ 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; } - 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 != null) { + result.add(new ExprStatement(node)); } } if (changed) { 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 88% 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 index 75004dfa..8230cb8a 100644 --- a/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/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/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..0f7d1cd9 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationStage.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.parser.optimization; + +import com.annimon.ownlang.parser.ast.Node; +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 Node perform(StagesData stagesData, Node 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 result; + } +} 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 73% 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 index 4311b410..29a90d35 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -5,23 +5,20 @@ 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 { @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 +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, (Expression) exprNode); + return new AssignmentExpression(s.operation, (Accessible) targetNode, exprNode); } return s; } @@ -44,7 +41,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 +50,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) { @@ -77,6 +72,33 @@ public Node visit(BreakStatement s, T t) { @Override public Node visit(ClassDeclarationStatement s, T t) { + 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; } @@ -85,7 +107,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 +117,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 +141,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 +150,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 +164,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 +174,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 +184,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; } @@ -174,7 +196,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; } @@ -188,7 +210,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 +218,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 +244,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 +254,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 LinkedHashMap<>(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); @@ -261,14 +283,14 @@ 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)) { 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; @@ -276,21 +298,22 @@ public Node visit(MatchExpression s, T t) { } } - if (pattern instanceof MatchExpression.TuplePattern) { - final MatchExpression.TuplePattern tuple = (MatchExpression.TuplePattern) pattern; - final List newValues = new ArrayList<>(tuple.values.size()); + if (pattern instanceof MatchExpression.TuplePattern tuple) { + 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; + for (Node value : tuple.values) { + if (value != MatchExpression.ANY) { + final Node node = value.accept(this, t); + if (node != value) { + valuesChanged = true; + 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; @@ -308,28 +331,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) { @@ -342,7 +365,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; } @@ -351,7 +374,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; } @@ -360,7 +383,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; } @@ -371,7 +394,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; } @@ -380,15 +403,14 @@ 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; } @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); @@ -407,17 +429,13 @@ 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; } @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; } @@ -427,7 +445,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; } @@ -436,25 +454,26 @@ 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.getValueExpr(); + final Node 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(), expr); } } return changed; } protected Statement consumeStatement(Node node) { - if (node instanceof Statement) { - return (Statement) node; + if (node instanceof Statement statement) { + return statement; } - return new ExprStatement((Expression) node); + return new ExprStatement(node); } } 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 59% 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 index 1ee16362..6553e91c 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -1,12 +1,11 @@ package com.annimon.ownlang.parser.optimization; 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> { @@ -32,17 +31,16 @@ 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); } 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); } @@ -50,30 +48,30 @@ 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); } @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; - t.put(variableName, variableInfo(t, variableName)); + if (pattern instanceof MatchExpression.VariablePattern varPattern) { + final String variableName = varPattern.variable; + grabVariableInfo(t, variableName); } } return super.visit(s, t); @@ -82,15 +80,13 @@ 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; - t.put(variableName, variableInfo(t, variableName)); + if (s.expr1 instanceof VariableExpression varExpr) { + grabVariableInfo(t, varExpr.name); } - 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)); + grabVariableInfo(t, variableName); } } } @@ -100,55 +96,31 @@ 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()) { - final VariableInfo var = variableInfo(t, entry.getKey()); + for (Map.Entry entry : s.loadConstants().entrySet()) { + final VariableInfo var = grabVariableInfo(t, entry.getKey()); var.value = entry.getValue(); - t.put(entry.getKey(), var); } - // Restore previous variables - Variables.variables().putAll(currentVariables); } return super.visit(s, t); } - private boolean canLoadConstants(Expression expression) { - if (expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) expression; - for (Expression expr : ae.elements) { - if (!isValue(expr)) { - return false; - } - } - return true; - } - return isValue(expression); - } - @Override protected boolean visit(Arguments in, Arguments out, Map t) { for (Argument argument : in) { - final String variableName = argument.getName(); - final VariableInfo var = variableInfo(t, variableName); + 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; }*/ - 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); @@ -156,6 +128,7 @@ private VariableInfo variableInfo(Map t, final String vari } else { var = new VariableInfo(); var.modifications = 1; + t.put(variableName, var); } return var; } 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 86% 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 index 6455abfa..5bc53ea7 100644 --- a/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); } } @@ -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 @@ -53,7 +58,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 +115,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 +136,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 +149,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); } } @@ -185,13 +190,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) { - st.expression.accept(this); + public void visit(UseStatement s) { + } } \ No newline at end of file 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 62% 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 index 96a25abe..7f03cb2e 100644 --- a/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,11 @@ public final class FunctionAdder extends AbstractVisitor { @Override public void visit(FunctionDefineStatement s) { super.visit(s); - s.execute(); + s.eval(); + } + + @Override + public void visit(ClassDeclarationStatement s) { + // skip, otherwise class methods will be visible outside of class } } 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 new file mode 100644 index 00000000..7b8e3dfa --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -0,0 +1,26 @@ +package com.annimon.ownlang.parser.visitors; + +import com.annimon.ownlang.parser.ast.Node; +import com.annimon.ownlang.parser.ast.UseStatement; +import java.util.HashSet; +import java.util.Set; + +public class ModuleDetector extends AbstractVisitor { + + private final Set modules; + + public ModuleDetector() { + modules = new HashSet<>(); + } + + public Set detect(Node s) { + s.accept(this); + return modules; + } + + @Override + public void visit(UseStatement s) { + modules.addAll(s.modules); + super.visit(s); + } +} 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 95% 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 index 7d3ef2e6..7fdadc80 100644 --- a/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); @@ -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; } @@ -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); @@ -441,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); @@ -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/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 84% 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 index 1c593b81..1b842a51 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VisitorUtils.java @@ -3,16 +3,20 @@ 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; 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; import java.io.IOException; import java.util.HashSet; +import java.util.List; import java.util.Set; public final class VisitorUtils { @@ -27,10 +31,13 @@ public static boolean isVariable(Node node) { return (node instanceof VariableExpression); } - public static Statement includeProgram(IncludeStatement s) { - if (!isValue(s)) return null; + public static Node includeProgram(IncludeStatement s) { + 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/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..ceb69b10 --- /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.Node; + +public class ExecutionStage implements Stage { + + @Override + 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 new file mode 100644 index 00000000..effb6234 --- /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.Node; +import com.annimon.ownlang.parser.visitors.FunctionAdder; + +public class FunctionAddingStage implements Stage { + + @Override + 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/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/ParserStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParserStage.java new file mode 100644 index 00000000..9b522af9 --- /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.Node; +import java.util.List; + +public class ParserStage implements Stage, Node> { + + public static final String TAG_PROGRAM = "program"; + public static final String TAG_HAS_PARSE_ERRORS = "hasParseErrors"; + + @Override + public Node perform(StagesData stagesData, List input) { + final Parser parser = new Parser(input); + final Node 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/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java similarity index 85% rename from src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerBenchmarkTest.java index b654fff2..8fa8421a 100644 --- a/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/LexerPositionsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java new file mode 100644 index 00000000..aac808e3 --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerPositionsTest.java @@ -0,0 +1,64 @@ +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" + 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.1 + """.stripIndent(); + List result = Lexer.tokenize(input); + + assertThat(result) + .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, 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 new file mode 100644 index 00000000..062cfc8e --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -0,0 +1,120 @@ +package com.annimon.ownlang.parser; + +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; +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.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 { + + public static Stream validData() { + return LexerValidDataProvider.getAll(); + } + + 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", "#")) + .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 testNumbers() { + String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF"; + List result = Lexer.tokenize(input); + 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 testDecimalNumbersExponent() { + String input = "4e+7 0.3E-19 2e0 5e0000000000000200 5E-000000089"; + List result = Lexer.tokenize(input); + assertThat(result) + .allMatch(t -> t.type().equals(DECIMAL_NUMBER)) + .extracting(Token::text) + .containsExactly("4e7", "0.3E-19", "2e0", "5e200", "5E-89"); + } + + @Test + public void testString() { + String input = "\"1\\\"2\""; + List result = Lexer.tokenize(input); + assertTokens(result, TEXT); + assertEquals("1\"2", result.get(0).text()); + } + + @Test + public void testEscapeString() { + String input = """ + "\\\\/\\\\" + """.stripIndent(); + List result = Lexer.tokenize(input); + assertTokens(result, TEXT); + assertEquals("\\/\\", result.get(0).text()); + } + + @Test + public void testEmptyString() { + String input = "\"\""; + List result = Lexer.tokenize(input); + assertTokens(result, TEXT); + assertEquals("", result.get(0).text()); + } + + @Test + public void testComments() { + String input = "// 1234 \n /* */ 123 /* \n 12345 \n\n\n */"; + List result = Lexer.tokenize(input); + assertTokens(result, NUMBER); + assertEquals("123", result.get(0).text()); + } + + @ParameterizedTest + @MethodSource("validData") + public void testValidInput(String name, String input, List tokenTypes) throws IOException { + List result = Lexer.tokenize(input); + assertThat(result) + .hasSize(tokenTypes.size()) + .extracting(Token::type) + .containsAll(tokenTypes); + } + + @ParameterizedTest + @MethodSource("invalidData") + public void testInvalidInput(String name, String input) throws IOException { + assertThatThrownBy(() -> Lexer.tokenize(input)) + .isInstanceOf(OwnLangParserException.class); + } + + private static void assertTokens(List result, TokenType... tokenTypes) { + assertThat(result) + .hasSize(tokenTypes.length) + .extracting(Token::type) + .containsExactly(tokenTypes); + } +} 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..891eab2e --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java @@ -0,0 +1,94 @@ +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 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(DECIMAL_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 hex numbers", + "0Xf7p6", + List.of(HEX_NUMBER, WORD)) + ); + } +} 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 94% rename from src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserBenchmarkTest.java index 68e3b93d..05c867e3 100644 --- a/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/src/test/java/com/annimon/ownlang/parser/ParserTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java similarity index 91% rename from src/test/java/com/annimon/ownlang/parser/ParserTest.java rename to ownlang-parser/src/test/java/com/annimon/ownlang/parser/ParserTest.java index 21e11bf9..2d8434a0 100644 --- a/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; @@ -32,10 +32,10 @@ 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(); - final Value actual = Variables.get("a"); + program.eval(); + final Value actual = ScopeHandler.getVariable("a"); try { assertEquals(expectedValue.asNumber(), actual.asNumber(), 0.001); } catch (NumberFormatException nfe) { @@ -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 new file mode 100644 index 00000000..01a459cf --- /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.Node; +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 Node 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.eval(); + } + } + + @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 new file mode 100644 index 00000000..d08787d4 --- /dev/null +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -0,0 +1,125 @@ +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.ClassDeclarationStatement; +import com.annimon.ownlang.parser.ast.FunctionDefineStatement; +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.*; +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.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import java.io.File; +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) + .map(InputSourceFile::new); + } + + @BeforeAll + 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); + return input; + }); + } + + 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) { + final StagesDataMap stagesData = new StagesDataMap(); + try { + testPipeline.perform(stagesData, inputSource); + } catch (OwnLangParserException ex) { + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + fail(inputSource + "\n" + error, ex); + } catch (Exception oae) { + Console.handleException(stagesData, Thread.currentThread(), oae); + fail(inputSource.toString(), oae); + } + } + + private static final Visitor testFunctionsExecutor = new AbstractVisitor() { + @Override + public void visit(FunctionDefineStatement s) { + if (s.name.startsWith("test")) { + try { + ScopeHandler.getFunction(s.name).execute(); + } catch (AssertionError err) { + throw new AssertionError(s.name + ": " + err.getMessage(), err); + } + } + } + + @Override + public void visit(ClassDeclarationStatement s) { + + } + }; +} 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/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 91% 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 index aaf4d3c4..ef65b332 100644 --- a/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/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 70% 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 index 87d9b00a..8d48a64d 100644 --- a/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,11 +13,16 @@ * @author aNNiMON */ public class VariableExpressionTest { + + @BeforeEach + void setUp() { + ScopeHandler.resetScope(); + } @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()); @@ -23,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/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/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") 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..4756c8d0 --- /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 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 75% rename from src/test/resources/expressions/foreachKeyValue.own rename to ownlang-parser/src/test/resources/expressions/foreachKeyValue.own index d0256c8a..e45b19b2 100644 --- a/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/src/test/resources/expressions/foreachValue.own b/ownlang-parser/src/test/resources/expressions/foreachValue.own similarity index 94% rename from src/test/resources/expressions/foreachValue.own rename to ownlang-parser/src/test/resources/expressions/foreachValue.own index 36942a06..f94d1ec0 100644 --- a/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 @@ -36,6 +36,6 @@ def testScope() { sum += v } assertEquals(6, sum) - assertEquals(45, v) + assertEquals(3, v) } 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 98% rename from src/test/resources/expressions/include.own rename to ownlang-parser/src/test/resources/expressions/include.own index 41eeb1eb..1ee041d6 100644 --- a/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/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/ownlang-parser/src/test/resources/expressions/matchExpression.own b/ownlang-parser/src/test/resources/expressions/matchExpression.own new file mode 100644 index 00000000..fbe892e5 --- /dev/null +++ b/ownlang-parser/src/test/resources/expressions/matchExpression.own @@ -0,0 +1,214 @@ +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 + ", []]" + case value: value +} + +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 [a]: 1 + case []: 0 + } + 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 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 + 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 + rest = 300 + result = match [1] { + case [head :: tail]: fail("Multi-array") + case []: fail("Empty array") + case rest: fail("Array should not be equal " + rest) + case [last]: assertEquals(1, last) + } + assertEquals(100, head) + assertEquals(200, tail) + assertEquals(300, rest) + 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 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) +} 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 97% rename from src/test/resources/modules/base64/base64.own rename to ownlang-parser/src/test/resources/modules/base64/base64.own index b9e11db5..f894c602 100644 --- a/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/src/test/resources/modules/date/compareDates.own b/ownlang-parser/src/test/resources/modules/date/compareDates.own similarity index 95% rename from src/test/resources/modules/date/compareDates.own rename to ownlang-parser/src/test/resources/modules/date/compareDates.own index 7db9f5db..be3a8a96 100644 --- a/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/src/test/resources/modules/date/dateFormat.own b/ownlang-parser/src/test/resources/modules/date/dateFormat.own similarity index 92% rename from src/test/resources/modules/date/dateFormat.own rename to ownlang-parser/src/test/resources/modules/date/dateFormat.own index 6437f7a1..fb1cc3fc 100644 --- a/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/src/test/resources/modules/date/dateParse.own b/ownlang-parser/src/test/resources/modules/date/dateParse.own similarity index 94% rename from src/test/resources/modules/date/dateParse.own rename to ownlang-parser/src/test/resources/modules/date/dateParse.own index 361cb9d1..e2ce6de4 100644 --- a/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/src/test/resources/modules/date/newDate.own b/ownlang-parser/src/test/resources/modules/date/newDate.own similarity index 95% rename from src/test/resources/modules/date/newDate.own rename to ownlang-parser/src/test/resources/modules/date/newDate.own index f33a3527..8cf3e04d 100644 --- a/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/src/test/resources/modules/files/files.own b/ownlang-parser/src/test/resources/modules/files/files.own similarity index 95% rename from src/test/resources/modules/files/files.own rename to ownlang-parser/src/test/resources/modules/files/files.own index 4f9c7e35..15fb49a3 100644 --- a/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/src/test/resources/modules/functional/chain.own b/ownlang-parser/src/test/resources/modules/functional/chain.own similarity index 92% rename from src/test/resources/modules/functional/chain.own rename to ownlang-parser/src/test/resources/modules/functional/chain.own index 19692093..1900d721 100644 --- a/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/src/test/resources/modules/functional/foreach.own b/ownlang-parser/src/test/resources/modules/functional/foreach.own similarity index 67% rename from src/test/resources/modules/functional/foreach.own rename to ownlang-parser/src/test/resources/modules/functional/foreach.own index 6d452f4c..24dc5674 100644 --- a/src/test/resources/modules/functional/foreach.own +++ b/ownlang-parser/src/test/resources/modules/functional/foreach.own @@ -1,6 +1,6 @@ -use ["std", "functional"] +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/groupby.own b/ownlang-parser/src/test/resources/modules/functional/groupby.own new file mode 100644 index 00000000..6330312b --- /dev/null +++ b/ownlang-parser/src/test/resources/modules/functional/groupby.own @@ -0,0 +1,28 @@ +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) + 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/src/test/resources/modules/functional/stream.own b/ownlang-parser/src/test/resources/modules/functional/stream.own similarity index 50% rename from src/test/resources/modules/functional/stream.own rename to ownlang-parser/src/test/resources/modules/functional/stream.own index b9478b0f..da98a294 100644 --- a/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] @@ -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()) @@ -43,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()) @@ -69,11 +88,69 @@ def testSorted() { assertEquals([-2,3,4,-5,6,6,-8], stream(data).sorted(def(a,b) = abs(a) - abs(b)).toArray()) } -def reverse(container) { - size = length(container) - result = newarray(size) - for i : range(size) { - result[size - i - 1] = container[i] - } - return result -} \ No newline at end of file +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 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])) +} + +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) +} diff --git a/src/test/resources/modules/gzip/gzipBytes.own b/ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own similarity index 99% rename from src/test/resources/modules/gzip/gzipBytes.own rename to ownlang-parser/src/test/resources/modules/gzip/gzipBytes.own index 37e57a03..9e2fd13e 100644 --- a/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/src/test/resources/modules/java/classes.own b/ownlang-parser/src/test/resources/modules/java/classes.own similarity index 98% rename from src/test/resources/modules/java/classes.own rename to ownlang-parser/src/test/resources/modules/java/classes.own index 0aaecbfa..45d33d38 100644 --- a/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/src/test/resources/modules/regex/match.own b/ownlang-parser/src/test/resources/modules/regex/match.own similarity index 96% rename from src/test/resources/modules/regex/match.own rename to ownlang-parser/src/test/resources/modules/regex/match.own index 24954fe5..deaa9dba 100644 --- a/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/src/test/resources/modules/regex/replaceCallback.own b/ownlang-parser/src/test/resources/modules/regex/replaceCallback.own similarity index 89% rename from src/test/resources/modules/regex/replaceCallback.own rename to ownlang-parser/src/test/resources/modules/regex/replaceCallback.own index cdfb5255..234ac4a3 100644 --- a/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/src/test/resources/modules/std/arraySplice.own b/ownlang-parser/src/test/resources/modules/std/arraySplice.own similarity index 99% rename from src/test/resources/modules/std/arraySplice.own rename to ownlang-parser/src/test/resources/modules/std/arraySplice.own index 0ec3d500..ec47aaf0 100644 --- a/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/src/test/resources/modules/std/default.own b/ownlang-parser/src/test/resources/modules/std/default.own similarity index 93% rename from src/test/resources/modules/std/default.own rename to ownlang-parser/src/test/resources/modules/std/default.own index 375aa333..2f9c67e8 100644 --- a/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/src/test/resources/modules/std/getBytes.own b/ownlang-parser/src/test/resources/modules/std/getBytes.own similarity index 94% rename from src/test/resources/modules/std/getBytes.own rename to ownlang-parser/src/test/resources/modules/std/getBytes.own index 48ba8fd8..dbc93f97 100644 --- a/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/src/test/resources/modules/std/indexOf.own b/ownlang-parser/src/test/resources/modules/std/indexOf.own similarity index 95% rename from src/test/resources/modules/std/indexOf.own rename to ownlang-parser/src/test/resources/modules/std/indexOf.own index 176a40bc..0fcd3940 100644 --- a/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/src/test/resources/modules/std/lastIndexOf.own b/ownlang-parser/src/test/resources/modules/std/lastIndexOf.own similarity index 96% rename from src/test/resources/modules/std/lastIndexOf.own rename to ownlang-parser/src/test/resources/modules/std/lastIndexOf.own index fb194084..75d9ddaf 100644 --- a/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/src/test/resources/modules/std/parseInt.own b/ownlang-parser/src/test/resources/modules/std/parseInt.own similarity index 96% rename from src/test/resources/modules/std/parseInt.own rename to ownlang-parser/src/test/resources/modules/std/parseInt.own index 7aa4a299..dd1cf0db 100644 --- a/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/src/test/resources/modules/std/parseLong.own b/ownlang-parser/src/test/resources/modules/std/parseLong.own similarity index 97% rename from src/test/resources/modules/std/parseLong.own rename to ownlang-parser/src/test/resources/modules/std/parseLong.own index 77d426d5..30ece4ca 100644 --- a/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/src/test/resources/modules/std/range.own b/ownlang-parser/src/test/resources/modules/std/range.own similarity index 98% rename from src/test/resources/modules/std/range.own rename to ownlang-parser/src/test/resources/modules/std/range.own index 85a60eda..cec28b84 100644 --- a/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/src/test/resources/modules/std/stringFromBytes.own b/ownlang-parser/src/test/resources/modules/std/stringFromBytes.own similarity index 94% rename from src/test/resources/modules/std/stringFromBytes.own rename to ownlang-parser/src/test/resources/modules/std/stringFromBytes.own index 6964c31b..3489bf1c 100644 --- a/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/src/test/resources/modules/std/stripMargin.own b/ownlang-parser/src/test/resources/modules/std/stripMargin.own similarity index 99% rename from src/test/resources/modules/std/stripMargin.own rename to ownlang-parser/src/test/resources/modules/std/stripMargin.own index e5ba625c..6d743614 100644 --- a/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/src/test/resources/modules/std/toHexString.own b/ownlang-parser/src/test/resources/modules/std/toHexString.own similarity index 94% rename from src/test/resources/modules/std/toHexString.own rename to ownlang-parser/src/test/resources/modules/std/toHexString.own index f557fc17..2de2d7cf 100644 --- a/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/src/test/resources/modules/std/try.own b/ownlang-parser/src/test/resources/modules/std/try.own similarity index 97% rename from src/test/resources/modules/std/try.own rename to ownlang-parser/src/test/resources/modules/std/try.own index bb9751f1..161ab5e6 100644 --- a/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/src/test/resources/modules/yaml/yamldecode.own b/ownlang-parser/src/test/resources/modules/yaml/yamldecode.own similarity index 70% rename from src/test/resources/modules/yaml/yamldecode.own rename to ownlang-parser/src/test/resources/modules/yaml/yamldecode.own index fc335af7..4deea1a4 100644 --- a/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 new file mode 100644 index 00000000..7ee56093 --- /dev/null +++ b/ownlang-parser/src/test/resources/modules/yaml/yamlencode.own @@ -0,0 +1,20 @@ +use std, yaml + +yml = yamlencode({ + "name": "Yaml Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } + }) + obj = yamldecode(yml) + + assertEquals("Yaml Example", obj.name) + assertEquals(1, obj.version) + assertEquals(4, length(obj.arrayData)) + assertEquals("value", obj.objectData.key) + assertEquals("1000", obj.objectData["10"]) \ No newline at end of file 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 98% rename from src/test/resources/other/stringFunctions.own rename to ownlang-parser/src/test/resources/other/stringFunctions.own index 7e0e60a3..38cad8d0 100644 --- a/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/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/ownlang-parser/src/test/resources/other/useStatementScope.own b/ownlang-parser/src/test/resources/other/useStatementScope.own new file mode 100644 index 00000000..f90a6e4e --- /dev/null +++ b/ownlang-parser/src/test/resources/other/useStatementScope.own @@ -0,0 +1,25 @@ +use std + +def testRegular() { + assertEquals("7f", "127".toHexString()) +} + +def testInCondition() { + if (true) { + use date + } + assertNotEquals("", newDate()) +} + +def testInScope() { + PI = "fallback" + assertEquals("fallback", PI) + + useMath() + assertNotEquals("fallback", PI) + assertEquals(3, abs(-3)) +} + +def useMath() { + use math +} \ No newline at end of file diff --git a/ownlang-utils/build.gradle b/ownlang-utils/build.gradle new file mode 100644 index 00000000..f8e2dbbf --- /dev/null +++ b/ownlang-utils/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'java-library' +} + +group = 'com.annimon' +version = versions.project + +dependencies { + api project(":ownlang-parser") + implementation "jline:jline:${versions.jline}" + + testImplementation platform("org.junit:junit-bom:${versions.junit}") + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} 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 98% rename from src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/OptimizationDumper.java index c4d7797c..d30001eb 100644 --- a/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/src/main/java/com/annimon/ownlang/utils/Repl.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java similarity index 68% 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..9d8aece7 100644 --- a/src/main/java/com/annimon/ownlang/utils/Repl.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Repl.java @@ -1,18 +1,14 @@ package com.annimon.ownlang.utils; import com.annimon.ownlang.Console; -import com.annimon.ownlang.Main; -import com.annimon.ownlang.exceptions.LexerException; +import com.annimon.ownlang.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.parser.Lexer; -import com.annimon.ownlang.parser.Parser; -import com.annimon.ownlang.parser.Token; -import com.annimon.ownlang.parser.TokenType; +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; import com.annimon.ownlang.utils.repl.OwnLangCompleter; @@ -36,17 +32,17 @@ 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, "", Pos.ZERO); 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(); 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; @@ -70,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); @@ -86,8 +82,8 @@ public static void main(String[] args) { continue; } } - program.execute(); - } catch (LexerException lex) { + program.eval(); + } catch (OwnLangParserException lex) { continue; } catch (StoppedException ex) { // skip @@ -119,9 +115,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"); @@ -135,7 +131,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() @@ -146,24 +142,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/src/main/java/com/annimon/ownlang/utils/Sandbox.java b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java similarity index 90% rename from src/main/java/com/annimon/ownlang/utils/Sandbox.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java index 7d23ed69..773882b5 100644 --- a/src/main/java/com/annimon/ownlang/utils/Sandbox.java +++ b/ownlang-utils/src/main/java/com/annimon/ownlang/utils/Sandbox.java @@ -8,7 +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.Statement; +import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.visitors.FunctionAdder; import java.io.File; import java.io.IOException; @@ -31,7 +31,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,13 +40,13 @@ public File fileInstance(String path) { program.accept(new FunctionAdder()); try { - program.execute(); + program.eval(); } catch (StoppedException ex) { // skip } catch (Exception ex) { // ownlang call stack to stdout System.out.format("%s in %s%n", ex.getMessage(), Thread.currentThread().getName()); - CallStack.getCalls().forEach(call -> System.out.format("\tat %s%n", call)); + System.out.println(CallStack.getFormattedCalls()); // java stack trace to stderr Console.handleException(Thread.currentThread(), ex); } 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 67% rename from src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java rename to ownlang-utils/src/main/java/com/annimon/ownlang/utils/TimeMeasurement.java index 2c1e4e28..ca39ca54 100644 --- a/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)); } } 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 78% 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 index e0a2f9e9..38e8abad 100644 --- a/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()); } } 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 diff --git a/program.own b/program.own index 239b8394..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,10 +240,7 @@ println 1 :: 2 :: 3 println "\u042a" -include "visitor.own" - - -use "date" +use date d = newDate(); println d println formatDate(d) diff --git a/settings.gradle b/settings.gradle index b699976e..68c60705 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,14 @@ rootProject.name = 'OwnLang' + +include 'ownlang-core' +include 'ownlang-parser' +include 'ownlang-desktop' +include 'ownlang-utils' +include 'docs' + +final def modules = ['main', 'canvasfx', 'server'] + +for (final def module in modules) { + include "modules:$module" + findProject(":modules:$module")?.name = module +} diff --git a/src/main/java/com/annimon/ownlang/Main.java b/src/main/java/com/annimon/ownlang/Main.java deleted file mode 100644 index 470422da..00000000 --- a/src/main/java/com/annimon/ownlang/Main.java +++ /dev/null @@ -1,235 +0,0 @@ -package com.annimon.ownlang; - -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.ast.Statement; -import com.annimon.ownlang.parser.visitors.FunctionAdder; -import com.annimon.ownlang.utils.Repl; -import com.annimon.ownlang.utils.Sandbox; -import com.annimon.ownlang.utils.TimeMeasurement; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; - -/** - * @author aNNiMON - */ -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 { - runDefault(); - } catch (IOException ioe) { - printUsage(); - } - return; - } - - final RunOptions options = new RunOptions(); - String input = null; - for (int i = 0; i < args.length; i++) { - switch (args[i]) { - case "-a": - case "--showast": - options.showAst = true; - break; - - case "-b": - case "--beautify": - options.beautifyMode = true; - break; - - case "-t": - case "--showtokens": - options.showTokens = true; - break; - - case "-m": - case "--showtime": - options.showMeasurements = true; - break; - - case "-o": - case "--optimize": - if (i + 1 < args.length) { - try { - options.optimizationLevel = Integer.parseInt(args[i + 1]); - } catch (NumberFormatException nfe) { - options.optimizationLevel = 2; - } - i++; - } else { - options.optimizationLevel = 2; - } - break; - - case "-r": - case "--repl": - Repl.main(new String[0]); - return; - - case "-l": - case "--lint": - options.lintMode = true; - return; - - case "-f": - case "--file": - if (i + 1 < args.length) { - input = SourceLoader.readSource(args[i + 1]); - createOwnLangArgs(args, i + 2); - i++; - } - break; - - case "--sandbox": - createOwnLangArgs(args, i + 1); - String[] newArgs = new String[ownlangArgs.length]; - System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length); - Sandbox.main(newArgs); - return; - - default: - if (input == null) { - input = args[i]; - createOwnLangArgs(args, i + 1); - } - break; - } - } - if (input == null) { - throw new IllegalArgumentException("Empty input"); - } - if (options.beautifyMode) { - 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); - } - - private static void printUsage() { - System.out.println("OwnLang version " + VERSION + "\n\n" + - "Usage: ownlang [options]\n" + - " options:\n" + - " -f, --file [input] Run program file. Required.\n" + - " -r, --repl Enter to a REPL mode\n" + - " -l, --lint Find bugs in code\n" + - " -o N, --optimize N Perform optimization with N passes\n" + - " -b, --beautify Beautify source code\n" + - " -a, --showast Show AST of program\n" + - " -t, --showtokens Show lexical tokens\n" + - " -m, --showtime Show elapsed time of parsing and execution"); - } - - private static void createOwnLangArgs(String[] javaArgs, int index) { - if (index >= javaArgs.length) return; - ownlangArgs = new String[javaArgs.length - index]; - System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length); - } - - 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.toString()); - } - 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()); - try { - measurement.start("Execution time"); - program.execute(); - } catch (StoppedException ex) { - // skip - } catch (Exception ex) { - Console.handleException(Thread.currentThread(), ex); - } finally { - if (options.showMeasurements) { - measurement.stop("Execution time"); - System.out.println("======================"); - System.out.println(measurement.summary(TimeUnit.MILLISECONDS, true)); - } - } - } - - 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; - } - - void validate() { - if (lintMode) { - showTokens = false; - showAst = false; - showMeasurements = false; - optimizationLevel = 0; - } - } - } -} diff --git a/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java b/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java deleted file mode 100644 index 165f8631..00000000 --- a/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.annimon.ownlang.exceptions; - -public final class ArgumentsMismatchException extends RuntimeException { - - public ArgumentsMismatchException() { - } - - public ArgumentsMismatchException(String message) { - super(message); - } -} diff --git a/src/main/java/com/annimon/ownlang/exceptions/LexerException.java b/src/main/java/com/annimon/ownlang/exceptions/LexerException.java deleted file mode 100644 index 26689e14..00000000 --- a/src/main/java/com/annimon/ownlang/exceptions/LexerException.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.annimon.ownlang.exceptions; - -/** - * - * @author aNNiMON - */ -public final class LexerException extends RuntimeException { - - public LexerException(String message) { - super(message); - } - - public LexerException(int row, int col, String message) { - super("["+row+":"+col+"] " + message); - } -} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/src/main/java/com/annimon/ownlang/exceptions/ParseException.java deleted file mode 100644 index ed2d47e4..00000000 --- a/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.annimon.ownlang.exceptions; - -/** - * - * @author aNNiMON - */ -public final class ParseException extends RuntimeException { - - public ParseException() { - super(); - } - - public ParseException(String string) { - super(string); - } -} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java b/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java deleted file mode 100644 index 8ce24c33..00000000 --- a/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 { - - -} diff --git a/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java b/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java deleted file mode 100644 index 40c29b9d..00000000 --- a/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.annimon.ownlang.exceptions; - -public final class UnknownClassException extends RuntimeException { - - private final String className; - - public UnknownClassException(String name) { - super("Unknown class " + name); - this.className = name; - } - - public String getClassName() { - return className; - } -} diff --git a/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java b/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java deleted file mode 100644 index 0981de17..00000000 --- a/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.annimon.ownlang.exceptions; - -public final class VariableDoesNotExistsException extends RuntimeException { - - private final String variable; - - public VariableDoesNotExistsException(String variable) { - super("Variable " + variable + " does not exists"); - this.variable = variable; - } - - public String getVariable() { - return variable; - } -} diff --git a/src/main/java/com/annimon/ownlang/lib/CallStack.java b/src/main/java/com/annimon/ownlang/lib/CallStack.java deleted file mode 100644 index c7517bd3..00000000 --- a/src/main/java/com/annimon/ownlang/lib/CallStack.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.annimon.ownlang.lib; - -import java.util.Deque; -import java.util.concurrent.ConcurrentLinkedDeque; - -public final class CallStack { - - private static final Deque calls = new ConcurrentLinkedDeque<>(); - - private CallStack() { } - - public static synchronized void clear() { - calls.clear(); - } - - public static synchronized void enter(String name, Function function) { - calls.push(new CallInfo(name, function)); - } - - public static synchronized void exit() { - calls.pop(); - } - - 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; - } - - @Override - public String toString() { - return String.format("%s: %s", name, function.toString().trim()); - } - } -} diff --git a/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java b/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java deleted file mode 100644 index b079ef3d..00000000 --- a/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/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/src/main/java/com/annimon/ownlang/lib/ClassMethod.java deleted file mode 100644 index a5950398..00000000 --- a/src/main/java/com/annimon/ownlang/lib/ClassMethod.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.annimon.ownlang.lib; - -import com.annimon.ownlang.parser.ast.Arguments; -import com.annimon.ownlang.parser.ast.Statement; - -public class ClassMethod extends UserDefinedFunction { - - public final ClassInstanceValue classInstance; - - public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance) { - super(arguments, body); - this.classInstance = classInstance; - } - - @Override - public Value execute(Value[] values) { - Variables.push(); - Variables.define("this", classInstance.getThisMap()); - - try { - return super.execute(values); - } finally { - Variables.pop(); - } - } -} diff --git a/src/main/java/com/annimon/ownlang/lib/Classes.java b/src/main/java/com/annimon/ownlang/lib/Classes.java deleted file mode 100644 index 488d5466..00000000 --- a/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/src/main/java/com/annimon/ownlang/lib/Functions.java b/src/main/java/com/annimon/ownlang/lib/Functions.java deleted file mode 100644 index f43d0297..00000000 --- a/src/main/java/com/annimon/ownlang/lib/Functions.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.annimon.ownlang.lib; - -import com.annimon.ownlang.exceptions.UnknownFunctionException; -import java.util.HashMap; -import java.util.Map; - -/** - * - * @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; - } - - public static boolean isExists(String key) { - return functions.containsKey(key); - } - - public static Function get(String key) { - if (!isExists(key)) throw new UnknownFunctionException(key); - return functions.get(key); - } - - public static void set(String key, Function function) { - functions.put(key, function); - } -} diff --git a/src/main/java/com/annimon/ownlang/lib/Variables.java b/src/main/java/com/annimon/ownlang/lib/Variables.java deleted file mode 100644 index 4033fa33..00000000 --- a/src/main/java/com/annimon/ownlang/lib/Variables.java +++ /dev/null @@ -1,118 +0,0 @@ -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; - } - } - } - - public static boolean isExists(String key) { - synchronized (lock) { - return findScope(key).isFound; - } - } - - 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 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 remove(String key) { - synchronized (lock) { - findScope(key).scope.variables.remove(key); - } - } - - /* - * Find scope where variable exists. - */ - 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; - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/Module.java b/src/main/java/com/annimon/ownlang/modules/Module.java deleted file mode 100644 index cda5ca4f..00000000 --- a/src/main/java/com/annimon/ownlang/modules/Module.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.annimon.ownlang.modules; - -/** - * - * @author aNNiMON - */ -public interface Module { - - void init(); -} diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional.java b/src/main/java/com/annimon/ownlang/modules/functional/functional.java deleted file mode 100644 index f75d0080..00000000 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional.java +++ /dev/null @@ -1,34 +0,0 @@ -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.modules.Module; - -/** - * - * @author aNNiMON - */ -public final class functional implements Module { - - public static void initConstants() { - Variables.define("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()); - - Functions.set("chain", new functional_chain()); - Functions.set("stream", new functional_stream()); - Functions.set("combine", new functional_combine()); - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java deleted file mode 100644 index fc743839..00000000 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_foreach.java +++ /dev/null @@ -1,62 +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 { - - 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; - } - - 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/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java deleted file mode 100644 index dbf429ab..00000000 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_reduce.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.annimon.ownlang.modules.functional; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.ValueUtils; -import java.util.Map; - -public final class functional_reduce implements Function { - - @Override - public Value execute(Value... args) { - Arguments.check(3, args.length); - - final Value container = args[0]; - final Value identity = args[1]; - final Function accumulator = ValueUtils.consumeFunction(args[2], 2); - if (container.type() == Types.ARRAY) { - Value result = identity; - final ArrayValue array = (ArrayValue) container; - for (Value element : array) { - result = accumulator.execute(result, element); - } - return result; - } - if (container.type() == Types.MAP) { - Value result = identity; - final MapValue map = (MapValue) container; - for (Map.Entry element : map) { - result = accumulator.execute(result, element.getKey(), element.getValue()); - } - return result; - } - throw new TypeException("Invalid first argument. Array or map expected"); - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java b/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java deleted file mode 100644 index f0888aa5..00000000 --- a/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java +++ /dev/null @@ -1,141 +0,0 @@ -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 { - - @Override - public Value execute(Value[] args) { - Arguments.checkAtLeast(1, args.length); - - if (args.length > 1) { - return new StreamValue(new ArrayValue(args)); - } - - final Value value = args[0]; - switch (value.type()) { - case Types.MAP: - return new StreamValue(((MapValue) value).toPairs()); - case Types.ARRAY: - return new StreamValue((ArrayValue) value); - default: - throw new TypeException("Invalid argument. Array or map expected"); - } - } - - private static class StreamValue extends MapValue { - - private final ArrayValue container; - - public StreamValue(ArrayValue container) { - super(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/src/main/java/com/annimon/ownlang/modules/http/http.java b/src/main/java/com/annimon/ownlang/modules/http/http.java deleted file mode 100644 index b8d22cad..00000000 --- a/src/main/java/com/annimon/ownlang/modules/http/http.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.annimon.ownlang.modules.http; - -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.modules.Module; - -/** - * - * @author aNNiMON - */ -public final class http implements Module { - - public static void initConstants() { - } - - @Override - public void init() { - initConstants(); - Functions.set("urlencode", new http_urlencode()); - Functions.set("http", new http_http()); - Functions.set("download", new http_download()); - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/json/json.java b/src/main/java/com/annimon/ownlang/modules/json/json.java deleted file mode 100644 index 1ff77282..00000000 --- a/src/main/java/com/annimon/ownlang/modules/json/json.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.annimon.ownlang.modules.json; - -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.modules.Module; - -/** - * - * @author aNNiMON - */ -public final class json implements Module { - - public static void initConstants() { - } - - @Override - public void init() { - initConstants(); - Functions.set("jsonencode", new json_encode()); - Functions.set("jsondecode", new json_decode()); - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/std/std.java b/src/main/java/com/annimon/ownlang/modules/std/std.java deleted file mode 100644 index eca4d2bf..00000000 --- a/src/main/java/com/annimon/ownlang/modules/std/std.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.annimon.ownlang.modules.std; - -import com.annimon.ownlang.Main; -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.modules.Module; - -/** - * - * @author aNNiMON - */ -public final class std implements Module { - - public static void initConstants() { - MapValue ownlang = new MapValue(5); - ownlang.set("PLATFORM", new StringValue("desktop")); - ownlang.set("VERSION", new StringValue(Main.VERSION)); - ownlang.set("VERSION_MAJOR", NumberValue.of(Main.VERSION_MAJOR)); - ownlang.set("VERSION_MINOR", NumberValue.of(Main.VERSION_MINOR)); - ownlang.set("VERSION_PATCH", NumberValue.of(Main.VERSION_PATCH)); - Variables.define("OwnLang", ownlang); - } - - @Override - public void init() { - initConstants(); - Variables.define("ARGS", ArrayValue.of(Main.getOwnlangArgs())); // is not constant - Functions.set("echo", new std_echo()); - Functions.set("readln", new std_readln()); - Functions.set("length", new std_length()); - 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()); - - // Numbers - Functions.set("toHexString", NumberFunctions::toHexString); - - // String - Functions.set("getBytes", StringFunctions::getBytes); - Functions.set("sprintf", new std_sprintf()); - Functions.set("split", new std_split()); - Functions.set("indexOf", new std_indexof()); - 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); - - // 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); - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_default.java b/src/main/java/com/annimon/ownlang/modules/std/std_default.java deleted file mode 100644 index 68819bf8..00000000 --- a/src/main/java/com/annimon/ownlang/modules/std/std_default.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.annimon.ownlang.modules.std; - -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; - -public final class std_default implements Function { - - @Override - public Value execute(Value... args) { - Arguments.check(2, args.length); - if (isEmpty(args[0])) { - return args[1]; - } - return args[0]; - } - - private boolean isEmpty(Value value) { - if (value == null || value.raw() == null) { - return true; - } - switch (value.type()) { - case Types.NUMBER: - return (value.asInt() == 0); - case Types.STRING: - return (value.asString().isEmpty()); - case Types.ARRAY: - return ((ArrayValue) value).size() == 0; - case Types.MAP: - return ((MapValue) value).size() == 0; - default: - return false; - } - } -} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_join.java b/src/main/java/com/annimon/ownlang/modules/std/std_join.java deleted file mode 100644 index 3696fb14..00000000 --- a/src/main/java/com/annimon/ownlang/modules/std/std_join.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.annimon.ownlang.modules.std; - -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.Arguments; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.Function; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; - -public final class std_join implements Function { - - @Override - 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"); - } - - 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"); - } - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_length.java b/src/main/java/com/annimon/ownlang/modules/std/std_length.java deleted file mode 100644 index bdd7a210..00000000 --- a/src/main/java/com/annimon/ownlang/modules/std/std_length.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.annimon.ownlang.modules.std; - -import com.annimon.ownlang.lib.*; - -public 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(); - if (func instanceof UserDefinedFunction) { - length = ((UserDefinedFunction) func).getArgsCount(); - } else { - length = 0; - } - break; - default: - length = 0; - - } - return NumberValue.of(length); - } -} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/std/std_time.java b/src/main/java/com/annimon/ownlang/modules/std/std_time.java deleted file mode 100644 index 3a5e81ce..00000000 --- a/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/src/main/java/com/annimon/ownlang/modules/types/types.java b/src/main/java/com/annimon/ownlang/modules/types/types.java deleted file mode 100644 index fcdc1388..00000000 --- a/src/main/java/com/annimon/ownlang/modules/types/types.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.annimon.ownlang.modules.types; - -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.modules.Module; - -/** - * - * @author aNNiMON - */ -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)); - } - - @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())); - - 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())); - } -} diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java deleted file mode 100644 index 97ddf2f2..00000000 --- a/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.annimon.ownlang.modules.yaml; - -import com.annimon.ownlang.lib.*; -import com.annimon.ownlang.modules.Module; - -/** - * - * @author aNNiMON - */ -public final class yaml implements Module { - - @Override - public void init() { - Functions.set("yamlencode", new yaml_encode()); - Functions.set("yamldecode", new yaml_decode()); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/Lexer.java b/src/main/java/com/annimon/ownlang/parser/Lexer.java deleted file mode 100644 index 4bed5181..00000000 --- a/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ /dev/null @@ -1,366 +0,0 @@ -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; - -/** - * - * @author aNNiMON - */ -public final class Lexer { - - public static List tokenize(String input) { - return new Lexer(input).tokenize(); - } - - private static final String OPERATOR_CHARS = "+-*/%()[]{}=<>!&|.,^~?:"; - - private static final Map OPERATORS; - 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); - - 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.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.COLONCOLON); - - OPERATORS.put("&&", TokenType.AMPAMP); - OPERATORS.put("||", TokenType.BARBAR); - - 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); - } - - 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); - } - - public static Set getKeywords() { - return KEYWORDS.keySet(); - } - - private final String input; - private final int length; - - private final List tokens; - private final StringBuilder buffer; - - private int pos; - private int row, col; - - public Lexer(String input) { - this.input = input; - length = input.length(); - - tokens = new ArrayList<>(); - buffer = new StringBuilder(); - row = col = 1; - } - - public List tokenize() { - while (pos < length) { - final char current = peek(0); - if (Character.isDigit(current)) tokenizeNumber(); - else if (isOwnLangIdentifierStart(current)) tokenizeWord(); - else if (current == '`') tokenizeExtendedWord(); - else if (current == '"') tokenizeText(); - else if (current == '#') { - next(); - tokenizeHexNumber(1); - } - else if (OPERATOR_CHARS.indexOf(current) != -1) { - tokenizeOperator(); - } else { - // whitespaces - next(); - } - } - return tokens; - } - - private void tokenizeNumber() { - clearBuffer(); - char current = peek(0); - if (current == '0' && (peek(1) == 'x' || (peek(1) == 'X'))) { - next(); - next(); - tokenizeHexNumber(2); - return; - } - while (true) { - if (current == '.') { - if (buffer.indexOf(".") != -1) throw error("Invalid float number"); - } else if (!Character.isDigit(current)) { - break; - } - buffer.append(current); - current = next(); - } - addToken(TokenType.NUMBER, buffer.toString()); - } - - private void tokenizeHexNumber(int skipped) { - clearBuffer(); - char current = peek(0); - while (isHexNumber(current) || (current == '_')) { - if (current != '_') { - // allow _ symbol - buffer.append(current); - } - current = next(); - } - final int length = buffer.length(); - if (length > 0) { - addToken(TokenType.HEX_NUMBER, buffer.toString()); - } - } - - private static boolean isHexNumber(char current) { - return Character.isDigit(current) - || ('a' <= current && current <= 'f') - || ('A' <= current && current <= 'F'); - } - - private void tokenizeOperator() { - char current = peek(0); - if (current == '/') { - if (peek(1) == '/') { - next(); - next(); - tokenizeComment(); - return; - } else if (peek(1) == '*') { - next(); - next(); - tokenizeMultilineComment(); - return; - } - } - clearBuffer(); - while (true) { - final String text = buffer.toString(); - if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) { - addToken(OPERATORS.get(text)); - return; - } - buffer.append(current); - current = next(); - } - } - - private void tokenizeWord() { - clearBuffer(); - buffer.append(peek(0)); - char current = next(); - while (true) { - if (!isOwnLangIdentifierPart(current)) { - break; - } - buffer.append(current); - current = next(); - } - - final String word = buffer.toString(); - if (KEYWORDS.containsKey(word)) { - addToken(KEYWORDS.get(word)); - } else { - addToken(TokenType.WORD, word); - } - } - - private void tokenizeExtendedWord() { - next();// skip ` - clearBuffer(); - char current = peek(0); - while (true) { - if (current == '`') break; - if (current == '\0') throw error("Reached end of file while parsing extended word."); - if (current == '\n' || current == '\r') throw error("Reached end of line while parsing extended word."); - buffer.append(current); - current = next(); - } - next(); // skip closing ` - addToken(TokenType.WORD, buffer.toString()); - } - - private void tokenizeText() { - next();// skip " - clearBuffer(); - char current = peek(0); - while (true) { - if (current == '\\') { - current = next(); - switch (current) { - case '"': current = next(); buffer.append('"'); continue; - case '0': current = next(); buffer.append('\0'); continue; - case 'b': current = next(); buffer.append('\b'); continue; - case 'f': current = next(); buffer.append('\f'); continue; - case 'n': current = next(); buffer.append('\n'); continue; - case 'r': current = next(); buffer.append('\r'); continue; - case 't': current = next(); buffer.append('\t'); continue; - case 'u': // http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.3 - int rollbackPosition = pos; - while (current == 'u') current = next(); - int escapedValue = 0; - for (int i = 12; i >= 0 && escapedValue != -1; i -= 4) { - if (isHexNumber(current)) { - escapedValue |= (Character.digit(current, 16) << i); - } else { - escapedValue = -1; - } - current = next(); - } - if (escapedValue >= 0) { - buffer.append((char) escapedValue); - } else { - // rollback - buffer.append("\\u"); - pos = rollbackPosition; - } - continue; - } - buffer.append('\\'); - continue; - } - if (current == '"') break; - if (current == '\0') throw error("Reached end of file while parsing text string."); - buffer.append(current); - current = next(); - } - next(); // skip closing " - - addToken(TokenType.TEXT, buffer.toString()); - } - - private void tokenizeComment() { - char current = peek(0); - while ("\r\n\0".indexOf(current) == -1) { - current = next(); - } - } - - private void tokenizeMultilineComment() { - char current = peek(0); - while (true) { - if (current == '*' && peek(1) == '/') break; - if (current == '\0') throw error("Reached end of file while parsing multiline comment"); - current = next(); - } - next(); // * - next(); // / - } - - private boolean isOwnLangIdentifierStart(char current) { - return (Character.isLetter(current) || (current == '_') || (current == '$')); - } - - private boolean isOwnLangIdentifierPart(char current) { - return (Character.isLetterOrDigit(current) || (current == '_') || (current == '$')); - } - - private void clearBuffer() { - buffer.setLength(0); - } - - private char next() { - pos++; - final char result = peek(0); - if (result == '\n') { - row++; - col = 1; - } else col++; - return result; - } - - private char peek(int relativePosition) { - final int position = pos + relativePosition; - if (position >= length) return '\0'; - return input.charAt(position); - } - - private void addToken(TokenType type) { - addToken(type, ""); - } - - private void addToken(TokenType type, String text) { - tokens.add(new Token(type, text, row, col)); - } - - private LexerException error(String text) { - return new LexerException(row, col, text); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/Linter.java b/src/main/java/com/annimon/ownlang/parser/Linter.java deleted file mode 100644 index 999236a4..00000000 --- a/src/main/java/com/annimon/ownlang/parser/Linter.java +++ /dev/null @@ -1,42 +0,0 @@ -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.parser.ast.Statement; -import com.annimon.ownlang.parser.ast.Visitor; - -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 UseWithNonStringValueValidator(), - new AssignValidator(), - new DefaultFunctionsOverrideValidator() - }; - resetState(); - for (Visitor validator : validators) { - program.accept(validator); - resetState(); - } - Console.println("Lint validation complete!"); - } - - private void resetState() { - Variables.clear(); - Functions.getFunctions().clear(); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/Optimizer.java b/src/main/java/com/annimon/ownlang/parser/Optimizer.java deleted file mode 100644 index a4f2ff61..00000000 --- a/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/src/main/java/com/annimon/ownlang/parser/ParseError.java b/src/main/java/com/annimon/ownlang/parser/ParseError.java deleted file mode 100644 index 7506552a..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ParseError.java +++ /dev/null @@ -1,25 +0,0 @@ -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; - } - - @Override - public String toString() { - return "ParseError on line " + line + ": " + exception.getMessage(); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/Token.java b/src/main/java/com/annimon/ownlang/parser/Token.java deleted file mode 100644 index 3d4f4e7d..00000000 --- a/src/main/java/com/annimon/ownlang/parser/Token.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.annimon.ownlang.parser; - -/** - * - * @author aNNiMON - */ -public final class Token { - - 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 + "]"; - } - - @Override - public String toString() { - return type.name() + " " + position() + " " + text; - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Argument.java b/src/main/java/com/annimon/ownlang/parser/ast/Argument.java deleted file mode 100644 index d900fa12..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ast/Argument.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.annimon.ownlang.parser.ast; - -public final class Argument { - - private final String name; - private final 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/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java deleted file mode 100644 index 6ee83055..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.annimon.ownlang.parser.ast; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.*; -import java.util.List; - -/** - * - * @author aNNiMON - */ -public final class ContainerAccessExpression implements Expression, Accessible { - - public final Expression root; - public final List indices; - private boolean rootIsVariable; - - public ContainerAccessExpression(String variable, List indices) { - this(new VariableExpression(variable), indices); - } - - public ContainerAccessExpression(Expression root, List indices) { - rootIsVariable = root instanceof VariableExpression; - this.root = root; - this.indices = indices; - } - - public boolean rootIsVariable() { - return rootIsVariable; - } - - public Expression getRoot() { - return root; - } - - @Override - public Value eval() { - return get(); - } - - @Override - 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())); - } - } - - @Override - public Value set(Value value) { - final Value container = getContainer(); - final Value lastIndex = lastIndex(); - switch (container.type()) { - case Types.ARRAY: - final int arrayIndex = lastIndex.asInt(); - ((ArrayValue) container).set(arrayIndex, 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()); - } - } - - public Value getContainer() { - Value container = root.eval(); - 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"); - } - } - return container; - } - - public Value lastIndex() { - return index(indices.size() - 1); - } - - private Value index(int index) { - return indices.get(index).eval(); - } - - public MapValue consumeMap(Value value) { - if (value.type() != Types.MAP) { - throw new TypeException("Map expected"); - } - return (MapValue) value; - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public R accept(ResultVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public String toString() { - return root.toString() + indices; - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/Expression.java b/src/main/java/com/annimon/ownlang/parser/ast/Expression.java deleted file mode 100644 index 98865f52..00000000 --- a/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/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java deleted file mode 100644 index eb4c5c43..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.annimon.ownlang.parser.ast; - -import com.annimon.ownlang.exceptions.ArgumentsMismatchException; -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; -import com.annimon.ownlang.exceptions.UnknownFunctionException; -import com.annimon.ownlang.lib.*; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * - * @author aNNiMON - */ -public final class FunctionalExpression extends InterruptableNode implements Expression, Statement { - - public final Expression functionExpr; - public final List arguments; - - public FunctionalExpression(Expression functionExpr) { - this.functionExpr = functionExpr; - arguments = new ArrayList<>(); - } - - public void addArgument(Expression arg) { - arguments.add(arg); - } - - @Override - public void execute() { - eval(); - } - - @Override - public Value eval() { - super.interruptionCheck(); - final int size = arguments.size(); - final Value[] values = new Value[size]; - for (int i = 0; i < size; i++) { - values[i] = arguments.get(i).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); - } - } - - private Function consumeFunction(Expression expr) { - try { - final Value value = expr.eval(); - if (value.type() == Types.FUNCTION) { - return ((FunctionValue) value).getValue(); - } - return getFunction(value.asString()); - } catch (VariableDoesNotExistsException ex) { - return getFunction(ex.getVariable()); - } - } - - private Function getFunction(String key) { - if (Functions.isExists(key)) { - return Functions.get(key); - } - if (Variables.isExists(key)) { - final Value variable = Variables.get(key); - if (variable.type() == Types.FUNCTION) { - return ((FunctionValue)variable).getValue(); - } - } - throw new UnknownFunctionException(key); - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public R accept(ResultVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - if (functionExpr instanceof ValueExpression && ((ValueExpression)functionExpr).value.type() == Types.STRING) { - sb.append(((ValueExpression)functionExpr).value.asString()).append('('); - } else { - sb.append(functionExpr).append('('); - } - final Iterator it = arguments.iterator(); - if (it.hasNext()) { - sb.append(it.next()); - while (it.hasNext()) { - sb.append(", ").append(it.next()); - } - } - sb.append(')'); - return sb.toString(); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java deleted file mode 100644 index 0c131292..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ast/IncludeStatement.java +++ /dev/null @@ -1,63 +0,0 @@ -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; -import com.annimon.ownlang.parser.Token; -import com.annimon.ownlang.parser.visitors.FunctionAdder; -import java.io.IOException; -import java.util.List; - -/** - * - * @author aNNiMON - */ -public final class IncludeStatement extends InterruptableNode implements Statement { - - public final Expression expression; - - public IncludeStatement(Expression expression) { - this.expression = expression; - } - - @Override - public void execute() { - super.interruptionCheck(); - try { - final Statement program = loadProgram(expression.eval().asString()); - if (program != null) { - program.accept(new FunctionAdder()); - program.execute(); - } - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - 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; - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public R accept(ResultVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public String toString() { - return "include " + expression; - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java deleted file mode 100644 index 6e15ad03..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ast/ObjectCreationExpression.java +++ /dev/null @@ -1,81 +0,0 @@ -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; - -public final class ObjectCreationExpression implements Expression { - - public final String className; - public final List constructorArguments; - - public ObjectCreationExpression(String className, List constructorArguments) { - this.className = className; - this.constructorArguments = constructorArguments; - } - - @Override - public Value eval() { - final ClassDeclarationStatement cd = ClassDeclarations.get(className); - 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); - for (AssignmentExpression f : cd.fields) { - // TODO check only variable assignments - final String fieldName = ((VariableExpression) f.target).name; - instance.addField(fieldName, f.eval()); - } - for (FunctionDefineStatement m : cd.methods) { - instance.addMethod(m.name, new ClassMethod(m.arguments, m.body, instance)); - } - - // Call a constructor - 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(); - } - return ctorArgs; - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public R accept(ResultVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("new ").append(className).append(' '); - final Iterator it = constructorArguments.iterator(); - if (it.hasNext()) { - sb.append(it.next()); - while (it.hasNext()) { - sb.append(", ").append(it.next()); - } - } - sb.append(')'); - return sb.toString(); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java b/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java deleted file mode 100644 index 79f4e401..00000000 --- a/src/main/java/com/annimon/ownlang/parser/ast/UseStatement.java +++ /dev/null @@ -1,96 +0,0 @@ -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; - -/** - * - * @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 Expression expression; - - public UseStatement(Expression expression) { - this.expression = expression; - } - - @Override - public void execute() { - super.interruptionCheck(); - final Value value = expression.eval(); - switch (value.type()) { - case Types.ARRAY: - for (Value module : ((ArrayValue) value)) { - loadModule(module.asString()); - } - break; - case Types.STRING: - loadModule(value.asString()); - break; - default: - throw typeException(value); - } - } - - private void loadModule(String name) { - try { - final Module module = (Module) Class.forName(String.format(PACKAGE, name, name)).newInstance(); - module.init(); - } catch (Exception ex) { - throw new RuntimeException("Unable to load module " + name, ex); - } - } - - public void loadConstants() { - if (expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) expression; - for (Expression expr : ae.elements) { - loadConstants(expr.eval().asString()); - } - } - if (expression instanceof ValueExpression) { - ValueExpression ve = (ValueExpression) expression; - loadConstants(ve.value.asString()); - } - } - - private TypeException typeException(Value value) { - return new TypeException("Array or string required in 'use' statement, " + - "got " + Types.typeToString(value.type()) + " " + value); - } - - private void loadConstants(String moduleName) { - try { - final Class moduleClass = Class.forName(String.format(PACKAGE, moduleName, moduleName)); - final Method method = moduleClass.getMethod(INIT_CONSTANTS_METHOD); - if (method != null) { - method.invoke(this); - } - } catch (Exception ex) { - // ignore - } - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public R accept(ResultVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public String toString() { - return "use " + expression; - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java deleted file mode 100644 index 0b65f2bc..00000000 --- a/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.annimon.ownlang.parser.linters; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Variables; -import com.annimon.ownlang.parser.ast.*; - -/** - * - * @author aNNiMON - */ -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 (Variables.isExists(variable)) { - Console.error(String.format( - "Warning: variable \"%s\" overrides constant", variable)); - } - } - } - - @Override - public void visit(IncludeStatement st) { - super.visit(st); - applyVisitor(st, this); - } - - @Override - public void visit(UseStatement st) { - super.visit(st); - st.execute(); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java deleted file mode 100644 index e5bff529..00000000 --- a/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.annimon.ownlang.parser.linters; - -import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.parser.ast.*; - -public final class DefaultFunctionsOverrideValidator extends LintVisitor { - - @Override - public void visit(FunctionDefineStatement s) { - super.visit(s); - if (Functions.isExists(s.name)) { - Console.error(String.format( - "Warning: function \"%s\" overrides default module function", s.name)); - } - } - - @Override - public void visit(IncludeStatement st) { - super.visit(st); - applyVisitor(st, this); - } - - @Override - public void visit(UseStatement st) { - super.visit(st); - st.execute(); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java b/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java deleted file mode 100644 index 451be324..00000000 --- a/src/main/java/com/annimon/ownlang/parser/linters/UseWithNonStringValueValidator.java +++ /dev/null @@ -1,54 +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) { - ArrayExpression ae = (ArrayExpression) st.expression; - for (Expression expr : ae.elements) { - if (!checkExpression(expr)) { - return; - } - } - } else { - if (!checkExpression(st.expression)) { - return; - } - } - } - - private boolean checkExpression(Expression expr) { - if (!(expr instanceof ValueExpression)) { - Console.error(String.format( - "Warning: `use` with %s, not ValueExpression", 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) { - Console.error(String.format( - "Warning: `use` with %s - %s, not string", - Types.typeToString(value.type()), value.asString())); - } -} diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java deleted file mode 100644 index 98f291cb..00000000 --- a/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ /dev/null @@ -1,38 +0,0 @@ -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.UseStatement; -import com.annimon.ownlang.parser.ast.ValueExpression; -import java.util.HashSet; -import java.util.Set; - -public class ModuleDetector extends AbstractVisitor { - - private Set modules; - - public ModuleDetector() { - modules = new HashSet<>(); - } - - public Set detect(Statement s) { - s.accept(this); - return modules; - } - - @Override - public void visit(UseStatement st) { - if (st.expression instanceof ArrayExpression) { - ArrayExpression ae = (ArrayExpression) st.expression; - for (Expression expr : ae.elements) { - modules.add(expr.eval().asString()); - } - } - if (st.expression instanceof ValueExpression) { - ValueExpression ve = (ValueExpression) st.expression; - modules.add(ve.value.asString()); - } - super.visit(st); - } -} diff --git a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java b/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java deleted file mode 100644 index 7cc70689..00000000 --- a/src/main/java/com/annimon/ownlang/utils/ModulesInfoCreator.java +++ /dev/null @@ -1,166 +0,0 @@ -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.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 InstantiationException, IllegalAccessException, ClassNotFoundException { - 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()) - .filter(File::isDirectory) - .map(File::getName) - .toArray(String[]::new); - for (String moduleName : moduleNames) { - final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName); - Class moduleClass = Class.forName(moduleClassPath); - Functions.getFunctions().clear(); - Variables.variables().clear(); - final Module module = (Module) moduleClass.newInstance(); - module.init(); - - final ModuleInfo moduleInfo = new ModuleInfo(moduleName); - moduleInfo.functions.addAll(Functions.getFunctions().keySet()); - moduleInfo.constants.putAll(Variables.variables()); - moduleInfo.types.addAll(listValues(moduleClass)); - 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; - List functions; - Map constants; - 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(Comparator.comparing(Map.Entry::getKey)) - .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/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/src/test/java/com/annimon/ownlang/parser/LexerTest.java deleted file mode 100644 index a4d94006..00000000 --- a/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.annimon.ownlang.parser; - -import com.annimon.ownlang.exceptions.LexerException; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static com.annimon.ownlang.parser.TokenType.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * - * @author aNNiMON - */ -public class LexerTest { - - @Test - public void testNumbers() { - String input = "0 3.1415 0xCAFEBABE 0Xf7_d6_c5 #FFFF #"; - List expList = list(NUMBER, NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("0", result.get(0).getText()); - assertEquals("3.1415", result.get(1).getText()); - assertEquals("CAFEBABE", result.get(2).getText()); - assertEquals("f7d6c5", result.get(3).getText()); - } - - @Test - 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).getText()); - } - - @Test - public void testKeywords() { - String input = "if else while for include"; - List expList = list(IF, ELSE, WHILE, FOR, INCLUDE); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - public void testWord() { - String input = "if bool include \"text\n\ntext\""; - List expList = list(IF, WORD, INCLUDE, TEXT); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - public void testString() { - String input = "\"1\\\"2\""; - List expList = list(TEXT); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("1\"2", result.get(0).getText()); - } - - @Test - public void testEmptyString() { - String input = "\"\""; - List expList = list(TEXT); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("", result.get(0).getText()); - } - - @Test - 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).getText()); - }); - } - - @Test - public void testOperators() { - String input = "=+-*/%<>!&|"; - List expList = list(EQ, PLUS, MINUS, STAR, SLASH, PERCENT, LT, GT, EXCL, AMP, BAR); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - public void testOperators2Char() { - String input = "== != <= >= && || ==+ >=- ->"; - List expList = list(EQEQ, EXCLEQ, LTEQ, GTEQ, AMPAMP, BARBAR, - EQEQ, PLUS, GTEQ, MINUS, MINUS, GT); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - public void testComments() { - String input = "// 1234 \n /* */ 123 /* \n 12345 \n\n\n */"; - List expList = list(NUMBER); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - assertEquals("123", result.get(0).getText()); - } - - @Test - public void testComments2() { - String input = "// /* 1234 \n */"; - List expList = list(STAR, SLASH); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @Test - 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); - List result = Lexer.tokenize(input); - assertTokens(expList, result); - } - - @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()); - } - - private static void assertTokens(List expList, List result) { - final int length = expList.size(); - assertEquals(length, result.size()); - for (int i = 0; i < length; i++) { - assertEquals(expList.get(i).getType(), result.get(i).getType()); - } - } - - private static List list(TokenType... types) { - final List list = new ArrayList(); - for (TokenType t : types) { - list.add(token(t)); - } - return list; - } - - private static Token token(TokenType type) { - return token(type, "", 0, 0); - } - - private static Token token(TokenType type, String text, int row, int col) { - return new Token(type, text, row, col); - } - -} diff --git a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java deleted file mode 100644 index caa2708e..00000000 --- a/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ /dev/null @@ -1,123 +0,0 @@ -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.outputsettings.OutputSettings; -import com.annimon.ownlang.outputsettings.StringOutputSettings; -import com.annimon.ownlang.parser.ast.FunctionDefineStatement; -import com.annimon.ownlang.parser.ast.Statement; -import com.annimon.ownlang.parser.ast.Visitor; -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.stream.Stream; - -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) - .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(); - Functions.clear(); - // Let's mock junit methods as ounit functions - Functions.set("assertEquals", (args) -> { - assertEquals(args[0], args[1]); - return NumberValue.ONE; - }); - Functions.set("assertNotEquals", (args) -> { - assertNotEquals(args[0], args[1]); - return NumberValue.ONE; - }); - Functions.set("assertSameType", (args) -> { - assertEquals(args[0].type(), args[1].type()); - return NumberValue.ONE; - }); - Functions.set("assertTrue", (args) -> { - assertTrue(args[0].asInt() != 0); - return NumberValue.ONE; - }); - Functions.set("assertFalse", (args) -> { - assertFalse(args[0].asInt() != 0); - return NumberValue.ONE; - }); - Functions.set("assertFail", (args) -> { - assertThrows(Throwable.class, - () -> ((FunctionValue) args[0]).getValue().execute()); - return NumberValue.ONE; - }); - } - - @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() throws IOException { - OutputSettings oldSettings = Console.getSettings(); - Console.useSettings(new StringOutputSettings()); - String source = "for i = 0, i <= 5, i++\n print i"; - final Statement s = Parser.parse(Lexer.tokenize(source)); - try { - s.execute(); - assertEquals("012345", Console.text()); - } catch (Exception oae) { - fail(oae.toString()); - } finally { - Console.useSettings(oldSettings); - } - } - - private static Visitor testFunctionsExecutor = new AbstractVisitor() { - @Override - public void visit(FunctionDefineStatement s) { - if (s.name.startsWith("test")) { - try { - Functions.get(s.name).execute(); - } catch (AssertionError err) { - throw new AssertionError(s.name + ": " + err.getMessage(), err); - } - } - } - }; -} diff --git a/src/test/resources/modules/yaml/yamlencode.own b/src/test/resources/modules/yaml/yamlencode.own deleted file mode 100644 index cf2e465d..00000000 --- a/src/test/resources/modules/yaml/yamlencode.own +++ /dev/null @@ -1,20 +0,0 @@ -use ["std", "yaml", "ounit"] - -yml = yamlencode({ - "name": "Yaml Example", - "version": 1, - "arrayData": [ - 1, 2, 3, 4 - ], - "objectData": { - "key": "value", - 10: "1000" - } -}) -obj = yamldecode(yml) - -assertEquals("Yaml Example", obj.name) -assertEquals(1, obj.version) -assertEquals(4, length(obj.arrayData)) -assertEquals("value", obj.objectData.key) -assertEquals("1000", obj.objectData["10"]) \ No newline at end of file diff --git a/tests.own b/tests.own deleted file mode 100644 index 2687ae8c..00000000 --- a/tests.own +++ /dev/null @@ -1,132 +0,0 @@ -use "ounit" -use "types" -use "functional" -use "date" -use "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 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