while/else practical sample

!fixed else-while not calling on never run loop bug
This commit is contained in:
Sergey Chernov 2025-06-02 18:08:54 +04:00
parent 25ace7370b
commit 3cd2786ef0
3 changed files with 72 additions and 16 deletions

View File

@ -572,17 +572,15 @@ We can skip the rest of the loop and restart it, as usual, with `continue` opera
"found even numbers: " + countEven "found even numbers: " + countEven
>>> "found even numbers: 5" >>> "found even numbers: 5"
`continue` can't "return" anything: it just restarts the loop. It can use labeled loops to restart outer ones: `continue` can't "return" anything: it just restarts the loop. It can use labeled loops to restart outer ones (we intentionally avoid using for loops here):
var count = 0 var count = 0
var total = 0 var total = 0
// notice the label: // notice the label:
outerLoop@ while( count < 5 ) { outerLoop@ while( count++ < 5 ) {
count = count + 1
var innerCount = 0 var innerCount = 0
while( innerCount < 10 ) { while( innerCount < 10 ) {
innerCount = innerCount + 1 if( ++innerCount == 10 )
if( innerCount == 10 )
continue@outerLoop continue@outerLoop
} }
// we don't reach it because continue above restarts our loop // we don't reach it because continue above restarts our loop
@ -600,6 +598,20 @@ The while and for loops can be followed by the else block, which is executed whe
ends normally, without breaks. It allows override loop result value, for example, ends normally, without breaks. It allows override loop result value, for example,
to not calculate it in every iteration. See for loop example just below. to not calculate it in every iteration. See for loop example just below.
fun naive_is_prime(candidate) {
val x = if( candidate !is Int) candidate.toInt() else candidate
var divisor = 1
while( ++divisor < x/2 || divisor == 2 ) {
if( x % divisor == 0 ) break false
}
else true
}
assert( !naive_is_prime(16) )
assert( naive_is_prime(17) )
assert( naive_is_prime(3) )
assert( !naive_is_prime(4) )
>>> void
## Loop return value diagram ## Loop return value diagram
```mermaid ```mermaid

View File

@ -753,23 +753,25 @@ class Compiler(
} }
return statement(body.pos) { return statement(body.pos) {
var result: Obj = ObjVoid var result: Obj = ObjVoid
var wasBroken = false
while (condition.execute(it).toBool()) { while (condition.execute(it).toBool()) {
try { try {
// we don't need to create new context here: if body is a block, // we don't need to create new context here: if body is a block,
// parse block will do it, otherwise single statement doesn't need it: // parse block will do it, otherwise single statement doesn't need it:
result = body.execute(it) result = body.execute(it)
elseStatement?.let { s -> result = s.execute(it) }
} catch (lbe: LoopBreakContinueException) { } catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) { if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue if (lbe.doContinue) continue
else { else {
result = lbe.result result = lbe.result
wasBroken = true
break break
} }
} else } else
throw lbe throw lbe
} }
} }
if( !wasBroken ) elseStatement?.let { s -> result = s.execute(it) }
result result
} }
} }

View File

@ -1041,7 +1041,8 @@ class ScriptTest {
@Test @Test
fun testLambdaWithIt1() = runTest { fun testLambdaWithIt1() = runTest {
eval(""" eval(
"""
val x = { val x = {
it + "!" it + "!"
} }
@ -1050,43 +1051,51 @@ class ScriptTest {
assert( x is Callable) assert( x is Callable)
assert(y == "OK") assert(y == "OK")
assert( x("hello") == "hello!") assert( x("hello") == "hello!")
""".trimIndent()) """.trimIndent()
)
} }
@Test @Test
fun testLambdaWithIt2() = runTest { fun testLambdaWithIt2() = runTest {
eval(""" eval(
"""
val x = { val x = {
assert(it == void) assert(it == void)
} }
assert( x() == void) assert( x() == void)
""".trimIndent()) """.trimIndent()
)
} }
@Test @Test
fun testLambdaWithIt3() = runTest { fun testLambdaWithIt3() = runTest {
eval(""" eval(
"""
val x = { val x = {
assert( it == [1,2,"end"]) assert( it == [1,2,"end"])
} }
println("0----") println("0----")
assert( x(1, 2, "end") == void) assert( x(1, 2, "end") == void)
""".trimIndent()) """.trimIndent()
)
} }
@Test @Test
fun testLambdaWithArgs() = runTest { fun testLambdaWithArgs() = runTest {
eval(""" eval(
"""
val x = { x, y, z -> val x = { x, y, z ->
assert( [x, y, z] == [1,2,"end"]) assert( [x, y, z] == [1,2,"end"])
} }
assert( x(1, 2, "end") == void) assert( x(1, 2, "end") == void)
""".trimIndent()) """.trimIndent()
)
} }
@Test @Test
fun testLambdaWithArgsEllipsis() = runTest { fun testLambdaWithArgsEllipsis() = runTest {
eval(""" eval(
"""
val x = { x, y... -> val x = { x, y... ->
println("-- y=",y) println("-- y=",y)
println(":: "+y::class) println(":: "+y::class)
@ -1094,7 +1103,8 @@ class ScriptTest {
} }
assert( x(1, 2, "end") == void) assert( x(1, 2, "end") == void)
assert( x(1, ...[2, "end"]) == void) assert( x(1, ...[2, "end"]) == void)
""".trimIndent()) """.trimIndent()
)
} }
@Test @Test
@ -1111,4 +1121,36 @@ class ScriptTest {
) )
} }
} }
@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())
}
} }