fix #29 mapentries, map iterators, => operator and .toMap()
This commit is contained in:
parent
c002204420
commit
f9416105ec
46
docs/Map.md
46
docs/Map.md
@ -53,11 +53,57 @@ To iterate maps it is convenient to use `keys` method that returns [Set] of keys
|
|||||||
>>> answer
|
>>> answer
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
|
Or iterate its key-value pairs that are instances of [MapEntry] class:
|
||||||
|
|
||||||
|
val map = Map( ["foo", 1], ["bar", "buzz"], [42, "answer"] )
|
||||||
|
for( entry in map ) {
|
||||||
|
println("map[%s] = %s"(entry.key, entry.value))
|
||||||
|
}
|
||||||
|
void
|
||||||
|
>>> map[foo] = 1
|
||||||
|
>>> map[bar] = buzz
|
||||||
|
>>> map[42] = answer
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
There is a shortcut to use `MapEntry` to create maps: operator `=>` which creates `MapEntry`:
|
||||||
|
|
||||||
|
val entry = "answer" => 42
|
||||||
|
assert( entry is MapEntry )
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
And you can use it to construct maps:
|
||||||
|
|
||||||
|
val map = Map( "foo" => 1, "bar" => 22)
|
||||||
|
assertEquals(1, map["foo"])
|
||||||
|
assertEquals(22, map["bar"])
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
Or use `.toMap` on anything that implements [Iterable] and which elements implements [Array] with 2 elements size, for example, `MapEntry`:
|
||||||
|
|
||||||
|
val map = ["foo" => 1, "bar" => 22].toMap()
|
||||||
|
assert( map is Map )
|
||||||
|
assertEquals(1, map["foo"])
|
||||||
|
assertEquals(22, map["bar"])
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
|
||||||
It is possible also to get values as [List] (values are not unique):
|
It is possible also to get values as [List] (values are not unique):
|
||||||
|
|
||||||
val map = Map( ["foo", 1], ["bar", "buzz"], [42, "answer"] )
|
val map = Map( ["foo", 1], ["bar", "buzz"], [42, "answer"] )
|
||||||
assertEquals(map.values, [1, "buzz", "answer"] )
|
assertEquals(map.values, [1, "buzz", "answer"] )
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
|
Map could be tested to be equal: when all it key-value pairs are equal, the map
|
||||||
|
is equal.
|
||||||
|
|
||||||
|
val m1 = Map(["foo", 1])
|
||||||
|
val m2 = Map(["foo", 1])
|
||||||
|
val m3 = Map(["foo", 2])
|
||||||
|
assert( m1 == m2 )
|
||||||
|
// but the references are different:
|
||||||
|
assert( m1 !== m2 )
|
||||||
|
// different maps:
|
||||||
|
assert( m1 != m3 )
|
||||||
|
>>> void
|
||||||
|
|
||||||
[Collection](Collection.md)
|
[Collection](Collection.md)
|
@ -14,7 +14,7 @@ __Other documents to read__ maybe after this one:
|
|||||||
- [Advanced topics](advanced_topics.md), [declaring arguments](declaring_arguments.md)
|
- [Advanced topics](advanced_topics.md), [declaring arguments](declaring_arguments.md)
|
||||||
- [OOP notes](OOP.md), [exception handling](exceptions_handling.md)
|
- [OOP notes](OOP.md), [exception handling](exceptions_handling.md)
|
||||||
- [math in Lyng](math.md)
|
- [math in Lyng](math.md)
|
||||||
- Some class references: [List], [Real], [Range], [Iterable], [Iterator]
|
- Some class references: [List], [Set], [Map], [Real], [Range], [Iterable], [Iterator]
|
||||||
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
|
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
|
||||||
|
|
||||||
# Expressions
|
# Expressions
|
||||||
@ -583,6 +583,28 @@ You can get ranges to extract a portion from a list:
|
|||||||
assertEquals( [2,3], list[1..<3])
|
assertEquals( [2,3], list[1..<3])
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
|
# Sets
|
||||||
|
|
||||||
|
Set are unordered collection of unique elements, see [Set]. Sets are [Iterable] but have no indexing access.
|
||||||
|
|
||||||
|
assertEquals( Set(3, 2, 1), Set( 1, 2, 3))
|
||||||
|
assert( 5 !in Set(1, 2, 6) )
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
Please see [Set] for detailed description.
|
||||||
|
|
||||||
|
# Maps
|
||||||
|
|
||||||
|
Maps are unordered collection of key-value pairs, where keys are unique. See [Map] for details. Map also
|
||||||
|
are [Iterable]:
|
||||||
|
|
||||||
|
val m = Map( "foo" => 77, "bar" => "buzz" )
|
||||||
|
assertEquals( m["foo"], 77 )
|
||||||
|
>>> void
|
||||||
|
|
||||||
|
Please see [Map] reference for detailed description on using Maps.
|
||||||
|
|
||||||
|
|
||||||
# Flow control operators
|
# Flow control operators
|
||||||
|
|
||||||
## if-then-else
|
## if-then-else
|
||||||
@ -1172,3 +1194,5 @@ See [math functions](math.md). Other general purpose functions are:
|
|||||||
[Range]: Range.md
|
[Range]: Range.md
|
||||||
[String]: String.md
|
[String]: String.md
|
||||||
[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
|
||||||
|
[Map]: Map.md
|
@ -4,7 +4,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 = "0.6.4-SNAPSHOT"
|
version = "0.6.5-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -1616,6 +1616,8 @@ class Compiler(
|
|||||||
// bitwise or 2
|
// bitwise or 2
|
||||||
// bitwise and 3
|
// bitwise and 3
|
||||||
// equality/ne 4
|
// equality/ne 4
|
||||||
|
Operator.simple(Token.Type.EQARROW, ++lastPrty) { c, a, b -> ObjMapEntry(a,b) },
|
||||||
|
//
|
||||||
Operator.simple(Token.Type.EQ, ++lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) == 0) },
|
Operator.simple(Token.Type.EQ, ++lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) == 0) },
|
||||||
Operator.simple(Token.Type.NEQ, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) != 0) },
|
Operator.simple(Token.Type.NEQ, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) != 0) },
|
||||||
Operator.simple(Token.Type.REF_EQ, lastPrty) { _, a, b -> ObjBool(a === b) },
|
Operator.simple(Token.Type.REF_EQ, lastPrty) { _, a, b -> ObjBool(a === b) },
|
||||||
|
@ -253,7 +253,7 @@ open class Obj {
|
|||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
inline fun <reified T> from(obj: T): Obj {
|
inline fun from(obj: Any?): Obj {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return when (obj) {
|
return when (obj) {
|
||||||
is Obj -> obj
|
is Obj -> obj
|
||||||
@ -268,6 +268,10 @@ open class Obj {
|
|||||||
Unit -> ObjVoid
|
Unit -> ObjVoid
|
||||||
null -> ObjNull
|
null -> ObjNull
|
||||||
is Iterator<*> -> ObjKotlinIterator(obj)
|
is Iterator<*> -> ObjKotlinIterator(obj)
|
||||||
|
is Map.Entry<*, *> -> {
|
||||||
|
obj as MutableMap.MutableEntry<Obj, Obj>
|
||||||
|
ObjMapEntry(obj.key, obj.value)
|
||||||
|
}
|
||||||
else -> throw IllegalArgumentException("cannot convert to Obj: $obj")
|
else -> throw IllegalArgumentException("cannot convert to Obj: $obj")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,6 +454,7 @@ class ObjIndexOutOfBoundsException(context: Context, message: String = "index ou
|
|||||||
class ObjIllegalArgumentException(context: Context, message: String = "illegal argument") :
|
class ObjIllegalArgumentException(context: Context, message: String = "illegal argument") :
|
||||||
ObjException("IllegalArgumentException", context, message)
|
ObjException("IllegalArgumentException", context, message)
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
class ObjNoSuchElementException(context: Context, message: String = "no such element") :
|
class ObjNoSuchElementException(context: Context, message: String = "no such element") :
|
||||||
ObjException("IllegalArgumentException", context, message)
|
ObjException("IllegalArgumentException", context, message)
|
||||||
|
|
||||||
|
@ -80,6 +80,15 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as ObjInt
|
||||||
|
|
||||||
|
return value == other.value
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val Zero = ObjInt(0)
|
val Zero = ObjInt(0)
|
||||||
val One = ObjInt(1)
|
val One = ObjInt(1)
|
||||||
|
@ -47,6 +47,15 @@ val ObjIterable by lazy {
|
|||||||
ObjSet(result)
|
ObjSet(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addFn("toMap") {
|
||||||
|
val result = mutableListOf<Obj>()
|
||||||
|
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||||
|
while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
|
||||||
|
result += it.invokeInstanceMethod(this, "next")
|
||||||
|
}
|
||||||
|
ObjMap(ObjMap.listToMap(this, result))
|
||||||
|
}
|
||||||
|
|
||||||
addFn("forEach", isOpen = true) {
|
addFn("forEach", isOpen = true) {
|
||||||
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
val it = thisObj.invokeInstanceMethod(this, "iterator")
|
||||||
val fn = requiredArg<Statement>(0)
|
val fn = requiredArg<Statement>(0)
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
class ObjKotlinIterator(val iterator: Iterator<Any?>): Obj() {
|
/**
|
||||||
|
* Iterator wrapper to allow Kotlin collections to be returned from Lyng objects;
|
||||||
|
* each object is converted to a Lyng object.
|
||||||
|
*/
|
||||||
|
class ObjKotlinIterator(val iterator: Iterator<Any?>) : Obj() {
|
||||||
|
|
||||||
override val objClass = type
|
override val objClass = type
|
||||||
|
|
||||||
@ -12,3 +18,22 @@ class ObjKotlinIterator(val iterator: Iterator<Any?>): Obj() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagate kotlin iterator that already produces Lyng objects, no conversion
|
||||||
|
* is applied
|
||||||
|
*/
|
||||||
|
class ObjKotlinObjIterator(val iterator: Iterator<Obj>) : Obj() {
|
||||||
|
|
||||||
|
override val objClass = type
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val type = ObjClass("KotlinIterator", ObjIterator).apply {
|
||||||
|
addFn("next") {
|
||||||
|
thisAs<ObjKotlinObjIterator>().iterator.next()
|
||||||
|
}
|
||||||
|
addFn("hasNext") { thisAs<ObjKotlinIterator>().iterator.hasNext().toObj() }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -119,6 +119,15 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
return list.hashCode()
|
return list.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as ObjList
|
||||||
|
|
||||||
|
return list == other.list
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("List", ObjArray).apply {
|
val type = ObjClass("List", ObjArray).apply {
|
||||||
|
|
||||||
|
@ -1,6 +1,40 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
class ObjMap(val map: MutableMap<Obj,Obj>): Obj() {
|
class ObjMapEntry(val key: Obj, val value: Obj) : Obj() {
|
||||||
|
|
||||||
|
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||||
|
if (other !is ObjMapEntry) return -1
|
||||||
|
val c = key.compareTo(context, other.key)
|
||||||
|
if (c != 0) return c
|
||||||
|
return value.compareTo(context, other.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getAt(context: Context, index: Obj): Obj = when (index.toInt()) {
|
||||||
|
0 -> key
|
||||||
|
1 -> value
|
||||||
|
else -> context.raiseIndexOutOfBounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$key=>$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
override val objClass = type
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val type = object : ObjClass("MapEntry", ObjArray) {
|
||||||
|
override suspend fun callOn(context: Context): Obj {
|
||||||
|
return ObjMapEntry(context.requiredArg<Obj>(0), context.requiredArg<Obj>(1))
|
||||||
|
}
|
||||||
|
}.apply {
|
||||||
|
addFn("key") { thisAs<ObjMapEntry>().key }
|
||||||
|
addFn("value") { thisAs<ObjMapEntry>().value }
|
||||||
|
addFn("size") { 2.toObj() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjMap(val map: MutableMap<Obj, Obj>) : Obj() {
|
||||||
|
|
||||||
override val objClass = type
|
override val objClass = type
|
||||||
|
|
||||||
@ -10,16 +44,22 @@ class ObjMap(val map: MutableMap<Obj,Obj>): Obj() {
|
|||||||
override suspend fun contains(context: Context, other: Obj): Boolean {
|
override suspend fun contains(context: Context, other: Obj): Boolean {
|
||||||
return other in map
|
return other in map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||||
|
if( other is ObjMap && other.map == map) return 0
|
||||||
|
return -1
|
||||||
|
}
|
||||||
override fun toString(): String = map.toString()
|
override fun toString(): String = map.toString()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
suspend fun listToMap(context: Context, list: List<Obj>): MutableMap<Obj,Obj> {
|
suspend fun listToMap(context: Context, list: List<Obj>): MutableMap<Obj, Obj> {
|
||||||
val map = mutableMapOf<Obj, Obj>()
|
val map = mutableMapOf<Obj, Obj>()
|
||||||
if (list.isEmpty()) return map
|
if (list.isEmpty()) return map
|
||||||
|
|
||||||
val first = list.first()
|
val first = list.first()
|
||||||
if (first.isInstanceOf(ObjArray)) {
|
if (first.isInstanceOf(ObjArray)) {
|
||||||
if( first.invokeInstanceMethod(context,"size").toInt() != 2)
|
if (first.invokeInstanceMethod(context, "size").toInt() != 2)
|
||||||
context.raiseIllegalArgument(
|
context.raiseIllegalArgument(
|
||||||
"list to construct map entry should exactly be 2 element Array like [key,value], got $list"
|
"list to construct map entry should exactly be 2 element Array like [key,value], got $list"
|
||||||
)
|
)
|
||||||
@ -28,13 +68,13 @@ class ObjMap(val map: MutableMap<Obj,Obj>): Obj() {
|
|||||||
|
|
||||||
|
|
||||||
list.forEach {
|
list.forEach {
|
||||||
map[it.getAt(context,ObjInt.Zero)] = it.getAt(context,ObjInt.One)
|
map[it.getAt(context, ObjInt.Zero)] = it.getAt(context, ObjInt.One)
|
||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val type = object: ObjClass("Map", ObjCollection) {
|
val type = object : ObjClass("Map", ObjCollection) {
|
||||||
override suspend fun callOn(context: Context): Obj {
|
override suspend fun callOn(context: Context): Obj {
|
||||||
return ObjMap(listToMap(context, context.args.list))
|
return ObjMap(listToMap(context, context.args.list))
|
||||||
}
|
}
|
||||||
@ -66,6 +106,9 @@ class ObjMap(val map: MutableMap<Obj,Obj>): Obj() {
|
|||||||
addFn("values") {
|
addFn("values") {
|
||||||
ObjList(thisAs<ObjMap>().map.values.toMutableList())
|
ObjList(thisAs<ObjMap>().map.values.toMutableList())
|
||||||
}
|
}
|
||||||
|
addFn("iterator") {
|
||||||
|
ObjKotlinIterator(thisAs<ObjMap>().map.entries.iterator())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,6 +47,15 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as ObjReal
|
||||||
|
|
||||||
|
return value == other.value
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type: ObjClass = ObjClass("Real").apply {
|
val type: ObjClass = ObjClass("Real").apply {
|
||||||
createField(
|
createField(
|
||||||
|
@ -65,6 +65,15 @@ data class ObjString(val value: String) : Obj() {
|
|||||||
else context.raiseIllegalArgument("String.contains can't take $other")
|
else context.raiseIllegalArgument("String.contains can't take $other")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as ObjString
|
||||||
|
|
||||||
|
return value == other.value
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val type = ObjClass("String").apply {
|
val type = ObjClass("String").apply {
|
||||||
addFn("startsWith") {
|
addFn("startsWith") {
|
||||||
|
@ -45,12 +45,18 @@ private class Parser(fromPos: Pos) {
|
|||||||
'=' -> {
|
'=' -> {
|
||||||
if (pos.currentChar == '=') {
|
if (pos.currentChar == '=') {
|
||||||
pos.advance()
|
pos.advance()
|
||||||
if (currentChar == '=') {
|
when (currentChar) {
|
||||||
|
'=' -> {
|
||||||
pos.advance()
|
pos.advance()
|
||||||
Token("===", from, Token.Type.REF_EQ)
|
Token("===", from, Token.Type.REF_EQ)
|
||||||
} else
|
}
|
||||||
Token("==", from, Token.Type.EQ)
|
else -> Token("==", from, Token.Type.EQ)
|
||||||
} else
|
}
|
||||||
|
} else if( currentChar == '>' ) {
|
||||||
|
pos.advance()
|
||||||
|
Token("=>", from, Token.Type.EQARROW)
|
||||||
|
}
|
||||||
|
else
|
||||||
Token("=", from, Token.Type.ASSIGN)
|
Token("=", from, Token.Type.ASSIGN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ class Script(
|
|||||||
addConst("Set", ObjSet.type)
|
addConst("Set", ObjSet.type)
|
||||||
addConst("Range", ObjRange.type)
|
addConst("Range", ObjRange.type)
|
||||||
addConst("Map", ObjMap.type)
|
addConst("Map", ObjMap.type)
|
||||||
|
addConst("MapEntry", ObjMapEntry.type)
|
||||||
@Suppress("RemoveRedundantQualifierName")
|
@Suppress("RemoveRedundantQualifierName")
|
||||||
addConst("Callable", Statement.type)
|
addConst("Callable", Statement.type)
|
||||||
// interfaces
|
// interfaces
|
||||||
|
@ -14,7 +14,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
|
|||||||
IN, NOTIN, IS, NOTIS,
|
IN, NOTIN, IS, NOTIS,
|
||||||
EQ, NEQ, LT, LTE, GT, GTE, REF_EQ, REF_NEQ,
|
EQ, NEQ, LT, LTE, GT, GTE, REF_EQ, REF_NEQ,
|
||||||
SHUTTLE,
|
SHUTTLE,
|
||||||
AND, BITAND, OR, BITOR, NOT, BITNOT, DOT, ARROW, QUESTION, COLONCOLON,
|
AND, BITAND, OR, BITOR, NOT, BITNOT, DOT, ARROW, EQARROW, QUESTION, COLONCOLON,
|
||||||
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
|
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
|
||||||
LABEL, ATLABEL, // label@ at@label
|
LABEL, ATLABEL, // label@ at@label
|
||||||
//PUBLIC, PROTECTED, INTERNAL, EXPORT, OPEN, INLINE, OVERRIDE, ABSTRACT, SEALED, EXTERNAL, VAL, VAR, CONST, TYPE, FUN, CLASS, INTERFACE, ENUM, OBJECT, TRAIT, THIS,
|
//PUBLIC, PROTECTED, INTERNAL, EXPORT, OPEN, INLINE, OVERRIDE, ABSTRACT, SEALED, EXTERNAL, VAL, VAR, CONST, TYPE, FUN, CLASS, INTERFACE, ENUM, OBJECT, TRAIT, THIS,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user