Compare commits

...

26 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
804087f16d version bump 2025-08-12 00:16:09 +03:00
6d8eed7b8c added "dynamic" fields (get/set/call fields by name using dynamic standard function 2025-08-12 00:15:45 +03:00
e916d9805a correct end-block token processing (detect more errors) 2025-08-11 22:48:43 +03:00
c398496ee0 refining the readme/2 2025-08-11 14:41:35 +03:00
299738cffd refining the readme 2025-08-11 10:36:29 +03:00
62461c09cc readme fix 2025-08-10 23:41:57 +03:00
63de82393a multiline string literals 2025-08-10 23:32:03 +03:00
ed0a21cb06 added test config 2025-08-10 09:51:04 +03:00
113 changed files with 3169 additions and 292 deletions

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.
~
-->
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tests in 'lyng.lynglib.jvmTest'" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":lynglib:cleanJvmTest" />
<option value=":lynglib:jvmTest" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
</component>

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

@ -2,7 +2,7 @@
A KMP library and a standalone interpreter A KMP library and a standalone interpreter
- simple, compact, intuitive and elegant modern code style: - simple, compact, intuitive and elegant modern code:
``` ```
class Point(x,y) { class Point(x,y) {
@ -15,23 +15,31 @@ fun swapEnds(first, args..., last, f) {
} }
``` ```
- extremely simple Kotlin integration on any platform - extremely simple Kotlin integration on any platform (JVM, JS, WasmJS, Lunux, MacOS, iOS, Windows)
- 100% secure: no access to any API you didn't explicitly provide - 100% secure: no access to any API you didn't explicitly provide
- 100% coroutines! Every function/script is a coroutine, it does not block the thread, no async/await/suspend keyword garbage: - 100% coroutines! Every function/script is a coroutine, it does not block the thread, no async/await/suspend keyword garbage, see [parallelism]
``` ```
val deferred = launch {
delay(1.5) // coroutine is delayed for 1.5s, thread is not blocked! delay(1.5) // coroutine is delayed for 1.5s, thread is not blocked!
"done"
}
// ...
// suspend current coroutine, no thread is blocked again,
// and wait for deferred to return something:
assertEquals("donw", deferred.await())
``` ```
and it is multithreaded on platforms supporting it (automatically, no code changes required, just and it is multithreaded on platforms supporting it (automatically, no code changes required, just
`launch` more coroutines and they will be executed concurrently if possible). See [parallelism] `launch` more coroutines and they will be executed concurrently if possible). See [parallelism]
- functional style and OOP together, multiple inheritance, implementing interfaces for existing classes, writing extensions. - functional style and OOP together, multiple inheritance, implementing interfaces for existing classes, writing extensions.
- Any unicode letters can be used as identifiers: `assert( sin(π/2) == 1 )`. - Any Unicode letters can be used as identifiers: `assert( sin(π/2) == 1 )`.
## Resources: ## Resources:
- [introduction and tutorial](docs/tutorial.md) - start here please - [introduction and tutorial](docs/tutorial.md) - start here please
- [Samples directory](docs/samples) - [Samples directory](docs/samples)
- [Books directory](docs)
## Integration in Kotlin multiplatform ## Integration in Kotlin multiplatform
@ -84,8 +92,8 @@ import com.sun.source.tree.Scope
import new.sergeych.lyng.* import new.sergeych.lyng.*
// simple function // simple function
val scope = Scope().apply { val scope = Script.newScope().apply {
addFn("addArgs") { addFn("sumOf") {
var sum = 0.0 var sum = 0.0
for (a in args) sum += a.toDouble() for (a in args) sum += a.toDouble()
ObjReal(sum) ObjReal(sum)
@ -96,12 +104,14 @@ val scope = Scope().apply {
// suspend fun doSomeWork(text: String): Int // suspend fun doSomeWork(text: String): Int
addFn("doSomeWork") { addFn("doSomeWork") {
// this _is_ a suspend lambda, we can call suspend function, // this _is_ a suspend lambda, we can call suspend function,
// and it won't consume the thread: // and it won't consume the thread.
doSomeWork(args[0].toString()).toObj() // note that in kotlin handler, `args` is a list of `Obj` arguments
// and return value from this lambda should be Obj too:
doSomeWork(args[0]).toObj()
} }
} }
// adding constant: // adding constant:
scope.eval("addArgs(1,2,3)") // <- 6 scope.eval("sumOf(1,2,3)") // <- 6
``` ```
Note that the scope stores all changes in it so you can make calls on a single scope to preserve state between calls. Note that the scope stores all changes in it so you can make calls on a single scope to preserve state between calls.
@ -119,7 +129,7 @@ Designed to add scripting to kotlin multiplatform application in easy and effici
- Javascript, WasmJS, native, JVM, android - batteries included. - Javascript, WasmJS, native, JVM, android - batteries included.
- dynamic types in most elegant and concise way - dynamic types in most elegant and concise way
- async, 100% coroutines, supports multiple cores where platofrm supports thread - async, 100% coroutines, supports multiple cores where platform supports thread
- good for functional an object-oriented style - good for functional an object-oriented style
# Language Roadmap # Language Roadmap
@ -139,15 +149,20 @@ Ready features:
- [x] exception handling: throw, try-catch-finally, exception classes. - [x] exception handling: throw, try-catch-finally, exception classes.
- [x] multiplatform maven publication - [x] multiplatform maven publication
- [x] documentation for the current state - [x] documentation for the current state
Under way:
- [x] maps, sets and sequences (flows?) - [x] maps, sets and sequences (flows?)
- [ ] regular exceptions
- [x] modules - [x] modules
- [x] string formatting and tools - [x] string formatting and tools
- [ ] multiple inheritance for user classes
- [x] launch, deferred, CompletableDeferred, Mutex, etc. - [x] launch, deferred, CompletableDeferred, Mutex, etc.
- [x] multiline strings
- [x] typesafe bit-effective serialization
- [x] compression/decompression (integrated in serialization)
- [x] dynamic fields
- [x] function annotations
### Under way:
- [ ] regular exceptions
- [ ] multiple inheritance for user classes
- [ ] site with integrated interpreter to give a try - [ ] site with integrated interpreter to give a try
- [ ] kotlin part public API good docs, integration focused - [ ] kotlin part public API good docs, integration focused
- [ ] better stack reporting - [ ] better stack reporting
@ -158,12 +173,12 @@ Planned features.
- [ ] type specifications - [ ] type specifications
- [ ] source docs and maybe lyng.md to a standard - [ ] source docs and maybe lyng.md to a standard
- [ ] macro-style kotlin integration or something else to simplify it - [ ] metadata first class access from lyng
Further Further
- [ ] client with GUI support based on compose multiplatform somehow - [ ] client with GUI support based on compose multiplatform somehow
- [ ] notebook - style workbooks with graphs, formulaes, etc. - [ ] notebook - style workbooks with graphs, formulae, etc.
- [ ] language server or compose-based lyng-aware editor - [ ] language server or compose-based lyng-aware editor
[parallelism]: docs/parallelism.md [parallelism]: docs/parallelism.md

View File

@ -1,5 +1,22 @@
#!/bin/bash #!/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 set -e
root=./lyng/build/install/lyng-jvm/ root=./lyng/build/install/lyng-jvm/

View File

@ -1,5 +1,22 @@
#!/bin/bash #!/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 set -e
file=./lyng/build/bin/linuxX64/releaseExecutable/lyng.kexe 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 { plugins {
alias(libs.plugins.androidLibrary) apply false alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.kotlinMultiplatform) 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 >>> 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 ## Members
| name | meaning | type | | name | meaning | type |
|---------------|------------------------------------|---------------| |----------------------------|-----------------------------------------|---------------|
| `size` | size | Int | | `size` | size | Int |
| `decodeUtf8` | decodee to String using UTF8 rules | Any | | `decodeUtf8` | decode to String using UTF8 rules | Any |
| `+` | buffer concatenation | Any | | `+` | buffer concatenation | Any |
| `toMutable()` | create a mutable copy | MutableBuffer | | `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) (1)
: optimized implementation that override `Iterable` one : 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]. Also, it inherits methods from [Iterable] and [Array].
[Range]: Range.md [Range]: Range.md
[Iterable]: Iterable.md [Iterable]: Iterable.md

View File

@ -26,16 +26,34 @@ 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 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! val r = 1..10 // Range is Iterable!
assertEquals( [9,10] r.takeLast(2) ) assertEquals( [9,10], r.takeLast(2).toList() )
assertEquals( [1,2,3] r.take(3) ) assertEquals( [1,2,3], r.take(3).toList() )
assertEquals( [9,10] r.drop(8) ) assertEquals( [9,10], r.drop(8).toList() )
assertEquals( [1,2] r.dropLast(8) ) 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 >>> void
## Instance methods: ## Instance methods:
| fun/method | description | | fun/method | description |
|-----------------|---------------------------------------------------------------------------------| |-------------------|---------------------------------------------------------------------------|
| toList() | create a list from iterable | | toList() | create a list from iterable |
| toSet() | create a set from iterable | | toSet() | create a set from iterable |
| contains(i) | check that iterable contains `i` | | contains(i) | check that iterable contains `i` |
@ -52,10 +70,14 @@ Having `Iterable` in base classes allows to use it in for loop. Also, each `Iter
| taleLast(n) | return [Iterable] of up to n last elements | | taleLast(n) | return [Iterable] of up to n last elements |
| drop(n) | return new [Iterable] without first n elements | | drop(n) | return new [Iterable] without first n elements |
| dropLast(n) | return new [Iterable] without last n elements | | dropLast(n) | return new [Iterable] without last n elements |
| joinToString(s,t) | convert iterable to string, see (2) |
(1) (1)
: throws `NoSuchElementException` if there is no such element : 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.toList(): List
fun Iterable.toSet(): Set fun Iterable.toSet(): Set
fun Iterable.indexOf(element): Int fun Iterable.indexOf(element): Int

View File

@ -235,6 +235,63 @@ as they are modifying the type, not the context.
Beware of it. We might need to reconsider it later. Beware of it. We might need to reconsider it later.
## dynamic symbols
Sometimes it is convenient to provide methods and variables whose names are not known at compile time. For example, it could be external interfaces not known to library code, user-defined data fields, etc. You can use `dynamic` function to create such:
// val only dynamic object
val accessor = dynamic {
// all symbol reads are redirected here:
get { name ->
// lets provide one dynamic symbol:
if( name == "foo" ) "bar" else null
// consider also throw SymbolNotDefinedException
}
}
// now we can access dynamic "fields" of accessor:
assertEquals("bar", accessor.foo)
assertEquals(null, accessor.bar)
>>> void
The same we can provide writable dynamic fields (var-type), adding set method:
// store one dynamic field here
var storedValueForBar = null
// create dynamic object with 2 fields:
val accessor = dynamic {
get { name ->
when(name) {
// constant field
"foo" -> "bar"
// mutable field
"bar" -> setValueForBar
else -> throw SymbolNotFoundException()
}
}
set { name, value ->
// only 'bar' is mutable:
if( name == "bar" )
storedValueForBar = value
// the rest is immotable. consider throw also
// SymbolNotFoundException when needed.
else throw IllegalAssignmentException("Can't assign "+name)
}
}
assertEquals("bar", accessor.foo)
assertEquals(null, accessor.bar)
accessor.bar = "buzz"
assertEquals("buzz", accessor.bar)
assertThrows {
accessor.bad = "!23"
}
Of course, you can return any object from dynamic fields; returning lambdas let create _dynamic methods_ - the callable method. It is very convenient to implement libraries with dynamic remote interfaces, etc.
# Theory # Theory
## Basic principles: ## Basic principles:

View File

@ -112,6 +112,49 @@ arguments list in almost arbitrary ways. For example:
) )
>>> void >>> 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 [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
}
}

View File

@ -48,6 +48,7 @@ $$ \ln(1+x)=x-{\dfrac {x^{2}}{2}}+{\dfrac {x^{3}}{3}}-\cdots =\sum \limits _{n=0
} }
assert( почти_равны( 0.0005, 0.000501 ) ) assert( почти_равны( 0.0005, 0.000501 ) )
Во многих случаях вычисление $n+1$ члена значительно проще cчитается от предыдущего члена, в нашем случае это можно было бы записать через итератор, что мы вскоре добавим. Во многих случаях вычисление $n+1$ члена значительно проще cчитается от предыдущего члена, в нашем случае это можно было бы записать через итератор, см [Iterable] и `flow` в [parallelism].
(продолжение следует) [Iterable]: ../Iterable.md
[parallelism]: ../parallelism.md

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)) assertEquals(p, Point(2,3))
>>> void >>> 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 ## Math
It is rather simple, like everywhere else: 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 [1, "two", 3.33].size
>>> 3 >>> 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: 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 `!in container`. The container is any object that provides `contains` method, otherwise the runtime exception will be
thrown. thrown.
Typical builtin types that are containers (e.g. support `conain`): Typical builtin types that are containers (e.g. support `contains`):
| class | notes | | class | notes |
|------------|------------------------------------------------| |------------|------------------------------------------------|
@ -756,6 +769,8 @@ Typical builtin types that are containers (e.g. support `conain`):
| List | faster than Array's | | List | faster than Array's |
| String | character in string or substring in string (3) | | String | character in string or substring in string (3) |
| Range | object is included in the range (2) | | Range | object is included in the range (2) |
| Buffer | byte is in buffer |
| RingBuffer | object is in buffer |
(1) (1)
: Iterable is not the container as it can be infinite : 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) 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 # Comments
// single line comment // single line comment
@ -1259,14 +1293,26 @@ String literal could be multiline:
"Hello "Hello
World" World"
though multiline literals is yet work in progress. In this case, it will be passed literally ot "hello\n World". But, if there are
several lines with common left indent, it will be removed, also, forst and last lines,
if blank, will be removed too, for example:
println("
This is a multiline text.
This is a second line.
")
>>> This is a multiline text.
>>> This is a second line.
>>> void
- as expected, empty lines and common indent were removed. It is much like kotlin's `""" ... """.trimIndent()` technique, but simpler ;)
# Built-in functions # Built-in functions
See [math functions](math.md). Other general purpose functions are: See [math functions](math.md). Other general purpose functions are:
| name | description | | name | description |
|--------------------------------------------|-----------------------------------------------------------| |----------------------------------------------|------------------------------------------------------------|
| assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them | | assert(condition,message="assertion failed") | runtime code check. There will be an option to skip them |
| assertEquals(a,b) | | | assertEquals(a,b) | |
| assertNotEquals(a,b) | | | assertNotEquals(a,b) | |
@ -1277,6 +1323,9 @@ See [math functions](math.md). Other general purpose functions are:
| print(args...) | Open for overriding, it prints to stdout without newline. | | print(args...) | Open for overriding, it prints to stdout without newline. |
| flow {} | create flow sequence, see [parallelism] | | flow {} | create flow sequence, see [parallelism] |
| delay, launch, yield | 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 # 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 #Gradle
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
org.gradle.caching=true 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip

5
gradlew vendored
View File

@ -1,13 +1,13 @@
#!/bin/sh #!/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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # 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 # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # 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 # See the License for the specific language governing permissions and
# limitations under the License. # 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 { plugins {
kotlin("multiplatform") version "2.1.21" 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 package net.sergeych
import com.github.ajalt.clikt.core.CliktCommand 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 package net.sergeych
import kotlin.system.exitProcess 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") @file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package net.sergeych 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 package net.sergeych.lyng_cli
import net.sergeych.runMain 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 import net.sergeych.runMain
fun main(args: Array<String>) { 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 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 com.codingfeline.buildkonfig.compiler.FieldSpec.Type.STRING
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych" group = "net.sergeych"
version = "0.8.4-SNAPSHOT" version = "0.8.14-SNAPSHOT"
buildscript { buildscript {
repositories { 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 package net.sergeych.lyng
enum class AccessType(val isMutable: Boolean) { 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj 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, arguments: Arguments = scope.args,
defaultAccessType: AccessType = AccessType.Var, defaultAccessType: AccessType = AccessType.Var,
defaultVisibility: Visibility = Visibility.Public, defaultVisibility: Visibility = Visibility.Public,
defaultRecordType: ObjRecord.Type = ObjRecord.Type.ConstructorField
) { ) {
fun assign(a: Item, value: Obj) { fun assign(a: Item, value: Obj) {
scope.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable, scope.addItem(a.name, (a.accessType ?: defaultAccessType).isMutable,
value.byValueCopy(), value.byValueCopy(),
a.visibility ?: defaultVisibility, a.visibility ?: defaultVisibility,
recordType = defaultRecordType) recordType = ObjRecord.Type.Argument)
} }
// will be used with last lambda arg fix // 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj 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 package net.sergeych.lyng
//fun buildDoubleFromParts( //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 package net.sergeych.lyng
import net.sergeych.lyng.obj.ObjRecord 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 * Inherits [Scope.args] and [Scope.thisObj] from [callScope] and adds lookup for symbols
* from [closureScope] with proper precedence * 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? { override fun get(name: String): ObjRecord? {
// closure should be treated below callScope // we take arguments from the callerScope, the rest
return super.get(name) ?: closureScope.get(name) // 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 package net.sergeych.lyng
import ObjEnumClass
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportProvider import net.sergeych.lyng.pacman.ImportProvider
@ -27,6 +45,17 @@ class Compiler(
private fun popInitScope(): MutableList<Statement> = initStack.removeLast() 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 { private suspend fun parseScript(): Script {
val statements = mutableListOf<Statement>() val statements = mutableListOf<Statement>()
val start = cc.currentPos() val start = cc.currentPos()
@ -65,9 +94,17 @@ class Compiler(
} }
} }
} }
parseStatement(braceMeansLambda = true)?.also { val s = parseStatement(braceMeansLambda = true)?.also {
statements += it statements += it
} ?: break }
if (s == null) {
when (t.type) {
Token.Type.RBRACE, Token.Type.EOF, Token.Type.SEMICOLON -> {}
else ->
throw ScriptError(t.pos, "unexpeced `${t.value}` here")
}
break
}
} while (true) } while (true)
return Script(start, statements)//returnScope.needCatch) return Script(start, statements)//returnScope.needCatch)
@ -88,7 +125,10 @@ class Compiler(
return result.toString() return result.toString()
} }
private var lastAnnotation: (suspend (Scope, ObjString, Statement) -> Statement)? = null
private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? { private suspend fun parseStatement(braceMeansLambda: Boolean = false): Statement? {
lastAnnotation = null
while (true) { while (true) {
val t = cc.next() val t = cc.next()
return when (t.type) { return when (t.type) {
@ -105,6 +145,11 @@ class Compiler(
parseExpression() parseExpression()
} }
Token.Type.ATLABEL -> {
lastAnnotation = parseAnnotation(t)
continue
}
Token.Type.LABEL -> continue Token.Type.LABEL -> continue
Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue Token.Type.SINLGE_LINE_COMMENT, Token.Type.MULTILINE_COMMENT -> continue
@ -331,6 +376,13 @@ class Compiler(
} }
"throw" -> {
val s = parseThrowStatement()
operand = Accessor {
s.execute(it).asReadonly
}
}
else -> operand?.let { left -> else -> operand?.let { left ->
// selector: <lvalue>, '.' , <id> // selector: <lvalue>, '.' , <id>
// we replace operand with selector code, that // we replace operand with selector code, that
@ -470,7 +522,7 @@ class Compiler(
val callStatement = statement { val callStatement = statement {
// and the source closure of the lambda which might have other thisObj. // 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) { if (argsDeclaration == null) {
// no args: automatic var 'it' // no args: automatic var 'it'
val l = args.list val l = args.list
@ -482,7 +534,7 @@ class Compiler(
// more args: it is a list of args // more args: it is a list of args
else -> ObjList(l.toMutableList()) else -> ObjList(l.toMutableList())
} }
context.addItem("it", false, itValue) context.addItem("it", false, itValue, recordType = ObjRecord.Type.Argument)
} else { } else {
// assign vars as declared the standard way // assign vars as declared the standard way
argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val) argsDeclaration.assignToContext(context, defaultAccessType = AccessType.Val)
@ -491,7 +543,7 @@ class Compiler(
} }
return Accessor { x -> return Accessor { x ->
if (closure == null) closure = x closure = x
callStatement.asReadonly callStatement.asReadonly
} }
} }
@ -810,6 +862,26 @@ class Compiler(
return parseNumberOrNull(isPlus) ?: throw ScriptError(cc.currentPos(), "Expecting number") 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. * Parse keyword-starting statement.
* @return parsed statement or null if, for example. [id] is not among keywords * @return parsed statement or null if, for example. [id] is not among keywords
@ -823,7 +895,8 @@ class Compiler(
"break" -> parseBreakStatement(id.pos) "break" -> parseBreakStatement(id.pos)
"continue" -> parseContinueStatement(id.pos) "continue" -> parseContinueStatement(id.pos)
"if" -> parseIfStatement() "if" -> parseIfStatement()
"class" -> parseClassDeclaration(false) "class" -> parseClassDeclaration()
"enum" -> parseEnumDeclaration()
"try" -> parseTryStatement() "try" -> parseTryStatement()
"throw" -> parseThrowStatement() "throw" -> parseThrowStatement()
"when" -> parseWhenStatement() "when" -> parseWhenStatement()
@ -833,8 +906,18 @@ class Compiler(
val isExtern = cc.skipId("extern") val isExtern = cc.skipId("extern")
when { when {
cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern) cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
cc.matchQualifiers("fun", "private", "static") -> parseFunctionDeclaration(Visibility.Private, isExtern, isStatic = true) cc.matchQualifiers("fun", "private", "static") -> parseFunctionDeclaration(
cc.matchQualifiers("fun", "static") -> parseFunctionDeclaration(Visibility.Public, isExtern, isStatic = true) Visibility.Private,
isExtern,
isStatic = true
)
cc.matchQualifiers("fun", "static") -> parseFunctionDeclaration(
Visibility.Public,
isExtern,
isStatic = true
)
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern) cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(Visibility.Private, isExtern)
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern) cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern) cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(isOpen = true, isExtern = isExtern)
@ -1100,8 +1183,44 @@ class Compiler(
} }
} }
private suspend fun parseClassDeclaration(isStruct: Boolean): Statement { private fun parseEnumDeclaration(): Statement {
val nameToken = cc.requireToken(Token.Type.ID) val nameToken = cc.requireToken(Token.Type.ID)
// so far only simplest enums:
val names = mutableListOf<String>()
// skip '{'
cc.skipTokenOfType(Token.Type.LBRACE)
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
Token.Type.RBRACE -> break
else -> {
t1.raiseSyntax("unexpected token")
}
}
}
else -> t.raiseSyntax("expected enum entry name")
}
} while (true)
return statement {
ObjEnumClass.createSimpleEnum(nameToken.value, names).also {
addItem(nameToken.value, false, it, recordType = ObjRecord.Type.Enum)
}
}
}
private suspend fun parseClassDeclaration(): Statement {
val nameToken = cc.requireToken(Token.Type.ID)
return inCodeContext(CodeContext.ClassBody(nameToken.value)) {
val constructorArgsDeclaration = val constructorArgsDeclaration =
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true)) if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
parseArgsDeclaration(isClassDeclaration = true) parseArgsDeclaration(isClassDeclaration = true)
@ -1133,8 +1252,8 @@ class Compiler(
// create class // create class
val className = nameToken.value val className = nameToken.value
@Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization // @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Var else AccessType.Initialization
@Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public // @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
// create instance constructor // create instance constructor
// create custom objClass with all fields and instance constructor // create custom objClass with all fields and instance constructor
@ -1157,7 +1276,7 @@ class Compiler(
constructorMeta = constructorArgsDeclaration constructorMeta = constructorArgsDeclaration
} }
return statement { statement {
// the main statement should create custom ObjClass instance with field // the main statement should create custom ObjClass instance with field
// accessors, constructor registration, etc. // accessors, constructor registration, etc.
addItem(className, false, newClass) addItem(className, false, newClass)
@ -1166,12 +1285,13 @@ class Compiler(
newClass.classScope = classScope newClass.classScope = classScope
for (s in initScope) for (s in initScope)
s.execute(classScope) s.execute(classScope)
.also { println("executed, ${classScope.objects}") }
} }
newClass newClass
} }
} }
}
private fun getLabel(maxDepth: Int = 2): String? { private fun getLabel(maxDepth: Int = 2): String? {
var cnt = 0 var cnt = 0
@ -1540,8 +1660,7 @@ class Compiler(
} }
} }
private suspend fun private suspend fun parseFunctionDeclaration(
parseFunctionDeclaration(
visibility: Visibility = Visibility.Public, visibility: Visibility = Visibility.Public,
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false, @Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
isExtern: Boolean = false, isExtern: Boolean = false,
@ -1554,6 +1673,9 @@ class Compiler(
throw ScriptError(t.pos, "Expected identifier after 'fun'") throw ScriptError(t.pos, "Expected identifier after 'fun'")
else t.value else t.value
val annotation = lastAnnotation
val parentContext = codeContexts.last()
t = cc.next() t = cc.next()
// Is extension? // Is extension?
if (t.type == Token.Type.DOT) { if (t.type == Token.Type.DOT) {
@ -1577,6 +1699,8 @@ class Compiler(
if (cc.current().type == Token.Type.COLON) parseTypeDeclaration() if (cc.current().type == Token.Type.COLON) parseTypeDeclaration()
return inCodeContext(CodeContext.Function(name)) {
// Here we should be at open body // Here we should be at open body
val fnStatements = if (isExtern) val fnStatements = if (isExtern)
statement { raiseError("extern function not provided: $name") } statement { raiseError("extern function not provided: $name") }
@ -1589,9 +1713,10 @@ class Compiler(
callerContext.pos = start callerContext.pos = start
// restore closure where the function was defined, and making a copy of it // restore closure where the function was defined, and making a copy of it
// for local space (otherwise it will write local stuff to closure!) // 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) } val context = closure?.let { ClosureScope(callerContext, it) }
?: callerContext.raiseError("bug: closure not set") ?: callerContext
// load params from caller context // load params from caller context
argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val) argsDeclaration.assignToContext(context, callerContext.args, defaultAccessType = AccessType.Val)
@ -1602,8 +1727,13 @@ class Compiler(
} }
val fnCreateStatement = statement(start) { context -> val fnCreateStatement = statement(start) { context ->
// we added fn in the context. now we must save closure // we added fn in the context. now we must save closure
// for the function // for the function, unless we're in the class scope:
if (isStatic || parentContext !is CodeContext.ClassBody)
closure = context closure = context
val annotatedFnBody = annotation?.invoke(context, ObjString(name), fnBody)
?: fnBody
extTypeName?.let { typeName -> extTypeName?.let { typeName ->
// class extension method // class extension method
val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found") val type = context[typeName]?.value ?: context.raiseSymbolNotFound("class $typeName not found")
@ -1611,24 +1741,25 @@ class Compiler(
type.addFn(name, isOpen = true) { type.addFn(name, isOpen = true) {
// ObjInstance has a fixed instance scope, so we need to build a closure // ObjInstance has a fixed instance scope, so we need to build a closure
(thisObj as? ObjInstance)?.let { i -> (thisObj as? ObjInstance)?.let { i ->
fnBody.execute(ClosureScope(this, i.instanceScope)) annotatedFnBody.execute(ClosureScope(this, i.instanceScope))
} }
// other classes can create one-time scope for this rare case: // other classes can create one-time scope for this rare case:
?: fnBody.execute(thisObj.autoInstanceScope(this)) ?: annotatedFnBody.execute(thisObj.autoInstanceScope(this))
} }
} }
// regular function/method // regular function/method
?: context.addItem(name, false, fnBody, visibility) ?: context.addItem(name, false, annotatedFnBody, visibility)
// as the function can be called from anywhere, we have // as the function can be called from anywhere, we have
// saved the proper context in the closure // saved the proper context in the closure
fnBody annotatedFnBody
} }
return if (isStatic) { if (isStatic) {
currentInitScope += fnCreateStatement currentInitScope += fnCreateStatement
NopStatement NopStatement
} else } else
fnCreateStatement fnCreateStatement
} }
}
private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement { private suspend fun parseBlock(skipLeadingBrace: Boolean = false): Statement {
val startPos = cc.currentPos() val startPos = cc.currentPos()
@ -1662,6 +1793,10 @@ class Compiler(
val eqToken = cc.next() val eqToken = cc.next()
var setNull = false var setNull = false
val isDelegate = if (eqToken.isId("by")) {
true
} else {
if (eqToken.type != Token.Type.ASSIGN) { if (eqToken.type != Token.Type.ASSIGN) {
if (!isMutable) if (!isMutable)
throw ScriptError(start, "val must be initialized") throw ScriptError(start, "val must be initialized")
@ -1670,16 +1805,19 @@ class Compiler(
setNull = true 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") ?: throw ScriptError(eqToken.pos, "Expected initializer expression")
if (isStatic) { if (isStatic) {
// find objclass instance: this is tricky: this code executes in object initializer, // 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 // 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? // 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 { currentInitScope += statement {
val initValue = initialExpression?.execute(this)?.byValueCopy() ?: ObjNull val initValue = initialExpression?.execute(this)?.byValueCopy() ?: ObjNull
(thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, pos) (thisObj as ObjClass).createClassField(name, initValue, isMutable, visibility, pos)
@ -1693,14 +1831,34 @@ class Compiler(
if (context.containsLocal(name)) if (context.containsLocal(name))
throw ScriptError(nameToken.pos, "Variable $name is already defined") throw ScriptError(nameToken.pos, "Variable $name is already defined")
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 // init value could be a val; when we initialize by-value type var with it, we need to
// create a separate copy: // create a separate copy:
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field) context.addItem(name, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
initValue initValue
} }
} }
}
data class Operator( data class Operator(
val tokenType: Token.Type, val tokenType: Token.Type,
@ -1817,7 +1975,15 @@ class Compiler(
Operator.simple(Token.Type.IS, lastPriority) { _, a, b -> ObjBool(a.isInstanceOf(b)) }, 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.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 // shuttle <=> 6
Operator.simple(Token.Type.SHUTTLE, ++lastPriority) { c, a, b -> 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 package net.sergeych.lyng
class CompilerContext(val tokens: List<Token>) { class CompilerContext(val tokens: List<Token>) {
@ -20,7 +37,7 @@ class CompilerContext(val tokens: List<Token>) {
fun hasNext() = currentIndex < tokens.size fun hasNext() = currentIndex < tokens.size
fun hasPrevious() = currentIndex > 0 fun hasPrevious() = currentIndex > 0
fun next() = fun next() =
if( currentIndex < tokens.size ) tokens[currentIndex++] if (currentIndex < tokens.size) tokens[currentIndex++]
else Token("", tokens.last().pos, Token.Type.EOF) else Token("", tokens.last().pos, Token.Type.EOF)
// throw IllegalStateException("No more tokens") // throw IllegalStateException("No more tokens")
@ -61,7 +78,7 @@ class CompilerContext(val tokens: List<Token>) {
*/ */
fun skipId(name: String): Boolean { fun skipId(name: String): Boolean {
current().let { t -> current().let { t ->
if( t.type == Token.Type.ID && t.value == name ) { if (t.type == Token.Type.ID && t.value == name) {
next() next()
return true return true
} }
@ -93,6 +110,20 @@ class CompilerContext(val tokens: List<Token>) {
} else true } 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") @Suppress("unused")
fun skipTokens(vararg tokenTypes: Token.Type) { fun skipTokens(vararg tokenTypes: Token.Type) {
while (next().type in tokenTypes) { /**/ while (next().type in tokenTypes) { /**/
@ -143,19 +174,24 @@ class CompilerContext(val tokens: List<Token>) {
fun matchQualifiers(keyword: String, vararg qualifiers: String): Boolean { fun matchQualifiers(keyword: String, vararg qualifiers: String): Boolean {
val pos = savePos() val pos = savePos()
var count = 0 var count = 0
while( count < qualifiers.size) { while (count < qualifiers.size) {
val t = next() val t = next()
when(t.type) { when (t.type) {
Token.Type.ID -> { Token.Type.ID -> {
if( t.value in qualifiers ) count++ if (t.value in qualifiers) count++
else { restorePos(pos); return false } else {
restorePos(pos); return false
} }
}
Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT, Token.Type.NEWLINE -> {} 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() val t = next()
if( t.type == Token.Type.ID && t.value == keyword ) { if (t.type == Token.Type.ID && t.value == keyword) {
return true return true
} else { } else {
restorePos(pos) restorePos(pos)
@ -168,7 +204,7 @@ class CompilerContext(val tokens: List<Token>) {
* Note that [Token.Type.EOF] is not considered a whitespace token. * Note that [Token.Type.EOF] is not considered a whitespace token.
*/ */
fun skipWsTokens(): Token { fun skipWsTokens(): Token {
while( current().type in wstokens ) { while (current().type in wstokens) {
next() next()
} }
return 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.Accessor 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.ObjRecord 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 package net.sergeych.lyng
val digitsSet = ('0'..'9').toSet() val digitsSet = ('0'..'9').toSet()
@ -375,6 +392,32 @@ private class Parser(fromPos: Pos) {
private val currentChar: Char get() = pos.currentChar private val currentChar: Char get() = pos.currentChar
private fun fixMultilineStringLiteral(source: String): String {
val sizes = mutableListOf<Int>()
val lines = source.lines().toMutableList()
if( lines.size == 0 ) return ""
if( lines[0].isBlank() ) lines.removeFirst()
if( lines.isEmpty()) return ""
if( lines.last().isBlank() ) lines.removeLast()
val normalized = lines.map { l ->
if( l.isBlank() ) {
sizes.add(-1)
""
}
else {
val margin = leftMargin(l)
sizes += margin
" ".repeat(margin) + l.trim()
}
}
val commonMargin = sizes.filter { it >= 0 }.min()
val fixed = if( commonMargin < 1 ) lines else normalized.map {
if( it.isBlank() ) "" else it.drop(commonMargin)
}
return fixed.joinToString("\n")
}
private fun loadStringToken(): Token { private fun loadStringToken(): Token {
val start = currentPos val start = currentPos
@ -383,6 +426,7 @@ private class Parser(fromPos: Pos) {
// start = start.back() // start = start.back()
val sb = StringBuilder() val sb = StringBuilder()
var newlineDetected = false
while (currentChar != '"') { while (currentChar != '"') {
if (pos.end) throw ScriptError(start, "unterminated string started there") if (pos.end) throw ScriptError(start, "unterminated string started there")
when (currentChar) { when (currentChar) {
@ -397,6 +441,12 @@ private class Parser(fromPos: Pos) {
} }
} }
'\n', '\r'-> {
newlineDetected = true
sb.append(currentChar)
pos.advance()
}
else -> { else -> {
sb.append(currentChar) sb.append(currentChar)
pos.advance() pos.advance()
@ -404,7 +454,10 @@ private class Parser(fromPos: Pos) {
} }
} }
pos.advance() pos.advance()
return Token(sb.toString(), start, Token.Type.STRING)
val result = sb.toString().let { if( newlineDetected ) fixMultilineStringLiteral(it) else it }
return Token(result, start, Token.Type.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 package net.sergeych.lyng
data class Pos(val source: Source, val line: Int, val column: Int) { 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
@ -71,6 +88,11 @@ open class Scope(
throw ExecutionError(obj) 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 { 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}") if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}")
return (args.list[index].byValueCopy() as? T) return (args.list[index].byValueCopy() as? T)
@ -109,9 +131,16 @@ open class Scope(
open operator fun get(name: String): ObjRecord? = open operator fun get(name: String): ObjRecord? =
if (name == "this") thisObj.asReadonly if (name == "this") thisObj.asReadonly
else { else {
objects[name] (objects[name]
?: parent?.get(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 = 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 { companion object {
fun new(): Scope = 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 package net.sergeych.lyng
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -29,6 +46,11 @@ class Script(
companion object { companion object {
/**
* Create new scope using standard safe set of modules, using [defaultImportManager]. It is
* suspended as first time calls requires compilation of standard library or other
* asynchronous initialization.
*/
suspend fun newScope(pos: Pos = Pos.builtIn) = defaultImportManager.newStdScope(pos) suspend fun newScope(pos: Pos = Pos.builtIn) = defaultImportManager.newStdScope(pos)
internal val rootScope: Scope = Scope(null).apply { internal val rootScope: Scope = Scope(null).apply {
@ -165,6 +187,11 @@ class Script(
} }
result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown")) result ?: raiseError(ObjAssertionFailedException(this,"Expected exception but nothing was thrown"))
} }
addFn("dynamic") {
ObjDynamic.create(this, requireOnlyArg())
}
addFn("require") { addFn("require") {
val condition = requiredArg<ObjBool>(0) val condition = requiredArg<ObjBool>(0)
if( !condition.value ) { if( !condition.value ) {

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") @file:Suppress("CanBeParameter")
package net.sergeych.lyng 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 package net.sergeych.lyng
interface SecurityManager { 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 package net.sergeych.lyng
class Source(val fileName: String, text: String) { 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 package net.sergeych.lyng
data class Token(val value: String, val pos: Pos, val type: Type) { 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 } 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") @Suppress("unused")
enum class Type { enum class Type {
ID, INT, REAL, HEX, STRING, CHAR, 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") @file:Suppress("unused")
package net.sergeych.lyng 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 package net.sergeych.lyng
enum class Visibility { 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 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 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 package net.sergeych.lyng.obj
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
@ -68,13 +85,15 @@ open class Obj {
name: String, name: String,
args: Arguments = Arguments.EMPTY, args: Arguments = Arguments.EMPTY,
onNotFoundResult: Obj?=null onNotFoundResult: Obj?=null
): Obj = ): Obj {
objClass.getInstanceMemberOrNull(name)?.value?.invoke( return objClass.getInstanceMemberOrNull(name)?.value?.invoke(
scope, scope,
this, this,
args) args
)
?: onNotFoundResult ?: onNotFoundResult
?: scope.raiseSymbolNotFound(name) ?: scope.raiseSymbolNotFound(name)
}
open suspend fun getInstanceMethod( open suspend fun getInstanceMethod(
scope: Scope, scope: Scope,
@ -144,6 +163,8 @@ open class Obj {
open suspend fun assign(scope: Scope, other: Obj): Obj? = null open suspend fun assign(scope: Scope, other: Obj): Obj? = null
open fun getValue(scope: Scope) = this
/** /**
* a += b * a += b
* if( the operation is not defined, it returns null and the compiler would try * if( the operation is not defined, it returns null and the compiler would try
@ -269,6 +290,9 @@ open class Obj {
addFn("toString") { addFn("toString") {
thisObj.asStr thisObj.asStr
} }
addFn("inspect", true) {
thisObj.inspect().toObj()
}
addFn("contains") { addFn("contains") {
ObjBool(thisObj.contains(this, args.firstAndOnly())) ObjBool(thisObj.contains(this, args.firstAndOnly()))
} }
@ -277,15 +301,23 @@ open class Obj {
args.firstAndOnly().callOn(copy(Arguments(thisObj))) args.firstAndOnly().callOn(copy(Arguments(thisObj)))
} }
addFn("apply") { addFn("apply") {
val newContext = (thisObj as? ObjInstance)?.instanceScope ?: this val body = args.firstAndOnly()
args.firstAndOnly() (thisObj as? ObjInstance)?.let {
.callOn(newContext) 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 thisObj
} }
addFn("also") { addFn("also") {
args.firstAndOnly().callOn(copy(Arguments(thisObj))) args.firstAndOnly().callOn(copy(Arguments(thisObj)))
thisObj thisObj
} }
addFn("run") {
args.firstAndOnly().callOn(this)
}
addFn("getAt") { addFn("getAt") {
requireExactCount(1) requireExactCount(1)
thisObj.getAt(this, requiredArg<Obj>(0)) thisObj.getAt(this, requiredArg<Obj>(0))
@ -513,6 +545,7 @@ open class ObjException(exceptionClass: ExceptionClass, val scope: Scope, val me
"IterationEndException", "IterationEndException",
"AccessException", "AccessException",
"UnknownException", "UnknownException",
"NotFoundException"
)) { )) {
scope.addConst(name, getOrCreateExceptionClass(name)) 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") : class ObjIllegalOperationException(scope: Scope, message: String = "Operation is illegal") :
ObjException("IllegalOperationException", scope, message) 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 package net.sergeych.lyng.obj
val ObjArray by lazy { 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import net.sergeych.bintools.toDump 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import net.sergeych.bintools.decodeHex
import net.sergeych.bintools.encodeToHex import net.sergeych.bintools.encodeToHex
import net.sergeych.bintools.toDump import net.sergeych.bintools.toDump
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
import net.sergeych.lyng.statement import net.sergeych.lyng.statement
import net.sergeych.lynon.BitArray
import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonDecoder
import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonEncoder
import net.sergeych.lynon.LynonType import net.sergeych.lynon.LynonType
import net.sergeych.mp_tools.decodeBase64Url
import net.sergeych.mp_tools.encodeToBase64Url
import kotlin.math.min import kotlin.math.min
open class ObjBuffer(val byteArray: UByteArray) : Obj() { open class ObjBuffer(val byteArray: UByteArray) : Obj() {
override val objClass: ObjClass = type 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 { fun checkIndex(scope: Scope, index: Obj): Int {
if (index !is ObjInt) if (index !is ObjInt)
scope.raiseIllegalArgument("index must be Int") 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()}") } else scope.raiseIllegalArgument("can't concatenate buffer with ${other.inspect()}")
} }
override fun toString(): String { override fun toString(): String = base64
return "Buffer(${byteArray.encodeToHex()})"
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@ -85,6 +107,8 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
encoder.encodeCachedBytes(byteArray.asByteArray()) encoder.encodeCachedBytes(byteArray.asByteArray())
} }
override fun inspect(): String = "Buf($base64)"
companion object { companion object {
private suspend fun createBufferFrom(scope: Scope, obj: Obj): ObjBuffer = private suspend fun createBufferFrom(scope: Scope, obj: Obj): ObjBuffer =
when (obj) { when (obj) {
@ -141,11 +165,27 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
}) })
}.apply { }.apply {
addClassFn("decodeBase64") {
ObjBuffer(requireOnlyArg<Obj>().toString().decodeBase64Url().asUByteArray())
}
addClassFn("decodeHex") {
ObjBuffer(requireOnlyArg<Obj>().toString().decodeHex().asUByteArray())
}
createField("size", createField("size",
statement { statement {
(thisObj as ObjBuffer).byteArray.size.toObj() (thisObj as ObjBuffer).byteArray.size.toObj()
} }
) )
createField("hex",
statement {
thisAs<ObjBuffer>().hex.toObj()
}
)
createField("base64",
statement {
thisAs<ObjBuffer>().base64.toObj()
}
)
addFn("decodeUtf8") { addFn("decodeUtf8") {
ObjString( ObjString(
thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString() thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString()
@ -161,6 +201,9 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
thisAs<ObjBuffer>().byteArray.toByteArray().toDump() 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.* 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 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 package net.sergeych.lyng.obj
import kotlinx.coroutines.CompletableDeferred 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 package net.sergeych.lyng.obj
import kotlinx.coroutines.Deferred 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope

View File

@ -0,0 +1,82 @@
/*
* 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
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
class ObjDynamicContext(val delegate: ObjDynamic) : Obj() {
override val objClass: ObjClass = type
companion object {
val type = ObjClass("DelegateContext").apply {
addFn("get") {
val d = thisAs<ObjDynamicContext>().delegate
if (d.readCallback != null)
raiseIllegalState("get already defined")
d.readCallback = requireOnlyArg()
ObjVoid
}
addFn("set") {
val d = thisAs<ObjDynamicContext>().delegate
if (d.writeCallback != null)
raiseIllegalState("set already defined")
d.writeCallback = requireOnlyArg()
ObjVoid
}
}
}
}
class ObjDynamic : Obj() {
internal var readCallback: Statement? = null
internal var writeCallback: Statement? = null
override suspend fun readField(scope: Scope, name: String): ObjRecord {
return readCallback?.execute(scope.copy(Arguments(ObjString(name))))?.let {
if (writeCallback != null)
it.asMutable
else
it.asReadonly
}
?: super.readField(scope, name)
}
override suspend fun writeField(scope: Scope, name: String, newValue: Obj) {
writeCallback?.execute(scope.copy(Arguments(ObjString(name), newValue)))
?: super.writeField(scope, name, newValue)
}
companion object {
suspend fun create(scope: Scope, builder: Statement): ObjDynamic {
val delegate = ObjDynamic()
val context = ObjDynamicContext(delegate)
builder.execute(scope.copy(newThisObj = context))
return delegate
}
val type = object : ObjClass("Delegate") {}
}
}

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 package net.sergeych.lyng.obj
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@ -23,12 +40,12 @@ class ObjFlowBuilder(val output: SendChannel<Obj>) : Obj() {
val data = requireOnlyArg<Obj>() val data = requireOnlyArg<Obj>()
try { try {
val channel = thisAs<ObjFlowBuilder>().output val channel = thisAs<ObjFlowBuilder>().output
if( !channel.isClosedForSend ) if (!channel.isClosedForSend)
channel.send(data) channel.send(data)
else else
throw ScriptFlowIsNoMoreCollected() throw ScriptFlowIsNoMoreCollected()
} catch (x: Exception) { } catch (x: Exception) {
if( x !is CancellationException ) if (x !is CancellationException)
x.printStackTrace() x.printStackTrace()
throw ScriptFlowIsNoMoreCollected() throw ScriptFlowIsNoMoreCollected()
} }
@ -45,12 +62,10 @@ private fun createLyngFlowInput(scope: Scope, producer: Statement): ReceiveChann
globalLaunch { globalLaunch {
try { try {
producer.execute(builderScope) producer.execute(builderScope)
} } catch (x: ScriptFlowIsNoMoreCollected) {
catch(x: ScriptFlowIsNoMoreCollected) {
x.printStackTrace() x.printStackTrace()
// premature flow closing, OK // premature flow closing, OK
} } catch (x: Exception) {
catch(x: Exception) {
x.printStackTrace() x.printStackTrace()
} }
channel.close() channel.close()
@ -70,7 +85,11 @@ class ObjFlow(val producer: Statement, val scope: Scope) : Obj() {
}.apply { }.apply {
addFn("iterator") { addFn("iterator") {
val objFlow = thisAs<ObjFlow>() 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 var isCancelled = false
private fun checkNotCancelled(scope: Scope) { private fun checkNotCancelled(scope: Scope) {
if( isCancelled ) if (isCancelled)
scope.raiseIllegalState("iteration is cancelled") scope.raiseIllegalState("iteration is cancelled")
} }
suspend fun hasNext(scope: Scope): ObjBool { suspend fun hasNext(scope: Scope): ObjBool {
checkNotCancelled(scope) checkNotCancelled(scope)
// cold start: // 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Arguments
@ -71,18 +88,15 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val vars = instanceVars.values.map { it.value } val vars = instanceVars.values.map { it.value }
if( vars.isNotEmpty()) { if( vars.isNotEmpty()) {
encoder.encodeAnyList(scope, vars) encoder.encodeAnyList(scope, vars)
println("serialized state vars $vars")
} }
} }
internal suspend fun deserializeStateVars(scope: Scope, decoder: LynonDecoder) { internal suspend fun deserializeStateVars(scope: Scope, decoder: LynonDecoder) {
val localVars = instanceVars.values.toList() val localVars = instanceVars.values.toList()
if( localVars.isNotEmpty() ) { if( localVars.isNotEmpty() ) {
println("gonna read vars")
val vars = decoder.decodeAnyList(scope) val vars = decoder.decodeAnyList(scope)
if (vars.size > instanceVars.size) if (vars.size > instanceVars.size)
scope.raiseIllegalArgument("serialized vars has bigger size than instance vars") scope.raiseIllegalArgument("serialized vars has bigger size than instance vars")
println("deser state vars $vars")
for ((i, v) in vars.withIndex()) { for ((i, v) in vars.withIndex()) {
localVars[i].value = v 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments 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 { override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
val args = decoder.decodeAnyList(scope) val args = decoder.decodeAnyList(scope)
println("deserializing constructor $name, $args params")
val actualSize = constructorMeta?.params?.size ?: 0 val actualSize = constructorMeta?.params?.size ?: 0
if( args.size > actualSize ) if( args.size > actualSize )
scope.raiseIllegalArgument("constructor $name has only $actualSize but serialized version has ${args.size}") 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 package net.sergeych.lyng.obj
import kotlinx.datetime.Clock 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 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 = override suspend fun plus(scope: Scope, other: Obj): Obj =
if (other is ObjInt) 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Arguments import net.sergeych.lyng.Arguments
@ -42,6 +59,9 @@ val ObjIterable by lazy {
} }
addFn("toSet") { addFn("toSet") {
if( thisObj.isInstanceOf(ObjSet.type) )
thisObj
else {
val result = mutableSetOf<Obj>() val result = mutableSetOf<Obj>()
val it = thisObj.invokeInstanceMethod(this, "iterator") val it = thisObj.invokeInstanceMethod(this, "iterator")
while (it.invokeInstanceMethod(this, "hasNext").toBool()) { while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
@ -49,6 +69,7 @@ val ObjIterable by lazy {
} }
ObjSet(result) ObjSet(result)
} }
}
addFn("toMap") { addFn("toMap") {
val result = ObjMap() val result = ObjMap()

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 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") @file:Suppress("unused")
package net.sergeych.lyng.obj 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import kotlinx.coroutines.flow.map 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 package net.sergeych.lyng.obj
import kotlinx.coroutines.sync.Mutex 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
@ -39,7 +56,7 @@ class ObjRangeIterator(val self: ObjRange) : Obj() {
} }
companion object { companion object {
val type = ObjClass("RangeIterator", ObjIterable).apply { val type = ObjClass("RangeIterator", ObjIterator).apply {
addFn("hasNext") { addFn("hasNext") {
thisAs<ObjRangeIterator>().hasNext().toObj() 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Pos 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
@ -18,8 +35,15 @@ data class ObjRecord(
Field(true, true), Field(true, true),
@Suppress("unused") @Suppress("unused")
Fun, Fun,
@Suppress("unused")
ConstructorField(true, true), ConstructorField(true, true),
Other Argument(true, true),
@Suppress("unused")
Class,
Enum,
Other;
val isArgument get() = this == Argument
} }
@Suppress("unused") @Suppress("unused")
fun qualifiedName(name: String): String = 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope 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 package net.sergeych.lyng.obj
import net.sergeych.lyng.Scope import net.sergeych.lyng.Scope
@ -92,7 +109,7 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
companion object { companion object {
val type = object : ObjClass("Set", ObjCollection) { val type: ObjClass = object : ObjClass("Set", ObjCollection) {
override suspend fun callOn(scope: Scope): Obj { override suspend fun callOn(scope: Scope): Obj {
return ObjSet(scope.args.list.toMutableSet()) 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 package net.sergeych.lyng.obj
import kotlinx.serialization.SerialName 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 package net.sergeych.lyng.pacman
import net.sergeych.lyng.* 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 package net.sergeych.lyng.pacman
import net.sergeych.lyng.* 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 package net.sergeych.lyng.pacman
import kotlinx.coroutines.CompletableDeferred 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 package net.sergeych.lyng
import net.sergeych.lyng.obj.Obj import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjClass import net.sergeych.lyng.obj.ObjClass
import net.sergeych.lyng.obj.ObjNull
import net.sergeych.lyng.obj.ObjVoid import net.sergeych.lyng.obj.ObjVoid
fun String.toSource(name: String = "eval"): Source = Source(name, this) fun String.toSource(name: String = "eval"): Source = Source(name, this)
@ -28,6 +46,7 @@ abstract class Statement(
abstract suspend fun execute(scope: Scope): Obj abstract suspend fun execute(scope: Scope): Obj
override suspend fun compareTo(scope: Scope, other: Obj): Int { override suspend fun compareTo(scope: Scope, other: Obj): Int {
if( other == ObjNull || other == ObjVoid ) return 1
throw UnsupportedOperationException("not comparable") 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 package net.sergeych.lyng.stdlib_included
internal val rootLyng = """ internal val rootLyng = """
package lyng.stdlib package lyng.stdlib
fun cached(builder) {
var calculated = false
var value = null
{
if( !calculated ) {
value = builder()
calculated = true
}
value
}
}
fun Iterable.filter(predicate) { fun Iterable.filter(predicate) {
val list = this val list = this
flow { flow {
for( item in list ) { for( item in list ) {
if( predicate(item) ) { if( predicate(item) ) {ln
emit(item) emit(item)
} }
} }
@ -21,7 +50,7 @@ fun Iterable.drop(n) {
fun Iterable.first() { fun Iterable.first() {
val i = iterator() val i = iterator()
if( !i.hasNext() ) throw NoSuchElementException() if( !i.hasNext() ) throw NoSuchElementException()
i.next() i.next().also { i.cancelIteration() }
} }
fun Iterable.last() { fun Iterable.last() {
@ -54,5 +83,26 @@ fun Iterable.takeLast(n) {
buffer 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() """.trimIndent()

View File

@ -0,0 +1,30 @@
/*
* 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 {
var cnt = 0
for (c in s) {
when (c) {
' ' -> cnt++
'\t' -> cnt = (cnt / 4.0 + 0.9).toInt() * 4
else -> break
}
}
return cnt
}

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 package net.sergeych.lynon
interface BitInput { interface BitInput {
@ -82,6 +99,8 @@ interface BitInput {
fun decompressStringOrNull(): String? = decompressOrNull()?.decodeToString() fun decompressStringOrNull(): String? = decompressOrNull()?.decodeToString()
fun decompressString(): String = decompress().decodeToString() fun decompressString(): String = decompress().decodeToString()
@Suppress("unused")
fun unpackDouble(): Double { fun unpackDouble(): Double {
val bits = getBits(64) val bits = getBits(64)
return Double.fromBits(bits.toLong()) 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 package net.sergeych.lynon
@Suppress("unused") @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 package net.sergeych.lynon
interface BitOutput { interface BitOutput {
@ -84,7 +101,6 @@ interface BitOutput {
val compressed = LZW.compress(source.asUByteArray()) val compressed = LZW.compress(source.asUByteArray())
// check that compression is effective including header bits size: // check that compression is effective including header bits size:
if( compressed.size + 2 < source.size * 8L) { if( compressed.size + 2 < source.size * 8L) {
println("write compressed")
putBit(1) putBit(1)
// LZW algorithm // LZW algorithm
putBits(0, 2) 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 package net.sergeych.lynon
class DecompressionException(message: String) : IllegalArgumentException(message) {} 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 package net.sergeych.lynon
import net.sergeych.lyng.Scope 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 { private suspend fun decodeClassObj(scope: Scope): ObjClass {
val className = decodeObject(scope, ObjString.type, null) as ObjString val className = decodeObject(scope, ObjString.type, null) as ObjString
println("expected class name $className")
return scope.get(className.value)?.value?.let { return scope.get(className.value)?.value?.let {
if (it !is ObjClass) if (it !is ObjClass)
scope.raiseClassCastError("Expected obj class but got ${it::class.simpleName}") 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 package net.sergeych.lynon
import net.sergeych.bintools.ByteChunk 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 package net.sergeych.lynon
import net.sergeych.lyng.obj.ObjBool 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 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 package net.sergeych.lynon
import kotlin.math.min 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. * in the bit 0x02 of the first byte, etc.
* *
* This allows automatic fill of the last byte with zeros. This is important when * 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 * 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 * [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] * 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 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 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 package net.sergeych.lynon
import net.sergeych.collections.SortedList import net.sergeych.collections.SortedList

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