From c167ebe6a82b8f0e1edfa76b209f45a0c443243d Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 2 Jun 2025 18:59:47 +0400 Subject: [PATCH] +lambda after fn call as last argument --- docs/tutorial.md | 16 ++++++++++ .../kotlin/net/sergeych/lyng/Compiler.kt | 32 +++++++++++++++---- .../net/sergeych/lyng/CompilerContext.kt | 7 ++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index d4318af..983bf1a 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -325,6 +325,22 @@ one could be with ellipsis that means "the rest pf arguments as List": assert( [11, 21, 31] == mapValues( [1,2,3], { it*10+1 })) >>> void +### Auto last parameter + +When the function call is follower by the `{` in the same line, e.g. lambda immediately +after function call, it is treated as a last argument to the call, e.g.: + + fun mapValues(iterable, transform) { + var result = [] + for( x in iterable ) result += transform(x) + } + val mapped = mapValues( [1,2,3]) { + it*10+1 + } + assert( [11, 21, 31] == mapped) + >>> void + + # Lists (aka arrays) Lyng has built-in mutable array class `List` with simple literals: diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 6173afc..41c2624 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -343,7 +343,7 @@ class Compiler( context.addItem("it", false, itValue) } else { // assign vars as declared - if( args.size != argsDeclaration.args.size && !argsDeclaration.args.last().isEllipsis) + if (args.size != argsDeclaration.args.size && !argsDeclaration.args.last().isEllipsis) raiseArgumentError("Too many arguments : called with ${args.size}, lambda accepts only ${argsDeclaration.args.size}") for ((n, a) in argsDeclaration.args.withIndex()) { if (n >= args.size) { @@ -351,10 +351,9 @@ class Compiler( context.addItem(a.name, false, a.initialValue.execute(context)) else throw ScriptError(a.pos, "argument $n is out of scope") } else { - val value = if( a.isEllipsis) { + val value = if (a.isEllipsis) { ObjList(args.values.subList(n, args.values.size).toMutableList()) - } - else + } else args[n] context.addItem(a.name, false, value) } @@ -491,7 +490,10 @@ class Compiler( do { val t = cc.next() when (t.type) { - Token.Type.RPAREN, Token.Type.COMMA -> {} + Token.Type.NEWLINE, + 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") @@ -501,9 +503,25 @@ class Compiler( cc.previous() parseExpression(cc)?.let { args += ParsedArgument(it, t.pos) } ?: throw ScriptError(t.pos, "Expecting arguments list") + // Here should be a valid termination: } } } while (t.type != Token.Type.RPAREN) + // block after? + val pos = cc.savePos() + val end = cc.next() + if (end.type == Token.Type.LBRACE) { + // last argument - callable + val callableAccessor = parseLambdaExpression(cc) + args += ParsedArgument( + // transform accessor to the callable: + statement { + callableAccessor.getter(this).value + }, + end.pos + ) + } else + cc.restorePos(pos) return args } @@ -771,7 +789,7 @@ class Compiler( throw lbe } } - if( !wasBroken ) elseStatement?.let { s -> result = s.execute(it) } + if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) } result } } @@ -958,7 +976,7 @@ class Compiler( private fun parseBlock(cc: CompilerContext, skipLeadingBrace: Boolean = false): Statement { val startPos = cc.currentPos() - if( !skipLeadingBrace ) { + if (!skipLeadingBrace) { val t = cc.next() if (t.type != Token.Type.LBRACE) throw ScriptError(t.pos, "Expected block body start: {") diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt index 76d2ebf..5d6c719 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/CompilerContext.kt @@ -55,6 +55,13 @@ internal class CompilerContext(val tokens: List) { } else true } + @Suppress("unused") + fun skipTokens(vararg tokenTypes: Token.Type) { + while( next().type in tokenTypes ) { /**/ } + previous() + } + + fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean { val t = next() return if (t.type == typeId) {