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
|
>>> just 3
|
||||||
>>> void
|
>>> 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
|
## while
|
||||||
|
|
||||||
Regular pre-condition while loop, as expression, loop returns the last expression as everything else:
|
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
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.5.2-SNAPSHOT"
|
version = "0.6.0-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -650,7 +650,7 @@ class Compiler(
|
|||||||
"void" -> Accessor { ObjVoid.asReadonly }
|
"void" -> Accessor { ObjVoid.asReadonly }
|
||||||
"null" -> Accessor { ObjNull.asReadonly }
|
"null" -> Accessor { ObjNull.asReadonly }
|
||||||
"true" -> Accessor { ObjBool(true).asReadonly }
|
"true" -> Accessor { ObjBool(true).asReadonly }
|
||||||
"false" -> Accessor { ObjBool(false).asReadonly }
|
"false" -> Accessor { ObjFalse.asReadonly }
|
||||||
else -> {
|
else -> {
|
||||||
Accessor({
|
Accessor({
|
||||||
it.pos = t.pos
|
it.pos = t.pos
|
||||||
@ -709,6 +709,7 @@ class Compiler(
|
|||||||
"class" -> parseClassDeclaration(cc, false)
|
"class" -> parseClassDeclaration(cc, false)
|
||||||
"try" -> parseTryStatement(cc)
|
"try" -> parseTryStatement(cc)
|
||||||
"throw" -> parseThrowStatement(cc)
|
"throw" -> parseThrowStatement(cc)
|
||||||
|
"when" -> parseWhenStatement(cc)
|
||||||
else -> {
|
else -> {
|
||||||
// triples
|
// triples
|
||||||
cc.previous()
|
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 {
|
private fun parseThrowStatement(cc: CompilerContext): Statement {
|
||||||
val throwStatement = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
val throwStatement = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
||||||
return statement {
|
return statement {
|
||||||
|
@ -21,7 +21,11 @@ internal class CompilerContext(val tokens: List<Token>) {
|
|||||||
|
|
||||||
fun hasNext() = currentIndex < tokens.size
|
fun hasNext() = currentIndex < tokens.size
|
||||||
fun hasPrevious() = currentIndex > 0
|
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 previous() = if (!hasPrevious()) throw IllegalStateException("No previous token") else tokens[--currentIndex]
|
||||||
|
|
||||||
fun savePos() = currentIndex
|
fun savePos() = currentIndex
|
||||||
@ -47,9 +51,7 @@ internal class CompilerContext(val tokens: List<Token>) {
|
|||||||
throw ScriptError(at, message)
|
throw ScriptError(at, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun currentPos() =
|
fun currentPos(): Pos = tokens[currentIndex].pos
|
||||||
if (hasNext()) next().pos.also { previous() }
|
|
||||||
else previous().pos.also { next() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips next token if its type is `tokenType`, returns `true` if so.
|
* 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()
|
* Skip newlines and comments. Returns (and reads) first non-whitespace token.
|
||||||
// if (t.type != Token.Type.ID && t.value !in keyword) {
|
* Note that [Token.Type.EOF] is not considered a whitespace token.
|
||||||
// throw ScriptError(t.pos, "expected one of ${keyword.joinToString()}")
|
*/
|
||||||
//
|
fun skipWsTokens(): Token {
|
||||||
// }
|
while( current().type in wstokens ) next()
|
||||||
|
return next()
|
||||||
// data class ReturnScope(val needCatch: Boolean = false)
|
}
|
||||||
|
|
||||||
// private val
|
companion object {
|
||||||
|
val wstokens = setOf(Token.Type.NEWLINE, Token.Type.MULTILINE_COMMENT, Token.Type.SINLGE_LINE_COMMENT)
|
||||||
// fun startReturnScope(): ReturnScope {
|
}
|
||||||
// return ReturnScope()
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
@ -89,7 +89,7 @@ open class Obj {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open suspend fun contains(context: Context, other: Obj): Boolean {
|
open suspend fun contains(context: Context, other: Obj): Boolean {
|
||||||
context.raiseNotImplemented()
|
return invokeInstanceMethod(context, "contains", other).toBool()
|
||||||
}
|
}
|
||||||
|
|
||||||
open val asStr: ObjString by lazy {
|
open val asStr: ObjString by lazy {
|
||||||
|
@ -42,7 +42,8 @@ open class ObjClass(
|
|||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
pos: Pos = Pos.builtIn
|
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")
|
throw ScriptError(pos, "$name is already defined in $objClass or one of its supertypes")
|
||||||
members[name] = ObjRecord(initialValue, isMutable, visibility)
|
members[name] = ObjRecord(initialValue, isMutable, visibility)
|
||||||
}
|
}
|
||||||
@ -97,7 +98,19 @@ val ObjIterable by lazy {
|
|||||||
*/
|
*/
|
||||||
val ObjCollection by lazy {
|
val ObjCollection by lazy {
|
||||||
val i: ObjClass = ObjIterable
|
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") }
|
val ObjIterator by lazy { ObjClass("Iterator") }
|
||||||
@ -145,6 +158,15 @@ val ObjArray by lazy {
|
|||||||
addFn("iterator") {
|
addFn("iterator") {
|
||||||
ObjArrayIterator(thisObj).also { it.init(this) }
|
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() }
|
addFn("isample") { "ok".toObj() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,10 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(context: Context, other: Obj): Boolean {
|
||||||
|
return list.contains(other)
|
||||||
|
}
|
||||||
|
|
||||||
override val objClass: ObjClass
|
override val objClass: ObjClass
|
||||||
get() = type
|
get() = type
|
||||||
|
|
||||||
|
@ -31,6 +31,14 @@ data class ObjString(val value: String) : Obj() {
|
|||||||
return ObjChar(value[index])
|
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 {
|
companion object {
|
||||||
val type = ObjClass("String").apply {
|
val type = ObjClass("String").apply {
|
||||||
addConst("startsWith",
|
addConst("startsWith",
|
||||||
|
@ -298,9 +298,9 @@ class ScriptTest {
|
|||||||
@Test
|
@Test
|
||||||
fun eqNeqTest() = runTest {
|
fun eqNeqTest() = runTest {
|
||||||
assertEquals(ObjBool(true), eval("val x = 2; x == 2"))
|
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(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("1 == 1").toBool() }
|
||||||
assertTrue { eval("true == true").toBool() }
|
assertTrue { eval("true == true").toBool() }
|
||||||
@ -313,17 +313,17 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun logicTest() = runTest {
|
fun logicTest() = runTest {
|
||||||
assertEquals(ObjBool(false), eval("true && false"))
|
assertEquals(ObjFalse, eval("true && false"))
|
||||||
assertEquals(ObjBool(false), eval("false && false"))
|
assertEquals(ObjFalse, eval("false && false"))
|
||||||
assertEquals(ObjBool(false), eval("false && true"))
|
assertEquals(ObjFalse, eval("false && true"))
|
||||||
assertEquals(ObjBool(true), eval("true && true"))
|
assertEquals(ObjBool(true), eval("true && true"))
|
||||||
|
|
||||||
assertEquals(ObjBool(true), eval("true || false"))
|
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("false || true"))
|
||||||
assertEquals(ObjBool(true), eval("true || true"))
|
assertEquals(ObjBool(true), eval("true || true"))
|
||||||
|
|
||||||
assertEquals(ObjBool(false), eval("!true"))
|
assertEquals(ObjFalse, eval("!true"))
|
||||||
assertEquals(ObjBool(true), eval("!false"))
|
assertEquals(ObjBool(true), eval("!false"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1979,4 +1979,138 @@ class ScriptTest {
|
|||||||
""".trimIndent()
|
""".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