+lambda after fn call as last argument

This commit is contained in:
Sergey Chernov 2025-06-02 18:59:47 +04:00
parent 29c643eed2
commit c167ebe6a8
3 changed files with 48 additions and 7 deletions

View File

@ -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 })) assert( [11, 21, 31] == mapValues( [1,2,3], { it*10+1 }))
>>> void >>> 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) # Lists (aka arrays)
Lyng has built-in mutable array class `List` with simple literals: Lyng has built-in mutable array class `List` with simple literals:

View File

@ -343,7 +343,7 @@ class Compiler(
context.addItem("it", false, itValue) context.addItem("it", false, itValue)
} else { } else {
// assign vars as declared // 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}") raiseArgumentError("Too many arguments : called with ${args.size}, lambda accepts only ${argsDeclaration.args.size}")
for ((n, a) in argsDeclaration.args.withIndex()) { for ((n, a) in argsDeclaration.args.withIndex()) {
if (n >= args.size) { if (n >= args.size) {
@ -351,10 +351,9 @@ class Compiler(
context.addItem(a.name, false, a.initialValue.execute(context)) context.addItem(a.name, false, a.initialValue.execute(context))
else throw ScriptError(a.pos, "argument $n is out of scope") else throw ScriptError(a.pos, "argument $n is out of scope")
} else { } else {
val value = if( a.isEllipsis) { val value = if (a.isEllipsis) {
ObjList(args.values.subList(n, args.values.size).toMutableList()) ObjList(args.values.subList(n, args.values.size).toMutableList())
} } else
else
args[n] args[n]
context.addItem(a.name, false, value) context.addItem(a.name, false, value)
} }
@ -491,7 +490,10 @@ class Compiler(
do { do {
val t = cc.next() val t = cc.next()
when (t.type) { when (t.type) {
Token.Type.RPAREN, Token.Type.COMMA -> {} Token.Type.NEWLINE,
Token.Type.RPAREN, Token.Type.COMMA -> {
}
Token.Type.ELLIPSIS -> { Token.Type.ELLIPSIS -> {
parseStatement(cc)?.let { args += ParsedArgument(it, t.pos, isSplat = true) } parseStatement(cc)?.let { args += ParsedArgument(it, t.pos, isSplat = true) }
?: throw ScriptError(t.pos, "Expecting arguments list") ?: throw ScriptError(t.pos, "Expecting arguments list")
@ -501,9 +503,25 @@ class Compiler(
cc.previous() cc.previous()
parseExpression(cc)?.let { args += ParsedArgument(it, t.pos) } parseExpression(cc)?.let { args += ParsedArgument(it, t.pos) }
?: throw ScriptError(t.pos, "Expecting arguments list") ?: throw ScriptError(t.pos, "Expecting arguments list")
// Here should be a valid termination:
} }
} }
} while (t.type != Token.Type.RPAREN) } 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 return args
} }
@ -771,7 +789,7 @@ class Compiler(
throw lbe throw lbe
} }
} }
if( !wasBroken ) elseStatement?.let { s -> result = s.execute(it) } if (!wasBroken) elseStatement?.let { s -> result = s.execute(it) }
result result
} }
} }
@ -958,7 +976,7 @@ class Compiler(
private fun parseBlock(cc: CompilerContext, skipLeadingBrace: Boolean = false): Statement { private fun parseBlock(cc: CompilerContext, skipLeadingBrace: Boolean = false): Statement {
val startPos = cc.currentPos() val startPos = cc.currentPos()
if( !skipLeadingBrace ) { if (!skipLeadingBrace) {
val t = cc.next() val t = cc.next()
if (t.type != Token.Type.LBRACE) if (t.type != Token.Type.LBRACE)
throw ScriptError(t.pos, "Expected block body start: {") throw ScriptError(t.pos, "Expected block body start: {")

View File

@ -55,6 +55,13 @@ internal class CompilerContext(val tokens: List<Token>) {
} else true } 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 { fun ifNextIs(typeId: Token.Type, f: (Token) -> Unit): Boolean {
val t = next() val t = next()
return if (t.type == typeId) { return if (t.type == typeId) {