more dics & minor bugs fixed

This commit is contained in:
Sergey Chernov 2025-06-08 14:39:39 +04:00
parent 9a08da0dfd
commit 5591a66af3
5 changed files with 119 additions and 5 deletions

View File

@ -0,0 +1,76 @@
# Declaring arguments in Lyng
It is a common thing that occurs in many places in Lyng, function declarations,
lambdas, struct and class declarations.
## Regular
## default values
Default parameters should not be mixed with mandatory ones:
// ok:
fun validFun(a, b, c=0, d=1) {}
// this is a compilration error
fun invalidFun(a, b=1, c) {} // throw error
Valid examples:
fun foo(bar, baz="buz", end= -1) {
println(bar + ' ' + baz + ' ' + end)
}
foo("bar")
foo("nobar", "buzz")
foo("nobar", "buzz", 120)
>>> bar buz -1
>>> nobar buzz -1
>>> nobar buzz 120
>>> void
# Ellipsis
Ellipsis are used to declare variadic arguments. It basically means "all the arguments available here". It means, ellipsis argument could be in any part of the list, being, end or middle, but there could be only one ellipsis argument and it must not have default value, its default value is always `[]`, en empty list.
Ellipsis argument receives what is left from arguments after processing regular one that could be before or after.
Ellipsis could be a first argument:
fun testCountArgs(data...,size) {
assert(size is Int)
assertEquals(size, data.size)
}
testCountArgs( 1, 2, "three", 3)
>>> void
Ellipsis could also be a last one:
fun testCountArgs(size, data...) {
assert(size is Int)
assertEquals(size, data.size)
}
testCountArgs( 3, 10, 2, "three")
>>> void
Or in the middle:
fun testCountArgs(size, data..., textToReturn) {
assert(size is Int)
assertEquals(size, data.size)
textToReturn
}
testCountArgs( 3, 10, 2, "three", "All OK")
>>> "All OK"
## Destructuring with splats
When combined with splat arguments discussed in the [tutorial] it could be used to effectively
destructuring arrays when calling functions and lambdas:
fun getFirstAndLast(first, args..., last) {
[ first, last ]
}
getFirstAndLast( ...(1..10).toList() )
>>> [1, 10]
[tutorial]: tutorial.md

View File

@ -238,6 +238,8 @@ but Lyng syntax requires using the __lambda syntax__ to create such.
See lambdas section below. See lambdas section below.
## Declaring arguments
There are default parameters in Lyng: There are default parameters in Lyng:
fn check(amount, prefix = "answer: ") { fn check(amount, prefix = "answer: ") {
@ -250,6 +252,17 @@ There are default parameters in Lyng:
check(120) check(120)
>>> "answer: enough" >>> "answer: enough"
It is possible to define also vararg using ellipsis:
fun sum(args...) {
var result = args[0]
for( i in 1 ..< args.size ) result += args[i]
}
sum(10,20,30)
>>> 60
See the [arguments reference](declaring_arguments.md) for more details.
## Closures ## Closures
Each __block has an isolated context that can be accessed from closures__. For example: Each __block has an isolated context that can be accessed from closures__. For example:

View File

@ -8,6 +8,13 @@ data class ArgsDeclaration(val args: List<Item>, val endTokenType: Token.Type) {
init { init {
val i = args.count { it.isEllipsis } val i = args.count { it.isEllipsis }
if (i > 1) throw ScriptError(args[i].pos, "there can be only one argument") if (i > 1) throw ScriptError(args[i].pos, "there can be only one argument")
val start = args.indexOfFirst { it.defaultValue != null }
if (start >= 0)
for (j in start + 1 until args.size)
if (args[j].defaultValue == null) throw ScriptError(
args[j].pos,
"required argument can't follow default one"
)
} }
/** /**
@ -69,8 +76,7 @@ data class ArgsDeclaration(val args: List<Item>, val endTokenType: Token.Type) {
if (leftIndex < args.size) { if (leftIndex < args.size) {
val end = processTail(leftIndex) val end = processTail(leftIndex)
processEllipsis(leftIndex, end) processEllipsis(leftIndex, end)
} } else {
else {
if (leftIndex < fromArgs.size) if (leftIndex < fromArgs.size)
context.raiseArgumentError("too many arguments for the call") context.raiseArgumentError("too many arguments for the call")
} }

View File

@ -125,6 +125,20 @@ class Script(
if( a.compareTo(this, b) != 0 ) if( a.compareTo(this, b) != 0 )
raiseError(ObjAssertionError(this,"Assertion failed: ${a.inspect()} == ${b.inspect()}")) raiseError(ObjAssertionError(this,"Assertion failed: ${a.inspect()} == ${b.inspect()}"))
} }
addFn("assertThrows") {
val code = requireOnlyArg<Statement>()
val result =try {
code.execute(this)
null
}
catch( e: ExecutionError ) {
e.errorObject
}
catch (e: ScriptError) {
ObjNull
}
result ?: raiseError(ObjAssertionError(this,"Expected exception but nothing was thrown"))
}
addVoidFn("delay") { addVoidFn("delay") {
delay((this.args.firstAndOnly().toDouble()/1000.0).roundToLong()) delay((this.args.firstAndOnly().toDouble()/1000.0).roundToLong())

View File

@ -195,7 +195,7 @@ suspend fun DocTest.test(context: Context = Context()) {
System.err.println("\nfailed: ${this.detailedString}") System.err.println("\nfailed: ${this.detailedString}")
} }
error?.let { error?.let {
fail("test failed", it) fail(it.message, it)
} }
assertEquals(expectedOutput, collectedOutput.toString(), "script output do not match") assertEquals(expectedOutput, collectedOutput.toString(), "script output do not match")
assertEquals(expectedResult, result.toString(), "script result does not match") assertEquals(expectedResult, result.toString(), "script result does not match")
@ -263,4 +263,9 @@ class BookTest {
} }
} }
} }
@Test
fun testArgumentBooks() = runTest {
runDocTests("../docs/declaring_arguments.md")
}
} }