for loop and cleaner iteration over values

This commit is contained in:
Irmen de Jong 2019-06-25 21:10:45 +02:00
parent b11d10e2ff
commit 343978d164
9 changed files with 103 additions and 68 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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'

View File

@ -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>) {

View File

@ -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")

View File

@ -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)
}

View File

@ -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()
}
}

View File

@ -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

View File

@ -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')
}
}