fix #14 when(value) with blows and whistles. Collection now is container (has Contains). greatly improved container properties of builtin classes.
This commit is contained in:
		
							parent
							
								
									7cc80e2433
								
							
						
					
					
						commit
						be4f2c7f45
					
				@ -552,6 +552,89 @@ Or, more neat:
 | 
			
		||||
    >>> just 3
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
## When
 | 
			
		||||
 | 
			
		||||
It is very much like the kotlin's:
 | 
			
		||||
 | 
			
		||||
    fun type(x) {
 | 
			
		||||
        when(x) {
 | 
			
		||||
            in 'a'..'z', in 'A'..'Z' -> "letter"
 | 
			
		||||
            in '0'..'9' -> "digit"
 | 
			
		||||
            '$' -> "dollar"
 | 
			
		||||
            "EUR" -> "crap"
 | 
			
		||||
            in ['@', '#', '^'] -> "punctuation1"
 | 
			
		||||
            in "*&.," -> "punctuation2"
 | 
			
		||||
            else -> "unknown"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assertEquals("digit", type('3'))
 | 
			
		||||
    assertEquals("dollar", type('$'))
 | 
			
		||||
    assertEquals("crap", type("EUR"))
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
Notice, several conditions can be grouped with a comma.
 | 
			
		||||
Also, you can check the type too:
 | 
			
		||||
 | 
			
		||||
    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))
 | 
			
		||||
    assertEquals("number", type("153"))
 | 
			
		||||
    assertEquals("number", type(π/2))
 | 
			
		||||
    assertEquals("unknown", type("12%"))
 | 
			
		||||
    assertEquals("answer to the great question", type(42))
 | 
			
		||||
    assertEquals("answer to the great question", type("42"))
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
### supported when conditions:
 | 
			
		||||
 | 
			
		||||
#### Contains: 
 | 
			
		||||
 | 
			
		||||
You can thest that _when expression_ is _contained_, or not contained, in some object using `in container` and `!in container`. The container is any object that provides `contains` method, otherwise the runtime exception will be thrown.
 | 
			
		||||
 | 
			
		||||
Typical builtin types that are containers (e.g. support `conain`):
 | 
			
		||||
 | 
			
		||||
| class      | notes                                      |
 | 
			
		||||
|------------|--------------------------------------------|
 | 
			
		||||
| Collection | contains an element (1)                    |
 | 
			
		||||
| Array      | faster maybe that Collection's             |
 | 
			
		||||
| List       | faster than Array's                        |
 | 
			
		||||
| String     | character in string or substring in string |
 | 
			
		||||
| Range      | object is included in the range (2)        |
 | 
			
		||||
 | 
			
		||||
(1)
 | 
			
		||||
: Iterable is not the container as it can be infinite
 | 
			
		||||
 | 
			
		||||
(2)
 | 
			
		||||
: Depending on the inclusivity and open/closed range parameters. BE careful here: String range is allowed, but it is usually not what you expect of it:
 | 
			
		||||
 | 
			
		||||
    assert( "more" in "a".."z") // string range ok
 | 
			
		||||
    assert( 'x' !in "a".."z") // char in string range: probably error
 | 
			
		||||
    assert( 'x' in 'a'..'z') // character range: ok
 | 
			
		||||
    assert( "x" !in 'a'..'z') // string in character range: could be error
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
So we recommend not to mix characters and string ranges; use `ch in str` that works
 | 
			
		||||
as expected:
 | 
			
		||||
 | 
			
		||||
    "foo" in "foobar"
 | 
			
		||||
    >>> true
 | 
			
		||||
 | 
			
		||||
and also character inclusion:
 | 
			
		||||
 | 
			
		||||
    'o' in "foobar"
 | 
			
		||||
    >>> true
 | 
			
		||||
 | 
			
		||||
## while
 | 
			
		||||
 | 
			
		||||
Regular pre-condition while loop, as expression, loop returns the last expression as everything else:
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
group = "net.sergeych"
 | 
			
		||||
version = "0.5.2-SNAPSHOT"
 | 
			
		||||
version = "0.6.0-SNAPSHOT"
 | 
			
		||||
 | 
			
		||||
buildscript {
 | 
			
		||||
    repositories {
 | 
			
		||||
 | 
			
		||||
@ -454,7 +454,7 @@ class Compiler(
 | 
			
		||||
 | 
			
		||||
                Token.Type.ID -> {
 | 
			
		||||
                    // visibility
 | 
			
		||||
                    val visibility = if( isClassDeclaration && t.value == "private" ) {
 | 
			
		||||
                    val visibility = if (isClassDeclaration && t.value == "private") {
 | 
			
		||||
                        t = cc.next()
 | 
			
		||||
                        Visibility.Private
 | 
			
		||||
                    } else Visibility.Public
 | 
			
		||||
@ -650,7 +650,7 @@ class Compiler(
 | 
			
		||||
                    "void" -> Accessor { ObjVoid.asReadonly }
 | 
			
		||||
                    "null" -> Accessor { ObjNull.asReadonly }
 | 
			
		||||
                    "true" -> Accessor { ObjBool(true).asReadonly }
 | 
			
		||||
                    "false" -> Accessor { ObjBool(false).asReadonly }
 | 
			
		||||
                    "false" -> Accessor { ObjFalse.asReadonly }
 | 
			
		||||
                    else -> {
 | 
			
		||||
                        Accessor({
 | 
			
		||||
                            it.pos = t.pos
 | 
			
		||||
@ -709,6 +709,7 @@ class Compiler(
 | 
			
		||||
        "class" -> parseClassDeclaration(cc, false)
 | 
			
		||||
        "try" -> parseTryStatement(cc)
 | 
			
		||||
        "throw" -> parseThrowStatement(cc)
 | 
			
		||||
        "when" -> parseWhenStatement(cc)
 | 
			
		||||
        else -> {
 | 
			
		||||
            // triples
 | 
			
		||||
            cc.previous()
 | 
			
		||||
@ -729,6 +730,113 @@ class Compiler(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data class WhenCase(val condition: Statement, val block: Statement)
 | 
			
		||||
 | 
			
		||||
    private fun parseWhenStatement(cc: CompilerContext): Statement {
 | 
			
		||||
        // has a value, when(value) ?
 | 
			
		||||
        var t = cc.skipWsTokens()
 | 
			
		||||
        return if (t.type == Token.Type.LPAREN) {
 | 
			
		||||
            // when(value)
 | 
			
		||||
            val value = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "when(value) expected")
 | 
			
		||||
            cc.skipTokenOfType(Token.Type.RPAREN)
 | 
			
		||||
            t = cc.next()
 | 
			
		||||
            if (t.type != Token.Type.LBRACE) throw ScriptError(t.pos, "when { ... } expected")
 | 
			
		||||
            val cases = mutableListOf<WhenCase>()
 | 
			
		||||
            var elseCase: Statement? = null
 | 
			
		||||
            lateinit var whenValue: Obj
 | 
			
		||||
 | 
			
		||||
            // there could be 0+ then clauses
 | 
			
		||||
            // condition could be a value, in and is clauses:
 | 
			
		||||
            // parse several conditions for one then clause
 | 
			
		||||
 | 
			
		||||
            // loop cases
 | 
			
		||||
            outer@ while (true) {
 | 
			
		||||
 | 
			
		||||
                var skipParseBody = false
 | 
			
		||||
                val currentCondition = mutableListOf<Statement>()
 | 
			
		||||
 | 
			
		||||
                // loop conditions
 | 
			
		||||
                while (true) {
 | 
			
		||||
                    t = cc.skipWsTokens()
 | 
			
		||||
 | 
			
		||||
                    when (t.type) {
 | 
			
		||||
                        Token.Type.IN,
 | 
			
		||||
                        Token.Type.NOTIN -> {
 | 
			
		||||
                            // we need a copy in the closure:
 | 
			
		||||
                            val isIn = t.type == Token.Type.IN
 | 
			
		||||
                            val container = parseExpression(cc) ?: throw ScriptError(cc.currentPos(), "type expected")
 | 
			
		||||
                            currentCondition += statement {
 | 
			
		||||
                                val r = container.execute(this).contains(this, whenValue)
 | 
			
		||||
                                ObjBool(if (isIn) r else !r)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Token.Type.IS, Token.Type.NOTIS -> {
 | 
			
		||||
                            // we need a copy in the closure:
 | 
			
		||||
                            val isIn = t.type == Token.Type.IS
 | 
			
		||||
                            val caseType = parseExpression(cc) ?: throw ScriptError(cc.currentPos(), "type expected")
 | 
			
		||||
                            currentCondition += statement {
 | 
			
		||||
                                val r = whenValue.isInstanceOf(caseType.execute(this))
 | 
			
		||||
                                ObjBool(if (isIn) r else !r)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        Token.Type.COMMA ->
 | 
			
		||||
                            continue
 | 
			
		||||
 | 
			
		||||
                        Token.Type.ARROW ->
 | 
			
		||||
                            break
 | 
			
		||||
 | 
			
		||||
                        Token.Type.RBRACE ->
 | 
			
		||||
                            break@outer
 | 
			
		||||
 | 
			
		||||
                        else -> {
 | 
			
		||||
                            if (t.value == "else") {
 | 
			
		||||
                                cc.skipTokens(Token.Type.ARROW)
 | 
			
		||||
                                if (elseCase != null) throw ScriptError(
 | 
			
		||||
                                    cc.currentPos(),
 | 
			
		||||
                                    "when else block already defined"
 | 
			
		||||
                                )
 | 
			
		||||
                                elseCase =
 | 
			
		||||
                                    parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "when else block expected")
 | 
			
		||||
                                skipParseBody = true
 | 
			
		||||
                            } else {
 | 
			
		||||
                                cc.previous()
 | 
			
		||||
                                val x = parseExpression(cc)
 | 
			
		||||
                                    ?: throw ScriptError(cc.currentPos(), "when case condition expected")
 | 
			
		||||
                                currentCondition += statement {
 | 
			
		||||
                                    ObjBool(x.execute(this).compareTo(this, whenValue) == 0)
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // parsed conditions?
 | 
			
		||||
                if (!skipParseBody) {
 | 
			
		||||
                    val block = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "when case block expected")
 | 
			
		||||
                    for (c in currentCondition) cases += WhenCase(c, block)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            statement {
 | 
			
		||||
                var result: Obj = ObjVoid
 | 
			
		||||
                // in / is and like uses whenValue from closure:
 | 
			
		||||
                whenValue = value.execute(this)
 | 
			
		||||
                var found = false
 | 
			
		||||
                for (c in cases)
 | 
			
		||||
                    if (c.condition.execute(this).toBool()) {
 | 
			
		||||
                        result = c.block.execute(this)
 | 
			
		||||
                        found = true
 | 
			
		||||
                        break
 | 
			
		||||
                    }
 | 
			
		||||
                if (!found && elseCase != null) result = elseCase.execute(this)
 | 
			
		||||
                result
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // when { cond -> ... }
 | 
			
		||||
            TODO("when without object is not yet implemented")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseThrowStatement(cc: CompilerContext): Statement {
 | 
			
		||||
        val throwStatement = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "throw object expected")
 | 
			
		||||
        return statement {
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,11 @@ internal class CompilerContext(val tokens: List<Token>) {
 | 
			
		||||
 | 
			
		||||
    fun hasNext() = currentIndex < tokens.size
 | 
			
		||||
    fun hasPrevious() = currentIndex > 0
 | 
			
		||||
    fun next() = tokens.getOrElse(currentIndex) { throw IllegalStateException("No next token") }.also { currentIndex++ }
 | 
			
		||||
    fun next() =
 | 
			
		||||
        if( currentIndex < tokens.size ) tokens[currentIndex++]
 | 
			
		||||
        else Token("", tokens.last().pos, Token.Type.EOF)
 | 
			
		||||
//        throw IllegalStateException("No more tokens")
 | 
			
		||||
 | 
			
		||||
    fun previous() = if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex]
 | 
			
		||||
 | 
			
		||||
    fun savePos() = currentIndex
 | 
			
		||||
@ -47,9 +51,7 @@ internal class CompilerContext(val tokens: List<Token>) {
 | 
			
		||||
        throw ScriptError(at, message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun currentPos() =
 | 
			
		||||
        if (hasNext()) next().pos.also { previous() }
 | 
			
		||||
        else previous().pos.also { next() }
 | 
			
		||||
    fun currentPos(): Pos = tokens[currentIndex].pos
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Skips next token if its type is `tokenType`, returns `true` if so.
 | 
			
		||||
@ -145,19 +147,16 @@ internal class CompilerContext(val tokens: List<Token>) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//    fun expectKeyword(vararg keyword: String): String {
 | 
			
		||||
//        val t = next()
 | 
			
		||||
//        if (t.type != Token.Type.ID && t.value !in keyword) {
 | 
			
		||||
//            throw ScriptError(t.pos, "expected one of ${keyword.joinToString()}")
 | 
			
		||||
//
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
//    data class ReturnScope(val needCatch: Boolean = false)
 | 
			
		||||
 | 
			
		||||
//    private val
 | 
			
		||||
 | 
			
		||||
//    fun startReturnScope(): ReturnScope {
 | 
			
		||||
//        return ReturnScope()
 | 
			
		||||
//    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Skip newlines and comments. Returns (and reads) first non-whitespace token.
 | 
			
		||||
     * Note that [Token.Type.EOF] is not considered a whitespace token.
 | 
			
		||||
     */
 | 
			
		||||
    fun skipWsTokens(): Token {
 | 
			
		||||
        while( current().type in wstokens ) next()
 | 
			
		||||
        return next()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val wstokens = setOf(Token.Type.NEWLINE, Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -89,7 +89,7 @@ open class Obj {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
        context.raiseNotImplemented()
 | 
			
		||||
        return invokeInstanceMethod(context, "contains", other).toBool()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open val asStr: ObjString by lazy {
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,8 @@ open class ObjClass(
 | 
			
		||||
        visibility: Visibility = Visibility.Public,
 | 
			
		||||
        pos: Pos = Pos.builtIn
 | 
			
		||||
    ) {
 | 
			
		||||
        if (name in members || allParentsSet.any { name in it.members })
 | 
			
		||||
        val existing = members[name] ?: allParentsSet.firstNotNullOfOrNull { it.members[name] }
 | 
			
		||||
        if( existing?.isMutable == false)
 | 
			
		||||
            throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
 | 
			
		||||
        members[name] = ObjRecord(initialValue, isMutable, visibility)
 | 
			
		||||
    }
 | 
			
		||||
@ -97,7 +98,19 @@ val ObjIterable by lazy {
 | 
			
		||||
 */
 | 
			
		||||
val ObjCollection by lazy {
 | 
			
		||||
    val i: ObjClass = ObjIterable
 | 
			
		||||
    ObjClass("Collection", i)
 | 
			
		||||
    ObjClass("Collection", i).apply {
 | 
			
		||||
        // it is not effective, but it is open:
 | 
			
		||||
        addFn("contains", isOpen = true) {
 | 
			
		||||
            val obj = args.firstAndOnly()
 | 
			
		||||
            val it = thisObj.invokeInstanceMethod(this, "iterator")
 | 
			
		||||
            while (it.invokeInstanceMethod(this, "hasNext").toBool()) {
 | 
			
		||||
                if( obj.compareTo(this, it.invokeInstanceMethod(this, "next")) == 0 )
 | 
			
		||||
                    return@addFn ObjTrue
 | 
			
		||||
            }
 | 
			
		||||
            ObjFalse
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val ObjIterator by lazy { ObjClass("Iterator") }
 | 
			
		||||
@ -145,6 +158,15 @@ val ObjArray by lazy {
 | 
			
		||||
        addFn("iterator") {
 | 
			
		||||
            ObjArrayIterator(thisObj).also { it.init(this) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addFn("contains", isOpen = true) {
 | 
			
		||||
            val obj = args.firstAndOnly()
 | 
			
		||||
            for( i in 0..< thisObj.invokeInstanceMethod(this, "size").toInt()) {
 | 
			
		||||
                if( thisObj.getAt(this, i).compareTo(this, obj) == 0 ) return@addFn ObjTrue
 | 
			
		||||
            }
 | 
			
		||||
            ObjFalse
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addFn("isample") { "ok".toObj() }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,10 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
 | 
			
		||||
        return this
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
        return list.contains(other)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override val objClass: ObjClass
 | 
			
		||||
        get() = type
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,14 @@ data class ObjString(val value: String) : Obj() {
 | 
			
		||||
        return ObjChar(value[index])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun contains(context: Context, other: Obj): Boolean {
 | 
			
		||||
        return if (other is ObjString)
 | 
			
		||||
            value.contains(other.value)
 | 
			
		||||
        else if (other is ObjChar)
 | 
			
		||||
            value.contains(other.value)
 | 
			
		||||
        else context.raiseArgumentError("String.contains can't take $other")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val type = ObjClass("String").apply {
 | 
			
		||||
            addConst("startsWith",
 | 
			
		||||
 | 
			
		||||
@ -298,9 +298,9 @@ class ScriptTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun eqNeqTest() = runTest {
 | 
			
		||||
        assertEquals(ObjBool(true), eval("val x = 2; x == 2"))
 | 
			
		||||
        assertEquals(ObjBool(false), eval("val x = 3; x == 2"))
 | 
			
		||||
        assertEquals(ObjFalse, eval("val x = 3; x == 2"))
 | 
			
		||||
        assertEquals(ObjBool(true), eval("val x = 3; x != 2"))
 | 
			
		||||
        assertEquals(ObjBool(false), eval("val x = 3; x != 3"))
 | 
			
		||||
        assertEquals(ObjFalse, eval("val x = 3; x != 3"))
 | 
			
		||||
 | 
			
		||||
        assertTrue { eval("1 == 1").toBool() }
 | 
			
		||||
        assertTrue { eval("true == true").toBool() }
 | 
			
		||||
@ -313,17 +313,17 @@ class ScriptTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun logicTest() = runTest {
 | 
			
		||||
        assertEquals(ObjBool(false), eval("true && false"))
 | 
			
		||||
        assertEquals(ObjBool(false), eval("false && false"))
 | 
			
		||||
        assertEquals(ObjBool(false), eval("false && true"))
 | 
			
		||||
        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(ObjBool(false), eval("false || false"))
 | 
			
		||||
        assertEquals(ObjFalse, eval("false || false"))
 | 
			
		||||
        assertEquals(ObjBool(true), eval("false || true"))
 | 
			
		||||
        assertEquals(ObjBool(true), eval("true || true"))
 | 
			
		||||
 | 
			
		||||
        assertEquals(ObjBool(false), eval("!true"))
 | 
			
		||||
        assertEquals(ObjFalse, eval("!true"))
 | 
			
		||||
        assertEquals(ObjBool(true), eval("!false"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1979,4 +1979,138 @@ class ScriptTest {
 | 
			
		||||
            """.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()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user