diff --git a/library/build.gradle.kts b/library/build.gradle.kts index b2c4242..767a991 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -5,7 +5,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget group = "net.sergeych" -version = "0.3.0-SNAPSHOT" +version = "0.3.1-SNAPSHOT" buildscript { repositories { diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt index 928b27f..bb72d99 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/ArgsDeclaration.kt @@ -23,7 +23,7 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) */ suspend fun assignToContext( context: Context, - _fromArgs: Arguments = context.args, + arguments: Arguments = context.args, defaultAccessType: AccessType = AccessType.Var, defaultVisibility: Visibility = Visibility.Public ) { @@ -33,15 +33,25 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) } // will be used with last lambda arg fix - val fromArgs = _fromArgs + val callArgs: List + 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 { var i = index - while (i != params.size) { + while (i != paramsSize) { val a = params[i] if (a.isEllipsis) break val value = when { - i < fromArgs.size -> fromArgs[i] + i < callArgs.size -> callArgs[i] a.defaultValue != null -> a.defaultValue.execute(context) else -> context.raiseArgumentError("too few arguments for the call") } @@ -52,14 +62,14 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) } suspend fun processTail(index: Int): Int { - var i = params.size - 1 - var j = fromArgs.size - 1 + var i = paramsSize - 1 + var j = callArgs.size - 1 while (i > index) { val a = params[i] if (a.isEllipsis) break val value = when { j >= index -> { - fromArgs[j--] + callArgs[j--] } a.defaultValue != null -> a.defaultValue.execute(context) @@ -74,16 +84,16 @@ data class ArgsDeclaration(val params: List, val endTokenType: Token.Type) fun processEllipsis(index: Int, toFromIndex: Int) { val a = params[index] val l = if (index > toFromIndex) ObjList() - else ObjList(fromArgs.list.subList(index, toFromIndex + 1).toMutableList()) + else ObjList(callArgs.subList(index, toFromIndex + 1).toMutableList()) assign(a, l) } val leftIndex = processHead(0) - if (leftIndex < params.size) { + if (leftIndex < paramsSize) { val end = processTail(leftIndex) processEllipsis(leftIndex, end) } else { - if (leftIndex < fromArgs.size) + if (leftIndex < callArgs.size) context.raiseArgumentError("too many arguments for the call") } } diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Arguments.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Arguments.kt index 2edee8b..414a5f4 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Arguments.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Arguments.kt @@ -2,7 +2,7 @@ package net.sergeych.lyng data class ParsedArgument(val value: Statement, val pos: Pos, val isSplat: Boolean = false) -suspend fun Collection.toArguments(context: Context): Arguments { +suspend fun Collection.toArguments(context: Context,tailBlockMode: Boolean): Arguments { val list = mutableListOf() for (x in this) { @@ -23,10 +23,10 @@ suspend fun Collection.toArguments(context: Context): Arguments } else list.add(value) } - return Arguments(list) + return Arguments(list,tailBlockMode) } -data class Arguments(val list: List) : List by list { +data class Arguments(val list: List,val tailBlockMode: Boolean = false) : List by list { fun firstAndOnly(pos: Pos = Pos.UNKNOWN): Obj { if (list.size != 1) throw ScriptError(pos, "expected one argument, got ${list.size}") diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 8cd7bcc..116ef24 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -149,7 +149,7 @@ class Compiler( v.invokeInstanceMethod( context, next.value, - args.toArguments(context) + args.toArguments(context,false) ), isMutable = false ) } @@ -157,7 +157,7 @@ class Compiler( Token.Type.LBRACE -> { // single lambda arg, like assertTrows { ... } - cc.next() + cc.next() isCall = true val lambda = parseExpression(cc) ?: throw ScriptError(t.pos, "expected valid lambda here") @@ -170,7 +170,7 @@ class Compiler( v.invokeInstanceMethod( context, next.value, - Arguments(listOf(lambda)) + Arguments(listOf(lambda),true) ), isMutable = false ) } @@ -611,7 +611,7 @@ class Compiler( v.value.callOn( context.copy( context.pos, - args.toArguments(context) + args.toArguments(context, blockArgument) // Arguments( // args.map { Arguments.Info((it.value as Statement).execute(context), it.pos) } // ), diff --git a/library/src/commonTest/kotlin/ScriptTest.kt b/library/src/commonTest/kotlin/ScriptTest.kt index 3ac12d6..95b2089 100644 --- a/library/src/commonTest/kotlin/ScriptTest.kt +++ b/library/src/commonTest/kotlin/ScriptTest.kt @@ -1585,16 +1585,59 @@ class ScriptTest { assertEquals("1.0E-6", eval("1e-6").toString()) } -// @Test -// fun testLambdaLastArgAfterDetault() = runTest { -// val c = Context() -// eval(""" -// // this means last is lambda: -// fun f(e=1, f) { -// "e="+e+"f="+f() -// } -// assertEquals("e=1f=xx", f { "xx" }) -// """.trimIndent()) -// -// } + @Test + fun testCallLastBlockAfterDetault() = runTest { + eval(""" + // this means last is lambda: + fun f(e=1, f) { + "e="+e+"f="+f() + } + assertEquals("e=1f=xx", f { "xx" }) + """.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()) + + } } \ No newline at end of file