mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
for loop and comparison expression fixes
This commit is contained in:
parent
b72bd805e1
commit
455f60fb84
@ -4,8 +4,8 @@
|
||||
|
||||
sub start() -> () {
|
||||
|
||||
const word width = 159
|
||||
const word height = 127
|
||||
const word width = 320
|
||||
const word height = 256
|
||||
word pixelx
|
||||
byte pixely
|
||||
float xx
|
||||
@ -20,27 +20,28 @@
|
||||
_vm_write_str("Calculating Mandelbrot fractal, have patience...\n")
|
||||
_vm_gfx_clearscr(11)
|
||||
|
||||
for pixely in 0 to height { ; @todo 255 as upper limit doesn't work it overflows the loop
|
||||
for pixelx in 0 to width {
|
||||
xx=pixelx/width/3+0.2
|
||||
yy=pixely/height/3.6+0.4
|
||||
for pixely in 0 to height-1 {
|
||||
for pixelx in 0 to width-1 {
|
||||
xx = pixelx/width/3+0.2
|
||||
yy = pixely/height/3.6+0.4
|
||||
|
||||
x=0.0
|
||||
y=0.0
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
|
||||
for iter in 0 to 31 {
|
||||
if(x*x + y*y > 4) break
|
||||
if (x*x + y*y > 4) break
|
||||
x2 = x*x - y*y + xx
|
||||
y=x*y*2 + yy
|
||||
x=x2
|
||||
y = x*y*2 + yy
|
||||
x = x2
|
||||
}
|
||||
|
||||
plotx = pixelx*2
|
||||
ploty = pixely*2
|
||||
_vm_gfx_pixel(plotx, ploty, iter)
|
||||
_vm_gfx_pixel(plotx+1, ploty, iter)
|
||||
_vm_gfx_pixel(plotx, ploty+1, iter)
|
||||
_vm_gfx_pixel(plotx+1, ploty+1, iter)
|
||||
_vm_gfx_pixel(pixelx, pixely, iter)
|
||||
; plotx = pixelx*2
|
||||
; ploty = pixely*2
|
||||
; _vm_gfx_pixel(plotx, ploty, iter)
|
||||
; _vm_gfx_pixel(plotx+1, ploty, iter)
|
||||
; _vm_gfx_pixel(plotx, ploty+1, iter)
|
||||
; _vm_gfx_pixel(plotx+1, ploty+1, iter)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,56 +1,34 @@
|
||||
%output prg
|
||||
%launcher basic
|
||||
%option enable_floats
|
||||
|
||||
~ main {
|
||||
|
||||
sub start() -> () {
|
||||
|
||||
if(X) goto yesx
|
||||
else goto nox
|
||||
byte i
|
||||
|
||||
byte from = 250
|
||||
byte last = 255
|
||||
|
||||
yesx:
|
||||
|
||||
if(X) {
|
||||
A=0
|
||||
goto yesx ;; @todo fix undefined symbol error
|
||||
return
|
||||
} else {
|
||||
A=1
|
||||
goto nox
|
||||
for i in from to last {
|
||||
_vm_write_num(i)
|
||||
_vm_write_char($8d)
|
||||
}
|
||||
|
||||
return
|
||||
; _vm_write_char($8d)
|
||||
; _vm_write_num(i)
|
||||
; _vm_write_char($8d)
|
||||
; _vm_write_char($8d)
|
||||
;
|
||||
; from = 8
|
||||
; last = 0
|
||||
;
|
||||
; for i in from to last step -1 {
|
||||
; _vm_write_num(i)
|
||||
; _vm_write_char($8d)
|
||||
; }
|
||||
;
|
||||
; _vm_write_char($8d)
|
||||
; _vm_write_num(i)
|
||||
; _vm_write_char($8d)
|
||||
|
||||
sub bla() -> () {
|
||||
return
|
||||
sub fooz() -> () {
|
||||
return
|
||||
}
|
||||
|
||||
sub fooz2() -> () {
|
||||
return
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
A=45
|
||||
|
||||
nox:
|
||||
word i
|
||||
|
||||
for i in 10 to 20 {
|
||||
if(i>12) goto fout ;; @todo fix undefined symbol error
|
||||
break
|
||||
continue
|
||||
|
||||
|
||||
bla() ;; @todo fix undefined symbol error
|
||||
}
|
||||
|
||||
fout:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ data class AssignTarget(val register: Register?, val identifier: IdentifierRefer
|
||||
Register.AX, Register.AY, Register.XY -> DataType.WORD
|
||||
}
|
||||
|
||||
val symbol = namespace.lookup(identifier!!.nameInSource, stmt) ?: throw FatalAstException("symbol lookup failed: ${identifier!!.nameInSource}")
|
||||
val symbol = namespace.lookup(identifier!!.nameInSource, stmt) ?: throw FatalAstException("symbol lookup failed: ${identifier.nameInSource}")
|
||||
if(symbol is VarDecl) return symbol.datatype
|
||||
throw FatalAstException("cannot determine datatype of assignment target $this")
|
||||
}
|
||||
@ -781,6 +781,8 @@ class LiteralValue(val type: DataType,
|
||||
override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
|
||||
|
||||
val isString = type==DataType.STR || type==DataType.STR_P || type==DataType.STR_S || type==DataType.STR_PS
|
||||
val isNumeric = type==DataType.BYTE || type==DataType.WORD || type==DataType.FLOAT
|
||||
val isArray = type==DataType.ARRAY || type==DataType.ARRAY_W || type==DataType.MATRIX
|
||||
|
||||
companion object {
|
||||
fun fromBoolean(bool: Boolean, position: Position) =
|
||||
@ -863,12 +865,39 @@ class LiteralValue(val type: DataType,
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> true
|
||||
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val bh = bytevalue?.hashCode() ?: 0x10001234
|
||||
val wh = wordvalue?.hashCode() ?: 0x01002345
|
||||
val fh = floatvalue?.hashCode() ?: 0x00103456
|
||||
val sh = strvalue?.hashCode() ?: 0x00014567
|
||||
val ah = arrayvalue?.hashCode() ?: 0x11119876
|
||||
return bh xor wh xor fh xor sh xor ah xor type.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is LiteralValue)
|
||||
return false
|
||||
return compareTo(other)==0
|
||||
}
|
||||
|
||||
operator fun compareTo(other: LiteralValue): Int {
|
||||
val numLeft = asNumericValue?.toDouble()
|
||||
val numRight = other.asNumericValue?.toDouble()
|
||||
if(numLeft!=null && numRight!=null)
|
||||
return numLeft.compareTo(numRight)
|
||||
|
||||
if(strvalue!=null && other.strvalue!=null)
|
||||
return strvalue.compareTo(other.strvalue)
|
||||
|
||||
throw ExpressionError("cannot compare type $type with ${other.type}", other.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RangeExpr(var from: IExpression,
|
||||
var to: IExpression,
|
||||
var step: IExpression?,
|
||||
var step: IExpression,
|
||||
override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
@ -876,7 +905,7 @@ class RangeExpr(var from: IExpression,
|
||||
this.parent = parent
|
||||
from.linkParents(this)
|
||||
to.linkParents(this)
|
||||
step?.linkParents(this)
|
||||
step.linkParents(this)
|
||||
}
|
||||
|
||||
override fun constValue(namespace: INameScope): LiteralValue? = null
|
||||
@ -926,8 +955,7 @@ class RangeExpr(var from: IExpression,
|
||||
fromVal = (from as LiteralValue).asIntegerValue!!
|
||||
toVal = (to as LiteralValue).asIntegerValue!!
|
||||
}
|
||||
val stepLv = step as? LiteralValue ?: return null
|
||||
val stepVal = stepLv.asIntegerValue ?: 1
|
||||
val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1
|
||||
return when {
|
||||
fromVal <= toVal -> when {
|
||||
stepVal <= 0 -> IntRange.EMPTY
|
||||
@ -1030,7 +1058,10 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
||||
}
|
||||
|
||||
|
||||
class Jump(val address: Int?, val identifier: IdentifierReference?, override val position: Position) : IStatement {
|
||||
class Jump(val address: Int?,
|
||||
val identifier: IdentifierReference?,
|
||||
val generatedLabel: String?,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@ -1041,7 +1072,7 @@ class Jump(val address: Int?, val identifier: IdentifierReference?, override val
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Jump(addr: $address, identifier: $identifier, target: pos=$position)"
|
||||
return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1445,7 +1476,7 @@ private fun prog8Parser.UnconditionaljumpContext.toAst(): IStatement {
|
||||
if(identifier()!=null) identifier()?.toAst()
|
||||
else scoped_identifier()?.toAst()
|
||||
|
||||
return Jump(address, identifier, toPosition())
|
||||
return Jump(address, identifier, null, toPosition())
|
||||
}
|
||||
|
||||
|
||||
@ -1585,7 +1616,8 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
||||
if(funcall!=null) return funcall
|
||||
|
||||
if (rangefrom!=null && rangeto!=null) {
|
||||
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), rangestep?.toAst(), toPosition())
|
||||
val step = rangestep?.toAst() ?: LiteralValue(DataType.BYTE, 1, position = toPosition())
|
||||
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
||||
}
|
||||
|
||||
if(childCount==3 && children[0].text=="(" && children[2].text==")")
|
||||
|
@ -428,7 +428,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
super.process(range)
|
||||
val from = range.from.constValue(namespace)
|
||||
val to = range.to.constValue(namespace)
|
||||
var step = 0
|
||||
var step = 1
|
||||
if(range.step!=null) {
|
||||
val stepLv = range.step?.constValue(namespace) ?: LiteralValue(DataType.BYTE, 1, position = range.position)
|
||||
if (stepLv.asIntegerValue == null || stepLv.asIntegerValue == 0) {
|
||||
@ -441,10 +441,10 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
when {
|
||||
from.asIntegerValue!=null && to.asIntegerValue!=null -> {
|
||||
if(from.asIntegerValue == to.asIntegerValue)
|
||||
printWarning("range contains just a single value", range.position)
|
||||
else if(from.asIntegerValue < to.asIntegerValue && step<0)
|
||||
err("range is just a single value - don't use a loop here, use an assignment") // TODO optimize this away automatically in the statement optimizer
|
||||
else if(from.asIntegerValue < to.asIntegerValue && step<=0)
|
||||
err("ascending range requires step > 0")
|
||||
else if(from.asIntegerValue > to.asIntegerValue && step>0)
|
||||
else if(from.asIntegerValue > to.asIntegerValue && step>=0)
|
||||
err("descending range requires step < 0")
|
||||
}
|
||||
from.strvalue!=null && to.strvalue!=null -> {
|
||||
@ -452,9 +452,9 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
err("range from and to must be a single character")
|
||||
if(from.strvalue[0] == to.strvalue[0])
|
||||
printWarning("range contains just a single character", range.position)
|
||||
else if(from.strvalue[0] < to.strvalue[0] && step<0)
|
||||
else if(from.strvalue[0] < to.strvalue[0] && step<=0)
|
||||
err("ascending range requires step > 0")
|
||||
else if(from.strvalue[0] > to.strvalue[0] && step<0)
|
||||
else if(from.strvalue[0] > to.strvalue[0] && step>=0)
|
||||
err("descending range requires step < 0")
|
||||
}
|
||||
else -> err("range expression must be over integers or over characters")
|
||||
|
@ -1,5 +1,6 @@
|
||||
package prog8.compiler
|
||||
|
||||
import jdk.nashorn.internal.ir.JumpStatement
|
||||
import prog8.ast.*
|
||||
import prog8.stackvm.*
|
||||
import java.io.PrintStream
|
||||
@ -388,14 +389,16 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
var jumpAddress: Value? = null
|
||||
var jumpLabel: String? = null
|
||||
|
||||
if(stmt.address!=null) {
|
||||
jumpAddress = Value(DataType.WORD, stmt.address)
|
||||
} else {
|
||||
val target = stmt.identifier!!.targetStatement(namespace)!!
|
||||
jumpLabel = when(target) {
|
||||
is Label -> target.scopedname
|
||||
is Subroutine -> target.scopedname
|
||||
else -> throw CompilerException("invalid jump target type ${target::class}")
|
||||
when {
|
||||
stmt.generatedLabel!=null -> jumpLabel = stmt.generatedLabel
|
||||
stmt.address!=null -> jumpAddress = Value(DataType.WORD, stmt.address)
|
||||
else -> {
|
||||
val target = stmt.identifier!!.targetStatement(namespace)!!
|
||||
jumpLabel = when(target) {
|
||||
is Label -> target.scopedname
|
||||
is Subroutine -> target.scopedname
|
||||
else -> throw CompilerException("invalid jump target type ${target::class}")
|
||||
}
|
||||
}
|
||||
}
|
||||
stackvmProg.line(stmt.position)
|
||||
@ -521,27 +524,34 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
|
||||
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 (loopVarDt) {
|
||||
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")
|
||||
if(range!=null) {
|
||||
// loop over a range with constant start, last and step values
|
||||
if (range.isEmpty())
|
||||
throw CompilerException("loop over empty range should have been optimized away")
|
||||
else if (range.count()==1)
|
||||
throw CompilerException("loop over just 1 value should have been optimized away")
|
||||
if((range.last-range.first) % range.step != 0)
|
||||
throw CompilerException("range first and last must be inclusive")
|
||||
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")
|
||||
}
|
||||
translateForOverConstantRange(loopVarName, loopVarDt, range, loop.body)
|
||||
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")
|
||||
}
|
||||
loop.loopRegister!=null ->
|
||||
translateForOverConstantRange(loopVarName, loopVarDt, range, loop.body)
|
||||
} else {
|
||||
// loop over a range where one or more of the start, last or step values is not a constant
|
||||
if(loop.loopRegister!=null) {
|
||||
translateForOverVariableRange(null, loop.loopRegister, loopVarDt, loop.iterable as RangeExpr, loop.body)
|
||||
else ->
|
||||
}
|
||||
else {
|
||||
translateForOverVariableRange(loop.loopVar!!.nameInSource, null, loopVarDt, loop.iterable as RangeExpr, loop.body)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val litVal = loop.iterable as? LiteralValue
|
||||
@ -562,7 +572,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
private fun translateForOverConstantRange(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)
|
||||
* (and we already know that the range is not empty, and first and last are exactly inclusive.)
|
||||
* (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)
|
||||
* ->
|
||||
@ -573,16 +583,16 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
* ..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
|
||||
* LV++ (if step=1) / LV += step (if step > 1)
|
||||
* LV-- (if step=-1) / LV -= abs(step) (if step < 1)
|
||||
* if LV!=(last+step) goto loop
|
||||
* break:
|
||||
*
|
||||
* nop
|
||||
*/
|
||||
val loopLabel = makeLabel("loop")
|
||||
val continueLabel = makeLabel("continue")
|
||||
val breakLabel = makeLabel("break")
|
||||
|
||||
continueStmtLabelStack.push(continueLabel)
|
||||
breakStmtLabelStack.push(breakLabel)
|
||||
|
||||
@ -608,17 +618,16 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO: optimize edge cases if last value = 255 or 0 (for bytes) etc. to avoid PUSH / SUB opcodes and make use of the wrapping around of the value.
|
||||
stackvmProg.instr(Opcode.PUSH, Value(varDt, range.last+range.step))
|
||||
stackvmProg.instr(Opcode.PUSH_VAR, varValue)
|
||||
stackvmProg.instr(Opcode.SUB)
|
||||
stackvmProg.instr(Opcode.BNE, callLabel = loopLabel)
|
||||
|
||||
stackvmProg.label(breakLabel)
|
||||
stackvmProg.instr(Opcode.NOP)
|
||||
// note: ending value of loop register / variable is *undefined* after this point!
|
||||
|
||||
breakStmtLabelStack.pop()
|
||||
continueStmtLabelStack.pop()
|
||||
@ -627,22 +636,26 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
private fun translateForOverVariableRange(varname: List<String>?, register: Register?, varDt: DataType, range: RangeExpr, 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)
|
||||
* (and finally, start and last are constant integer values)
|
||||
* (where at least one of the start, last, step values is not a constant)
|
||||
* (so we can't make any static assumptions about them)
|
||||
* ->
|
||||
* LV = start
|
||||
* loop:
|
||||
* if (step > 0) {
|
||||
* if(LV>last) goto break
|
||||
* } else {
|
||||
* if(LV<last) goto break
|
||||
* }
|
||||
* ..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
|
||||
* LV ++ / LV-- (if step =1 or step=-1)
|
||||
* LV += step (otherwise)
|
||||
* goto loop
|
||||
* break:
|
||||
*
|
||||
* nop
|
||||
*/
|
||||
fun makeAssignmentTarget(): AssignTarget {
|
||||
return if(varname!=null)
|
||||
@ -651,54 +664,72 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
AssignTarget(register, null, range.position)
|
||||
}
|
||||
|
||||
var assignmentTarget = makeAssignmentTarget()
|
||||
val startAssignment = Assignment(assignmentTarget, null, range.from, range.position)
|
||||
val startAssignment = Assignment(makeAssignmentTarget(), null, range.from, range.position)
|
||||
startAssignment.linkParents(range.parent)
|
||||
|
||||
var stepIncrement: PostIncrDecr? = null
|
||||
var stepAddition: Assignment? = null
|
||||
if(range.step==null) {
|
||||
assignmentTarget = makeAssignmentTarget()
|
||||
stepIncrement = PostIncrDecr(assignmentTarget, "++", range.position)
|
||||
stepIncrement.linkParents(range.parent)
|
||||
}
|
||||
else {
|
||||
assignmentTarget = makeAssignmentTarget()
|
||||
stepAddition = Assignment(
|
||||
assignmentTarget,
|
||||
"+=",
|
||||
range.step ?: LiteralValue(DataType.BYTE, 1, position = range.position),
|
||||
range.position
|
||||
)
|
||||
stepAddition.linkParents(range.parent)
|
||||
}
|
||||
translate(startAssignment)
|
||||
|
||||
val loopLabel = makeLabel("loop")
|
||||
val continueLabel = makeLabel("continue")
|
||||
val breakLabel = makeLabel("break")
|
||||
val literalStepValue = (range.step as? LiteralValue)?.asNumericValue?.toInt()
|
||||
|
||||
continueStmtLabelStack.push(continueLabel)
|
||||
breakStmtLabelStack.push(breakLabel)
|
||||
|
||||
TODO("fix this code generated")
|
||||
stackvmProg.label(loopLabel)
|
||||
if(literalStepValue!=null) {
|
||||
val loopVar =
|
||||
if(varname!=null)
|
||||
IdentifierReference(varname, range.position)
|
||||
else
|
||||
RegisterExpr(register!!, range.position)
|
||||
|
||||
val condition =
|
||||
if(literalStepValue > 0) {
|
||||
// if LV > last goto break
|
||||
BinaryExpression(loopVar,">", range.to, range.position)
|
||||
} else {
|
||||
// if LV < last goto break
|
||||
BinaryExpression(loopVar,"<", range.to, range.position)
|
||||
}
|
||||
val ifstmt = IfStatement(condition,
|
||||
listOf(Jump(null, null, breakLabel, range.position)),
|
||||
emptyList(),
|
||||
range.position)
|
||||
ifstmt.linkParents(range.parent)
|
||||
translate(ifstmt)
|
||||
} else {
|
||||
// generate code for the whole if statement
|
||||
TODO("code for non-constant step comparison")
|
||||
}
|
||||
|
||||
translate(body)
|
||||
stackvmProg.label(continueLabel)
|
||||
if(stepAddition!=null)
|
||||
translate(stepAddition)
|
||||
if(stepIncrement!=null)
|
||||
translate(stepIncrement)
|
||||
when (literalStepValue) {
|
||||
1 -> {
|
||||
// LV++
|
||||
val postIncr = PostIncrDecr(makeAssignmentTarget(), "++", range.position)
|
||||
postIncr.linkParents(range.parent)
|
||||
translate(postIncr)
|
||||
}
|
||||
-1 -> {
|
||||
// LV--
|
||||
val postIncr = PostIncrDecr(makeAssignmentTarget(), "--", range.position)
|
||||
postIncr.linkParents(range.parent)
|
||||
translate(postIncr)
|
||||
}
|
||||
else -> {
|
||||
// LV += step
|
||||
val addAssignment = Assignment(makeAssignmentTarget(), "+=", range.step, range.position)
|
||||
addAssignment.linkParents(range.parent)
|
||||
translate(addAssignment)
|
||||
}
|
||||
}
|
||||
|
||||
val comparison = BinaryExpression(
|
||||
if(varname!=null)
|
||||
IdentifierReference(varname, range.position)
|
||||
else
|
||||
RegisterExpr(register!!, range.position)
|
||||
,"<=", range.to, range.position)
|
||||
comparison.linkParents(range.parent)
|
||||
translate(comparison)
|
||||
stackvmProg.instr(Opcode.BNE, callLabel = loopLabel)
|
||||
stackvmProg.label(breakLabel)
|
||||
stackvmProg.instr(Opcode.NOP)
|
||||
// note: ending value of loop register / variable is *undefined* after this point!
|
||||
|
||||
breakStmtLabelStack.pop()
|
||||
continueStmtLabelStack.pop()
|
||||
|
@ -1,7 +1,6 @@
|
||||
package prog8.optimizing
|
||||
|
||||
import prog8.ast.*
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.pow
|
||||
|
||||
class ConstExprEvaluator {
|
||||
@ -21,85 +20,16 @@ class ConstExprEvaluator {
|
||||
"and" -> logicaland(left, right)
|
||||
"or" -> logicalor(left, right)
|
||||
"xor" -> logicalxor(left, right)
|
||||
"<" -> compareless(left, right)
|
||||
">" -> comparegreater(left, right)
|
||||
"<=" -> comparelessequal(left, right)
|
||||
">=" -> comparegreaterequal(left, right)
|
||||
"==" -> compareequal(left, right)
|
||||
"!=" -> comparenotequal(left, right)
|
||||
"<" -> LiteralValue.fromBoolean(left < right, left.position)
|
||||
">" -> LiteralValue.fromBoolean(left > right, left.position)
|
||||
"<=" -> LiteralValue.fromBoolean(left <= right, left.position)
|
||||
">=" -> LiteralValue.fromBoolean(left >= right, left.position)
|
||||
"==" -> LiteralValue.fromBoolean(left == right, left.position)
|
||||
"!=" -> LiteralValue.fromBoolean(left != right, left.position)
|
||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun comparenotequal(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val leq = compareequal(left, right)
|
||||
return LiteralValue.fromBoolean(leq.bytevalue == 1.toShort(), left.position)
|
||||
}
|
||||
|
||||
private fun compareequal(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val leftvalue: Any = when {
|
||||
left.bytevalue!=null -> left.bytevalue
|
||||
left.wordvalue!=null -> left.wordvalue
|
||||
left.floatvalue!=null -> left.floatvalue
|
||||
left.strvalue!=null -> left.strvalue
|
||||
left.arrayvalue!=null -> left.arrayvalue
|
||||
else -> throw FatalAstException("missing literal value")
|
||||
}
|
||||
val rightvalue: Any = when {
|
||||
right.bytevalue!=null -> right.bytevalue
|
||||
right.wordvalue!=null -> right.wordvalue
|
||||
right.floatvalue!=null -> right.floatvalue
|
||||
right.strvalue!=null -> right.strvalue
|
||||
right.arrayvalue!=null -> right.arrayvalue
|
||||
else -> throw FatalAstException("missing literal value")
|
||||
}
|
||||
return LiteralValue.fromBoolean(leftvalue == rightvalue, left.position)
|
||||
}
|
||||
|
||||
private fun comparegreaterequal(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val error = "cannot compute $left >= $right"
|
||||
return when {
|
||||
left.asIntegerValue!=null -> when {
|
||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue >= right.asIntegerValue, left.position)
|
||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue >= right.floatvalue, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue >= right.asIntegerValue, left.position)
|
||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue >= right.floatvalue, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun comparelessequal(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val error = "cannot compute $left >= $right"
|
||||
return when {
|
||||
left.asIntegerValue!=null -> when {
|
||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.asIntegerValue <= right.asIntegerValue, left.position)
|
||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.asIntegerValue <= right.floatvalue, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
left.floatvalue!=null -> when {
|
||||
right.asIntegerValue!=null -> LiteralValue.fromBoolean(left.floatvalue <= right.asIntegerValue, left.position)
|
||||
right.floatvalue!=null -> LiteralValue.fromBoolean(left.floatvalue <= right.floatvalue, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun comparegreater(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val leq = comparelessequal(left, right)
|
||||
return LiteralValue.fromBoolean(leq.bytevalue == 1.toShort(), left.position)
|
||||
}
|
||||
|
||||
private fun compareless(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val leq = comparegreaterequal(left, right)
|
||||
return LiteralValue.fromBoolean(leq.bytevalue == 1.toShort(), left.position)
|
||||
}
|
||||
|
||||
private fun logicalxor(left: LiteralValue, right: LiteralValue): LiteralValue {
|
||||
val error = "cannot compute $left locical-bitxor $right"
|
||||
return when {
|
||||
|
@ -286,6 +286,16 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is Value)
|
||||
return false
|
||||
return compareTo(other)==0
|
||||
}
|
||||
|
||||
operator fun compareTo(other: Value): Int {
|
||||
return numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
||||
}
|
||||
|
||||
fun add(other: Value): Value {
|
||||
val v1 = numericValue()
|
||||
val v2 = other.numericValue()
|
||||
@ -499,13 +509,6 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
||||
else -> throw VmExecutionException("not can only work on byte/word")
|
||||
}
|
||||
}
|
||||
|
||||
fun compareLess(other: Value) = Value(DataType.BYTE, if(this.numericValue().toDouble() < other.numericValue().toDouble()) 1 else 0)
|
||||
fun compareGreater(other: Value) = Value(DataType.BYTE, if(this.numericValue().toDouble() > other.numericValue().toDouble()) 1 else 0)
|
||||
fun compareLessEq(other: Value) = Value(DataType.BYTE, if(this.numericValue().toDouble() <= other.numericValue().toDouble()) 1 else 0)
|
||||
fun compareGreaterEq(other: Value) = Value(DataType.BYTE, if(this.numericValue().toDouble() >= other.numericValue().toDouble()) 1 else 0)
|
||||
fun compareEqual(other: Value) = Value(DataType.BYTE, if(this.numericValue() == other.numericValue()) 1 else 0)
|
||||
fun compareNotEqual(other: Value) = Value(DataType.BYTE, if(this.numericValue() != other.numericValue()) 1 else 0)
|
||||
}
|
||||
|
||||
|
||||
@ -1392,27 +1395,27 @@ class StackVm(val traceOutputFile: String?) {
|
||||
}
|
||||
Opcode.LESS -> {
|
||||
val (top, second) = evalstack.pop2()
|
||||
evalstack.push(second.compareLess(top))
|
||||
evalstack.push(Value(DataType.BYTE, if(second < top) 1 else 0))
|
||||
}
|
||||
Opcode.GREATER -> {
|
||||
val (top, second) = evalstack.pop2()
|
||||
evalstack.push(second.compareGreater(top))
|
||||
evalstack.push(Value(DataType.BYTE, if(second > top) 1 else 0))
|
||||
}
|
||||
Opcode.LESSEQ -> {
|
||||
val (top, second) = evalstack.pop2()
|
||||
evalstack.push(second.compareLessEq(top))
|
||||
evalstack.push(Value(DataType.BYTE, if(second <= top) 1 else 0))
|
||||
}
|
||||
Opcode.GREATEREQ -> {
|
||||
val (top, second) = evalstack.pop2()
|
||||
evalstack.push(second.compareGreaterEq(top))
|
||||
evalstack.push(Value(DataType.BYTE, if(second >= top) 1 else 0))
|
||||
}
|
||||
Opcode.EQUAL -> {
|
||||
val (top, second) = evalstack.pop2()
|
||||
evalstack.push(second.compareEqual(top))
|
||||
evalstack.push(Value(DataType.BYTE, if(second == top) 1 else 0))
|
||||
}
|
||||
Opcode.NOTEQUAL -> {
|
||||
val (top, second) = evalstack.pop2()
|
||||
evalstack.push(second.compareNotEqual(top))
|
||||
evalstack.push(Value(DataType.BYTE, if(second != top) 1 else 0))
|
||||
}
|
||||
Opcode.B2WORD -> {
|
||||
val byte = evalstack.pop()
|
||||
|
@ -1,9 +1,5 @@
|
||||
package prog8tests
|
||||
|
||||
import prog8.ast.DataType
|
||||
import prog8.ast.Position
|
||||
import prog8.ast.VarDecl
|
||||
import prog8.ast.VarDeclType
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.c64.*
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
@ -11,6 +7,8 @@ import org.hamcrest.Matchers.closeTo
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.*
|
||||
import prog8.stackvm.Value
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
@ -290,4 +288,56 @@ class TestPetscii {
|
||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1)) }
|
||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(256)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLiteralValueComparisons() {
|
||||
val ten = LiteralValue(DataType.WORD, wordvalue=10, position=Position("", 0 ,0 ,0))
|
||||
val nine = LiteralValue(DataType.BYTE, bytevalue=9, position=Position("", 0 ,0 ,0))
|
||||
assertTrue(ten == ten)
|
||||
assertFalse(ten == nine)
|
||||
assertFalse(ten != ten)
|
||||
assertTrue(ten != nine)
|
||||
|
||||
assertTrue(ten > nine)
|
||||
assertTrue(ten >= nine)
|
||||
assertTrue(ten >= ten)
|
||||
assertFalse(ten > ten)
|
||||
|
||||
assertFalse(ten < nine)
|
||||
assertFalse(ten <= nine)
|
||||
assertTrue(ten <= ten)
|
||||
assertFalse(ten < ten)
|
||||
|
||||
val abc = LiteralValue(DataType.STR, strvalue = "abc", position=Position("", 0 ,0 ,0))
|
||||
val abd = LiteralValue(DataType.STR, strvalue = "abd", position=Position("", 0 ,0 ,0))
|
||||
assertTrue(abc==abc)
|
||||
assertTrue(abc!=abd)
|
||||
assertFalse(abc!=abc)
|
||||
assertTrue(abc < abd)
|
||||
assertTrue(abc <= abd)
|
||||
assertFalse(abd <= abc)
|
||||
assertTrue(abd >= abc)
|
||||
assertTrue(abd > abc)
|
||||
assertFalse(abc > abd)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStackvmValueComparisons() {
|
||||
val ten = Value(DataType.FLOAT, 10)
|
||||
val nine = Value(DataType.WORD, 9)
|
||||
assertTrue(ten == ten)
|
||||
assertFalse(ten == nine)
|
||||
assertFalse(ten != ten)
|
||||
assertTrue(ten != nine)
|
||||
|
||||
assertTrue(ten > nine)
|
||||
assertTrue(ten >= nine)
|
||||
assertTrue(ten >= ten)
|
||||
assertFalse(ten > ten)
|
||||
|
||||
assertFalse(ten < nine)
|
||||
assertFalse(ten <= nine)
|
||||
assertTrue(ten <= ten)
|
||||
assertFalse(ten < ten)
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,8 @@ Label
|
||||
This is a named position in your code where you can jump to from another place.
|
||||
You can jump to it with a jump statement elsewhere. It is also possible to use a
|
||||
subroutine call to a label (but without parameters and return value).
|
||||
|
||||
Labels can only be defined in a block or in another subroutine, so you can't define a label
|
||||
inside a loop statement block for instance.
|
||||
|
||||
Scope
|
||||
Also known as 'namespace', this is a named box around the symbols defined in it.
|
||||
@ -299,6 +300,11 @@ The *repeat--until* loop is used to repeat a piece of code until a certain condi
|
||||
|
||||
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
||||
|
||||
.. attention::
|
||||
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
|
||||
after the loop without first assigning a new value to it!
|
||||
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
||||
|
||||
|
||||
Conditional Execution
|
||||
---------------------
|
||||
|
Loading…
Reference in New Issue
Block a user