mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
compilation of builtin functions to opcode
untit tests for stackvm opcodes, value and parser literalvalue
This commit is contained in:
parent
3a0c1c5ada
commit
cef0aae927
@ -23,11 +23,14 @@
|
|||||||
_vm_gfx_clearscr(11)
|
_vm_gfx_clearscr(11)
|
||||||
_vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...")
|
_vm_gfx_text(5, 5, 7, "Calculating Mandelbrot Fractal...")
|
||||||
|
|
||||||
|
flt(44)
|
||||||
for pixely in yoffset to yoffset+height-1 {
|
for pixely in yoffset to yoffset+height-1 {
|
||||||
yy = (pixely-yoffset)/height/3.6+0.4
|
; yy = (pixely-yoffset)/height/3.6+0.4 ; @todo compiler float error
|
||||||
|
yy = flt((pixely-yoffset))/height/3.6+0.4
|
||||||
|
|
||||||
for pixelx in xoffset to xoffset+width-1 {
|
for pixelx in xoffset to xoffset+width-1 {
|
||||||
xx = (pixelx-xoffset)/width/3+0.2
|
; xx = (pixelx-xoffset)/width/3+0.2 ; @todo compiler float error
|
||||||
|
xx = flt((pixelx-xoffset))/width/3+0.2
|
||||||
|
|
||||||
x = 0.0
|
x = 0.0
|
||||||
y = 0.0
|
y = 0.0
|
||||||
|
@ -1,9 +1,26 @@
|
|||||||
|
%option enable_floats
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
sub start() -> () {
|
sub start() -> () {
|
||||||
|
|
||||||
byte i=2
|
byte i=2
|
||||||
|
float f
|
||||||
|
word ww = $55aa
|
||||||
|
|
||||||
|
; P_carry(1) @todo function -> assignment
|
||||||
|
; P_irqd(1) @todo function -> assignment
|
||||||
|
f=flt(i)
|
||||||
|
i = msb(ww)
|
||||||
|
i = lsb(ww)
|
||||||
|
lsl(i)
|
||||||
|
lsr(i)
|
||||||
|
rol(i)
|
||||||
|
ror(i)
|
||||||
|
rol2(i)
|
||||||
|
ror2(i)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while i<10 {
|
while i<10 {
|
||||||
_vm_write_num(i)
|
_vm_write_num(i)
|
||||||
|
@ -1137,14 +1137,8 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
"all" -> builtinAll(arglist, position, namespace)
|
"all" -> builtinAll(arglist, position, namespace)
|
||||||
"floor" -> builtinFloor(arglist, position, namespace)
|
"floor" -> builtinFloor(arglist, position, namespace)
|
||||||
"ceil" -> builtinCeil(arglist, position, namespace)
|
"ceil" -> builtinCeil(arglist, position, namespace)
|
||||||
"lsl" -> builtinLsl(arglist, position, namespace)
|
"lsl", "lsr", "rol", "rol2", "ror", "ror2", "P_carry", "P_irqd" ->
|
||||||
"lsr" -> builtinLsr(arglist, position, namespace)
|
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used in expressions because it doesn't return a value", position)
|
||||||
"rol" -> throw ExpressionError("builtin function rol can't be used in expressions because it doesn't return a value", position)
|
|
||||||
"rol2" -> throw ExpressionError("builtin function rol2 can't be used in expressions because it doesn't return a value", position)
|
|
||||||
"ror" -> throw ExpressionError("builtin function ror can't be used in expressions because it doesn't return a value", position)
|
|
||||||
"ror2" -> throw ExpressionError("builtin function ror2 can't be used in expressions because it doesn't return a value", position)
|
|
||||||
"P_carry" -> throw ExpressionError("builtin function P_carry can't be used in expressions because it doesn't return a value", position)
|
|
||||||
"P_irqd" -> throw ExpressionError("builtin function P_irqd can't be used in expressions because it doesn't return a value", position)
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if(withDatatypeCheck) {
|
if(withDatatypeCheck) {
|
||||||
|
@ -303,9 +303,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
|||||||
expr.arglist.forEach { translate(it) }
|
expr.arglist.forEach { translate(it) }
|
||||||
val target = expr.target.targetStatement(namespace)
|
val target = expr.target.targetStatement(namespace)
|
||||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||||
// call to a builtin function
|
// call to a builtin function (some will just be an opcode!)
|
||||||
val funcname = expr.target.nameInSource[0].toUpperCase()
|
val funcname = expr.target.nameInSource[0]
|
||||||
createFunctionCall(funcname) // call builtin function
|
translateFunctionCall(funcname, expr.arglist)
|
||||||
} else {
|
} else {
|
||||||
when(target) {
|
when(target) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
@ -344,6 +344,31 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translateFunctionCall(funcname: String, args: List<IExpression>) {
|
||||||
|
// some functions are implemented as vm opcodes
|
||||||
|
when (funcname) {
|
||||||
|
"flt" -> {
|
||||||
|
// 1 argument, type determines the exact opcode to use
|
||||||
|
val arg = args.single()
|
||||||
|
when (arg.resultingDatatype(namespace)) {
|
||||||
|
DataType.BYTE -> stackvmProg.instr(Opcode.B2FLOAT)
|
||||||
|
DataType.WORD -> stackvmProg.instr(Opcode.W2FLOAT)
|
||||||
|
DataType.FLOAT -> stackvmProg.instr(Opcode.NOP)
|
||||||
|
else -> throw CompilerException("wrong datatype for flt()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"msb" -> stackvmProg.instr(Opcode.MSB)
|
||||||
|
"lsb" -> stackvmProg.instr(Opcode.LSB)
|
||||||
|
"lsl" -> stackvmProg.instr(Opcode.SHL)
|
||||||
|
"lsr" -> stackvmProg.instr(Opcode.SHR)
|
||||||
|
"rol" -> stackvmProg.instr(Opcode.ROL)
|
||||||
|
"ror" -> stackvmProg.instr(Opcode.ROR)
|
||||||
|
"rol2" -> stackvmProg.instr(Opcode.ROL2)
|
||||||
|
"ror2" -> stackvmProg.instr(Opcode.ROR2)
|
||||||
|
else -> createSyscall(funcname) // call builtin function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateBinaryOperator(operator: String) {
|
private fun translateBinaryOperator(operator: String) {
|
||||||
val opcode = when(operator) {
|
val opcode = when(operator) {
|
||||||
"+" -> Opcode.ADD
|
"+" -> Opcode.ADD
|
||||||
@ -375,8 +400,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
|||||||
val targetStmt = stmt.target.targetStatement(namespace)!!
|
val targetStmt = stmt.target.targetStatement(namespace)!!
|
||||||
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
||||||
stmt.arglist.forEach { translate(it) }
|
stmt.arglist.forEach { translate(it) }
|
||||||
val funcname = stmt.target.nameInSource[0].toUpperCase()
|
val funcname = stmt.target.nameInSource[0]
|
||||||
createFunctionCall(funcname) // call builtin function
|
translateFunctionCall(funcname, stmt.arglist)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,9 +414,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
|||||||
stackvmProg.instr(Opcode.CALL, callLabel = targetname)
|
stackvmProg.instr(Opcode.CALL, callLabel = targetname)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFunctionCall(funcname: String) {
|
private fun createSyscall(funcname: String) {
|
||||||
val function = (
|
val function = (
|
||||||
if (funcname.startsWith("_VM_"))
|
if (funcname.startsWith("_vm_"))
|
||||||
funcname.substring(4)
|
funcname.substring(4)
|
||||||
else
|
else
|
||||||
"FUNC_$funcname"
|
"FUNC_$funcname"
|
||||||
@ -724,7 +749,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
|||||||
translate(ifstmt)
|
translate(ifstmt)
|
||||||
} else {
|
} else {
|
||||||
// Step is a variable. We can't optimize anything...
|
// Step is a variable. We can't optimize anything...
|
||||||
TODO("code for non-constant step comparison of LV")
|
TODO("for loop with non-constant step comparison of LV")
|
||||||
}
|
}
|
||||||
|
|
||||||
translate(body)
|
translate(body)
|
||||||
@ -760,7 +785,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
|||||||
val postIncr = PostIncrDecr(makeAssignmentTarget(), "--", range.position)
|
val postIncr = PostIncrDecr(makeAssignmentTarget(), "--", range.position)
|
||||||
postIncr.linkParents(range.parent)
|
postIncr.linkParents(range.parent)
|
||||||
translate(postIncr)
|
translate(postIncr)
|
||||||
TODO("signed numbers and/or special condition still needed for decreasing for loop. Try increasing loop and/or constant loop values instead? At: ${range.position}")
|
TODO("signed numbers and/or special condition are needed for decreasing for loop. Try an increasing loop and/or constant loop values instead? At: ${range.position}")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
TODO("non-literal-const or other-than-one step increment code At: ${range.position}")
|
TODO("non-literal-const or other-than-one step increment code At: ${range.position}")
|
||||||
|
@ -14,7 +14,8 @@ val BuiltinFunctionNames = setOf(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
val BuiltinFunctionsWithoutSideEffects = BuiltinFunctionNames - setOf("P_carry", "P_irqd",
|
val BuiltinFunctionsWithoutSideEffects = BuiltinFunctionNames - setOf(
|
||||||
|
"P_carry", "P_irqd", "lsl", "lsr", "rol", "ror", "rol2", "ror2",
|
||||||
"_vm_write_memchr", "_vm_write_memstr", "_vm_write_num", "_vm_write_char",
|
"_vm_write_memchr", "_vm_write_memstr", "_vm_write_num", "_vm_write_char",
|
||||||
"_vm_write_str", "_vm_gfx_clearscr", "_vm_gfx_pixel", "_vm_gfx_text")
|
"_vm_write_str", "_vm_gfx_clearscr", "_vm_gfx_pixel", "_vm_gfx_text")
|
||||||
|
|
||||||
@ -232,12 +233,6 @@ fun builtinLsb(args: List<IExpression>, position: Position, namespace:INameScope
|
|||||||
fun builtinMsb(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
fun builtinMsb(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
||||||
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 8 and 255}
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 8 and 255}
|
||||||
|
|
||||||
fun builtinLsl(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
|
||||||
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
|
|
||||||
|
|
||||||
fun builtinLsr(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
|
||||||
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 1 }
|
|
||||||
|
|
||||||
fun builtinMin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
fun builtinMin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
||||||
= collectionArgOutputNumber(args, position, namespace) { it.min()!! }
|
= collectionArgOutputNumber(args, position, namespace) { it.min()!! }
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ enum class Syscall(val callNr: Short) {
|
|||||||
FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0)
|
FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0)
|
||||||
|
|
||||||
// note: not all builtin functions of the Prog8 language are present as functions:
|
// note: not all builtin functions of the Prog8 language are present as functions:
|
||||||
// some of them are already opcodes (such as MSB and ROL and FLT)!
|
// some of them are already opcodes (such as MSB, LSB, LSL, LSR, ROL, ROR, ROL2, ROR2, and FLT)!
|
||||||
}
|
}
|
||||||
|
|
||||||
class Memory {
|
class Memory {
|
||||||
@ -301,61 +301,130 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: Value): Int {
|
operator fun compareTo(other: Value): Int {
|
||||||
if(stringvalue!=null && other.stringvalue!=null)
|
return when(type) {
|
||||||
return stringvalue.compareTo(other.stringvalue)
|
DataType.BYTE, DataType.WORD, DataType.FLOAT -> {
|
||||||
return numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
when(other.type) {
|
||||||
|
DataType.BYTE, DataType.WORD, DataType.FLOAT -> {
|
||||||
|
numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
||||||
|
}
|
||||||
|
else -> throw VmExecutionException("comparison can only be done between two numeric values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw VmExecutionException("comparison can only be done between two numeric values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): Value {
|
||||||
|
if(result.toDouble() < 0 ) {
|
||||||
|
return when(leftDt) {
|
||||||
|
DataType.BYTE -> {
|
||||||
|
// BYTE can become WORD if right operand is WORD, or when value is too large for byte
|
||||||
|
when(rightDt) {
|
||||||
|
DataType.BYTE -> Value(DataType.BYTE, result.toInt() and 255)
|
||||||
|
DataType.WORD -> Value(DataType.WORD, result.toInt() and 65535)
|
||||||
|
DataType.FLOAT -> throw VmExecutionException("floating point loss of precision")
|
||||||
|
else -> throw VmExecutionException("$op on non-numeric result type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> Value(DataType.WORD, result.toInt() and 65535)
|
||||||
|
DataType.FLOAT -> Value(DataType.FLOAT, result)
|
||||||
|
else -> throw VmExecutionException("$op on non-numeric type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return when(leftDt) {
|
||||||
|
DataType.BYTE -> {
|
||||||
|
// BYTE can become WORD if right operand is WORD, or when value is too large for byte
|
||||||
|
if(result.toDouble() >= 256)
|
||||||
|
return Value(DataType.WORD, result)
|
||||||
|
when(rightDt) {
|
||||||
|
DataType.BYTE -> Value(DataType.BYTE, result)
|
||||||
|
DataType.WORD -> Value(DataType.WORD, result)
|
||||||
|
DataType.FLOAT -> throw VmExecutionException("floating point loss of precision")
|
||||||
|
else -> throw VmExecutionException("$op on non-numeric result type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> Value(DataType.WORD, result)
|
||||||
|
DataType.FLOAT -> Value(DataType.FLOAT, result)
|
||||||
|
else -> throw VmExecutionException("$op on non-numeric type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun add(other: Value): Value {
|
fun add(other: Value): Value {
|
||||||
|
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||||
|
throw VmExecutionException("floating point loss of precision on type $type")
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
val result = v1.toDouble() + v2.toDouble()
|
val result = v1.toDouble() + v2.toDouble()
|
||||||
return Value(type, result)
|
return arithResult(type, result, other.type, "add")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sub(other: Value): Value {
|
fun sub(other: Value): Value {
|
||||||
|
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||||
|
throw VmExecutionException("floating point loss of precision on type $type")
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
val result = v1.toDouble() - v2.toDouble()
|
val result = v1.toDouble() - v2.toDouble()
|
||||||
return Value(type, result)
|
return arithResult(type, result, other.type, "sub")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mul(other: Value): Value {
|
fun mul(other: Value): Value {
|
||||||
|
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||||
|
throw VmExecutionException("floating point loss of precision on type $type")
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
val result = v1.toDouble() * v2.toDouble()
|
val result = v1.toDouble() * v2.toDouble()
|
||||||
return Value(type, result)
|
return arithResult(type, result, other.type, "mul")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun div(other: Value): Value {
|
fun div(other: Value): Value {
|
||||||
|
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||||
|
throw VmExecutionException("floating point loss of precision on type $type")
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
|
if(v2.toDouble()==0.0) {
|
||||||
|
if (type == DataType.BYTE)
|
||||||
|
return Value(DataType.BYTE, 255)
|
||||||
|
else if(type == DataType.WORD)
|
||||||
|
return Value(DataType.WORD, 65535)
|
||||||
|
}
|
||||||
val result = v1.toDouble() / v2.toDouble()
|
val result = v1.toDouble() / v2.toDouble()
|
||||||
return Value(DataType.FLOAT, result)
|
// NOTE: integer division returns integer result!
|
||||||
|
return when(type) {
|
||||||
|
DataType.BYTE -> Value(DataType.BYTE, result)
|
||||||
|
DataType.WORD -> Value(DataType.WORD, result)
|
||||||
|
DataType.FLOAT -> Value(DataType.FLOAT, result)
|
||||||
|
else -> throw VmExecutionException("div on non-numeric type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun floordiv(other: Value): Value {
|
fun floordiv(other: Value): Value {
|
||||||
|
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||||
|
throw VmExecutionException("floating point loss of precision on type $type")
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
val result = (v1.toDouble() / v2.toDouble()).toInt()
|
val result = floor(v1.toDouble() / v2.toDouble())
|
||||||
return if(this.type==DataType.BYTE)
|
// NOTE: integer division returns integer result!
|
||||||
Value(DataType.BYTE, (result and 255).toShort())
|
return when(type) {
|
||||||
else
|
DataType.BYTE -> Value(DataType.BYTE, result)
|
||||||
Value(DataType.WORD, result and 65535)
|
DataType.WORD -> Value(DataType.WORD, result)
|
||||||
|
DataType.FLOAT -> Value(DataType.FLOAT, result)
|
||||||
|
else -> throw VmExecutionException("div on non-numeric type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remainder(other: Value): Value? {
|
fun remainder(other: Value): Value? {
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
val result = v1.toDouble() % v2.toDouble()
|
val result = v1.toDouble() % v2.toDouble()
|
||||||
return Value(type, result)
|
return arithResult(type, result, other.type, "remainder")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pow(other: Value): Value {
|
fun pow(other: Value): Value {
|
||||||
val v1 = numericValue()
|
val v1 = numericValue()
|
||||||
val v2 = other.numericValue()
|
val v2 = other.numericValue()
|
||||||
val result = v1.toDouble().pow(v2.toDouble())
|
val result = v1.toDouble().pow(v2.toDouble())
|
||||||
return Value(type, result) // @todo datatype of pow is now always float, maybe allow byte/word results as well
|
return arithResult(type, result, other.type,"pow")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shl(): Value {
|
fun shl(): Value {
|
||||||
|
@ -344,8 +344,8 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 40)
|
Value(DataType.BYTE, 40)
|
||||||
)
|
)
|
||||||
val expected = listOf(
|
val expected = listOf(
|
||||||
Value(DataType.FLOAT, 3999.0/40.0),
|
Value(DataType.WORD, 99),
|
||||||
Value(DataType.FLOAT, 42.25/(3999.0/40.0)))
|
Value(DataType.FLOAT, 42.25/99))
|
||||||
val operator = Opcode.DIV
|
val operator = Opcode.DIV
|
||||||
|
|
||||||
testBinaryOperator(values, operator, expected)
|
testBinaryOperator(values, operator, expected)
|
||||||
@ -354,13 +354,13 @@ class TestStackVmOpcodes {
|
|||||||
@Test
|
@Test
|
||||||
fun testFloorDiv() {
|
fun testFloorDiv() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.FLOAT, 42.25),
|
Value(DataType.FLOAT, 4000.25),
|
||||||
Value(DataType.WORD, 3999),
|
Value(DataType.WORD, 3999),
|
||||||
Value(DataType.BYTE, 40)
|
Value(DataType.BYTE, 40)
|
||||||
)
|
)
|
||||||
val expected = listOf(
|
val expected = listOf(
|
||||||
Value(DataType.WORD, floor(3999.0/40.0)),
|
Value(DataType.WORD, 99),
|
||||||
Value(DataType.WORD, floor(42.25/floor(3999.0/40.0))))
|
Value(DataType.FLOAT, 40.0))
|
||||||
val operator = Opcode.FLOORDIV
|
val operator = Opcode.FLOORDIV
|
||||||
|
|
||||||
testBinaryOperator(values, operator, expected)
|
testBinaryOperator(values, operator, expected)
|
||||||
@ -833,10 +833,6 @@ class TestStackVmOpcodes {
|
|||||||
@Test
|
@Test
|
||||||
fun testLess() {
|
fun testLess() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.STR, null, stringvalue = "hello"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "hello"), // 0
|
|
||||||
Value(DataType.STR, null, stringvalue = "abc"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "abd"), // 1
|
|
||||||
Value(DataType.BYTE, 0),
|
Value(DataType.BYTE, 0),
|
||||||
Value(DataType.BYTE, 1), // 1
|
Value(DataType.BYTE, 1), // 1
|
||||||
Value(DataType.BYTE, 1),
|
Value(DataType.BYTE, 1),
|
||||||
@ -852,17 +848,21 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 21),
|
Value(DataType.BYTE, 21),
|
||||||
Value(DataType.FLOAT, 21.0001) // 1
|
Value(DataType.FLOAT, 21.0001) // 1
|
||||||
)
|
)
|
||||||
val expected = listOf(0, 1, 1, 0, 1, 1, 0, 0, 1)
|
val expected = listOf(1, 0, 1, 1, 0, 0, 1)
|
||||||
testComparisonOperator(values, expected, Opcode.LESS)
|
testComparisonOperator(values, expected, Opcode.LESS)
|
||||||
|
|
||||||
|
val valuesInvalid = listOf(
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello"),
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello")
|
||||||
|
)
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
testComparisonOperator(valuesInvalid, listOf(0), Opcode.LESS) // can't compare strings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLessEq() {
|
fun testLessEq() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.STR, null, stringvalue = "hello"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "hello"), // 1
|
|
||||||
Value(DataType.STR, null, stringvalue = "abc"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "abd"), // 1
|
|
||||||
Value(DataType.BYTE, 0),
|
Value(DataType.BYTE, 0),
|
||||||
Value(DataType.BYTE, 1), // 1
|
Value(DataType.BYTE, 1), // 1
|
||||||
Value(DataType.BYTE, 1),
|
Value(DataType.BYTE, 1),
|
||||||
@ -878,17 +878,21 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 22),
|
Value(DataType.BYTE, 22),
|
||||||
Value(DataType.FLOAT, 21.999) // 0
|
Value(DataType.FLOAT, 21.999) // 0
|
||||||
)
|
)
|
||||||
val expected = listOf(1,1,1,1,0,1,1,1,0)
|
val expected = listOf(1,1,0,1,1,1,0)
|
||||||
testComparisonOperator(values, expected, Opcode.LESSEQ)
|
testComparisonOperator(values, expected, Opcode.LESSEQ)
|
||||||
|
|
||||||
|
val valuesInvalid = listOf(
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello"),
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello")
|
||||||
|
)
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
testComparisonOperator(valuesInvalid, listOf(0), Opcode.LESSEQ) // can't compare strings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGreater() {
|
fun testGreater() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.STR, null, stringvalue = "hello"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "hello"), // 0
|
|
||||||
Value(DataType.STR, null, stringvalue = "abd"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "abc"), // 1
|
|
||||||
Value(DataType.BYTE, 0),
|
Value(DataType.BYTE, 0),
|
||||||
Value(DataType.BYTE, 1), // 0
|
Value(DataType.BYTE, 1), // 0
|
||||||
Value(DataType.BYTE, 1),
|
Value(DataType.BYTE, 1),
|
||||||
@ -904,17 +908,21 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 21),
|
Value(DataType.BYTE, 21),
|
||||||
Value(DataType.FLOAT, 20.9999) // 1
|
Value(DataType.FLOAT, 20.9999) // 1
|
||||||
)
|
)
|
||||||
val expected = listOf(0, 1, 0, 0, 1, 0, 1, 0, 1)
|
val expected = listOf(0, 0, 1, 0, 1, 0, 1)
|
||||||
testComparisonOperator(values, expected, Opcode.GREATER)
|
testComparisonOperator(values, expected, Opcode.GREATER)
|
||||||
|
|
||||||
|
val valuesInvalid = listOf(
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello"),
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello")
|
||||||
|
)
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
testComparisonOperator(valuesInvalid, listOf(0), Opcode.GREATER) // can't compare strings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGreaterEq() {
|
fun testGreaterEq() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.STR, null, stringvalue = "hello"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "hello"), // 1
|
|
||||||
Value(DataType.STR, null, stringvalue = "abd"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "abc"), // 1
|
|
||||||
Value(DataType.BYTE, 0),
|
Value(DataType.BYTE, 0),
|
||||||
Value(DataType.BYTE, 1), // 0
|
Value(DataType.BYTE, 1), // 0
|
||||||
Value(DataType.BYTE, 1),
|
Value(DataType.BYTE, 1),
|
||||||
@ -930,17 +938,21 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 22),
|
Value(DataType.BYTE, 22),
|
||||||
Value(DataType.FLOAT, 21.999) // 1
|
Value(DataType.FLOAT, 21.999) // 1
|
||||||
)
|
)
|
||||||
val expected = listOf(1,1,0,1,1,0,0,1,1)
|
val expected = listOf(0,1,1,0,0,1,1)
|
||||||
testComparisonOperator(values, expected, Opcode.GREATEREQ)
|
testComparisonOperator(values, expected, Opcode.GREATEREQ)
|
||||||
|
|
||||||
|
val valuesInvalid = listOf(
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello"),
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello")
|
||||||
|
)
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
testComparisonOperator(valuesInvalid, listOf(0), Opcode.GREATEREQ) // can't compare strings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEqual() {
|
fun testEqual() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.STR, null, stringvalue = "hello"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "hello"), // 1
|
|
||||||
Value(DataType.STR, null, stringvalue = "abd"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "abc"), // 0
|
|
||||||
Value(DataType.BYTE, 0),
|
Value(DataType.BYTE, 0),
|
||||||
Value(DataType.BYTE, 1), // 0
|
Value(DataType.BYTE, 1), // 0
|
||||||
Value(DataType.BYTE, 1),
|
Value(DataType.BYTE, 1),
|
||||||
@ -956,17 +968,21 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 22),
|
Value(DataType.BYTE, 22),
|
||||||
Value(DataType.FLOAT, 21.999) // 0
|
Value(DataType.FLOAT, 21.999) // 0
|
||||||
)
|
)
|
||||||
val expected = listOf(1,0,0,1,0,0,1,1,0)
|
val expected = listOf(0,1,0,0,1,1,0)
|
||||||
testComparisonOperator(values, expected, Opcode.EQUAL)
|
testComparisonOperator(values, expected, Opcode.EQUAL)
|
||||||
|
|
||||||
|
val valuesInvalid = listOf(
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello"),
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello")
|
||||||
|
)
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
testComparisonOperator(valuesInvalid, listOf(0), Opcode.EQUAL) // can't compare strings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNotEqual() {
|
fun testNotEqual() {
|
||||||
val values = listOf(
|
val values = listOf(
|
||||||
Value(DataType.STR, null, stringvalue = "hello"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "hello"), // 0
|
|
||||||
Value(DataType.STR, null, stringvalue = "abd"),
|
|
||||||
Value(DataType.STR, null, stringvalue = "abc"), // 1
|
|
||||||
Value(DataType.BYTE, 0),
|
Value(DataType.BYTE, 0),
|
||||||
Value(DataType.BYTE, 1), // 1
|
Value(DataType.BYTE, 1), // 1
|
||||||
Value(DataType.BYTE, 1),
|
Value(DataType.BYTE, 1),
|
||||||
@ -982,8 +998,16 @@ class TestStackVmOpcodes {
|
|||||||
Value(DataType.BYTE, 22),
|
Value(DataType.BYTE, 22),
|
||||||
Value(DataType.FLOAT, 21.999) // 1
|
Value(DataType.FLOAT, 21.999) // 1
|
||||||
)
|
)
|
||||||
val expected = listOf(0,1,1,0,1,1,0,0,1)
|
val expected = listOf(1,0,1,1,0,0,1)
|
||||||
testComparisonOperator(values, expected, Opcode.NOTEQUAL)
|
testComparisonOperator(values, expected, Opcode.NOTEQUAL)
|
||||||
|
|
||||||
|
val valuesInvalid = listOf(
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello"),
|
||||||
|
Value(DataType.STR, null, stringvalue = "hello")
|
||||||
|
)
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
testComparisonOperator(valuesInvalid, listOf(0), Opcode.NOTEQUAL) // can't compare strings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
294
compiler/test/ValueOperationsTests.kt
Normal file
294
compiler/test/ValueOperationsTests.kt
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
package prog8tests
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.*
|
||||||
|
import prog8.stackvm.*
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestStackVmValue {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIdentity() {
|
||||||
|
val v = Value(DataType.WORD, 12345)
|
||||||
|
assertEquals(v, v)
|
||||||
|
assertFalse(v != v)
|
||||||
|
assertTrue(v<=v)
|
||||||
|
assertTrue(v>=v)
|
||||||
|
assertFalse(v<v)
|
||||||
|
assertFalse(v>v)
|
||||||
|
|
||||||
|
assertEquals(Value(DataType.BYTE, 100), Value(DataType.BYTE, 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEqualsAndNotEquals() {
|
||||||
|
assertEquals(Value(DataType.BYTE, 100), Value(DataType.BYTE, 100))
|
||||||
|
assertEquals(Value(DataType.BYTE, 100), Value(DataType.WORD, 100))
|
||||||
|
assertEquals(Value(DataType.BYTE, 100), Value(DataType.FLOAT, 100))
|
||||||
|
assertEquals(Value(DataType.WORD, 254), Value(DataType.BYTE, 254))
|
||||||
|
assertEquals(Value(DataType.WORD, 12345), Value(DataType.WORD, 12345))
|
||||||
|
assertEquals(Value(DataType.WORD, 12345), Value(DataType.FLOAT, 12345))
|
||||||
|
assertEquals(Value(DataType.FLOAT, 100.0), Value(DataType.BYTE, 100))
|
||||||
|
assertEquals(Value(DataType.FLOAT, 22239.0), Value(DataType.WORD, 22239))
|
||||||
|
assertEquals(Value(DataType.FLOAT, 9.99), Value(DataType.FLOAT, 9.99))
|
||||||
|
|
||||||
|
assertNotEquals(Value(DataType.BYTE, 100), Value(DataType.BYTE, 101))
|
||||||
|
assertNotEquals(Value(DataType.BYTE, 100), Value(DataType.WORD, 101))
|
||||||
|
assertNotEquals(Value(DataType.BYTE, 100), Value(DataType.FLOAT, 101))
|
||||||
|
assertNotEquals(Value(DataType.WORD, 245), Value(DataType.BYTE, 246))
|
||||||
|
assertNotEquals(Value(DataType.WORD, 12345), Value(DataType.WORD, 12346))
|
||||||
|
assertNotEquals(Value(DataType.WORD, 12345), Value(DataType.FLOAT, 12346))
|
||||||
|
assertNotEquals(Value(DataType.FLOAT, 9.99), Value(DataType.BYTE, 9))
|
||||||
|
assertNotEquals(Value(DataType.FLOAT, 9.99), Value(DataType.WORD, 9))
|
||||||
|
assertNotEquals(Value(DataType.FLOAT, 9.99), Value(DataType.FLOAT, 9.0))
|
||||||
|
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
assertEquals(Value(DataType.STR, null, "hello"), Value(DataType.STR, null, "hello"))
|
||||||
|
}
|
||||||
|
assertFailsWith<VmExecutionException> {
|
||||||
|
assertEquals(Value(DataType.ARRAY, null, arrayvalue = intArrayOf(1,2,3)), Value(DataType.ARRAY, null, arrayvalue = intArrayOf(1,2,3)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGreaterThan(){
|
||||||
|
assertTrue(Value(DataType.BYTE, 100) > Value(DataType.BYTE, 99))
|
||||||
|
assertTrue(Value(DataType.WORD, 254) > Value(DataType.WORD, 253))
|
||||||
|
assertTrue(Value(DataType.FLOAT, 100.0) > Value(DataType.FLOAT, 99.9))
|
||||||
|
|
||||||
|
assertTrue(Value(DataType.BYTE, 100) >= Value(DataType.BYTE, 100))
|
||||||
|
assertTrue(Value(DataType.WORD, 254) >= Value(DataType.WORD, 254))
|
||||||
|
assertTrue(Value(DataType.FLOAT, 100.0) >= Value(DataType.FLOAT, 100.0))
|
||||||
|
|
||||||
|
assertFalse(Value(DataType.BYTE, 100) > Value(DataType.BYTE, 100))
|
||||||
|
assertFalse(Value(DataType.WORD, 254) > Value(DataType.WORD, 254))
|
||||||
|
assertFalse(Value(DataType.FLOAT, 100.0) > Value(DataType.FLOAT, 100.0))
|
||||||
|
|
||||||
|
assertFalse(Value(DataType.BYTE, 100) >= Value(DataType.BYTE, 101))
|
||||||
|
assertFalse(Value(DataType.WORD, 254) >= Value(DataType.WORD, 255))
|
||||||
|
assertFalse(Value(DataType.FLOAT, 100.0) >= Value(DataType.FLOAT, 100.1))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLessThan() {
|
||||||
|
assertTrue(Value(DataType.BYTE, 100) < Value(DataType.BYTE, 101))
|
||||||
|
assertTrue(Value(DataType.WORD, 254) < Value(DataType.WORD, 255))
|
||||||
|
assertTrue(Value(DataType.FLOAT, 100.0) < Value(DataType.FLOAT, 100.1))
|
||||||
|
|
||||||
|
assertTrue(Value(DataType.BYTE, 100) <= Value(DataType.BYTE, 100))
|
||||||
|
assertTrue(Value(DataType.WORD, 254) <= Value(DataType.WORD, 254))
|
||||||
|
assertTrue(Value(DataType.FLOAT, 100.0) <= Value(DataType.FLOAT, 100.0))
|
||||||
|
|
||||||
|
assertFalse(Value(DataType.BYTE, 100) < Value(DataType.BYTE, 100))
|
||||||
|
assertFalse(Value(DataType.WORD, 254) < Value(DataType.WORD, 254))
|
||||||
|
assertFalse(Value(DataType.FLOAT, 100.0) < Value(DataType.FLOAT, 100.0))
|
||||||
|
|
||||||
|
assertFalse(Value(DataType.BYTE, 100) <= Value(DataType.BYTE, 99))
|
||||||
|
assertFalse(Value(DataType.WORD, 254) <= Value(DataType.WORD, 253))
|
||||||
|
assertFalse(Value(DataType.FLOAT, 100.0) <= Value(DataType.FLOAT, 99.9))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testArithmeticByteFirstOperand() {
|
||||||
|
var r = Value(DataType.BYTE, 100).add(Value(DataType.BYTE, 120))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(220, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).add(Value(DataType.BYTE, 199))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(299, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).sub(Value(DataType.BYTE, 88))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(12, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).sub(Value(DataType.BYTE, 188))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(168, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 5).mul(Value(DataType.BYTE, 33))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(165, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 22).mul(Value(DataType.BYTE, 33))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(726, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 233).div(Value(DataType.BYTE, 12))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(19, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 233).div(Value(DataType.BYTE, 0))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(255, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 233).floordiv(Value(DataType.BYTE, 19))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(12, r.integerValue())
|
||||||
|
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).add(Value(DataType.WORD, 120))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(220, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).add(Value(DataType.WORD, 199))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(299, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).sub(Value(DataType.WORD, 88))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(12, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 100).sub(Value(DataType.WORD, 188))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(65448, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 5).mul(Value(DataType.WORD, 33))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(165, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 22).mul(Value(DataType.WORD, 33))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(726, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 233).div(Value(DataType.WORD, 12))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(19, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 233).div(Value(DataType.WORD, 0))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(255, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.BYTE, 233).floordiv(Value(DataType.WORD, 19))
|
||||||
|
assertEquals(DataType.BYTE, r.type)
|
||||||
|
assertEquals(12, r.integerValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testArithmeticWordFirstOperand() {
|
||||||
|
var r = Value(DataType.WORD, 100).add(Value(DataType.BYTE, 120))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(220, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.WORD, 100).div(Value(DataType.BYTE, 10))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(10, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.WORD, 100).div(Value(DataType.BYTE, 0))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(65535, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.WORD, 100).div(Value(DataType.WORD, 0))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(65535, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.WORD, 33445).floordiv(Value(DataType.WORD, 123))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(271, r.integerValue())
|
||||||
|
|
||||||
|
r = Value(DataType.WORD, 33445).floordiv(Value(DataType.WORD, 999))
|
||||||
|
assertEquals(DataType.WORD, r.type)
|
||||||
|
assertEquals(33, r.integerValue())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestParserLiteralValue {
|
||||||
|
|
||||||
|
private val dummyPos = Position("test", 0,0,0)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIdentity() {
|
||||||
|
val v = LiteralValue(DataType.WORD, wordvalue = 12345, position = dummyPos)
|
||||||
|
assertEquals(v, v)
|
||||||
|
assertFalse(v != v)
|
||||||
|
assertTrue(v <= v)
|
||||||
|
assertTrue(v >= v)
|
||||||
|
assertFalse(v < v)
|
||||||
|
assertFalse(v > v)
|
||||||
|
|
||||||
|
assertEquals(LiteralValue(DataType.WORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.WORD, wordvalue = 12345, position = dummyPos))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEqualsAndNotEquals() {
|
||||||
|
assertEquals(LiteralValue(DataType.BYTE, 100, position=dummyPos), LiteralValue(DataType.BYTE, 100, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.BYTE, 100, position=dummyPos), LiteralValue(DataType.WORD, wordvalue=100, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.BYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos), LiteralValue(DataType.BYTE, 254, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.WORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.WORD, wordvalue=12345, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.WORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12345.0, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos), LiteralValue(DataType.BYTE, 100, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.FLOAT, floatvalue=22239.0, position=dummyPos), LiteralValue(DataType.WORD,wordvalue=22239, position=dummyPos))
|
||||||
|
assertEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos))
|
||||||
|
|
||||||
|
assertNotEquals(LiteralValue(DataType.BYTE, 100, position=dummyPos), LiteralValue(DataType.BYTE, 101, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.BYTE, 100, position=dummyPos), LiteralValue(DataType.WORD, wordvalue=101, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.BYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=101.0, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.WORD, wordvalue=245, position=dummyPos), LiteralValue(DataType.BYTE, 246, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.WORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.WORD, wordvalue=12346, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.WORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12346.0, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.BYTE, 9, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.WORD, wordvalue=9, position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.0, position=dummyPos))
|
||||||
|
|
||||||
|
assertEquals(LiteralValue(DataType.STR, strvalue = "hello", position=dummyPos), LiteralValue(DataType.STR, strvalue="hello", position=dummyPos))
|
||||||
|
assertNotEquals(LiteralValue(DataType.STR, strvalue = "hello", position=dummyPos), LiteralValue(DataType.STR, strvalue="bye", position=dummyPos))
|
||||||
|
|
||||||
|
val lvOne = LiteralValue(DataType.BYTE, 1, position=dummyPos)
|
||||||
|
val lvTwo = LiteralValue(DataType.BYTE, 2, position=dummyPos)
|
||||||
|
val lvThree = LiteralValue(DataType.BYTE, 3, position=dummyPos)
|
||||||
|
val lvOneR = LiteralValue(DataType.BYTE, 1, position=dummyPos)
|
||||||
|
val lvTwoR = LiteralValue(DataType.BYTE, 2, position=dummyPos)
|
||||||
|
val lvThreeR = LiteralValue(DataType.BYTE, 3, position=dummyPos)
|
||||||
|
val lv1 = LiteralValue(DataType.ARRAY, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position=dummyPos)
|
||||||
|
val lv2 = LiteralValue(DataType.ARRAY, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position=dummyPos)
|
||||||
|
assertFailsWith<ExpressionError> {
|
||||||
|
assertEquals(lv1, lv2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGreaterThan(){
|
||||||
|
assertTrue(LiteralValue(DataType.BYTE, 100, position=dummyPos) > LiteralValue(DataType.BYTE, 99, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) > LiteralValue(DataType.WORD, wordvalue=253, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) > LiteralValue(DataType.FLOAT, floatvalue=99.9, position=dummyPos))
|
||||||
|
|
||||||
|
assertTrue(LiteralValue(DataType.BYTE, 100, position=dummyPos) >= LiteralValue(DataType.BYTE, 100, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) >= LiteralValue(DataType.WORD,wordvalue= 254, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||||
|
|
||||||
|
assertFalse(LiteralValue(DataType.BYTE, 100, position=dummyPos) > LiteralValue(DataType.BYTE, 100, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) > LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) > LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||||
|
|
||||||
|
assertFalse(LiteralValue(DataType.BYTE, 100, position=dummyPos) >= LiteralValue(DataType.BYTE, 101, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) >= LiteralValue(DataType.WORD,wordvalue= 255, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue=100.1, position=dummyPos))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLessThan() {
|
||||||
|
assertTrue(LiteralValue(DataType.BYTE, 100, position=dummyPos) < LiteralValue(DataType.BYTE, 101, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) < LiteralValue(DataType.WORD, wordvalue=255, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) < LiteralValue(DataType.FLOAT, floatvalue=100.1, position=dummyPos))
|
||||||
|
|
||||||
|
assertTrue(LiteralValue(DataType.BYTE, 100, position=dummyPos) <= LiteralValue(DataType.BYTE, 100, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) <= LiteralValue(DataType.WORD,wordvalue= 254, position=dummyPos))
|
||||||
|
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||||
|
|
||||||
|
assertFalse(LiteralValue(DataType.BYTE, 100, position=dummyPos) < LiteralValue(DataType.BYTE, 100, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos) < LiteralValue(DataType.WORD, wordvalue=254, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) < LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||||
|
|
||||||
|
assertFalse(LiteralValue(DataType.BYTE, 100, position=dummyPos) <= LiteralValue(DataType.BYTE, 99, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.WORD,wordvalue= 254, position=dummyPos) <= LiteralValue(DataType.WORD,wordvalue= 253, position=dummyPos))
|
||||||
|
assertFalse(LiteralValue(DataType.FLOAT,floatvalue= 100.0, position=dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue=99.9, position=dummyPos))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -264,10 +264,11 @@ The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (ne
|
|||||||
Initial values across multiple runs of the program
|
Initial values across multiple runs of the program
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The initial values of your variables will be restored automatically when the program is (re)started,
|
.. todo::
|
||||||
*except for string variables, arrays and matrices*. It is assumed these are left unchanged by the program.
|
The initial values of your variables will be restored automatically when the program is (re)started,
|
||||||
If you do modify them in-place, you should take care yourself that they work as
|
*except for string variables, arrays and matrices*. It is assumed these are left unchanged by the program.
|
||||||
expected when the program is restarted.
|
If you do modify them in-place, you should take care yourself that they work as
|
||||||
|
expected when the program is restarted.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -363,26 +364,45 @@ Assignment statements assign a single value to a target variable or memory locat
|
|||||||
Augmented assignments (such as ``A += X``) are also available, but these are just shorthands
|
Augmented assignments (such as ``A += X``) are also available, but these are just shorthands
|
||||||
for normal assignments (``A = A + X``).
|
for normal assignments (``A = A + X``).
|
||||||
|
|
||||||
|
.. attention::
|
||||||
|
**Data type conversion (in assignments):**
|
||||||
|
When assigning a value with a 'smaller' datatype to a register or variable with a 'larger' datatype,
|
||||||
|
the value will be automatically converted to the target datatype: byte --> word --> float.
|
||||||
|
So assigning a byte to a word variable, or a word to a floating point variable, is fine.
|
||||||
|
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
|
||||||
|
a variable of a smaller datatype without an explicit conversion. Otherwise you'll get an error telling you
|
||||||
|
that there is a loss of precision. You can use builtin functions such as ``round`` and ``lsb`` to convert
|
||||||
|
to a smaller datatype.
|
||||||
|
|
||||||
Expressions
|
Expressions
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
In most places where a number or other value is expected, you can use just the number, or a constant expression.
|
In most places where a number or other value is expected, you can use just the number, or a constant expression.
|
||||||
The expression is parsed and evaluated by the compiler itself at compile time, and the (constant) resulting value is used in its place.
|
If possible, the expression is parsed and evaluated by the compiler itself at compile time, and the (constant) resulting value is used in its place.
|
||||||
|
Expressions that cannot be compile-time evaluated will result in code that calculates them at runtime.
|
||||||
Expressions can contain procedure and function calls.
|
Expressions can contain procedure and function calls.
|
||||||
There are various built-in functions such as sin(), cos(), min(), max() that can be used in expressions (see :ref:`builtinfunctions`).
|
There are various built-in functions such as sin(), cos(), min(), max() that can be used in expressions (see :ref:`builtinfunctions`).
|
||||||
You can also reference idendifiers defined elsewhere in your code.
|
You can also reference idendifiers defined elsewhere in your code.
|
||||||
The compiler will evaluate the expression if it is a constant, and just use the resulting value from then on.
|
|
||||||
Expressions that cannot be compile-time evaluated will result in code that calculates them at runtime.
|
.. attention::
|
||||||
|
**Data type conversion (during calculations):**
|
||||||
|
BYTE values used in arithmetic expressions (calculations) will be automatically converted into WORD values
|
||||||
|
if the calculation needs that to store the resulting value. Once a WORD value is used, all other results will be WORDs as well
|
||||||
|
(there's no automatic conversion of WORD into BYTE).
|
||||||
|
*There is never an automatic conversion into floating point values, and the compiler will NOT issue a warning for this.*
|
||||||
|
If you require float precision, you'll have to first convert into a floating point explicitly using the ``flt`` builtin function.
|
||||||
|
For example, this means that if you divide two integer values (say: ``32500 / 99``) the result will be the integer floor
|
||||||
|
division (328) rather than the floating point result (328.2828282828283). If you need the full precision,
|
||||||
|
you'll have to write ``flt(32500) / 99`` (or if they're constants, simply ``32500.0 / 99``), to make sure the
|
||||||
|
first operand is a floating point value.
|
||||||
|
|
||||||
|
|
||||||
Arithmetic and Logical expressions
|
Arithmetic and Logical expressions
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Arithmetic expressions are expressions that calculate a numeric result (integer or floating point).
|
Arithmetic expressions are expressions that calculate a numeric result (integer or floating point).
|
||||||
Many common arithmetic operators can be used and follow the regular precedence rules.
|
Many common arithmetic operators can be used and follow the regular precedence rules.
|
||||||
|
Logical expressions are expressions that calculate a boolean result: true or false
|
||||||
Logical expressions are expressions that calculate a boolean result, true or false
|
(which in reality are just a 1 or 0 integer value).
|
||||||
(which in Prog8 will effectively be a 1 or 0 integer value).
|
|
||||||
|
|
||||||
You can use parentheses to group parts of an expresion to change the precedence.
|
You can use parentheses to group parts of an expresion to change the precedence.
|
||||||
Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions
|
Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but subexpressions
|
||||||
@ -516,7 +536,7 @@ msb(x)
|
|||||||
|
|
||||||
flt(x)
|
flt(x)
|
||||||
Explicitly convert the number x to a floating point number.
|
Explicitly convert the number x to a floating point number.
|
||||||
Usually this is done automatically but sometimes it may be required to force this.
|
This is required if you want calculations to have floating point precision when the values aren't float already.
|
||||||
|
|
||||||
any(x)
|
any(x)
|
||||||
1 ('true') if any of the values in the non-scalar (array or matrix) value x is 'true' (not zero), else 0 ('false')
|
1 ('true') if any of the values in the non-scalar (array or matrix) value x is 'true' (not zero), else 0 ('false')
|
||||||
@ -536,12 +556,12 @@ rndf()
|
|||||||
lsl(x)
|
lsl(x)
|
||||||
Shift the bits in x (byte or word) one position to the left.
|
Shift the bits in x (byte or word) one position to the left.
|
||||||
Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)
|
Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)
|
||||||
Modifies in-place but also returns the new value.
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
lsr(x)
|
lsr(x)
|
||||||
Shift the bits in x (byte or word) one position to the right.
|
Shift the bits in x (byte or word) one position to the right.
|
||||||
The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag)
|
The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag)
|
||||||
Modifies in-place but also returns the new value.
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
rol(x)
|
rol(x)
|
||||||
Rotate the bits in x (byte or word) one position to the left.
|
Rotate the bits in x (byte or word) one position to the left.
|
||||||
@ -574,3 +594,6 @@ P_carry(bit)
|
|||||||
P_irqd(bit)
|
P_irqd(bit)
|
||||||
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
||||||
(translated into ``SEI`` or ``CLI`` cpu instruction)
|
(translated into ``SEI`` or ``CLI`` cpu instruction)
|
||||||
|
|
||||||
|
|
||||||
|
@todo remove P_carry and P_irqd as functions and turn them into assignments instead (allowing only 0 or 1 as value)
|
||||||
|
@ -319,8 +319,9 @@ If used in the place of a literal value, it expands into the actual array of val
|
|||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
|
|
||||||
address-of: ``#``
|
.. todo::
|
||||||
Takes the address of the symbol following it: ``word address = #somevar``
|
address-of: ``#``
|
||||||
|
Takes the address of the symbol following it: ``word address = #somevar``
|
||||||
|
|
||||||
|
|
||||||
arithmetic: ``+`` ``-`` ``*`` ``/`` ``//`` ``**`` ``%``
|
arithmetic: ``+`` ``-`` ``*`` ``/`` ``//`` ``**`` ``%``
|
||||||
|
@ -123,6 +123,9 @@ The following 6502 CPU hardware registers are directly usable in program code (a
|
|||||||
- ``AX``, ``AY``, ``XY`` surrogate 16-bit registers: LSB-order (lo/hi) combined register pairs
|
- ``AX``, ``AY``, ``XY`` surrogate 16-bit registers: LSB-order (lo/hi) combined register pairs
|
||||||
- the status register (P) carry flag and interrupt disable flag can be written via the ``P_carry`` and ``P_irqd`` builtin functions.
|
- the status register (P) carry flag and interrupt disable flag can be written via the ``P_carry`` and ``P_irqd`` builtin functions.
|
||||||
|
|
||||||
|
@todo remove P_carry and P_irqd as functions and turn them into assignments instead (allowing only 0 or 1 as value)
|
||||||
|
|
||||||
|
|
||||||
Subroutine Calling Conventions
|
Subroutine Calling Conventions
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user