3217 lines
84 KiB
Kotlin
3217 lines
84 KiB
Kotlin
/*
|
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.flow.map
|
|
import kotlinx.coroutines.flow.toList
|
|
import kotlinx.coroutines.test.runTest
|
|
import net.sergeych.lyng.*
|
|
import net.sergeych.lyng.obj.*
|
|
import net.sergeych.lyng.pacman.InlineSourcesImportProvider
|
|
import kotlin.test.*
|
|
|
|
class ScriptTest {
|
|
|
|
@Test
|
|
fun testVersion() {
|
|
println("--------------------------------------------")
|
|
println("version = ${LyngVersion}")
|
|
}
|
|
|
|
@Test
|
|
fun parseNewlines() {
|
|
fun check(expected: String, type: Token.Type, row: Int, col: Int, src: String, offset: Int = 0) {
|
|
val source = src.toSource()
|
|
assertEquals(
|
|
Token(expected, source.posAt(row, col), type),
|
|
parseLyng(source)[offset]
|
|
)
|
|
}
|
|
check("1", Token.Type.INT, 0, 0, "1 + x\n2", 0)
|
|
check("+", Token.Type.PLUS, 0, 2, "1 + x\n2", 1)
|
|
check("x", Token.Type.ID, 0, 4, "1 + x\n2", 2)
|
|
check("\n", Token.Type.NEWLINE, 0, 5, "1 + x\n2", 3)
|
|
// check("2", Token.Type.INT, 1, 0, "1 + x\n2", 4)
|
|
// check("", Token.Type.EOF, 1, 0, "1 + x\n2", 5)
|
|
|
|
}
|
|
|
|
@Test
|
|
fun parseNumbersTest() {
|
|
fun check(expected: String, type: Token.Type, row: Int, col: Int, src: String, offset: Int = 0) {
|
|
val source = src.toSource()
|
|
assertEquals(
|
|
Token(expected, source.posAt(row, col), type),
|
|
parseLyng(source)[offset]
|
|
)
|
|
}
|
|
check("1", Token.Type.INT, 0, 0, "1")
|
|
check("7", Token.Type.INT, 0, 0, "7")
|
|
check("17", Token.Type.INT, 0, 0, "17")
|
|
check("17", Token.Type.INT, 0, 0, "17.")
|
|
check(".", Token.Type.DOT, 0, 2, "17.", 1)
|
|
|
|
// decimals
|
|
check("17.2", Token.Type.REAL, 0, 0, "17.2")
|
|
check("17.2", Token.Type.REAL, 0, 0, "17.2")
|
|
check("17.2", Token.Type.REAL, 0, 0, "17.2 ")
|
|
check("17.2", Token.Type.REAL, 0, 1, " 17.2")
|
|
check("17.2", Token.Type.REAL, 0, 2, " 17.2 ")
|
|
check("17.2e0", Token.Type.REAL, 0, 0, "17.2e0")
|
|
check("17.2e-22", Token.Type.REAL, 0, 0, "17.2e-22")
|
|
check("17.2e22", Token.Type.REAL, 0, 0, "17.2e+22")
|
|
check("17.2e22", Token.Type.REAL, 0, 0, "17.2E+22")
|
|
check("17.2e22", Token.Type.REAL, 0, 0, "17.2E22")
|
|
check("17.2e-22", Token.Type.REAL, 0, 0, "17.2E-22")
|
|
check("17.2e-22", Token.Type.REAL, 0, 0, "17.2E-22")
|
|
check("1e-22", Token.Type.REAL, 0, 0, "1E-22")
|
|
|
|
// hex
|
|
check("1", Token.Type.HEX, 0, 0, "0x1")
|
|
check("12", Token.Type.HEX, 0, 0, "0x12")
|
|
check("12abcdef", Token.Type.HEX, 0, 0, "0x12abcdef.gh")
|
|
check(".", Token.Type.DOT, 0, 10, "0x12abcdef.gh", 1)
|
|
check("gh", Token.Type.ID, 0, 11, "0x12abcdef.gh", 2)
|
|
|
|
check("5", Token.Type.INT, 0, 0, "5 6")
|
|
check("6", Token.Type.INT, 0, 2, "5 6", 1)
|
|
|
|
}
|
|
|
|
@Test
|
|
fun parseRangeTest() {
|
|
var tt = parseLyng("5 .. 4".toSource())
|
|
|
|
assertEquals(Token.Type.INT, tt[0].type)
|
|
assertEquals(Token.Type.DOTDOT, tt[1].type)
|
|
assertEquals(Token.Type.INT, tt[2].type)
|
|
|
|
tt = parseLyng("5 ..< 4".toSource())
|
|
|
|
assertEquals(Token.Type.INT, tt[0].type)
|
|
assertEquals(Token.Type.DOTDOTLT, tt[1].type)
|
|
assertEquals(Token.Type.INT, tt[2].type)
|
|
}
|
|
|
|
@Test
|
|
fun parseInTest() {
|
|
var tt = parseLyng("5 in 4".toSource())
|
|
|
|
assertEquals(Token.Type.INT, tt[0].type)
|
|
assertEquals(Token.Type.IN, tt[1].type)
|
|
assertEquals(Token.Type.INT, tt[2].type)
|
|
|
|
tt = parseLyng("5 ..< 4".toSource())
|
|
|
|
assertEquals(Token.Type.INT, tt[0].type)
|
|
assertEquals(Token.Type.DOTDOTLT, tt[1].type)
|
|
assertEquals(Token.Type.INT, tt[2].type)
|
|
}
|
|
|
|
@Test
|
|
fun parserLabelsTest() {
|
|
val src = "label@ break@label".toSource()
|
|
val tt = parseLyng(src)
|
|
assertEquals(Token("label", src.posAt(0, 0), Token.Type.LABEL), tt[0])
|
|
assertEquals(Token("break", src.posAt(0, 7), Token.Type.ID), tt[1])
|
|
assertEquals(Token("label", src.posAt(0, 12), Token.Type.ATLABEL), tt[2])
|
|
}
|
|
|
|
// @Test
|
|
// fun parse0Test() {
|
|
// val src = """
|
|
// println("Hello")
|
|
// println( "world" )
|
|
// """.trimIndent().toSource()
|
|
//
|
|
// val p = parseLyng(src).listIterator()
|
|
//
|
|
// assertEquals(Token("println", src.posAt(0, 0), Token.Type.ID), p.next())
|
|
// assertEquals(Token("(", src.posAt(0, 7), Token.Type.LPAREN), p.next())
|
|
// assertEquals(Token("Hello", src.posAt(0, 9), Token.Type.STRING), p.next())
|
|
// assertEquals(Token(")", src.posAt(0, 15), Token.Type.RPAREN), p.next())
|
|
// assertEquals(Token("\n", src.posAt(0, 16), Token.Type.NEWLINE), p.next())
|
|
// assertEquals(Token("println", src.posAt(1, 0), Token.Type.ID), p.next())
|
|
// assertEquals(Token("(", src.posAt(1, 7), Token.Type.LPAREN), p.next())
|
|
// assertEquals(Token("world", src.posAt(1, 9), Token.Type.STRING), p.next())
|
|
// assertEquals(Token(")", src.posAt(1, 17), Token.Type.RPAREN), p.next())
|
|
// }
|
|
|
|
@Test
|
|
fun parse1Test() {
|
|
val src = "2 + 7".toSource()
|
|
|
|
val p = parseLyng(src).listIterator()
|
|
|
|
assertEquals(Token("2", src.posAt(0, 0), Token.Type.INT), p.next())
|
|
assertEquals(Token("+", src.posAt(0, 2), Token.Type.PLUS), p.next())
|
|
assertEquals(Token("7", src.posAt(0, 4), Token.Type.INT), p.next())
|
|
}
|
|
|
|
@Test
|
|
fun compileNumbersTest() = runTest {
|
|
assertEquals(ObjInt(17), eval("17"))
|
|
assertEquals(ObjInt(17), eval("+17"))
|
|
assertEquals(ObjInt(-17), eval("-17"))
|
|
|
|
|
|
assertEquals(ObjInt(1970), eval("1900 + 70"))
|
|
assertEquals(ObjInt(1970), eval("2000 - 30"))
|
|
|
|
// assertEquals(ObjReal(3.14), eval("3.14"))
|
|
assertEquals(ObjReal(314.0), eval("3.14e2"))
|
|
assertEquals(ObjReal(314.0), eval("100 3.14e2"))
|
|
assertEquals(ObjReal(314.0), eval("100\n 3.14e2"))
|
|
}
|
|
|
|
@Test
|
|
fun compileBuiltinCallsTest() = runTest {
|
|
// println(eval("π"))
|
|
// val pi = eval("Math.PI")
|
|
// assertIs<ObjReal>(pi)
|
|
// assertTrue(pi.value - PI < 0.000001)
|
|
// assertTrue(eval("Math.PI+1").toDouble() - PI - 1.0 < 0.000001)
|
|
|
|
// assertTrue(eval("sin(Math.PI)").toDouble() - 1 < 0.000001)
|
|
assertTrue(eval("sin(π)").toDouble() - 1 < 0.000001)
|
|
}
|
|
|
|
@Test
|
|
fun varsAndConstsTest() = runTest {
|
|
val scope = Scope(pos = Pos.builtIn)
|
|
assertEquals(
|
|
ObjInt(3L), scope.eval(
|
|
"""
|
|
val a = 17
|
|
var b = 3
|
|
""".trimIndent()
|
|
)
|
|
)
|
|
assertEquals(17, scope.eval("a").toInt())
|
|
assertEquals(20, scope.eval("b + a").toInt())
|
|
assertFailsWith<ScriptError> {
|
|
scope.eval("a = 10")
|
|
}
|
|
assertEquals(17, scope.eval("a").toInt())
|
|
assertEquals(5, scope.eval("b = a - 7 - 5").toInt())
|
|
assertEquals(5, scope.eval("b").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun functionTest() = runTest {
|
|
val scope = Scope(pos = Pos.builtIn)
|
|
scope.eval(
|
|
"""
|
|
fun foo(a, b) {
|
|
a + b
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals(17, scope.eval("foo(3,14)").toInt())
|
|
assertFailsWith<ScriptError> {
|
|
assertEquals(17, scope.eval("foo(3)").toInt())
|
|
}
|
|
|
|
scope.eval(
|
|
"""
|
|
fn bar(a, b=10) {
|
|
a + b + 1
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals(10, scope.eval("bar(3, 6)").toInt())
|
|
assertEquals(14, scope.eval("bar(3)").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun simpleClosureTest() = runTest {
|
|
val scope = Scope(pos = Pos.builtIn)
|
|
scope.eval(
|
|
"""
|
|
var global = 10
|
|
|
|
fun foo(a, b) {
|
|
global + a + b
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals(27, scope.eval("foo(3,14)").toInt())
|
|
scope.eval("global = 20")
|
|
assertEquals(37, scope.eval("foo(3,14)").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun nullAndVoidTest() = runTest {
|
|
val scope = Scope(pos = Pos.builtIn)
|
|
assertEquals(ObjVoid, scope.eval("void"))
|
|
assertEquals(ObjNull, scope.eval("null"))
|
|
}
|
|
|
|
@Test
|
|
fun arithmeticOperatorsTest() = runTest {
|
|
assertEquals(2, eval("5/2").toInt())
|
|
assertEquals(2.5, eval("5.0/2").toDouble())
|
|
assertEquals(2.5, eval("5/2.0").toDouble())
|
|
assertEquals(2.5, eval("5.0/2.0").toDouble())
|
|
|
|
assertEquals(1, eval("5%2").toInt())
|
|
assertEquals(1.0, eval("5.0%2").toDouble())
|
|
|
|
assertEquals(77, eval("11 * 7").toInt())
|
|
|
|
assertEquals(2.0, eval("floor(5.0/2)").toDouble())
|
|
assertEquals(3, eval("ceil(5.0/2)").toInt())
|
|
|
|
assertEquals(2.0, eval("round(4.7/2)").toDouble())
|
|
assertEquals(3.0, eval("round(5.1/2)").toDouble())
|
|
}
|
|
|
|
@Test
|
|
fun arithmetics() = runTest {
|
|
// integer
|
|
assertEquals(17, eval("2 + 3 * 5").toInt())
|
|
assertEquals(4, eval("5-1").toInt())
|
|
assertEquals(2, eval("8/4").toInt())
|
|
assertEquals(2, eval("8 % 3").toInt())
|
|
|
|
// int-real
|
|
assertEquals(9.5, eval("2 + 3 * 2.5").toDouble())
|
|
assertEquals(4.5, eval("5 - 0.5").toDouble())
|
|
assertEquals(2.5, eval("5 / 2.0").toDouble())
|
|
assertEquals(2.5, eval("5.0 / 2.0").toDouble())
|
|
|
|
// real
|
|
assertEquals(7.5, eval("2.5 + 5.0").toDouble())
|
|
assertEquals(4.5, eval("5.0 - 0.5").toDouble())
|
|
assertEquals(12.5, eval("5.0 * 2.5").toDouble())
|
|
assertEquals(2.5, eval("5.0 / 2.0").toDouble())
|
|
}
|
|
|
|
@Test
|
|
fun arithmeticParenthesisTest() = runTest {
|
|
assertEquals(17, eval("2.0 + 3 * 5").toInt())
|
|
assertEquals(17, eval("2 + (3 * 5)").toInt())
|
|
assertEquals(25, eval("(2 + 3) * 5").toInt())
|
|
assertEquals(24, eval("(2 + 3) * 5 -1").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun stringOpTest() = runTest {
|
|
assertEquals("foobar", eval(""" "foo" + "bar" """).toString())
|
|
assertEquals("foo17", eval(""" "foo" + 17 """).toString())
|
|
}
|
|
|
|
@Test
|
|
fun eqNeqTest() = runTest {
|
|
assertEquals(ObjBool(true), eval("val x = 2; x == 2"))
|
|
assertEquals(ObjFalse, eval("val x = 3; x == 2"))
|
|
assertEquals(ObjBool(true), eval("val x = 3; x != 2"))
|
|
assertEquals(ObjFalse, eval("val x = 3; x != 3"))
|
|
|
|
assertTrue { eval("1 == 1").toBool() }
|
|
assertTrue { eval("true == true").toBool() }
|
|
assertTrue { eval("true != false").toBool() }
|
|
assertFalse { eval("true == false").toBool() }
|
|
assertFalse { eval("false != false").toBool() }
|
|
|
|
assertTrue { eval("2 == 2 && 3 != 4").toBool() }
|
|
}
|
|
|
|
@Test
|
|
fun logicTest() = runTest {
|
|
assertEquals(ObjFalse, eval("true && false"))
|
|
assertEquals(ObjFalse, eval("false && false"))
|
|
assertEquals(ObjFalse, eval("false && true"))
|
|
assertEquals(ObjBool(true), eval("true && true"))
|
|
|
|
assertEquals(ObjBool(true), eval("true || false"))
|
|
assertEquals(ObjFalse, eval("false || false"))
|
|
assertEquals(ObjBool(true), eval("false || true"))
|
|
assertEquals(ObjBool(true), eval("true || true"))
|
|
|
|
assertEquals(ObjFalse, eval("!true"))
|
|
assertEquals(ObjBool(true), eval("!false"))
|
|
}
|
|
|
|
@Test
|
|
fun gtLtTest() = runTest {
|
|
assertTrue { eval("3 > 2").toBool() }
|
|
assertFalse { eval("3 > 3").toBool() }
|
|
assertTrue { eval("3 >= 2").toBool() }
|
|
assertFalse { eval("3 >= 4").toBool() }
|
|
assertFalse { eval("3 < 2").toBool() }
|
|
assertFalse { eval("3 <= 2").toBool() }
|
|
assertTrue { eval("3 <= 3").toBool() }
|
|
assertTrue { eval("3 <= 4").toBool() }
|
|
assertTrue { eval("3 < 4").toBool() }
|
|
assertFalse { eval("4 < 3").toBool() }
|
|
assertFalse { eval("4 <= 3").toBool() }
|
|
}
|
|
|
|
@Test
|
|
fun ifTest() = runTest {
|
|
// if - single line
|
|
var scope = Scope(pos = Pos.builtIn)
|
|
scope.eval(
|
|
"""
|
|
fn test1(n) {
|
|
var result = "more"
|
|
if( n >= 10 )
|
|
result = "enough"
|
|
result
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals("enough", scope.eval("test1(11)").toString())
|
|
assertEquals("more", scope.eval("test1(1)").toString())
|
|
|
|
// if - multiline (block)
|
|
scope = Scope(pos = Pos.builtIn)
|
|
scope.eval(
|
|
"""
|
|
fn test1(n) {
|
|
var prefix = "answer: "
|
|
var result = "more"
|
|
if( n >= 10 ) {
|
|
var prefix = "bad:" // local prefix
|
|
prefix = "too bad:"
|
|
result = "enough"
|
|
}
|
|
prefix + result
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals("answer: enough", scope.eval("test1(11)").toString())
|
|
assertEquals("answer: more", scope.eval("test1(1)").toString())
|
|
|
|
// else single line1
|
|
scope = Scope(pos = Pos.builtIn)
|
|
scope.eval(
|
|
"""
|
|
fn test1(n) {
|
|
if( n >= 10 )
|
|
"enough"
|
|
else
|
|
"more"
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals("enough", scope.eval("test1(11)").toString())
|
|
assertEquals("more", scope.eval("test1(1)").toString())
|
|
|
|
// if/else with blocks
|
|
scope = Scope(pos = Pos.builtIn)
|
|
scope.eval(
|
|
"""
|
|
fn test1(n) {
|
|
if( n > 20 ) {
|
|
"too much"
|
|
} else if( n >= 10 ) {
|
|
"enough"
|
|
}
|
|
else {
|
|
"more"
|
|
}
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
assertEquals("enough", scope.eval("test1(11)").toString())
|
|
assertEquals("more", scope.eval("test1(1)").toString())
|
|
assertEquals("too much", scope.eval("test1(100)").toString())
|
|
}
|
|
|
|
@Test
|
|
fun lateInitTest() = runTest {
|
|
assertEquals(
|
|
"ok", eval(
|
|
"""
|
|
|
|
var late
|
|
|
|
fun init() {
|
|
late = "ok"
|
|
}
|
|
|
|
init()
|
|
late
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
|
|
}
|
|
|
|
@Test
|
|
fun whileAssignTest() = runTest {
|
|
eval(
|
|
"""
|
|
var t = 0
|
|
val x = while( t < 5 ) { t++ }
|
|
// last returned value is 4 - when t was 5 body was not executed
|
|
assertEquals( 4, x )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun whileTest() = runTest {
|
|
assertEquals(
|
|
5.0,
|
|
eval(
|
|
"""
|
|
var acc = 0
|
|
while( acc < 5 ) acc = acc + 0.5
|
|
acc
|
|
"""
|
|
).toDouble()
|
|
)
|
|
assertEquals(
|
|
5.0,
|
|
eval(
|
|
"""
|
|
var acc = 0
|
|
// return from while
|
|
while( acc < 5 ) {
|
|
acc = acc + 0.5
|
|
acc
|
|
}
|
|
"""
|
|
).toDouble()
|
|
)
|
|
assertEquals(
|
|
3.0,
|
|
eval(
|
|
"""
|
|
var acc = 0
|
|
while( acc < 5 ) {
|
|
acc = acc + 0.5
|
|
if( acc >= 3 ) break
|
|
}
|
|
|
|
acc
|
|
|
|
"""
|
|
).toDouble()
|
|
)
|
|
assertEquals(
|
|
17.0,
|
|
eval(
|
|
"""
|
|
var acc = 0
|
|
while( acc < 5 ) {
|
|
acc = acc + 0.5
|
|
if( acc >= 3 ) break 17
|
|
}
|
|
"""
|
|
).toDouble()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testAssignArgumentsNoEllipsis() = runTest {
|
|
// equal args, no ellipsis, no defaults, ok
|
|
val ttEnd = Token.Type.RBRACE
|
|
var pa = ArgsDeclaration(
|
|
listOf(
|
|
ArgsDeclaration.Item("a"),
|
|
ArgsDeclaration.Item("b"),
|
|
ArgsDeclaration.Item("c"),
|
|
), ttEnd
|
|
)
|
|
var c = Scope(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
assertEquals(ObjInt(1), c["a"]?.value)
|
|
assertEquals(ObjInt(2), c["b"]?.value)
|
|
assertEquals(ObjInt(3), c["c"]?.value)
|
|
// less args: error
|
|
c = Scope(pos = Pos.builtIn, args = Arguments.from(listOf(1, 2).map { it.toObj() }))
|
|
assertFailsWith<ScriptError> {
|
|
pa.assignToContext(c)
|
|
}
|
|
// less args, no ellipsis, defaults, ok
|
|
pa = ArgsDeclaration(
|
|
listOf(
|
|
ArgsDeclaration.Item("a"),
|
|
ArgsDeclaration.Item("b"),
|
|
ArgsDeclaration.Item("c", defaultValue = statement { ObjInt(100) }),
|
|
), ttEnd
|
|
)
|
|
pa.assignToContext(c)
|
|
assertEquals(ObjInt(1), c["a"]?.value)
|
|
assertEquals(ObjInt(2), c["b"]?.value)
|
|
assertEquals(ObjInt(100), c["c"]?.value)
|
|
// enough args. default value is ignored:
|
|
c = Scope(pos = Pos.builtIn, args = Arguments.from(listOf(10, 2, 5).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
assertEquals(ObjInt(10), c["a"]?.value)
|
|
assertEquals(ObjInt(2), c["b"]?.value)
|
|
assertEquals(ObjInt(5), c["c"]?.value)
|
|
}
|
|
|
|
@Test
|
|
fun testAssignArgumentsEndEllipsis() = runTest {
|
|
// equal args,
|
|
// less args, no ellipsis, defaults, ok
|
|
val ttEnd = Token.Type.RBRACE
|
|
val pa = ArgsDeclaration(
|
|
listOf(
|
|
ArgsDeclaration.Item("a"),
|
|
ArgsDeclaration.Item("b", isEllipsis = true),
|
|
), ttEnd
|
|
)
|
|
var c = Scope(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assert( a == 1 ); println(b)")
|
|
c.eval("assert( b == [2,3] )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(1, 2).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( a, 1 ); println(b)")
|
|
c.eval("assertEquals( b, [2] )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(1).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assert( a == 1 ); println(b)")
|
|
c.eval("assert( b == [] )")
|
|
}
|
|
|
|
@Test
|
|
fun testAssignArgumentsStartEllipsis() = runTest {
|
|
val ttEnd = Token.Type.RBRACE
|
|
val pa = ArgsDeclaration(
|
|
listOf(
|
|
ArgsDeclaration.Item("a", isEllipsis = true),
|
|
ArgsDeclaration.Item("b"),
|
|
ArgsDeclaration.Item("c"),
|
|
), ttEnd
|
|
)
|
|
var c = Scope(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( a,[0,1] )")
|
|
c.eval("assertEquals( b, 2 )")
|
|
c.eval("assertEquals( c, 3 )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( a,[1] )")
|
|
c.eval("assertEquals( b, 2 )")
|
|
c.eval("assertEquals( c, 3 )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( a,[] )")
|
|
c.eval("assertEquals( b, 2 )")
|
|
c.eval("assertEquals( c, 3 )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(3).map { it.toObj() }))
|
|
assertFailsWith<ExecutionError> {
|
|
pa.assignToContext(c)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun testAssignArgumentsMiddleEllipsis() = runTest {
|
|
val ttEnd = Token.Type.RBRACE
|
|
val pa = ArgsDeclaration(
|
|
listOf(
|
|
ArgsDeclaration.Item("i"),
|
|
ArgsDeclaration.Item("a", isEllipsis = true),
|
|
ArgsDeclaration.Item("b"),
|
|
ArgsDeclaration.Item("c"),
|
|
), ttEnd
|
|
)
|
|
var c = Scope(args = Arguments.from(listOf(-1, 0, 1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( i, -1 )")
|
|
c.eval("assertEquals( a,[0,1] )")
|
|
c.eval("assertEquals( b, 2 )")
|
|
c.eval("assertEquals( c, 3 )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(0, 1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( i, 0 )")
|
|
c.eval("assertEquals( a,[1] )")
|
|
c.eval("assertEquals( b, 2 )")
|
|
c.eval("assertEquals( c, 3 )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(1, 2, 3).map { it.toObj() }))
|
|
pa.assignToContext(c)
|
|
c.eval("assertEquals( i, 1)")
|
|
c.eval("assertEquals( a,[] )")
|
|
c.eval("assertEquals( b, 2 )")
|
|
c.eval("assertEquals( c, 3 )")
|
|
|
|
c = Scope(args = Arguments.from(listOf(2, 3).map { it.toObj() }))
|
|
assertFailsWith<ExecutionError> {
|
|
pa.assignToContext(c)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun testWhileBlockIsolation1() = runTest {
|
|
eval(
|
|
"""
|
|
var x = 100
|
|
var cnt = 2
|
|
while( cnt-- > 0 ) {
|
|
var x = cnt + 1
|
|
assert(x == cnt + 1)
|
|
}
|
|
assert( x == 100 )
|
|
assert( cnt == -1 )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testWhileBlockIsolation2() = runTest {
|
|
assertFails {
|
|
eval(
|
|
"""
|
|
var cnt = 2
|
|
while( cnt-- > 0 ) {
|
|
var inner = cnt + 1
|
|
assert(inner == cnt + 1)
|
|
}
|
|
println("inner "+inner)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun testWhileBlockIsolation3() = runTest {
|
|
eval(
|
|
"""
|
|
var outer = 7
|
|
var sum = 0
|
|
var cnt1 = 0
|
|
val initialForCnt2 = 0
|
|
while( ++cnt1 < 3 ) {
|
|
var cnt2 = initialForCnt2
|
|
|
|
assert(cnt2 == 0)
|
|
assert(outer == 7)
|
|
|
|
while(++cnt2 < 5) {
|
|
assert(initialForCnt2 == 0)
|
|
var outer = 1
|
|
sum = sum + outer
|
|
}
|
|
}
|
|
println("sum "+sum)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun whileNonLocalBreakTest() = runTest {
|
|
assertEquals(
|
|
"ok2:3:7", eval(
|
|
"""
|
|
var t1 = 10
|
|
outer@ while( t1 > 0 ) {
|
|
var t2 = 10
|
|
while( t2 > 0 ) {
|
|
t2 = t2 - 1
|
|
if( t2 == 3 && t1 == 7) {
|
|
break@outer "ok2:"+t2+":"+t1
|
|
}
|
|
}
|
|
--t1
|
|
}
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun bookTest0() = runTest {
|
|
assertEquals(
|
|
"just 3",
|
|
eval(
|
|
"""
|
|
val count = 3
|
|
val res = if( count > 10 ) "too much" else "just " + count
|
|
res
|
|
""".trimIndent()
|
|
)
|
|
.toString()
|
|
)
|
|
assertEquals(
|
|
"just 3",
|
|
eval(
|
|
"""
|
|
val count = 3
|
|
var res = if( count > 10 ) "too much" else "it's " + count
|
|
res = if( count > 10 ) "too much" else "just " + count
|
|
res
|
|
""".trimIndent()
|
|
)
|
|
.toString()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIncr() = runTest {
|
|
val c = Scope()
|
|
c.eval("var x = 10")
|
|
assertEquals(10, c.eval("x++").toInt())
|
|
assertEquals(11, c.eval("x++").toInt())
|
|
assertEquals(12, c.eval("x").toInt())
|
|
|
|
assertEquals(12, c.eval("x").toInt())
|
|
assertEquals(12, c.eval("x").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testDecr() = runTest {
|
|
val c = Scope()
|
|
c.eval("var x = 9")
|
|
assertEquals(9, c.eval("println(x); val a = x--; println(x); println(a); a").toInt())
|
|
assertEquals(8, c.eval("x--").toInt())
|
|
assertEquals(7, c.eval("x--").toInt())
|
|
assertEquals(6, c.eval("x--").toInt())
|
|
assertEquals(5, c.eval("x").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testDecrIncr() = runTest {
|
|
val c = Scope()
|
|
c.eval("var x = 9")
|
|
assertEquals(9, c.eval("x++").toInt())
|
|
assertEquals(10, c.eval("x++").toInt())
|
|
assertEquals(11, c.eval("x").toInt())
|
|
assertEquals(11, c.eval("x--").toInt())
|
|
assertEquals(10, c.eval("x--").toInt())
|
|
assertEquals(9, c.eval("x--").toInt())
|
|
assertEquals(8, c.eval("x--").toInt())
|
|
assertEquals(7, c.eval("x + 0").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testDecrIncr2() = runTest {
|
|
val c = Scope()
|
|
c.eval("var x = 9")
|
|
assertEquals(9, c.eval("x--").toInt())
|
|
assertEquals(8, c.eval("x--").toInt())
|
|
assertEquals(7, c.eval("x--").toInt())
|
|
assertEquals(6, c.eval("x").toInt())
|
|
assertEquals(6, c.eval("x++").toInt())
|
|
assertEquals(7, c.eval("x++").toInt())
|
|
assertEquals(8, c.eval("x")
|
|
.also {
|
|
println("${it.toDouble()} ${it.toInt()} ${it.toLong()} ${it.toInt()}")
|
|
}
|
|
.toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testDecrIncr3() = runTest {
|
|
val c = Scope()
|
|
c.eval("var x = 9")
|
|
assertEquals(9, c.eval("x++").toInt())
|
|
assertEquals(10, c.eval("x++").toInt())
|
|
assertEquals(11, c.eval("x++").toInt())
|
|
assertEquals(12, c.eval("x").toInt())
|
|
assertEquals(12, c.eval("x--").toInt())
|
|
assertEquals(11, c.eval("x").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testIncrAndDecr() = runTest {
|
|
val c = Scope()
|
|
assertEquals(
|
|
"8", c.eval(
|
|
"""
|
|
var x = 5
|
|
x--
|
|
x--
|
|
x++
|
|
x * 2
|
|
"""
|
|
).toString()
|
|
)
|
|
|
|
assertEquals("4", c.eval("x").toString())
|
|
// assertEquals( "8", c.eval("x*2").toString())
|
|
// assertEquals( "4", c.eval("x+0").toString())
|
|
}
|
|
|
|
@Test
|
|
fun bookTest2() = runTest {
|
|
val src = """
|
|
fn check(amount, prefix = "answer: ") {
|
|
prefix + if( amount > 100 )
|
|
"enough"
|
|
else
|
|
"more"
|
|
|
|
}
|
|
""".trimIndent()
|
|
eval(src)
|
|
}
|
|
|
|
@Test
|
|
fun testAssign1() = runTest {
|
|
assertEquals(10, eval("var x = 5; x=10; x").toInt())
|
|
val ctx = Scope()
|
|
ctx.eval(
|
|
"""
|
|
var a = 1
|
|
var b = 1
|
|
""".trimIndent()
|
|
)
|
|
assertEquals(3, ctx.eval("a + a + 1").toInt())
|
|
assertEquals(12, ctx.eval("a + (b = 10) + 1").toInt())
|
|
assertEquals(10, ctx.eval("b").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testAssign2() = runTest {
|
|
val ctx = Scope()
|
|
ctx.eval("var x = 10")
|
|
assertEquals(14, ctx.eval("x += 4").toInt())
|
|
assertEquals(14, ctx.eval("x").toInt())
|
|
assertEquals(12, ctx.eval("x -= 2").toInt())
|
|
assertEquals(12, ctx.eval("x").toInt())
|
|
|
|
assertEquals(24, ctx.eval("x *= 2").toInt())
|
|
assertEquals(24, ctx.eval("x").toInt())
|
|
|
|
assertEquals(12, ctx.eval("x /= 2").toInt())
|
|
assertEquals(12, ctx.eval("x").toInt())
|
|
|
|
assertEquals(2, ctx.eval("x %= 5").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testVals() = runTest {
|
|
val cxt = Scope()
|
|
cxt.eval("val x = 11")
|
|
assertEquals(11, cxt.eval("x").toInt())
|
|
assertFails { cxt.eval("x = 12") }
|
|
assertFails { cxt.eval("x += 12") }
|
|
assertFails { cxt.eval("x -= 12") }
|
|
assertFails { cxt.eval("x *= 2") }
|
|
assertFails { cxt.eval("x /= 2") }
|
|
assertFails { cxt.eval("x++") }
|
|
assertFails { cxt.eval("++x") }
|
|
assertFails { cxt.eval("x--") }
|
|
assertFails { cxt.eval("--x") }
|
|
assertEquals(11, cxt.eval("x").toInt())
|
|
}
|
|
|
|
@Test
|
|
fun testValVarConverting() = runTest {
|
|
eval(
|
|
"""
|
|
val x = 5
|
|
var y = x
|
|
y = 1
|
|
assert(x == 5)
|
|
""".trimIndent()
|
|
)
|
|
assertFails {
|
|
eval(
|
|
"""
|
|
val x = 5
|
|
fun fna(t) {
|
|
t = 11
|
|
}
|
|
fna(1)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
eval(
|
|
"""
|
|
var x = 5
|
|
val y = x
|
|
x = 10
|
|
assert(y == 5)
|
|
assert(x == 10)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testListLiteral() = runTest {
|
|
eval(
|
|
"""
|
|
val list = [1,22,3]
|
|
assert(list[0] == 1)
|
|
assert(list[1] == 22)
|
|
assert(list[2] == 3)
|
|
""".trimIndent()
|
|
)
|
|
|
|
eval(
|
|
"""
|
|
val x0 = 100
|
|
val list = [x0 + 1, x0 * 10, 3]
|
|
assert(list[0] == 101)
|
|
assert(list[1] == 1000)
|
|
assert(list[2] == 3)
|
|
""".trimIndent()
|
|
)
|
|
|
|
eval(
|
|
"""
|
|
val x0 = 100
|
|
val list = [x0 + 1, x0 * 10, if(x0 < 100) "low" else "high", 5]
|
|
assert(list[0] == 101)
|
|
assert(list[1] == 1000)
|
|
assert(list[2] == "high")
|
|
assert(list[3] == 5)
|
|
""".trimIndent()
|
|
)
|
|
|
|
}
|
|
|
|
@Test
|
|
fun testListLiteralSpread() = runTest {
|
|
eval(
|
|
"""
|
|
val list1 = [1,22,3]
|
|
val list = ["start", ...list1, "end"]
|
|
assert(list[0] == "start")
|
|
assert(list[1] == 1)
|
|
assert(list[2] == 22)
|
|
assert(list[3] == 3)
|
|
assert(list[4] == "end")
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testListSize() = runTest {
|
|
eval(
|
|
"""
|
|
val a = [4,3]
|
|
assert(a.size == 2)
|
|
assert( 3 == a[1] )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testArrayCompare() = runTest {
|
|
eval(
|
|
"""
|
|
val a = [4,3]
|
|
val b = [4,3]
|
|
assert(a == b)
|
|
assert( a === a )
|
|
assert( !(a === b) )
|
|
assert( a !== b )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun forLoop1() = runTest {
|
|
eval(
|
|
"""
|
|
var sum = 0
|
|
for(i in [1,2,3]) {
|
|
println(i)
|
|
sum += i
|
|
}
|
|
assert(sum == 6)
|
|
""".trimIndent()
|
|
)
|
|
eval(
|
|
"""
|
|
fun test1(array) {
|
|
var sum = 0
|
|
for(i in array) {
|
|
if( i > 2 ) break "too much"
|
|
sum += i
|
|
}
|
|
}
|
|
println("result=",test1([1,2]))
|
|
println("result=",test1([1,2,3]))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun forLoop2() = runTest {
|
|
println(
|
|
eval(
|
|
"""
|
|
fun search(haystack, needle) {
|
|
for(ch in haystack) {
|
|
if( ch == needle)
|
|
break "found"
|
|
}
|
|
else null
|
|
}
|
|
assert( search("hello", 'l') == "found")
|
|
assert( search("hello", 'z') == null)
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIntClosedRangeInclusive() = runTest {
|
|
eval(
|
|
"""
|
|
val r = 10 .. 20
|
|
assert( r::class == Range)
|
|
assert(r.isOpen == false)
|
|
assert(r.start == 10)
|
|
assert(r.end == 20)
|
|
assert(r.isEndInclusive == true)
|
|
assert(r.isIntRange)
|
|
|
|
assert(12 in r)
|
|
assert(10 in r)
|
|
assert(20 in r)
|
|
|
|
assert(9 !in r)
|
|
assert(21 !in r)
|
|
|
|
assert( (11..12) in r)
|
|
assert( (10..11) in r)
|
|
assert( (11..20) in r)
|
|
assert( (10..20) in r)
|
|
|
|
assert( (9..12) !in r)
|
|
assert( (1..9) !in r)
|
|
assert( (17..22) !in r)
|
|
assert( (21..22) !in r)
|
|
|
|
// assert(r.size == 11)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIntClosedRangeExclusive() = runTest {
|
|
eval(
|
|
"""
|
|
val r = 10 ..< 20
|
|
assert( r::class == Range)
|
|
assert(r.isOpen == false)
|
|
assert(r.start == 10)
|
|
assert(r.end == 20)
|
|
assert(r.isEndInclusive == false)
|
|
assert(r.isIntRange)
|
|
|
|
assert(12 in r)
|
|
assert(10 in r)
|
|
assert(20 !in r)
|
|
|
|
assert(9 !in r)
|
|
assert(21 !in r)
|
|
|
|
assert( (11..12) in r)
|
|
assert( (10..11) in r)
|
|
assert( (11..20) !in r)
|
|
assert( (10..20) !in r)
|
|
|
|
assert( (10..<20) in r)
|
|
|
|
assert( (9..12) !in r)
|
|
assert( (1..9) !in r)
|
|
assert( (17..22) !in r)
|
|
assert( (21..22) !in r)
|
|
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIntClosedRangeInExclusive() = runTest {
|
|
eval(
|
|
"""
|
|
assert( (1..3) !in (1..<3) )
|
|
assert( (1..<3) in (1..3) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testOpenStartRanges() = runTest {
|
|
eval(
|
|
"""
|
|
var r = ..5
|
|
assert( r::class == Range)
|
|
assert( r.start == null)
|
|
assert( r.end == 5)
|
|
assert( r.isEndInclusive)
|
|
|
|
r = ..< 5
|
|
assert( r::class == Range)
|
|
assert( r.start == null)
|
|
assert( r.end == 5)
|
|
assert( !r.isEndInclusive)
|
|
|
|
assert( r.start == null)
|
|
|
|
assert( (-2..3) in r)
|
|
assert( (-2..12) !in r)
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testOpenEndRanges() = runTest {
|
|
eval(
|
|
"""
|
|
var r = 5..
|
|
assert( r::class == Range)
|
|
assert( r.end == null)
|
|
assert( r.start == 5)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testOpenEndRanges2() = runTest {
|
|
eval(
|
|
"""
|
|
var r = 5..; var r2 = 6..
|
|
val r3 = 7.. // open end
|
|
assert( r::class == Range)
|
|
assert( r.end == null)
|
|
assert( r.start == 5)
|
|
|
|
assert( r3::class == Range)
|
|
assertEquals( r3.end, null)
|
|
assert( r3.start == 7)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testOpenEndRanges3() = runTest {
|
|
eval(
|
|
"""
|
|
val r3 = 7.. // open end with comment
|
|
assert( r3::class == Range)
|
|
assertEquals( r3.end, null)
|
|
assert( r3.start == 7)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testCharacterRange() = runTest {
|
|
eval(
|
|
"""
|
|
val x = '0'..'9'
|
|
println(x)
|
|
assert( '5' in x)
|
|
assert( 'z' !in x)
|
|
for( ch in x )
|
|
println(ch)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIs() = runTest {
|
|
eval(
|
|
"""
|
|
val x = 1..10
|
|
assert( x is Range )
|
|
assert( x is Iterable )
|
|
assert( x !is String)
|
|
assert( "foo" is String)
|
|
|
|
assert( x is Iterable )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testForRange() = runTest {
|
|
eval(
|
|
"""
|
|
val x = 1..3
|
|
val result = []
|
|
for( i in x ) {
|
|
println(i)
|
|
result += (i*10)
|
|
}
|
|
assert( result == [10,20,30] )
|
|
""".trimIndent()
|
|
)
|
|
val a = mutableListOf(1, 2)
|
|
val b = listOf(3, 4)
|
|
a += 10
|
|
a += b
|
|
println(a)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaWithIt1() = runTest {
|
|
eval(
|
|
"""
|
|
val x = {
|
|
it + "!"
|
|
}
|
|
val y = if( 4 < 3 ) "NG" else "OK"
|
|
assert( x::class == Callable)
|
|
assert( x is Callable)
|
|
assert(y == "OK")
|
|
assert( x("hello") == "hello!")
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaWithIt2() = runTest {
|
|
eval(
|
|
"""
|
|
val x = {
|
|
assert(it == void)
|
|
}
|
|
assert( x() == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaWithIt3() = runTest {
|
|
eval(
|
|
"""
|
|
val x = {
|
|
assert( it == [1,2,"end"])
|
|
}
|
|
println("0----")
|
|
assert( x(1, 2, "end") == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaWithArgs() = runTest {
|
|
eval(
|
|
"""
|
|
val x = { x, y, z ->
|
|
println("-- x=",x)
|
|
println("-- y=",y)
|
|
println("-- z=",z)
|
|
println([x,y,z])
|
|
assert( [x, y, z] == [1,2,"end"])
|
|
println("----:")
|
|
}
|
|
assert( x(1, 2, "end") == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testCaptureLocals() = runTest {
|
|
eval(
|
|
"""
|
|
|
|
fun outer(prefix) {
|
|
val p1 = "0" + prefix
|
|
{
|
|
p1 + "2" + it
|
|
}
|
|
}
|
|
fun outer2(prefix) {
|
|
val p1 = "*" + prefix
|
|
{
|
|
p1 + "2" + it
|
|
}
|
|
}
|
|
val x = outer("1")
|
|
val y = outer2("1")
|
|
println(x("!"))
|
|
assertEquals( "0123", x("3") )
|
|
assertEquals( "*123", y("3") )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testInstanceCallScopeIsCorrect() = runTest {
|
|
eval(
|
|
"""
|
|
|
|
val prefix = ":"
|
|
|
|
class T(text) {
|
|
fun getText() {
|
|
println(text)
|
|
prefix + text + "!"
|
|
}
|
|
}
|
|
|
|
val text = "invalid"
|
|
|
|
val t1 = T("foo")
|
|
val t2 = T("bar")
|
|
|
|
// get inside the block
|
|
for( i in 1..3 ) {
|
|
assertEquals( "foo", t1.text )
|
|
assertEquals( ":foo!", t1.getText() )
|
|
assertEquals( "bar", t2.text )
|
|
assertEquals( ":bar!", t2.getText() )
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testAppliedScopes() = runTest {
|
|
eval(
|
|
"""
|
|
class T(text) {
|
|
fun getText() {
|
|
println(text)
|
|
text + "!"
|
|
}
|
|
}
|
|
|
|
val prefix = ":"
|
|
|
|
val text = "invalid"
|
|
val t1 = T("foo")
|
|
val t2 = T("bar")
|
|
|
|
t1.apply {
|
|
// it must take "text" from class t1:
|
|
assertEquals("foo", text)
|
|
assertEquals( "foo!", getText() )
|
|
assertEquals( ":foo!!", {
|
|
prefix + getText() + "!"
|
|
}())
|
|
}
|
|
t2.apply {
|
|
assertEquals("bar", text)
|
|
assertEquals( "bar!", getText() )
|
|
assertEquals( ":bar!!", {
|
|
prefix + getText() + "!"
|
|
}())
|
|
}
|
|
// worst case: names clash
|
|
fun badOne() {
|
|
val prefix = "&"
|
|
t1.apply {
|
|
assertEquals( ":foo!!", prefix + getText() + "!" )
|
|
}
|
|
}
|
|
badOne()
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaWithArgsEllipsis() = runTest {
|
|
eval(
|
|
"""
|
|
val x = { x, y... ->
|
|
println("-- y=",y)
|
|
println(":: "+y::class)
|
|
assert( [x, ...y] == [1,2,"end"])
|
|
}
|
|
assert( x(1, 2, "end") == void)
|
|
assert( x(1, ...[2, "end"]) == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaWithBadArgs() = runTest {
|
|
assertFails {
|
|
eval(
|
|
"""
|
|
val x = { x, y ->
|
|
void
|
|
}
|
|
assert( x(1, 2) == void)
|
|
assert( x(1, ...[2, "end"]) == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun testWhileExecuteElseIfNotExecuted() = runTest {
|
|
assertEquals(
|
|
"ok",
|
|
eval(
|
|
"""
|
|
while( 5 < 1 ) {
|
|
"bad"
|
|
} else "ok"
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIsPrimeSampleBug() = runTest {
|
|
eval(
|
|
"""
|
|
fun naive_is_prime(candidate) {
|
|
val x = if( candidate !is Int) candidate.toInt() else candidate
|
|
var divisor = 1
|
|
println("start with ",x)
|
|
while( ++divisor < x/2 && divisor != 2 ) {
|
|
println("x=", x, " // ", divisor, " :: ", x % divisor)
|
|
if( x % divisor == 0 ) break false
|
|
}
|
|
else true
|
|
}
|
|
naive_is_prime(4)
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLambdaAsFnCallArg() = runTest {
|
|
eval(
|
|
"""
|
|
fun mapValues(iterable, transform) {
|
|
println("start: ", transform)
|
|
var result = []
|
|
for( x in iterable ) result += transform(x)
|
|
}
|
|
assert( [11, 21, 31] == mapValues( if( true) [1,2,3] else [10], { it*10+1 }))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testNewFnParser() = runTest {
|
|
eval(
|
|
"""
|
|
fun f1(a,b) { a + b }
|
|
println(f1(1,2))
|
|
assertEquals( 7, f1(3,4) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSpoilArgsBug() = runTest {
|
|
eval(
|
|
"""
|
|
fun fnb(a,b) { a + b }
|
|
|
|
fun fna(a, b) {
|
|
val a0 = a
|
|
val b0 = b
|
|
fnb(a + 1, b + 1)
|
|
assert( a0 == a )
|
|
assert( b0 == b )
|
|
}
|
|
|
|
fna(5,6)
|
|
"""
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSpoilLamdaArgsBug() = runTest {
|
|
eval(
|
|
"""
|
|
val fnb = { a,b -> a + b }
|
|
|
|
val fna = { a, b ->
|
|
val a0 = a
|
|
val b0 = b
|
|
fnb(a + 1, b + 1)
|
|
assert( a0 == a )
|
|
assert( b0 == b )
|
|
}
|
|
|
|
fna(5,6)
|
|
"""
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun commentBlocksShouldNotAlterBehavior() = runTest {
|
|
eval(
|
|
"""
|
|
fun test() {
|
|
10
|
|
/*
|
|
*/
|
|
//val x = 11
|
|
}
|
|
assert( test() == 10 )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testShuttle() = runTest {
|
|
eval(
|
|
"""
|
|
assert( 5 <=> 3 > 0 )
|
|
assert( 0 < 5 <=> 3 )
|
|
assert( 5 <=> 5 == 0 )
|
|
assert( 5 <=> 7 < 0 )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSimpleStruct() = runTest {
|
|
val c = Scope()
|
|
c.eval(
|
|
"""
|
|
class Point(x,y)
|
|
assert( Point::class is Class )
|
|
val p = Point(2,3)
|
|
assert(p is Point)
|
|
println(p)
|
|
println(p.x)
|
|
assert( p.x == 2 )
|
|
assert( p.y == 3 )
|
|
|
|
val p2 = Point(p.x+1,p.y+1)
|
|
p.x = 0
|
|
assertEquals( 0, p.x )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testNonAssignalbeFieldInStruct() = runTest {
|
|
val c = Scope()
|
|
c.eval(
|
|
"""
|
|
class Point(x,y)
|
|
val p = Point("2",3)
|
|
assert(p is Point)
|
|
assert( p.x == "2" )
|
|
assert( p.y == 3 )
|
|
|
|
p.x = 0
|
|
assertEquals( 0, p.x )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testStructBodyVal() = runTest {
|
|
val c = Scope()
|
|
c.eval(
|
|
"""
|
|
class Point(x,y) {
|
|
val length = sqrt(x*x+y*y)
|
|
var foo = "zero"
|
|
}
|
|
val p = Point(3,4)
|
|
assertEquals(5, p.length)
|
|
assertEquals("zero", p.foo)
|
|
p.y = 10
|
|
p.foo = "bar"
|
|
assert( p.y == 10 )
|
|
assert( p.foo == "bar")
|
|
// length is a val, is shoud not change
|
|
assert( p.length == 5 )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testStructBodyFun() = runTest {
|
|
val c = Scope()
|
|
c.eval(
|
|
"""
|
|
class Point(x,y) {
|
|
fun length() {
|
|
sqrt(x*x+y*y)
|
|
}
|
|
var foo = "zero"
|
|
}
|
|
val p = Point(3,4)
|
|
assertEquals(5, p.length())
|
|
p.y = 10
|
|
println(p.length())
|
|
assertEquals(sqrt(109), p.length())
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testPrivateConstructorParams() = runTest {
|
|
val c = Scope()
|
|
c.eval(
|
|
"""
|
|
class Point(private var x,y)
|
|
val p = Point(1,2)
|
|
p.y = 101
|
|
assertThrows { p.x = 10 }
|
|
"""
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLBraceMethodCall() = runTest {
|
|
eval(
|
|
"""
|
|
class Foo() {
|
|
fun cond(block) {
|
|
block()
|
|
}
|
|
}
|
|
val f = Foo()
|
|
assertEquals( 1, f.cond { 1 } )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLBraceFnCall() = runTest {
|
|
eval(
|
|
"""
|
|
fun cond(block) {
|
|
block()
|
|
}
|
|
assertEquals( 1, cond { 1 } )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testClasstoString() = runTest {
|
|
eval(
|
|
"""
|
|
class Point {
|
|
var x
|
|
var y
|
|
}
|
|
val p = Point()
|
|
p.x = 1
|
|
p.y = 2
|
|
println(p)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testClassDefaultCompare() = runTest {
|
|
eval(
|
|
"""
|
|
class Point(x,y)
|
|
assert( Point(1,2) == Point(1,2) )
|
|
assert( Point(1,2) !== Point(1,2) )
|
|
assert( Point(1,2) != Point(1,3) )
|
|
assert( Point(1,2) < Point(2,2) )
|
|
assert( Point(1,2) < Point(1,3) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testAccessShortcuts() {
|
|
assertTrue(Visibility.Public.isPublic)
|
|
assertFalse(Visibility.Private.isPublic)
|
|
assertFalse(Visibility.Protected.isPublic)
|
|
}
|
|
|
|
@Test
|
|
fun segfault1Test() = runTest {
|
|
eval(
|
|
"""
|
|
|
|
fun findSumLimit(f) {
|
|
var sum = 0.0
|
|
for( n in 1..1000000 ) {
|
|
val s0 = sum
|
|
sum += f(n)
|
|
if( abs(sum - s0) < 0.00001 ) {
|
|
println("limit reached after "+n+" rounds")
|
|
break sum
|
|
}
|
|
n++
|
|
}
|
|
else {
|
|
println("limit not reached")
|
|
null
|
|
}
|
|
}
|
|
|
|
val limit = findSumLimit { n -> 1.0/n/n }
|
|
|
|
println("Result: "+limit)
|
|
"""
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIntExponentRealForm() = runTest {
|
|
assertEquals("1.0E-6", eval("1e-6").toString())
|
|
}
|
|
|
|
@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()
|
|
)
|
|
|
|
}
|
|
|
|
@Test
|
|
fun nationalCharsTest() = runTest {
|
|
eval(
|
|
"""
|
|
fun сумма_ряда(x, погрешность=0.0001, f) {
|
|
var сумма = 0
|
|
for( n in 1..100000) {
|
|
val следующая_сумма = сумма + f(x, n)
|
|
if( n > 1 && abs(следующая_сумма - сумма) < погрешность )
|
|
break следующая_сумма
|
|
сумма = следующая_сумма
|
|
}
|
|
else null
|
|
}
|
|
val x = сумма_ряда(1) { x, n ->
|
|
val sign = if( n % 2 == 1 ) 1 else -1
|
|
sign * pow(x, n) / n
|
|
}
|
|
assert( x - ln(2) < 0.001 )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun doWhileSimpleTest() = runTest {
|
|
eval(
|
|
"""
|
|
var sum = 0
|
|
var x = do {
|
|
val s = sum
|
|
sum += 1
|
|
} while( s < 10 )
|
|
assertEquals(11, x)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testFailDoWhileSample1() = runTest {
|
|
eval(
|
|
"""
|
|
fun readLine() { "done: result" }
|
|
val result = do {
|
|
val line = readLine()
|
|
} while( !line.startsWith("done:") )
|
|
assertEquals("result", result.drop(6))
|
|
result
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testForContinue() = runTest {
|
|
eval(
|
|
"""
|
|
var x = 0
|
|
for( i in 1..10 ) {
|
|
if( i % 2 == 0 ) continue
|
|
println(i)
|
|
x++
|
|
}
|
|
assertEquals(5, x)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testForLabelNreakTest() = runTest {
|
|
eval(
|
|
"""
|
|
var x = 0
|
|
var y = 0
|
|
FOR0@ for( i in 1..10 ) {
|
|
x = i
|
|
for( j in 1..20 ) {
|
|
y = j
|
|
if( i == 3 && j == 5 ) {
|
|
println("bb")
|
|
break@FOR0
|
|
}
|
|
}
|
|
}
|
|
assertEquals( 5, y )
|
|
assertEquals( 3, x )
|
|
"""
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testThrowExisting() = runTest {
|
|
eval(
|
|
"""
|
|
val x = IllegalArgumentException("test")
|
|
assert( x is Exception )
|
|
|
|
var t = 0
|
|
var finallyCaught = false
|
|
try {
|
|
t = 1
|
|
throw x
|
|
t = 2
|
|
}
|
|
catch( e: SymbolNotDefinedException ) {
|
|
t = 101
|
|
}
|
|
catch( e: IllegalArgumentException ) {
|
|
t = 3
|
|
}
|
|
finally {
|
|
finallyCaught = true
|
|
}
|
|
assertEquals(3, t)
|
|
assert(finallyCaught)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testCatchShort1() = runTest {
|
|
eval(
|
|
"""
|
|
val x = IllegalArgumentException("test")
|
|
var t = 0
|
|
var finallyCaught = false
|
|
try {
|
|
t = 1
|
|
throw x
|
|
t = 2
|
|
}
|
|
catch(e) {
|
|
t = 31
|
|
}
|
|
finally {
|
|
finallyCaught = true
|
|
}
|
|
assertEquals(31, t)
|
|
assert(finallyCaught)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testCatchShort2() = runTest {
|
|
eval(
|
|
"""
|
|
val x = IllegalArgumentException("test")
|
|
var caught = null
|
|
try {
|
|
throw x
|
|
}
|
|
catch {
|
|
caught = it
|
|
}
|
|
assert( caught is IllegalArgumentException )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testAccessEHData() = runTest {
|
|
eval(
|
|
"""
|
|
val x = IllegalArgumentException("test")
|
|
val m = try {
|
|
throw x
|
|
null
|
|
}
|
|
catch(e) {
|
|
println(e)
|
|
println(e::class)
|
|
println(e.message)
|
|
println("--------------")
|
|
e.message
|
|
}
|
|
println(m)
|
|
assert( m == "test" )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testTryFinally() = runTest {
|
|
val c = Scope()
|
|
assertFails {
|
|
c.eval(
|
|
"""
|
|
var resource = "used"
|
|
try {
|
|
throw "test"
|
|
}
|
|
finally {
|
|
resource = "freed"
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
c.eval(
|
|
"""
|
|
assertEquals("freed", resource)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testThrowFromKotlin() = runTest {
|
|
val c = Script.newScope()
|
|
c.addFn("callThrow") {
|
|
raiseIllegalArgument("fromKotlin")
|
|
}
|
|
c.eval(
|
|
"""
|
|
val result = try {
|
|
callThrow()
|
|
"fail"
|
|
}
|
|
catch(e) {
|
|
println("caught:"+e)
|
|
println(e.message)
|
|
assert( e is IllegalArgumentException )
|
|
assertEquals("fromKotlin", e.message)
|
|
"ok"
|
|
}
|
|
assertEquals(result, "ok")
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testReturnValue1() = runTest {
|
|
val r = eval(
|
|
"""
|
|
class Point(x,y) {
|
|
println("1")
|
|
fun length() { sqrt(d2()) }
|
|
println("2")
|
|
private fun d2() {x*x + y*y}
|
|
println("3")
|
|
}
|
|
println("Helluva")
|
|
val p = Point(3,4)
|
|
// assertEquals( 5, p.length() )
|
|
// assertThrows { p.d2() }
|
|
"111"
|
|
""".trimIndent()
|
|
)
|
|
assertEquals("111", r.toString())
|
|
}
|
|
|
|
@Test
|
|
fun doWhileValuesTest() = runTest {
|
|
eval(
|
|
"""
|
|
var count = 0
|
|
val result = do {
|
|
count++
|
|
if( count < 10 )
|
|
continue
|
|
if( count % 2 == 1 )
|
|
break "found "+count
|
|
} while(count < 100)
|
|
else "no"
|
|
assertEquals("found 11", result)
|
|
""".trimIndent()
|
|
)
|
|
eval(
|
|
"""
|
|
var count = 0
|
|
val result = do {
|
|
count++
|
|
if( count < 10 )
|
|
continue
|
|
if( count % 2 == 1 )
|
|
break "found "+count
|
|
} while(count < 5)
|
|
else "no"
|
|
assertEquals("no", result)
|
|
""".trimIndent()
|
|
)
|
|
eval(
|
|
"""
|
|
var count = 0
|
|
val result = do {
|
|
count++
|
|
if( count % 2 == 3 )
|
|
break "found "+count
|
|
"proc "+count
|
|
} while(count < 5)
|
|
assertEquals("proc 5", result)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun doWhileValuesLabelTest() = runTest {
|
|
eval(
|
|
"""
|
|
var count = 0
|
|
var count2 = 0
|
|
var count3 = 0
|
|
val result = outer@ do {
|
|
count2++
|
|
count = 0
|
|
do {
|
|
count++
|
|
if( count < 10 || count2 < 5 ) {
|
|
continue
|
|
}
|
|
if( count % 2 == 1 )
|
|
break@outer "found "+count + "/" + count2
|
|
} while(count < 14)
|
|
count3++
|
|
} while( count2 < 100 )
|
|
else "no"
|
|
assertEquals("found 11/5", result)
|
|
assertEquals( 4, count3)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSimpleWhen() = runTest {
|
|
eval(
|
|
"""
|
|
var result = when("a") {
|
|
"a" -> "ok"
|
|
else -> "fail"
|
|
}
|
|
assertEquals(result, "ok")
|
|
result = when(5) {
|
|
3 -> "fail1"
|
|
4 -> "fail2"
|
|
else -> "ok2"
|
|
}
|
|
assert(result == "ok2")
|
|
result = when(5) {
|
|
3 -> "fail"
|
|
4 -> "fail2"
|
|
}
|
|
assert(result == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testWhenIs() = runTest {
|
|
eval(
|
|
"""
|
|
var result = when("a") {
|
|
is Int -> "fail2"
|
|
is String -> "ok"
|
|
else -> "fail"
|
|
}
|
|
assertEquals(result, "ok")
|
|
result = when(5) {
|
|
3 -> "fail1"
|
|
4 -> "fail2"
|
|
else -> "ok2"
|
|
}
|
|
assert(result == "ok2")
|
|
result = when(5) {
|
|
3 -> "fail"
|
|
4 -> "fail2"
|
|
}
|
|
assert(result == void)
|
|
result = when(5) {
|
|
!is String -> "ok"
|
|
4 -> "fail2"
|
|
}
|
|
assert(result == "ok")
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testWhenIn() = runTest {
|
|
eval(
|
|
"""
|
|
var result = when('e') {
|
|
in 'a'..'c' -> "fail2"
|
|
in 'a'..'z' -> "ok"
|
|
else -> "fail"
|
|
}
|
|
// assertEquals(result, "ok")
|
|
result = when(5) {
|
|
in [1,2,3,4,6] -> "fail1"
|
|
in [7, 0, 9] -> "fail2"
|
|
else -> "ok2"
|
|
}
|
|
assert(result == "ok2")
|
|
result = when(5) {
|
|
in [1,2,3,4,6] -> "fail1"
|
|
in [7, 0, 9] -> "fail2"
|
|
in [-1, 5, 11] -> "ok3"
|
|
else -> "fail3"
|
|
}
|
|
assert(result == "ok3")
|
|
result = when(5) {
|
|
!in [1,2,3,4,6, 5] -> "fail1"
|
|
!in [7, 0, 9, 5] -> "fail2"
|
|
!in [-1, 15, 11] -> "ok4"
|
|
else -> "fail3"
|
|
}
|
|
assert(result == "ok4")
|
|
result = when(5) {
|
|
in [1,3] -> "fail"
|
|
in 2..4 -> "fail2"
|
|
}
|
|
assert(result == void)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testWhenSample1() = runTest {
|
|
eval(
|
|
"""
|
|
fun type(x) {
|
|
when(x) {
|
|
in 'a'..'z', in 'A'..'Z' -> "letter"
|
|
in '0'..'9' -> "digit"
|
|
in "$%&" -> "hate char"
|
|
else -> "unknown"
|
|
}
|
|
}
|
|
assertEquals("digit", type('3'))
|
|
assertEquals("letter", type('E'))
|
|
assertEquals("hate char", type('%'))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testWhenSample2() = runTest {
|
|
eval(
|
|
"""
|
|
fun type(x) {
|
|
when(x) {
|
|
"42", 42 -> "answer to the great question"
|
|
is Real, is Int -> "number"
|
|
is String -> {
|
|
for( d in x ) {
|
|
if( d !in '0'..'9' )
|
|
break "unknown"
|
|
}
|
|
else "number"
|
|
}
|
|
}
|
|
}
|
|
assertEquals("number", type(5))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testNull1() = runTest {
|
|
eval(
|
|
"""
|
|
var s = null
|
|
assertThrows { s.length }
|
|
assertThrows { s.size() }
|
|
|
|
assertEquals( null, s?.size() )
|
|
assertEquals( null, s?.length )
|
|
assertEquals( null, s?.length ?{ "test" } )
|
|
assertEquals( null, s?[1] )
|
|
assertEquals( null, s ?{ "test" } )
|
|
|
|
s = "xx"
|
|
assert(s.lower().size == 2)
|
|
assert(s.length == 2)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSprintf() = runTest {
|
|
eval(
|
|
"""
|
|
assertEquals( "123.45", "%3.2f"(123.451678) )
|
|
assertEquals( "123.45: hello", "%3.2f: %s"(123.451678, "hello") )
|
|
assertEquals( "123.45: true", "%3.2f: %s"(123.451678, true) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSubstringRangeFailure() = runTest {
|
|
eval(
|
|
"""
|
|
assertEquals("pult", "catapult"[4..])
|
|
assertEquals("cat", "catapult"[..2])
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun passingOpenEndedRangeAsParam() = runTest {
|
|
eval(
|
|
"""
|
|
fun test(r) {
|
|
assert( r is Range )
|
|
}
|
|
test( 1.. )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testCollectionStructure() = runTest {
|
|
eval(
|
|
"""
|
|
val list = [1,2,3]
|
|
assert( 1 in list )
|
|
assert( list.indexOf(3) == 2 )
|
|
assert( list.indexOf(5) == -1 )
|
|
assert( list is List )
|
|
assert( list is Array )
|
|
assert( list is Iterable )
|
|
assert( list is Collection )
|
|
|
|
val other = []
|
|
list.forEach { other += it }
|
|
assertEquals( list, other )
|
|
|
|
assert( list.isEmpty() == false )
|
|
|
|
assertEquals( [10, 20, 30], list.map { it * 10 } )
|
|
assertEquals( [10, 20, 30], (1..3).map { it * 10 } )
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSet() = runTest {
|
|
eval(
|
|
"""
|
|
val set = Set(1,2,3)
|
|
|
|
assert( set.contains(1) )
|
|
assert( 1 in set )
|
|
|
|
assert(set is Set)
|
|
assert(set is Iterable)
|
|
assert(set is Collection)
|
|
println(set)
|
|
for( x in set ) println(x)
|
|
assert([1,2,3] == set.toList())
|
|
set += 4
|
|
assert(set.toList() == [1,2,3,4])
|
|
assert(set == Set(1,2,3,4))
|
|
|
|
val s1 = [1, 2].toSet()
|
|
assertEquals( Set(1,2), s1 * set)
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSet2() = runTest {
|
|
eval(
|
|
"""
|
|
assertEquals( Set( ...[1,2,3]), Set(1,2,3) )
|
|
assertEquals( Set( ...[1,false,"ok"]), Set("ok", 1, false) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testLet() = runTest {
|
|
eval(
|
|
"""
|
|
class Point(x=0,y=0)
|
|
assert( Point() is Object)
|
|
Point().let { println(it.x, it.y) }
|
|
val x = null
|
|
x?.let { println(it.x, it.y) }
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testApply() = runTest {
|
|
eval(
|
|
"""
|
|
class Point(x,y)
|
|
// see the difference: apply changes this to newly created Point:
|
|
val p = Point(1,2).apply {
|
|
x++; y++
|
|
}
|
|
assertEquals(p, Point(2,3))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testApplyThis() = runTest {
|
|
eval(
|
|
"""
|
|
class Point(x,y)
|
|
|
|
// see the difference: apply changes this to newly created Point:
|
|
val p = Point(1,2).apply {
|
|
this.x++
|
|
y++
|
|
}
|
|
assertEquals(p, Point(2,3))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testExtend() = runTest() {
|
|
eval(
|
|
"""
|
|
|
|
fun Int.isEven() {
|
|
this % 2 == 0
|
|
}
|
|
|
|
fun Object.isInteger() {
|
|
println(this)
|
|
println(this is Int)
|
|
println(this is Real)
|
|
println(this is String)
|
|
when(this) {
|
|
is Int -> true
|
|
is Real -> toInt() == this
|
|
is String -> toReal().isInteger()
|
|
else -> false
|
|
}
|
|
}
|
|
|
|
assert( 4.isEven() )
|
|
assert( !5.isEven() )
|
|
|
|
assert( 12.isInteger() == true )
|
|
assert( 12.1.isInteger() == false )
|
|
assert( "5".isInteger() )
|
|
assert( !"5.2".isInteger() )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testToFlow() = runTest() {
|
|
val c = Scope()
|
|
val arr = c.eval("[1,2,3]")
|
|
// array is iterable so we can:
|
|
assertEquals(listOf(1, 2, 3), arr.toFlow(c).map { it.toInt() }.toList())
|
|
}
|
|
|
|
@Test
|
|
fun testAssociateBy() = runTest() {
|
|
eval(
|
|
"""
|
|
val m = [123, 456].associateBy { "k:%s"(it) }
|
|
println(m)
|
|
assertEquals(123, m["k:123"])
|
|
assertEquals(456, m["k:456"])
|
|
"""
|
|
)
|
|
listOf(1, 2, 3).associateBy { it * 10 }
|
|
}
|
|
|
|
// @Test
|
|
// fun testImports1() = runTest() {
|
|
// val foosrc = """
|
|
// package lyng.foo
|
|
//
|
|
// fun foo() { "foo1" }
|
|
// """.trimIndent()
|
|
// val pm = InlineSourcesPacman(Pacman.emptyAllowAll, listOf(Source("foosrc", foosrc)))
|
|
// assertNotNull(pm.modules["lyng.foo"])
|
|
// assertIs<ModuleScope>(pm.modules["lyng.foo"]!!.deferredModule.await())
|
|
|
|
// assertEquals("foo1", pm.modules["lyng.foo"]!!.deferredModule.await().eval("foo()").toString())
|
|
// }
|
|
|
|
@Test
|
|
fun testImports2() = runTest() {
|
|
val foosrc = """
|
|
package lyng.foo
|
|
|
|
fun foo() { "foo1" }
|
|
""".trimIndent()
|
|
val pm = InlineSourcesImportProvider(listOf(Source("foosrc", foosrc)))
|
|
|
|
val src = """
|
|
import lyng.foo
|
|
|
|
foo()
|
|
""".trimIndent().toSource("test")
|
|
|
|
val scope = ModuleScope(pm, src)
|
|
assertEquals("foo1", scope.eval(src).toString())
|
|
}
|
|
|
|
@Test
|
|
fun testImports3() = runTest {
|
|
val foosrc = """
|
|
package lyng.foo
|
|
|
|
import lyng.bar
|
|
|
|
fun foo() { "foo1" }
|
|
""".trimIndent()
|
|
val barsrc = """
|
|
package lyng.bar
|
|
|
|
fun bar() { "bar1" }
|
|
""".trimIndent()
|
|
val pm = InlineSourcesImportProvider(
|
|
listOf(
|
|
Source("barsrc", barsrc),
|
|
Source("foosrc", foosrc),
|
|
)
|
|
)
|
|
|
|
val src = """
|
|
import lyng.foo
|
|
|
|
foo() + " / " + bar()
|
|
""".trimIndent().toSource("test")
|
|
|
|
val scope = ModuleScope(pm, src)
|
|
assertEquals("foo1 / bar1", scope.eval(src).toString())
|
|
}
|
|
|
|
@Test
|
|
fun testImportsCircular() = runTest {
|
|
val foosrc = """
|
|
package lyng.foo
|
|
|
|
import lyng.bar
|
|
|
|
fun foo() { "foo1" }
|
|
""".trimIndent()
|
|
val barsrc = """
|
|
package lyng.bar
|
|
|
|
import lyng.foo
|
|
|
|
fun bar() { "bar1" }
|
|
""".trimIndent()
|
|
val pm = InlineSourcesImportProvider(
|
|
listOf(
|
|
Source("barsrc", barsrc),
|
|
Source("foosrc", foosrc),
|
|
)
|
|
)
|
|
|
|
val src = """
|
|
import lyng.bar
|
|
|
|
foo() + " / " + bar()
|
|
""".trimIndent().toSource("test")
|
|
|
|
val scope = ModuleScope(pm, src)
|
|
assertEquals("foo1 / bar1", scope.eval(src).toString())
|
|
}
|
|
|
|
@Test
|
|
fun testDefaultImportManager() = runTest {
|
|
val scope = Scope.new()
|
|
assertFails {
|
|
scope.eval(
|
|
"""
|
|
import foo
|
|
foo()
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
scope.importManager.addTextPackages(
|
|
"""
|
|
package foo
|
|
|
|
fun foo() { "bar" }
|
|
""".trimIndent()
|
|
)
|
|
scope.eval(
|
|
"""
|
|
import foo
|
|
assertEquals( "bar", foo())
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testMaps() = runTest {
|
|
eval(
|
|
"""
|
|
val map = Map( "a" => 1, "b" => 2 )
|
|
assertEquals( 1, map["a"] )
|
|
assertEquals( 2, map["b"] )
|
|
assertEquals( null, map["c"] )
|
|
map["c"] = 3
|
|
assertEquals( 3, map["c"] )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testBuffer() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.buffer
|
|
|
|
assertEquals( 0, Buffer().size )
|
|
assertEquals( 3, Buffer(1, 2, 3).size )
|
|
assertEquals( 5, Buffer("hello").size )
|
|
|
|
var buffer = Buffer("Hello")
|
|
assertEquals( 5, buffer.size)
|
|
assertEquals('l'.code, buffer[2] )
|
|
assertEquals('l'.code, buffer[3] )
|
|
assertEquals("Hello", buffer.decodeUtf8())
|
|
|
|
buffer = buffer.toMutable()
|
|
|
|
buffer[2] = 101
|
|
assertEquals(101, buffer[2])
|
|
assertEquals("Heelo", buffer.decodeUtf8())
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testBufferEncodings() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.buffer
|
|
|
|
val b = Buffer("hello")
|
|
println(b.toDump())
|
|
assertEquals( "hello", b.decodeUtf8() )
|
|
|
|
println(b.base64)
|
|
println(b.hex)
|
|
|
|
assertEquals( b, Buffer.decodeBase64(b.base64) )
|
|
assertEquals( b, Buffer.decodeHex(b.hex) )
|
|
|
|
println(b.inspect())
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testBufferCompare() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.buffer
|
|
|
|
println("Hello".characters())
|
|
val b1 = Buffer("Hello")
|
|
val b2 = Buffer("Hello".characters())
|
|
|
|
assertEquals( b1, b2 )
|
|
val b3 = b1 + Buffer("!")
|
|
assertEquals( "Hello!", b3.decodeUtf8())
|
|
assert( b3 > b1 )
|
|
assert( b1 !== b2)
|
|
|
|
val map = Map( b1 => "foo")
|
|
assertEquals("foo", map[b1])
|
|
assertEquals("foo", map[b2])
|
|
assertEquals(null, map[b3])
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testInstant() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.time
|
|
|
|
val now = Instant()
|
|
// assertEquals( now.epochSeconds, Instant(now.epochSeconds).epochSeconds )
|
|
|
|
assert( 10.seconds is Duration )
|
|
assertEquals( 10.seconds, Duration(10) )
|
|
assertEquals( 10.milliseconds, Duration(0.01) )
|
|
assertEquals( 10.milliseconds, 0.01.seconds )
|
|
assertEquals( 1001.5.milliseconds, 1.0015.seconds )
|
|
|
|
val n1 = now + 7.seconds
|
|
assert( n1 is Instant )
|
|
|
|
assertEquals( n1 - now, 7.seconds )
|
|
assertEquals( now - n1, -7.seconds )
|
|
|
|
""".trimIndent()
|
|
)
|
|
delay(1000)
|
|
}
|
|
|
|
@Test
|
|
fun testTimeStatics() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.time
|
|
assert( 100.minutes is Duration )
|
|
assert( 100.days is Duration )
|
|
assert( 1.day == 24.hours )
|
|
assert( 1.day.hours == 24 )
|
|
assert( 1.hour.seconds == 3600 )
|
|
assert( 1.minute.milliseconds == 60_000 )
|
|
|
|
assert(Instant.distantFuture is Instant)
|
|
assert(Instant.distantPast is Instant)
|
|
assert( Instant.distantFuture - Instant.distantPast > 70_000_000.days)
|
|
val maxRange = Instant.distantFuture - Instant.distantPast
|
|
println("всего лет %g"(maxRange.days/365.2425))
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testInstantFormatting() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.time
|
|
val now = Instant()
|
|
val unixEpoch = "%ts"(now)
|
|
println("current seconds is %s"(unixEpoch))
|
|
println("current time is %tT"(now))
|
|
assertEquals( unixEpoch.toInt(), now.epochSeconds.toInt() )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testDoubleImports() = runTest {
|
|
val s = Scope.new()
|
|
println(Script.defaultImportManager.packageNames)
|
|
println(s.importManager.packageNames)
|
|
|
|
s.importManager.addTextPackages(
|
|
"""
|
|
package foo
|
|
|
|
import lyng.time
|
|
|
|
fun foo() {
|
|
println("foo: %s"(Instant()))
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
s.importManager.addTextPackages(
|
|
"""
|
|
package bar
|
|
|
|
import lyng.time
|
|
|
|
fun bar() {
|
|
println("bar: %s"(Instant()))
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
|
|
println(s.importManager.packageNames)
|
|
|
|
s.eval(
|
|
"""
|
|
import foo
|
|
import bar
|
|
|
|
foo()
|
|
bar()
|
|
|
|
""".trimIndent()
|
|
)
|
|
|
|
}
|
|
|
|
@Test
|
|
fun testIndexIntIncrements() = runTest {
|
|
eval(
|
|
"""
|
|
val x = [1,2,3]
|
|
x[1]++
|
|
++x[0]
|
|
assertEquals( [2,3,3], x )
|
|
|
|
import lyng.buffer
|
|
|
|
val b = MutableBuffer(1,2,3)
|
|
b[1]++
|
|
assert( b == Buffer(1,3,3) )
|
|
++b[0]
|
|
assertEquals( b, Buffer(2,3,3) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testIndexIntDecrements() = runTest {
|
|
eval(
|
|
"""
|
|
val x = [1,2,3]
|
|
x[1]--
|
|
--x[0]
|
|
assertEquals( [0,1,3], x )
|
|
|
|
import lyng.buffer
|
|
|
|
val b = Buffer(1,2,3).toMutable()
|
|
b[1]--
|
|
assert( b == Buffer(1,1,3) )
|
|
--b[0]
|
|
assertEquals( b, Buffer(0,1,3) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testRangeToList() = runTest {
|
|
val x = eval("""(1..10).toList()""") as ObjList
|
|
assertEquals(listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), x.list.map { it.toInt() })
|
|
val y = eval("""(-2..3).toList()""") as ObjList
|
|
println(y.list)
|
|
}
|
|
|
|
@Test
|
|
fun testMultilineStrings() = runTest {
|
|
assertEquals(
|
|
"""
|
|
This is a multiline text.
|
|
This is a second line.
|
|
""".trimIndent(), eval(
|
|
"""
|
|
"
|
|
This is a multiline text.
|
|
This is a second line.
|
|
"
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
assertEquals(
|
|
"""
|
|
This is a multiline text.
|
|
""".trimIndent(), eval(
|
|
"""
|
|
"
|
|
This is a multiline text.
|
|
"
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
assertEquals(
|
|
"""
|
|
|
|
This is a multiline text.
|
|
""".trimIndent(), eval(
|
|
"""
|
|
"
|
|
|
|
This is a multiline text.
|
|
"
|
|
""".trimIndent()
|
|
).toString()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun tesFunAnnotation() = runTest {
|
|
eval(
|
|
"""
|
|
|
|
val exportedSymbols = Map()
|
|
|
|
fun Exported(name, f, overrideName = null) {
|
|
assertEquals(name, "getBalance")
|
|
assertEquals(null, overrideName)
|
|
exportedSymbols[ overrideName ?: name ] = f
|
|
f
|
|
}
|
|
|
|
@Exported
|
|
fun getBalance(x = 0) {
|
|
121 + x
|
|
}
|
|
|
|
assert( exportedSymbols["getBalance"] != null )
|
|
assertEquals(122, getBalance(1))
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun enumTest() = runTest {
|
|
eval(
|
|
"""
|
|
enum Color {
|
|
RED, GREEN, BLUE
|
|
}
|
|
|
|
assert( Color.RED is Color )
|
|
assertEquals( 2, Color.BLUE.ordinal )
|
|
assertEquals( "BLUE", Color.BLUE.name )
|
|
|
|
assertEquals( [Color.RED,Color.GREEN,Color.BLUE], Color.entries)
|
|
|
|
assertEquals( Color.valueOf("GREEN"), Color.GREEN )
|
|
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun enumSerializationTest() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.serialization
|
|
|
|
enum Color {
|
|
RED, GREEN, BLUE
|
|
}
|
|
|
|
val e = Lynon.encode(Color.BLUE)
|
|
assertEquals( Color.BLUE, Lynon.decode(e) )
|
|
println(e.toDump())
|
|
|
|
val e1 = Lynon.encode( (1..1000).map { Color.GREEN } )
|
|
println(e1.toDump())
|
|
assert( e1.size / 1000.0 < 6)
|
|
println(Lynon.encode( (1..100).map { "RED" } ).toDump() )
|
|
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun cachedTest() = runTest {
|
|
eval(
|
|
"""
|
|
|
|
var counter = 0
|
|
var value = cached {
|
|
counter++
|
|
"ok"
|
|
}
|
|
|
|
assertEquals(0, counter)
|
|
assertEquals("ok", value())
|
|
assertEquals(1, counter)
|
|
assertEquals("ok", value())
|
|
assertEquals(1, counter)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testJoinToString() = runTest {
|
|
eval(
|
|
"""
|
|
assertEquals( (1..3).joinToString(), "1 2 3")
|
|
assertEquals( (1..3).joinToString(":"), "1:2:3")
|
|
assertEquals( (1..3).joinToString { it * 10 }, "10 20 30")
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testElvisAndThrow() = runTest {
|
|
eval(
|
|
"""
|
|
val x = assertThrows {
|
|
null ?: throw "test" + "x"
|
|
}
|
|
assertEquals( "testx", x.message)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testElvisAndThrow2() = runTest {
|
|
eval(
|
|
"""
|
|
val t = "112"
|
|
val x = t ?: run { throw "testx" }
|
|
}
|
|
assertEquals( "112", x)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testElvisAndRunThrow() = runTest {
|
|
eval(
|
|
"""
|
|
val x = assertThrows {
|
|
null ?: run { throw "testx" }
|
|
}
|
|
assertEquals( "testx", x.message)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testNewlinesAnsCommentsInExpressions() = runTest {
|
|
assertEquals(
|
|
2, (Scope().eval(
|
|
"""
|
|
val e = 1 + 4 -
|
|
3
|
|
""".trimIndent()
|
|
)).toInt()
|
|
)
|
|
|
|
eval(
|
|
"""
|
|
val x = [1,2,3]
|
|
.map { it * 10 }
|
|
.map { it + 1 }
|
|
assertEquals( [11,21,31], x)
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testNotExpressionWithoutWs() = runTest {
|
|
eval(
|
|
"""
|
|
fun test() { false }
|
|
class T(value)
|
|
assert( !false )
|
|
assert( !test() )
|
|
assert( !test() )
|
|
val t = T(false)
|
|
assert( !t.value )
|
|
assert( !if( true ) false else true )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testMultilineFnDeclaration() = runTest {
|
|
eval(
|
|
"""
|
|
fun test(
|
|
x = 1,
|
|
y = 2
|
|
) {
|
|
x * y
|
|
}
|
|
assertEquals( 10, test(5) )
|
|
assertEquals( 42, test(
|
|
6,
|
|
7
|
|
) )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testOverridenListToString() = runTest {
|
|
eval(
|
|
"""
|
|
val x = [1,2,3]
|
|
assertEquals( "[1,2,3]", x.toString() )
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testExceptionSerialization() = runTest {
|
|
eval(
|
|
"""
|
|
import lyng.serialization
|
|
val x = [1,2,3]
|
|
assertEquals( "[1,2,3]", x.toString() )
|
|
try {
|
|
require(false)
|
|
}
|
|
catch (e) {
|
|
println(e.stackTrace)
|
|
e.printStackTrace()
|
|
val coded = Lynon.encode(e)
|
|
val decoded = Lynon.decode(coded)
|
|
assertEquals( e::class, decoded::class )
|
|
assertEquals( e.stackTrace, decoded.stackTrace )
|
|
assertEquals( e.message, decoded.message )
|
|
println("-------------------- e")
|
|
println(e.toString())
|
|
println("-------------------- dee")
|
|
println(decoded.toString())
|
|
assertEquals( e.toString(), decoded.toString() )
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testThisInClosure() = runTest {
|
|
eval(
|
|
"""
|
|
fun Iterable.sum2by(f) {
|
|
var acc = null
|
|
for( x in this ) {
|
|
println(x)
|
|
println(f(x))
|
|
acc = acc?.let { acc + f(x) } ?: f(x)
|
|
}
|
|
}
|
|
class T(val coll, val factor) {
|
|
fun sum() {
|
|
// here we use ths::T and it must be available:
|
|
coll.sum2by { it * factor }
|
|
}
|
|
}
|
|
assertEquals(60, T([1,2,3], 10).sum())
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testThisInFlowClosure() = runTest {
|
|
eval(
|
|
"""
|
|
class T(val coll, val factor) {
|
|
fun seq() {
|
|
flow {
|
|
for( x in coll ) {
|
|
emit(x*factor)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assertEquals([10,20,30], T([1,2,3], 10).seq().toList())
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSum() = runTest {
|
|
eval(
|
|
"""
|
|
assertEquals(1, [1].sum())
|
|
assertEquals(null, [].sum())
|
|
assertEquals(6, [1,2,3].sum())
|
|
assertEquals(30, [3].sumOf { it * 10 })
|
|
assertEquals(null, [].sumOf { it * 10 })
|
|
assertEquals(60, [1,2,3].sumOf { it * 10 })
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testSort() = runTest {
|
|
eval("""
|
|
val coll = [5,4,1,7]
|
|
assertEquals( [1,4,5,7], coll.sortedWith { a,b -> a <=> b })
|
|
assertEquals( [1,4,5,7], coll.sorted())
|
|
assertEquals( [7,5,4,1], coll.sortedBy { -it })
|
|
assertEquals( [1,4,5,7], coll.sortedBy { -it }.reversed())
|
|
""".trimIndent()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun testListSortInPlace() = runTest {
|
|
eval("""
|
|
val l1 = [6,3,1,9]
|
|
l1.sort()
|
|
assertEquals( [1,3,6,9], l1)
|
|
l1.sortBy { -it }
|
|
assertEquals( [1,3,6,9].reversed(), l1)
|
|
l1.sort()
|
|
l1.sortBy { it % 4 }
|
|
// 1,3,6,9
|
|
// 1 3 2 1
|
|
// we hope we got it also stable:
|
|
assertEquals( [1,9,6,3], l1)
|
|
""")
|
|
}
|
|
} |