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

1197 lines
32 KiB
Kotlin

package io.github.kotlin.fibonacci
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.*
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),
parseLing(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),
parseLing(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")
// 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 = parseLing("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 = parseLing("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 = parseLing("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 = parseLing("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 = parseLing(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 = parseLing(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, 8), 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 = parseLing(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 context = Context(pos = Pos.builtIn)
assertEquals(
ObjVoid, context.eval(
"""
val a = 17
var b = 3
""".trimIndent()
)
)
assertEquals(17, context.eval("a").toInt())
assertEquals(20, context.eval("b + a").toInt())
assertFailsWith<ScriptError> {
context.eval("a = 10")
}
assertEquals(17, context.eval("a").toInt())
assertEquals(5, context.eval("b = a - 7 - 5").toInt())
assertEquals(5, context.eval("b").toInt())
}
@Test
fun functionTest() = runTest {
val context = Context(pos = Pos.builtIn)
context.eval(
"""
fun foo(a, b) {
a + b
}
""".trimIndent()
)
assertEquals(17, context.eval("foo(3,14)").toInt())
assertFailsWith<ScriptError> {
assertEquals(17, context.eval("foo(3)").toInt())
}
context.eval(
"""
fn bar(a, b=10) {
a + b + 1
}
""".trimIndent()
)
assertEquals(10, context.eval("bar(3, 6)").toInt())
assertEquals(14, context.eval("bar(3)").toInt())
}
@Test
fun simpleClosureTest() = runTest {
val context = Context(pos = Pos.builtIn)
context.eval(
"""
var global = 10
fun foo(a, b) {
global + a + b
}
""".trimIndent()
)
assertEquals(27, context.eval("foo(3,14)").toInt())
context.eval("global = 20")
assertEquals(37, context.eval("foo(3,14)").toInt())
}
@Test
fun nullAndVoidTest() = runTest {
val context = Context(pos = Pos.builtIn)
assertEquals(ObjVoid, context.eval("void"))
assertEquals(ObjNull, context.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(ObjBool(false), eval("val x = 3; x == 2"))
assertEquals(ObjBool(true), eval("val x = 3; x != 2"))
assertEquals(ObjBool(false), 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(ObjBool(false), eval("true && false"))
assertEquals(ObjBool(false), eval("false && false"))
assertEquals(ObjBool(false), eval("false && true"))
assertEquals(ObjBool(true), eval("true && true"))
assertEquals(ObjBool(true), eval("true || false"))
assertEquals(ObjBool(false), eval("false || false"))
assertEquals(ObjBool(true), eval("false || true"))
assertEquals(ObjBool(true), eval("true || true"))
assertEquals(ObjBool(false), 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 context = Context(pos = Pos.builtIn)
context.eval(
"""
fn test1(n) {
var result = "more"
if( n >= 10 )
result = "enough"
result
}
""".trimIndent()
)
assertEquals("enough", context.eval("test1(11)").toString())
assertEquals("more", context.eval("test1(1)").toString())
// if - multiline (block)
context = Context(pos = Pos.builtIn)
context.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", context.eval("test1(11)").toString())
assertEquals("answer: more", context.eval("test1(1)").toString())
// else single line1
context = Context(pos = Pos.builtIn)
context.eval(
"""
fn test1(n) {
if( n >= 10 )
"enough"
else
"more"
}
""".trimIndent()
)
assertEquals("enough", context.eval("test1(11)").toString())
assertEquals("more", context.eval("test1(1)").toString())
// if/else with blocks
context = Context(pos = Pos.builtIn)
context.eval(
"""
fn test1(n) {
if( n > 20 ) {
"too much"
} else if( n >= 10 ) {
"enough"
}
else {
"more"
}
}
""".trimIndent()
)
assertEquals("enough", context.eval("test1(11)").toString())
assertEquals("more", context.eval("test1(1)").toString())
assertEquals("too much", context.eval("test1(100)").toString())
}
@Test
fun lateInitTest() = runTest {
assertEquals(
"ok", eval(
"""
var late
fun init() {
late = "ok"
}
init()
late
""".trimIndent()
).toString()
)
}
@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 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 = Context()
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 = Context()
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 = Context()
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 = Context()
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 = Context()
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 = Context()
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 = Context()
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 = Context()
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 = Context()
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 testIntOpenRangeInclusive() = 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 testIntOpenRangeExclusive() = 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 testIntOpenRangeInExclusive() = runTest {
eval(
"""
assert( (1..3) !in (1..<3) )
assert( (1..<3) in (1..3) )
""".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 iterableList() = runTest {
// 473
eval(
"""
for( i in 0..<1024 ) {
val list = (1..1024).toList()
assert(list.size == 1024)
assert(list[0] == 1)
assert(list[-1] == 1024)
}
""".trimIndent()
)
}
@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 ->
assert( [x, y, z] == [1,2,"end"])
}
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) {
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 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)
"""
)
}
}