for loop and comparison expression fixes

This commit is contained in:
Irmen de Jong 2018-09-20 01:13:21 +02:00
parent b72bd805e1
commit 455f60fb84
9 changed files with 281 additions and 250 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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