Add descending ranges and for-loop support

This commit is contained in:
sergeych committed 2026-04-03 20:58:08 +03:00
1 parent d8c53c500e
commit f1003f5b95
15 files changed
+411 -98

No files matched your search

@@ -294,6 +294,18 @@ class ScriptTest {
assertEquals(Token.Type.INT, tt[0].type)
assertEquals(Token.Type.DOTDOTLT, tt[1].type)
assertEquals(Token.Type.INT, tt[2].type)
tt = parseLyng("5 downTo 4".toSource())
assertEquals(Token.Type.INT, tt[0].type)
assertEquals(Token.Type.DOWNTO, tt[1].type)
assertEquals(Token.Type.INT, tt[2].type)
tt = parseLyng("5 downUntil 4".toSource())
assertEquals(Token.Type.INT, tt[0].type)
assertEquals(Token.Type.DOWNUNTIL, tt[1].type)
assertEquals(Token.Type.INT, tt[2].type)
}
@Test
@@ -1280,6 +1292,36 @@ class ScriptTest {
assertTrue(convIndex > incIndex, "INT_TO_REAL should appear after INC_INT")
}
@Test
fun testDescendingForLoopDisasm() = runTest {
val scope = Script.newScope()
scope.eval(
"""
fun countDown() {
var acc = 0
for (i in 5 downTo 1) {
acc += i
}
}
fun countDownVar() {
var acc = 0
val r = 5 downTo 1
for (i in r) {
acc += i
}
}
""".trimIndent()
)
val constDisasm = scope.disassembleSymbol("countDown")
val varDisasm = scope.disassembleSymbol("countDownVar")
assertTrue("DEC_INT" in constDisasm, "expected DEC_INT in descending for-loop disasm")
assertTrue("JMP_IF_LTE_INT" in constDisasm, "expected JMP_IF_LTE_INT in descending for-loop disasm")
assertTrue("CALL_MEMBER_SLOT" !in constDisasm, "descending literal range should avoid iterator fallback")
assertTrue("DEC_INT" in varDisasm, "expected DEC_INT in descending range-variable for-loop disasm")
assertTrue("JMP_IF_LTE_INT" in varDisasm, "expected descending comparison in range-variable for-loop disasm")
assertTrue("CALL_MEMBER_SLOT" !in varDisasm, "descending range-variable loop should avoid iterator fallback")
}
@Test
fun testIntClosedRangeInclusive() = runTest {
eval(
@@ -3485,17 +3527,58 @@ class ScriptTest {
fun testRangeStepIteration() = runTest {
val ints = eval("""(1..5 step 2).toList()""") as ObjList
assertEquals(listOf(1, 3, 5), ints.list.map { it.toInt() })
val descending = eval("""(5 downTo 1).toList()""") as ObjList
assertEquals(listOf(5, 4, 3, 2, 1), descending.list.map { it.toInt() })
val descendingExclusive = eval("""(5 downUntil 1).toList()""") as ObjList
assertEquals(listOf(5, 4, 3, 2), descendingExclusive.list.map { it.toInt() })
val descendingStep = eval("""(10 downTo 1 step 3).toList()""") as ObjList
assertEquals(listOf(10, 7, 4, 1), descendingStep.list.map { it.toInt() })
val descendingChars = eval("""('e' downTo 'a' step 2).toList()""") as ObjList
assertEquals(listOf('e', 'c', 'a'), descendingChars.list.map { it.toString().single() })
val chars = eval("""('a'..'e' step 2).toList()""") as ObjList
assertEquals(listOf('a', 'c', 'e'), chars.list.map { it.toString().single() })
val reals = eval("""(0.0..1.0 step 0.25).toList()""") as ObjList
assertEquals(listOf(0.0, 0.25, 0.5, 0.75, 1.0), reals.list.map { it.toDouble() })
val empty = eval("""(5..1 step 1).toList()""") as ObjList
assertEquals(0, empty.list.size)
val plainDescending = eval("""(5..1).toList()""") as ObjList
assertEquals(0, plainDescending.list.size)
val openEnd = eval("""(0.. step 1).take(3).toList()""") as ObjList
assertEquals(listOf(0, 1, 2), openEnd.list.map { it.toInt() })
assertEquals(
true,
eval(
"""
val r = 10 downTo 1
r.isDescending && r.isEndInclusive && (10 in r) && (1 in r) && (0 !in r)
""".trimIndent()
).toBool()
)
assertEquals(
true,
eval(
"""
val r = 10 downUntil 1
r.isDescending && !r.isEndInclusive && (10 in r) && (1 !in r) && ((8 downTo 3) in r)
""".trimIndent()
).toBool()
)
assertEquals(
15,
(eval(
"""
var s = 0
for (i in 5 downTo 1) s += i
s
""".trimIndent()
) as ObjInt).toInt()
)
assertFailsWith<ExecutionError> {
eval("""(0.0..1.0).toList()""")
}
assertFailsWith<ExecutionError> {
eval("""(5 downTo 1 step -1).toList()""")
}
assertFailsWith<ExecutionError> {
eval("""(0..).toList()""")
}