mirror of
https://github.com/irmen/prog8.git
synced 2024-07-10 23:29:02 +00:00
simple for loop (with constant ranges) is now compiled
This commit is contained in:
parent
326e5cd81a
commit
dd96ef8ef2
@ -39,10 +39,21 @@ sub start() -> () {
|
||||
float time = 0.0
|
||||
_vm_gfx_clearscr(0)
|
||||
|
||||
for X in 3 to 100 {
|
||||
A=X
|
||||
continue
|
||||
break
|
||||
for XY in 0 to 300 step 3 {
|
||||
_vm_gfx_pixel(XY, 2, XY)
|
||||
_vm_gfx_pixel(XY+1, 2, XY)
|
||||
_vm_gfx_pixel(XY, 3, XY)
|
||||
_vm_gfx_pixel(XY+1, 3, XY)
|
||||
; continue
|
||||
; break
|
||||
}
|
||||
for XY in 315 to 0 step -3 {
|
||||
_vm_gfx_pixel(XY, 6, XY)
|
||||
_vm_gfx_pixel(XY+1, 6, XY)
|
||||
_vm_gfx_pixel(XY, 7, XY)
|
||||
_vm_gfx_pixel(XY+1, 7, XY)
|
||||
; continue
|
||||
; break
|
||||
}
|
||||
|
||||
loop:
|
||||
|
@ -879,12 +879,14 @@ class RangeExpr(var from: IExpression,
|
||||
val toLv = (to as? LiteralValue)
|
||||
if(fromLv==null || toLv==null)
|
||||
return null
|
||||
return toKotlinRange().count()
|
||||
return toConstantIntegerRange()?.count()
|
||||
}
|
||||
|
||||
fun toKotlinRange(): IntProgression {
|
||||
val fromLv = from as LiteralValue
|
||||
val toLv = to as LiteralValue
|
||||
fun toConstantIntegerRange(): IntProgression? {
|
||||
val fromLv = from as? LiteralValue
|
||||
val toLv = to as? LiteralValue
|
||||
if(fromLv==null || toLv==null)
|
||||
return null // non-constant range
|
||||
val fromVal: Int
|
||||
val toVal: Int
|
||||
if(fromLv.isString && toLv.isString) {
|
||||
|
@ -82,7 +82,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
|
||||
}
|
||||
Register.AX, Register.AY, Register.XY -> {
|
||||
if (iterableDt != DataType.BYTE)
|
||||
if (iterableDt != DataType.WORD && iterableDt != DataType.BYTE)
|
||||
checkResult.add(ExpressionError("register pair can only loop over words", forLoop.position))
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package prog8.compiler
|
||||
import prog8.ast.*
|
||||
import prog8.stackvm.*
|
||||
import java.io.PrintStream
|
||||
import javax.xml.crypto.Data
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
class CompilerException(message: String?) : Exception(message)
|
||||
@ -491,20 +493,112 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
if(loop.loopRegister!=null) {
|
||||
val reg = loop.loopRegister
|
||||
if(loop.iterable is RangeExpr) {
|
||||
val range = (loop.iterable as RangeExpr).toKotlinRange()
|
||||
if(range.isEmpty())
|
||||
throw CompilerException("loop over empty range")
|
||||
if(range.step==1) {
|
||||
println("@todo for loop, register, range, step 1") // todo
|
||||
val range = (loop.iterable as RangeExpr).toConstantIntegerRange()
|
||||
if(range!=null) {
|
||||
if (range.isEmpty())
|
||||
throw CompilerException("loop over empty range")
|
||||
val varDt =
|
||||
when (reg) {
|
||||
Register.A, Register.X, Register.Y -> {
|
||||
if (range.first < 0 || range.first > 255 || range.last < 0 || range.last > 255)
|
||||
throw CompilerException("range out of bounds for register")
|
||||
DataType.BYTE
|
||||
}
|
||||
Register.AX, Register.AY, Register.XY -> {
|
||||
if (range.first < 0 || range.first > 65535 || range.last < 0 || range.last > 65535)
|
||||
throw CompilerException("range out of bounds for register")
|
||||
DataType.WORD
|
||||
}
|
||||
}
|
||||
translateForConstantRange(reg.toString(), varDt, range, loop.body)
|
||||
} else {
|
||||
TODO("loop over range with step != 1")
|
||||
TODO("loop over non-constant range: ${loop.iterable}")
|
||||
}
|
||||
} else {
|
||||
TODO("loop over something else as a Range: ${loop.iterable}")
|
||||
}
|
||||
} else {
|
||||
val loopvar = (loop.loopVar!!.targetStatement(namespace) as VarDecl)
|
||||
println("@TODO translate for loop (variable $loopvar)") // TODO
|
||||
if(loop.iterable is RangeExpr) {
|
||||
val range = (loop.iterable as RangeExpr).toConstantIntegerRange()
|
||||
if(range!=null) {
|
||||
if (range.isEmpty())
|
||||
throw CompilerException("loop over empty range")
|
||||
when (loopvar.datatype) {
|
||||
DataType.BYTE -> {
|
||||
if (range.first < 0 || range.first > 255 || range.last < 0 || range.last > 255)
|
||||
throw CompilerException("range out of bounds for byte")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if (range.first < 0 || range.first > 65535 || range.last < 0 || range.last > 65535)
|
||||
throw CompilerException("range out of bounds for word")
|
||||
}
|
||||
else -> throw CompilerException("range must be byte or word")
|
||||
}
|
||||
translateForConstantRange(loopvar.scopedname, loopvar.datatype, range, loop.body)
|
||||
} else {
|
||||
TODO("loop over non-constant range: ${loop.iterable}")
|
||||
}
|
||||
} else {
|
||||
TODO("loop over something else as a Range: ${loop.iterable}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateForConstantRange(varname: String, varDt: DataType, range: IntProgression, body: MutableList<IStatement>) {
|
||||
/**
|
||||
* for LV in start..last { body }
|
||||
* (and we already know that the range is not empty)
|
||||
* (also we know that the range's last value is really the exact last occurring value of the range)
|
||||
* ->
|
||||
* LV = start
|
||||
* loop:
|
||||
* ..body..
|
||||
* ..break statement: goto break
|
||||
* ..continue statement: goto continue
|
||||
* ..
|
||||
* continue:
|
||||
* LV++ (if step=1) or LV+=step (if step > 1)
|
||||
* LV-- (if step=-11) or LV-=abs(step) (if step < 1)
|
||||
* if LV!=last goto loop ; if last > 0
|
||||
* if LV>=0 goto loop ; if last==0
|
||||
* break:
|
||||
*
|
||||
*/
|
||||
val loopLabel = makeLabel("loop")
|
||||
val continueLabel = makeLabel("continue")
|
||||
val breakLabel = makeLabel("break")
|
||||
val varValue = Value(DataType.STR, null, varname)
|
||||
stackvmProg.instr(Opcode.PUSH, Value(varDt, range.first))
|
||||
stackvmProg.instr(Opcode.POP_VAR, varValue)
|
||||
stackvmProg.label(loopLabel)
|
||||
translate(body)
|
||||
stackvmProg.label(continueLabel)
|
||||
when {
|
||||
range.step==1 -> stackvmProg.instr(Opcode.INC_VAR, varValue)
|
||||
range.step==-1 -> stackvmProg.instr(Opcode.DEC_VAR, varValue)
|
||||
range.step>1 -> {
|
||||
stackvmProg.instr(Opcode.PUSH_VAR, varValue)
|
||||
stackvmProg.instr(Opcode.PUSH, Value(varDt, range.step))
|
||||
stackvmProg.instr(Opcode.ADD)
|
||||
stackvmProg.instr(Opcode.POP_VAR, varValue)
|
||||
}
|
||||
range.step<1 -> {
|
||||
stackvmProg.instr(Opcode.PUSH_VAR, varValue)
|
||||
stackvmProg.instr(Opcode.PUSH, Value(varDt, abs(range.step)))
|
||||
stackvmProg.instr(Opcode.SUB)
|
||||
stackvmProg.instr(Opcode.POP_VAR, varValue)
|
||||
}
|
||||
}
|
||||
if(range.last>0) {
|
||||
stackvmProg.instr(Opcode.PUSH, Value(varDt, range.last))
|
||||
stackvmProg.instr(Opcode.PUSH_VAR, varValue)
|
||||
stackvmProg.instr(Opcode.SUB)
|
||||
stackvmProg.instr(Opcode.BNE, callLabel = loopLabel)
|
||||
} else {
|
||||
stackvmProg.instr(Opcode.PUSH_VAR, varValue)
|
||||
stackvmProg.instr(Opcode.BNE, callLabel = loopLabel)
|
||||
}
|
||||
stackvmProg.label(breakLabel)
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import kotlin.system.exitProcess
|
||||
enum class Opcode {
|
||||
|
||||
// pushing values on the (evaluation) stack
|
||||
PUSH, // push constant byte value
|
||||
PUSH, // push constant value (any type)
|
||||
PUSH_MEM, // push byte value from memory to stack
|
||||
PUSH_MEM_W, // push word value from memory to stack
|
||||
PUSH_MEM_F, // push float value from memory to stack
|
||||
@ -1208,13 +1208,21 @@ class StackVm(val traceOutputFile: String?) {
|
||||
}
|
||||
|
||||
Opcode.JUMP -> {} // do nothing; the next instruction is wired up already to the jump target
|
||||
Opcode.BCS -> return if(carry) ins.next else ins.nextAlt!!
|
||||
Opcode.BCC -> return if(carry) ins.nextAlt!! else ins.next
|
||||
Opcode.BEQ -> return if(evalstack.pop().numericValue().toDouble()==0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.BNE -> return if(evalstack.pop().numericValue().toDouble()!=0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.BMI -> return if(evalstack.pop().numericValue().toDouble()<0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.BPL -> return if(evalstack.pop().numericValue().toDouble()>=0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.CALL -> callstack.push(ins.nextAlt)
|
||||
Opcode.BCS ->
|
||||
return if(carry) ins.next else ins.nextAlt!!
|
||||
Opcode.BCC ->
|
||||
return if(carry) ins.nextAlt!! else ins.next
|
||||
Opcode.BEQ ->
|
||||
return if(evalstack.pop().numericValue().toDouble()==0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.BNE ->
|
||||
return if(evalstack.pop().numericValue().toDouble()!=0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.BMI ->
|
||||
return if(evalstack.pop().numericValue().toDouble()<0.0) ins.next else ins.nextAlt!!
|
||||
Opcode.BPL -> {
|
||||
return if (evalstack.pop().numericValue().toDouble() >= 0.0) ins.next else ins.nextAlt!!
|
||||
}
|
||||
Opcode.CALL ->
|
||||
callstack.push(ins.nextAlt)
|
||||
Opcode.RETURN -> {
|
||||
if(callstack.empty())
|
||||
throw VmTerminationException("return instruction with empty call stack")
|
||||
|
Loading…
Reference in New Issue
Block a user