Compare commits
No commits in common. "9735774efdca532a9dd2f2efdb8e2b6dc1a21c01" and "d0f51928deba8dfe166d4cf4920672633a667572" have entirely different histories.
9735774efd
...
d0f51928de
52
CHANGELOG.md
52
CHANGELOG.md
@ -9,46 +9,24 @@ History note:
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- No unreleased entries yet.
|
### Database access
|
||||||
|
- Added the portable `lyng.io.db` SQL contract and the first concrete provider, `lyng.io.db.sqlite`.
|
||||||
|
- Added SQLite support on JVM and Linux Native with:
|
||||||
|
- generic `openDatabase("sqlite:...")` dispatch
|
||||||
|
- typed `openSqlite(...)` helper
|
||||||
|
- real nested transactions via savepoints
|
||||||
|
- generated keys through `ExecutionResult.getGeneratedKeys()`
|
||||||
|
- strict schema-driven value conversion for `Bool`, `Decimal`, `Date`, `DateTime`, and `Instant`
|
||||||
|
- documented option handling for `readOnly`, `createIfMissing`, `foreignKeys`, and `busyTimeoutMillis`
|
||||||
|
- Added public docs for database usage and SQLite provider behavior.
|
||||||
|
|
||||||
## 1.5.5 (2026-04-23)
|
### Time
|
||||||
|
- Added `Date` to `lyng.time` and the core library as a first-class calendar-date type.
|
||||||
### Concurrency and collections
|
- Added `Instant.toDate(...)`, `DateTime.date`, `DateTime.toDate()`, `Date.toDateTime(...)`, and related date arithmetic.
|
||||||
- Added coroutine coordination primitives and helpers for everyday parallel code:
|
- Added docs, stdlib reference updates, serialization support, and comprehensive tests for `Date`.
|
||||||
- `Channel` for coroutine-to-coroutine communication
|
|
||||||
- `LaunchPool` for bounded-concurrency task execution
|
|
||||||
- `Iterable<Deferred>.joinAll()` to await a whole collection of deferreds in input order
|
|
||||||
- `CompletableDeferred.completeExceptionally(...)` and `Deferred.cancelAndJoin()`
|
|
||||||
- Added docs and examples for the new concurrency APIs, including `joinAll()` coverage in iterable and parallelism references.
|
|
||||||
|
|
||||||
### Database and time APIs
|
|
||||||
- Added the portable `lyng.io.db` SQL contract and the first concrete providers:
|
|
||||||
- `lyng.io.db.sqlite` on JVM and Linux Native
|
|
||||||
- `lyng.io.db.jdbc` on JVM
|
|
||||||
- Added SQLite/JDBC release hardening:
|
|
||||||
- nested transactions via savepoints
|
|
||||||
- detached materialized rows
|
|
||||||
- generated-key support through `ExecutionResult.getGeneratedKeys()`
|
|
||||||
- schema-driven value conversion for `Bool`, `Decimal`, `Date`, `DateTime`, and `Instant`
|
|
||||||
- portable SQLite linker/deployment fixes and documented runtime options
|
|
||||||
- Added `Date` to `lyng.time` and the core runtime as a first-class calendar-date type, plus conversions and arithmetic across `Instant`, `DateTime`, and `Date`.
|
|
||||||
|
|
||||||
### Language, stdlib, and tooling
|
|
||||||
- Added extensions on singleton `object` declarations, including object-scoped indexer overrides for bracket syntax.
|
|
||||||
- Added backtick string literals and formatter support.
|
|
||||||
- Added `lyng.legacy_digest` for SHA-1 compatibility work, `String.replace`, and `buffer.base64std`.
|
|
||||||
- Improved CLI/runtime behavior with `atExit` shutdown handlers, native release-binary work, and follow-up CLI packaging/import fixes.
|
|
||||||
- Expanded docs across the tutorial, stdlib references, database docs, networking docs, and release notes.
|
|
||||||
|
|
||||||
### Runtime/compiler stability and performance
|
|
||||||
- Extended exact-call and higher-order lambda inlining through the bytecode compiler, including compiled fast paths for simple lambdas, wrappers, captures, and common higher-order helpers.
|
|
||||||
- Fixed import caching and class/object bytecode dispatch on JVM.
|
|
||||||
- Fixed immutable `val` compound assignments so true mutating `*Assign` operations continue to work while fallback reassignments report the correct read-only error.
|
|
||||||
- Fixed closure/capture and import regressions across launched loops, singleton/object extensions, aliasing, transitive re-exports, and immutable capture escaping.
|
|
||||||
- Improved list-fill/list-append fast paths, nullable-let inference, Decimal/Complex interop, and related regression coverage.
|
|
||||||
|
|
||||||
### Release notes
|
### Release notes
|
||||||
- Release metadata, homepage samples, docs, and README now point to `1.5.5`.
|
- Full `:lyngio:jvmTest` and `:lyngio:linuxX64Test` pass on the release tree after SQLite hardening.
|
||||||
|
|
||||||
## 1.5.4 (2026-04-03)
|
## 1.5.4 (2026-04-03)
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ assertEquals(A.E.One, A.One)
|
|||||||
|
|
||||||
- [Language home](https://lynglang.com)
|
- [Language home](https://lynglang.com)
|
||||||
- [introduction and tutorial](docs/tutorial.md) - start here please
|
- [introduction and tutorial](docs/tutorial.md) - start here please
|
||||||
- [Latest release notes (1.5.5)](docs/whats_new.md)
|
- [Latest release notes (1.5.4)](docs/whats_new.md)
|
||||||
- [What's New in 1.5](docs/whats_new_1_5.md)
|
- [What's New in 1.5](docs/whats_new_1_5.md)
|
||||||
- [Testing and Assertions](docs/Testing.md)
|
- [Testing and Assertions](docs/Testing.md)
|
||||||
- [Filesystem and Processes (lyngio)](docs/lyngio.md)
|
- [Filesystem and Processes (lyngio)](docs/lyngio.md)
|
||||||
@ -66,7 +66,7 @@ assertEquals(A.E.One, A.One)
|
|||||||
### Add dependency to your project
|
### Add dependency to your project
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val lyngVersion = "1.5.5"
|
val lyngVersion = "1.5.4"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// ...
|
// ...
|
||||||
@ -186,7 +186,7 @@ Designed to add scripting to kotlin multiplatform application in easy and effici
|
|||||||
|
|
||||||
# Language Roadmap
|
# Language Roadmap
|
||||||
|
|
||||||
The current stable release is **v1.5.5**: the 1.5 cycle now includes the database/date/concurrency additions as well as the latest compiler/runtime stabilization work, and the language, tooling, and site are aligned around this release.
|
The current stable release is **v1.5.4**: the 1.5 cycle is feature-complete, compiler/runtime stabilization work is in, and the language, tooling, and site are aligned around the current release.
|
||||||
|
|
||||||
Ready features:
|
Ready features:
|
||||||
|
|
||||||
|
|||||||
@ -18,12 +18,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
upload_only=false
|
upload_only=false
|
||||||
target=vps # default: new server; use --old for d.lynglang.com
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
if [[ "$arg" == "-u" || "$arg" == "--upload-only" ]]; then
|
if [[ "$arg" == "-u" || "$arg" == "--upload-only" ]]; then
|
||||||
upload_only=true
|
upload_only=true
|
||||||
elif [[ "$arg" == "--old" ]]; then
|
break
|
||||||
target=com
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -90,20 +88,19 @@ function updateIdeaPluginDownloadLink() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# target settings (-t com | -t vps)
|
# default target settings
|
||||||
case "$target" in
|
case "com" in
|
||||||
com)
|
com)
|
||||||
SSH_HOST=sergeych@d.lynglang.com
|
SSH_HOST=sergeych@d.lynglang.com # host to deploy to
|
||||||
SSH_PORT=22
|
SSH_PORT=22 # ssh port on it
|
||||||
ROOT=/bigstore/sergeych_pub/lyng
|
ROOT=/bigstore/sergeych_pub/lyng # directory to rsync to
|
||||||
;;
|
|
||||||
vps)
|
|
||||||
SSH_HOST=sergeych@94.130.36.94
|
|
||||||
SSH_PORT=22
|
|
||||||
ROOT=/var/www/lynglang
|
|
||||||
;;
|
;;
|
||||||
|
# com)
|
||||||
|
# SSH_HOST=vvk@front-01.neurodatalab.com
|
||||||
|
# ROOT=/home/vvk
|
||||||
|
# ;;
|
||||||
*)
|
*)
|
||||||
echo "*** ERROR: unknown target '$target' (use -t com | -t vps)"
|
echo "*** ERROR: target not specified (use deploy com | dev)"
|
||||||
echo "*** stop"
|
echo "*** stop"
|
||||||
exit 101
|
exit 101
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -55,23 +55,6 @@ Here is the sample:
|
|||||||
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
|
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
## joinAll
|
|
||||||
|
|
||||||
`joinAll()` is an `Iterable<Deferred>` helper that awaits every deferred in iteration order and returns a `List`
|
|
||||||
with the collected results.
|
|
||||||
|
|
||||||
val jobs = (1..4).map { n ->
|
|
||||||
launch { n * n }
|
|
||||||
}
|
|
||||||
assertEquals([1, 4, 9, 16], jobs.joinAll())
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- it does not start any task by itself; it only awaits the deferreds already present in the iterable.
|
|
||||||
- awaiting happens in iteration order, so the result list keeps the same order as the input iterable.
|
|
||||||
- if any deferred fails or was cancelled, that `await()` error is propagated from `joinAll()`.
|
|
||||||
|
|
||||||
## `sum` and `sumOf`
|
## `sum` and `sumOf`
|
||||||
|
|
||||||
These, again, does the thing:
|
These, again, does the thing:
|
||||||
@ -201,7 +184,6 @@ Search for the first element that satisfies the given predicate:
|
|||||||
| sortedWith(comparator) | sort using a comparator that compares elements (1) |
|
| sortedWith(comparator) | sort using a comparator that compares elements (1) |
|
||||||
| sortedBy(predicate) | sort by comparing results of the predicate function |
|
| sortedBy(predicate) | sort by comparing results of the predicate function |
|
||||||
| joinToString(s,t) | convert iterable to string, see (2) |
|
| joinToString(s,t) | convert iterable to string, see (2) |
|
||||||
| joinAll() | for `Iterable<Deferred>`, await all items in order and collect results to [List] |
|
|
||||||
| reversed() | create a list containing items from this in reverse order |
|
| reversed() | create a list containing items from this in reverse order |
|
||||||
| shuffled() | create a list of shuffled elements |
|
| shuffled() | create a list of shuffled elements |
|
||||||
|
|
||||||
|
|||||||
@ -73,13 +73,13 @@ pool.closeAndJoin()
|
|||||||
|
|
||||||
## Collecting all results
|
## Collecting all results
|
||||||
|
|
||||||
`launch` returns a `Deferred`, so you can collect results with `joinAll()`:
|
`launch` returns a `Deferred`, so you can collect results via `map`:
|
||||||
|
|
||||||
```lyng
|
```lyng
|
||||||
val pool = LaunchPool(4)
|
val pool = LaunchPool(4)
|
||||||
val jobs = (1..10).map { n -> pool.launch { n * n } }
|
val jobs = (1..10).map { n -> pool.launch { n * n } }
|
||||||
pool.closeAndJoin()
|
pool.closeAndJoin()
|
||||||
val results = jobs.joinAll()
|
val results = jobs.map { (it as Deferred).await() }
|
||||||
// results == [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
|
// results == [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,6 @@ Sources: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`, `lynglib/s
|
|||||||
- Async/concurrency: `launch`, `yield`, `flow`, `delay`.
|
- Async/concurrency: `launch`, `yield`, `flow`, `delay`.
|
||||||
- `Deferred.cancel()` cancels an active task.
|
- `Deferred.cancel()` cancels an active task.
|
||||||
- `Deferred.await()` throws `CancellationException` if that task was cancelled.
|
- `Deferred.await()` throws `CancellationException` if that task was cancelled.
|
||||||
- `Iterable<Deferred>.joinAll()` awaits every deferred in iteration order and returns a `List` of results.
|
|
||||||
- Math: `floor`, `ceil`, `round`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`, `exp`, `ln`, `log10`, `log2`, `pow`, `sqrt`, `abs`, `clamp`.
|
- Math: `floor`, `ceil`, `round`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`, `exp`, `ln`, `log10`, `log2`, `pow`, `sqrt`, `abs`, `clamp`.
|
||||||
- These helpers also accept `lyng.decimal.Decimal`.
|
- These helpers also accept `lyng.decimal.Decimal`.
|
||||||
- Exact Decimal path today: `abs`, `floor`, `ceil`, `round`, and `pow` with integral exponent.
|
- Exact Decimal path today: `abs`, `floor`, `ceil`, `round`, and `pow` with integral exponent.
|
||||||
|
|||||||
@ -36,17 +36,6 @@ This example shows how to launch a coroutine with `launch` which returns [Deferr
|
|||||||
|
|
||||||
Launch has the only argument which should be a callable (lambda usually) that is run in parallel (or cooperatively in parallel), and return anything as the result.
|
Launch has the only argument which should be a callable (lambda usually) that is run in parallel (or cooperatively in parallel), and return anything as the result.
|
||||||
|
|
||||||
When you have an iterable of deferreds, use `joinAll()` to await all of them and collect results in input order:
|
|
||||||
|
|
||||||
val jobs = (1..4).map { n ->
|
|
||||||
launch {
|
|
||||||
delay(1)
|
|
||||||
n * 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEquals([10, 20, 30, 40], jobs.joinAll())
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
If you no longer need the result, cancel the deferred. Awaiting a cancelled deferred throws `CancellationException`:
|
If you no longer need the result, cancel the deferred. Awaiting a cancelled deferred throws `CancellationException`:
|
||||||
|
|
||||||
var reached = false
|
var reached = false
|
||||||
@ -280,7 +269,7 @@ val jobs = (1..20).map { n ->
|
|||||||
}
|
}
|
||||||
pool.closeAndJoin() // wait for all tasks to complete
|
pool.closeAndJoin() // wait for all tasks to complete
|
||||||
|
|
||||||
val results = jobs.joinAll()
|
val results = jobs.map { (it as Deferred).await() }
|
||||||
```
|
```
|
||||||
|
|
||||||
Exceptions thrown inside a submitted lambda are captured in the returned `Deferred` and do not crash the pool, so other tasks continue running normally.
|
Exceptions thrown inside a submitted lambda are captured in the returned `Deferred` and do not crash the pool, so other tasks continue running normally.
|
||||||
|
|||||||
@ -1,29 +1,26 @@
|
|||||||
# What's New in Lyng
|
# What's New in Lyng
|
||||||
|
|
||||||
This document highlights the current Lyng release, **1.5.5**, and the broader additions from the 1.5 cycle.
|
This document highlights the current Lyng release, **1.5.4**, and the broader additions from the 1.5 cycle.
|
||||||
It is intentionally user-facing: new language features, new modules, new tools, and the practical things you can build with them.
|
It is intentionally user-facing: new language features, new modules, new tools, and the practical things you can build with them.
|
||||||
For a programmer-focused migration summary across 1.5.x, see `docs/whats_new_1_5.md`.
|
For a programmer-focused migration summary across 1.5.x, see `docs/whats_new_1_5.md`.
|
||||||
|
|
||||||
## Release 1.5.5 Highlights
|
## Release 1.5.4 Highlights
|
||||||
|
|
||||||
- `1.5.5` extends the 1.5 line with practical database APIs, first-class calendar dates, and better coroutine building blocks.
|
- `1.5.4` is the stabilization release for the 1.5 feature set.
|
||||||
- The 1.5 line now brings together richer ranges and loops, interpolation, math modules, immutable and observable collections, richer `lyngio`, and much better CLI/IDE support.
|
- The 1.5 line now brings together richer ranges and loops, interpolation, math modules, immutable and observable collections, richer `lyngio`, and much better CLI/IDE support.
|
||||||
- `1.5.5` adds `Channel`, `LaunchPool`, and `joinAll()` so coroutine-heavy scripts can coordinate work more directly.
|
- `1.5.4` specifically fixes user-visible issues around decimal arithmetic, mixed numeric flows, list behavior, and observable list hooks.
|
||||||
- `1.5.5` adds `Date`, the portable `lyng.io.db` layer, SQLite/JDBC providers, and a compatibility `lyng.legacy_digest` module.
|
- `1.5.4` also fixes extension-member registration for named singleton `object` declarations, so `fun X.foo()` and `val X.bar` now work as expected.
|
||||||
- `1.5.5` also continues runtime/compiler hardening with better import dispatch, faster exact lambda calls, and correct `val +=`/`-=` behavior for mutating types versus real reassignment.
|
- `1.5.4` also lets named singleton `object` declarations use scoped indexer extensions with bracket syntax, so patterns like `Storage["name"]` can be implemented with `override fun Storage.getAt(...)` / `putAt(...)`.
|
||||||
- The docs, homepage samples, and release metadata now point at the current stable version.
|
- The docs, homepage samples, and release metadata now point at the current stable version.
|
||||||
|
|
||||||
## User Highlights Across 1.5.x
|
## User Highlights Across 1.5.x
|
||||||
|
|
||||||
- Descending ranges and loops with `downTo` / `downUntil`
|
- Descending ranges and loops with `downTo` / `downUntil`
|
||||||
- String interpolation with `$name` and `${expr}`
|
- String interpolation with `$name` and `${expr}`
|
||||||
- Backtick string literals for raw-ish string text
|
|
||||||
- Decimal arithmetic, matrices/vectors, and complex numbers
|
- Decimal arithmetic, matrices/vectors, and complex numbers
|
||||||
- Calendar `Date` support in `lyng.time`
|
- Calendar `Date` support in `lyng.time`
|
||||||
- `Channel`, `LaunchPool`, and `joinAll()` for coroutine workflows
|
|
||||||
- Immutable collections and opt-in `ObservableList`
|
- Immutable collections and opt-in `ObservableList`
|
||||||
- Rich `lyngio` modules for SQLite/JDBC databases, console, HTTP, WebSocket, TCP, and UDP
|
- Rich `lyngio` modules for SQLite databases, console, HTTP, WebSocket, TCP, and UDP
|
||||||
- Legacy SHA-1 compatibility helpers in `lyng.legacy_digest`
|
|
||||||
- CLI improvements including the built-in formatter `lyng fmt`
|
- CLI improvements including the built-in formatter `lyng fmt`
|
||||||
- Better IDE support and stronger docs around the released feature set
|
- Better IDE support and stronger docs around the released feature set
|
||||||
|
|
||||||
@ -327,7 +324,7 @@ Singleton objects are declared using the `object` keyword. They provide a conven
|
|||||||
|
|
||||||
```lyng
|
```lyng
|
||||||
object Config {
|
object Config {
|
||||||
val version = "1.5.5"
|
val version = "1.5.4"
|
||||||
fun show() = println("Config version: " + version)
|
fun show() = println("Config version: " + version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
[vps]
|
|
||||||
94.130.36.94 ansible_user=sergeych
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Setup lynglang.com static site on VPS
|
|
||||||
hosts: vps
|
|
||||||
become: yes
|
|
||||||
vars:
|
|
||||||
domain: lynglang.com
|
|
||||||
web_root: /var/www/lynglang
|
|
||||||
deploy_user: sergeych
|
|
||||||
certbot_email: real.sergeych@gmail.com
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
# Debian 10 buster is EOL; security/backports repos moved to archive.debian.org
|
|
||||||
- name: Fix sources.list for Debian buster EOL
|
|
||||||
copy:
|
|
||||||
dest: /etc/apt/sources.list
|
|
||||||
content: |
|
|
||||||
deb http://archive.debian.org/debian/ buster main contrib non-free
|
|
||||||
deb http://archive.debian.org/debian-security/ buster/updates main contrib non-free
|
|
||||||
deb http://archive.debian.org/debian/ buster-backports main contrib non-free
|
|
||||||
|
|
||||||
- name: Remove stale third-party sources (broken for buster EOL)
|
|
||||||
file:
|
|
||||||
path: "/etc/apt/sources.list.d/{{ item }}"
|
|
||||||
state: absent
|
|
||||||
loop:
|
|
||||||
- cassandra.list
|
|
||||||
- icinga.list
|
|
||||||
- postgres.list
|
|
||||||
- salt-stack.list
|
|
||||||
- yarn.list
|
|
||||||
|
|
||||||
- name: Install nginx, certbot, and python3-certbot-nginx
|
|
||||||
apt:
|
|
||||||
name:
|
|
||||||
- nginx
|
|
||||||
- certbot
|
|
||||||
- python3-certbot-nginx
|
|
||||||
state: present
|
|
||||||
update_cache: yes
|
|
||||||
|
|
||||||
- name: Create web root directory
|
|
||||||
file:
|
|
||||||
path: "{{ web_root }}/release/dist"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ deploy_user }}"
|
|
||||||
group: www-data
|
|
||||||
mode: "0755"
|
|
||||||
recurse: yes
|
|
||||||
|
|
||||||
- name: Create distributables directory
|
|
||||||
file:
|
|
||||||
path: "{{ web_root }}/release/dist/distributables"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ deploy_user }}"
|
|
||||||
group: www-data
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Deploy nginx site config (HTTP, pre-certbot)
|
|
||||||
template:
|
|
||||||
src: templates/nginx_lynglang.conf.j2
|
|
||||||
dest: /etc/nginx/sites-available/{{ domain }}
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: Enable nginx site
|
|
||||||
file:
|
|
||||||
src: /etc/nginx/sites-available/{{ domain }}
|
|
||||||
dest: /etc/nginx/sites-enabled/{{ domain }}
|
|
||||||
state: link
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: Disable default nginx site
|
|
||||||
file:
|
|
||||||
path: /etc/nginx/sites-enabled/default
|
|
||||||
state: absent
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: Ensure nginx is started
|
|
||||||
service:
|
|
||||||
name: nginx
|
|
||||||
state: started
|
|
||||||
enabled: yes
|
|
||||||
|
|
||||||
- name: Reload nginx before certbot
|
|
||||||
meta: flush_handlers
|
|
||||||
|
|
||||||
- name: Obtain SSL certificate via certbot (--nginx plugin)
|
|
||||||
command: >
|
|
||||||
certbot --nginx
|
|
||||||
-d {{ domain }} -d www.{{ domain }}
|
|
||||||
--non-interactive --agree-tos
|
|
||||||
--email {{ certbot_email }}
|
|
||||||
--redirect
|
|
||||||
args:
|
|
||||||
creates: /etc/letsencrypt/live/{{ domain }}/fullchain.pem
|
|
||||||
|
|
||||||
handlers:
|
|
||||||
- name: reload nginx
|
|
||||||
service:
|
|
||||||
name: nginx
|
|
||||||
state: reloaded
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name {{ domain }} www.{{ domain }};
|
|
||||||
|
|
||||||
root {{ web_root }}/release/dist;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
# SPA fallback
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Distributables served directly
|
|
||||||
location /distributables/ {
|
|
||||||
try_files $uri =404;
|
|
||||||
autoindex on;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Long-lived cache for hashed assets
|
|
||||||
location ~* \.(js|css|woff2?|ttf|eot|svg|png|jpg|ico)$ {
|
|
||||||
expires 1y;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "1.5.5"
|
version = "1.5.5-SNAPSHOT"
|
||||||
|
|
||||||
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
||||||
|
|
||||||
|
|||||||
@ -2881,18 +2881,14 @@ class BytecodeCompiler(
|
|||||||
val slot = resolveCapturedOwnerScopeSlot(localTarget) ?: resolveSlot(localTarget) ?: return null
|
val slot = resolveCapturedOwnerScopeSlot(localTarget) ?: resolveSlot(localTarget) ?: return null
|
||||||
val targetType = slotTypes[slot] ?: SlotType.OBJ
|
val targetType = slotTypes[slot] ?: SlotType.OBJ
|
||||||
if (!localTarget.isMutable) {
|
if (!localTarget.isMutable) {
|
||||||
|
if (targetType != SlotType.OBJ && targetType != SlotType.UNKNOWN) return compileEvalRef(ref)
|
||||||
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||||
if (!shouldUseAssignOpForImmutableLocal(slot, targetType, ref.op)) {
|
|
||||||
emitImmutableLocalReassignError(localTarget.name, localTarget.pos())
|
|
||||||
return rhs
|
|
||||||
}
|
|
||||||
val rhsObj = ensureObjSlot(rhs)
|
val rhsObj = ensureObjSlot(rhs)
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
|
||||||
if (nameId > 0xFFFF) return compileEvalRef(ref)
|
if (nameId > 0xFFFF) return compileEvalRef(ref)
|
||||||
val dst = allocSlot()
|
val dst = allocSlot()
|
||||||
builder.emit(Opcode.ASSIGN_OP_OBJ, ref.op.ordinal, slot, rhsObj.slot, dst, nameId)
|
builder.emit(Opcode.ASSIGN_OP_OBJ, ref.op.ordinal, slot, rhsObj.slot, dst, nameId)
|
||||||
updateSlotType(dst, SlotType.OBJ)
|
updateSlotType(dst, SlotType.OBJ)
|
||||||
slotObjClass[slot]?.let { slotObjClass[dst] = it }
|
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
var rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
var rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||||
@ -8450,38 +8446,6 @@ class BytecodeCompiler(
|
|||||||
builder.emit(Opcode.THROW, posId, msgSlot)
|
builder.emit(Opcode.THROW, posId, msgSlot)
|
||||||
return msgSlot
|
return msgSlot
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignOpMethodName(op: BinOp): String? = when (op) {
|
|
||||||
BinOp.PLUS -> "plusAssign"
|
|
||||||
BinOp.MINUS -> "minusAssign"
|
|
||||||
BinOp.STAR -> "mulAssign"
|
|
||||||
BinOp.SLASH -> "divAssign"
|
|
||||||
BinOp.PERCENT -> "modAssign"
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun knownBuiltinAssignOpSupport(cls: ObjClass, op: BinOp): Boolean? = when (cls) {
|
|
||||||
ObjList.type -> op == BinOp.PLUS || op == BinOp.MINUS
|
|
||||||
ObjObservableList.type -> op == BinOp.PLUS || op == BinOp.MINUS
|
|
||||||
ObjSet.type -> op == BinOp.PLUS
|
|
||||||
ObjMap.type -> op == BinOp.PLUS
|
|
||||||
ObjRingBuffer.type -> op == BinOp.PLUS
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun shouldUseAssignOpForImmutableLocal(slot: Int, targetType: SlotType, op: BinOp): Boolean {
|
|
||||||
return when (targetType) {
|
|
||||||
SlotType.INT, SlotType.REAL, SlotType.BOOL -> false
|
|
||||||
SlotType.OBJ, SlotType.UNKNOWN -> {
|
|
||||||
val cls = slotObjClass[slot] ?: return true
|
|
||||||
val methodName = assignOpMethodName(op)
|
|
||||||
if (methodName != null && cls.getInstanceMemberOrNull(methodName, includeStatic = false) != null) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
knownBuiltinAssignOpSupport(cls, op) ?: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun binaryLeft(ref: BinaryOpRef): ObjRef = ref.left
|
private fun binaryLeft(ref: BinaryOpRef): ObjRef = ref.left
|
||||||
private fun binaryRight(ref: BinaryOpRef): ObjRef = ref.right
|
private fun binaryRight(ref: BinaryOpRef): ObjRef = ref.right
|
||||||
private fun binaryOp(ref: BinaryOpRef): BinOp = ref.op
|
private fun binaryOp(ref: BinaryOpRef): BinOp = ref.op
|
||||||
|
|||||||
@ -2204,8 +2204,8 @@ class CmdAssignOpObj(
|
|||||||
}
|
}
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
val name = (frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal)?.value
|
val name = (frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal)?.value
|
||||||
if (name != null) frame.ensureScope().raiseIllegalAssignment("can't reassign val $name")
|
if (name != null) frame.ensureScope().raiseIllegalAssignment("symbol is readonly: $name")
|
||||||
frame.ensureScope().raiseIllegalAssignment("can't reassign val")
|
frame.ensureScope().raiseIllegalAssignment("symbol is readonly")
|
||||||
}
|
}
|
||||||
frame.storeObjResult(dst, result)
|
frame.storeObjResult(dst, result)
|
||||||
return
|
return
|
||||||
@ -4552,7 +4552,7 @@ class CmdFrame(
|
|||||||
type = inherited.type
|
type = inherited.type
|
||||||
)
|
)
|
||||||
copied.delegate = inherited.delegate
|
copied.delegate = inherited.delegate
|
||||||
return@mapIndexed freezeImmutableCaptureRecord(copied)
|
return@mapIndexed copied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val isMutable = fn.localSlotMutables.getOrNull(localIndex) ?: false
|
val isMutable = fn.localSlotMutables.getOrNull(localIndex) ?: false
|
||||||
@ -4576,13 +4576,11 @@ class CmdFrame(
|
|||||||
val record = findNamedExistingRecord(scope, name)
|
val record = findNamedExistingRecord(scope, name)
|
||||||
if (record != null) {
|
if (record != null) {
|
||||||
val value = record.value
|
val value = record.value
|
||||||
return@mapIndexed freezeImmutableCaptureRecord(
|
return@mapIndexed when (value) {
|
||||||
when (value) {
|
|
||||||
is FrameSlotRef -> ObjRecord(value, isMutable)
|
is FrameSlotRef -> ObjRecord(value, isMutable)
|
||||||
is RecordSlotRef -> ObjRecord(value, isMutable)
|
is RecordSlotRef -> ObjRecord(value, isMutable)
|
||||||
else -> ObjRecord(value, isMutable)
|
else -> ObjRecord(value, isMutable)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (hasNamedScopeBinding(scope, name)) {
|
if (hasNamedScopeBinding(scope, name)) {
|
||||||
throw ScriptError(
|
throw ScriptError(
|
||||||
@ -4591,13 +4589,11 @@ class CmdFrame(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freezeImmutableCaptureRecord(
|
when (raw) {
|
||||||
when (raw) {
|
is FrameSlotRef -> ObjRecord(raw, isMutable)
|
||||||
is FrameSlotRef -> ObjRecord(raw, isMutable)
|
is RecordSlotRef -> ObjRecord(raw, isMutable)
|
||||||
is RecordSlotRef -> ObjRecord(raw, isMutable)
|
else -> ObjRecord(FrameSlotRef(frame, localIndex), isMutable)
|
||||||
else -> ObjRecord(FrameSlotRef(frame, localIndex), isMutable)
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -104,14 +104,14 @@ class WebsiteSamplesTest {
|
|||||||
val name = "Lyng"
|
val name = "Lyng"
|
||||||
val base = { id:, name: } // Shorthand for id: id, name: name
|
val base = { id:, name: } // Shorthand for id: id, name: name
|
||||||
|
|
||||||
val full = { ...base, version: "1.5.5", status: "stable" }
|
val full = { ...base, version: "1.5.4", status: "stable" }
|
||||||
full
|
full
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
assertTrue(result is ObjMap)
|
assertTrue(result is ObjMap)
|
||||||
val m = result.map
|
val m = result.map
|
||||||
assertEquals(101L, (m[ObjString("id")] as ObjInt).value)
|
assertEquals(101L, (m[ObjString("id")] as ObjInt).value)
|
||||||
assertEquals("Lyng", (m[ObjString("name")] as ObjString).value)
|
assertEquals("Lyng", (m[ObjString("name")] as ObjString).value)
|
||||||
assertEquals("1.5.5", (m[ObjString("version")] as ObjString).value)
|
assertEquals("1.5.4", (m[ObjString("version")] as ObjString).value)
|
||||||
assertEquals("stable", (m[ObjString("status")] as ObjString).value)
|
assertEquals("stable", (m[ObjString("status")] as ObjString).value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,8 +19,6 @@ package net.sergeych.lyng
|
|||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertContains
|
|
||||||
import kotlin.test.assertFailsWith
|
|
||||||
|
|
||||||
class OperatorOverloadingTest {
|
class OperatorOverloadingTest {
|
||||||
@Test
|
@Test
|
||||||
@ -65,24 +63,6 @@ class OperatorOverloadingTest {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBuiltinListPlusAssignOnVal() = runTest {
|
|
||||||
eval("""
|
|
||||||
val list = [1, 2]
|
|
||||||
list += 3
|
|
||||||
assertEquals([1, 2, 3], list)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBuiltinListMinusAssignOnVal() = runTest {
|
|
||||||
eval("""
|
|
||||||
val list = [1, 2, 3]
|
|
||||||
list -= 2
|
|
||||||
assertEquals([1, 3], list)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPlusAssignFallback() = runTest {
|
fun testPlusAssignFallback() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
@ -96,130 +76,6 @@ class OperatorOverloadingTest {
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testPlusAssignFallbackOnValReportsReadonlyError() = runTest {
|
|
||||||
val ex = assertFailsWith<ScriptError> {
|
|
||||||
eval("""
|
|
||||||
class Vector(var x: Int, var y: Int) {
|
|
||||||
fun plus(other: Vector) = Vector(this.x + other.x, this.y + other.y)
|
|
||||||
fun equals(other: Vector) = this.x == other.x && this.y == other.y
|
|
||||||
}
|
|
||||||
val v = Vector(1, 2)
|
|
||||||
v += Vector(3, 4)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertContains(ex.errorMessage, "can't reassign val v")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMinusAssignOverloadingOnVal() = runTest {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun minusAssign(x: Int) { this.n = this.n - x }
|
|
||||||
}
|
|
||||||
val c = Counter(10)
|
|
||||||
c -= 3
|
|
||||||
assertEquals(7, c.n)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMinusAssignFallbackOnValReportsReadonlyError() = runTest {
|
|
||||||
val ex = assertFailsWith<ScriptError> {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun minus(x: Int) = Counter(this.n - x)
|
|
||||||
}
|
|
||||||
val c = Counter(10)
|
|
||||||
c -= 3
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertContains(ex.errorMessage, "can't reassign val c")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMulAssignOverloadingOnVal() = runTest {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun mulAssign(x: Int) { this.n = this.n * x }
|
|
||||||
}
|
|
||||||
val c = Counter(10)
|
|
||||||
c *= 3
|
|
||||||
assertEquals(30, c.n)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMulAssignFallbackOnValReportsReadonlyError() = runTest {
|
|
||||||
val ex = assertFailsWith<ScriptError> {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun times(x: Int) = Counter(this.n * x)
|
|
||||||
}
|
|
||||||
val c = Counter(10)
|
|
||||||
c *= 3
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertContains(ex.errorMessage, "can't reassign val c")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDivAssignOverloadingOnVal() = runTest {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun divAssign(x: Int) { this.n = this.n / x }
|
|
||||||
}
|
|
||||||
val c = Counter(21)
|
|
||||||
c /= 3
|
|
||||||
assertEquals(7, c.n)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDivAssignFallbackOnValReportsReadonlyError() = runTest {
|
|
||||||
val ex = assertFailsWith<ScriptError> {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun div(x: Int) = Counter(this.n / x)
|
|
||||||
}
|
|
||||||
val c = Counter(21)
|
|
||||||
c /= 3
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertContains(ex.errorMessage, "can't reassign val c")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testModAssignOverloadingOnVal() = runTest {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun modAssign(x: Int) { this.n = this.n % x }
|
|
||||||
}
|
|
||||||
val c = Counter(23)
|
|
||||||
c %= 5
|
|
||||||
assertEquals(3, c.n)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testModAssignFallbackOnValReportsReadonlyError() = runTest {
|
|
||||||
val ex = assertFailsWith<ScriptError> {
|
|
||||||
eval("""
|
|
||||||
class Counter(var n: Int) {
|
|
||||||
fun mod(x: Int) = Counter(this.n % x)
|
|
||||||
}
|
|
||||||
val c = Counter(23)
|
|
||||||
c %= 5
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
assertContains(ex.errorMessage, "can't reassign val c")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCompareOverloading() = runTest {
|
fun testCompareOverloading() = runTest {
|
||||||
eval("""
|
eval("""
|
||||||
|
|||||||
@ -90,48 +90,4 @@ class OptTest {
|
|||||||
}
|
}
|
||||||
assertContains(ex.errorMessage, "can't reassign val a")
|
assertContains(ex.errorMessage, "can't reassign val a")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAssignOpErrorMessageFromExample() = runTest {
|
|
||||||
val source = Source(
|
|
||||||
"examples/error1.lyng",
|
|
||||||
"""
|
|
||||||
val a = 1
|
|
||||||
a += 2
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
|
|
||||||
val ex = assertFailsWith<ScriptError> {
|
|
||||||
Script.newScope().eval(source)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertContains(ex.errorMessage, "can't reassign val a")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testClosuresInLaunchPool() = runTest {
|
|
||||||
eval($$"""
|
|
||||||
val result = Set()
|
|
||||||
val mu = Mutex()
|
|
||||||
fn doSomething(value) {
|
|
||||||
delay(100)
|
|
||||||
println(value)
|
|
||||||
mu.withLock {
|
|
||||||
result += value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val lp = LaunchPool(4, 1000)
|
|
||||||
for (i in 1 .. 10) {
|
|
||||||
val ii: Int = i
|
|
||||||
lp.launch {
|
|
||||||
doSomething( ii )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("all tasks were placed into lauchpool")
|
|
||||||
lp.closeAndJoin()
|
|
||||||
println("ALL DONE: $result")
|
|
||||||
assertEquals((1..10).toSet(), result)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -159,7 +159,7 @@ fun HomePage() {
|
|||||||
val id = 101
|
val id = 101
|
||||||
val name = "Lyng"
|
val name = "Lyng"
|
||||||
val base = { id:, name: }
|
val base = { id:, name: }
|
||||||
val full = { ...base, version: "1.5.5", status: "stable", tags: ["typed", "portable"] }
|
val full = { ...base, version: "1.5.4", status: "stable", tags: ["typed", "portable"] }
|
||||||
|
|
||||||
println(full)
|
println(full)
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user