mirror of
https://github.com/irmen/prog8.git
synced 2024-12-26 14:29:35 +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_text(5, 5, 7, "Calculating Mandelbrot Fractal...")
|
||||
|
||||
flt(44)
|
||||
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 {
|
||||
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
|
||||
y = 0.0
|
||||
|
@ -1,9 +1,26 @@
|
||||
%option enable_floats
|
||||
|
||||
~ main {
|
||||
|
||||
sub start() -> () {
|
||||
|
||||
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 {
|
||||
_vm_write_num(i)
|
||||
|
@ -1137,14 +1137,8 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
"all" -> builtinAll(arglist, position, namespace)
|
||||
"floor" -> builtinFloor(arglist, position, namespace)
|
||||
"ceil" -> builtinCeil(arglist, position, namespace)
|
||||
"lsl" -> builtinLsl(arglist, position, namespace)
|
||||
"lsr" -> builtinLsr(arglist, position, namespace)
|
||||
"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)
|
||||
"lsl", "lsr", "rol", "rol2", "ror", "ror2", "P_carry", "P_irqd" ->
|
||||
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used in expressions because it doesn't return a value", position)
|
||||
else -> null
|
||||
}
|
||||
if(withDatatypeCheck) {
|
||||
|
@ -303,9 +303,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
expr.arglist.forEach { translate(it) }
|
||||
val target = expr.target.targetStatement(namespace)
|
||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||
// call to a builtin function
|
||||
val funcname = expr.target.nameInSource[0].toUpperCase()
|
||||
createFunctionCall(funcname) // call builtin function
|
||||
// call to a builtin function (some will just be an opcode!)
|
||||
val funcname = expr.target.nameInSource[0]
|
||||
translateFunctionCall(funcname, expr.arglist)
|
||||
} else {
|
||||
when(target) {
|
||||
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) {
|
||||
val opcode = when(operator) {
|
||||
"+" -> Opcode.ADD
|
||||
@ -375,8 +400,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
val targetStmt = stmt.target.targetStatement(namespace)!!
|
||||
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
||||
stmt.arglist.forEach { translate(it) }
|
||||
val funcname = stmt.target.nameInSource[0].toUpperCase()
|
||||
createFunctionCall(funcname) // call builtin function
|
||||
val funcname = stmt.target.nameInSource[0]
|
||||
translateFunctionCall(funcname, stmt.arglist)
|
||||
return
|
||||
}
|
||||
|
||||
@ -389,9 +414,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
stackvmProg.instr(Opcode.CALL, callLabel = targetname)
|
||||
}
|
||||
|
||||
private fun createFunctionCall(funcname: String) {
|
||||
private fun createSyscall(funcname: String) {
|
||||
val function = (
|
||||
if (funcname.startsWith("_VM_"))
|
||||
if (funcname.startsWith("_vm_"))
|
||||
funcname.substring(4)
|
||||
else
|
||||
"FUNC_$funcname"
|
||||
@ -724,7 +749,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
translate(ifstmt)
|
||||
} else {
|
||||
// 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)
|
||||
@ -760,7 +785,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, priva
|
||||
val postIncr = PostIncrDecr(makeAssignmentTarget(), "--", range.position)
|
||||
postIncr.linkParents(range.parent)
|
||||
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 -> {
|
||||
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_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
|
||||
= 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
|
||||
= 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)
|
||||
|
||||
// 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 {
|
||||
@ -301,61 +301,130 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
||||
}
|
||||
|
||||
operator fun compareTo(other: Value): Int {
|
||||
if(stringvalue!=null && other.stringvalue!=null)
|
||||
return stringvalue.compareTo(other.stringvalue)
|
||||
return numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
||||
return when(type) {
|
||||
DataType.BYTE, DataType.WORD, DataType.FLOAT -> {
|
||||
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 {
|
||||
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||
throw VmExecutionException("floating point loss of precision on type $type")
|
||||
val v1 = numericValue()
|
||||
val v2 = other.numericValue()
|
||||
val result = v1.toDouble() + v2.toDouble()
|
||||
return Value(type, result)
|
||||
return arithResult(type, result, other.type, "add")
|
||||
}
|
||||
|
||||
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 v2 = other.numericValue()
|
||||
val result = v1.toDouble() - v2.toDouble()
|
||||
return Value(type, result)
|
||||
return arithResult(type, result, other.type, "sub")
|
||||
}
|
||||
|
||||
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 v2 = other.numericValue()
|
||||
val result = v1.toDouble() * v2.toDouble()
|
||||
return Value(type, result)
|
||||
return arithResult(type, result, other.type, "mul")
|
||||
}
|
||||
|
||||
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 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()
|
||||
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 {
|
||||
if(other.type == DataType.FLOAT && (type!=DataType.FLOAT))
|
||||
throw VmExecutionException("floating point loss of precision on type $type")
|
||||
val v1 = numericValue()
|
||||
val v2 = other.numericValue()
|
||||
val result = (v1.toDouble() / v2.toDouble()).toInt()
|
||||
return if(this.type==DataType.BYTE)
|
||||
Value(DataType.BYTE, (result and 255).toShort())
|
||||
else
|
||||
Value(DataType.WORD, result and 65535)
|
||||
val result = floor(v1.toDouble() / v2.toDouble())
|
||||
// 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 remainder(other: Value): Value? {
|
||||
val v1 = numericValue()
|
||||
val v2 = other.numericValue()
|
||||
val result = v1.toDouble() % v2.toDouble()
|
||||
return Value(type, result)
|
||||
return arithResult(type, result, other.type, "remainder")
|
||||
}
|
||||
|
||||
fun pow(other: Value): Value {
|
||||
val v1 = numericValue()
|
||||
val v2 = other.numericValue()
|
||||
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 {
|
||||
|
@ -344,8 +344,8 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 40)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.FLOAT, 3999.0/40.0),
|
||||
Value(DataType.FLOAT, 42.25/(3999.0/40.0)))
|
||||
Value(DataType.WORD, 99),
|
||||
Value(DataType.FLOAT, 42.25/99))
|
||||
val operator = Opcode.DIV
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
@ -354,13 +354,13 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testFloorDiv() {
|
||||
val values = listOf(
|
||||
Value(DataType.FLOAT, 42.25),
|
||||
Value(DataType.FLOAT, 4000.25),
|
||||
Value(DataType.WORD, 3999),
|
||||
Value(DataType.BYTE, 40)
|
||||
)
|
||||
val expected = listOf(
|
||||
Value(DataType.WORD, floor(3999.0/40.0)),
|
||||
Value(DataType.WORD, floor(42.25/floor(3999.0/40.0))))
|
||||
Value(DataType.WORD, 99),
|
||||
Value(DataType.FLOAT, 40.0))
|
||||
val operator = Opcode.FLOORDIV
|
||||
|
||||
testBinaryOperator(values, operator, expected)
|
||||
@ -833,10 +833,6 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testLess() {
|
||||
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, 1), // 1
|
||||
Value(DataType.BYTE, 1),
|
||||
@ -852,17 +848,21 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 21),
|
||||
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)
|
||||
|
||||
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
|
||||
fun testLessEq() {
|
||||
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, 1), // 1
|
||||
Value(DataType.BYTE, 1),
|
||||
@ -878,17 +878,21 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 22),
|
||||
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)
|
||||
|
||||
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
|
||||
fun testGreater() {
|
||||
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, 1), // 0
|
||||
Value(DataType.BYTE, 1),
|
||||
@ -904,17 +908,21 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 21),
|
||||
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)
|
||||
|
||||
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
|
||||
fun testGreaterEq() {
|
||||
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, 1), // 0
|
||||
Value(DataType.BYTE, 1),
|
||||
@ -930,17 +938,21 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 22),
|
||||
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)
|
||||
|
||||
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
|
||||
fun testEqual() {
|
||||
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, 1), // 0
|
||||
Value(DataType.BYTE, 1),
|
||||
@ -956,17 +968,21 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 22),
|
||||
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)
|
||||
|
||||
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
|
||||
fun testNotEqual() {
|
||||
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, 1), // 1
|
||||
Value(DataType.BYTE, 1),
|
||||
@ -982,8 +998,16 @@ class TestStackVmOpcodes {
|
||||
Value(DataType.BYTE, 22),
|
||||
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)
|
||||
|
||||
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
|
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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The initial values of your variables will be restored automatically when the program is (re)started,
|
||||
*except for string variables, arrays and matrices*. It is assumed these are left unchanged by the program.
|
||||
If you do modify them in-place, you should take care yourself that they work as
|
||||
expected when the program is restarted.
|
||||
.. todo::
|
||||
The initial values of your variables will be restored automatically when the program is (re)started,
|
||||
*except for string variables, arrays and matrices*. It is assumed these are left unchanged by the program.
|
||||
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
|
||||
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
|
||||
-----------
|
||||
|
||||
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.
|
||||
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.
|
||||
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 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.
|
||||
|
||||
Logical expressions are expressions that calculate a boolean result, true or false
|
||||
(which in Prog8 will effectively be a 1 or 0 integer value).
|
||||
Logical expressions are expressions that calculate a boolean result: true or false
|
||||
(which in reality are just a 1 or 0 integer value).
|
||||
|
||||
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
|
||||
@ -516,7 +536,7 @@ msb(x)
|
||||
|
||||
flt(x)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
Rotate the bits in x (byte or word) one position to the left.
|
||||
@ -574,3 +594,6 @@ P_carry(bit)
|
||||
P_irqd(bit)
|
||||
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
||||
(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
|
||||
---------
|
||||
|
||||
address-of: ``#``
|
||||
Takes the address of the symbol following it: ``word address = #somevar``
|
||||
.. todo::
|
||||
address-of: ``#``
|
||||
Takes the address of the symbol following it: ``word address = #somevar``
|
||||
|
||||
|
||||
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
|
||||
- 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
|
||||
------------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user