mirror of
https://github.com/irmen/prog8.git
synced 2024-12-26 14:29:35 +00:00
1074 lines
32 KiB
Kotlin
1074 lines
32 KiB
Kotlin
package prog8tests
|
|
|
|
import io.kotest.core.spec.style.FunSpec
|
|
import io.kotest.matchers.ints.shouldBeGreaterThan
|
|
import io.kotest.matchers.shouldBe
|
|
import io.kotest.matchers.shouldNotBe
|
|
import io.kotest.matchers.string.shouldContain
|
|
import io.kotest.matchers.types.instanceOf
|
|
import prog8.ast.IFunctionCall
|
|
import prog8.ast.expressions.*
|
|
import prog8.ast.statements.Assignment
|
|
import prog8.ast.statements.IfElse
|
|
import prog8.ast.statements.VarDecl
|
|
import prog8.code.core.DataType
|
|
import prog8.code.core.Position
|
|
import prog8.code.target.C64Target
|
|
import prog8.code.target.VMTarget
|
|
import prog8tests.helpers.ErrorReporterForTests
|
|
import prog8tests.helpers.compileText
|
|
|
|
|
|
class TestTypecasts: FunSpec({
|
|
|
|
test("integer args for builtin funcs") {
|
|
val text="""
|
|
%import floats
|
|
main {
|
|
sub start() {
|
|
float fl
|
|
floats.print_f(lsb(fl))
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false, errors=errors)
|
|
result shouldBe null
|
|
errors.errors.size shouldBe 1
|
|
errors.errors[0] shouldContain "type mismatch, was: FLOAT expected one of: [UWORD, WORD]"
|
|
}
|
|
|
|
test("not casting bool operands to logical operators") {
|
|
val text="""
|
|
%import textio
|
|
%zeropage basicsafe
|
|
|
|
main {
|
|
sub start() {
|
|
bool bb2=true
|
|
bool @shared bb = bb2 and true
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 4
|
|
val expr = (stmts[3] as Assignment).value as BinaryExpression
|
|
expr.operator shouldBe "and"
|
|
expr.right shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
|
(expr.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
|
|
|
|
val result2 = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
val stmts2 = result2.compilerAst.entrypoint.statements
|
|
stmts2.size shouldBe 5
|
|
val expr2 = (stmts2[3] as Assignment).value as BinaryExpression
|
|
expr2.operator shouldBe "&"
|
|
expr2.right shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
|
}
|
|
|
|
test("bool expressions with functioncalls") {
|
|
val text="""
|
|
main {
|
|
sub ftrue(ubyte arg) -> ubyte {
|
|
arg++
|
|
return 64
|
|
}
|
|
|
|
sub start() {
|
|
bool ub1 = true
|
|
bool ub2 = true
|
|
bool ub3 = true
|
|
bool ub4 = 0
|
|
bool @shared bvalue
|
|
|
|
bvalue = ub1 xor ub2 xor ub3 xor true
|
|
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
|
bvalue = ub1 and ub2 and ftrue(99)
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
/*
|
|
ubyte ub1
|
|
ub1 = 1
|
|
ubyte ub2
|
|
ub2 = 1
|
|
ubyte ub3
|
|
ub3 = 1
|
|
ubyte @shared bvalue
|
|
bvalue = ub1 ^ ub2 ^ ub3 ^ true
|
|
bvalue = (((ub1^ub2)^ub3)^(ftrue(99)!=0))
|
|
bvalue = ((ub1&ub2)&(ftrue(99)!=0))
|
|
return
|
|
*/
|
|
stmts.size shouldBe 11
|
|
val assignValue1 = (stmts[7] as Assignment).value as BinaryExpression
|
|
val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression
|
|
val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression
|
|
assignValue1.operator shouldBe "^"
|
|
assignValue2.operator shouldBe "^"
|
|
assignValue3.operator shouldBe "&"
|
|
val right2 = assignValue2.right as BinaryExpression
|
|
val right3 = assignValue3.right as BinaryExpression
|
|
right2.operator shouldBe "!="
|
|
right3.operator shouldBe "!="
|
|
right2.left shouldBe instanceOf<IFunctionCall>()
|
|
right2.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
|
right3.left shouldBe instanceOf<IFunctionCall>()
|
|
right3.right shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
|
}
|
|
|
|
test("simple logical with bool no typecast") {
|
|
val text="""
|
|
main {
|
|
bool bb
|
|
|
|
sub start() {
|
|
bb = bb and 123
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 2
|
|
val assignValue = (stmts[0] as Assignment).value as BinaryExpression
|
|
assignValue.left shouldBe instanceOf<IdentifierReference>()
|
|
assignValue.operator shouldBe "&"
|
|
(assignValue.right as NumericLiteral).number shouldBe 1.0
|
|
}
|
|
|
|
test("simple logical with byte instead of bool ok with typecasting") {
|
|
val text="""
|
|
main {
|
|
ubyte ubb
|
|
|
|
sub start() {
|
|
ubb = ubb and 123
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 2
|
|
val assignValue = (stmts[0] as Assignment).value as BinaryExpression
|
|
assignValue.left shouldBe instanceOf<BinaryExpression>() // as a result of the cast to boolean
|
|
assignValue.operator shouldBe "&"
|
|
(assignValue.right as NumericLiteral).number shouldBe 1.0
|
|
}
|
|
|
|
test("logical with byte instead of bool") {
|
|
val text="""
|
|
%import textio
|
|
|
|
main {
|
|
|
|
sub ftrue(ubyte arg) -> ubyte {
|
|
arg++
|
|
return 128
|
|
}
|
|
|
|
sub ffalse(ubyte arg) -> ubyte {
|
|
arg++
|
|
return 0
|
|
}
|
|
|
|
sub start() {
|
|
ubyte ub1 = 2
|
|
ubyte ub2 = 4
|
|
ubyte ub3 = 8
|
|
ubyte ub4 = 0
|
|
ubyte bvalue
|
|
|
|
txt.print("const not 0: ")
|
|
txt.print_ub(not 129)
|
|
txt.nl()
|
|
txt.print("const not 1: ")
|
|
txt.print_ub(not 0)
|
|
txt.nl()
|
|
txt.print("const inv 126: ")
|
|
txt.print_ub(~ 129)
|
|
txt.nl()
|
|
txt.print("const inv 255: ")
|
|
txt.print_ub(~ 0)
|
|
txt.nl()
|
|
bvalue = 129
|
|
txt.print("bitwise inv 126: ")
|
|
bvalue = ~ bvalue
|
|
txt.print_ub(bvalue)
|
|
txt.nl()
|
|
bvalue = 0
|
|
txt.print("bitwise inv 255: ")
|
|
bvalue = ~ bvalue
|
|
txt.print_ub(bvalue)
|
|
txt.nl()
|
|
|
|
txt.print("bitwise or 14: ")
|
|
txt.print_ub(ub1 | ub2 | ub3 | ub4)
|
|
txt.nl()
|
|
txt.print("bitwise or 142: ")
|
|
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
|
|
txt.nl()
|
|
txt.print("bitwise and 0: ")
|
|
txt.print_ub(ub1 & ub2 & ub3 & ub4)
|
|
txt.nl()
|
|
txt.print("bitwise and 8: ")
|
|
txt.print_ub(ub3 & ub3 & 127)
|
|
txt.nl()
|
|
txt.print("bitwise xor 14: ")
|
|
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
|
|
txt.nl()
|
|
txt.print("bitwise xor 6: ")
|
|
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
|
|
txt.nl()
|
|
txt.print("bitwise not 247: ")
|
|
txt.print_ub(~ub3)
|
|
txt.nl()
|
|
txt.print("bitwise not 255: ")
|
|
txt.print_ub(~ub4)
|
|
txt.nl()
|
|
|
|
txt.print("not 0: ")
|
|
bvalue = 3 * (ub4 | not (ub3 | ub3 | ub3))
|
|
txt.print_ub(bvalue)
|
|
if 3*(ub4 | not (ub1 | ub1 | ub1))
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
|
|
txt.print("not 0: ")
|
|
bvalue = not ub3
|
|
txt.print_ub(bvalue)
|
|
if not ub1
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
|
|
txt.print("not 1: ")
|
|
bvalue = not ub4
|
|
txt.print_ub(bvalue)
|
|
if not ub4
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
bvalue = bvalue and 128
|
|
txt.print("bvl 1: ")
|
|
txt.print_ub(bvalue)
|
|
if bvalue and 128
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
txt.print("and 1: ")
|
|
bvalue = ub1 and ub2 and ub3
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("and 1: ")
|
|
bvalue = ub1 and ub2 and ub3 and 64
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and 64
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("and 1: ")
|
|
bvalue = ub1 and ub2 and ub3 and ftrue(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and ftrue(99)
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("and 0: ")
|
|
bvalue = ub1 and ub2 and ub3 and ub4
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and ub4
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
txt.print("and 0: ")
|
|
bvalue = ub1 and ub2 and ub3 and ffalse(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and ffalse(99)
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
|
|
txt.print(" or 1: ")
|
|
bvalue = ub1 or ub2 or ub3 or ub4
|
|
txt.print_ub(bvalue)
|
|
if ub1 or ub2 or ub3 or ub4
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print(" or 1: ")
|
|
bvalue = ub4 or ub4 or ub1
|
|
txt.print_ub(bvalue)
|
|
if ub4 or ub4 or ub1
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print(" or 1: ")
|
|
bvalue = ub1 or ub2 or ub3 or ftrue(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 or ub2 or ub3 or ftrue(99)
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
txt.print("xor 1: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ub4
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ub4
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("xor 1: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ffalse(99)
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
txt.print("xor 0: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ub4 xor true
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
txt.print("xor 0: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ftrue(99)
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
}
|
|
}
|
|
"""
|
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBeGreaterThan 10
|
|
}
|
|
|
|
test("logical with bools") {
|
|
val text="""
|
|
%import textio
|
|
|
|
main {
|
|
|
|
sub ftrue(ubyte arg) -> ubyte {
|
|
arg++
|
|
return 128
|
|
}
|
|
|
|
sub ffalse(ubyte arg) -> ubyte {
|
|
arg++
|
|
return 0
|
|
}
|
|
|
|
sub start() {
|
|
bool ub1 = 2
|
|
bool ub2 = 4
|
|
bool ub3 = 8
|
|
bool ub4 = 0
|
|
bool bvalue
|
|
|
|
txt.print("const not 0: ")
|
|
txt.print_ub(not 129)
|
|
txt.nl()
|
|
txt.print("const not 1: ")
|
|
txt.print_ub(not 0)
|
|
txt.nl()
|
|
txt.print("const inv 126: ")
|
|
txt.print_ub(~ 129)
|
|
txt.nl()
|
|
txt.print("const inv 255: ")
|
|
txt.print_ub(~ 0)
|
|
txt.nl()
|
|
; bvalue = 129
|
|
; txt.print("bitwise inv 126: ")
|
|
; bvalue = ~ bvalue
|
|
; txt.print_ub(bvalue)
|
|
; txt.nl()
|
|
; bvalue = 0
|
|
; txt.print("bitwise inv 255: ")
|
|
; bvalue = ~ bvalue
|
|
; txt.print_ub(bvalue)
|
|
; txt.nl()
|
|
|
|
txt.print("bitwise or 14: ")
|
|
txt.print_ub(ub1 | ub2 | ub3 | ub4)
|
|
txt.nl()
|
|
txt.print("bitwise or 142: ")
|
|
txt.print_ub(ub1 | ub2 | ub3 | ub4 | 128)
|
|
txt.nl()
|
|
txt.print("bitwise and 0: ")
|
|
txt.print_ub(ub1 & ub2 & ub3 & ub4)
|
|
txt.nl()
|
|
txt.print("bitwise and 8: ")
|
|
txt.print_ub(ub3 & ub3 & 127)
|
|
txt.nl()
|
|
txt.print("bitwise xor 14: ")
|
|
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ ub4)
|
|
txt.nl()
|
|
txt.print("bitwise xor 6: ")
|
|
txt.print_ub(ub1 ^ ub2 ^ ub3 ^ 8)
|
|
txt.nl()
|
|
;txt.print("bitwise not 247: ")
|
|
;txt.print_ub(~ub3)
|
|
;txt.nl()
|
|
;txt.print("bitwise not 255: ")
|
|
;txt.print_ub(~ub4)
|
|
;txt.nl()
|
|
|
|
txt.print("not 0: ")
|
|
bvalue = 3 * (ub4 | not (ub3 | ub3 | ub3))
|
|
txt.print_ub(bvalue)
|
|
if 3*(ub4 | not (ub1 | ub1 | ub1))
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
|
|
txt.print("not 0: ")
|
|
bvalue = not ub3
|
|
txt.print_ub(bvalue)
|
|
if not ub1
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
|
|
txt.print("not 1: ")
|
|
bvalue = not ub4
|
|
txt.print_ub(bvalue)
|
|
if not ub4
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
bvalue = bvalue and 128
|
|
txt.print("bvl 1: ")
|
|
txt.print_ub(bvalue)
|
|
if bvalue and 128
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
txt.print("and 1: ")
|
|
bvalue = ub1 and ub2 and ub3
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("and 1: ")
|
|
bvalue = ub1 and ub2 and ub3 and 64
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and 64
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("and 1: ")
|
|
bvalue = ub1 and ub2 and ub3 and ftrue(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and ftrue(99)
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("and 0: ")
|
|
bvalue = ub1 and ub2 and ub3 and ub4
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and ub4
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
txt.print("and 0: ")
|
|
bvalue = ub1 and ub2 and ub3 and ffalse(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 and ub2 and ub3 and ffalse(99)
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
|
|
txt.print(" or 1: ")
|
|
bvalue = ub1 or ub2 or ub3 or ub4
|
|
txt.print_ub(bvalue)
|
|
if ub1 or ub2 or ub3 or ub4
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print(" or 1: ")
|
|
bvalue = ub4 or ub4 or ub1
|
|
txt.print_ub(bvalue)
|
|
if ub4 or ub4 or ub1
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print(" or 1: ")
|
|
bvalue = ub1 or ub2 or ub3 or ftrue(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 or ub2 or ub3 or ftrue(99)
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
txt.print("xor 1: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ub4
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ub4
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
txt.print("xor 1: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ffalse(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ffalse(99)
|
|
txt.print(" / ok")
|
|
else
|
|
txt.print(" / fail")
|
|
txt.nl()
|
|
|
|
txt.print("xor 0: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ub4 xor true
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ub4 xor true
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
txt.nl()
|
|
txt.print("xor 0: ")
|
|
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)
|
|
txt.print_ub(bvalue)
|
|
if ub1 xor ub2 xor ub3 xor ftrue(99)
|
|
txt.print(" / fail")
|
|
else
|
|
txt.print(" / ok")
|
|
}
|
|
}
|
|
"""
|
|
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBeGreaterThan 10
|
|
}
|
|
|
|
test("bool arrays") {
|
|
val text="""
|
|
main {
|
|
sub start() {
|
|
bool[] barray = [true, false, 1, 0, 222]
|
|
bool bb
|
|
ubyte xx
|
|
|
|
for bb in barray {
|
|
if bb
|
|
xx++
|
|
}
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 6
|
|
val arraydecl = stmts[0] as VarDecl
|
|
arraydecl.datatype shouldBe DataType.ARRAY_BOOL
|
|
val values = (arraydecl.value as ArrayLiteral).value
|
|
values.size shouldBe 5
|
|
values[0] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
|
values[1] shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
|
values[2] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
|
values[3] shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
|
values[4] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
|
}
|
|
|
|
test("correct handling of bool parameters") {
|
|
val text="""
|
|
main {
|
|
|
|
sub thing(bool b1, bool b2) -> bool {
|
|
return (b1 and b2) or b1
|
|
}
|
|
|
|
sub start() {
|
|
bool boolvalue1 = true
|
|
bool boolvalue2 = false
|
|
uword xx
|
|
|
|
boolvalue1 = thing(true, false)
|
|
boolvalue2 = thing(xx, xx)
|
|
|
|
if boolvalue1 and boolvalue2
|
|
boolvalue1=false
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 9
|
|
val fcall1 = ((stmts[6] as Assignment).value as IFunctionCall)
|
|
fcall1.args[0] shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
|
|
fcall1.args[1] shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
|
val fcall2 = ((stmts[7] as Assignment).value as IFunctionCall)
|
|
(fcall2.args[0] as TypecastExpression).type shouldBe DataType.BOOL
|
|
(fcall2.args[1] as TypecastExpression).type shouldBe DataType.BOOL
|
|
val ifCond = (stmts[8] as IfElse).condition as BinaryExpression
|
|
ifCond.operator shouldBe "and" // no asm writing so logical expressions haven't been replaced with bitwise equivalents yet
|
|
(ifCond.left as IdentifierReference).nameInSource shouldBe listOf("boolvalue1")
|
|
(ifCond.right as IdentifierReference).nameInSource shouldBe listOf("boolvalue2")
|
|
}
|
|
|
|
test("correct evaluation of words in boolean expressions") {
|
|
val text="""
|
|
main {
|
|
sub start() {
|
|
uword camg
|
|
ubyte @shared interlaced
|
|
interlaced = (camg & ${'$'}0004) != 0
|
|
interlaced++
|
|
interlaced = (${'$'}0004 & camg) != 0
|
|
interlaced++
|
|
uword @shared ww
|
|
ww = (camg & ${'$'}0004)
|
|
ww++
|
|
ww = (${'$'}0004 & camg)
|
|
|
|
ubyte @shared wordNr2 = (interlaced >= ${'$'}33) + (interlaced >= ${'$'}66) + (interlaced >= ${'$'}99) + (interlaced >= ${'$'}CC)
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBeGreaterThan 10
|
|
}
|
|
|
|
test("word to byte casts") {
|
|
val text="""
|
|
%import textio
|
|
main {
|
|
sub func(ubyte arg) -> word {
|
|
return arg-99
|
|
}
|
|
|
|
sub start() {
|
|
txt.print_ub(func(0) as ubyte)
|
|
txt.print_uw(func(0) as ubyte)
|
|
txt.print_w(func(0) as ubyte)
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 3
|
|
}
|
|
|
|
test("add missing & to function arguments") {
|
|
val text="""
|
|
main {
|
|
|
|
sub handler(uword fptr) {
|
|
}
|
|
|
|
sub start() {
|
|
uword variable
|
|
|
|
pushw(variable)
|
|
pushw(handler)
|
|
pushw(&handler)
|
|
handler(variable)
|
|
handler(handler)
|
|
handler(&handler)
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
|
val stmts = result.compilerAst.entrypoint.statements
|
|
stmts.size shouldBe 8
|
|
val arg1 = (stmts[2] as IFunctionCall).args.single()
|
|
val arg2 = (stmts[3] as IFunctionCall).args.single()
|
|
val arg3 = (stmts[4] as IFunctionCall).args.single()
|
|
val arg4 = (stmts[5] as IFunctionCall).args.single()
|
|
val arg5 = (stmts[6] as IFunctionCall).args.single()
|
|
val arg6 = (stmts[7] as IFunctionCall).args.single()
|
|
arg1 shouldBe instanceOf<IdentifierReference>()
|
|
arg2 shouldBe instanceOf<AddressOf>()
|
|
arg3 shouldBe instanceOf<AddressOf>()
|
|
arg4 shouldBe instanceOf<IdentifierReference>()
|
|
arg5 shouldBe instanceOf<AddressOf>()
|
|
arg6 shouldBe instanceOf<AddressOf>()
|
|
}
|
|
|
|
test("correct typecasts") {
|
|
val text = """
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
float @shared fl = 3.456
|
|
uword @shared uw = 5555
|
|
byte @shared bb = -44
|
|
|
|
bb = uw as byte
|
|
uw = bb as uword
|
|
fl = uw as float
|
|
fl = bb as float
|
|
bb = fl as byte
|
|
uw = fl as uword
|
|
uw = 8888 + (bb as ubyte)
|
|
}
|
|
}
|
|
"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
|
result.compilerAst.entrypoint.statements.size shouldBe 14
|
|
}
|
|
|
|
test("invalid typecasts of numbers") {
|
|
val text = """
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
ubyte @shared bb
|
|
|
|
bb = 5555 as ubyte
|
|
routine(5555 as ubyte)
|
|
}
|
|
|
|
sub routine(ubyte bb) {
|
|
bb++
|
|
}
|
|
}
|
|
"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "no cast"
|
|
errors.errors[1] shouldContain "no cast"
|
|
}
|
|
|
|
test("refuse to truncate float literal 1") {
|
|
val text = """
|
|
%option enable_floats
|
|
main {
|
|
sub start() {
|
|
float @shared fl = 3.456 as uword
|
|
fl = 1.234 as uword
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "refused"
|
|
errors.errors[1] shouldContain "refused"
|
|
}
|
|
|
|
test("refuse to truncate float literal 2") {
|
|
val text = """
|
|
%option enable_floats
|
|
main {
|
|
sub start() {
|
|
float @shared fl = 3.456
|
|
fl++
|
|
fl = fl as uword
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 1
|
|
errors.errors[0] shouldContain "in-place makes no sense"
|
|
}
|
|
|
|
test("refuse to truncate float literal 3") {
|
|
val text = """
|
|
%option enable_floats
|
|
main {
|
|
sub start() {
|
|
uword @shared ww = 3.456 as uword
|
|
ww++
|
|
ww = 3.456 as uword
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "refused"
|
|
errors.errors[1] shouldContain "refused"
|
|
}
|
|
|
|
test("correct implicit casts of signed number comparison and logical expressions") {
|
|
val text = """
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
byte bb = -10
|
|
word ww = -1000
|
|
|
|
if bb>0 {
|
|
bb++
|
|
}
|
|
if bb < 0 {
|
|
bb ++
|
|
}
|
|
if bb & 1 {
|
|
bb++
|
|
}
|
|
if bb & 128 {
|
|
bb++
|
|
}
|
|
if bb & 255 {
|
|
bb++
|
|
}
|
|
|
|
if ww>0 {
|
|
ww++
|
|
}
|
|
if ww < 0 {
|
|
ww ++
|
|
}
|
|
if ww & 1 {
|
|
ww++
|
|
}
|
|
if ww & 32768 {
|
|
ww++
|
|
}
|
|
if ww & 65535 {
|
|
ww++
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
|
val statements = result.compilerAst.entrypoint.statements
|
|
statements.size shouldBeGreaterThan 10
|
|
}
|
|
|
|
test("cast to unsigned in conditional") {
|
|
val text = """
|
|
main {
|
|
sub start() {
|
|
byte bb
|
|
word ww
|
|
|
|
ubyte iteration_in_progress
|
|
uword num_bytes
|
|
|
|
if not iteration_in_progress or not num_bytes {
|
|
num_bytes++
|
|
}
|
|
|
|
if bb as ubyte {
|
|
bb++
|
|
}
|
|
if ww as uword {
|
|
ww++
|
|
}
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
|
val statements = result.compilerAst.entrypoint.statements
|
|
statements.size shouldBeGreaterThan 10
|
|
}
|
|
|
|
test("no infinite typecast loop in assignment asmgen") {
|
|
val text = """
|
|
main {
|
|
sub start() {
|
|
word @shared qq = calculate(33)
|
|
}
|
|
|
|
sub calculate(ubyte row) -> word {
|
|
return (8-(row as byte))
|
|
}
|
|
}
|
|
"""
|
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
|
}
|
|
|
|
test("(u)byte extend to word parameters") {
|
|
val text = """
|
|
main {
|
|
sub start() {
|
|
byte ub1 = -50
|
|
byte ub2 = -51
|
|
byte ub3 = -52
|
|
byte ub4 = 100
|
|
word @shared ww = func(ub1, ub2, ub3, ub4)
|
|
ww = func(ub4, ub2, ub3, ub1)
|
|
ww=afunc(ub1, ub2, ub3, ub4)
|
|
ww=afunc(ub4, ub2, ub3, ub1)
|
|
}
|
|
|
|
sub func(word x1, word y1, word x2, word y2) -> word {
|
|
return x1
|
|
}
|
|
|
|
asmsub afunc(word x1 @R0, word y1 @R1, word x2 @R2, word y2 @R3) -> word @AY {
|
|
%asm {{
|
|
lda cx16.r0
|
|
ldy cx16.r0+1
|
|
rts
|
|
}}
|
|
}
|
|
}"""
|
|
compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null
|
|
}
|
|
|
|
test("lsb msb used as args with word types") {
|
|
val text = """
|
|
main {
|
|
sub start() {
|
|
uword xx=${'$'}ea31
|
|
uword @shared ww = plot(lsb(xx), msb(xx))
|
|
}
|
|
|
|
inline asmsub plot(uword plotx @R0, uword ploty @R1) -> uword @AY{
|
|
%asm {{
|
|
lda cx16.r0
|
|
ldy cx16.r1
|
|
rts
|
|
}}
|
|
}
|
|
}"""
|
|
compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null
|
|
}
|
|
|
|
test("memory reads byte into word variable") {
|
|
val text = """
|
|
main {
|
|
sub start() {
|
|
uword @shared ww
|
|
uword address = $1000
|
|
ww = @(address+100)
|
|
ww = @(address+1000)
|
|
cx16.r0 = @(address+100)
|
|
cx16.r0 = @(address+1000)
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
|
}
|
|
|
|
test("various floating point casts don't crash the compiler") {
|
|
val text="""
|
|
%import floats
|
|
|
|
main {
|
|
sub score() -> ubyte {
|
|
cx16.r15++
|
|
return 5
|
|
}
|
|
|
|
sub start() {
|
|
float @shared total = 0
|
|
ubyte bb = 5
|
|
|
|
cx16.r0 = 5
|
|
total += cx16.r0 as float
|
|
total += score() as float
|
|
uword ww = 5
|
|
total += ww as float
|
|
total += bb as float
|
|
float result = score() as float
|
|
total += result
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
|
compileText(C64Target(), true, text, writeAssembly = true) shouldNotBe null
|
|
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
|
compileText(VMTarget(), true, text, writeAssembly = true) shouldNotBe null
|
|
}
|
|
|
|
test("byte when choices silently converted to word for convenience") {
|
|
val text="""
|
|
main {
|
|
sub start() {
|
|
uword z = 3
|
|
when z {
|
|
1-> z++
|
|
2-> z++
|
|
else -> z++
|
|
}
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, text, writeAssembly = false) shouldNotBe null
|
|
}
|
|
|
|
test("returning smaller dt than returndt is ok") {
|
|
val text="""
|
|
main {
|
|
sub start() {
|
|
void test()
|
|
}
|
|
|
|
sub test() -> uword {
|
|
return cx16.r0L
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, text, writeAssembly = false) shouldNotBe null
|
|
}
|
|
|
|
test("returning bigger dt than returndt is not ok") {
|
|
val text="""
|
|
main {
|
|
sub start() {
|
|
void test()
|
|
}
|
|
|
|
sub test() -> ubyte {
|
|
return cx16.r0
|
|
}
|
|
}"""
|
|
val errors=ErrorReporterForTests()
|
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
|
errors.errors.single() shouldContain "doesn't match"
|
|
}
|
|
|
|
test("long type okay in const expr but otherwise overflow") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
const ubyte HEIGHT=240
|
|
uword large = 320*240/8/8
|
|
thing(large)
|
|
thing(320*240/8/8)
|
|
thing(320*HEIGHT/8/8)
|
|
thing(320*HEIGHT) ; overflow
|
|
large = 12345678 ; overflow
|
|
}
|
|
|
|
sub thing(uword value) {
|
|
value++
|
|
}
|
|
}"""
|
|
val errors=ErrorReporterForTests()
|
|
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 5
|
|
errors.errors[0] shouldContain "no cast"
|
|
errors.errors[1] shouldContain "overflow"
|
|
errors.errors[2] shouldContain "LONG doesn't match"
|
|
errors.errors[3] shouldContain "out of range"
|
|
errors.errors[4] shouldContain "overflow"
|
|
}
|
|
|
|
})
|