Compare commits

...

18 Commits

Author SHA1 Message Date
23737f9b5c removed debug stuff 2025-08-18 01:48:23 +03:00
fb6e2aa49e bugfix: elvis + throw 2025-08-18 01:19:49 +03:00
95c1da60ed better elvis + throw
+added run {}
2025-08-18 00:56:52 +03:00
835333dfad fixed objClass initialization in ObjInt.Zero, added Buffer.toBitInput, minimal descrription of Lynon 2025-08-17 23:51:50 +03:00
eefecae7b4 more Buffer encodings 2025-08-17 19:26:15 +03:00
2ac92a1d09 more elegant Iterable.all 2025-08-17 18:21:52 +03:00
464a6dcb99 fixed many bugs with variable visibility in mixed scopes 2025-08-17 12:24:39 +03:00
eca746b189 commented out delegate creation 2025-08-14 14:34:44 +03:00
b07452e66e more stdlib and docs, bugfixes 2025-08-14 14:31:51 +03:00
202e70a99a less debug noise 2025-08-14 01:36:37 +03:00
f45310f7d9 default implementation for Iterator.cancelIteration (does nothing) and not implemented errors for iterator methods (better error messages) 2025-08-13 23:34:54 +03:00
48a7f0839c less warnings 2025-08-13 23:24:51 +03:00
2adb0ff512 +copyrght, apache 2.0 2025-08-13 23:22:04 +03:00
6735499959 more enum tests 2025-08-12 14:57:52 +03:00
b5e89c7e78 smple enums w/serialization 2025-08-12 14:47:41 +03:00
84e345b04e more docs on function annotations 2025-08-12 05:01:02 +03:00
9bd7aa368e cleanup 2025-08-12 04:52:40 +03:00
9704f18284 function annotation and some docs for it 2025-08-12 04:51:26 +03:00
111 changed files with 2806 additions and 251 deletions

View File

@ -1,3 +1,20 @@
<!--
~ Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tests in 'lyng.lynglib.jvmTest'" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>

3
NOTICE Normal file
View File

@ -0,0 +1,3 @@
The Lyng programming language.
Copyright (c) 2024-2025 Sergey Chernov real.sergeych@gmail.com

View File

@ -157,6 +157,7 @@ Ready features:
- [x] typesafe bit-effective serialization
- [x] compression/decompression (integrated in serialization)
- [x] dynamic fields
- [x] function annotations
### Under way:

View File

@ -1,5 +1,22 @@
#!/bin/bash
#
# Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
set -e
root=./lyng/build/install/lyng-jvm/

View File

@ -1,5 +1,22 @@
#!/bin/bash
#
# Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
set -e
file=./lyng/build/bin/linuxX64/releaseExecutable/lyng.kexe

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
plugins {
alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.kotlinMultiplatform) apply false

View File

@ -102,20 +102,50 @@ As with [List], it is possible to use ranges as indexes to slice a Buffer:
>>> void
## Encoding
You can encode `String` to buffer using buffer constructor, as was shown. Also, buffer supports out of the box base64 (
which is used in `toString`) and hex encoding:
import lyng.buffer
// to UTF8 and back:
val b = Buffer("hello")
assertEquals( "hello", b.decodeUtf8() )
// to base64 and back:
assertEquals( b, Buffer.decodeBase64(b.base64) )
assertEquals( b, Buffer.decodeHex(b.hex) )
>>> void
## Members
| name | meaning | type |
|---------------|------------------------------------|---------------|
| `size` | size | Int |
| `decodeUtf8` | decodee to String using UTF8 rules | Any |
| `+` | buffer concatenation | Any |
| `toMutable()` | create a mutable copy | MutableBuffer |
| name | meaning | type |
|----------------------------|-----------------------------------------|---------------|
| `size` | size | Int |
| `decodeUtf8` | decode to String using UTF8 rules | Any |
| `+` | buffer concatenation | Any |
| `toMutable()` | create a mutable copy | MutableBuffer |
| `hex` | encode to hex strign | String |
| `Buffer.decodeHex(hexStr) | decode hex string | Buffer |
| `base64` | encode to base64 (url flavor) (2) | String |
| `Buffer.decodeBase64(str)` | decode base64 to new Buffer (2) | Buffer |
| `toBitInput()` | create bit input from a byte buffer (3) | |
(1)
: optimized implementation that override `Iterable` one
(2)
: base64url alphabet is used without trailing '=', which allows string to be used in URI without escaping. Note that
decoding supports both traditional and URL alphabets automatically, and ignores filling `=` characters. Base64URL is
well known and mentioned in the internet, for example, [here](https://base64.guru/standards/base64url).
(3)
: `BitInput` is a bit buffer that is used, for example, in [Lynon.decode](serialization.md)
Also, it inherits methods from [Iterable] and [Array].
[Range]: Range.md
[Iterable]: Iterable.md

View File

@ -26,36 +26,58 @@ Just remember at this stage typed declarations are not yet supported.
Having `Iterable` in base classes allows to use it in for loop. Also, each `Iterable` has some utility functions available, for example
val r = 1..10 // Range is Iterable!
assertEquals( [9,10] r.takeLast(2) )
assertEquals( [1,2,3] r.take(3) )
assertEquals( [9,10] r.drop(8) )
assertEquals( [1,2] r.dropLast(8) )
assertEquals( [9,10], r.takeLast(2).toList() )
assertEquals( [1,2,3], r.take(3).toList() )
assertEquals( [9,10], r.drop(8).toList() )
assertEquals( [1,2], r.dropLast(8).toList() )
>>> void
## joinToString
This methods convert any iterable to a string joining string representation of each element, optionally transforming it and joining using specified suffix.
Iterable.joinToString(suffux=' ', transform=null)
- if `Iterable` `isEmpty`, the empty string `""` is returned.
- `suffix` is inserted between items when there are more than one.
- `transform` of specified is applied to each element, otherwise its `toString()` method is used.
Here is the sample:
assertEquals( (1..3).joinToString(), "1 2 3")
assertEquals( (1..3).joinToString(":"), "1:2:3")
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
>>> void
## Instance methods:
| fun/method | description |
|-----------------|---------------------------------------------------------------------------------|
| toList() | create a list from iterable |
| toSet() | create a set from iterable |
| contains(i) | check that iterable contains `i` |
| `i in iterator` | same as `contains(i)` |
| isEmpty() | check iterable is empty |
| forEach(f) | call f for each element |
| toMap() | create a map from list of key-value pairs (arrays of 2 items or like) |
| map(f) | create a list of values returned by `f` called for each element of the iterable |
| indexOf(i) | return index if the first encounter of i or a negative value if not found |
| associateBy(kf) | create a map where keys are returned by kf that will be called for each element |
| first | first element (1) |
| last | last element (1) |
| take(n) | return [Iterable] of up to n first elements |
| taleLast(n) | return [Iterable] of up to n last elements |
| drop(n) | return new [Iterable] without first n elements |
| dropLast(n) | return new [Iterable] without last n elements |
| fun/method | description |
|-------------------|---------------------------------------------------------------------------|
| toList() | create a list from iterable |
| toSet() | create a set from iterable |
| contains(i) | check that iterable contains `i` |
| `i in iterator` | same as `contains(i)` |
| isEmpty() | check iterable is empty |
| forEach(f) | call f for each element |
| toMap() | create a map from list of key-value pairs (arrays of 2 items or like) |
| map(f) | create a list of values returned by `f` called for each element of the iterable |
| indexOf(i) | return index if the first encounter of i or a negative value if not found |
| associateBy(kf) | create a map where keys are returned by kf that will be called for each element |
| first | first element (1) |
| last | last element (1) |
| take(n) | return [Iterable] of up to n first elements |
| taleLast(n) | return [Iterable] of up to n last elements |
| drop(n) | return new [Iterable] without first n elements |
| dropLast(n) | return new [Iterable] without last n elements |
| joinToString(s,t) | convert iterable to string, see (2) |
(1)
: throws `NoSuchElementException` if there is no such element
(2)
: `joinToString(suffix=" ",transform=null)`: suffix is inserted between items if there are more than one, trasnfom is optional function applied to each item that must return result string for an item, otherwise `item.toString()` is used.
fun Iterable.toList(): List
fun Iterable.toSet(): Set
fun Iterable.indexOf(element): Int

View File

@ -112,6 +112,49 @@ arguments list in almost arbitrary ways. For example:
)
>>> void
,
# Annotations
Annotation in Lyng resembles these proposed for Javascript. Annotation is just regular functions that, if used as annotation, are called when defining a function, var, val or class.
## Function annotation
When used without params, annotation calls a function with two arguments: actual function name and callable function body. Function annotation __must return callable for the function__, either what it received as a second argument (most often), or something else. Annotation name convention is upper scaled:
var annotated = false
// this is annotation function:
fun Special(name, body) {
assertEquals("foo", name)
annotated = true
{ body(it) + 100 }
}
@Special
fun foo(value) { value + 1 }
assert(annotated)
assertEquals(111, foo( 10 ))
>>> void
Function annotation can have more args specified at call time. There arguments must follow two mandatory ones (name and body). Use default values in order to allow parameterless annotation to be used simultaneously.
val registered = Map()
// it is recommended to provide defaults for extra parameters:
fun Registered(name, body, overrideName = null) {
registered[ overrideName ?: name ] = body
body
}
// witout parameters is Ok as we provided default value
@Registered
fun foo() { "called foo" }
@Registered("bar")
fun foo2() { "called foo2" }
assertEquals(registered["foo"](), "called foo")
assertEquals(registered["bar"](), "called foo2")
>>> void
[parallelism]: parallelism.md

View File

@ -0,0 +1,14 @@
Provide:
fun outer(a1)
// a1 is caller.a1:arg
val a1_local = a1 + 1
// we return lambda:
{ it ->
// a1_local
a1_lcoal + it
}
}

46
docs/serialization.md Normal file
View File

@ -0,0 +1,46 @@
# Lyng serialization
Lyng has builting binary bit-effective serialization format, called Lynon for LYng Object Notation. It is typed, binary, implements caching, automatic compression, variable-length ints, one-bit Booleans an many nice features.
It is as simple as:
import lyng.serialization
val text = "
We hold these truths to be self-evident, that all men are created equal,
that they are endowed by their Creator with certain unalienable Rights,
that among these are Life, Liberty and the pursuit of Happiness.
"
val encodedBits = Lynon.encode(text)
// decode bits source:
assertEquals( text, Lynon.decode(encodedBits) )
// compression was used automatically
assert( text.length > encodedBits.toBuffer().size )
>>> void
Any class you create is serializable by default; lynon serializes first constructor fields, then any `var` member fields:
import lyng.serialization
class Point(x,y)
val p = Lynon.decode( Lynon.encode( Point(5,6) ) )
assertEquals( 5, p.x )
assertEquals( 6, p.y )
>>> void
just as expected.
Important is to understand that normally `Lynon.decode` wants [BitBuffer], as `Lynon.encode` produces. If you have the regular [Buffer], be sure to convert it:
buffer.toBitInput()
this possibly creates extra zero bits at the end, as bit content could be shorter than byte-grained but for the Lynon format it does not make sense. Note that when you serialize [BitBuffer], exact number of bits is written. To convert bit buffer to bytes:
Lynon.encode("hello").toBuffer()
(topic is incomplete and under construction)

View File

@ -225,6 +225,19 @@ It works much like `also`, but is executed in the context of the source object:
assertEquals(p, Point(2,3))
>>> void
## run
Executes a block after it returning the value passed by the block. for example, can be used with elvis operator:
var someVar = null
val result = someVar ?: run {
someVar = 121
"reset"
}
assertEquals("reset", result)
assertEquals(121, someVar)
>>> void
## Math
It is rather simple, like everywhere else:
@ -475,7 +488,7 @@ Lyng has built-in mutable array class `List` with simple literals:
[1, "two", 3.33].size
>>> 3
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable].
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable]. Please read [Iterable], many collection based methods are implemented there.
Lists can contain any type of objects, lists too:
@ -747,7 +760,7 @@ You can thest that _when expression_ is _contained_, or not contained, in some o
`!in container`. The container is any object that provides `contains` method, otherwise the runtime exception will be
thrown.
Typical builtin types that are containers (e.g. support `conain`):
Typical builtin types that are containers (e.g. support `contains`):
| class | notes |
|------------|------------------------------------------------|
@ -756,6 +769,8 @@ Typical builtin types that are containers (e.g. support `conain`):
| List | faster than Array's |
| String | character in string or substring in string (3) |
| Range | object is included in the range (2) |
| Buffer | byte is in buffer |
| RingBuffer | object is in buffer |
(1)
: Iterable is not the container as it can be infinite
@ -1120,6 +1135,25 @@ These should be imported from [lyng.time](time.md). For example:
See [more docs on time manipulation](time.md)
# Enums
For the moment, only simple enums are implemented. Enum is a list of constants, represented also by their _ordinal_ - [Int] value.
enum Color {
RED, GREEN, BLUE
}
assert( Color.RED is Color )
assertEquals( 2, Color.BLUE.ordinal )
assertEquals( "BLUE", Color.BLUE.name )
assertEquals( [Color.RED,Color.GREEN,Color.BLUE], Color.entries)
assertEquals( Color.valueOf("GREEN"), Color.GREEN )
>>> void
Enums are serialized as ordinals. Please note that due to caching, serialized string arrays could be even more compact than enum arrays, until `Lynon.encodeTyped` will be implemented.
# Comments
// single line comment
@ -1277,18 +1311,21 @@ if blank, will be removed too, for example:
See [math functions](math.md). Other general purpose functions are:
| name | description |
|--------------------------------------------|-----------------------------------------------------------|
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
| assertEquals(a,b) | |
| assertNotEquals(a,b) | |
| assertTrows { /* block */ } | |
| check(condition, message=<default>) | throws IllegalStateException" of condition isn't met |
| require(condition, message=<default>) | throws IllegalArgumentException" of condition isn't met |
| println(args...) | Open for overriding, it prints to stdout with newline. |
| print(args...) | Open for overriding, it prints to stdout without newline. |
| flow {} | create flow sequence, see [parallelism] |
| delay, launch, yield | see [parallelism] |
| name | description |
|----------------------------------------------|------------------------------------------------------------|
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
| assertEquals(a,b) | |
| assertNotEquals(a,b) | |
| assertTrows { /* block */ } | |
| check(condition, message=<default>) | throws IllegalStateException" of condition isn't met |
| require(condition, message=<default>) | throws IllegalArgumentException" of condition isn't met |
| println(args...) | Open for overriding, it prints to stdout with newline. |
| print(args...) | Open for overriding, it prints to stdout without newline. |
| flow {} | create flow sequence, see [parallelism] |
| delay, launch, yield | see [parallelism] |
| cached(builder) | remembers builder() on first invocation and return it then |
| let, also, apply, run | see above, flow controls |
# Built-in constants

View File

@ -1,3 +1,20 @@
#
# Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
#Gradle
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
org.gradle.caching=true

View File

@ -1,3 +1,20 @@
#
# Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip

5
gradlew vendored
View File

@ -1,13 +1,13 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
##############################################################################
#

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
plugins {
kotlin("multiplatform") version "2.1.21"
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych
import com.github.ajalt.clikt.core.CliktCommand

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych
import kotlin.system.exitProcess

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package net.sergeych

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng_cli
import net.sergeych.runMain

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import net.sergeych.runMain
fun main(args: Array<String>) {

View File

@ -1,4 +1,23 @@
@file:OptIn(ExperimentalForeignApi::class, ExperimentalForeignApi::class, ExperimentalForeignApi::class)
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
@file:OptIn(ExperimentalForeignApi::class, ExperimentalForeignApi::class, ExperimentalForeignApi::class,
ExperimentalForeignApi::class
)
package net.sergeych

View File

@ -1,10 +1,27 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.STRING
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych"
version = "0.8.6-SNAPSHOT"
version = "0.8.14-SNAPSHOT"
buildscript {
repositories {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
enum class AccessType(val isMutable: Boolean) {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj
@ -31,13 +48,12 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
arguments: Arguments = scope.args,
defaultAccessType: AccessType = AccessType.Var,
defaultVisibility: Visibility = Visibility.Public,
defaultRecordType: ObjRecord.Type = ObjRecord.Type.ConstructorField
) {
fun assign(a: Item, value: Obj) {
scope.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable,
value.byValueCopy(),
a.visibility ?: defaultVisibility,
recordType = defaultRecordType)
recordType = ObjRecord.Type.Argument)
}
// will be used with last lambda arg fix

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
//fun buildDoubleFromParts(

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.ObjRecord
@ -7,10 +24,33 @@ import net.sergeych.lyng.obj.ObjRecord
* Inherits [Scope.args] and [Scope.thisObj] from [callScope] and adds lookup for symbols
* from [closureScope] with proper precedence
*/
class ClosureScope(val callScope: Scope,val closureScope: Scope) : Scope(callScope, callScope.args, thisObj = callScope.thisObj) {
class ClosureScope(val callScope: Scope, val closureScope: Scope) :
Scope(callScope, callScope.args, thisObj = callScope.thisObj) {
override fun get(name: String): ObjRecord? {
// closure should be treated below callScope
return super.get(name) ?: closureScope.get(name)
// we take arguments from the callerScope, the rest
// from the closure.
// note using super, not callScope, as arguments are assigned by the constructor
// and are not assigned yet to vars in callScope self:
super.objects[name]?.let {
// if( name == "predicate" ) {
// println("predicate: ${it.type.isArgument}: ${it.value}")
// }
if( it.type.isArgument ) return it
}
return closureScope.get(name)
}
}
class ApplyScope(_parent: Scope,val applied: Scope) : Scope(_parent, thisObj = applied.thisObj) {
override fun get(name: String): ObjRecord? {
return applied.get(name) ?: super.get(name)
}
override fun applyClosure(closure: Scope): Scope {
return this
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
sealed class CodeContext {
class Module(@Suppress("unused") val packageName: String?): CodeContext()
class Function(val name: String): CodeContext()
class ClassBody(val name: String): CodeContext()
}

View File

@ -1,5 +1,23 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import ObjEnumClass
import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportProvider
@ -27,6 +45,17 @@ class Compiler(
private fun popInitScope(): MutableList<Statement> = initStack.removeLast()
private val codeContexts = mutableListOf<CodeContext>(CodeContext.Module(null))
private suspend fun <T> inCodeContext(context: CodeContext, f: suspend () -> T): T {
return try {
codeContexts.add(context)
f()
} finally {
codeContexts.removeLast()
}
}
private suspend fun parseScript(): Script {
val statements = mutableListOf<Statement>()
val start = cc.currentPos()
@ -69,7 +98,7 @@ class Compiler(
statements += it
}
if (s == null) {
when( t.type ) {
when (t.type) {
Token.Type.RBRACE, Token.Type.EOF, Token.Type.SEMICOLON -> {}
else ->
throw ScriptError(t.pos, "unexpeced `${t.value}` here")
@ -96,7 +125,10 @@ class Compiler(
return result.toString()
}
private var lastAnnotation: (suspend (Scope, ObjString, Statement) -> Statement)? = null
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
lastAnnotation = null
while (true) {
val t = cc.next()
return when (t.type) {
@ -113,6 +145,11 @@ class Compiler(
parseExpression()
}
Token.Type.ATLABEL -> {
lastAnnotation = parseAnnotation(t)
continue
}
Token.Type.LABEL -> continue
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
@ -339,6 +376,13 @@ class Compiler(
}
"throw" -> {
val s = parseThrowStatement()
operand = Accessor {
s.execute(it).asReadonly
}
}
else -> operand?.let { left ->
// selector: <lvalue>, '.' , <id>
// we replace operand with selector code, that
@ -478,7 +522,7 @@ class Compiler(
val callStatement = statement {
// and the source closure of the lambda which might have other thisObj.
val context = ClosureScope(this, closure!!) //AppliedScope(closure!!, args, this)
val context = this.applyClosure(closure!!)
if (argsDeclaration == null) {
// no args: automatic var 'it'
val l = args.list
@ -490,7 +534,7 @@ class Compiler(
// more args: it is a list of args
else -> ObjList(l.toMutableList())
}
context.addItem("it", false, itValue)
context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
} else {
// assign vars as declared the standard way
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
@ -499,7 +543,7 @@ class Compiler(
}
return Accessor { x ->
if (closure == null) closure = x
closure = x
callStatement.asReadonly
}
}
@ -818,6 +862,26 @@ class Compiler(
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number")
}
suspend fun parseAnnotation(t: Token): (suspend (Scope, ObjString, Statement) -> Statement) {
val extraArgs = parseArgsOrNull()
println("annotation ${t.value}: args: $extraArgs")
return { scope, name, body ->
val extras = extraArgs?.first?.toArguments(scope, extraArgs.second)?.list
val required = listOf(name, body)
val args = extras?.let { required + it } ?: required
val fn = scope.get(t.value)?.value ?: scope.raiseSymbolNotFound("annotation not found: ${t.value}")
if (fn !is Statement) scope.raiseIllegalArgument("annotation must be callable, got ${fn.objClass}")
(fn.execute(scope.copy(Arguments(args))) as? Statement)
?: scope.raiseClassCastError("function annotation must return callable")
}
}
suspend fun parseArgsOrNull(): Pair<List<ParsedArgument>, Boolean>? =
if (cc.skipNextIf(Token.Type.LPAREN))
parseArgs()
else
null
/**
* Parse keyword-starting statement.
* @return parsed statement or null if, for example. [id] is not among keywords
@ -831,7 +895,8 @@ class Compiler(
"break" -> parseBreakStatement(id.pos)
"continue" -> parseContinueStatement(id.pos)
"if" -> parseIfStatement()
"class" -> parseClassDeclaration(false)
"class" -> parseClassDeclaration()
"enum" -> parseEnumDeclaration()
"try" -> parseTryStatement()
"throw" -> parseThrowStatement()
"when" -> parseWhenStatement()
@ -1118,78 +1183,115 @@ class Compiler(
}
}
private suspend fun parseClassDeclaration(isStruct: Boolean): Statement {
private fun parseEnumDeclaration(): Statement {
val nameToken = cc.requireToken(Token.Type.ID)
val constructorArgsDeclaration =
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
parseArgsDeclaration(isClassDeclaration = true)
else null
// so far only simplest enums:
val names = mutableListOf<String>()
// skip '{'
cc.skipTokenOfType(Token.Type.LBRACE)
if (constructorArgsDeclaration != null && constructorArgsDeclaration.endTokenType != Token.Type.RPAREN)
throw ScriptError(
nameToken.pos,
"Bad class declaration: expected ')' at the end of the primary constructor"
)
do {
val t = cc.skipWsTokens()
when (t.type) {
Token.Type.ID -> {
names += t.value
val t1 = cc.skipWsTokens()
when (t1.type) {
Token.Type.COMMA ->
continue
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
val t = cc.next()
Token.Type.RBRACE -> break
else -> {
t1.raiseSyntax("unexpected token")
}
}
}
pushInitScope()
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
// parse body
parseScript().also {
cc.skipTokens(Token.Type.RBRACE)
else -> t.raiseSyntax("expected enum entry name")
}
} else {
cc.previous()
null
}
val initScope = popInitScope()
// create class
val className = nameToken.value
@Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
@Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
// create instance constructor
// create custom objClass with all fields and instance constructor
val constructorCode = statement {
// constructor code is registered with class instance and is called over
// new `thisObj` already set by class to ObjInstance.instanceContext
thisObj as ObjInstance
// the context now is a "class creation context", we must use its args to initialize
// fields. Note that 'this' is already set by class
constructorArgsDeclaration?.assignToContext(this)
bodyInit?.execute(this)
thisObj
}
// inheritance must alter this code:
val newClass = ObjInstanceClass(className).apply {
instanceConstructor = constructorCode
constructorMeta = constructorArgsDeclaration
}
} while (true)
return statement {
// the main statement should create custom ObjClass instance with field
// accessors, constructor registration, etc.
addItem(className, false, newClass)
if (initScope.isNotEmpty()) {
val classScope = copy(newThisObj = newClass)
newClass.classScope = classScope
for (s in initScope)
s.execute(classScope)
.also { println("executed, ${classScope.objects}") }
ObjEnumClass.createSimpleEnum(nameToken.value, names).also {
addItem(nameToken.value, false, it, recordType = ObjRecord.Type.Enum)
}
newClass
}
}
private suspend fun parseClassDeclaration(): Statement {
val nameToken = cc.requireToken(Token.Type.ID)
return inCodeContext(CodeContext.ClassBody(nameToken.value)) {
val constructorArgsDeclaration =
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
parseArgsDeclaration(isClassDeclaration = true)
else null
if (constructorArgsDeclaration != null && constructorArgsDeclaration.endTokenType != Token.Type.RPAREN)
throw ScriptError(
nameToken.pos,
"Bad class declaration: expected ')' at the end of the primary constructor"
)
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
val t = cc.next()
pushInitScope()
val bodyInit: Statement? = if (t.type == Token.Type.LBRACE) {
// parse body
parseScript().also {
cc.skipTokens(Token.Type.RBRACE)
}
} else {
cc.previous()
null
}
val initScope = popInitScope()
// create class
val className = nameToken.value
// @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
// @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
// create instance constructor
// create custom objClass with all fields and instance constructor
val constructorCode = statement {
// constructor code is registered with class instance and is called over
// new `thisObj` already set by class to ObjInstance.instanceContext
thisObj as ObjInstance
// the context now is a "class creation context", we must use its args to initialize
// fields. Note that 'this' is already set by class
constructorArgsDeclaration?.assignToContext(this)
bodyInit?.execute(this)
thisObj
}
// inheritance must alter this code:
val newClass = ObjInstanceClass(className).apply {
instanceConstructor = constructorCode
constructorMeta = constructorArgsDeclaration
}
statement {
// the main statement should create custom ObjClass instance with field
// accessors, constructor registration, etc.
addItem(className, false, newClass)
if (initScope.isNotEmpty()) {
val classScope = copy(newThisObj = newClass)
newClass.classScope = classScope
for (s in initScope)
s.execute(classScope)
}
newClass
}
}
}
private fun getLabel(maxDepth: Int = 2): String? {
var cnt = 0
@ -1558,8 +1660,7 @@ class Compiler(
}
}
private suspend fun
parseFunctionDeclaration(
private suspend fun parseFunctionDeclaration(
visibility: Visibility = Visibility.Public,
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
isExtern: Boolean = false,
@ -1572,6 +1673,9 @@ class Compiler(
throw ScriptError(t.pos, "Expected identifier after 'fun'")
else t.value
val annotation = lastAnnotation
val parentContext = codeContexts.last()
t = cc.next()
// Is extension?
if (t.type == Token.Type.DOT) {
@ -1595,57 +1699,66 @@ class Compiler(
if (cc.current().type == Token.Type.COLON) parseTypeDeclaration()
// Here we should be at open body
val fnStatements = if (isExtern)
statement { raiseError("extern function not provided: $name") }
else
parseBlock()
return inCodeContext(CodeContext.Function(name)) {
var closure: Scope? = null
// Here we should be at open body
val fnStatements = if (isExtern)
statement { raiseError("extern function not provided: $name") }
else
parseBlock()
val fnBody = statement(t.pos) { callerContext ->
callerContext.pos = start
var closure: Scope? = null
// restore closure where the function was defined, and making a copy of it
// for local space (otherwise it will write local stuff to closure!)
val context = closure?.let { ClosureScope(callerContext, it) }
?: callerContext.raiseError("bug: closure not set")
val fnBody = statement(t.pos) { callerContext ->
callerContext.pos = start
// load params from caller context
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
if (extTypeName != null) {
context.thisObj = callerContext.thisObj
}
fnStatements.execute(context)
}
val fnCreateStatement = statement(start) { context ->
// we added fn in the context. now we must save closure
// for the function
closure = context
extTypeName?.let { typeName ->
// class extension method
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
if (type !is ObjClass) context.raiseClassCastError("$typeName is not the class instance")
type.addFn(name, isOpen = true) {
// ObjInstance has a fixed instance scope, so we need to build a closure
(thisObj as? ObjInstance)?.let { i ->
fnBody.execute(ClosureScope(this, i.instanceScope))
}
// other classes can create one-time scope for this rare case:
?: fnBody.execute(thisObj.autoInstanceScope(this))
// restore closure where the function was defined, and making a copy of it
// for local space. If there is no closure, we are in, say, class context where
// the closure is in the class initialization and we needn't more:
val context = closure?.let { ClosureScope(callerContext, it) }
?: callerContext
// load params from caller context
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
if (extTypeName != null) {
context.thisObj = callerContext.thisObj
}
fnStatements.execute(context)
}
// regular function/method
?: context.addItem(name, false, fnBody, visibility)
// as the function can be called from anywhere, we have
// saved the proper context in the closure
fnBody
val fnCreateStatement = statement(start) { context ->
// we added fn in the context. now we must save closure
// for the function, unless we're in the class scope:
if (isStatic || parentContext !is CodeContext.ClassBody)
closure = context
val annotatedFnBody = annotation?.invoke(context, ObjString(name), fnBody)
?: fnBody
extTypeName?.let { typeName ->
// class extension method
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
if (type !is ObjClass) context.raiseClassCastError("$typeName is not the class instance")
type.addFn(name, isOpen = true) {
// ObjInstance has a fixed instance scope, so we need to build a closure
(thisObj as? ObjInstance)?.let { i ->
annotatedFnBody.execute(ClosureScope(this, i.instanceScope))
}
// other classes can create one-time scope for this rare case:
?: annotatedFnBody.execute(thisObj.autoInstanceScope(this))
}
}
// regular function/method
?: context.addItem(name, false, annotatedFnBody, visibility)
// as the function can be called from anywhere, we have
// saved the proper context in the closure
annotatedFnBody
}
if (isStatic) {
currentInitScope += fnCreateStatement
NopStatement
} else
fnCreateStatement
}
return if (isStatic) {
currentInitScope += fnCreateStatement
NopStatement
} else
fnCreateStatement
}
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
@ -1680,24 +1793,31 @@ class Compiler(
val eqToken = cc.next()
var setNull = false
if (eqToken.type != Token.Type.ASSIGN) {
if (!isMutable)
throw ScriptError(start, "val must be initialized")
else {
cc.previous()
setNull = true
val isDelegate = if (eqToken.isId("by")) {
true
} else {
if (eqToken.type != Token.Type.ASSIGN) {
if (!isMutable)
throw ScriptError(start, "val must be initialized")
else {
cc.previous()
setNull = true
}
}
false
}
val initialExpression = if (setNull) null else parseStatement(true)
val initialExpression = if (setNull) null
else parseStatement(true)
?: throw ScriptError(eqToken.pos, "Expected initializer expression")
if (isStatic) {
// find objclass instance: this is tricky: this code executes in object initializer,
// when creating instance, but we need to execute it in the class initializer which
// is missing as for now. Add it to the compiler context?
// add there
// return
// if (isDelegate) throw ScriptError(start, "static delegates are not yet implemented")
currentInitScope += statement {
val initValue = initialExpression?.execute(this)?.byValueCopy() ?: ObjNull
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, pos)
@ -1711,12 +1831,32 @@ class Compiler(
if (context.containsLocal(name))
throw ScriptError(nameToken.pos, "Variable $name is already defined")
// init value could be a val; when we initialize by-value type var with it, we need to
// create a separate copy:
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
initValue
if (isDelegate) {
TODO()
// println("initial expr = $initialExpression")
// val initValue =
// (initialExpression?.execute(context.copy(Arguments(ObjString(name)))) as? Statement)
// ?.execute(context.copy(Arguments(ObjString(name))))
// ?: context.raiseError("delegate initialization required")
// println("delegate init: $initValue")
// if (!initValue.isInstanceOf(ObjArray))
// context.raiseIllegalArgument("delegate initialized must be an array")
// val s = initValue.getAt(context, 1)
// val setter = if (s == ObjNull) statement { raiseNotImplemented("setter is not provided") }
// else (s as? Statement) ?: context.raiseClassCastError("setter must be a callable")
// ObjDelegate(
// (initValue.getAt(context, 0) as? Statement)
// ?: context.raiseClassCastError("getter must be a callable"), setter
// ).also {
// context.addItem(name, isMutable, it, visibility, recordType = ObjRecord.Type.Field)
// }
} else {
// init value could be a val; when we initialize by-value type var with it, we need to
// create a separate copy:
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
initValue
}
}
}
@ -1835,7 +1975,15 @@ class Compiler(
Operator.simple(Token.Type.IS, lastPriority) { _, a, b -> ObjBool(a.isInstanceOf(b)) },
Operator.simple(Token.Type.NOTIS, lastPriority) { _, a, b -> ObjBool(!a.isInstanceOf(b)) },
Operator.simple(Token.Type.ELVIS, ++lastPriority) { _, a, b -> if (a == ObjNull) b else a },
Operator(Token.Type.ELVIS, ++lastPriority, 2) { _: Pos, a: Accessor, b: Accessor ->
Accessor {
val aa = a.getter(it).value
(
if (aa != ObjNull) aa
else b.getter(it).value
).asReadonly
}
},
// shuttle <=> 6
Operator.simple(Token.Type.SHUTTLE, ++lastPriority) { c, a, b ->

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
class CompilerContext(val tokens: List<Token>) {
@ -20,7 +37,7 @@ class CompilerContext(val tokens: List<Token>) {
fun hasNext() = currentIndex < tokens.size
fun hasPrevious() = currentIndex > 0
fun next() =
if( currentIndex < tokens.size ) tokens[currentIndex++]
if (currentIndex < tokens.size) tokens[currentIndex++]
else Token("", tokens.last().pos, Token.Type.EOF)
// throw IllegalStateException("No more tokens")
@ -61,7 +78,7 @@ class CompilerContext(val tokens: List<Token>) {
*/
fun skipId(name: String): Boolean {
current().let { t ->
if( t.type == Token.Type.ID && t.value == name ) {
if (t.type == Token.Type.ID && t.value == name) {
next()
return true
}
@ -93,6 +110,20 @@ class CompilerContext(val tokens: List<Token>) {
} else true
}
/**
* If next token is one of these types, skip it.
* @return true if token was found and skipped
*/
fun skipNextIf(vararg types: Token.Type): Boolean {
val t = next()
return if (t.type in types)
true
else {
previous()
false
}
}
@Suppress("unused")
fun skipTokens(vararg tokenTypes: Token.Type) {
while (next().type in tokenTypes) { /**/
@ -143,19 +174,24 @@ class CompilerContext(val tokens: List<Token>) {
fun matchQualifiers(keyword: String, vararg qualifiers: String): Boolean {
val pos = savePos()
var count = 0
while( count < qualifiers.size) {
while (count < qualifiers.size) {
val t = next()
when(t.type) {
when (t.type) {
Token.Type.ID -> {
if( t.value in qualifiers ) count++
else { restorePos(pos); return false }
if (t.value in qualifiers) count++
else {
restorePos(pos); return false
}
}
Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT, Token.Type.NEWLINE -> {}
else -> { restorePos(pos); return false }
else -> {
restorePos(pos); return false
}
}
}
val t = next()
if( t.type == Token.Type.ID && t.value == keyword ) {
if (t.type == Token.Type.ID && t.value == keyword) {
return true
} else {
restorePos(pos)
@ -168,7 +204,7 @@ class CompilerContext(val tokens: List<Token>) {
* Note that [Token.Type.EOF] is not considered a whitespace token.
*/
fun skipWsTokens(): Token {
while( current().type in wstokens ) {
while (current().type in wstokens) {
next()
}
return next()

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.Accessor

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.ObjRecord

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
val digitsSet = ('0'..'9').toSet()

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
data class Pos(val source: Source, val line: Int, val column: Int) {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.*
@ -71,6 +88,11 @@ open class Scope(
throw ExecutionError(obj)
}
@Suppress("unused")
fun raiseNotFound(message: String="not found"): Nothing {
throw ExecutionError(ObjNotFoundException(this,message))
}
inline fun <reified T : Obj> requiredArg(index: Int): T {
if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}")
return (args.list[index].byValueCopy() as? T)
@ -109,9 +131,16 @@ open class Scope(
open operator fun get(name: String): ObjRecord? =
if (name == "this") thisObj.asReadonly
else {
objects[name]
(objects[name]
?: parent?.get(name)
?: thisObj.objClass.getInstanceMemberOrNull(name)
?: thisObj.objClass
.getInstanceMemberOrNull(name)
)
// ?.also {
// if( name == "predicate") {
// println("got predicate $it")
// }
// }
}
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
@ -220,6 +249,8 @@ open class Scope(
}
open fun applyClosure(closure: Scope): Scope = ClosureScope(this, closure)
companion object {
fun new(): Scope =

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import kotlinx.coroutines.delay

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
@file:Suppress("CanBeParameter")
package net.sergeych.lyng

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
interface SecurityManager {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
class Source(val fileName: String, text: String) {

View File

@ -1,8 +1,33 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
data class Token(val value: String, val pos: Pos, val type: Type) {
fun raiseSyntax(text: String): Nothing {
throw ScriptError(pos, text)
}
val isComment: Boolean by lazy { type == Type.SINLGE_LINE_COMMENT || type == Type.MULTILINE_COMMENT }
fun isId(text: String) =
type == Type.ID && value == text
@Suppress("unused")
enum class Type {
ID, INT, REAL, HEX, STRING, CHAR,

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
@file:Suppress("unused")
package net.sergeych.lyng

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
enum class Visibility {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.sync.Mutex
@ -68,13 +85,15 @@ open class Obj {
name: String,
args: Arguments = Arguments.EMPTY,
onNotFoundResult: Obj?=null
): Obj =
objClass.getInstanceMemberOrNull(name)?.value?.invoke(
): Obj {
return objClass.getInstanceMemberOrNull(name)?.value?.invoke(
scope,
this,
args)
args
)
?: onNotFoundResult
?: scope.raiseSymbolNotFound(name)
}
open suspend fun getInstanceMethod(
scope: Scope,
@ -144,6 +163,8 @@ open class Obj {
open suspend fun assign(scope: Scope, other: Obj): Obj? = null
open fun getValue(scope: Scope) = this
/**
* a += b
* if( the operation is not defined, it returns null and the compiler would try
@ -269,6 +290,9 @@ open class Obj {
addFn("toString") {
thisObj.asStr
}
addFn("inspect", true) {
thisObj.inspect().toObj()
}
addFn("contains") {
ObjBool(thisObj.contains(this, args.firstAndOnly()))
}
@ -277,15 +301,23 @@ open class Obj {
args.firstAndOnly().callOn(copy(Arguments(thisObj)))
}
addFn("apply") {
val newContext = (thisObj as? ObjInstance)?.instanceScope ?: this
args.firstAndOnly()
.callOn(newContext)
val body = args.firstAndOnly()
(thisObj as? ObjInstance)?.let {
println("apply in ${thisObj is ObjInstance}, ${it.instanceScope}")
body.callOn(ApplyScope(this, it.instanceScope))
} ?: run {
println("apply on non-instance $thisObj")
body.callOn(this)
}
thisObj
}
addFn("also") {
args.firstAndOnly().callOn(copy(Arguments(thisObj)))
thisObj
}
addFn("run") {
args.firstAndOnly().callOn(this)
}
addFn("getAt") {
requireExactCount(1)
thisObj.getAt(this, requiredArg<Obj>(0))
@ -513,6 +545,7 @@ open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val me
"IterationEndException",
"AccessException",
"UnknownException",
"NotFoundException"
)) {
scope.addConst(name, getOrCreateExceptionClass(name))
}
@ -556,3 +589,6 @@ class ObjUnknownException(scope: Scope, message: String = "access not allowed er
class ObjIllegalOperationException(scope: Scope, message: String = "Operation is illegal") :
ObjException("IllegalOperationException", scope, message)
class ObjNotFoundException(scope: Scope, message: String = "not found") :
ObjException("NotFoundException", scope, message)

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
val ObjArray by lazy {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.bintools.toDump

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,20 +1,44 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import net.sergeych.bintools.decodeHex
import net.sergeych.bintools.encodeToHex
import net.sergeych.bintools.toDump
import net.sergeych.lyng.Scope
import net.sergeych.lyng.statement
import net.sergeych.lynon.BitArray
import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder
import net.sergeych.lynon.LynonType
import net.sergeych.mp_tools.decodeBase64Url
import net.sergeych.mp_tools.encodeToBase64Url
import kotlin.math.min
open class ObjBuffer(val byteArray: UByteArray) : Obj() {
override val objClass: ObjClass = type
val hex by lazy { byteArray.encodeToHex("")}
val base64 by lazy { byteArray.toByteArray().encodeToBase64Url()}
fun checkIndex(scope: Scope, index: Obj): Int {
if (index !is ObjInt)
scope.raiseIllegalArgument("index must be Int")
@ -66,9 +90,7 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
} else scope.raiseIllegalArgument("can't concatenate buffer with ${other.inspect()}")
}
override fun toString(): String {
return "Buffer(${byteArray.encodeToHex()})"
}
override fun toString(): String = base64
override fun equals(other: Any?): Boolean {
if (this === other) return true
@ -85,6 +107,8 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
encoder.encodeCachedBytes(byteArray.asByteArray())
}
override fun inspect(): String = "Buf($base64)"
companion object {
private suspend fun createBufferFrom(scope: Scope, obj: Obj): ObjBuffer =
when (obj) {
@ -141,11 +165,27 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
})
}.apply {
addClassFn("decodeBase64") {
ObjBuffer(requireOnlyArg<Obj>().toString().decodeBase64Url().asUByteArray())
}
addClassFn("decodeHex") {
ObjBuffer(requireOnlyArg<Obj>().toString().decodeHex().asUByteArray())
}
createField("size",
statement {
(thisObj as ObjBuffer).byteArray.size.toObj()
}
)
createField("hex",
statement {
thisAs<ObjBuffer>().hex.toObj()
}
)
createField("base64",
statement {
thisAs<ObjBuffer>().base64.toObj()
}
)
addFn("decodeUtf8") {
ObjString(
thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString()
@ -161,6 +201,9 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
thisAs<ObjBuffer>().byteArray.toByteArray().toDump()
)
}
addFn("toBitInput") {
ObjBitBuffer(BitArray(thisAs<ObjBuffer>().byteArray, 8))
}
}
}
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.*

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
/**

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.CompletableDeferred

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.Deferred

View File

@ -0,0 +1,42 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
//class ObjDelegateContext()
//
//class ObjDelegate(
// val getter: Statement,
// val setter: Statement = statement { raiseNotImplemented("setter is not implemented") }
//): Obj() {
//
// override suspend fun assign(scope: Scope, other: Obj): Obj? {
// setter.execute(scope.copy(Arguments(other)))
// return other
// }
//
// companion object {
// val type = object: ObjClass("Delegate") {
// override suspend fun callOn(scope: Scope): Obj {
// scope.raiseError("Delegate should not be constructed directly")
// }
// }.apply {
//
// }
// }
//
//}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments

View File

@ -0,0 +1,79 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import net.sergeych.lyng.Scope
import net.sergeych.lyng.obj.*
import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder
import net.sergeych.lynon.LynonType
open class ObjEnumEntry(enumClass: ObjEnumClass, val name: ObjString, val ordinal: ObjInt) : Obj() {
override val objClass = enumClass
override fun toString(): String {
return "$objClass.$name"
}
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
encoder.encodeUnsigned(ordinal.value.toULong())
}
override suspend fun compareTo(scope: Scope, other: Obj): Int {
if( other !is ObjEnumEntry) return -2
if( other.objClass != objClass ) return -2
return ordinal.compareTo(scope, other.ordinal)
}
}
object EnumBase : ObjClass("Enum") {
}
class ObjEnumClass(val name: String) : ObjClass(name, EnumBase) {
val objEntries = ObjList()
val byName by lazy { objEntries.list.associateBy { (it as ObjEnumEntry).name } }
init {
addClassConst("entries", objEntries )
addClassFn("valueOf") {
val name = requireOnlyArg<ObjString>()
byName[name] ?: raiseSymbolNotFound("does not exists: enum ${className}.$name")
}
addFn("name") { thisAs<ObjEnumEntry>().name }
addFn("ordinal") { thisAs<ObjEnumEntry>().ordinal }
}
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
val index = decoder.unpackUnsigned().toInt()
return objEntries.list[index]
}
companion object {
fun createSimpleEnum(enumName: String, names: List<String>): ObjEnumClass {
val klass = ObjEnumClass(enumName)
names.forEachIndexed { index, name ->
val entry = ObjEnumEntry(klass, ObjString(name), ObjInt(index.toLong(), isConst = true))
klass.objEntries.list += entry
klass.addClassConst(name, entry)
}
return klass
}
}
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.DelicateCoroutinesApi
@ -23,12 +40,12 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
val data = requireOnlyArg<Obj>()
try {
val channel = thisAs<ObjFlowBuilder>().output
if( !channel.isClosedForSend )
if (!channel.isClosedForSend)
channel.send(data)
else
throw ScriptFlowIsNoMoreCollected()
} catch (x: Exception) {
if( x !is CancellationException )
if (x !is CancellationException)
x.printStackTrace()
throw ScriptFlowIsNoMoreCollected()
}
@ -45,12 +62,10 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
globalLaunch {
try {
producer.execute(builderScope)
}
catch(x: ScriptFlowIsNoMoreCollected) {
} catch (x: ScriptFlowIsNoMoreCollected) {
x.printStackTrace()
// premature flow closing, OK
}
catch(x: Exception) {
} catch (x: Exception) {
x.printStackTrace()
}
channel.close()
@ -70,7 +85,11 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
}.apply {
addFn("iterator") {
val objFlow = thisAs<ObjFlow>()
ObjFlowIterator( statement { objFlow.producer.execute(ClosureScope(this,objFlow.scope)) } )
ObjFlowIterator(statement {
objFlow.producer.execute(
ClosureScope(this, objFlow.scope)
)
})
}
}
}
@ -88,9 +107,10 @@ class ObjFlowIterator(val producer: Statement) : Obj() {
private var isCancelled = false
private fun checkNotCancelled(scope: Scope) {
if( isCancelled )
if (isCancelled)
scope.raiseIllegalState("iteration is cancelled")
}
suspend fun hasNext(scope: Scope): ObjBool {
checkNotCancelled(scope)
// cold start:

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments
@ -71,18 +88,15 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val vars = instanceVars.values.map { it.value }
if( vars.isNotEmpty()) {
encoder.encodeAnyList(scope, vars)
println("serialized state vars $vars")
}
}
internal suspend fun deserializeStateVars(scope: Scope, decoder: LynonDecoder) {
val localVars = instanceVars.values.toList()
if( localVars.isNotEmpty() ) {
println("gonna read vars")
val vars = decoder.decodeAnyList(scope)
if (vars.size > instanceVars.size)
scope.raiseIllegalArgument("serialized vars has bigger size than instance vars")
println("deser state vars $vars")
for ((i, v) in vars.withIndex()) {
localVars[i].value = v
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments
@ -12,7 +29,6 @@ class ObjInstanceClass(val name: String) : ObjClass(name) {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
val args = decoder.decodeAnyList(scope)
println("deserializing constructor $name, $args params")
val actualSize = constructorMeta?.params?.size ?: 0
if( args.size > actualSize )
scope.raiseIllegalArgument("constructor $name has only $actualSize but serialized version has ${args.size}")

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.datetime.Clock

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
@ -45,7 +62,7 @@ class ObjInt(var value: Long, override val isConst: Boolean = false) : Obj(), Nu
override fun toString(): String = value.toString()
override val objClass: ObjClass = type
override val objClass: ObjClass by lazy { type }
override suspend fun plus(scope: Scope, other: Obj): Obj =
if (other is ObjInt)

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments
@ -42,12 +59,16 @@ val ObjIterable by lazy {
}
addFn("toSet") {
val result = mutableSetOf<Obj>()
val it = thisObj.invokeInstanceMethod(this, "iterator")
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
result += it.invokeInstanceMethod(this, "next")
if( thisObj.isInstanceOf(ObjSet.type) )
thisObj
else {
val result = mutableSetOf<Obj>()
val it = thisObj.invokeInstanceMethod(this, "iterator")
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
result += it.invokeInstanceMethod(this, "next")
}
ObjSet(result)
}
ObjSet(result)
}
addFn("toMap") {

View File

@ -1,4 +1,42 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
val ObjIterator by lazy { ObjClass("Iterator") }
/**
* Iterator should provide lyng-level iterator functions:
*
* - hasNext()
* - next()
* - optional cancelIteration() that _may_ be called when iteration is performed
* only on the part of the iterable entity. Implement it when there are resources
* to be reclaimed on iteration interruption.
*/
val ObjIterator by lazy {
ObjClass("Iterator").apply {
addFn("cancelIteration", true) {
ObjVoid
}
addFn("hasNext", true) {
raiseNotImplemented("hasNext() is not implemented")
}
addFn("next", true) {
raiseNotImplemented("next() is not implemented")
}
}
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
@file:Suppress("unused")
package net.sergeych.lyng.obj

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.flow.map

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.coroutines.sync.Mutex

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
@ -39,7 +56,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
}
companion object {
val type = ObjClass("RangeIterator", ObjIterable).apply {
val type = ObjClass("RangeIterator", ObjIterator).apply {
addFn("hasNext") {
thisAs<ObjRangeIterator>().hasNext().toObj()
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Pos

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
@ -18,8 +35,15 @@ data class ObjRecord(
Field(true, true),
@Suppress("unused")
Fun,
@Suppress("unused")
ConstructorField(true, true),
Other
Argument(true, true),
@Suppress("unused")
Class,
Enum,
Other;
val isArgument get() = this == Argument
}
@Suppress("unused")
fun qualifiedName(name: String): String =

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope
@ -92,7 +109,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
companion object {
val type = object : ObjClass("Set", ObjCollection) {
val type: ObjClass = object : ObjClass("Set", ObjCollection) {
override suspend fun callOn(scope: Scope): Obj {
return ObjSet(scope.args.list.toMutableSet())
}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import kotlinx.serialization.SerialName

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.pacman
import net.sergeych.lyng.*

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.pacman
import net.sergeych.lyng.*

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.pacman
import kotlinx.coroutines.CompletableDeferred

View File

@ -1,7 +1,25 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass
import net.sergeych.lyng.obj.ObjNull
import net.sergeych.lyng.obj.ObjVoid
fun String.toSource(name: String = "eval"): Source = Source(name, this)
@ -28,6 +46,7 @@ abstract class Statement(
abstract suspend fun execute(scope: Scope): Obj
override suspend fun compareTo(scope: Scope, other: Obj): Int {
if( other == ObjNull || other == ObjVoid ) return 1
throw UnsupportedOperationException("not comparable")
}

View File

@ -1,12 +1,41 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.stdlib_included
internal val rootLyng = """
package lyng.stdlib
fun cached(builder) {
var calculated = false
var value = null
{
if( !calculated ) {
value = builder()
calculated = true
}
value
}
}
fun Iterable.filter(predicate) {
val list = this
flow {
for( item in list ) {
if( predicate(item) ) {
if( predicate(item) ) {ln
emit(item)
}
}
@ -21,7 +50,7 @@ fun Iterable.drop(n) {
fun Iterable.first() {
val i = iterator()
if( !i.hasNext() ) throw NoSuchElementException()
i.next()
i.next().also { i.cancelIteration() }
}
fun Iterable.last() {
@ -54,5 +83,26 @@ fun Iterable.takeLast(n) {
buffer
}
fun Iterable.joinToString(prefix=" ", transformer=null) {
var result = null
for( part in this ) {
val transformed = transformer?(part)?.toString() ?: part.toString()
if( result == null ) result = transformed
else result += prefix + transformed
}
result ?: ""
}
fun Iterable.any(predicate): Bool {
for( i in this ) {
if( predicate(i) )
break true
} else false
}
fun Iterable.all(predicate): Bool {
!any { !predicate(it) }
}
""".trimIndent()

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
fun leftMargin(s: String): Int {

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
interface BitInput {
@ -82,6 +99,8 @@ interface BitInput {
fun decompressStringOrNull(): String? = decompressOrNull()?.decodeToString()
fun decompressString(): String = decompress().decodeToString()
@Suppress("unused")
fun unpackDouble(): Double {
val bits = getBits(64)
return Double.fromBits(bits.toLong())

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
@Suppress("unused")

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
interface BitOutput {
@ -84,7 +101,6 @@ interface BitOutput {
val compressed = LZW.compress(source.asUByteArray())
// check that compression is effective including header bits size:
if( compressed.size + 2 < source.size * 8L) {
println("write compressed")
putBit(1)
// LZW algorithm
putBits(0, 2)

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
class DecompressionException(message: String) : IllegalArgumentException(message) {}

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import net.sergeych.lyng.Scope
@ -48,7 +65,6 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
private suspend fun decodeClassObj(scope: Scope): ObjClass {
val className = decodeObject(scope, ObjString.type, null) as ObjString
println("expected class name $className")
return scope.get(className.value)?.value?.let {
if (it !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}")

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import net.sergeych.bintools.ByteChunk

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import net.sergeych.lyng.obj.ObjBool

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import kotlin.math.min
@ -85,7 +102,7 @@ class BitArray(val bytes: UByteArray, val lastByteBits: Int) : BitList {
* in the bit 0x02 of the first byte, etc.
*
* This allows automatic fill of the last byte with zeros. This is important when
* using bytes stored from [asByteArray] or [asUbyteArray]. When converting to
* using bytes stored from [asByteArray] or `asUbyteArray`. When converting to
* bytes, automatic padding to byte size is applied. With such bit order, constructing
* [BitInput] to read from [ByteArray.toUByteArray] result only provides 0 to 7 extra zeroes bits
* at teh end which is often acceptable. To avoid this, use [toBitArray]; the [BitArray]

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
/**

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import net.sergeych.collections.SortedList

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import net.sergeych.bintools.ByteChunk

View File

@ -1,3 +1,20 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lynon
import net.sergeych.lyng.Scope
@ -9,28 +26,43 @@ import net.sergeych.lyng.obj.ObjString
// Most often used types:
val ObjLynonClass = object : ObjClass("Lynon") {
object ObjLynonClass : ObjClass("Lynon") {
suspend fun Scope.encodeAny(obj: Obj): Obj {
suspend fun encodeAny(scope: Scope, obj: Obj): ObjBitBuffer {
val bout = MemoryBitOutput()
val serializer = LynonEncoder(bout)
serializer.encodeAny(this, obj)
serializer.encodeAny(scope, obj)
return ObjBitBuffer(bout.toBitArray())
}
suspend fun Scope.decodeAny(source: Obj): Obj {
if( source !is ObjBitBuffer) throw Exception("Invalid source: $source")
suspend fun decodeAny(scope: Scope, source: Obj): Obj {
if (source !is ObjBitBuffer) throw Exception("Invalid source: $source")
val bin = source.bitArray.toInput()
val deserializer = LynonDecoder(bin)
return deserializer.decodeAny(this)
return deserializer.decodeAny(scope)
}
}.apply {
addClassConst("test", ObjString("test_const"))
addClassFn("encode") {
encodeAny(requireOnlyArg<Obj>())
}
addClassFn("decode") {
decodeAny(requireOnlyArg<Obj>())
init {
addClassConst("test", ObjString("test_const"))
addClassFn("encode") {
encodeAny(this, requireOnlyArg<Obj>())
}
addClassFn("decode") {
decodeAny(this, requireOnlyArg<Obj>())
}
}
}
@Suppress("unused")
suspend fun lynonEncodeAny(scope: Scope, value: Obj): UByteArray =
(ObjLynonClass.encodeAny(scope, value))
.bitArray.asUbyteArray()
@Suppress("unused")
suspend fun lynonDecodeAny(scope: Scope, encoded: UByteArray): Obj =
ObjLynonClass.decodeAny(
scope,
ObjBitBuffer(
BitArray(encoded, 8)
)
)

Some files were not shown because too many files have changed in this diff Show More