Compare commits
No commits in common. "62dc5016579042d42e4dfc6e6931181555243ddc" and "90e586b5d2259c457d80b571c26afe58aea2f17f" have entirely different histories.
62dc501657
...
90e586b5d2
@ -20,12 +20,13 @@
|
|||||||
set -e
|
set -e
|
||||||
echo "publishing all artifacts"
|
echo "publishing all artifacts"
|
||||||
echo
|
echo
|
||||||
./gradlew publishToMavenLocal publish buildInstallablePlugin
|
./gradlew publishToMavenLocal
|
||||||
|
./gradlew publish
|
||||||
|
|
||||||
#echo
|
echo
|
||||||
#echo "Creating plugin"
|
echo "Creating plugin"
|
||||||
#echo
|
echo
|
||||||
#./gradlew buildInstallablePlugin
|
./gradlew buildInstallablePlugin
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "building CLI tools"
|
echo "building CLI tools"
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# Array
|
# Array
|
||||||
|
|
||||||
It's an interface if the [Collection] that provides indexing access, like `array[3] = 0`.
|
It's an interface if the [Collection] that provides indexing access, like `array[3] = 0`.
|
||||||
Array therefore implements [Iterable] too. Well known implementations of `Array` are
|
Array therefore implements [Iterable] too. The well known implementatino of the `Array` is
|
||||||
[List] and [ImmutableList].
|
[List].
|
||||||
|
|
||||||
Array adds the following methods:
|
Array adds the following methods:
|
||||||
|
|
||||||
@ -35,4 +35,3 @@ To pre-sort and array use `Iterable.sorted*` or in-place `List.sort*` families,
|
|||||||
[Collection]: Collection.md
|
[Collection]: Collection.md
|
||||||
[Iterable]: Iterable.md
|
[Iterable]: Iterable.md
|
||||||
[List]: List.md
|
[List]: List.md
|
||||||
[ImmutableList]: ImmutableList.md
|
|
||||||
|
|||||||
@ -6,12 +6,6 @@ Is a [Iterable] with known `size`, a finite [Iterable]:
|
|||||||
val size
|
val size
|
||||||
}
|
}
|
||||||
|
|
||||||
`Collection` is a read/traversal contract shared by mutable and immutable collections.
|
|
||||||
Concrete collection classes:
|
|
||||||
|
|
||||||
- Mutable: [List], [Set], [Map]
|
|
||||||
- Immutable: [ImmutableList], [ImmutableSet], [ImmutableMap]
|
|
||||||
|
|
||||||
| name | description |
|
| name | description |
|
||||||
|------------------------|------------------------------------------------------|
|
|------------------------|------------------------------------------------------|
|
||||||
|
|
||||||
@ -22,8 +16,4 @@ See [List], [Set], [Iterable] and [Efficient Iterables in Kotlin Interop](Effici
|
|||||||
|
|
||||||
[Iterable]: Iterable.md
|
[Iterable]: Iterable.md
|
||||||
[List]: List.md
|
[List]: List.md
|
||||||
[Set]: Set.md
|
[Set]: Set.md
|
||||||
[Map]: Map.md
|
|
||||||
[ImmutableList]: ImmutableList.md
|
|
||||||
[ImmutableSet]: ImmutableSet.md
|
|
||||||
[ImmutableMap]: ImmutableMap.md
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
# ImmutableList built-in class
|
|
||||||
|
|
||||||
`ImmutableList` is an immutable, indexable list value.
|
|
||||||
It implements [Array], therefore [Collection] and [Iterable].
|
|
||||||
|
|
||||||
Use it when API contracts require a list that cannot be mutated through aliases.
|
|
||||||
|
|
||||||
## Creating
|
|
||||||
|
|
||||||
val a = ImmutableList(1,2,3)
|
|
||||||
val b = [1,2,3].toImmutable()
|
|
||||||
val c = (1..3).toImmutableList()
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
## Converting
|
|
||||||
|
|
||||||
val i = ImmutableList(1,2,3)
|
|
||||||
val m = i.toMutable()
|
|
||||||
m += 4
|
|
||||||
assertEquals( ImmutableList(1,2,3), i )
|
|
||||||
assertEquals( [1,2,3,4], m )
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
## Members
|
|
||||||
|
|
||||||
| name | meaning |
|
|
||||||
|---------------|-----------------------------------------|
|
|
||||||
| `size` | number of elements |
|
|
||||||
| `[index]` | element access by index |
|
|
||||||
| `[Range]` | immutable slice |
|
|
||||||
| `+` | append element(s), returns new immutable list |
|
|
||||||
| `-` | remove element(s), returns new immutable list |
|
|
||||||
| `toMutable()` | create mutable copy |
|
|
||||||
|
|
||||||
[Array]: Array.md
|
|
||||||
[Collection]: Collection.md
|
|
||||||
[Iterable]: Iterable.md
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
# ImmutableMap built-in class
|
|
||||||
|
|
||||||
`ImmutableMap` is an immutable map of key-value pairs.
|
|
||||||
It implements [Collection] and [Iterable] of [MapEntry].
|
|
||||||
|
|
||||||
## Creating
|
|
||||||
|
|
||||||
val a = ImmutableMap("a" => 1, "b" => 2)
|
|
||||||
val b = Map("a" => 1, "b" => 2).toImmutable()
|
|
||||||
val c = ["a" => 1, "b" => 2].toImmutableMap
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
## Converting
|
|
||||||
|
|
||||||
val i = ImmutableMap("a" => 1)
|
|
||||||
val m = i.toMutable()
|
|
||||||
m["a"] = 2
|
|
||||||
assertEquals( 1, i["a"] )
|
|
||||||
assertEquals( 2, m["a"] )
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
## Members
|
|
||||||
|
|
||||||
| name | meaning |
|
|
||||||
|-----------------|------------------------------------------|
|
|
||||||
| `size` | number of entries |
|
|
||||||
| `[key]` | get value by key, or `null` if absent |
|
|
||||||
| `getOrNull(key)`| same as `[key]` |
|
|
||||||
| `keys` | list of keys |
|
|
||||||
| `values` | list of values |
|
|
||||||
| `+` | merge (rightmost wins), returns new immutable map |
|
|
||||||
| `toMutable()` | create mutable copy |
|
|
||||||
|
|
||||||
[Collection]: Collection.md
|
|
||||||
[Iterable]: Iterable.md
|
|
||||||
[MapEntry]: Map.md
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
# ImmutableSet built-in class
|
|
||||||
|
|
||||||
`ImmutableSet` is an immutable set of unique elements.
|
|
||||||
It implements [Collection] and [Iterable].
|
|
||||||
|
|
||||||
## Creating
|
|
||||||
|
|
||||||
val a = ImmutableSet(1,2,3)
|
|
||||||
val b = Set(1,2,3).toImmutable()
|
|
||||||
val c = [1,2,3].toImmutableSet
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
## Converting
|
|
||||||
|
|
||||||
val i = ImmutableSet(1,2,3)
|
|
||||||
val m = i.toMutable()
|
|
||||||
m += 4
|
|
||||||
assertEquals( ImmutableSet(1,2,3), i )
|
|
||||||
assertEquals( Set(1,2,3,4), m )
|
|
||||||
>>> void
|
|
||||||
|
|
||||||
## Members
|
|
||||||
|
|
||||||
| name | meaning |
|
|
||||||
|---------------|-----------------------------------------------------|
|
|
||||||
| `size` | number of elements |
|
|
||||||
| `contains(x)` | membership test |
|
|
||||||
| `+`, `union` | union, returns new immutable set |
|
|
||||||
| `-`, `subtract` | subtraction, returns new immutable set |
|
|
||||||
| `*`, `intersect` | intersection, returns new immutable set |
|
|
||||||
| `toMutable()` | create mutable copy |
|
|
||||||
|
|
||||||
[Collection]: Collection.md
|
|
||||||
[Iterable]: Iterable.md
|
|
||||||
@ -147,15 +147,12 @@ Search for the first element that satisfies the given predicate:
|
|||||||
| fun/method | description |
|
| fun/method | description |
|
||||||
|------------------------|---------------------------------------------------------------------------------|
|
|------------------------|---------------------------------------------------------------------------------|
|
||||||
| toList() | create a list from iterable |
|
| toList() | create a list from iterable |
|
||||||
| toImmutableList() | create an immutable list from iterable |
|
|
||||||
| toSet() | create a set from iterable |
|
| toSet() | create a set from iterable |
|
||||||
| toImmutableSet | create an immutable set from iterable |
|
|
||||||
| contains(i) | check that iterable contains `i` |
|
| contains(i) | check that iterable contains `i` |
|
||||||
| `i in iterable` | same as `contains(i)` |
|
| `i in iterable` | same as `contains(i)` |
|
||||||
| isEmpty() | check iterable is empty |
|
| isEmpty() | check iterable is empty |
|
||||||
| forEach(f) | call f for each element |
|
| forEach(f) | call f for each element |
|
||||||
| toMap() | create a map from list of key-value pairs (arrays of 2 items or like) |
|
| toMap() | create a map from list of key-value pairs (arrays of 2 items or like) |
|
||||||
| toImmutableMap | create an immutable map from list of key-value pairs |
|
|
||||||
| any(p) | true if any element matches predicate `p` |
|
| any(p) | true if any element matches predicate `p` |
|
||||||
| all(p) | true if all elements match predicate `p` |
|
| all(p) | true if all elements match predicate `p` |
|
||||||
| map(f) | create a list of values returned by `f` called for each element of the iterable |
|
| map(f) | create a list of values returned by `f` called for each element of the iterable |
|
||||||
@ -209,20 +206,16 @@ For high-performance Kotlin-side interop and custom iterable implementation deta
|
|||||||
|
|
||||||
## Implemented in classes:
|
## Implemented in classes:
|
||||||
|
|
||||||
- [List], [ImmutableList], [Range], [Buffer](Buffer.md), [BitBuffer], [Buffer], [Set], [ImmutableSet], [Map], [ImmutableMap], [RingBuffer]
|
- [List], [Range], [Buffer](Buffer.md), [BitBuffer], [Buffer], [Set], [RingBuffer]
|
||||||
|
|
||||||
[Collection]: Collection.md
|
[Collection]: Collection.md
|
||||||
|
|
||||||
[List]: List.md
|
[List]: List.md
|
||||||
[ImmutableList]: ImmutableList.md
|
|
||||||
|
|
||||||
[Flow]: parallelism.md#flow
|
[Flow]: parallelism.md#flow
|
||||||
|
|
||||||
[Range]: Range.md
|
[Range]: Range.md
|
||||||
|
|
||||||
[Set]: Set.md
|
[Set]: Set.md
|
||||||
[ImmutableSet]: ImmutableSet.md
|
|
||||||
[Map]: Map.md
|
|
||||||
[ImmutableMap]: ImmutableMap.md
|
|
||||||
|
|
||||||
[RingBuffer]: RingBuffer.md
|
[RingBuffer]: RingBuffer.md
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
# List built-in class
|
# List built-in class
|
||||||
|
|
||||||
Mutable list of any objects.
|
Mutable list of any objects.
|
||||||
For immutable list values, see [ImmutableList].
|
|
||||||
|
|
||||||
It's class in Lyng is `List`:
|
It's class in Lyng is `List`:
|
||||||
|
|
||||||
@ -197,5 +196,4 @@ It inherits from [Iterable] too and thus all iterable methods are applicable to
|
|||||||
|
|
||||||
[Range]: Range.md
|
[Range]: Range.md
|
||||||
|
|
||||||
[Iterable]: Iterable.md
|
[Iterable]: Iterable.md
|
||||||
[ImmutableList]: ImmutableList.md
|
|
||||||
@ -3,7 +3,6 @@
|
|||||||
Map is a mutable collection of key-value pairs, where keys are unique. You can create maps in two ways:
|
Map is a mutable collection of key-value pairs, where keys are unique. You can create maps in two ways:
|
||||||
- with the constructor `Map(...)` or `.toMap()` helpers; and
|
- with the constructor `Map(...)` or `.toMap()` helpers; and
|
||||||
- with map literals using braces: `{ "key": value, id: expr, id: }`.
|
- with map literals using braces: `{ "key": value, id: expr, id: }`.
|
||||||
For immutable map values, see [ImmutableMap].
|
|
||||||
|
|
||||||
When constructing from a list, each list item must be a [Collection] with exactly 2 elements, for example, a [List].
|
When constructing from a list, each list item must be a [Collection] with exactly 2 elements, for example, a [List].
|
||||||
|
|
||||||
@ -178,4 +177,3 @@ Notes:
|
|||||||
- When you need computed or non-string keys, use the constructor form `Map(...)`, map literals with computed keys (if supported), or build entries with `=>` and then merge.
|
- When you need computed or non-string keys, use the constructor form `Map(...)`, map literals with computed keys (if supported), or build entries with `=>` and then merge.
|
||||||
|
|
||||||
[Collection](Collection.md)
|
[Collection](Collection.md)
|
||||||
[ImmutableMap]: ImmutableMap.md
|
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
# Set built-in class
|
# List built-in class
|
||||||
|
|
||||||
Mutable set of any objects: a group of different objects, no repetitions.
|
Mutable set of any objects: a group of different objects, no repetitions.
|
||||||
Sets are not ordered, order of appearance does not matter.
|
Sets are not ordered, order of appearance does not matter.
|
||||||
For immutable set values, see [ImmutableSet].
|
|
||||||
|
|
||||||
val set = Set(1,2,3, "foo")
|
val set = Set(1,2,3, "foo")
|
||||||
assert( 1 in set )
|
assert( 1 in set )
|
||||||
@ -93,4 +92,3 @@ Also, it inherits methods from [Iterable].
|
|||||||
|
|
||||||
|
|
||||||
[Range]: Range.md
|
[Range]: Range.md
|
||||||
[ImmutableSet]: ImmutableSet.md
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ __Other documents to read__ maybe after this one:
|
|||||||
- [time](time.md) and [parallelism](parallelism.md)
|
- [time](time.md) and [parallelism](parallelism.md)
|
||||||
- [parallelism] - multithreaded code, coroutines, etc.
|
- [parallelism] - multithreaded code, coroutines, etc.
|
||||||
- Some class
|
- Some class
|
||||||
references: [List], [ImmutableList], [Set], [ImmutableSet], [Map], [ImmutableMap], [Real], [Range], [Iterable], [Iterator], [time manipulation](time.md), [Array], [RingBuffer], [Buffer].
|
references: [List], [Set], [Map], [Real], [Range], [Iterable], [Iterator], [time manipulation](time.md), [Array], [RingBuffer], [Buffer].
|
||||||
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and
|
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and
|
||||||
loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
|
loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
|
||||||
|
|
||||||
@ -785,7 +785,6 @@ Lyng has built-in mutable array class `List` with simple literals:
|
|||||||
|
|
||||||
[List] is an implementation of the type `Array`, and through it `Collection` and [Iterable]. Please read [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.
|
many collection based methods are implemented there.
|
||||||
For immutable list values, use `list.toImmutable()` and [ImmutableList].
|
|
||||||
|
|
||||||
Lists can contain any type of objects, lists too:
|
Lists can contain any type of objects, lists too:
|
||||||
|
|
||||||
@ -968,7 +967,6 @@ Set are unordered collection of unique elements, see [Set]. Sets are [Iterable]
|
|||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
Please see [Set] for detailed description.
|
Please see [Set] for detailed description.
|
||||||
For immutable set values, use `set.toImmutable()` and [ImmutableSet].
|
|
||||||
|
|
||||||
# Maps
|
# Maps
|
||||||
|
|
||||||
@ -1029,7 +1027,6 @@ Notes:
|
|||||||
- When you need computed (expression) keys or non-string keys, use `Map(...)` constructor with entries, e.g. `Map( ("a" + "b") => 1 )`, then merge with a literal if needed: `{ base: } + (computedKey => 42)`.
|
- When you need computed (expression) keys or non-string keys, use `Map(...)` constructor with entries, e.g. `Map( ("a" + "b") => 1 )`, then merge with a literal if needed: `{ base: } + (computedKey => 42)`.
|
||||||
|
|
||||||
Please see the [Map] reference for a deeper guide.
|
Please see the [Map] reference for a deeper guide.
|
||||||
For immutable map values, use `map.toImmutable()` and [ImmutableMap].
|
|
||||||
|
|
||||||
# Flow control operators
|
# Flow control operators
|
||||||
|
|
||||||
@ -1753,7 +1750,6 @@ Lambda avoid unnecessary execution if assertion is not failed. for example:
|
|||||||
| π | See [math](math.md) |
|
| π | See [math](math.md) |
|
||||||
|
|
||||||
[List]: List.md
|
[List]: List.md
|
||||||
[ImmutableList]: ImmutableList.md
|
|
||||||
|
|
||||||
[Testing]: Testing.md
|
[Testing]: Testing.md
|
||||||
|
|
||||||
@ -1770,10 +1766,8 @@ Lambda avoid unnecessary execution if assertion is not failed. for example:
|
|||||||
[string formatting]: https://github.com/sergeych/mp_stools?tab=readme-ov-file#sprintf-syntax-summary
|
[string formatting]: https://github.com/sergeych/mp_stools?tab=readme-ov-file#sprintf-syntax-summary
|
||||||
|
|
||||||
[Set]: Set.md
|
[Set]: Set.md
|
||||||
[ImmutableSet]: ImmutableSet.md
|
|
||||||
|
|
||||||
[Map]: Map.md
|
[Map]: Map.md
|
||||||
[ImmutableMap]: ImmutableMap.md
|
|
||||||
|
|
||||||
[Buffer]: Buffer.md
|
[Buffer]: Buffer.md
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "1.5.0-SNAPSHOT"
|
version = "1.5.1-SNAPSHOT"
|
||||||
|
|
||||||
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,6 @@ class ClassInstanceFieldDeclStatement(
|
|||||||
val isMutable: Boolean,
|
val isMutable: Boolean,
|
||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
val writeVisibility: Visibility?,
|
val writeVisibility: Visibility?,
|
||||||
val typeDecl: TypeDecl?,
|
|
||||||
val isAbstract: Boolean,
|
val isAbstract: Boolean,
|
||||||
val isClosed: Boolean,
|
val isClosed: Boolean,
|
||||||
val isOverride: Boolean,
|
val isOverride: Boolean,
|
||||||
|
|||||||
@ -169,7 +169,6 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
private val typeAliases: MutableMap<String, TypeAliasDecl> = mutableMapOf()
|
private val typeAliases: MutableMap<String, TypeAliasDecl> = mutableMapOf()
|
||||||
private val methodReturnTypeDeclByRef: MutableMap<ObjRef, TypeDecl> = mutableMapOf()
|
private val methodReturnTypeDeclByRef: MutableMap<ObjRef, TypeDecl> = mutableMapOf()
|
||||||
private val callReturnTypeDeclByRef: MutableMap<CallRef, TypeDecl> = mutableMapOf()
|
|
||||||
private val callableReturnTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
private val callableReturnTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
||||||
private val callableReturnTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
private val callableReturnTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
||||||
private val lambdaReturnTypeByRef: MutableMap<ObjRef, ObjClass> = mutableMapOf()
|
private val lambdaReturnTypeByRef: MutableMap<ObjRef, ObjClass> = mutableMapOf()
|
||||||
@ -212,9 +211,6 @@ class Compiler(
|
|||||||
scopeSeedNames.add(name)
|
scopeSeedNames.add(name)
|
||||||
if (record.typeDecl != null && nameTypeDecl[name] == null) {
|
if (record.typeDecl != null && nameTypeDecl[name] == null) {
|
||||||
nameTypeDecl[name] = record.typeDecl
|
nameTypeDecl[name] = record.typeDecl
|
||||||
if (nameObjClass[name] == null) {
|
|
||||||
resolveTypeDeclObjClass(record.typeDecl)?.let { nameObjClass[name] = it }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val instance = record.value as? ObjInstance
|
val instance = record.value as? ObjInstance
|
||||||
if (instance != null && nameObjClass[name] == null) {
|
if (instance != null && nameObjClass[name] == null) {
|
||||||
@ -294,9 +290,6 @@ class Compiler(
|
|||||||
scopeSeedNames.add(name)
|
scopeSeedNames.add(name)
|
||||||
if (record.typeDecl != null && nameTypeDecl[name] == null) {
|
if (record.typeDecl != null && nameTypeDecl[name] == null) {
|
||||||
nameTypeDecl[name] = record.typeDecl
|
nameTypeDecl[name] = record.typeDecl
|
||||||
if (nameObjClass[name] == null) {
|
|
||||||
resolveTypeDeclObjClass(record.typeDecl)?.let { nameObjClass[name] = it }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (record.typeDecl != null) {
|
if (record.typeDecl != null) {
|
||||||
slotTypeDeclByScopeId.getOrPut(plan.id) { mutableMapOf() }[slotIndex] = record.typeDecl
|
slotTypeDeclByScopeId.getOrPut(plan.id) { mutableMapOf() }[slotIndex] = record.typeDecl
|
||||||
@ -1214,11 +1207,6 @@ class Compiler(
|
|||||||
for ((name, record) in current.objects) {
|
for ((name, record) in current.objects) {
|
||||||
if (!record.visibility.isPublic) continue
|
if (!record.visibility.isPublic) continue
|
||||||
if (nameObjClass.containsKey(name)) continue
|
if (nameObjClass.containsKey(name)) continue
|
||||||
val declaredClass = record.typeDecl?.let { resolveTypeDeclObjClass(it) }
|
|
||||||
if (declaredClass != null) {
|
|
||||||
nameObjClass[name] = declaredClass
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val resolved = when (val raw = record.value) {
|
val resolved = when (val raw = record.value) {
|
||||||
is FrameSlotRef -> raw.peekValue() ?: raw.read()
|
is FrameSlotRef -> raw.peekValue() ?: raw.read()
|
||||||
is RecordSlotRef -> raw.peekValue() ?: raw.read()
|
is RecordSlotRef -> raw.peekValue() ?: raw.read()
|
||||||
@ -2530,9 +2518,6 @@ class Compiler(
|
|||||||
} else {
|
} else {
|
||||||
val rvalue = parseExpressionLevel(level + 1)
|
val rvalue = parseExpressionLevel(level + 1)
|
||||||
?: throw ScriptError(opToken.pos, "Expecting expression")
|
?: throw ScriptError(opToken.pos, "Expecting expression")
|
||||||
if (opToken.type == Token.Type.PLUSASSIGN) {
|
|
||||||
checkCollectionPlusAssignTypes(lvalue!!, rvalue, opToken.pos)
|
|
||||||
}
|
|
||||||
op.generate(opToken.pos, lvalue!!, rvalue)
|
op.generate(opToken.pos, lvalue!!, rvalue)
|
||||||
}
|
}
|
||||||
if (opToken.type == Token.Type.ASSIGN) {
|
if (opToken.type == Token.Type.ASSIGN) {
|
||||||
@ -2559,7 +2544,6 @@ class Compiler(
|
|||||||
|
|
||||||
private suspend fun parseTerm(): ObjRef? {
|
private suspend fun parseTerm(): ObjRef? {
|
||||||
var operand: ObjRef? = null
|
var operand: ObjRef? = null
|
||||||
var pendingCallTypeArgs: List<TypeDecl>? = null
|
|
||||||
|
|
||||||
// newlines _before_
|
// newlines _before_
|
||||||
cc.skipWsTokens()
|
cc.skipWsTokens()
|
||||||
@ -2807,35 +2791,20 @@ class Compiler(
|
|||||||
operand = parseScopeOperator(operand)
|
operand = parseScopeOperator(operand)
|
||||||
}
|
}
|
||||||
|
|
||||||
Token.Type.LT -> {
|
|
||||||
val parsedTypeArgs = operand
|
|
||||||
?.takeIf { isGenericCallCalleeCandidate(it) }
|
|
||||||
?.let { tryParseCallTypeArgsAfterLt() }
|
|
||||||
if (parsedTypeArgs != null) {
|
|
||||||
pendingCallTypeArgs = parsedTypeArgs
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cc.previous()
|
|
||||||
return operand
|
|
||||||
}
|
|
||||||
|
|
||||||
Token.Type.LPAREN, Token.Type.NULL_COALESCE_INVOKE -> {
|
Token.Type.LPAREN, Token.Type.NULL_COALESCE_INVOKE -> {
|
||||||
operand?.let { left ->
|
operand?.let { left ->
|
||||||
// this is function call from <left>
|
// this is function call from <left>
|
||||||
operand = parseFunctionCall(
|
operand = parseFunctionCall(
|
||||||
left,
|
left,
|
||||||
false,
|
false,
|
||||||
t.type == Token.Type.NULL_COALESCE_INVOKE,
|
t.type == Token.Type.NULL_COALESCE_INVOKE
|
||||||
pendingCallTypeArgs
|
|
||||||
)
|
)
|
||||||
pendingCallTypeArgs = null
|
|
||||||
} ?: run {
|
} ?: run {
|
||||||
// Expression in parentheses
|
// Expression in parentheses
|
||||||
val statement = parseStatement() ?: throw ScriptError(t.pos, "Expecting expression")
|
val statement = parseStatement() ?: throw ScriptError(t.pos, "Expecting expression")
|
||||||
operand = StatementRef(statement)
|
operand = StatementRef(statement)
|
||||||
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true)
|
||||||
cc.skipTokenOfType(Token.Type.RPAREN, "missing ')'")
|
cc.skipTokenOfType(Token.Type.RPAREN, "missing ')'")
|
||||||
pendingCallTypeArgs = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3015,8 +2984,7 @@ class Compiler(
|
|||||||
parseFunctionCall(
|
parseFunctionCall(
|
||||||
left,
|
left,
|
||||||
blockArgument = true,
|
blockArgument = true,
|
||||||
isOptional = t.type == Token.Type.NULL_COALESCE_BLOCKINVOKE,
|
isOptional = t.type == Token.Type.NULL_COALESCE_BLOCKINVOKE
|
||||||
explicitTypeArgs = pendingCallTypeArgs
|
|
||||||
)
|
)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
// Disambiguate between lambda and map literal.
|
// Disambiguate between lambda and map literal.
|
||||||
@ -3043,54 +3011,6 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun tryParseCallTypeArgsAfterLt(): List<TypeDecl>? {
|
|
||||||
val savedAfterLt = cc.savePos()
|
|
||||||
return try {
|
|
||||||
val args = mutableListOf<TypeDecl>()
|
|
||||||
do {
|
|
||||||
val (argSem, _) = parseTypeExpressionWithMini()
|
|
||||||
args += argSem
|
|
||||||
val sep = cc.next()
|
|
||||||
when (sep.type) {
|
|
||||||
Token.Type.COMMA -> continue
|
|
||||||
Token.Type.GT -> break
|
|
||||||
Token.Type.SHR -> {
|
|
||||||
cc.pushPendingGT()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
cc.restorePos(savedAfterLt)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (true)
|
|
||||||
val nextType = cc.peekNextNonWhitespace().type
|
|
||||||
if (nextType != Token.Type.LPAREN && nextType != Token.Type.NULL_COALESCE_INVOKE) {
|
|
||||||
cc.restorePos(savedAfterLt)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
args
|
|
||||||
} catch (_: ScriptError) {
|
|
||||||
cc.restorePos(savedAfterLt)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isGenericCallCalleeCandidate(ref: ObjRef): Boolean {
|
|
||||||
val name = when (ref) {
|
|
||||||
is LocalVarRef -> ref.name
|
|
||||||
is FastLocalVarRef -> ref.name
|
|
||||||
is LocalSlotRef -> ref.name
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (name != null) {
|
|
||||||
if (lookupGenericFunctionDecl(name) != null) return true
|
|
||||||
if (name.firstOrNull()?.isUpperCase() == true) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return ref is ConstRef && ref.constValue is ObjClass
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse lambda expression, leading '{' is already consumed
|
* Parse lambda expression, leading '{' is already consumed
|
||||||
*/
|
*/
|
||||||
@ -4211,22 +4131,6 @@ class Compiler(
|
|||||||
is ListLiteralRef -> inferListLiteralTypeDecl(ref)
|
is ListLiteralRef -> inferListLiteralTypeDecl(ref)
|
||||||
is MapLiteralRef -> inferMapLiteralTypeDecl(ref)
|
is MapLiteralRef -> inferMapLiteralTypeDecl(ref)
|
||||||
is ConstRef -> inferTypeDeclFromConst(ref.constValue)
|
is ConstRef -> inferTypeDeclFromConst(ref.constValue)
|
||||||
is CallRef -> {
|
|
||||||
inferCallReturnClass(ref)?.let { TypeDecl.Simple(it.className, false) }
|
|
||||||
?: run {
|
|
||||||
val targetName = when (val target = ref.target) {
|
|
||||||
is LocalVarRef -> target.name
|
|
||||||
is FastLocalVarRef -> target.name
|
|
||||||
is LocalSlotRef -> target.name
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (targetName != null && targetName.firstOrNull()?.isUpperCase() == true) {
|
|
||||||
TypeDecl.Simple(targetName, false)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4339,7 +4243,7 @@ class Compiler(
|
|||||||
val mapType = inferTypeDeclFromRef(entry.ref) ?: return TypeDecl.TypeAny to TypeDecl.TypeAny
|
val mapType = inferTypeDeclFromRef(entry.ref) ?: return TypeDecl.TypeAny to TypeDecl.TypeAny
|
||||||
if (mapType is TypeDecl.Generic) {
|
if (mapType is TypeDecl.Generic) {
|
||||||
val base = mapType.name.substringAfterLast('.')
|
val base = mapType.name.substringAfterLast('.')
|
||||||
if (base == "Map" || base == "ImmutableMap") {
|
if (base == "Map") {
|
||||||
val k = mapType.args.getOrNull(0) ?: TypeDecl.TypeAny
|
val k = mapType.args.getOrNull(0) ?: TypeDecl.TypeAny
|
||||||
val v = mapType.args.getOrNull(1) ?: TypeDecl.TypeAny
|
val v = mapType.args.getOrNull(1) ?: TypeDecl.TypeAny
|
||||||
addKey(k)
|
addKey(k)
|
||||||
@ -4374,73 +4278,13 @@ class Compiler(
|
|||||||
if (listType == TypeDecl.TypeAny || listType == TypeDecl.TypeNullableAny) return listType
|
if (listType == TypeDecl.TypeAny || listType == TypeDecl.TypeNullableAny) return listType
|
||||||
if (listType is TypeDecl.Generic) {
|
if (listType is TypeDecl.Generic) {
|
||||||
val base = listType.name.substringAfterLast('.')
|
val base = listType.name.substringAfterLast('.')
|
||||||
if (base == "List" || base == "ImmutableList" || base == "Array" || base == "Iterable") {
|
if (base == "List" || base == "Array" || base == "Iterable") {
|
||||||
return listType.args.firstOrNull() ?: TypeDecl.TypeAny
|
return listType.args.firstOrNull() ?: TypeDecl.TypeAny
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TypeDecl.TypeAny
|
return TypeDecl.TypeAny
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inferCollectionElementType(typeDecl: TypeDecl): TypeDecl? {
|
|
||||||
val generic = typeDecl as? TypeDecl.Generic ?: return null
|
|
||||||
val base = generic.name.substringAfterLast('.')
|
|
||||||
return when (base) {
|
|
||||||
"Set", "ImmutableSet", "List", "ImmutableList", "Iterable", "Collection", "Array" -> generic.args.firstOrNull()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun typeDeclSubtypeOf(arg: TypeDecl, param: TypeDecl): Boolean {
|
|
||||||
if (param == TypeDecl.TypeAny || param == TypeDecl.TypeNullableAny) return true
|
|
||||||
val (argBase, argNullable) = stripNullable(arg)
|
|
||||||
val (paramBase, paramNullable) = stripNullable(param)
|
|
||||||
if (argNullable && !paramNullable) return false
|
|
||||||
if (paramBase == TypeDecl.TypeAny) return true
|
|
||||||
if (paramBase is TypeDecl.TypeVar) return true
|
|
||||||
if (argBase is TypeDecl.TypeVar) return true
|
|
||||||
if (paramBase is TypeDecl.Simple && (paramBase.name == "Object" || paramBase.name == "Obj")) return true
|
|
||||||
if (argBase is TypeDecl.Ellipsis) return typeDeclSubtypeOf(argBase.elementType, paramBase)
|
|
||||||
if (paramBase is TypeDecl.Ellipsis) return typeDeclSubtypeOf(argBase, paramBase.elementType)
|
|
||||||
return when (argBase) {
|
|
||||||
is TypeDecl.Union -> argBase.options.all { typeDeclSubtypeOf(it, paramBase) }
|
|
||||||
is TypeDecl.Intersection -> argBase.options.any { typeDeclSubtypeOf(it, paramBase) }
|
|
||||||
else -> when (paramBase) {
|
|
||||||
is TypeDecl.Union -> paramBase.options.any { typeDeclSubtypeOf(argBase, it) }
|
|
||||||
is TypeDecl.Intersection -> paramBase.options.all { typeDeclSubtypeOf(argBase, it) }
|
|
||||||
else -> {
|
|
||||||
val argClass = resolveTypeDeclObjClass(argBase) ?: return false
|
|
||||||
val paramClass = resolveTypeDeclObjClass(paramBase) ?: return false
|
|
||||||
argClass == paramClass || argClass.allParentsSet.contains(paramClass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkCollectionPlusAssignTypes(targetRef: ObjRef, valueRef: ObjRef, pos: Pos) {
|
|
||||||
// Enforce strict compile-time element checks for declared members.
|
|
||||||
// Local vars can be inferred from literals and are allowed to widen dynamically.
|
|
||||||
if (targetRef !is FieldRef) return
|
|
||||||
val targetDeclRaw = resolveReceiverTypeDecl(targetRef) ?: return
|
|
||||||
val targetDecl = expandTypeAliases(targetDeclRaw, pos)
|
|
||||||
val targetGeneric = targetDecl as? TypeDecl.Generic ?: return
|
|
||||||
val targetBase = targetGeneric.name.substringAfterLast('.')
|
|
||||||
if (targetBase != "Set" && targetBase != "List") return
|
|
||||||
val elementRaw = targetGeneric.args.firstOrNull() ?: return
|
|
||||||
val elementDecl = expandTypeAliases(elementRaw, pos)
|
|
||||||
val valueDeclRaw = inferTypeDeclFromRef(valueRef) ?: return
|
|
||||||
val valueDecl = expandTypeAliases(valueDeclRaw, pos)
|
|
||||||
|
|
||||||
if (typeDeclSubtypeOf(valueDecl, elementDecl)) return
|
|
||||||
|
|
||||||
val sourceElementDecl = inferCollectionElementType(valueDecl)?.let { expandTypeAliases(it, pos) }
|
|
||||||
if (sourceElementDecl != null && typeDeclSubtypeOf(sourceElementDecl, elementDecl)) return
|
|
||||||
|
|
||||||
throw ScriptError(
|
|
||||||
pos,
|
|
||||||
"argument type ${typeDeclName(valueDecl)} does not match ${typeDeclName(elementDecl)} for '+='"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun stripNullable(type: TypeDecl): Pair<TypeDecl, Boolean> {
|
private fun stripNullable(type: TypeDecl): Pair<TypeDecl, Boolean> {
|
||||||
if (type is TypeDecl.TypeNullableAny) return TypeDecl.TypeAny to true
|
if (type is TypeDecl.TypeNullableAny) return TypeDecl.TypeAny to true
|
||||||
val nullable = type.isNullable
|
val nullable = type.isNullable
|
||||||
@ -4491,41 +4335,16 @@ class Compiler(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun seedTypeDeclByName(name: String): TypeDecl? {
|
|
||||||
seedScope?.getLocalRecordDirect(name)?.typeDecl?.let { return it }
|
|
||||||
seedScope?.get(name)?.typeDecl?.let { return it }
|
|
||||||
importManager.rootScope.getLocalRecordDirect(name)?.typeDecl?.let { return it }
|
|
||||||
importManager.rootScope.get(name)?.typeDecl?.let { return it }
|
|
||||||
for (module in importedModules.asReversed()) {
|
|
||||||
module.scope.get(name)?.typeDecl?.let { return it }
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveReceiverTypeDecl(ref: ObjRef): TypeDecl? {
|
private fun resolveReceiverTypeDecl(ref: ObjRef): TypeDecl? {
|
||||||
return when (ref) {
|
return when (ref) {
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
val ownerScopeId = ref.captureOwnerScopeId ?: ref.scopeId
|
val ownerScopeId = ref.captureOwnerScopeId ?: ref.scopeId
|
||||||
val ownerSlot = ref.captureOwnerSlot ?: ref.slot
|
val ownerSlot = ref.captureOwnerSlot ?: ref.slot
|
||||||
slotTypeDeclByScopeId[ownerScopeId]?.get(ownerSlot)
|
slotTypeDeclByScopeId[ownerScopeId]?.get(ownerSlot)
|
||||||
?: nameTypeDecl[ref.name]
|
|
||||||
?: seedTypeDeclByName(ref.name)
|
|
||||||
}
|
|
||||||
is LocalVarRef -> nameTypeDecl[ref.name] ?: seedTypeDeclByName(ref.name)
|
|
||||||
is FastLocalVarRef -> nameTypeDecl[ref.name] ?: seedTypeDeclByName(ref.name)
|
|
||||||
is FieldRef -> {
|
|
||||||
val targetDecl = resolveReceiverTypeDecl(ref.target) ?: return null
|
|
||||||
val targetClass = resolveTypeDeclObjClass(targetDecl) ?: resolveReceiverClassForMember(ref.target)
|
|
||||||
targetClass?.getInstanceMemberOrNull(ref.name, includeAbstract = true)?.typeDecl?.let { return it }
|
|
||||||
classFieldTypesByName[targetClass?.className]?.get(ref.name)
|
|
||||||
?.let { return TypeDecl.Simple(it.className, false) }
|
|
||||||
when (targetDecl) {
|
|
||||||
TypeDecl.TypeAny, TypeDecl.TypeNullableAny -> null
|
|
||||||
else -> TypeDecl.TypeVar("${typeDeclName(targetDecl)}.${ref.name}", false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
is LocalVarRef -> nameTypeDecl[ref.name]
|
||||||
|
is FastLocalVarRef -> nameTypeDecl[ref.name]
|
||||||
is MethodCallRef -> methodReturnTypeDeclByRef[ref]
|
is MethodCallRef -> methodReturnTypeDeclByRef[ref]
|
||||||
is CallRef -> callReturnTypeDeclByRef[ref]
|
|
||||||
is StatementRef -> (ref.statement as? ExpressionStatement)?.let { resolveReceiverTypeDecl(it.ref) }
|
is StatementRef -> (ref.statement as? ExpressionStatement)?.let { resolveReceiverTypeDecl(it.ref) }
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@ -4542,8 +4361,6 @@ class Compiler(
|
|||||||
} else {
|
} else {
|
||||||
slotTypeByScopeId[ownerScopeId]?.get(ownerSlot)
|
slotTypeByScopeId[ownerScopeId]?.get(ownerSlot)
|
||||||
?: slotTypeDeclByScopeId[ownerScopeId]?.get(ownerSlot)?.let { resolveTypeDeclObjClass(it) }
|
?: slotTypeDeclByScopeId[ownerScopeId]?.get(ownerSlot)?.let { resolveTypeDeclObjClass(it) }
|
||||||
?: nameTypeDecl[ref.name]?.let { resolveTypeDeclObjClass(it) }
|
|
||||||
?: seedTypeDeclByName(ref.name)?.let { resolveTypeDeclObjClass(it) }
|
|
||||||
?: knownClass
|
?: knownClass
|
||||||
}
|
}
|
||||||
?: resolveClassByName(ref.name)
|
?: resolveClassByName(ref.name)
|
||||||
@ -4669,58 +4486,6 @@ class Compiler(
|
|||||||
name == "next" && receiver is TypeDecl.Generic && base == "Iterator" -> {
|
name == "next" && receiver is TypeDecl.Generic && base == "Iterator" -> {
|
||||||
receiver.args.firstOrNull()
|
receiver.args.firstOrNull()
|
||||||
}
|
}
|
||||||
name == "toImmutableList" && receiver is TypeDecl.Generic && (base == "Iterable" || base == "Collection" || base == "Array" || base == "List" || base == "ImmutableList") -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("ImmutableList", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toList" && receiver is TypeDecl.Generic && (base == "ImmutableList" || base == "List") -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("List", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toMutable" && receiver is TypeDecl.Generic && base == "ImmutableList" -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("List", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toImmutable" && receiver is TypeDecl.Generic && base == "List" -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("ImmutableList", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toImmutableSet" && receiver is TypeDecl.Generic && (base == "Iterable" || base == "Collection" || base == "Set" || base == "ImmutableSet") -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("ImmutableSet", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toSet" && receiver is TypeDecl.Generic && (base == "Iterable" || base == "Collection" || base == "Set" || base == "ImmutableSet") -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("Set", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toMutable" && receiver is TypeDecl.Generic && base == "ImmutableSet" -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("Set", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toImmutable" && receiver is TypeDecl.Generic && base == "Set" -> {
|
|
||||||
val arg = receiver.args.firstOrNull() ?: TypeDecl.TypeAny
|
|
||||||
TypeDecl.Generic("ImmutableSet", listOf(arg), false)
|
|
||||||
}
|
|
||||||
name == "toImmutableMap" && receiver is TypeDecl.Generic && base == "Iterable" -> {
|
|
||||||
TypeDecl.Generic("ImmutableMap", listOf(TypeDecl.TypeAny, TypeDecl.TypeAny), false)
|
|
||||||
}
|
|
||||||
name == "toMap" && receiver is TypeDecl.Generic && base == "Iterable" -> {
|
|
||||||
TypeDecl.Generic("Map", listOf(TypeDecl.TypeAny, TypeDecl.TypeAny), false)
|
|
||||||
}
|
|
||||||
name == "toMutable" && receiver is TypeDecl.Generic && base == "ImmutableMap" -> {
|
|
||||||
val args = receiver.args.ifEmpty { listOf(TypeDecl.TypeAny, TypeDecl.TypeAny) }
|
|
||||||
TypeDecl.Generic("Map", args, false)
|
|
||||||
}
|
|
||||||
name == "toImmutable" && receiver is TypeDecl.Generic && base == "Map" -> {
|
|
||||||
val args = receiver.args.ifEmpty { listOf(TypeDecl.TypeAny, TypeDecl.TypeAny) }
|
|
||||||
TypeDecl.Generic("ImmutableMap", args, false)
|
|
||||||
}
|
|
||||||
name == "toImmutable" && base == "List" -> TypeDecl.Simple("ImmutableList", false)
|
|
||||||
name == "toMutable" && base == "ImmutableList" -> TypeDecl.Simple("List", false)
|
|
||||||
name == "toImmutable" && base == "Set" -> TypeDecl.Simple("ImmutableSet", false)
|
|
||||||
name == "toMutable" && base == "ImmutableSet" -> TypeDecl.Simple("Set", false)
|
|
||||||
name == "toImmutable" && base == "Map" -> TypeDecl.Simple("ImmutableMap", false)
|
|
||||||
name == "toMutable" && base == "ImmutableMap" -> TypeDecl.Simple("Map", false)
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4791,10 +4556,6 @@ class Compiler(
|
|||||||
"matches" -> ObjBool.type
|
"matches" -> ObjBool.type
|
||||||
"toInt",
|
"toInt",
|
||||||
"toEpochSeconds" -> ObjInt.type
|
"toEpochSeconds" -> ObjInt.type
|
||||||
"toImmutableList" -> ObjImmutableList.type
|
|
||||||
"toImmutableSet" -> ObjImmutableSet.type
|
|
||||||
"toImmutableMap" -> ObjImmutableMap.type
|
|
||||||
"toImmutable" -> Obj.rootObjectType
|
|
||||||
"toMutable" -> ObjMutableBuffer.type
|
"toMutable" -> ObjMutableBuffer.type
|
||||||
"seq" -> ObjFlow.type
|
"seq" -> ObjFlow.type
|
||||||
"encode" -> ObjBitBuffer.type
|
"encode" -> ObjBitBuffer.type
|
||||||
@ -4806,18 +4567,6 @@ class Compiler(
|
|||||||
if (targetClass == null) return null
|
if (targetClass == null) return null
|
||||||
if (targetClass == ObjDynamic.type) return ObjDynamic.type
|
if (targetClass == ObjDynamic.type) return ObjDynamic.type
|
||||||
classFieldTypesByName[targetClass.className]?.get(name)?.let { return it }
|
classFieldTypesByName[targetClass.className]?.get(name)?.let { return it }
|
||||||
var hasUntypedMember = false
|
|
||||||
targetClass.getInstanceMemberOrNull(name, includeAbstract = true)?.let { member ->
|
|
||||||
member.typeDecl?.let { declaredType ->
|
|
||||||
when (declaredType) {
|
|
||||||
TypeDecl.TypeAny, TypeDecl.TypeNullableAny -> return Obj.rootObjectType
|
|
||||||
else -> {
|
|
||||||
resolveTypeDeclObjClass(declaredType)?.let { return it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hasUntypedMember = true
|
|
||||||
}
|
|
||||||
enumEntriesByName[targetClass.className]?.let { entries ->
|
enumEntriesByName[targetClass.className]?.let { entries ->
|
||||||
return when {
|
return when {
|
||||||
name == "entries" -> ObjList.type
|
name == "entries" -> ObjList.type
|
||||||
@ -4904,9 +4653,6 @@ class Compiler(
|
|||||||
if (targetClass == ObjRegex.type && name == "pattern") {
|
if (targetClass == ObjRegex.type && name == "pattern") {
|
||||||
return ObjString.type
|
return ObjString.type
|
||||||
}
|
}
|
||||||
if (hasUntypedMember) {
|
|
||||||
return ObjDynamic.type
|
|
||||||
}
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4920,10 +4666,6 @@ class Compiler(
|
|||||||
if (left is LocalSlotRef && left.name == "scope") return
|
if (left is LocalSlotRef && left.name == "scope") return
|
||||||
val receiverClass = resolveReceiverClassForMember(left)
|
val receiverClass = resolveReceiverClassForMember(left)
|
||||||
if (receiverClass == null) {
|
if (receiverClass == null) {
|
||||||
val receiverDecl = resolveReceiverTypeDecl(left)
|
|
||||||
if (receiverDecl != null && receiverDecl != TypeDecl.TypeAny && receiverDecl != TypeDecl.TypeNullableAny) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (isAllowedObjectMember(memberName)) return
|
if (isAllowedObjectMember(memberName)) return
|
||||||
throw ScriptError(pos, "member access requires compile-time receiver type: $memberName")
|
throw ScriptError(pos, "member access requires compile-time receiver type: $memberName")
|
||||||
}
|
}
|
||||||
@ -5620,8 +5362,7 @@ class Compiler(
|
|||||||
private suspend fun parseFunctionCall(
|
private suspend fun parseFunctionCall(
|
||||||
left: ObjRef,
|
left: ObjRef,
|
||||||
blockArgument: Boolean,
|
blockArgument: Boolean,
|
||||||
isOptional: Boolean,
|
isOptional: Boolean
|
||||||
explicitTypeArgs: List<TypeDecl>? = null
|
|
||||||
): ObjRef {
|
): ObjRef {
|
||||||
var detectedBlockArgument = blockArgument
|
var detectedBlockArgument = blockArgument
|
||||||
val expectedReceiver = tailBlockReceiverType(left)
|
val expectedReceiver = tailBlockReceiverType(left)
|
||||||
@ -5662,9 +5403,7 @@ class Compiler(
|
|||||||
val result = when (left) {
|
val result = when (left) {
|
||||||
is ImplicitThisMemberRef ->
|
is ImplicitThisMemberRef ->
|
||||||
if (left.methodId == null && left.fieldId != null) {
|
if (left.methodId == null && left.fieldId != null) {
|
||||||
CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
CallRef(left, args, detectedBlockArgument, isOptional)
|
||||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ImplicitThisMethodCallRef(
|
ImplicitThisMethodCallRef(
|
||||||
left.name,
|
left.name,
|
||||||
@ -5697,9 +5436,7 @@ class Compiler(
|
|||||||
checkFunctionTypeCallArity(left, args, left.pos())
|
checkFunctionTypeCallArity(left, args, left.pos())
|
||||||
checkFunctionTypeCallTypes(left, args, left.pos())
|
checkFunctionTypeCallTypes(left, args, left.pos())
|
||||||
checkGenericBoundsAtCall(left.name, args, left.pos())
|
checkGenericBoundsAtCall(left.name, args, left.pos())
|
||||||
CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
CallRef(left, args, detectedBlockArgument, isOptional)
|
||||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
@ -5723,30 +5460,14 @@ class Compiler(
|
|||||||
checkFunctionTypeCallArity(left, args, left.pos())
|
checkFunctionTypeCallArity(left, args, left.pos())
|
||||||
checkFunctionTypeCallTypes(left, args, left.pos())
|
checkFunctionTypeCallTypes(left, args, left.pos())
|
||||||
checkGenericBoundsAtCall(left.name, args, left.pos())
|
checkGenericBoundsAtCall(left.name, args, left.pos())
|
||||||
CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
CallRef(left, args, detectedBlockArgument, isOptional)
|
||||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> CallRef(left, args, detectedBlockArgument, isOptional).also { callRef ->
|
else -> CallRef(left, args, detectedBlockArgument, isOptional)
|
||||||
applyExplicitCallTypeArgs(callRef, explicitTypeArgs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyExplicitCallTypeArgs(callRef: CallRef, explicitTypeArgs: List<TypeDecl>?) {
|
|
||||||
if (explicitTypeArgs.isNullOrEmpty()) return
|
|
||||||
val baseName = when (val target = callRef.target) {
|
|
||||||
is LocalVarRef -> target.name
|
|
||||||
is FastLocalVarRef -> target.name
|
|
||||||
is LocalSlotRef -> target.name
|
|
||||||
is ConstRef -> (target.constValue as? ObjClass)?.className
|
|
||||||
else -> null
|
|
||||||
} ?: return
|
|
||||||
callReturnTypeDeclByRef[callRef] = TypeDecl.Generic(baseName, explicitTypeArgs, isNullable = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inferReceiverTypeFromArgs(args: List<ParsedArgument>): String? {
|
private fun inferReceiverTypeFromArgs(args: List<ParsedArgument>): String? {
|
||||||
val stmt = args.firstOrNull()?.value as? ExpressionStatement ?: return null
|
val stmt = args.firstOrNull()?.value as? ExpressionStatement ?: return null
|
||||||
val ref = stmt.ref
|
val ref = stmt.ref
|
||||||
@ -8304,11 +8025,8 @@ class Compiler(
|
|||||||
"Bool" -> ObjBool.type
|
"Bool" -> ObjBool.type
|
||||||
"Char" -> ObjChar.type
|
"Char" -> ObjChar.type
|
||||||
"List" -> ObjList.type
|
"List" -> ObjList.type
|
||||||
"ImmutableList" -> ObjImmutableList.type
|
|
||||||
"Map" -> ObjMap.type
|
"Map" -> ObjMap.type
|
||||||
"ImmutableMap" -> ObjImmutableMap.type
|
|
||||||
"Set" -> ObjSet.type
|
"Set" -> ObjSet.type
|
||||||
"ImmutableSet" -> ObjImmutableSet.type
|
|
||||||
"Range", "IntRange" -> ObjRange.type
|
"Range", "IntRange" -> ObjRange.type
|
||||||
"Iterator" -> ObjIterator
|
"Iterator" -> ObjIterator
|
||||||
"Iterable" -> ObjIterable
|
"Iterable" -> ObjIterable
|
||||||
@ -9188,7 +8906,6 @@ class Compiler(
|
|||||||
isMutable = isMutable,
|
isMutable = isMutable,
|
||||||
visibility = visibility,
|
visibility = visibility,
|
||||||
writeVisibility = setterVisibility,
|
writeVisibility = setterVisibility,
|
||||||
typeDecl = if (varTypeDecl == TypeDecl.TypeAny || varTypeDecl == TypeDecl.TypeNullableAny) null else varTypeDecl,
|
|
||||||
isAbstract = isAbstract,
|
isAbstract = isAbstract,
|
||||||
isClosed = isClosed,
|
isClosed = isClosed,
|
||||||
isOverride = isOverride,
|
isOverride = isOverride,
|
||||||
|
|||||||
@ -471,62 +471,6 @@ open class Scope(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolvedRecordValueOrNull(record: ObjRecord): Obj? {
|
|
||||||
return when (val raw = record.value) {
|
|
||||||
is FrameSlotRef -> raw.read()
|
|
||||||
is RecordSlotRef -> raw.read()
|
|
||||||
else -> raw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun declaredTypeForValueInThisScope(value: Obj): TypeDecl? {
|
|
||||||
// Prefer direct bindings first.
|
|
||||||
for (record in objects.values) {
|
|
||||||
val decl = record.typeDecl ?: continue
|
|
||||||
if (resolvedRecordValueOrNull(record) === value) return decl
|
|
||||||
}
|
|
||||||
for ((_, record) in localBindings) {
|
|
||||||
val decl = record.typeDecl ?: continue
|
|
||||||
if (resolvedRecordValueOrNull(record) === value) return decl
|
|
||||||
}
|
|
||||||
// Then slots (for frame-first locals).
|
|
||||||
var i = 0
|
|
||||||
while (i < slots.size) {
|
|
||||||
val record = slots[i]
|
|
||||||
val decl = record.typeDecl
|
|
||||||
if (decl != null && resolvedRecordValueOrNull(record) === value) return decl
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun declaredCollectionElementTypeForValue(value: Obj, rawName: String): TypeDecl? {
|
|
||||||
var s: Scope? = this
|
|
||||||
var hops = 0
|
|
||||||
while (s != null && hops++ < 1024) {
|
|
||||||
val decl = s.declaredTypeForValueInThisScope(value)
|
|
||||||
if (decl is TypeDecl.Generic && decl.name.substringAfterLast('.') == rawName) {
|
|
||||||
return decl.args.firstOrNull()
|
|
||||||
}
|
|
||||||
s = s.parent
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Best-effort lookup of the declared Set element type for a runtime set instance.
|
|
||||||
* Returns null when type info is unavailable.
|
|
||||||
*/
|
|
||||||
fun declaredSetElementTypeForValue(value: Obj): TypeDecl? =
|
|
||||||
declaredCollectionElementTypeForValue(value, "Set")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Best-effort lookup of the declared List element type for a runtime list instance.
|
|
||||||
* Returns null when type info is unavailable.
|
|
||||||
*/
|
|
||||||
fun declaredListElementTypeForValue(value: Obj): TypeDecl? =
|
|
||||||
declaredCollectionElementTypeForValue(value, "List")
|
|
||||||
|
|
||||||
internal fun applySlotPlanReset(plan: Map<String, Int>, records: Map<String, ObjRecord>) {
|
internal fun applySlotPlanReset(plan: Map<String, Int>, records: Map<String, ObjRecord>) {
|
||||||
if (plan.isEmpty()) return
|
if (plan.isEmpty()) return
|
||||||
slots.clear()
|
slots.clear()
|
||||||
|
|||||||
@ -522,12 +522,9 @@ class Script(
|
|||||||
addConst("Bool", ObjBool.type)
|
addConst("Bool", ObjBool.type)
|
||||||
addConst("Char", ObjChar.type)
|
addConst("Char", ObjChar.type)
|
||||||
addConst("List", ObjList.type)
|
addConst("List", ObjList.type)
|
||||||
addConst("ImmutableList", ObjImmutableList.type)
|
|
||||||
addConst("Set", ObjSet.type)
|
addConst("Set", ObjSet.type)
|
||||||
addConst("ImmutableSet", ObjImmutableSet.type)
|
|
||||||
addConst("Range", ObjRange.type)
|
addConst("Range", ObjRange.type)
|
||||||
addConst("Map", ObjMap.type)
|
addConst("Map", ObjMap.type)
|
||||||
addConst("ImmutableMap", ObjImmutableMap.type)
|
|
||||||
addConst("MapEntry", ObjMapEntry.type)
|
addConst("MapEntry", ObjMapEntry.type)
|
||||||
@Suppress("RemoveRedundantQualifierName")
|
@Suppress("RemoveRedundantQualifierName")
|
||||||
addConst("Callable", Statement.type)
|
addConst("Callable", Statement.type)
|
||||||
|
|||||||
@ -4439,11 +4439,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val initClass = when (localTarget?.name) {
|
val initClass = when (localTarget?.name) {
|
||||||
"List" -> ObjList.type
|
"List" -> ObjList.type
|
||||||
"ImmutableList" -> ObjImmutableList.type
|
|
||||||
"Map" -> ObjMap.type
|
"Map" -> ObjMap.type
|
||||||
"ImmutableMap" -> ObjImmutableMap.type
|
|
||||||
"Set" -> ObjSet.type
|
|
||||||
"ImmutableSet" -> ObjImmutableSet.type
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
val callee = compileRefWithFallback(ref.target, null, refPosOrCurrent(ref.target)) ?: return null
|
val callee = compileRefWithFallback(ref.target, null, refPosOrCurrent(ref.target)) ?: return null
|
||||||
@ -5178,7 +5174,6 @@ class BytecodeCompiler(
|
|||||||
isMutable = stmt.isMutable,
|
isMutable = stmt.isMutable,
|
||||||
visibility = stmt.visibility,
|
visibility = stmt.visibility,
|
||||||
writeVisibility = stmt.writeVisibility,
|
writeVisibility = stmt.writeVisibility,
|
||||||
typeDecl = stmt.typeDecl,
|
|
||||||
isTransient = stmt.isTransient,
|
isTransient = stmt.isTransient,
|
||||||
isAbstract = stmt.isAbstract,
|
isAbstract = stmt.isAbstract,
|
||||||
isClosed = stmt.isClosed,
|
isClosed = stmt.isClosed,
|
||||||
@ -7003,9 +6998,7 @@ class BytecodeCompiler(
|
|||||||
val slot = resolveSlot(ref)
|
val slot = resolveSlot(ref)
|
||||||
val fromSlot = slot?.let { slotObjClass[it] }
|
val fromSlot = slot?.let { slotObjClass[it] }
|
||||||
fromSlot
|
fromSlot
|
||||||
?: slot?.let { typeDeclForSlot(it) }?.let { resolveClassFromTypeDecl(it) }
|
|
||||||
?: slotTypeByScopeId[ownerScopeId]?.get(ownerSlot)
|
?: slotTypeByScopeId[ownerScopeId]?.get(ownerSlot)
|
||||||
?: slotTypeDeclByScopeId[ownerScopeId]?.get(ownerSlot)?.let { resolveClassFromTypeDecl(it) }
|
|
||||||
?: nameObjClass[ref.name]
|
?: nameObjClass[ref.name]
|
||||||
?: resolveTypeNameClass(ref.name)
|
?: resolveTypeNameClass(ref.name)
|
||||||
?: slotInitClassByKey[ScopeSlotKey(ownerScopeId, ownerSlot)]
|
?: slotInitClassByKey[ScopeSlotKey(ownerScopeId, ownerSlot)]
|
||||||
@ -7023,14 +7016,9 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
||||||
if (fromSlot != null) return fromSlot
|
if (fromSlot != null) return fromSlot
|
||||||
val fromDirectTypeDecl = resolveDirectNameSlot(ref.name)
|
|
||||||
?.let { typeDeclForSlot(it.slot) }
|
|
||||||
?.let { resolveClassFromTypeDecl(it) }
|
|
||||||
if (fromDirectTypeDecl != null) return fromDirectTypeDecl
|
|
||||||
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
||||||
key?.let {
|
key?.let {
|
||||||
slotTypeByScopeId[it.scopeId]?.get(it.slot)
|
slotTypeByScopeId[it.scopeId]?.get(it.slot)
|
||||||
?: slotTypeDeclByScopeId[it.scopeId]?.get(it.slot)?.let { decl -> resolveClassFromTypeDecl(decl) }
|
|
||||||
?: slotInitClassByKey[it]
|
?: slotInitClassByKey[it]
|
||||||
} ?: nameObjClass[ref.name]
|
} ?: nameObjClass[ref.name]
|
||||||
?: resolveTypeNameClass(ref.name)
|
?: resolveTypeNameClass(ref.name)
|
||||||
@ -7041,14 +7029,9 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
||||||
if (fromSlot != null) return fromSlot
|
if (fromSlot != null) return fromSlot
|
||||||
val fromDirectTypeDecl = resolveDirectNameSlot(ref.name)
|
|
||||||
?.let { typeDeclForSlot(it.slot) }
|
|
||||||
?.let { resolveClassFromTypeDecl(it) }
|
|
||||||
if (fromDirectTypeDecl != null) return fromDirectTypeDecl
|
|
||||||
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
||||||
key?.let {
|
key?.let {
|
||||||
slotTypeByScopeId[it.scopeId]?.get(it.slot)
|
slotTypeByScopeId[it.scopeId]?.get(it.slot)
|
||||||
?: slotTypeDeclByScopeId[it.scopeId]?.get(it.slot)?.let { decl -> resolveClassFromTypeDecl(decl) }
|
|
||||||
?: slotInitClassByKey[it]
|
?: slotInitClassByKey[it]
|
||||||
} ?: nameObjClass[ref.name]
|
} ?: nameObjClass[ref.name]
|
||||||
?: resolveTypeNameClass(ref.name)
|
?: resolveTypeNameClass(ref.name)
|
||||||
@ -7090,23 +7073,6 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveClassFromTypeDecl(typeDecl: TypeDecl): ObjClass? {
|
|
||||||
return when (typeDecl) {
|
|
||||||
is TypeDecl.Simple -> {
|
|
||||||
resolveTypeNameClass(typeDecl.name) ?: nameObjClass[typeDecl.name]?.let { cls ->
|
|
||||||
if (cls == ObjClassType) ObjDynamic.type else cls
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is TypeDecl.Generic -> {
|
|
||||||
resolveTypeNameClass(typeDecl.name) ?: nameObjClass[typeDecl.name]?.let { cls ->
|
|
||||||
if (cls == ObjClassType) ObjDynamic.type else cls
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is TypeDecl.Ellipsis -> resolveClassFromTypeDecl(typeDecl.elementType)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isKnownClassReceiver(ref: ObjRef): Boolean {
|
private fun isKnownClassReceiver(ref: ObjRef): Boolean {
|
||||||
return when (ref) {
|
return when (ref) {
|
||||||
is LocalVarRef -> {
|
is LocalVarRef -> {
|
||||||
@ -7308,11 +7274,8 @@ class BytecodeCompiler(
|
|||||||
"Bool" -> ObjBool.type
|
"Bool" -> ObjBool.type
|
||||||
"Char" -> ObjChar.type
|
"Char" -> ObjChar.type
|
||||||
"List" -> ObjList.type
|
"List" -> ObjList.type
|
||||||
"ImmutableList" -> ObjImmutableList.type
|
|
||||||
"Map" -> ObjMap.type
|
"Map" -> ObjMap.type
|
||||||
"ImmutableMap" -> ObjImmutableMap.type
|
|
||||||
"Set" -> ObjSet.type
|
"Set" -> ObjSet.type
|
||||||
"ImmutableSet" -> ObjImmutableSet.type
|
|
||||||
"Range", "IntRange" -> ObjRange.type
|
"Range", "IntRange" -> ObjRange.type
|
||||||
"Iterator" -> ObjIterator
|
"Iterator" -> ObjIterator
|
||||||
"Iterable" -> ObjIterable
|
"Iterable" -> ObjIterable
|
||||||
@ -7386,9 +7349,7 @@ class BytecodeCompiler(
|
|||||||
"iterator" -> ObjIterator
|
"iterator" -> ObjIterator
|
||||||
"count" -> ObjInt.type
|
"count" -> ObjInt.type
|
||||||
"toSet" -> ObjSet.type
|
"toSet" -> ObjSet.type
|
||||||
"toImmutableSet" -> ObjImmutableSet.type
|
|
||||||
"toMap" -> ObjMap.type
|
"toMap" -> ObjMap.type
|
||||||
"toImmutableMap" -> ObjImmutableMap.type
|
|
||||||
"joinToString" -> ObjString.type
|
"joinToString" -> ObjString.type
|
||||||
"now",
|
"now",
|
||||||
"truncateToSecond",
|
"truncateToSecond",
|
||||||
@ -7415,8 +7376,6 @@ class BytecodeCompiler(
|
|||||||
"matches" -> ObjBool.type
|
"matches" -> ObjBool.type
|
||||||
"toInt",
|
"toInt",
|
||||||
"toEpochSeconds" -> ObjInt.type
|
"toEpochSeconds" -> ObjInt.type
|
||||||
"toImmutableList" -> ObjImmutableList.type
|
|
||||||
"toImmutable" -> Obj.rootObjectType
|
|
||||||
"toMutable" -> ObjMutableBuffer.type
|
"toMutable" -> ObjMutableBuffer.type
|
||||||
"seq" -> ObjFlow.type
|
"seq" -> ObjFlow.type
|
||||||
"encode" -> ObjBitBuffer.type
|
"encode" -> ObjBitBuffer.type
|
||||||
|
|||||||
@ -100,7 +100,6 @@ sealed class BytecodeConst {
|
|||||||
val isMutable: Boolean,
|
val isMutable: Boolean,
|
||||||
val visibility: Visibility,
|
val visibility: Visibility,
|
||||||
val writeVisibility: Visibility?,
|
val writeVisibility: Visibility?,
|
||||||
val typeDecl: TypeDecl?,
|
|
||||||
val isTransient: Boolean,
|
val isTransient: Boolean,
|
||||||
val isAbstract: Boolean,
|
val isAbstract: Boolean,
|
||||||
val isClosed: Boolean,
|
val isClosed: Boolean,
|
||||||
|
|||||||
@ -348,7 +348,6 @@ class BytecodeStatement private constructor(
|
|||||||
stmt.isMutable,
|
stmt.isMutable,
|
||||||
stmt.visibility,
|
stmt.visibility,
|
||||||
stmt.writeVisibility,
|
stmt.writeVisibility,
|
||||||
stmt.typeDecl,
|
|
||||||
stmt.isAbstract,
|
stmt.isAbstract,
|
||||||
stmt.isClosed,
|
stmt.isClosed,
|
||||||
stmt.isOverride,
|
stmt.isOverride,
|
||||||
|
|||||||
@ -2750,7 +2750,6 @@ class CmdDeclClassInstanceField(internal val constId: Int, internal val slot: In
|
|||||||
isClosed = decl.isClosed,
|
isClosed = decl.isClosed,
|
||||||
isOverride = decl.isOverride,
|
isOverride = decl.isOverride,
|
||||||
isTransient = decl.isTransient,
|
isTransient = decl.isTransient,
|
||||||
typeDecl = decl.typeDecl,
|
|
||||||
type = ObjRecord.Type.Field,
|
type = ObjRecord.Type.Field,
|
||||||
fieldId = decl.fieldId
|
fieldId = decl.fieldId
|
||||||
)
|
)
|
||||||
|
|||||||
@ -37,16 +37,8 @@ object StdlibDocsBootstrap {
|
|||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
val _list = net.sergeych.lyng.obj.ObjList.type
|
val _list = net.sergeych.lyng.obj.ObjList.type
|
||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
val _immutableList = net.sergeych.lyng.obj.ObjImmutableList.type
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val _map = net.sergeych.lyng.obj.ObjMap.type
|
val _map = net.sergeych.lyng.obj.ObjMap.type
|
||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
val _immutableMap = net.sergeych.lyng.obj.ObjImmutableMap.type
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val _set = net.sergeych.lyng.obj.ObjSet.type
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val _immutableSet = net.sergeych.lyng.obj.ObjImmutableSet.type
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val _int = net.sergeych.lyng.obj.ObjInt.type
|
val _int = net.sergeych.lyng.obj.ObjInt.type
|
||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
val _real = net.sergeych.lyng.obj.ObjReal.type
|
val _real = net.sergeych.lyng.obj.ObjReal.type
|
||||||
|
|||||||
@ -826,7 +826,6 @@ open class ObjClass(
|
|||||||
type: ObjRecord.Type = ObjRecord.Type.Field,
|
type: ObjRecord.Type = ObjRecord.Type.Field,
|
||||||
fieldId: Int? = null,
|
fieldId: Int? = null,
|
||||||
methodId: Int? = null,
|
methodId: Int? = null,
|
||||||
typeDecl: net.sergeych.lyng.TypeDecl? = null,
|
|
||||||
): ObjRecord {
|
): ObjRecord {
|
||||||
// Validation of override rules: only for non-system declarations
|
// Validation of override rules: only for non-system declarations
|
||||||
var existing: ObjRecord? = null
|
var existing: ObjRecord? = null
|
||||||
@ -922,7 +921,6 @@ open class ObjClass(
|
|||||||
isOverride = isOverride,
|
isOverride = isOverride,
|
||||||
isTransient = isTransient,
|
isTransient = isTransient,
|
||||||
type = type,
|
type = type,
|
||||||
typeDecl = typeDecl,
|
|
||||||
memberName = name,
|
memberName = name,
|
||||||
fieldId = effectiveFieldId,
|
fieldId = effectiveFieldId,
|
||||||
methodId = effectiveMethodId
|
methodId = effectiveMethodId
|
||||||
|
|||||||
@ -1,197 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.sergeych.lyng.obj
|
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
|
||||||
import net.sergeych.lyng.miniast.type
|
|
||||||
import net.sergeych.lynon.LynonDecoder
|
|
||||||
import net.sergeych.lynon.LynonEncoder
|
|
||||||
import net.sergeych.lynon.LynonType
|
|
||||||
|
|
||||||
class ObjImmutableList(items: List<Obj> = emptyList()) : Obj() {
|
|
||||||
private val data: List<Obj> = items.toList()
|
|
||||||
|
|
||||||
override val objClass: ObjClass
|
|
||||||
get() = type
|
|
||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
return when (other) {
|
|
||||||
is ObjImmutableList -> data.size == other.data.size && data.indices.all { i -> data[i].equals(scope, other.data[i]) }
|
|
||||||
else -> {
|
|
||||||
if (other.isInstanceOf(ObjIterable)) compareTo(scope, other) == 0 else false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
|
||||||
if (other is ObjImmutableList) {
|
|
||||||
val mySize = data.size
|
|
||||||
val otherSize = other.data.size
|
|
||||||
val commonSize = minOf(mySize, otherSize)
|
|
||||||
for (i in 0..<commonSize) {
|
|
||||||
val d = data[i].compareTo(scope, other.data[i])
|
|
||||||
if (d != 0) return d
|
|
||||||
}
|
|
||||||
return mySize.compareTo(otherSize)
|
|
||||||
}
|
|
||||||
if (other.isInstanceOf(ObjIterable)) {
|
|
||||||
val it1 = data.iterator()
|
|
||||||
val it2 = other.invokeInstanceMethod(scope, "iterator")
|
|
||||||
val hasNext2 = it2.getInstanceMethod(scope, "hasNext")
|
|
||||||
val next2 = it2.getInstanceMethod(scope, "next")
|
|
||||||
while (it1.hasNext()) {
|
|
||||||
if (!hasNext2.invoke(scope, it2).toBool()) return 1
|
|
||||||
val d = it1.next().compareTo(scope, next2.invoke(scope, it2))
|
|
||||||
if (d != 0) return d
|
|
||||||
}
|
|
||||||
return if (hasNext2.invoke(scope, it2).toBool()) -1 else 0
|
|
||||||
}
|
|
||||||
return -2
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj {
|
|
||||||
return when (index) {
|
|
||||||
is ObjInt -> data[index.toInt()]
|
|
||||||
is ObjRange -> {
|
|
||||||
when {
|
|
||||||
index.start is ObjInt && index.end is ObjInt -> {
|
|
||||||
if (index.isEndInclusive)
|
|
||||||
ObjImmutableList(data.subList(index.start.toInt(), index.end.toInt() + 1))
|
|
||||||
else
|
|
||||||
ObjImmutableList(data.subList(index.start.toInt(), index.end.toInt()))
|
|
||||||
}
|
|
||||||
index.isOpenStart && !index.isOpenEnd -> {
|
|
||||||
if (index.isEndInclusive)
|
|
||||||
ObjImmutableList(data.subList(0, index.end!!.toInt() + 1))
|
|
||||||
else
|
|
||||||
ObjImmutableList(data.subList(0, index.end!!.toInt()))
|
|
||||||
}
|
|
||||||
index.isOpenEnd && !index.isOpenStart -> ObjImmutableList(data.subList(index.start!!.toInt(), data.size))
|
|
||||||
index.isOpenStart && index.isOpenEnd -> ObjImmutableList(data)
|
|
||||||
else -> throw RuntimeException("Can't apply range for index: $index")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> scope.raiseIllegalArgument("Illegal index object for immutable list: ${index.inspect(scope)}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun contains(scope: Scope, other: Obj): Boolean {
|
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS && other is ObjInt) {
|
|
||||||
var i = 0
|
|
||||||
val sz = data.size
|
|
||||||
while (i < sz) {
|
|
||||||
val v = data[i]
|
|
||||||
if (v is ObjInt && v.value == other.value) return true
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return data.contains(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
|
|
||||||
for (item in data) {
|
|
||||||
if (!callback(item)) break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun plus(scope: Scope, other: Obj): Obj {
|
|
||||||
return when {
|
|
||||||
other is ObjImmutableList -> ObjImmutableList(data + other.data)
|
|
||||||
other is ObjList -> ObjImmutableList(data + other.list)
|
|
||||||
other.isInstanceOf(ObjIterable) && other !is ObjString && other !is ObjBuffer -> {
|
|
||||||
val l = other.callMethod<ObjList>(scope, "toList")
|
|
||||||
ObjImmutableList(data + l.list)
|
|
||||||
}
|
|
||||||
else -> ObjImmutableList(data + other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun minus(scope: Scope, other: Obj): Obj {
|
|
||||||
if (other !is ObjString && other !is ObjBuffer && other.isInstanceOf(ObjIterable)) {
|
|
||||||
val toRemove = mutableSetOf<Obj>()
|
|
||||||
other.enumerate(scope) {
|
|
||||||
toRemove += it
|
|
||||||
true
|
|
||||||
}
|
|
||||||
return ObjImmutableList(data.filterNot { toRemove.contains(it) })
|
|
||||||
}
|
|
||||||
val out = data.toMutableList()
|
|
||||||
out.remove(other)
|
|
||||||
return ObjImmutableList(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
|
|
||||||
encoder.encodeAnyList(scope, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun lynonType(): LynonType = LynonType.List
|
|
||||||
|
|
||||||
override suspend fun defaultToString(scope: Scope): ObjString {
|
|
||||||
return ObjString(buildString {
|
|
||||||
append("ImmutableList(")
|
|
||||||
var first = true
|
|
||||||
for (v in data) {
|
|
||||||
if (first) first = false else append(",")
|
|
||||||
append(v.toString(scope).value)
|
|
||||||
}
|
|
||||||
append(")")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toMutableList(): MutableList<Obj> = data.toMutableList()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val type = object : ObjClass("ImmutableList", ObjArray) {
|
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
|
||||||
return ObjImmutableList(scope.args.list)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
|
||||||
return ObjImmutableList(decoder.decodeAnyList(scope))
|
|
||||||
}
|
|
||||||
}.apply {
|
|
||||||
addPropertyDoc(
|
|
||||||
name = "size",
|
|
||||||
doc = "Number of elements in this immutable list.",
|
|
||||||
type = type("lyng.Int"),
|
|
||||||
moduleName = "lyng.stdlib",
|
|
||||||
getter = { (this.thisObj as ObjImmutableList).data.size.toObj() }
|
|
||||||
)
|
|
||||||
addFnDoc(
|
|
||||||
name = "toMutable",
|
|
||||||
doc = "Create a mutable copy of this immutable list.",
|
|
||||||
returns = type("lyng.List"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjList(thisAs<ObjImmutableList>().toMutableList())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutable",
|
|
||||||
doc = "Return this immutable list.",
|
|
||||||
returns = type("lyng.ImmutableList"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
thisObj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,196 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.sergeych.lyng.obj
|
|
||||||
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import net.sergeych.lyng.Scope
|
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
|
||||||
import net.sergeych.lyng.miniast.TypeGenericDoc
|
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
|
||||||
import net.sergeych.lyng.miniast.addPropertyDoc
|
|
||||||
import net.sergeych.lyng.miniast.type
|
|
||||||
import net.sergeych.lynon.LynonDecoder
|
|
||||||
import net.sergeych.lynon.LynonEncoder
|
|
||||||
import net.sergeych.lynon.LynonType
|
|
||||||
|
|
||||||
class ObjImmutableMap(entries: Map<Obj, Obj> = emptyMap()) : Obj() {
|
|
||||||
val map: Map<Obj, Obj> = LinkedHashMap(entries)
|
|
||||||
|
|
||||||
override val objClass get() = type
|
|
||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
val otherMap = when (other) {
|
|
||||||
is ObjImmutableMap -> other.map
|
|
||||||
is ObjMap -> other.map
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
if (map.size != otherMap.size) return false
|
|
||||||
for ((k, v) in map) {
|
|
||||||
val ov = other.getAt(scope, k)
|
|
||||||
if (ov === ObjNull && !other.contains(scope, k)) return false
|
|
||||||
if (!v.equals(scope, ov)) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
|
||||||
val otherMap = when (other) {
|
|
||||||
is ObjImmutableMap -> other.map
|
|
||||||
is ObjMap -> other.map
|
|
||||||
else -> return -1
|
|
||||||
}
|
|
||||||
if (map == otherMap) return 0
|
|
||||||
if (map.size != otherMap.size) return map.size.compareTo(otherMap.size)
|
|
||||||
return map.toString().compareTo(otherMap.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getAt(scope: Scope, index: Obj): Obj = map[index] ?: ObjNull
|
|
||||||
|
|
||||||
override suspend fun contains(scope: Scope, other: Obj): Boolean = other in map
|
|
||||||
|
|
||||||
override suspend fun defaultToString(scope: Scope): ObjString {
|
|
||||||
val rendered = buildString {
|
|
||||||
append("ImmutableMap(")
|
|
||||||
var first = true
|
|
||||||
for ((k, v) in map) {
|
|
||||||
if (!first) append(",")
|
|
||||||
append(k.inspect(scope))
|
|
||||||
append(" => ")
|
|
||||||
append(v.toString(scope).value)
|
|
||||||
first = false
|
|
||||||
}
|
|
||||||
append(")")
|
|
||||||
}
|
|
||||||
return ObjString(rendered)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun lynonType(): LynonType = LynonType.Map
|
|
||||||
|
|
||||||
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
|
|
||||||
val keys = map.keys.map { it.toObj() }
|
|
||||||
val values = map.values.map { it.toObj() }
|
|
||||||
encoder.encodeAnyList(scope, keys)
|
|
||||||
encoder.encodeAnyList(scope, values, fixedSize = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun toJson(scope: Scope): JsonElement {
|
|
||||||
return JsonObject(map.map { it.key.toString(scope).value to it.value.toJson(scope) }.toMap())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun plus(scope: Scope, other: Obj): Obj {
|
|
||||||
val out = LinkedHashMap(map)
|
|
||||||
mergeIn(scope, out, other)
|
|
||||||
return ObjImmutableMap(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun mergeIn(scope: Scope, out: MutableMap<Obj, Obj>, other: Obj) {
|
|
||||||
when (other) {
|
|
||||||
is ObjImmutableMap -> out.putAll(other.map)
|
|
||||||
is ObjMap -> out.putAll(other.map)
|
|
||||||
is ObjMapEntry -> out[other.key] = other.value
|
|
||||||
is ObjList -> {
|
|
||||||
for (e in other.list) {
|
|
||||||
when (e) {
|
|
||||||
is ObjMapEntry -> out[e.key] = e.value
|
|
||||||
else -> {
|
|
||||||
if (e.isInstanceOf(ObjArray)) {
|
|
||||||
if (e.invokeInstanceMethod(scope, "size").toInt() != 2)
|
|
||||||
scope.raiseIllegalArgument("Array element to merge into map must have 2 elements, got $e")
|
|
||||||
out[e.getAt(scope, 0)] = e.getAt(scope, 1)
|
|
||||||
} else {
|
|
||||||
scope.raiseIllegalArgument("map can only be merged with MapEntry elements; got $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> scope.raiseIllegalArgument("map can only be merged with Map, ImmutableMap, MapEntry, or List<MapEntry>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toMutableMapCopy(): MutableMap<Obj, Obj> = LinkedHashMap(map)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val type = object : ObjClass("ImmutableMap", ObjCollection) {
|
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
|
||||||
return ObjImmutableMap(ObjMap.listToMap(scope, scope.args.list))
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
|
|
||||||
val keys = decoder.decodeAnyList(scope)
|
|
||||||
val values = decoder.decodeAnyList(scope, fixedSize = keys.size)
|
|
||||||
if (keys.size != values.size) scope.raiseIllegalArgument("map keys and values should be same size")
|
|
||||||
return ObjImmutableMap(keys.zip(values).toMap())
|
|
||||||
}
|
|
||||||
}.apply {
|
|
||||||
addFnDoc(
|
|
||||||
name = "getOrNull",
|
|
||||||
doc = "Get value by key or return null if the key is absent.",
|
|
||||||
params = listOf(ParamDoc("key")),
|
|
||||||
returns = type("lyng.Any", nullable = true),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
thisAs<ObjImmutableMap>().map[args.firstAndOnly(pos)] ?: ObjNull
|
|
||||||
}
|
|
||||||
addPropertyDoc(
|
|
||||||
name = "size",
|
|
||||||
doc = "Number of entries in the immutable map.",
|
|
||||||
type = type("lyng.Int"),
|
|
||||||
moduleName = "lyng.stdlib",
|
|
||||||
getter = { thisAs<ObjImmutableMap>().map.size.toObj() }
|
|
||||||
)
|
|
||||||
addPropertyDoc(
|
|
||||||
name = "keys",
|
|
||||||
doc = "List of keys in this immutable map.",
|
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
|
||||||
moduleName = "lyng.stdlib",
|
|
||||||
getter = { thisAs<ObjImmutableMap>().map.keys.toObj() }
|
|
||||||
)
|
|
||||||
addPropertyDoc(
|
|
||||||
name = "values",
|
|
||||||
doc = "List of values in this immutable map.",
|
|
||||||
type = TypeGenericDoc(type("lyng.List"), listOf(type("lyng.Any"))),
|
|
||||||
moduleName = "lyng.stdlib",
|
|
||||||
getter = { ObjList(thisAs<ObjImmutableMap>().map.values.toMutableList()) }
|
|
||||||
)
|
|
||||||
addFnDoc(
|
|
||||||
name = "iterator",
|
|
||||||
doc = "Iterator over map entries as MapEntry objects.",
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjKotlinIterator(thisAs<ObjImmutableMap>().map.entries.iterator())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "toMutable",
|
|
||||||
doc = "Create a mutable copy of this immutable map.",
|
|
||||||
returns = type("lyng.Map"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjMap(thisAs<ObjImmutableMap>().toMutableMapCopy())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutable",
|
|
||||||
doc = "Return this immutable map.",
|
|
||||||
returns = type("lyng.ImmutableMap"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) { thisObj }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.sergeych.lyng.obj
|
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
|
||||||
import net.sergeych.lyng.miniast.ParamDoc
|
|
||||||
import net.sergeych.lyng.miniast.addFnDoc
|
|
||||||
import net.sergeych.lyng.miniast.type
|
|
||||||
import net.sergeych.lynon.LynonDecoder
|
|
||||||
import net.sergeych.lynon.LynonEncoder
|
|
||||||
import net.sergeych.lynon.LynonType
|
|
||||||
|
|
||||||
class ObjImmutableSet(items: Collection<Obj> = emptyList()) : Obj() {
|
|
||||||
private val data: Set<Obj> = LinkedHashSet(items)
|
|
||||||
|
|
||||||
override val objClass get() = type
|
|
||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
val otherSet = when (other) {
|
|
||||||
is ObjImmutableSet -> other.data
|
|
||||||
is ObjSet -> other.set
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
if (data.size != otherSet.size) return false
|
|
||||||
for (e in data) {
|
|
||||||
if (!other.contains(scope, e)) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
|
||||||
val otherSet = when (other) {
|
|
||||||
is ObjImmutableSet -> other.data
|
|
||||||
is ObjSet -> other.set
|
|
||||||
else -> return -2
|
|
||||||
}
|
|
||||||
if (data == otherSet) return 0
|
|
||||||
if (data.size != otherSet.size) return data.size.compareTo(otherSet.size)
|
|
||||||
return data.toString().compareTo(otherSet.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun contains(scope: Scope, other: Obj): Boolean = data.contains(other)
|
|
||||||
|
|
||||||
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
|
|
||||||
for (item in data) {
|
|
||||||
if (!callback(item)) break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun plus(scope: Scope, other: Obj): Obj {
|
|
||||||
val merged = LinkedHashSet(data)
|
|
||||||
when {
|
|
||||||
other is ObjImmutableSet -> merged.addAll(other.data)
|
|
||||||
other is ObjSet -> merged.addAll(other.set)
|
|
||||||
other is ObjString || other is ObjBuffer || !other.isInstanceOf(ObjIterable) -> merged.add(other)
|
|
||||||
else -> other.enumerate(scope) { merged += it; true }
|
|
||||||
}
|
|
||||||
return ObjImmutableSet(merged)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun minus(scope: Scope, other: Obj): Obj {
|
|
||||||
val out = LinkedHashSet(data)
|
|
||||||
when {
|
|
||||||
other is ObjImmutableSet -> out.removeAll(other.data)
|
|
||||||
other is ObjSet -> out.removeAll(other.set)
|
|
||||||
other is ObjString || other is ObjBuffer || !other.isInstanceOf(ObjIterable) -> out.remove(other)
|
|
||||||
else -> other.enumerate(scope) { out.remove(it); true }
|
|
||||||
}
|
|
||||||
return ObjImmutableSet(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun mul(scope: Scope, other: Obj): Obj {
|
|
||||||
val right = when (other) {
|
|
||||||
is ObjImmutableSet -> other.data
|
|
||||||
is ObjSet -> other.set
|
|
||||||
else -> scope.raiseIllegalArgument("set operator * requires another set")
|
|
||||||
}
|
|
||||||
return ObjImmutableSet(data.intersect(right))
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun lynonType(): LynonType = LynonType.Set
|
|
||||||
|
|
||||||
override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
|
|
||||||
encoder.encodeAnyList(scope, data.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toMutableSet(): MutableSet<Obj> = LinkedHashSet(data)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val type: ObjClass = object : ObjClass("ImmutableSet", ObjCollection) {
|
|
||||||
override suspend fun callOn(scope: Scope): Obj {
|
|
||||||
return ObjImmutableSet(scope.args.list)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
|
|
||||||
ObjImmutableSet(decoder.decodeAnyList(scope))
|
|
||||||
}.apply {
|
|
||||||
addFnDoc(
|
|
||||||
name = "size",
|
|
||||||
doc = "Number of elements in this immutable set.",
|
|
||||||
returns = type("lyng.Int"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
thisAs<ObjImmutableSet>().data.size.toObj()
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "intersect",
|
|
||||||
doc = "Intersection with another set. Returns a new immutable set.",
|
|
||||||
params = listOf(ParamDoc("other")),
|
|
||||||
returns = type("lyng.ImmutableSet"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
thisAs<ObjImmutableSet>().mul(requireScope(), args.firstAndOnly())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "iterator",
|
|
||||||
doc = "Iterator over elements of this immutable set.",
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjKotlinObjIterator(thisAs<ObjImmutableSet>().data.iterator())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "union",
|
|
||||||
doc = "Union with another set or iterable. Returns a new immutable set.",
|
|
||||||
params = listOf(ParamDoc("other")),
|
|
||||||
returns = type("lyng.ImmutableSet"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
thisAs<ObjImmutableSet>().plus(requireScope(), args.firstAndOnly())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "subtract",
|
|
||||||
doc = "Subtract another set or iterable from this set. Returns a new immutable set.",
|
|
||||||
params = listOf(ParamDoc("other")),
|
|
||||||
returns = type("lyng.ImmutableSet"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
thisAs<ObjImmutableSet>().minus(requireScope(), args.firstAndOnly())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "toMutable",
|
|
||||||
doc = "Create a mutable copy of this immutable set.",
|
|
||||||
returns = type("lyng.Set"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjSet(thisAs<ObjImmutableSet>().toMutableSet())
|
|
||||||
}
|
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutable",
|
|
||||||
doc = "Return this immutable set.",
|
|
||||||
returns = type("lyng.ImmutableSet"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) { thisObj }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -50,21 +50,6 @@ val ObjIterable by lazy {
|
|||||||
ObjList(result)
|
ObjList(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutableList",
|
|
||||||
doc = "Collect elements of this iterable into a new immutable list.",
|
|
||||||
returns = type("lyng.ImmutableList"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
val scope = requireScope()
|
|
||||||
val result = mutableListOf<Obj>()
|
|
||||||
val it = thisObj.invokeInstanceMethod(scope, "iterator")
|
|
||||||
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
|
|
||||||
result.add(it.invokeInstanceMethod(scope, "next"))
|
|
||||||
}
|
|
||||||
ObjImmutableList(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// it is not effective, but it is open:
|
// it is not effective, but it is open:
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "contains",
|
name = "contains",
|
||||||
@ -124,28 +109,6 @@ val ObjIterable by lazy {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
addPropertyDoc(
|
|
||||||
name = "toImmutableSet",
|
|
||||||
doc = "Collect elements of this iterable into a new immutable set.",
|
|
||||||
type = type("lyng.ImmutableSet"),
|
|
||||||
moduleName = "lyng.stdlib",
|
|
||||||
getter = {
|
|
||||||
when (val self = this.thisObj) {
|
|
||||||
is ObjImmutableSet -> self
|
|
||||||
is ObjSet -> ObjImmutableSet(self.set)
|
|
||||||
else -> {
|
|
||||||
val result = mutableSetOf<Obj>()
|
|
||||||
val scope = requireScope()
|
|
||||||
val it = self.invokeInstanceMethod(scope, "iterator")
|
|
||||||
while (it.invokeInstanceMethod(scope, "hasNext").toBool()) {
|
|
||||||
result.add(it.invokeInstanceMethod(scope, "next"))
|
|
||||||
}
|
|
||||||
ObjImmutableSet(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
addPropertyDoc(
|
addPropertyDoc(
|
||||||
name = "toMap",
|
name = "toMap",
|
||||||
doc = "Collect pairs into a map using [0] as key and [1] as value for each element.",
|
doc = "Collect pairs into a map using [0] as key and [1] as value for each element.",
|
||||||
@ -165,25 +128,6 @@ val ObjIterable by lazy {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
addPropertyDoc(
|
|
||||||
name = "toImmutableMap",
|
|
||||||
doc = "Collect pairs into an immutable map using [0] as key and [1] as value for each element.",
|
|
||||||
type = type("lyng.ImmutableMap"),
|
|
||||||
moduleName = "lyng.stdlib",
|
|
||||||
getter = {
|
|
||||||
val result = linkedMapOf<Obj, Obj>()
|
|
||||||
val scope = requireScope()
|
|
||||||
this.thisObj.enumerate(scope) { pair ->
|
|
||||||
when (pair) {
|
|
||||||
is ObjMapEntry -> result[pair.key] = pair.value
|
|
||||||
else -> result[pair.getAt(scope, 0)] = pair.getAt(scope, 1)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
ObjImmutableMap(result)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
addFnDoc(
|
addFnDoc(
|
||||||
name = "associateBy",
|
name = "associateBy",
|
||||||
doc = "Build a map from elements using the lambda result as key.",
|
doc = "Build a map from elements using the lambda result as key.",
|
||||||
|
|||||||
@ -30,15 +30,6 @@ import net.sergeych.lynon.LynonEncoder
|
|||||||
import net.sergeych.lynon.LynonType
|
import net.sergeych.lynon.LynonType
|
||||||
|
|
||||||
class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
||||||
private fun shouldTreatAsSingleElement(scope: Scope, other: Obj): Boolean {
|
|
||||||
if (!other.isInstanceOf(ObjIterable)) return true
|
|
||||||
val declaredElementType = scope.declaredListElementTypeForValue(this)
|
|
||||||
if (declaredElementType != null && matchesTypeDecl(scope, other, declaredElementType)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (other is ObjString || other is ObjBuffer) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@ -136,7 +127,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
other is ObjList ->
|
other is ObjList ->
|
||||||
ObjList((list + other.list).toMutableList())
|
ObjList((list + other.list).toMutableList())
|
||||||
|
|
||||||
!shouldTreatAsSingleElement(scope, other) && other.isInstanceOf(ObjIterable) -> {
|
other.isInstanceOf(ObjIterable) && other !is ObjString && other !is ObjBuffer -> {
|
||||||
val l = other.callMethod<ObjList>(scope, "toList")
|
val l = other.callMethod<ObjList>(scope, "toList")
|
||||||
ObjList((list + l.list).toMutableList())
|
ObjList((list + l.list).toMutableList())
|
||||||
}
|
}
|
||||||
@ -152,7 +143,7 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
|
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
|
||||||
if (other is ObjList) {
|
if (other is ObjList) {
|
||||||
list.addAll(other.list)
|
list.addAll(other.list)
|
||||||
} else if (!shouldTreatAsSingleElement(scope, other) && other.isInstanceOf(ObjIterable)) {
|
} else if (other.isInstanceOf(ObjIterable) && other !is ObjString && other !is ObjBuffer) {
|
||||||
val otherList = (other.invokeInstanceMethod(scope, "toList") as ObjList).list
|
val otherList = (other.invokeInstanceMethod(scope, "toList") as ObjList).list
|
||||||
list.addAll(otherList)
|
list.addAll(otherList)
|
||||||
} else {
|
} else {
|
||||||
@ -161,43 +152,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun minus(scope: Scope, other: Obj): Obj {
|
|
||||||
val out = list.toMutableList()
|
|
||||||
if (shouldTreatAsSingleElement(scope, other)) {
|
|
||||||
out.remove(other)
|
|
||||||
return ObjList(out)
|
|
||||||
}
|
|
||||||
if (other.isInstanceOf(ObjIterable)) {
|
|
||||||
val toRemove = mutableSetOf<Obj>()
|
|
||||||
other.enumerate(scope) {
|
|
||||||
toRemove += it
|
|
||||||
true
|
|
||||||
}
|
|
||||||
out.removeAll { toRemove.contains(it) }
|
|
||||||
return ObjList(out)
|
|
||||||
}
|
|
||||||
out.remove(other)
|
|
||||||
return ObjList(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun minusAssign(scope: Scope, other: Obj): Obj {
|
|
||||||
if (shouldTreatAsSingleElement(scope, other)) {
|
|
||||||
list.remove(other)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
if (other.isInstanceOf(ObjIterable)) {
|
|
||||||
val toRemove = mutableSetOf<Obj>()
|
|
||||||
other.enumerate(scope) {
|
|
||||||
toRemove += it
|
|
||||||
true
|
|
||||||
}
|
|
||||||
list.removeAll { toRemove.contains(it) }
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
list.remove(other)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun contains(scope: Scope, other: Obj): Boolean {
|
override suspend fun contains(scope: Scope, other: Obj): Boolean {
|
||||||
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
if (net.sergeych.lyng.PerfFlags.PRIMITIVE_FASTOPS) {
|
||||||
// Fast path: int membership in a list of ints (common case in benches)
|
// Fast path: int membership in a list of ints (common case in benches)
|
||||||
@ -562,14 +516,6 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
ObjInt((-1).toLong())
|
ObjInt((-1).toLong())
|
||||||
}
|
}
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutable",
|
|
||||||
doc = "Create an immutable snapshot of this list.",
|
|
||||||
returns = type("lyng.ImmutableList"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjImmutableList(thisAs<ObjList>().list)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,12 +107,8 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
val otherSize = when (other) {
|
if (other !is ObjMap) return false
|
||||||
is ObjMap -> other.map.size
|
if (map.size != other.map.size) return false
|
||||||
is ObjImmutableMap -> other.map.size
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
if (map.size != otherSize) return false
|
|
||||||
for ((k, v) in map) {
|
for ((k, v) in map) {
|
||||||
val otherV = other.getAt(scope, k)
|
val otherV = other.getAt(scope, k)
|
||||||
if (otherV === ObjNull && !other.contains(scope, k)) return false
|
if (otherV === ObjNull && !other.contains(scope, k)) return false
|
||||||
@ -135,16 +131,14 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
||||||
val otherMap = when (other) {
|
if (other is ObjMap) {
|
||||||
is ObjMap -> other.map
|
if (map == other.map) return 0
|
||||||
is ObjImmutableMap -> other.map
|
if (map.size != other.map.size) return map.size.compareTo(other.map.size)
|
||||||
else -> return -1
|
// for same size, if they are not equal, we don't have a stable order
|
||||||
|
// but let's try to be consistent
|
||||||
|
return map.toString().compareTo(other.map.toString())
|
||||||
}
|
}
|
||||||
if (map == otherMap) return 0
|
return -1
|
||||||
if (map.size != otherMap.size) return map.size.compareTo(otherMap.size)
|
|
||||||
// for same size, if they are not equal, we don't have a stable order
|
|
||||||
// but let's try to be consistent
|
|
||||||
return map.toString().compareTo(otherMap.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun defaultToString(scope: Scope): ObjString {
|
override suspend fun defaultToString(scope: Scope): ObjString {
|
||||||
@ -317,14 +311,6 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
) {
|
) {
|
||||||
ObjKotlinIterator(thisAs<ObjMap>().map.entries.iterator())
|
ObjKotlinIterator(thisAs<ObjMap>().map.entries.iterator())
|
||||||
}
|
}
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutable",
|
|
||||||
doc = "Create an immutable snapshot of this map.",
|
|
||||||
returns = type("lyng.ImmutableMap"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjImmutableMap(thisAs<ObjMap>().map)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,11 +334,6 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
|
|||||||
map[k] = v
|
map[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ObjImmutableMap -> {
|
|
||||||
for ((k, v) in other.map) {
|
|
||||||
map[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ObjMapEntry -> {
|
is ObjMapEntry -> {
|
||||||
map[other.key] = other.value
|
map[other.key] = other.value
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,25 +27,11 @@ import net.sergeych.lynon.LynonEncoder
|
|||||||
import net.sergeych.lynon.LynonType
|
import net.sergeych.lynon.LynonType
|
||||||
|
|
||||||
class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
||||||
private fun shouldTreatAsSingleElement(scope: Scope, other: Obj): Boolean {
|
|
||||||
if (!other.isInstanceOf(ObjIterable)) return true
|
|
||||||
val declaredElementType = scope.declaredSetElementTypeForValue(this)
|
|
||||||
if (declaredElementType != null && matchesTypeDecl(scope, other, declaredElementType)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Strings and buffers are iterable but usually expected to be atomic values for set +/- operators.
|
|
||||||
if (other is ObjString || other is ObjBuffer) return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
override suspend fun equals(scope: Scope, other: Obj): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
val otherSet = when (other) {
|
if (other !is ObjSet) return false
|
||||||
is ObjSet -> other.set
|
if (set.size != other.set.size) return false
|
||||||
is ObjImmutableSet -> other.toMutableSet()
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
if (set.size != otherSet.size) return false
|
|
||||||
// Sets are equal if all my elements are in other and vice versa
|
// Sets are equal if all my elements are in other and vice versa
|
||||||
// contains() in ObjSet uses equals(scope, ...), so we need to be careful
|
// contains() in ObjSet uses equals(scope, ...), so we need to be careful
|
||||||
for (e in set) {
|
for (e in set) {
|
||||||
@ -67,9 +53,6 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun plus(scope: Scope, other: Obj): Obj {
|
override suspend fun plus(scope: Scope, other: Obj): Obj {
|
||||||
if (shouldTreatAsSingleElement(scope, other)) {
|
|
||||||
return ObjSet((set + other).toMutableSet())
|
|
||||||
}
|
|
||||||
return ObjSet(
|
return ObjSet(
|
||||||
if (other is ObjSet)
|
if (other is ObjSet)
|
||||||
(set + other.set).toMutableSet()
|
(set + other.set).toMutableSet()
|
||||||
@ -90,10 +73,6 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
|
override suspend fun plusAssign(scope: Scope, other: Obj): Obj {
|
||||||
if (shouldTreatAsSingleElement(scope, other)) {
|
|
||||||
set += other
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
when (other) {
|
when (other) {
|
||||||
is ObjSet -> {
|
is ObjSet -> {
|
||||||
set += other.set
|
set += other.set
|
||||||
@ -119,17 +98,13 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun mul(scope: Scope, other: Obj): Obj {
|
override suspend fun mul(scope: Scope, other: Obj): Obj {
|
||||||
return when (other) {
|
return if (other is ObjSet) {
|
||||||
is ObjSet -> ObjSet(set.intersect(other.set).toMutableSet())
|
ObjSet(set.intersect(other.set).toMutableSet())
|
||||||
is ObjImmutableSet -> ObjSet(set.intersect(other.toMutableSet()).toMutableSet())
|
} else
|
||||||
else -> scope.raiseIllegalArgument("set operator * requires another set")
|
scope.raiseIllegalArgument("set operator * requires another set")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun minus(scope: Scope, other: Obj): Obj {
|
override suspend fun minus(scope: Scope, other: Obj): Obj {
|
||||||
if (shouldTreatAsSingleElement(scope, other)) {
|
|
||||||
return ObjSet((set - other).toMutableSet())
|
|
||||||
}
|
|
||||||
return when {
|
return when {
|
||||||
other is ObjSet -> ObjSet(set.minus(other.set).toMutableSet())
|
other is ObjSet -> ObjSet(set.minus(other.set).toMutableSet())
|
||||||
other.isInstanceOf(ObjIterable) -> {
|
other.isInstanceOf(ObjIterable) -> {
|
||||||
@ -140,7 +115,8 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
ObjSet((set - otherSet).toMutableSet())
|
ObjSet((set - otherSet).toMutableSet())
|
||||||
}
|
}
|
||||||
else -> ObjSet((set - other).toMutableSet())
|
else ->
|
||||||
|
scope.raiseIllegalArgument("set operator - requires another set or Iterable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,14 +125,12 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
override suspend fun compareTo(scope: Scope, other: Obj): Int {
|
||||||
val otherSet = when (other) {
|
if (other is ObjSet) {
|
||||||
is ObjSet -> other.set
|
if (set == other.set) return 0
|
||||||
is ObjImmutableSet -> other.toMutableSet()
|
if (set.size != other.set.size) return set.size.compareTo(other.set.size)
|
||||||
else -> return -2
|
return set.toString().compareTo(other.set.toString())
|
||||||
}
|
}
|
||||||
if (set == otherSet) return 0
|
return -2
|
||||||
if (set.size != otherSet.size) return set.size.compareTo(otherSet.size)
|
|
||||||
return set.toString().compareTo(otherSet.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@ -240,14 +214,6 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
|
|||||||
for( x in args.list ) set -= x
|
for( x in args.list ) set -= x
|
||||||
if( n == set.size ) ObjFalse else ObjTrue
|
if( n == set.size ) ObjFalse else ObjTrue
|
||||||
}
|
}
|
||||||
addFnDoc(
|
|
||||||
name = "toImmutable",
|
|
||||||
doc = "Create an immutable snapshot of this set.",
|
|
||||||
returns = type("lyng.ImmutableSet"),
|
|
||||||
moduleName = "lyng.stdlib"
|
|
||||||
) {
|
|
||||||
ObjImmutableSet(thisAs<ObjSet>().set)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Sergey S. Chernov
|
|
||||||
*
|
|
||||||
* 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 kotlinx.coroutines.test.runTest
|
|
||||||
import net.sergeych.lyng.eval
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertFails
|
|
||||||
|
|
||||||
class ImmutableCollectionsTest {
|
|
||||||
@Test
|
|
||||||
fun immutableListSnapshotAndConversion() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
val src = [1,2,3]
|
|
||||||
val imm = src.toImmutable()
|
|
||||||
assert(imm is ImmutableList<Int>)
|
|
||||||
assert(imm is Array<Int>)
|
|
||||||
assert(imm is Collection<Int>)
|
|
||||||
assert(imm is Iterable<Int>)
|
|
||||||
|
|
||||||
src += 4
|
|
||||||
assertEquals(3, imm.size)
|
|
||||||
assertEquals([1,2,3], imm.toMutable())
|
|
||||||
assertEquals([1,2,3], (1..3).toImmutableList().toMutable())
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun immutableSetSnapshotAndConversion() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
val src = Set(1,2,3)
|
|
||||||
val imm = src.toImmutable()
|
|
||||||
assert(imm is ImmutableSet<Int>)
|
|
||||||
assert(imm is Collection<Int>)
|
|
||||||
assert(imm is Iterable<Int>)
|
|
||||||
src += 4
|
|
||||||
assertEquals(3, imm.size)
|
|
||||||
assertEquals(Set(1,2,3), imm.toMutable())
|
|
||||||
assertEquals(Set(1,2,3), [1,2,3].toImmutableSet.toMutable())
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun immutableMapSnapshotAndConversion() = runTest {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
val src = Map("a" => 1, "b" => 2)
|
|
||||||
val imm = src.toImmutable()
|
|
||||||
assert(imm is ImmutableMap<String,Int>)
|
|
||||||
assert(imm is Collection<MapEntry<String,Int>>)
|
|
||||||
assert(imm is Iterable<MapEntry<String,Int>>)
|
|
||||||
src["a"] = 100
|
|
||||||
assertEquals(1, imm["a"])
|
|
||||||
assertEquals(Map("a" => 1, "b" => 2), imm.toMutable())
|
|
||||||
|
|
||||||
val imm2 = ["x" => 10, "y" => 20].toImmutableMap
|
|
||||||
assertEquals(10, imm2["x"])
|
|
||||||
assertEquals(Map("x" => 10, "y" => 20), imm2.toMutable())
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun immutableCollectionsRejectMutationOps() = runTest {
|
|
||||||
assertFails {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
val xs = ImmutableList(1,2,3)
|
|
||||||
xs += 4
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
assertFails {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
val s = ImmutableSet(1,2,3)
|
|
||||||
s += 4
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
assertFails {
|
|
||||||
eval(
|
|
||||||
"""
|
|
||||||
val m = ImmutableMap("a" => 1)
|
|
||||||
m["a"] = 10
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -20,7 +20,6 @@ import net.sergeych.lyng.Script
|
|||||||
import net.sergeych.lyng.ScriptError
|
import net.sergeych.lyng.ScriptError
|
||||||
import net.sergeych.lyng.eval
|
import net.sergeych.lyng.eval
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -188,12 +187,10 @@ class TypesTest {
|
|||||||
eval(
|
eval(
|
||||||
"""
|
"""
|
||||||
class Ctx {
|
class Ctx {
|
||||||
val contract = null
|
|
||||||
fun println(msg: String) = msg
|
fun println(msg: String) = msg
|
||||||
}
|
}
|
||||||
fun use(callContext) {
|
fun use(callContext) {
|
||||||
assert(callContext is Ctx)
|
assert(callContext is Ctx)
|
||||||
assert(callContext.contract == null)
|
|
||||||
callContext.println("hello")
|
callContext.println("hello")
|
||||||
}
|
}
|
||||||
assertEquals("hello", use(Ctx()))
|
assertEquals("hello", use(Ctx()))
|
||||||
@ -400,114 +397,4 @@ class TypesTest {
|
|||||||
fun testOk5() { l4(1, "a", "b", "x") }
|
fun testOk5() { l4(1, "a", "b", "x") }
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSetTyped() = runTest {
|
|
||||||
eval("""
|
|
||||||
var s = Set<String>()
|
|
||||||
val typed: Set<String> = s
|
|
||||||
assertEquals(Set(), typed)
|
|
||||||
|
|
||||||
s += "foo"
|
|
||||||
assertEquals(Set("foo"), s)
|
|
||||||
s -= "foo"
|
|
||||||
assertEquals(Set(), s)
|
|
||||||
s += ["foo", "bar"]
|
|
||||||
assertEquals(Set("foo", "bar"), s)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testListTyped() = runTest {
|
|
||||||
eval("""
|
|
||||||
var l = List<String>()
|
|
||||||
val typed: List<String> = l
|
|
||||||
assertEquals(List(), typed)
|
|
||||||
|
|
||||||
l += "foo"
|
|
||||||
assertEquals(List("foo"), l)
|
|
||||||
l -= "foo"
|
|
||||||
assertEquals(List(), l)
|
|
||||||
|
|
||||||
l += ["foo", "bar"]
|
|
||||||
assertEquals(List("foo", "bar"), l)
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAliasesInGenerics1() = runTest {
|
|
||||||
val scope = Script.newScope()
|
|
||||||
scope.eval("""
|
|
||||||
type IntList<T: Int> = List<T>
|
|
||||||
type IntMap<K,V> = Map<K,V>
|
|
||||||
type IntSet<T: Int> = Set<T>
|
|
||||||
type IntPair<T: Int> = Pair<T,T>
|
|
||||||
type IntTriple<T: Int> = Triple<T,T,T>
|
|
||||||
type IntQuad<T: Int> = Quad<T,T,T,T>
|
|
||||||
|
|
||||||
import lyng.buffer
|
|
||||||
type Tag = String | Buffer
|
|
||||||
|
|
||||||
class X {
|
|
||||||
var tags: Set<Tag> = Set()
|
|
||||||
}
|
|
||||||
val x = X()
|
|
||||||
x.tags += "tag1"
|
|
||||||
assertEquals(Set("tag1"), x.tags)
|
|
||||||
x.tags += "tag2"
|
|
||||||
assertEquals(Set("tag1", "tag2"), x.tags)
|
|
||||||
x.tags += Buffer("tag3")
|
|
||||||
assertEquals(Set("tag1", "tag2", Buffer("tag3")), x.tags)
|
|
||||||
x.tags += Buffer("tag4")
|
|
||||||
assertEquals(Set("tag1", "tag2", Buffer("tag3"), Buffer("tag4")), x.tags)
|
|
||||||
""")
|
|
||||||
scope.eval("""
|
|
||||||
assert(x is X)
|
|
||||||
x.tags += "42"
|
|
||||||
assertEquals(Set("tag1", "tag2", Buffer("tag3"), Buffer("tag4"), "42"), x.tags)
|
|
||||||
|
|
||||||
""".trimIndent())
|
|
||||||
// now this must fail becaise element type does not match the declared:
|
|
||||||
assertFailsWith<ScriptError> {
|
|
||||||
scope.eval(
|
|
||||||
"""
|
|
||||||
x.tags += 42
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAliasesInGenericsList1() = runTest {
|
|
||||||
val scope = Script.newScope()
|
|
||||||
scope.eval("""
|
|
||||||
import lyng.buffer
|
|
||||||
type Tag = String | Buffer
|
|
||||||
|
|
||||||
class X {
|
|
||||||
var tags: List<Tag> = List()
|
|
||||||
}
|
|
||||||
val x = X()
|
|
||||||
x.tags += "tag1"
|
|
||||||
assertEquals(List("tag1"), x.tags)
|
|
||||||
x.tags += "tag2"
|
|
||||||
assertEquals(List("tag1", "tag2"), x.tags)
|
|
||||||
x.tags += Buffer("tag3")
|
|
||||||
assertEquals(List("tag1", "tag2", Buffer("tag3")), x.tags)
|
|
||||||
x.tags += ["tag4", Buffer("tag5")]
|
|
||||||
assertEquals(List("tag1", "tag2", Buffer("tag3"), "tag4", Buffer("tag5")), x.tags)
|
|
||||||
""")
|
|
||||||
scope.eval("""
|
|
||||||
assert(x is X)
|
|
||||||
x.tags += "42"
|
|
||||||
assertEquals(List("tag1", "tag2", Buffer("tag3"), "tag4", Buffer("tag5"), "42"), x.tags)
|
|
||||||
""".trimIndent())
|
|
||||||
assertFailsWith<ScriptError> {
|
|
||||||
scope.eval(
|
|
||||||
"""
|
|
||||||
x.tags += 42
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,6 @@ extern class Iterable<T> {
|
|||||||
fun forEach(action: (T)->Void): Void
|
fun forEach(action: (T)->Void): Void
|
||||||
fun map<R>(transform: (T)->R): List<R>
|
fun map<R>(transform: (T)->R): List<R>
|
||||||
fun toList(): List<T>
|
fun toList(): List<T>
|
||||||
fun toImmutableList(): ImmutableList<T>
|
|
||||||
val toSet: Set<T>
|
|
||||||
val toImmutableSet: ImmutableSet<T>
|
|
||||||
val toMap: Map<Object,Object>
|
|
||||||
val toImmutableMap: ImmutableMap<Object,Object>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class Iterator<T> {
|
extern class Iterator<T> {
|
||||||
@ -33,19 +28,13 @@ class KotlinIterator<T> : Iterator<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern class Collection<T> : Iterable<T> {
|
extern class Collection<T> : Iterable<T> {
|
||||||
val size: Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class Array<T> : Collection<T> {
|
extern class Array<T> : Collection<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class ImmutableList<T> : Array<T> {
|
|
||||||
fun toMutable(): List<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
extern class List<T> : Array<T> {
|
extern class List<T> : Array<T> {
|
||||||
fun add(value: T, more...): Void
|
fun add(value: T, more...): Void
|
||||||
fun toImmutable(): ImmutableList<T>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class RingBuffer<T> : Iterable<T> {
|
extern class RingBuffer<T> : Iterable<T> {
|
||||||
@ -55,26 +44,12 @@ extern class RingBuffer<T> : Iterable<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern class Set<T> : Collection<T> {
|
extern class Set<T> : Collection<T> {
|
||||||
fun toImmutable(): ImmutableSet<T>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class ImmutableSet<T> : Collection<T> {
|
extern class Map<K,V> {
|
||||||
fun toMutable(): Set<T>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern class Map<K,V> : Collection<MapEntry<K,V>> {
|
extern class MapEntry<K,V>
|
||||||
fun toImmutable(): ImmutableMap<K,V>
|
|
||||||
}
|
|
||||||
|
|
||||||
extern class ImmutableMap<K,V> : Collection<MapEntry<K,V>> {
|
|
||||||
fun getOrNull(key: K): V?
|
|
||||||
fun toMutable(): Map<K,V>
|
|
||||||
}
|
|
||||||
|
|
||||||
extern class MapEntry<K,V> : Array<Object> {
|
|
||||||
val key: K
|
|
||||||
val value: V
|
|
||||||
}
|
|
||||||
|
|
||||||
// Built-in math helpers (implemented in host runtime).
|
// Built-in math helpers (implemented in host runtime).
|
||||||
extern fun abs(x: Object): Real
|
extern fun abs(x: Object): Real
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user