Compare commits
3 Commits
b253eed032
...
194fc8aca6
Author | SHA1 | Date | |
---|---|---|---|
194fc8aca6 | |||
382532e0e1 | |||
c0eba1ecf0 |
@ -5,7 +5,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.3.0-SNAPSHOT"
|
version = "0.3.1-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -23,7 +23,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
*/
|
*/
|
||||||
suspend fun assignToContext(
|
suspend fun assignToContext(
|
||||||
context: Context,
|
context: Context,
|
||||||
_fromArgs: Arguments = context.args,
|
arguments: Arguments = context.args,
|
||||||
defaultAccessType: AccessType = AccessType.Var,
|
defaultAccessType: AccessType = AccessType.Var,
|
||||||
defaultVisibility: Visibility = Visibility.Public
|
defaultVisibility: Visibility = Visibility.Public
|
||||||
) {
|
) {
|
||||||
@ -33,15 +33,25 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// will be used with last lambda arg fix
|
// will be used with last lambda arg fix
|
||||||
val fromArgs = _fromArgs
|
val callArgs: List<Obj>
|
||||||
|
val paramsSize: Int
|
||||||
|
|
||||||
|
if( arguments.tailBlockMode ) {
|
||||||
|
paramsSize = params.size - 1
|
||||||
|
assign(params.last(), arguments.list.last())
|
||||||
|
callArgs = arguments.list.dropLast(1)
|
||||||
|
} else {
|
||||||
|
paramsSize = params.size
|
||||||
|
callArgs = arguments.list
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun processHead(index: Int): Int {
|
suspend fun processHead(index: Int): Int {
|
||||||
var i = index
|
var i = index
|
||||||
while (i != params.size) {
|
while (i != paramsSize) {
|
||||||
val a = params[i]
|
val a = params[i]
|
||||||
if (a.isEllipsis) break
|
if (a.isEllipsis) break
|
||||||
val value = when {
|
val value = when {
|
||||||
i < fromArgs.size -> fromArgs[i]
|
i < callArgs.size -> callArgs[i]
|
||||||
a.defaultValue != null -> a.defaultValue.execute(context)
|
a.defaultValue != null -> a.defaultValue.execute(context)
|
||||||
else -> context.raiseArgumentError("too few arguments for the call")
|
else -> context.raiseArgumentError("too few arguments for the call")
|
||||||
}
|
}
|
||||||
@ -52,14 +62,14 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun processTail(index: Int): Int {
|
suspend fun processTail(index: Int): Int {
|
||||||
var i = params.size - 1
|
var i = paramsSize - 1
|
||||||
var j = fromArgs.size - 1
|
var j = callArgs.size - 1
|
||||||
while (i > index) {
|
while (i > index) {
|
||||||
val a = params[i]
|
val a = params[i]
|
||||||
if (a.isEllipsis) break
|
if (a.isEllipsis) break
|
||||||
val value = when {
|
val value = when {
|
||||||
j >= index -> {
|
j >= index -> {
|
||||||
fromArgs[j--]
|
callArgs[j--]
|
||||||
}
|
}
|
||||||
|
|
||||||
a.defaultValue != null -> a.defaultValue.execute(context)
|
a.defaultValue != null -> a.defaultValue.execute(context)
|
||||||
@ -74,16 +84,16 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
fun processEllipsis(index: Int, toFromIndex: Int) {
|
fun processEllipsis(index: Int, toFromIndex: Int) {
|
||||||
val a = params[index]
|
val a = params[index]
|
||||||
val l = if (index > toFromIndex) ObjList()
|
val l = if (index > toFromIndex) ObjList()
|
||||||
else ObjList(fromArgs.values.subList(index, toFromIndex + 1).toMutableList())
|
else ObjList(callArgs.subList(index, toFromIndex + 1).toMutableList())
|
||||||
assign(a, l)
|
assign(a, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
val leftIndex = processHead(0)
|
val leftIndex = processHead(0)
|
||||||
if (leftIndex < params.size) {
|
if (leftIndex < paramsSize) {
|
||||||
val end = processTail(leftIndex)
|
val end = processTail(leftIndex)
|
||||||
processEllipsis(leftIndex, end)
|
processEllipsis(leftIndex, end)
|
||||||
} else {
|
} else {
|
||||||
if (leftIndex < fromArgs.size)
|
if (leftIndex < callArgs.size)
|
||||||
context.raiseArgumentError("too many arguments for the call")
|
context.raiseArgumentError("too many arguments for the call")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,53 +2,40 @@ package net.sergeych.lyng
|
|||||||
|
|
||||||
data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false)
|
data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false)
|
||||||
|
|
||||||
suspend fun Collection<ParsedArgument>.toArguments(context: Context): Arguments {
|
suspend fun Collection<ParsedArgument>.toArguments(context: Context,tailBlockMode: Boolean): Arguments {
|
||||||
val list = mutableListOf<Arguments.Info>()
|
val list = mutableListOf<Obj>()
|
||||||
|
|
||||||
for (x in this) {
|
for (x in this) {
|
||||||
val value = x.value.execute(context)
|
val value = x.value.execute(context)
|
||||||
if (x.isSplat) {
|
if (x.isSplat) {
|
||||||
when {
|
when {
|
||||||
value is ObjList -> {
|
value is ObjList -> {
|
||||||
for (subitem in value.list) list.add(Arguments.Info(subitem, x.pos))
|
for (subitem in value.list) list.add(subitem)
|
||||||
}
|
}
|
||||||
|
|
||||||
value.isInstanceOf(ObjIterable) -> {
|
value.isInstanceOf(ObjIterable) -> {
|
||||||
val i = (value.invokeInstanceMethod(context, "toList") as ObjList).list
|
val i = (value.invokeInstanceMethod(context, "toList") as ObjList).list
|
||||||
i.forEach { list.add(Arguments.Info(it, x.pos)) }
|
i.forEach { list.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> context.raiseClassCastError("expected list of objects for splat argument")
|
else -> context.raiseClassCastError("expected list of objects for splat argument")
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
list.add(Arguments.Info(value, x.pos))
|
list.add(value)
|
||||||
}
|
}
|
||||||
return Arguments(list)
|
return Arguments(list,tailBlockMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Arguments(val list: List<Info>) : Iterable<Obj> {
|
data class Arguments(val list: List<Obj>,val tailBlockMode: Boolean = false) : List<Obj> by list {
|
||||||
|
|
||||||
data class Info(val value: Obj, val pos: Pos)
|
fun firstAndOnly(pos: Pos = Pos.UNKNOWN): Obj {
|
||||||
|
if (list.size != 1) throw ScriptError(pos, "expected one argument, got ${list.size}")
|
||||||
val size by list::size
|
return list.first()
|
||||||
|
|
||||||
operator fun get(index: Int): Obj = list[index].value
|
|
||||||
|
|
||||||
val values: List<Obj> by lazy { list.map { it.value } }
|
|
||||||
|
|
||||||
fun firstAndOnly(): Obj {
|
|
||||||
if (list.size != 1) throw IllegalArgumentException("Expected one argument, got ${list.size}")
|
|
||||||
return list.first().value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = Arguments(emptyList())
|
val EMPTY = Arguments(emptyList())
|
||||||
fun from(values: Collection<Obj>) = Arguments(values.map { Info(it, Pos.UNKNOWN) })
|
fun from(values: Collection<Obj>) = Arguments(values.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun iterator(): Iterator<Obj> {
|
|
||||||
return list.map { it.value }.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ class Compiler(
|
|||||||
v.invokeInstanceMethod(
|
v.invokeInstanceMethod(
|
||||||
context,
|
context,
|
||||||
next.value,
|
next.value,
|
||||||
args.toArguments(context)
|
args.toArguments(context,false)
|
||||||
), isMutable = false
|
), isMutable = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ class Compiler(
|
|||||||
|
|
||||||
Token.Type.LBRACE -> {
|
Token.Type.LBRACE -> {
|
||||||
// single lambda arg, like assertTrows { ... }
|
// single lambda arg, like assertTrows { ... }
|
||||||
cc.next()
|
cc.next()
|
||||||
isCall = true
|
isCall = true
|
||||||
val lambda =
|
val lambda =
|
||||||
parseExpression(cc) ?: throw ScriptError(t.pos, "expected valid lambda here")
|
parseExpression(cc) ?: throw ScriptError(t.pos, "expected valid lambda here")
|
||||||
@ -170,7 +170,7 @@ class Compiler(
|
|||||||
v.invokeInstanceMethod(
|
v.invokeInstanceMethod(
|
||||||
context,
|
context,
|
||||||
next.value,
|
next.value,
|
||||||
Arguments(listOf(Arguments.Info(lambda, t.pos)))
|
Arguments(listOf(lambda),true)
|
||||||
), isMutable = false
|
), isMutable = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ class Compiler(
|
|||||||
val context = closure!!.copy(pos, args)
|
val context = closure!!.copy(pos, args)
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
// no args: automatic var 'it'
|
// no args: automatic var 'it'
|
||||||
val l = args.values
|
val l = args.list
|
||||||
val itValue: Obj = when (l.size) {
|
val itValue: Obj = when (l.size) {
|
||||||
// no args: it == void
|
// no args: it == void
|
||||||
0 -> ObjVoid
|
0 -> ObjVoid
|
||||||
@ -611,7 +611,7 @@ class Compiler(
|
|||||||
v.value.callOn(
|
v.value.callOn(
|
||||||
context.copy(
|
context.copy(
|
||||||
context.pos,
|
context.pos,
|
||||||
args.toArguments(context)
|
args.toArguments(context, blockArgument)
|
||||||
// Arguments(
|
// Arguments(
|
||||||
// args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
// args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) }
|
||||||
// ),
|
// ),
|
||||||
|
@ -41,8 +41,8 @@ class Context(
|
|||||||
|
|
||||||
inline fun <reified T : Obj> requiredArg(index: Int): T {
|
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}")
|
if (args.list.size <= index) raiseError("Expected at least ${index + 1} argument, got ${args.list.size}")
|
||||||
return (args.list[index].value as? T)
|
return (args.list[index] as? T)
|
||||||
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index].value::class.simpleName}")
|
?: raiseClassCastError("Expected type ${T::class.simpleName}, got ${args.list[index]::class.simpleName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : Obj> requireOnlyArg(): T {
|
inline fun <reified T : Obj> requireOnlyArg(): T {
|
||||||
@ -80,7 +80,7 @@ class Context(
|
|||||||
value: Obj,
|
value: Obj,
|
||||||
visibility: Visibility = Visibility.Public
|
visibility: Visibility = Visibility.Public
|
||||||
): ObjRecord {
|
): ObjRecord {
|
||||||
return ObjRecord(value, isMutable, visibility).also { objects.put(name, it) }
|
return ObjRecord(value, isMutable, visibility).also { objects[name] = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOrCreateNamespace(name: String): ObjClass {
|
fun getOrCreateNamespace(name: String): ObjClass {
|
||||||
|
@ -61,7 +61,7 @@ open class Obj {
|
|||||||
fun isInstanceOf(someClass: Obj) = someClass === objClass || objClass.allParentsSet.contains(someClass)
|
fun isInstanceOf(someClass: Obj) = someClass === objClass || objClass.allParentsSet.contains(someClass)
|
||||||
|
|
||||||
suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj =
|
suspend fun invokeInstanceMethod(context: Context, name: String, vararg args: Obj): Obj =
|
||||||
invokeInstanceMethod(context, name, Arguments(args.map { Arguments.Info(it, context.pos) }))
|
invokeInstanceMethod(context, name, Arguments(args.toList()))
|
||||||
|
|
||||||
inline suspend fun <reified T : Obj> callMethod(
|
inline suspend fun <reified T : Obj> callMethod(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -218,7 +218,7 @@ open class Obj {
|
|||||||
callOn(
|
callOn(
|
||||||
context.copy(
|
context.copy(
|
||||||
context.pos,
|
context.pos,
|
||||||
args = Arguments(args.map { Arguments.Info(it, context.pos) }),
|
args = Arguments(args.toList()),
|
||||||
newThisObj = thisObj
|
newThisObj = thisObj
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1585,16 +1585,59 @@ class ScriptTest {
|
|||||||
assertEquals("1.0E-6", eval("1e-6").toString())
|
assertEquals("1.0E-6", eval("1e-6").toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
@Test
|
||||||
// fun testLambdaLastArgAfterDetault() = runTest {
|
fun testCallLastBlockAfterDetault() = runTest {
|
||||||
// val c = Context()
|
eval("""
|
||||||
// eval("""
|
// this means last is lambda:
|
||||||
// // this means last is lambda:
|
fun f(e=1, f) {
|
||||||
// fun f(e=1, f) {
|
"e="+e+"f="+f()
|
||||||
// "e="+e+"f="+f()
|
}
|
||||||
// }
|
assertEquals("e=1f=xx", f { "xx" })
|
||||||
// assertEquals("e=1f=xx", f { "xx" })
|
""".trimIndent())
|
||||||
// """.trimIndent())
|
|
||||||
//
|
}
|
||||||
// }
|
|
||||||
|
@Test
|
||||||
|
fun testCallLastBlockWithEllipsis() = runTest {
|
||||||
|
eval("""
|
||||||
|
// this means last is lambda:
|
||||||
|
fun f(e..., f) {
|
||||||
|
"e="+e+"f="+f()
|
||||||
|
}
|
||||||
|
assertEquals("e=[]f=xx", f { "xx" })
|
||||||
|
assertEquals("e=[1, 2]f=xx", f(1,2) { "xx" })
|
||||||
|
""".trimIndent())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMethodCallLastBlockAfterDefault() = runTest {
|
||||||
|
eval("""
|
||||||
|
class Foo {
|
||||||
|
// this means last is lambda:
|
||||||
|
fun f(e=1, f) {
|
||||||
|
"e="+e+"f="+f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val f = Foo()
|
||||||
|
assertEquals("e=1f=xx", f.f { "xx" })
|
||||||
|
""".trimIndent())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMethodCallLastBlockWithEllipsis() = runTest {
|
||||||
|
eval("""
|
||||||
|
class Foo {
|
||||||
|
// this means last is lambda:
|
||||||
|
fun f(e..., f) {
|
||||||
|
"e="+e+"f="+f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val f = Foo()
|
||||||
|
assertEquals("e=[]f=xx", f.f { "xx" })
|
||||||
|
assertEquals("e=[1, 2]f=xx", f.f(1,2) { "xx" })
|
||||||
|
""".trimIndent())
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user