lyng/lynglib/src/commonTest/kotlin/ScriptTest.kt

2463 lines
65 KiB
Kotlin

import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.*
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
println("starting t2 = " + t2)
while( t2 > 0 ) {
t2 = t2 - 1
println("t2 " + t2 + " t1 " + t1)
if( t2 == 3 && t1 == 7) {
println("will break")
break@outer "ok2:"+t2+":"+t1
}
}
println("next t1")
t1 = t1 - 1
println("t1 now "+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
println(count)
println(res)
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("x--").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 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 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 = Scope()
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 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))
>>> void
""".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++; this.y++
}
assertEquals(p, Point(2,3))
>>> void
""".trimIndent()
)
}
@Test
fun testExtend() = runTest() {
eval(
"""
fun Int.isEven() {
this % 2 == 0
}
fun Object.isInteger() {
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()
assertFails {
scope.eval("""
import foo
foo()
""".trimIndent())
}
scope.importManager.addTextPackages("""
package foo
fun foo() { "bar" }
""".trimIndent())
scope.eval("""
import foo
assertEquals( "bar", foo())
""".trimIndent())
}
}