Implement automatic substitution for named parameters (auto-named arguments). Supported name: shorthand for name: name in function calls and class constructors. Updated documentation and tests. Built and deployed IDEA plugin and site.
This commit is contained in:
parent
c9f96464f3
commit
157b716eb7
10
README.md
10
README.md
@ -4,11 +4,15 @@ Please visit the project homepage: [https://lynglang.com](https://lynglang.com)
|
|||||||
|
|
||||||
- simple, compact, intuitive and elegant modern code:
|
- simple, compact, intuitive and elegant modern code:
|
||||||
|
|
||||||
```
|
```lyng
|
||||||
class Point(x,y) {
|
class Point(x, y) {
|
||||||
fun dist() { sqrt(x*x + y*y) }
|
fun dist() { sqrt(x*x + y*y) }
|
||||||
}
|
}
|
||||||
Point(3,4).dist() //< 5
|
|
||||||
|
// Auto-named arguments shorthand (x: is x: x):
|
||||||
|
val x = 3
|
||||||
|
val y = 4
|
||||||
|
Point(x:, y:).dist() //< 5
|
||||||
|
|
||||||
fun swapEnds(first, args..., last, f) {
|
fun swapEnds(first, args..., last, f) {
|
||||||
f( last, ...args, first)
|
f( last, ...args, first)
|
||||||
|
|||||||
@ -85,6 +85,12 @@ statements discussed later, there could be default values, ellipsis, etc.
|
|||||||
// Named arguments in constructor calls use colon syntax:
|
// Named arguments in constructor calls use colon syntax:
|
||||||
val p2 = Point(y: 10, x: 5)
|
val p2 = Point(y: 10, x: 5)
|
||||||
assert( p2.x == 5 && p2.y == 10 )
|
assert( p2.x == 5 && p2.y == 10 )
|
||||||
|
|
||||||
|
// Auto-substitution shorthand for named arguments:
|
||||||
|
val x = 1
|
||||||
|
val y = 2
|
||||||
|
val p3 = Point(x:, y:)
|
||||||
|
assert( p3.x == 1 && p3.y == 2 )
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
Note that unlike **Kotlin**, which uses `=` for named arguments, Lyng uses `:` to avoid ambiguity with assignment expressions.
|
Note that unlike **Kotlin**, which uses `=` for named arguments, Lyng uses `:` to avoid ambiguity with assignment expressions.
|
||||||
|
|||||||
@ -107,16 +107,29 @@ There could be any number of splats at any positions. You can splat any other [I
|
|||||||
|
|
||||||
## Named arguments in calls
|
## Named arguments in calls
|
||||||
|
|
||||||
Lyng supports named arguments at call sites using colon syntax `name: value`:
|
Lyng supports named arguments at call sites using colon syntax `name: value`.
|
||||||
|
|
||||||
|
### Shorthand for Named Arguments
|
||||||
|
|
||||||
|
If you want to pass a variable as a named argument and the variable has the same name as the parameter, you can omit the value and use the shorthand `name:`. This is highly readable and matches the shorthand for map literals.
|
||||||
|
|
||||||
```lyng
|
```lyng
|
||||||
fun test(a="foo", b="bar", c="bazz") { [a, b, c] }
|
fun test(a, b, c) { [a, b, c] }
|
||||||
|
|
||||||
assertEquals(["foo", "b", "bazz"], test(b: "b"))
|
val a = 1
|
||||||
assertEquals(["a", "bar", "c"], test("a", c: "c"))
|
val b = 2
|
||||||
|
val c = 3
|
||||||
|
|
||||||
|
// Explicit:
|
||||||
|
assertEquals([1, 2, 3], test(a: a, b: b, c: c))
|
||||||
|
|
||||||
|
// Shorthand (preferred):
|
||||||
|
assertEquals([1, 2, 3], test(a:, b:, c:))
|
||||||
```
|
```
|
||||||
|
|
||||||
Rules:
|
This shorthand is elegant, reduces boilerplate, and is consistent with Lyng's map literal syntax. It works for both function calls and class constructors.
|
||||||
|
|
||||||
|
Rules for named arguments:
|
||||||
|
|
||||||
- Named arguments must follow positional arguments. After the first named argument, no positional arguments may appear inside the parentheses.
|
- Named arguments must follow positional arguments. After the first named argument, no positional arguments may appear inside the parentheses.
|
||||||
- The only exception is the syntactic trailing block after the call: `f(args) { ... }`. This block is outside the parentheses and is handled specially (see below).
|
- The only exception is the syntactic trailing block after the call: `f(args) { ... }`. This block is outside the parentheses and is handled specially (see below).
|
||||||
@ -127,21 +140,20 @@ Why `:` and not `=` at call sites? In Lyng, `=` is an expression (assignment), s
|
|||||||
|
|
||||||
## Named splats (map splats)
|
## Named splats (map splats)
|
||||||
|
|
||||||
Splat (`...`) of a Map provides named arguments to the call. Only string keys are allowed:
|
Splat (`...`) of a Map provides named arguments to the call. Only string keys are allowed. You can use the same auto-substitution shorthand inside map literals used for splats:
|
||||||
|
|
||||||
```lyng
|
```lyng
|
||||||
fun test(a="a", b="b", c="c", d="d") { [a, b, c, d] }
|
fun test(a="a", b="b", c="c", d="d") { [a, b, c, d] }
|
||||||
val r = test("A?", ...Map("d" => "D!", "b" => "B!"))
|
|
||||||
assertEquals(["A?","B!","c","D!"], r)
|
val b = "B!"
|
||||||
```
|
val d = "D!"
|
||||||
|
|
||||||
The same with a map literal is often more concise. Define the literal, then splat the variable:
|
// Auto-substitution in map literal:
|
||||||
|
val patch = { d:, b: }
|
||||||
fun test(a="a", b="b", c="c", d="d") { [a, b, c, d] }
|
|
||||||
val patch = { d: "D!", b: "B!" }
|
|
||||||
val r = test("A?", ...patch)
|
val r = test("A?", ...patch)
|
||||||
assertEquals(["A?","B!","c","D!"], r)
|
assertEquals(["A?","B!","c","D!"], r)
|
||||||
>>> void
|
```
|
||||||
|
|
||||||
Constraints:
|
Constraints:
|
||||||
|
|
||||||
|
|||||||
@ -1036,6 +1036,12 @@ class Compiler(
|
|||||||
if (t2.type == Token.Type.COLON) {
|
if (t2.type == Token.Type.COLON) {
|
||||||
// name: expr
|
// name: expr
|
||||||
val name = t1.value
|
val name = t1.value
|
||||||
|
// Check for shorthand: name: (comma or rparen)
|
||||||
|
val next = cc.peekNextNonWhitespace()
|
||||||
|
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||||
|
val localVar = LocalVarRef(name, t1.pos)
|
||||||
|
return ParsedArgument(statement(t1.pos) { localVar.get(it).value }, t1.pos, isSplat = false, name = name)
|
||||||
|
}
|
||||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||||
}
|
}
|
||||||
@ -1102,6 +1108,12 @@ class Compiler(
|
|||||||
val t2 = cc.next()
|
val t2 = cc.next()
|
||||||
if (t2.type == Token.Type.COLON) {
|
if (t2.type == Token.Type.COLON) {
|
||||||
val name = t1.value
|
val name = t1.value
|
||||||
|
// Check for shorthand: name: (comma or rparen)
|
||||||
|
val next = cc.peekNextNonWhitespace()
|
||||||
|
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||||
|
val localVar = LocalVarRef(name, t1.pos)
|
||||||
|
return ParsedArgument(statement(t1.pos) { localVar.get(it).value }, t1.pos, isSplat = false, name = name)
|
||||||
|
}
|
||||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3022,7 +3022,8 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMapWithNonStringKeys() = runTest {
|
fun testMapWithNonStringKeys() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
val map = Map( 1 => "one", 2 => "two" )
|
val map = Map( 1 => "one", 2 => "two" )
|
||||||
assertEquals( "one", map[1] )
|
assertEquals( "one", map[1] )
|
||||||
assertEquals( "two", map[2] )
|
assertEquals( "two", map[2] )
|
||||||
@ -3046,7 +3047,8 @@ class ScriptTest {
|
|||||||
val map4 = map3 + (3 => "c")
|
val map4 = map3 + (3 => "c")
|
||||||
assertEquals("c", map4[3])
|
assertEquals("c", map4[3])
|
||||||
assertEquals("a", map4[1])
|
assertEquals("a", map4[1])
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -3948,13 +3950,15 @@ class ScriptTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testJsonTime() = runTest {
|
fun testJsonTime() = runTest {
|
||||||
val now = Clock.System.now()
|
val now = Clock.System.now()
|
||||||
val x = eval("""
|
val x = eval(
|
||||||
|
"""
|
||||||
import lyng.time
|
import lyng.time
|
||||||
Instant.now().truncateToSecond()
|
Instant.now().truncateToSecond()
|
||||||
""".trimIndent()).decodeSerializable<Instant>()
|
""".trimIndent()
|
||||||
|
).decodeSerializable<Instant>()
|
||||||
println(x)
|
println(x)
|
||||||
assertIs<Instant>(x)
|
assertIs<Instant>(x)
|
||||||
assertTrue( (now - x).absoluteValue < 2.seconds)
|
assertTrue((now - x).absoluteValue < 2.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -4067,15 +4071,17 @@ class ScriptTest {
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class TestJson2(
|
data class TestJson2(
|
||||||
val value: Int,
|
val value: Int,
|
||||||
val inner: Map<String,Int>
|
val inner: Map<String, Int>
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deserializeMapWithJsonTest() = runTest {
|
fun deserializeMapWithJsonTest() = runTest {
|
||||||
val x = eval("""
|
val x = eval(
|
||||||
|
"""
|
||||||
import lyng.serialization
|
import lyng.serialization
|
||||||
{ value: 1, inner: { "foo": 1, "bar": 2 }}
|
{ value: 1, inner: { "foo": 1, "bar": 2 }}
|
||||||
""".trimIndent()).decodeSerializable<TestJson2>()
|
""".trimIndent()
|
||||||
|
).decodeSerializable<TestJson2>()
|
||||||
assertEquals(TestJson2(1, mapOf("foo" to 1, "bar" to 2)), x)
|
assertEquals(TestJson2(1, mapOf("foo" to 1, "bar" to 2)), x)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4084,42 +4090,56 @@ class ScriptTest {
|
|||||||
val value: Int,
|
val value: Int,
|
||||||
val inner: JsonObject
|
val inner: JsonObject
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deserializeAnyMapWithJsonTest() = runTest {
|
fun deserializeAnyMapWithJsonTest() = runTest {
|
||||||
val x = eval("""
|
val x = eval(
|
||||||
|
"""
|
||||||
import lyng.serialization
|
import lyng.serialization
|
||||||
{ value: 12, inner: { "foo": 1, "bar": "two" }}
|
{ value: 12, inner: { "foo": 1, "bar": "two" }}
|
||||||
""".trimIndent()).decodeSerializable<TestJson3>()
|
""".trimIndent()
|
||||||
assertEquals(TestJson3(12, JsonObject(mapOf("foo" to JsonPrimitive(1), "bar" to Json.encodeToJsonElement("two")))), x)
|
).decodeSerializable<TestJson3>()
|
||||||
|
assertEquals(
|
||||||
|
TestJson3(
|
||||||
|
12,
|
||||||
|
JsonObject(mapOf("foo" to JsonPrimitive(1), "bar" to Json.encodeToJsonElement("two")))
|
||||||
|
), x
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
enum class TestEnum {
|
enum class TestEnum {
|
||||||
One, Two
|
One, Two
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TestJson4(val value: TestEnum)
|
data class TestJson4(val value: TestEnum)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deserializeEnumJsonTest() = runTest {
|
fun deserializeEnumJsonTest() = runTest {
|
||||||
val x = eval("""
|
val x = eval(
|
||||||
|
"""
|
||||||
import lyng.serialization
|
import lyng.serialization
|
||||||
enum TestEnum { One, Two }
|
enum TestEnum { One, Two }
|
||||||
{ value: TestEnum.One }
|
{ value: TestEnum.One }
|
||||||
""".trimIndent()).decodeSerializable<TestJson4>()
|
""".trimIndent()
|
||||||
assertEquals( TestJson4(TestEnum.One), x)
|
).decodeSerializable<TestJson4>()
|
||||||
|
assertEquals(TestJson4(TestEnum.One), x)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testStringLast() = runTest {
|
fun testStringLast() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
assertEquals('t', "assert".last())
|
assertEquals('t', "assert".last())
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLogicalNot() = runTest {
|
fun testLogicalNot() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
val vf = false
|
val vf = false
|
||||||
fun f() { false }
|
fun f() { false }
|
||||||
assert( !false )
|
assert( !false )
|
||||||
@ -4156,7 +4176,8 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testHangOnPrintlnInMethods() = runTest {
|
fun testHangOnPrintlnInMethods() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
class T(someList) {
|
class T(someList) {
|
||||||
fun f() {
|
fun f() {
|
||||||
val x = [...someList]
|
val x = [...someList]
|
||||||
@ -4164,12 +4185,14 @@ class ScriptTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
T([1,2]).f()
|
T([1,2]).f()
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testHangOnNonexistingMethod() = runTest {
|
fun testHangOnNonexistingMethod() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
class T(someList) {
|
class T(someList) {
|
||||||
fun f() {
|
fun f() {
|
||||||
nonExistingMethod()
|
nonExistingMethod()
|
||||||
@ -4185,12 +4208,14 @@ class ScriptTest {
|
|||||||
println(t::class)
|
println(t::class)
|
||||||
// ok
|
// ok
|
||||||
}
|
}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testUsingClassConstructorVars() = runTest {
|
fun testUsingClassConstructorVars() = runTest {
|
||||||
val r = eval("""
|
val r = eval(
|
||||||
|
"""
|
||||||
import lyng.time
|
import lyng.time
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
@ -4214,7 +4239,8 @@ class ScriptTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test()
|
test()
|
||||||
""".trimIndent()).toJson()
|
""".trimIndent()
|
||||||
|
).toJson()
|
||||||
println(r)
|
println(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4222,7 +4248,8 @@ class ScriptTest {
|
|||||||
fun testScopeShortCircuit() = runTest() {
|
fun testScopeShortCircuit() = runTest() {
|
||||||
val baseScope = Script.newScope()
|
val baseScope = Script.newScope()
|
||||||
|
|
||||||
baseScope.eval("""
|
baseScope.eval(
|
||||||
|
"""
|
||||||
val exports = Map()
|
val exports = Map()
|
||||||
fun Export(name,f) {
|
fun Export(name,f) {
|
||||||
exports[name] = f
|
exports[name] = f
|
||||||
@ -4233,7 +4260,8 @@ class ScriptTest {
|
|||||||
|
|
||||||
val exports: MutableMap<Obj, Obj> = (baseScope.eval("exports") as ObjMap).map
|
val exports: MutableMap<Obj, Obj> = (baseScope.eval("exports") as ObjMap).map
|
||||||
|
|
||||||
baseScope.eval("""
|
baseScope.eval(
|
||||||
|
"""
|
||||||
class A(val a) {
|
class A(val a) {
|
||||||
fun methodA() {
|
fun methodA() {
|
||||||
a + 1
|
a + 1
|
||||||
@ -4250,29 +4278,41 @@ class ScriptTest {
|
|||||||
fun exportedFunction(x) {
|
fun exportedFunction(x) {
|
||||||
someFunction(x)
|
someFunction(x)
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
// Calling from the script is ok:
|
// Calling from the script is ok:
|
||||||
val instanceScope = baseScope.createChildScope()
|
val instanceScope = baseScope.createChildScope()
|
||||||
instanceScope.eval("""
|
instanceScope.eval(
|
||||||
|
"""
|
||||||
val a1 = a0 + 1
|
val a1 = a0 + 1
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
assertEquals( ObjInt(2), instanceScope.eval("""
|
)
|
||||||
|
assertEquals(
|
||||||
|
ObjInt(2), instanceScope.eval(
|
||||||
|
"""
|
||||||
exportedFunction(1)
|
exportedFunction(1)
|
||||||
"""))
|
"""
|
||||||
assertEquals( ObjInt(103), instanceScope.eval("""
|
)
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
ObjInt(103), instanceScope.eval(
|
||||||
|
"""
|
||||||
exportedFunction(a1 + 1)
|
exportedFunction(a1 + 1)
|
||||||
"""))
|
"""
|
||||||
|
)
|
||||||
|
)
|
||||||
val dummyThis = Obj()
|
val dummyThis = Obj()
|
||||||
// but we should be able to call it directly
|
// but we should be able to call it directly
|
||||||
val otherScope = baseScope.createChildScope()
|
val otherScope = baseScope.createChildScope()
|
||||||
val r = (exports["exportedFunction".toObj()] as Statement).invoke(otherScope, dummyThis,ObjInt(50))
|
val r = (exports["exportedFunction".toObj()] as Statement).invoke(otherScope, dummyThis, ObjInt(50))
|
||||||
println(r)
|
println(r)
|
||||||
assertEquals(51, r.toInt())
|
assertEquals(51, r.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFirstInEnum() = runTest {
|
fun testFirstInEnum() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
enum E {
|
enum E {
|
||||||
one, two, three
|
one, two, three
|
||||||
}
|
}
|
||||||
@ -4282,12 +4322,14 @@ class ScriptTest {
|
|||||||
it.name in ["aaa", "two"]
|
it.name in ["aaa", "two"]
|
||||||
} )
|
} )
|
||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAutoSplatArgs() = runTest {
|
fun testAutoSplatArgs() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
fun tf(x, y, z) {
|
fun tf(x, y, z) {
|
||||||
"x=%s, y=%s, z=%s"(x,y,z)
|
"x=%s, y=%s, z=%s"(x,y,z)
|
||||||
}
|
}
|
||||||
@ -4295,12 +4337,14 @@ class ScriptTest {
|
|||||||
val a = { x: 3, y: 4, z: 5 }
|
val a = { x: 3, y: 4, z: 5 }
|
||||||
assertEquals(tf(...a), "x=3, y=4, z=5")
|
assertEquals(tf(...a), "x=3, y=4, z=5")
|
||||||
assertEquals(tf(...{ x: 3, y: 4, z: 50 }), "x=3, y=4, z=50")
|
assertEquals(tf(...{ x: 3, y: 4, z: 50 }), "x=3, y=4, z=50")
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCached() = runTest {
|
fun testCached() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
var counter = 0
|
var counter = 0
|
||||||
val f = cached { ++counter }
|
val f = cached { ++counter }
|
||||||
|
|
||||||
@ -4308,12 +4352,14 @@ class ScriptTest {
|
|||||||
assertEquals(1, counter)
|
assertEquals(1, counter)
|
||||||
assertEquals(1,f())
|
assertEquals(1,f())
|
||||||
assertEquals(1, counter)
|
assertEquals(1, counter)
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCustomToStringBug() = runTest {
|
fun testCustomToStringBug() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
class A(x,y)
|
class A(x,y)
|
||||||
class B(x,y) {
|
class B(x,y) {
|
||||||
fun toString() {
|
fun toString() {
|
||||||
@ -4329,14 +4375,16 @@ class ScriptTest {
|
|||||||
// and this must be exactly same:
|
// and this must be exactly same:
|
||||||
assertEquals(":B(1,2)", ":" + B(1,2))
|
assertEquals(":B(1,2)", ":" + B(1,2))
|
||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDestructuringAssignment() = runTest {
|
fun testDestructuringAssignment() = runTest {
|
||||||
eval("""
|
eval(
|
||||||
|
"""
|
||||||
val abc = [1, 2, 3]
|
val abc = [1, 2, 3]
|
||||||
// plain:
|
// plain:
|
||||||
val [a, b, c] = abc
|
val [a, b, c] = abc
|
||||||
@ -4373,7 +4421,8 @@ class ScriptTest {
|
|||||||
assertEquals( 10, x )
|
assertEquals( 10, x )
|
||||||
assertEquals( 5, y )
|
assertEquals( 5, y )
|
||||||
|
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -4411,6 +4460,58 @@ class ScriptTest {
|
|||||||
println(x)
|
println(x)
|
||||||
assertEquals(5, x.pos.line)
|
assertEquals(5, x.pos.line)
|
||||||
assertContains(x.message!!, "throw \"success\"")
|
assertContains(x.message!!, "throw \"success\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClassAndFunAutoNamedArgs() = runTest {
|
||||||
|
// Shorthand for named arguments: name: is equivalent to name: name.
|
||||||
|
// This is consistent with map literal shorthand in Lyng.
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
fun test(a, b, c) {
|
||||||
|
"%s-%s-%s"(a,b,c)
|
||||||
|
}
|
||||||
|
|
||||||
|
val a = 1
|
||||||
|
val b = 2
|
||||||
|
val c = 3
|
||||||
|
|
||||||
|
// Basic usage:
|
||||||
|
assertEquals( "1-2-3", test(a:, b:, c:) )
|
||||||
|
assertEquals( "1-2-3", test(c:, b:, a:) )
|
||||||
|
|
||||||
|
// Class constructors also support it:
|
||||||
|
class Point(x, y) {
|
||||||
|
val r = "x:%s, y:%s"(x, y)
|
||||||
|
}
|
||||||
|
val x = 10
|
||||||
|
val y = 20
|
||||||
|
assertEquals( "x:10, y:20", Point(x:, y:).r )
|
||||||
|
assertEquals( "x:10, y:20", Point(y:, x:).r )
|
||||||
|
|
||||||
|
// Mixed with positional arguments:
|
||||||
|
assertEquals( "0-2-3", test(0, b:, c:) )
|
||||||
|
|
||||||
|
// Mixed with regular named arguments:
|
||||||
|
assertEquals( "1-99-3", test(a:, b: 99, c:) )
|
||||||
|
|
||||||
|
// Integration with splats (spread arguments):
|
||||||
|
val args = { b:, c: } // map literal shorthand
|
||||||
|
assertEquals( "1-2-3", test(a:, ...args) )
|
||||||
|
|
||||||
|
// Default values:
|
||||||
|
fun sum(a, b=10, c=100) { a + b + c }
|
||||||
|
assertEquals( 111, sum(a:) )
|
||||||
|
assertEquals( 103, sum(a:, b:) )
|
||||||
|
|
||||||
|
// Complex scenario with multiple splats and shorthands:
|
||||||
|
val p1 = 1
|
||||||
|
val p2 = 2
|
||||||
|
val more = { c: 3, d: 4 }
|
||||||
|
fun quad(a, b, c, d) { a + b + c + d }
|
||||||
|
assertEquals( 10, quad(a: p1, b: p2, ...more) )
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user