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: 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 total = 0
// notice the label:
outerLoop@ while( count < 5 ) {
count = count + 1
outerLoop@ while( count++ < 5 ) {
var innerCount = 0
while( innerCount < 10 ) {
innerCount = innerCount + 1
if( innerCount == 10 )
if( ++innerCount == 10 )
continue@outerLoop
}
// 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,
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
```mermaid

View File

@ -753,23 +753,25 @@ class Compiler(
}
return statement(body.pos) {
var result: Obj = ObjVoid
var wasBroken = false
while (condition.execute(it).toBool()) {
try {
// 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:
result = body.execute(it)
elseStatement?.let { s -> result = s.execute(it) }
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue
else {
result = lbe.result
wasBroken = true
break
}
} else
throw lbe
}
}
if( !wasBroken ) elseStatement?.let { s -> result = s.execute(it) }
result
}
}

View File

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