+better comparison
+splat arguments in calls
This commit is contained in:
parent
b56b5c521d
commit
1479df4426
@ -39,6 +39,7 @@ __Important__ negative indexes works wherever indexes are used, e.g. in insertio
|
||||
assert( [1, 2, 3] > [1, 2])
|
||||
assert( [1, 3] > [1, 2, 3])
|
||||
assert( [1, 2, 3] == [1, 2, 3])
|
||||
assert( [1, 2, 3] != [1, 2, "three"])
|
||||
// note that in the case above objects are referentially different:
|
||||
assert( [1, 2, 3] !== [1, 2, 3])
|
||||
>>> void
|
||||
|
@ -342,6 +342,27 @@ You can insert elements at any position using `addAt`:
|
||||
assert( x == [1, "foo", "bar", 2, 3])
|
||||
>>> void
|
||||
|
||||
Using splat arguments can simplify inserting list in list:
|
||||
|
||||
val x = [1, 2, 3]
|
||||
x.addAt( 1, ...[0,100,0])
|
||||
x
|
||||
>>> [1, 0, 100, 0, 2, 3]
|
||||
|
||||
Using negative indexes can insert elements as offset from the end, for example:
|
||||
|
||||
val x = [1,2,3]
|
||||
x.addAt(-1, 10)
|
||||
x
|
||||
>>> [1, 2, 10, 3]
|
||||
|
||||
Note that to add to the end you still need to use `add` or positive index of the after-last element:
|
||||
|
||||
val x = [1,2,3]
|
||||
x.addAt(3, 10)
|
||||
x
|
||||
>>> [1, 2, 3, 10]
|
||||
|
||||
## Removing list items
|
||||
|
||||
val x = [1, 2, 3, 4, 5]
|
||||
@ -352,6 +373,21 @@ You can insert elements at any position using `addAt`:
|
||||
assert( x == [1, 5])
|
||||
>>> void
|
||||
|
||||
Again, you can use negative indexes. For example, removing last elements like:
|
||||
|
||||
val x = [1, 2, 3, 4, 5]
|
||||
|
||||
// remove last:
|
||||
x.removeAt(-1)
|
||||
assert( x == [1, 2, 3, 4])
|
||||
|
||||
// remove 3 last:
|
||||
x.removeAt(-3,1)
|
||||
assert( x == [1])
|
||||
>>> void
|
||||
|
||||
|
||||
|
||||
# Flow control operators
|
||||
|
||||
## if-then-else
|
||||
|
@ -1,15 +1,31 @@
|
||||
package net.sergeych.ling
|
||||
|
||||
data class Arguments(val list: List<Info>): Iterable<Obj> {
|
||||
data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false)
|
||||
|
||||
data class Info(val value: Obj,val pos: Pos)
|
||||
suspend fun Collection<ParsedArgument>.toArguments(context: Context): Arguments {
|
||||
val list = mutableListOf<Arguments.Info>()
|
||||
|
||||
for (x in this) {
|
||||
val value = x.value.execute(context)
|
||||
if (x.isSplat) {
|
||||
(value as? ObjList) ?: context.raiseClassCastError("expected list of objects for splat argument")
|
||||
for (subitem in value.list) list.add(Arguments.Info(subitem, x.pos))
|
||||
} else
|
||||
list.add(Arguments.Info(value, x.pos))
|
||||
}
|
||||
return Arguments(list)
|
||||
}
|
||||
|
||||
data class Arguments(val list: List<Info>) : Iterable<Obj> {
|
||||
|
||||
data class Info(val value: Obj, val pos: Pos)
|
||||
|
||||
val size by list::size
|
||||
|
||||
operator fun get(index: Int): Obj = list[index].value
|
||||
|
||||
fun firstAndOnly(): Obj {
|
||||
if( list.size != 1 ) throw IllegalArgumentException("Expected one argument, got ${list.size}")
|
||||
if (list.size != 1) throw IllegalArgumentException("Expected one argument, got ${list.size}")
|
||||
return list.first().value
|
||||
}
|
||||
|
||||
@ -20,4 +36,6 @@ data class Arguments(val list: List<Info>): Iterable<Obj> {
|
||||
override fun iterator(): Iterator<Obj> {
|
||||
return list.map { it.value }.iterator()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -148,10 +148,7 @@ class Compiler(
|
||||
v.callInstanceMethod(
|
||||
context,
|
||||
next.value,
|
||||
Arguments(args.map {
|
||||
val st = it.value as Statement
|
||||
Arguments.Info(st.execute(context),it.pos) }
|
||||
)
|
||||
args.toArguments(context)
|
||||
), isMutable = false
|
||||
)
|
||||
}
|
||||
@ -275,11 +272,11 @@ class Compiler(
|
||||
} ?: run {
|
||||
// no lvalue means pre-increment, expression to increment follows
|
||||
val next = parseAccessor(cc) ?: throw ScriptError(t.pos, "Expecting expression")
|
||||
operand = Accessor({ ctx ->
|
||||
operand = Accessor { ctx ->
|
||||
next.getter(ctx).also {
|
||||
if (!it.isMutable) ctx.raiseError("Cannot increment immutable value")
|
||||
}.value.incrementAndGet(ctx).asReadonly
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,15 +348,19 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseArgs(cc: CompilerContext): List<Arguments.Info> {
|
||||
val args = mutableListOf<Arguments.Info>()
|
||||
private fun parseArgs(cc: CompilerContext): List<ParsedArgument> {
|
||||
val args = mutableListOf<ParsedArgument>()
|
||||
do {
|
||||
val t = cc.next()
|
||||
when (t.type) {
|
||||
Token.Type.RPAREN, Token.Type.COMMA -> {}
|
||||
Token.Type.ELLIPSIS -> {
|
||||
parseStatement(cc)?.let { args += ParsedArgument(it, t.pos, isSplat = true) }
|
||||
?: throw ScriptError(t.pos, "Expecting arguments list")
|
||||
}
|
||||
else -> {
|
||||
cc.previous()
|
||||
parseStatement(cc)?.let { args += Arguments.Info(it, t.pos) }
|
||||
parseStatement(cc)?.let { args += ParsedArgument(it, t.pos) }
|
||||
?: throw ScriptError(t.pos, "Expecting arguments list")
|
||||
}
|
||||
}
|
||||
@ -376,9 +377,10 @@ class Compiler(
|
||||
val v = left.getter(context)
|
||||
v.value.callOn(context.copy(
|
||||
context.pos,
|
||||
Arguments(
|
||||
args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
||||
),
|
||||
args.toArguments(context)
|
||||
// Arguments(
|
||||
// args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
||||
// ),
|
||||
)
|
||||
).asReadonly
|
||||
}
|
||||
|
@ -55,11 +55,6 @@ sealed class Obj {
|
||||
?: throw ScriptError(atPos, "symbol doesn't exist: $name")
|
||||
|
||||
suspend fun callInstanceMethod(context: Context, name: String, args: Arguments): Obj =
|
||||
// instance _methods_ are our ObjClass instance:
|
||||
// note that getInstanceMember traverses the hierarchy
|
||||
// instance _methods_ are our ObjClass instance:
|
||||
// note that getInstanceMember traverses the hierarchy
|
||||
// instance _methods_ are our ObjClass instance:
|
||||
// note that getInstanceMember traverses the hierarchy
|
||||
objClass.getInstanceMember(context.pos, name).value.invoke(context, this, args)
|
||||
|
||||
@ -154,7 +149,7 @@ sealed class Obj {
|
||||
// could be property or class field:
|
||||
val obj = objClass.getInstanceMemberOrNull(name)
|
||||
val value = obj?.value
|
||||
return when(value) {
|
||||
return when (value) {
|
||||
is Statement -> {
|
||||
// readonly property, important: call it on this
|
||||
value.execute(context.copy(context.pos, newThisObj = this)).asReadonly
|
||||
@ -253,7 +248,7 @@ object ObjNull : Obj() {
|
||||
data class ObjString(val value: String) : Obj() {
|
||||
|
||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||
if (other !is ObjString) context.raiseError("cannot compare string with $other")
|
||||
if (other !is ObjString) return -2
|
||||
return this.value.compareTo(other.value)
|
||||
}
|
||||
|
||||
@ -311,7 +306,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
|
||||
override fun byValueCopy(): Obj = ObjReal(value)
|
||||
|
||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||
if (other !is Numeric) context.raiseError("cannot compare $this with $other")
|
||||
if (other !is Numeric) return -2
|
||||
return value.compareTo(other.doubleValue)
|
||||
}
|
||||
|
||||
@ -372,7 +367,7 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
|
||||
}
|
||||
|
||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||
if (other !is Numeric) context.raiseError("cannot compare $this with $other")
|
||||
if (other !is Numeric) return -2
|
||||
return value.compareTo(other.doubleValue)
|
||||
}
|
||||
|
||||
@ -412,7 +407,7 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
|
||||
* assignment
|
||||
*/
|
||||
override suspend fun assign(context: Context, other: Obj): Obj? {
|
||||
return if( other is ObjInt) {
|
||||
return if (other is ObjInt) {
|
||||
value = other.value
|
||||
this
|
||||
} else null
|
||||
@ -427,7 +422,7 @@ data class ObjBool(val value: Boolean) : Obj() {
|
||||
override val asStr by lazy { ObjString(value.toString()) }
|
||||
|
||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||
if (other !is ObjBool) context.raiseError("cannot compare $this with $other")
|
||||
if (other !is ObjBool) return -2
|
||||
return value.compareTo(other.value)
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,9 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||
list.joinToString(separator = ", ") { it.inspect() }
|
||||
}]"
|
||||
|
||||
fun normalize(context: Context, index: Int): Int {
|
||||
fun normalize(context: Context, index: Int,allowInclusiveEnd: Boolean = false): Int {
|
||||
val i = if (index < 0) list.size + index else index
|
||||
if( allowInclusiveEnd && i == list.size ) return i
|
||||
if (i !in list.indices) context.raiseError("index $index out of bounds for size ${list.size}")
|
||||
return i
|
||||
}
|
||||
@ -23,7 +24,7 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||
}
|
||||
|
||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
||||
if (other !is ObjList) context.raiseError("cannot compare $this with $other")
|
||||
if (other !is ObjList) return -2
|
||||
val mySize = list.size
|
||||
val otherSize = other.list.size
|
||||
val commonSize = minOf(mySize, otherSize)
|
||||
@ -72,7 +73,8 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
||||
statement {
|
||||
if (args.size < 2) raiseError("addAt takes 2+ arguments")
|
||||
val l = thisAs<ObjList>()
|
||||
var index = l.normalize(this, requiredArg<ObjInt>(0).value.toInt())
|
||||
var index = l.normalize(this, requiredArg<ObjInt>(0).value.toInt(),
|
||||
allowInclusiveEnd = true)
|
||||
for (i in 1..<args.size) l.list.add(index++, args[i])
|
||||
ObjVoid
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user