+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, 2, 3] > [1, 2])
|
||||||
assert( [1, 3] > [1, 2, 3])
|
assert( [1, 3] > [1, 2, 3])
|
||||||
assert( [1, 2, 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:
|
// note that in the case above objects are referentially different:
|
||||||
assert( [1, 2, 3] !== [1, 2, 3])
|
assert( [1, 2, 3] !== [1, 2, 3])
|
||||||
>>> void
|
>>> void
|
||||||
|
@ -342,6 +342,27 @@ You can insert elements at any position using `addAt`:
|
|||||||
assert( x == [1, "foo", "bar", 2, 3])
|
assert( x == [1, "foo", "bar", 2, 3])
|
||||||
>>> void
|
>>> 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
|
## Removing list items
|
||||||
|
|
||||||
val x = [1, 2, 3, 4, 5]
|
val x = [1, 2, 3, 4, 5]
|
||||||
@ -352,6 +373,21 @@ You can insert elements at any position using `addAt`:
|
|||||||
assert( x == [1, 5])
|
assert( x == [1, 5])
|
||||||
>>> void
|
>>> 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
|
# Flow control operators
|
||||||
|
|
||||||
## if-then-else
|
## if-then-else
|
||||||
|
@ -1,15 +1,31 @@
|
|||||||
package net.sergeych.ling
|
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
|
val size by list::size
|
||||||
|
|
||||||
operator fun get(index: Int): Obj = list[index].value
|
operator fun get(index: Int): Obj = list[index].value
|
||||||
|
|
||||||
fun firstAndOnly(): Obj {
|
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
|
return list.first().value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,4 +36,6 @@ data class Arguments(val list: List<Info>): Iterable<Obj> {
|
|||||||
override fun iterator(): Iterator<Obj> {
|
override fun iterator(): Iterator<Obj> {
|
||||||
return list.map { it.value }.iterator()
|
return list.map { it.value }.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,10 +148,7 @@ class Compiler(
|
|||||||
v.callInstanceMethod(
|
v.callInstanceMethod(
|
||||||
context,
|
context,
|
||||||
next.value,
|
next.value,
|
||||||
Arguments(args.map {
|
args.toArguments(context)
|
||||||
val st = it.value as Statement
|
|
||||||
Arguments.Info(st.execute(context),it.pos) }
|
|
||||||
)
|
|
||||||
), isMutable = false
|
), isMutable = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -275,11 +272,11 @@ class Compiler(
|
|||||||
} ?: run {
|
} ?: run {
|
||||||
// no lvalue means pre-increment, expression to increment follows
|
// no lvalue means pre-increment, expression to increment follows
|
||||||
val next = parseAccessor(cc) ?: throw ScriptError(t.pos, "Expecting expression")
|
val next = parseAccessor(cc) ?: throw ScriptError(t.pos, "Expecting expression")
|
||||||
operand = Accessor({ ctx ->
|
operand = Accessor { ctx ->
|
||||||
next.getter(ctx).also {
|
next.getter(ctx).also {
|
||||||
if (!it.isMutable) ctx.raiseError("Cannot increment immutable value")
|
if (!it.isMutable) ctx.raiseError("Cannot increment immutable value")
|
||||||
}.value.incrementAndGet(ctx).asReadonly
|
}.value.incrementAndGet(ctx).asReadonly
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,15 +348,19 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseArgs(cc: CompilerContext): List<Arguments.Info> {
|
private fun parseArgs(cc: CompilerContext): List<ParsedArgument> {
|
||||||
val args = mutableListOf<Arguments.Info>()
|
val args = mutableListOf<ParsedArgument>()
|
||||||
do {
|
do {
|
||||||
val t = cc.next()
|
val t = cc.next()
|
||||||
when (t.type) {
|
when (t.type) {
|
||||||
Token.Type.RPAREN, Token.Type.COMMA -> {}
|
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 -> {
|
else -> {
|
||||||
cc.previous()
|
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")
|
?: throw ScriptError(t.pos, "Expecting arguments list")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,9 +377,10 @@ class Compiler(
|
|||||||
val v = left.getter(context)
|
val v = left.getter(context)
|
||||||
v.value.callOn(context.copy(
|
v.value.callOn(context.copy(
|
||||||
context.pos,
|
context.pos,
|
||||||
Arguments(
|
args.toArguments(context)
|
||||||
args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
// Arguments(
|
||||||
),
|
// args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
||||||
|
// ),
|
||||||
)
|
)
|
||||||
).asReadonly
|
).asReadonly
|
||||||
}
|
}
|
||||||
|
@ -55,11 +55,6 @@ sealed class Obj {
|
|||||||
?: throw ScriptError(atPos, "symbol doesn't exist: $name")
|
?: throw ScriptError(atPos, "symbol doesn't exist: $name")
|
||||||
|
|
||||||
suspend fun callInstanceMethod(context: Context, name: String, args: Arguments): Obj =
|
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
|
// note that getInstanceMember traverses the hierarchy
|
||||||
objClass.getInstanceMember(context.pos, name).value.invoke(context, this, args)
|
objClass.getInstanceMember(context.pos, name).value.invoke(context, this, args)
|
||||||
|
|
||||||
@ -154,7 +149,7 @@ sealed class Obj {
|
|||||||
// could be property or class field:
|
// could be property or class field:
|
||||||
val obj = objClass.getInstanceMemberOrNull(name)
|
val obj = objClass.getInstanceMemberOrNull(name)
|
||||||
val value = obj?.value
|
val value = obj?.value
|
||||||
return when(value) {
|
return when (value) {
|
||||||
is Statement -> {
|
is Statement -> {
|
||||||
// readonly property, important: call it on this
|
// readonly property, important: call it on this
|
||||||
value.execute(context.copy(context.pos, newThisObj = this)).asReadonly
|
value.execute(context.copy(context.pos, newThisObj = this)).asReadonly
|
||||||
@ -253,7 +248,7 @@ object ObjNull : Obj() {
|
|||||||
data class ObjString(val value: String) : Obj() {
|
data class ObjString(val value: String) : Obj() {
|
||||||
|
|
||||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
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)
|
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 fun byValueCopy(): Obj = ObjReal(value)
|
||||||
|
|
||||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
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)
|
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 {
|
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)
|
return value.compareTo(other.doubleValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,7 +407,7 @@ data class ObjInt(var value: Long) : Obj(), Numeric {
|
|||||||
* assignment
|
* assignment
|
||||||
*/
|
*/
|
||||||
override suspend fun assign(context: Context, other: Obj): Obj? {
|
override suspend fun assign(context: Context, other: Obj): Obj? {
|
||||||
return if( other is ObjInt) {
|
return if (other is ObjInt) {
|
||||||
value = other.value
|
value = other.value
|
||||||
this
|
this
|
||||||
} else null
|
} else null
|
||||||
@ -427,7 +422,7 @@ data class ObjBool(val value: Boolean) : Obj() {
|
|||||||
override val asStr by lazy { ObjString(value.toString()) }
|
override val asStr by lazy { ObjString(value.toString()) }
|
||||||
|
|
||||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
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)
|
return value.compareTo(other.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,9 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
|||||||
list.joinToString(separator = ", ") { it.inspect() }
|
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
|
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}")
|
if (i !in list.indices) context.raiseError("index $index out of bounds for size ${list.size}")
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
@ -23,7 +24,7 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun compareTo(context: Context, other: Obj): Int {
|
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 mySize = list.size
|
||||||
val otherSize = other.list.size
|
val otherSize = other.list.size
|
||||||
val commonSize = minOf(mySize, otherSize)
|
val commonSize = minOf(mySize, otherSize)
|
||||||
@ -72,7 +73,8 @@ class ObjList(val list: MutableList<Obj>) : Obj() {
|
|||||||
statement {
|
statement {
|
||||||
if (args.size < 2) raiseError("addAt takes 2+ arguments")
|
if (args.size < 2) raiseError("addAt takes 2+ arguments")
|
||||||
val l = thisAs<ObjList>()
|
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])
|
for (i in 1..<args.size) l.list.add(index++, args[i])
|
||||||
ObjVoid
|
ObjVoid
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user