mirror of
https://github.com/irmen/prog8.git
synced 2025-08-05 06:28:20 +00:00
allow integer range as when choice value
This commit is contained in:
@@ -319,4 +319,35 @@ _after:
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> {
|
||||
// replace a range expression in a when by the actual list of numbers it represents
|
||||
val values = whenChoice.values
|
||||
if(values!=null && values.size==1) {
|
||||
val conditionType = (whenChoice.parent as When).condition.inferType(program)
|
||||
val intRange = (values[0] as? RangeExpression)?.toConstantIntegerRange()
|
||||
if(conditionType.isKnown && intRange != null) {
|
||||
if(intRange.count()>255)
|
||||
errors.err("values list too long", values[0].position)
|
||||
else {
|
||||
val dt = conditionType.getOrUndef().base
|
||||
val newValues = intRange.map {
|
||||
val num = NumericLiteral(BaseDataType.LONG, it.toDouble(), values[0].position)
|
||||
num.linkParents(whenChoice)
|
||||
val cast = num.cast(dt, true)
|
||||
if (cast.isValid) cast.valueOrZero() else null
|
||||
}
|
||||
if(null !in newValues) {
|
||||
if(newValues.size>=10)
|
||||
errors.warn("long list of values, checking will not be very efficient", values[0].position)
|
||||
values.clear()
|
||||
for(num in newValues)
|
||||
values.add(num!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.engine.spec.tempdir
|
||||
import io.kotest.matchers.comparables.shouldBeGreaterThan
|
||||
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
@@ -684,6 +685,40 @@ main
|
||||
errors.errors[0] shouldContain "use if"
|
||||
}
|
||||
|
||||
test("when on range expressions is ok") {
|
||||
val src="""
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
when cx16.r0L {
|
||||
21 to 29 step 2 -> cx16.r1L++
|
||||
else -> cx16.r1L--
|
||||
}
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false).shouldNotBeNull()
|
||||
}
|
||||
|
||||
test("when on range expressions outside value datatype is error") {
|
||||
val src="""
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
when cx16.r0L {
|
||||
300 to 400 -> cx16.r1L++
|
||||
else -> cx16.r1L--
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "values must be constant numbers"
|
||||
}
|
||||
|
||||
|
||||
test("sizeof number const evaluation in vardecl") {
|
||||
val src="""
|
||||
main {
|
||||
|
@@ -751,19 +751,24 @@ action. It is possible to combine several choices to result in the same action::
|
||||
when value {
|
||||
4 -> txt.print("four")
|
||||
5 -> txt.print("five")
|
||||
10,20,30 -> {
|
||||
txt.print("ten or twenty or thirty")
|
||||
}
|
||||
10,20,30 -> txt.print("ten or twenty or thirty")
|
||||
50 to 60 step 2 -> txt.print("fifty to sixty, even")
|
||||
else -> txt.print("don't know")
|
||||
}
|
||||
|
||||
The when-*value* can be any expression but the choice values have to evaluate to
|
||||
compile-time constant integers (bytes or words). They also have to be the same
|
||||
datatype as the when-value, otherwise no efficient comparison can be done.
|
||||
The else part is optional.
|
||||
You can explicitly put a list of numbers that all should result in the same case,
|
||||
or even use any *range expression* as long as it denotes a constant list of numbers.
|
||||
Be aware that every number is compared individually so using long lists of numbers and/or
|
||||
many choice cases will result in poor performance.
|
||||
|
||||
Choices can result in a single statement or a block of multiple statements in which
|
||||
case you have to use { } to enclose them.
|
||||
|
||||
The else part is optional.
|
||||
|
||||
|
||||
.. note::
|
||||
Instead of chaining several value equality checks together using ``or`` (ex.: ``if x==1 or xx==5 or xx==9``),
|
||||
|
@@ -9,7 +9,6 @@ TODO
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- allow integer range as when choice? because 1,2,3,4,5 is already allowed, so perhaps 1 to 5 should be allowed too? However, [1,2,3,4,5] usually is the desugared equivalent of 1 to 5 and choice values can't be arrays. Unless we allow array literals as well and desugar those into separate labels too.
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- add float support to the configurable compiler targets
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
|
@@ -1,31 +1,17 @@
|
||||
%import psg
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
psg.init()
|
||||
cx16.r0L = 25
|
||||
|
||||
psg.voice(5, psg.LEFT, 0, psg.TRIANGLE, 0)
|
||||
psg.freq(5, 1600)
|
||||
psg.envelope(5, 63, 10, 50, 2)
|
||||
|
||||
psg.voice(6, psg.RIGHT, 0, psg.SAWTOOTH, 0)
|
||||
psg.freq(6, 1200)
|
||||
psg.envelope(6, 63, 2, 50, 10)
|
||||
|
||||
repeat 140 {
|
||||
sys.waitvsync()
|
||||
psg.envelopes_irq()
|
||||
}
|
||||
|
||||
psg.voice(5, psg.DISABLED, 0, 0, 0)
|
||||
psg.voice(6, psg.DISABLED, 0, 0, 0)
|
||||
psg.silent()
|
||||
repeat {
|
||||
sys.waitvsync()
|
||||
psg.envelopes_irq()
|
||||
when cx16.r0L {
|
||||
0 -> txt.print("zero")
|
||||
1 -> txt.print("one")
|
||||
21 to 29 step 2 -> txt.print("between 20 and 30 and odd")
|
||||
else -> txt.print("something else")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user