mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
for loop and cleaner iteration over values
This commit is contained in:
parent
b11d10e2ff
commit
343978d164
@ -928,7 +928,6 @@ data class AssignTarget(val register: Register?,
|
||||
|
||||
|
||||
interface IExpression: Node {
|
||||
fun isIterable(program: Program): Boolean
|
||||
fun constValue(program: Program): LiteralValue?
|
||||
fun process(processor: IAstProcessor): IExpression
|
||||
fun referencesIdentifier(name: String): Boolean
|
||||
@ -973,7 +972,6 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
||||
override fun resultingDatatype(program: Program): DataType? = expression.resultingDatatype(program)
|
||||
override fun isIterable(program: Program) = false
|
||||
|
||||
override fun toString(): String {
|
||||
return "Prefix($operator $expression)"
|
||||
@ -996,7 +994,6 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
|
||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
@ -1102,7 +1099,6 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||
arrayspec.linkParents(this)
|
||||
}
|
||||
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun process(processor: IAstProcessor): IExpression = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
|
||||
@ -1141,7 +1137,6 @@ class TypecastExpression(var expression: IExpression, var type: DataType, overri
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
||||
override fun resultingDatatype(program: Program): DataType? = type
|
||||
override fun isIterable(program: Program) = type in IterableDatatypes
|
||||
override fun constValue(program: Program): LiteralValue? {
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
||||
@ -1163,7 +1158,6 @@ data class AddressOf(val identifier: IdentifierReference, override val position:
|
||||
}
|
||||
|
||||
var scopedname: String? = null // will be set in a later state by the compiler
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun resultingDatatype(program: Program) = DataType.UWORD
|
||||
@ -1182,7 +1176,6 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun resultingDatatype(program: Program): DataType? = DataType.UBYTE
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun toString(): String {
|
||||
@ -1202,7 +1195,6 @@ class DirectMemoryWrite(var addressExpression: IExpression, override val positio
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun resultingDatatype(program: Program): DataType? = DataType.UBYTE
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun toString(): String {
|
||||
@ -1338,8 +1330,6 @@ open class LiteralValue(val type: DataType,
|
||||
|
||||
override fun resultingDatatype(program: Program) = type
|
||||
|
||||
override fun isIterable(program: Program): Boolean = type in IterableDatatypes
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val bh = bytevalue?.hashCode() ?: 0x10001234
|
||||
val wh = wordvalue?.hashCode() ?: 0x01002345
|
||||
@ -1468,7 +1458,6 @@ class RangeExpr(var from: IExpression,
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun isIterable(program: Program) = true
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
|
||||
override fun resultingDatatype(program: Program): DataType? {
|
||||
@ -1540,7 +1529,6 @@ class RegisterExpr(val register: Register, override val position: Position) : IE
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun process(processor: IAstProcessor) = this
|
||||
override fun referencesIdentifier(name: String): Boolean = false
|
||||
override fun isIterable(program: Program) = false
|
||||
override fun toString(): String {
|
||||
return "RegisterExpr(register=$register, pos=$position)"
|
||||
}
|
||||
@ -1593,8 +1581,6 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
}
|
||||
|
||||
override fun isIterable(program: Program): Boolean = resultingDatatype(program) in IterableDatatypes
|
||||
|
||||
fun heapId(namespace: INameScope): Int {
|
||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
||||
return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
|
||||
@ -1713,8 +1699,6 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
}
|
||||
return null // calling something we don't recognise...
|
||||
}
|
||||
|
||||
override fun isIterable(program: Program) = resultingDatatype(program) in IterableDatatypes
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,10 +150,10 @@ private class AstChecker(private val program: Program,
|
||||
if(forLoop.body.containsNoCodeNorVars())
|
||||
printWarning("for loop body is empty", forLoop.position)
|
||||
|
||||
if(!forLoop.iterable.isIterable(program)) {
|
||||
val iterableDt = forLoop.iterable.resultingDatatype(program)
|
||||
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
||||
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
|
||||
} else {
|
||||
val iterableDt = forLoop.iterable.resultingDatatype(program)
|
||||
if (forLoop.loopRegister != null) {
|
||||
printWarning("using a register as loop variable is risky (it could get clobbered in the body)", forLoop.position)
|
||||
// loop register
|
||||
|
@ -172,7 +172,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
||||
if(existing==null) {
|
||||
// create the local scoped for loop variable itself
|
||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, true, null, false, varName, null, forLoop.loopVar.position)
|
||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, false, varName, null, forLoop.loopVar.position)
|
||||
vardecl.linkParents(forLoop.body)
|
||||
forLoop.body.statements.add(0, vardecl)
|
||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||
|
@ -2,6 +2,7 @@ package prog8.astvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.compiler.RuntimeValueRange
|
||||
import java.awt.EventQueue
|
||||
|
||||
|
||||
@ -260,14 +261,37 @@ class AstVm(val program: Program) {
|
||||
TODO("$stmt")
|
||||
}
|
||||
is ForLoop -> {
|
||||
TODO("$stmt")
|
||||
// try {
|
||||
//
|
||||
// } catch(b: LoopControlBreak) {
|
||||
// break
|
||||
// } catch(c: LoopControlContinue){
|
||||
// continue
|
||||
// }
|
||||
val iterable = evaluate(stmt.iterable, program, runtimeVariables, ::executeSubroutine)
|
||||
if (iterable.type !in IterableDatatypes && iterable !is RuntimeValueRange)
|
||||
throw VmExecutionException("can only iterate over an iterable value: $stmt")
|
||||
val iterator: Iterator<Any> = iterable.iterator()
|
||||
if(stmt.loopRegister!=null)
|
||||
TODO("for with register")
|
||||
else if(stmt.loopVar!=null) {
|
||||
for(v in iterator) {
|
||||
try {
|
||||
val value: LiteralValue =
|
||||
when(stmt.loopVar.resultingDatatype(program)!!) {
|
||||
DataType.UBYTE -> LiteralValue(DataType.UBYTE, (v as Number).toShort(), position=stmt.position)
|
||||
DataType.BYTE -> LiteralValue(DataType.BYTE, (v as Number).toShort(), position=stmt.position)
|
||||
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = (v as Int), position=stmt.position)
|
||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = (v as Int), position=stmt.position)
|
||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = (v as Double), position=stmt.position)
|
||||
DataType.STR, DataType.STR_S -> LiteralValue(DataType.UBYTE, (v as Char).toShort(), position=stmt.position)
|
||||
else -> TODO("weird loopvar type")
|
||||
}
|
||||
val assignment = Assignment(listOf(AssignTarget(null, stmt.loopVar, null, null,
|
||||
position=stmt.loopVar.position)), null, value, stmt.iterable.position)
|
||||
assignment.linkParents(stmt.body)
|
||||
executeStatement(stmt.body, assignment) // assign the new loop value to the loopvar
|
||||
executeAnonymousScope(stmt.body) // and run the code
|
||||
} catch (b: LoopControlBreak) {
|
||||
break
|
||||
} catch (c: LoopControlContinue) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else TODO("strange for $stmt")
|
||||
}
|
||||
is WhileLoop -> {
|
||||
var condition = evaluate(stmt.condition, program, runtimeVariables, ::executeSubroutine)
|
||||
@ -300,7 +324,6 @@ class AstVm(val program: Program) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun evaluate(args: List<IExpression>): List<RuntimeValue> = args.map { evaluate(it, program, runtimeVariables, ::executeSubroutine) }
|
||||
|
||||
private fun performBuiltinFunction(name: String, args: List<RuntimeValue>) {
|
||||
|
@ -2,6 +2,8 @@ package prog8.astvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.compiler.RuntimeValueRange
|
||||
import kotlin.math.abs
|
||||
|
||||
fun evaluate(expr: IExpression, program: Program, runtimeVars: RuntimeVariables,
|
||||
executeSubroutine: (sub: Subroutine, args: List<RuntimeValue>) -> List<RuntimeValue>): RuntimeValue {
|
||||
@ -60,11 +62,9 @@ fun evaluate(expr: IExpression, program: Program, runtimeVars: RuntimeVariables,
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
TODO("$expr")
|
||||
|
||||
}
|
||||
is DirectMemoryWrite -> {
|
||||
TODO("$expr")
|
||||
|
||||
}
|
||||
is RegisterExpr -> {
|
||||
TODO("$expr")
|
||||
@ -94,7 +94,25 @@ fun evaluate(expr: IExpression, program: Program, runtimeVars: RuntimeVariables,
|
||||
}
|
||||
}
|
||||
is RangeExpr -> {
|
||||
TODO("eval range $expr")
|
||||
val cRange = expr.toConstantIntegerRange(program.heap)
|
||||
if(cRange!=null)
|
||||
return RuntimeValueRange(expr.resultingDatatype(program)!!, cRange)
|
||||
val fromVal = evaluate(expr.from, program, runtimeVars, executeSubroutine).integerValue()
|
||||
val toVal = evaluate(expr.to, program, runtimeVars, executeSubroutine).integerValue()
|
||||
val stepVal = evaluate(expr.step, program, runtimeVars, executeSubroutine).integerValue()
|
||||
val range = when {
|
||||
fromVal <= toVal -> when {
|
||||
stepVal <= 0 -> IntRange.EMPTY
|
||||
stepVal == 1 -> fromVal..toVal
|
||||
else -> fromVal..toVal step stepVal
|
||||
}
|
||||
else -> when {
|
||||
stepVal >= 0 -> IntRange.EMPTY
|
||||
stepVal == -1 -> fromVal downTo toVal
|
||||
else -> fromVal downTo toVal step abs(stepVal)
|
||||
}
|
||||
}
|
||||
return RuntimeValueRange(expr.resultingDatatype(program)!!, range)
|
||||
}
|
||||
else -> {
|
||||
TODO("implement eval $expr")
|
||||
|
@ -1676,7 +1676,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
val idRef = loop.iterable as IdentifierReference
|
||||
val vardecl = idRef.targetVarDecl(program.namespace)!!
|
||||
val iterableValue = vardecl.value as LiteralValue
|
||||
if(!iterableValue.isIterable(program))
|
||||
if(iterableValue.type !in IterableDatatypes)
|
||||
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
|
||||
translateForOverIterableVar(loop, loopVarDt, iterableValue)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
|
||||
@ -10,7 +11,7 @@ import kotlin.math.pow
|
||||
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
|
||||
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
|
||||
*/
|
||||
class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null, val array: Array<Number>?=null, val heapId: Int?=null) {
|
||||
open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null, val array: Array<Number>?=null, val heapId: Int?=null) {
|
||||
|
||||
val byteval: Short?
|
||||
val wordval: Int?
|
||||
@ -501,4 +502,22 @@ class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null,
|
||||
}
|
||||
}
|
||||
|
||||
open fun iterator(): Iterator<Any> {
|
||||
return when (type) {
|
||||
in StringDatatypes -> {
|
||||
Petscii.encodePetscii(str!!, true).iterator()
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
array!!.iterator()
|
||||
}
|
||||
else -> throw IllegalArgumentException("cannot iterate over $this")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValue(type, 0) {
|
||||
override fun iterator(): Iterator<Any> {
|
||||
return range.iterator()
|
||||
}
|
||||
}
|
||||
|
@ -523,7 +523,8 @@ And this is a loop over the values of the array ``fibonacci_numbers`` where the
|
||||
}
|
||||
|
||||
|
||||
You can inline the loop variable declaration in the for statement, including optional zp-tag::
|
||||
You can inline the loop variable declaration in the for statement, including optional zp-tag. In this case
|
||||
the variable is not visible outside of the for loop::
|
||||
|
||||
for ubyte @zp fastindex in 10 to 20 {
|
||||
; do something
|
||||
|
@ -3,49 +3,39 @@
|
||||
|
||||
~ main {
|
||||
|
||||
ubyte[256] sieve
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
sub start() {
|
||||
memset(sieve, 256, false) ; clear the sieve, to reset starting situation on subsequent runs
|
||||
ubyte i=99
|
||||
|
||||
; calculate primes
|
||||
c64scr.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
while true {
|
||||
ubyte prime = find_next_prime()
|
||||
if prime==0
|
||||
break
|
||||
c64scr.print_ub(prime)
|
||||
c64scr.print(", ")
|
||||
amount++
|
||||
for ubyte j in 20 to 40 step 5 {
|
||||
c64scr.print_ub(j)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("number of primes (expected 54): ")
|
||||
c64scr.print_ub(amount)
|
||||
|
||||
for uword j in 200 to 400 step 5 {
|
||||
c64scr.print_uw(j)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
for i in 200 to 50 step -20 {
|
||||
c64scr.print_ub(i)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
|
||||
sub find_next_prime() -> ubyte {
|
||||
for i in "hello" {
|
||||
c64scr.print_ub(i)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
|
||||
while sieve[candidate_prime] {
|
||||
candidate_prime++
|
||||
if candidate_prime==0
|
||||
return 0 ; we wrapped; no more primes available in the sieve
|
||||
for i in [1,2,3,4,5] {
|
||||
c64scr.print_ub(i)
|
||||
c64.CHROUT(',')
|
||||
}
|
||||
|
||||
; found next one, mark the multiples and return it.
|
||||
sieve[candidate_prime] = true
|
||||
uword multiple = candidate_prime
|
||||
|
||||
|
||||
while multiple < len(sieve) {
|
||||
sieve[lsb(multiple)] = true
|
||||
multiple += candidate_prime
|
||||
; c64scr.print_uw(multiple) ; TODO
|
||||
; c4.CHROUT('\n') ; TODO
|
||||
}
|
||||
return candidate_prime
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user