lists modification: insert, remove, offset-end indexing, comparison.
This commit is contained in:
1 parent
c18345823b
commit
b56b5c521d
13 files changed
+285
-72
No files matched your search
@@ -13,12 +13,6 @@ data class Arguments(val list: List<Info>): Iterable<Obj> {
|
||||
return list.first().value
|
||||
}
|
||||
|
||||
inline fun <reified T: Obj>required(index: Int, context: Context): T {
|
||||
if( list.size <= index ) context.raiseError("Expected at least ${index+1} argument, got ${list.size}")
|
||||
return (list[index].value as? T)
|
||||
?: context.raiseError("Expected type ${T::class.simpleName}, got ${list[index].value::class.simpleName}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
val EMPTY = Arguments(emptyList())
|
||||
}
|
||||
@@ -27,5 +21,3 @@ data class Arguments(val list: List<Info>): Iterable<Obj> {
|
||||
return list.map { it.value }.iterator()
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Arguments.Info>.toArguments() = Arguments(this )
|
||||
@@ -148,7 +148,10 @@ class Compiler(
|
||||
v.callInstanceMethod(
|
||||
context,
|
||||
next.value,
|
||||
args.toArguments()
|
||||
Arguments(args.map {
|
||||
val st = it.value as Statement
|
||||
Arguments.Info(st.execute(context),it.pos) }
|
||||
)
|
||||
), isMutable = false
|
||||
)
|
||||
}
|
||||
@@ -838,6 +841,8 @@ class Compiler(
|
||||
// equality/ne 4
|
||||
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) },
|
||||
Operator.simple(Token.Type.REF_NEQ, lastPrty) { _, a, b -> ObjBool(a !== b) },
|
||||
// relational <=,... 5
|
||||
Operator.simple(Token.Type.LTE, ++lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) <= 0) },
|
||||
Operator.simple(Token.Type.LT, lastPrty) { c, a, b -> ObjBool(a.compareTo(c, b) < 0) },
|
||||
|
||||
@@ -17,6 +17,8 @@ class Context(
|
||||
@Suppress("unused")
|
||||
fun raiseNPE(): Nothing = raiseError(ObjNullPointerError(this))
|
||||
|
||||
fun raiseClassCastError(msg: String): Nothing = raiseError(ObjClassCastError(this, msg))
|
||||
|
||||
fun raiseError(message: String): Nothing {
|
||||
throw ExecutionError(ObjError(this, message))
|
||||
}
|
||||
@@ -25,6 +27,15 @@ class Context(
|
||||
throw ExecutionError(obj)
|
||||
}
|
||||
|
||||
inline fun <reified T: Obj>requiredArg(index: Int): T {
|
||||
if( args.list.size <= index ) raiseError("Expected at least ${index+1} argument, got ${args.list.size}")
|
||||
return (args.list[index].value as? T)
|
||||
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index].value::class.simpleName}")
|
||||
}
|
||||
|
||||
inline fun <reified T: Obj>thisAs(): T = (thisObj as? T)
|
||||
?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
|
||||
|
||||
private val objects = mutableMapOf<String, StoredObj>()
|
||||
|
||||
operator fun get(name: String): StoredObj? =
|
||||
|
||||
@@ -31,6 +31,8 @@ sealed class Obj {
|
||||
// private val memberMutex = Mutex()
|
||||
private val parentInstances = listOf<Obj>()
|
||||
|
||||
open fun inspect(): String = toString()
|
||||
|
||||
/**
|
||||
* Some objects are by-value, historically [ObjInt] and [ObjReal] are usually treated as such.
|
||||
* When initializing a var with it, by value objects must be copied. By-reference ones aren't.
|
||||
@@ -255,7 +257,13 @@ data class ObjString(val value: String) : Obj() {
|
||||
return this.value.compareTo(other.value)
|
||||
}
|
||||
|
||||
override fun toString(): String = "\"$value\""
|
||||
override fun toString(): String = value
|
||||
|
||||
override val asStr: ObjString by lazy { this }
|
||||
|
||||
override fun inspect(): String {
|
||||
return "\"$value\""
|
||||
}
|
||||
|
||||
override val objClass: ObjClass
|
||||
get() = type
|
||||
@@ -445,33 +453,6 @@ data class ObjBool(val value: Boolean) : Obj() {
|
||||
// }
|
||||
//}
|
||||
|
||||
class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||
|
||||
override fun toString(): String = "[${list.joinToString(separator = ", ")}]"
|
||||
|
||||
override suspend fun getAt(context: Context, index: Int): Obj {
|
||||
return list[index]
|
||||
}
|
||||
|
||||
override suspend fun putAt(context: Context, index: Int, newValue: Obj) {
|
||||
list[index] = newValue
|
||||
}
|
||||
|
||||
override val objClass: ObjClass
|
||||
get() = type
|
||||
|
||||
companion object {
|
||||
val type = ObjClass("List").apply {
|
||||
createField("size",
|
||||
statement(Pos.builtIn) {
|
||||
(it.thisObj as ObjList).list.size.toObj()
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ObjNamespace(val name: String) : Obj() {
|
||||
override fun toString(): String {
|
||||
return "namespace ${name}"
|
||||
@@ -485,3 +466,4 @@ open class ObjError(val context: Context, val message: String) : Obj() {
|
||||
class ObjNullPointerError(context: Context) : ObjError(context, "object is null")
|
||||
|
||||
class ObjAssertionError(context: Context, message: String) : ObjError(context, message)
|
||||
class ObjClassCastError(context: Context, message: String) : ObjError(context, message)
|
||||
@@ -0,0 +1,93 @@
|
||||
package net.sergeych.ling
|
||||
|
||||
class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||
|
||||
override fun toString(): String = "[${
|
||||
list.joinToString(separator = ", ") { it.inspect() }
|
||||
}]"
|
||||
|
||||
fun normalize(context: Context, index: Int): Int {
|
||||
val i = if (index < 0) list.size + index else index
|
||||
if (i !in list.indices) context.raiseError("index $index out of bounds for size ${list.size}")
|
||||
return i
|
||||
}
|
||||
|
||||
override suspend fun getAt(context: Context, index: Int): Obj {
|
||||
val i = normalize(context, index)
|
||||
return list[i]
|
||||
}
|
||||
|
||||
override suspend fun putAt(context: Context, index: Int, newValue: Obj) {
|
||||
val i = normalize(context, index)
|
||||
list[i] = newValue
|
||||
}
|
||||
|
||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||
if (other !is ObjList) context.raiseError("cannot compare $this with $other")
|
||||
val mySize = list.size
|
||||
val otherSize = other.list.size
|
||||
val commonSize = minOf(mySize, otherSize)
|
||||
for (i in 0..<commonSize) {
|
||||
if (list[i].compareTo(context, other.list[i]) != 0) {
|
||||
return list[i].compareTo(context, other.list[i])
|
||||
}
|
||||
}
|
||||
// equal so far, longer is greater:
|
||||
return when {
|
||||
mySize < otherSize -> -1
|
||||
mySize > otherSize -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun plus(context: Context, other: Obj): Obj {
|
||||
(other as? ObjList) ?: context.raiseError("cannot concatenate $this with $other")
|
||||
return ObjList((list + other.list).toMutableList())
|
||||
}
|
||||
|
||||
override suspend fun plusAssign(context: Context, other: Obj): Obj {
|
||||
(other as? ObjList) ?: context.raiseError("cannot concatenate $this with $other")
|
||||
list += other.list
|
||||
return this
|
||||
}
|
||||
|
||||
override val objClass: ObjClass
|
||||
get() = type
|
||||
|
||||
companion object {
|
||||
val type = ObjClass("List").apply {
|
||||
createField("size",
|
||||
statement {
|
||||
(thisObj as ObjList).list.size.toObj()
|
||||
}
|
||||
)
|
||||
createField("add",
|
||||
statement {
|
||||
val l = thisAs<ObjList>().list
|
||||
for (a in args) l.add(a)
|
||||
ObjVoid
|
||||
}
|
||||
)
|
||||
createField("addAt",
|
||||
statement {
|
||||
if (args.size < 2) raiseError("addAt takes 2+ arguments")
|
||||
val l = thisAs<ObjList>()
|
||||
var index = l.normalize(this, requiredArg<ObjInt>(0).value.toInt())
|
||||
for (i in 1..<args.size) l.list.add(index++, args[i])
|
||||
ObjVoid
|
||||
}
|
||||
)
|
||||
createField("removeAt",
|
||||
statement {
|
||||
val self = thisAs<ObjList>()
|
||||
val start = self.normalize(this, requiredArg<ObjInt>(0).value.toInt())
|
||||
if (args.size == 2) {
|
||||
val end = requiredArg<ObjInt>(1).value.toInt()
|
||||
self.list.subList(start, self.normalize(this, end)).clear()
|
||||
} else
|
||||
self.list.removeAt(start)
|
||||
self
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,12 @@ private class Parser(fromPos: Pos) {
|
||||
'=' -> {
|
||||
if (pos.currentChar == '=') {
|
||||
pos.advance()
|
||||
Token("==", from, Token.Type.EQ)
|
||||
if( currentChar == '=' ) {
|
||||
pos.advance()
|
||||
Token("===", from, Token.Type.REF_EQ)
|
||||
}
|
||||
else
|
||||
Token("==", from, Token.Type.EQ)
|
||||
} else
|
||||
Token("=", from, Token.Type.ASSIGN)
|
||||
}
|
||||
@@ -150,7 +155,12 @@ private class Parser(fromPos: Pos) {
|
||||
'!' -> {
|
||||
if (currentChar == '=') {
|
||||
pos.advance()
|
||||
Token("!=", from, Token.Type.NEQ)
|
||||
if( currentChar == '=' ) {
|
||||
pos.advance()
|
||||
Token("!==", from, Token.Type.REF_NEQ)
|
||||
}
|
||||
else
|
||||
Token("!=", from, Token.Type.NEQ)
|
||||
} else
|
||||
Token("!", from, Token.Type.NOT)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class Script(
|
||||
}
|
||||
|
||||
addVoidFn("assert") {
|
||||
val cond = args.required<ObjBool>(0, this)
|
||||
val cond = requiredArg<ObjBool>(0)
|
||||
if( !cond.value == true )
|
||||
raiseError(ObjAssertionError(this,"Assertion failed"))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
|
||||
PLUS, MINUS, STAR, SLASH, PERCENT,
|
||||
ASSIGN, PLUSASSIGN, MINUSASSIGN, STARASSIGN, SLASHASSIGN, PERCENTASSIGN,
|
||||
PLUS2, MINUS2,
|
||||
EQ, NEQ, LT, LTE, GT, GTE,
|
||||
EQ, NEQ, LT, LTE, GT, GTE, REF_EQ, REF_NEQ,
|
||||
AND, BITAND, OR, BITOR, NOT, BITNOT, DOT, ARROW, QUESTION, COLONCOLON,
|
||||
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
|
||||
LABEL, ATLABEL, // label@ at@label
|
||||
|
||||
@@ -45,4 +45,10 @@ fun statement(pos: Pos, isStaticConst: Boolean = false, isConst: Boolean = false
|
||||
override suspend fun execute(context: Context): Obj = f(context)
|
||||
}
|
||||
|
||||
fun statement(isStaticConst: Boolean = false, isConst: Boolean = false, f: suspend Context.() -> Obj): Statement =
|
||||
object : Statement(isStaticConst, isConst) {
|
||||
override val pos: Pos = Pos.builtIn
|
||||
override suspend fun execute(context: Context): Obj = f(context)
|
||||
}
|
||||
|
||||
|
||||
@@ -795,6 +795,18 @@ class ScriptTest {
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArrayCompare() = runTest {
|
||||
eval("""
|
||||
val a = [4,3]
|
||||
val b = [4,3]
|
||||
assert(a == b)
|
||||
assert( a === a )
|
||||
assert( !(a === b) )
|
||||
assert( a !== b )
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun testLambda1() = runTest {
|
||||
// val l = eval("""
|
||||
|
||||
@@ -151,7 +151,7 @@ suspend fun DocTest.test() {
|
||||
} catch (e: Throwable) {
|
||||
error = e
|
||||
null
|
||||
}?.toString()?.replace(Regex("@\\d+"), "@...")
|
||||
}?.inspect()?.replace(Regex("@\\d+"), "@...")
|
||||
|
||||
if (error != null || expectedOutput != collectedOutput.toString() ||
|
||||
expectedResult != result
|
||||
|
||||
Reference in new issue
Block a user