fix #29 mapentries, map iterators, => operator and .toMap()

This commit is contained in:
sergeych committed 2025-06-16 02:14:53 +04:00
1 parent c002204420
commit f9416105ec
15 files changed
+213 -16

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych"
version = "0.6.4-SNAPSHOT"
version = "0.6.5-SNAPSHOT"
buildscript {
repositories {
@@ -1616,6 +1616,8 @@ class Compiler(
// bitwise or 2
// bitwise and 3
// 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.NEQ, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) != 0) },
Operator.simple(Token.Type.REF_EQ, lastPrty) { _, a, b -> ObjBool(a === b) },
@@ -253,7 +253,7 @@ open class Obj {
companion object {
inline fun <reified T> from(obj: T): Obj {
inline fun from(obj: Any?): Obj {
@Suppress("UNCHECKED_CAST")
return when (obj) {
is Obj -> obj
@@ -268,6 +268,10 @@ open class Obj {
Unit -> ObjVoid
null -> ObjNull
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")
}
}
@@ -450,6 +454,7 @@ class ObjIndexOutOfBoundsException(context: Context, message: String = "index ou
class ObjIllegalArgumentException(context: Context, message: String = "illegal argument") :
ObjException("IllegalArgumentException", context, message)
@Suppress("unused")
class ObjNoSuchElementException(context: Context, message: String = "no such element") :
ObjException("IllegalArgumentException", context, message)
@@ -80,6 +80,15 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
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 {
val Zero = ObjInt(0)
val One = ObjInt(1)
@@ -47,6 +47,15 @@ val ObjIterable by lazy {
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) {
val it = thisObj.invokeInstanceMethod(this, "iterator")
val fn = requiredArg<Statement>(0)
@@ -1,6 +1,12 @@
@file:Suppress("unused")
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
@@ -10,5 +16,24 @@ class ObjKotlinIterator(val iterator: Iterator<Any?>): Obj() {
addFn("hasNext") { thisAs<ObjKotlinIterator>().iterator.hasNext().toObj() }
}
}
}
/**
* 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()
}
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 {
val type = ObjClass("List", ObjArray).apply {
@@ -1,6 +1,40 @@
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
@@ -10,16 +44,22 @@ class ObjMap(val map: MutableMap<Obj,Obj>): Obj() {
override suspend fun contains(context: Context, other: Obj): Boolean {
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()
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>()
if (list.isEmpty()) return map
val first = list.first()
if (first.isInstanceOf(ObjArray)) {
if( first.invokeInstanceMethod(context,"size").toInt() != 2)
if (first.invokeInstanceMethod(context, "size").toInt() != 2)
context.raiseIllegalArgument(
"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 {
map[it.getAt(context,ObjInt.Zero)] = it.getAt(context,ObjInt.One)
map[it.getAt(context, ObjInt.Zero)] = it.getAt(context, ObjInt.One)
}
return map
}
val type = object: ObjClass("Map", ObjCollection) {
val type = object : ObjClass("Map", ObjCollection) {
override suspend fun callOn(context: Context): Obj {
return ObjMap(listToMap(context, context.args.list))
}
@@ -66,6 +106,9 @@ class ObjMap(val map: MutableMap<Obj,Obj>): Obj() {
addFn("values") {
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
}
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 {
val type: ObjClass = ObjClass("Real").apply {
createField(
@@ -65,6 +65,15 @@ data class ObjString(val value: String) : Obj() {
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 {
val type = ObjClass("String").apply {
addFn("startsWith") {
@@ -45,12 +45,18 @@ private class Parser(fromPos: Pos) {
'=' -> {
if (pos.currentChar == '=') {
pos.advance()
if (currentChar == '=') {
pos.advance()
Token("===", from, Token.Type.REF_EQ)
} else
Token("==", from, Token.Type.EQ)
} else
when (currentChar) {
'=' -> {
pos.advance()
Token("===", from, Token.Type.REF_EQ)
}
else -> Token("==", from, Token.Type.EQ)
}
} else if( currentChar == '>' ) {
pos.advance()
Token("=>", from, Token.Type.EQARROW)
}
else
Token("=", from, Token.Type.ASSIGN)
}
@@ -155,6 +155,7 @@ class Script(
addConst("Set", ObjSet.type)
addConst("Range", ObjRange.type)
addConst("Map", ObjMap.type)
addConst("MapEntry", ObjMapEntry.type)
@Suppress("RemoveRedundantQualifierName")
addConst("Callable", Statement.type)
// interfaces
@@ -14,7 +14,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
IN, NOTIN, IS, NOTIS,
EQ, NEQ, LT, LTE, GT, GTE, REF_EQ, REF_NEQ,
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,
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,