From 54aeee267642819bad415a954f84687fa1ac28c3 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 17 Sep 2018 21:05:33 +0200 Subject: [PATCH] for translation improvements --- compiler/examples/test.p8 | 1 + compiler/src/prog8/ast/AST.kt | 8 +- compiler/src/prog8/ast/AstChecker.kt | 18 +++- compiler/src/prog8/compiler/Compiler.kt | 134 +++++++++++++++++------- 4 files changed, 117 insertions(+), 44 deletions(-) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index 13c873c63..0b1696757 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -34,6 +34,7 @@ sub start() -> () { _vm_write_str(vs3) _vm_write_str(vs4) + byte lv = 0 word tx = 0 word ty = 12 % 5 float time = 0.0 diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 33d6eed5f..18f17fe1a 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -898,7 +898,8 @@ class RangeExpr(var from: IExpression, fromVal = (from as LiteralValue).asIntegerValue!! toVal = (to as LiteralValue).asIntegerValue!! } - val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1 + val stepLv = step as? LiteralValue ?: return null + val stepVal = stepLv.asIntegerValue ?: 1 return when { fromVal <= toVal -> when { stepVal <= 0 -> IntRange.EMPTY @@ -981,10 +982,7 @@ data class IdentifierReference(val nameInSource: List, override val posi } } - override val isIterable: Boolean - get() { - TODO("iterable identifierref?") - } + override val isIterable: Boolean = true // should be checked by caller by actually looking up the symbol } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index d5d52e078..57d454b1e 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -69,6 +69,8 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: } override fun process(forLoop: ForLoop): IStatement { + if(forLoop.body.isEmpty()) + printWarning("for loop body is empty", forLoop.position) if(!forLoop.iterable.isIterable) { checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position)) } else { @@ -78,11 +80,16 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: // loop register when (forLoop.loopRegister) { Register.A, Register.X, Register.Y -> { - if (iterableDt != DataType.BYTE) + if (iterableDt != DataType.BYTE && iterableDt!=DataType.ARRAY && iterableDt!=DataType.MATRIX && + iterableDt != DataType.STR && iterableDt != DataType.STR_P && + iterableDt != DataType.STR_S && iterableDt != DataType.STR_PS) checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position)) } Register.AX, Register.AY, Register.XY -> { - if (iterableDt != DataType.WORD && iterableDt != DataType.BYTE) + if (iterableDt != DataType.WORD && iterableDt != DataType.BYTE && + iterableDt != DataType.STR && iterableDt != DataType.STR_P && + iterableDt != DataType.STR_S && iterableDt != DataType.STR_PS && + iterableDt !=DataType.ARRAY && iterableDt!=DataType.ARRAY_W && iterableDt!=DataType.MATRIX) checkResult.add(ExpressionError("register pair can only loop over words", forLoop.position)) } } @@ -95,11 +102,14 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions: } else { when (loopvar.datatype) { DataType.BYTE -> { - if(iterableDt!=DataType.BYTE) + if(iterableDt!=DataType.BYTE && iterableDt!=DataType.ARRAY && iterableDt!=DataType.MATRIX && + iterableDt != DataType.STR && iterableDt != DataType.STR_P && + iterableDt != DataType.STR_S && iterableDt != DataType.STR_PS) checkResult.add(ExpressionError("can only loop over bytes", forLoop.position)) } DataType.WORD -> { - if(iterableDt!=DataType.BYTE && iterableDt!=DataType.WORD) + if(iterableDt!=DataType.BYTE && iterableDt!=DataType.WORD && + iterableDt !=DataType.ARRAY && iterableDt!=DataType.ARRAY_W && iterableDt!=DataType.MATRIX) checkResult.add(ExpressionError("can only loop over bytes or words", forLoop.position)) } else -> checkResult.add(ExpressionError("loop variable must be byte or word type", forLoop.position)) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index dcc3f5da2..932c90483 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -3,7 +3,6 @@ package prog8.compiler import prog8.ast.* import prog8.stackvm.* import java.io.PrintStream -import javax.xml.crypto.Data import kotlin.math.abs @@ -489,42 +488,31 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva } private fun translate(loop: ForLoop) { + if(loop.body.isEmpty()) return stackvmProg.line(loop.position) + val loopVarName: String + val loopVarDt: DataType + if(loop.loopRegister!=null) { val reg = loop.loopRegister - if(loop.iterable is RangeExpr) { - 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 non-constant range: ${loop.iterable}") - } - } else { - TODO("loop over something else as a Range: ${loop.iterable}") + loopVarName = reg.toString() + loopVarDt = when (reg) { + Register.A, Register.X, Register.Y -> DataType.BYTE + Register.AX, Register.AY, Register.XY -> DataType.WORD } } else { val loopvar = (loop.loopVar!!.targetStatement(namespace) as VarDecl) - if(loop.iterable is RangeExpr) { - val range = (loop.iterable as RangeExpr).toConstantIntegerRange() - if(range!=null) { + loopVarName = loopvar.scopedname + loopVarDt = loopvar.datatype + } + + if(loop.iterable is RangeExpr) { + val range = (loop.iterable as RangeExpr).toConstantIntegerRange() + when { + range!=null -> { if (range.isEmpty()) throw CompilerException("loop over empty range") - when (loopvar.datatype) { + when (loopVarDt) { DataType.BYTE -> { if (range.first < 0 || range.first > 255 || range.last < 0 || range.last > 255) throw CompilerException("range out of bounds for byte") @@ -535,21 +523,35 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva } 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}") + translateForOverConstantRange(loopVarName, loopVarDt, range, loop.body) } - } else { - TODO("loop over something else as a Range: ${loop.iterable}") + loop.loopRegister!=null -> + translateForOverVariableRange(null, loop.loopRegister, loopVarDt, loop.iterable as RangeExpr, loop.body) + else -> + translateForOverVariableRange(loopVarName, null, loopVarDt, loop.iterable as RangeExpr, loop.body) + } + } else { + val litVal = loop.iterable as? LiteralValue + val ident = loop.iterable as? IdentifierReference + when { + litVal?.strvalue != null -> { + TODO("loop over string $litVal") + } + ident!=null -> { + val symbol = ident.targetStatement(namespace) + TODO("loop over symbol: ${ident.nameInSource} -> $symbol") + } + else -> throw CompilerException("loopvar is something strange ${loop.iterable}") } } } - private fun translateForConstantRange(varname: String, varDt: DataType, range: IntProgression, body: MutableList) { + private fun translateForOverConstantRange(varname: String, varDt: DataType, range: IntProgression, body: MutableList) { /** * 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) + * (and finally, start and last are constant integer values) * -> * LV = start * loop: @@ -601,4 +603,66 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva } stackvmProg.label(breakLabel) } + + private fun translateForOverVariableRange(varname: String?, register: Register?, varDt: DataType, range: RangeExpr, body: MutableList) { + /** + * 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) + * (and finally, start and last are constant integer values) + * -> + * LV = start + * loop: + * ..body.. + * ..break statement: goto break + * ..continue statement: goto continue + * .. + * continue: + * LV++ (if step is not given, and is therefore 1) + * LV += step (if step is given) + * if LV<=last goto loop + * break: + * + */ + val assignmentTarget = + if(varname!=null) + AssignTarget(null, IdentifierReference(listOf(varname), range.position), range.position) + else + AssignTarget(register, null, range.position) + + val startAssignment = Assignment(assignmentTarget, null, range.from, range.position) + var stepIncrement: PostIncrDecr? = null + var stepAddition: Assignment? = null + if(range.step==null) + stepIncrement = PostIncrDecr(assignmentTarget, "++", range.position) + else + stepAddition = Assignment( + assignmentTarget, + "+=", + range.step ?: LiteralValue(DataType.BYTE, 1, position = range.position), + range.position + ) + translate(startAssignment) + + val loopLabel = makeLabel("loop") + val continueLabel = makeLabel("continue") + val breakLabel = makeLabel("break") + stackvmProg.label(loopLabel) + translate(body) + stackvmProg.label(continueLabel) + if(stepAddition!=null) + translate(stepAddition) + if(stepIncrement!=null) + translate(stepIncrement) + + val comparison = BinaryExpression( + if(varname!=null) + IdentifierReference(listOf(varname), range.position) + else + RegisterExpr(register!!, range.position) + ,"<=", range.to, range.position) + translate(comparison) + stackvmProg.instr(Opcode.BNE, callLabel = loopLabel) + stackvmProg.label(breakLabel) + } }