Files
prog8/compiler/test/TestTypecasts.kt
Irmen de Jong 093be9f2dd sys.push(), sys.pop() etc etc are now builtin functions again push() pop() to avoid storing value in temporary variables
this means that all of the syslib.p8 library files no longer contain all those stack related asmsubs
2026-02-21 02:11:34 +01:00

1027 lines
35 KiB
Kotlin

package prog8tests.compiler
import io.kotest.core.spec.style.FunSpec
import io.kotest.engine.spec.tempdir
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.printProgram
import prog8.ast.statements.Assignment
import prog8.ast.statements.IfElse
import prog8.code.core.BaseDataType
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({
val outputDir = tempdir().toPath()
test("integer args for builtin funcs") {
val text = """
%import floats
main {
sub start() {
float fl
floats.print(lsb(fl))
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(C64Target(), false, text, outputDir, 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, LONG]"
}
test("not casting bool operands to logical operators") {
val text = """
main {
sub start() {
bool @shared bb2=true
bool @shared bb3=false
bool @shared bb = bb2 and bb3
}
}"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 7
val expr = (stmts[5] as Assignment).value as BinaryExpression
expr.operator shouldBe "and"
(expr.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
(expr.right as IdentifierReference).nameInSource shouldBe listOf("bb3") // no cast
val result2 = compileText(C64Target(), true, text, outputDir, writeAssembly = true)!!
val stmts2 = result2.compilerAst.entrypoint.statements
stmts2.size shouldBe 7
val expr2 = (stmts2[5] as Assignment).value as BinaryExpression
expr2.operator shouldBe "and"
(expr2.left as IdentifierReference).nameInSource shouldBe listOf("bb2") // no cast
(expr2.right as IdentifierReference).nameInSource shouldBe listOf("bb3") // no cast
}
test("bool expressions with functioncalls") {
val text = """
main {
sub ftrue(ubyte arg) -> ubyte {
arg++
return 42
}
sub btrue(ubyte arg) -> bool {
arg++
return true
}
sub start() {
bool @shared ub1 = true
bool @shared ub2 = true
bool @shared ub3 = true
bool @shared bvalue
bvalue = ub1 xor ub2 xor ub3 xor true
bvalue = ub1 xor ub2 xor ub3 xor ftrue(99)!=0
bvalue = ub1 and ub2 and ftrue(99)!=0
bvalue = ub1 xor ub2 xor ub3 xor btrue(99)
bvalue = ub1 and ub2 and btrue(99)
}
}"""
val result = compileText(C64Target(), true, text, outputDir, writeAssembly = true)!!
val stmts = result.compilerAst.entrypoint.statements
printProgram(result.compilerAst)
/*
bool @shared ub1
ub1 = true
bool @shared ub2
ub2 = true
bool @shared ub3
ub3 = true
bool @shared bvalue
bvalue = not ((ub1 xor ub2) xor ub3)
bvalue = (((ub1 xor ub2) xor ub3) xor (ftrue(99)!=0))
bvalue = ((ub1 and ub2) and (ftrue(99)!=0))
bvalue = (((ub1 xor ub2) xor ub3) xor btrue(99))
bvalue = ((ub1 and ub2) and btrue(99))
return
*/
stmts.size shouldBe 13
val assignValue1 = (stmts[7] as Assignment).value as PrefixExpression
val assignValue2 = (stmts[8] as Assignment).value as BinaryExpression
val assignValue3 = (stmts[9] as Assignment).value as BinaryExpression
val assignValue4 = (stmts[10] as Assignment).value as BinaryExpression
val assignValue5 = (stmts[11] as Assignment).value as BinaryExpression
assignValue1.operator shouldBe "not"
assignValue2.operator shouldBe "xor"
assignValue3.operator shouldBe "and"
assignValue4.operator shouldBe "xor"
assignValue5.operator shouldBe "and"
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(BaseDataType.UBYTE, 0.0, Position.DUMMY)
right3.left shouldBe instanceOf<IFunctionCall>()
right3.right shouldBe NumericLiteral(BaseDataType.UBYTE, 0.0, Position.DUMMY)
assignValue4.right shouldBe instanceOf<IFunctionCall>()
assignValue5.right shouldBe instanceOf<IFunctionCall>()
}
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
boolvalue1 = thing(true, false)
boolvalue2 = thing(false, true)
if boolvalue1 and boolvalue2
boolvalue1=false
}
}"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 8
val fcall1 = ((stmts[4] as Assignment).value as IFunctionCall)
fcall1.args[0] shouldBe NumericLiteral(BaseDataType.BOOL, 1.0, Position.DUMMY)
fcall1.args[1] shouldBe NumericLiteral(BaseDataType.BOOL, 0.0, Position.DUMMY)
val fcall2 = ((stmts[5] as Assignment).value as IFunctionCall)
fcall2.args[0] shouldBe NumericLiteral(BaseDataType.BOOL, 0.0, Position.DUMMY)
fcall2.args[1] shouldBe NumericLiteral(BaseDataType.BOOL, 1.0, Position.DUMMY)
val ifCond = (stmts[6] 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
bool @shared interlaced
interlaced = (camg & $0004) != 0
cx16.r0L++
interlaced = ($0004 & camg) != 0
cx16.r0L++
uword @shared ww
ww = (camg & $0004)
ww++
ww = ($0004 & camg)
ubyte @shared value
bool @shared collected = (value >= $33) or (value >= $66) or (value >= $99) or (value >= ${'$'}CC)
}
}"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = true)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBeGreaterThan 10
}
test("word to byte casts") {
val text = """
main {
sub func(ubyte arg) -> word {
return arg-99
}
sub start() {
funcub(func(0) as ubyte)
funcuw(func(0) as ubyte)
funcw(func(0) as ubyte)
}
sub funcub(ubyte arg) { }
sub funcuw(uword arg) { }
sub funcw(word arg) { }
}"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 4
}
test("ubyte to word casts") {
val src = """
main {
sub start() {
ubyte @shared bb = 255
cx16.r0s = (bb as byte) as word ; should result in -1 word value
cx16.r1s = (bb as word) ; should result in 255 word value
}
}"""
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = false)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 5
val assign1tc = (stmts[2] as Assignment).value as TypecastExpression
val assign2tc = (stmts[3] as Assignment).value as TypecastExpression
assign1tc.type shouldBe DataType.WORD
assign2tc.type shouldBe DataType.WORD
assign2tc.expression shouldBe instanceOf<IdentifierReference>()
val assign1subtc = (assign1tc.expression as TypecastExpression)
assign1subtc.type shouldBe DataType.BYTE
assign1subtc.expression shouldBe instanceOf<IdentifierReference>()
}
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, outputDir, writeAssembly = false)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 9
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, outputDir, 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, outputDir, writeAssembly = true, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "no cast"
errors.errors[1] shouldContain "no cast"
}
test("allow explicit float literal cast to integer") {
val text = """
%option enable_floats
main {
sub start() {
cx16.r0 = 1234.5678 as uword
cx16.r1L = 99.333 as ubyte
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, errors=errors) shouldNotBe null
errors.errors.size shouldBe 0
}
test("refuse to truncate float inplace") {
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, outputDir, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "in-place makes no sense"
}
test("correct implicit casts of signed number comparison and logical expressions") {
val text = """
%import floats
main {
sub start() {
byte @shared bb = -10
word @shared ww = -1000
if bb>0 {
bb++
}
if bb < 0 {
bb ++
}
if bb & 1 !=0 {
bb++
}
if bb & 128 !=0 {
bb++
}
if bb & 255 !=0 {
bb++
}
if ww>0 {
ww++
}
if ww < 0 {
ww ++
}
if ww & 1 !=0 {
ww++
}
if ww & 32768 != 0 {
ww++
}
if ww & 65535 != 0 {
ww++
}
}
}
"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = true)!!
val statements = result.compilerAst.entrypoint.statements
statements.size shouldBeGreaterThan 10
}
test("cast to unsigned in conditional") {
val text = """
main {
sub start() {
byte @shared bb
word @shared ww
bool @shared iteration_in_progress
uword @shared num_bytes
if not iteration_in_progress or num_bytes==0 {
num_bytes++
}
if bb as ubyte !=0 {
bb++
}
if ww as uword !=0 {
ww++
}
}
}"""
val result = compileText(C64Target(), false, text, outputDir, 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, outputDir, writeAssembly = true) shouldNotBe null
}
test("(u)byte extend to word parameters") {
val text = """
main {
sub start() {
byte @shared ub1 = -50
byte @shared ub2 = -51
byte @shared ub3 = -52
byte @shared 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, outputDir, 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, outputDir, 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, outputDir, 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, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), true, text, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), true, text, outputDir, 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, outputDir, 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, outputDir, 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, outputDir, 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, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain ":9:"
errors.errors[0] shouldContain "no implicit cast"
errors.errors[1] shouldContain ":10:"
errors.errors[1] shouldContain "out of range"
errors.errors[2] shouldContain ":10:"
errors.errors[2] shouldContain "doesn't match"
}
test("various bool typecasts and type mismatches") {
val src = """
%option enable_floats
main {
sub start() {
float @shared fl
ubyte @shared flags
byte @shared flagss
uword @shared flagsw
word @shared flagssw
bool @shared bflags = 123
cx16.r0++
bflags = 123
cx16.r0++
bflags = 123 as bool
flags = bflags
flagss = bflags
flagsw = bflags
flagssw = bflags
fl = bflags
bflags = flags
bflags = flagss
bflags = flagsw
bflags = flagssw
bflags = fl
flags = bflags as ubyte
flagss = bflags as byte
flagsw = bflags as uword
flagssw = bflags as word
fl = bflags as float
bflags = flags as bool
bflags = flagss as bool
bflags = flagsw as bool
bflags = flagssw as bool
bflags = fl as bool
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 12
errors.errors.all { "value type" in it } shouldBe true
errors.errors.all { "doesn't match" in it } shouldBe true
}
test("bool to byte cast in expression is not allowed") {
val text = """
main {
sub start() {
ubyte[3] values
func1(22 in values)
func2(22 in values)
ubyte @shared qq = 22 in values
byte @shared ww = 22 in values
}
sub func1(ubyte arg) {
arg++
}
sub func2(byte arg) {
arg++
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ("argument 1 type mismatch")
errors.errors[1] shouldContain ("argument 1 type mismatch")
errors.errors[2] shouldContain ("value type bool doesn't match target")
errors.errors[3] shouldContain ("value type bool doesn't match target")
}
test("bool function parameters correct typing and no implicit casts to bool") {
val src = """
main {
sub start() {
^^word @shared pointer
bool @shared bb = func(true)
void func(true)
void func(0)
void func(1)
void func(42)
void func(65535)
void func(655.444)
void func(cx16.r0)
void func(pointer)
cx16.r0L = func(true)
}
sub func(bool draw) -> bool {
cx16.r0++
return true
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 8
errors.errors[0] shouldContain ("type mismatch")
errors.errors[1] shouldContain ("type mismatch")
errors.errors[2] shouldContain ("type mismatch")
errors.errors[3] shouldContain ("type mismatch")
errors.errors[4] shouldContain ("type mismatch")
errors.errors[5] shouldContain ("type mismatch")
errors.errors[6] shouldContain ("type mismatch")
errors.errors[7] shouldContain ("value type bool doesn't match target")
}
test("no implicit bool-to-int cast in assignment") {
val src = """
main {
sub start() {
cx16.r0L = true
cx16.r0L = true as ubyte
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain ("value type bool doesn't match target")
}
test("no implicit int-to-bool cast") {
val src = """
main {
sub start() {
func1(true)
func2(true)
func1(true as ubyte)
func2(true as uword)
bool @shared bb1 = 1
bool @shared bb2 = 12345
bool @shared bb3 = 1 as bool
bool @shared bb4 = 12345 as bool
}
sub func1(ubyte ub) {
cx16.r0++
}
sub func2(uword uw) {
cx16.r0++
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain (":4:15: no implicit cast")
errors.errors[1] shouldContain (":5:15: no implicit cast")
errors.errors[2] shouldContain (":8:28: value type ubyte doesn't match target")
errors.errors[3] shouldContain (":9:28: value type uword doesn't match target")
}
test("return 0 for str converted to uword") {
val src = """
main {
sub start() {
cx16.r0 = test()
}
sub test() -> str {
cx16.r0++
if cx16.r0L==255
return 0
return 42
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("bool to word cast") {
val src = """
main {
sub start() {
bool @shared flag, flag2
cx16.r0L = (flag and flag2) as ubyte
cx16.r0 = (flag and flag2) as uword
}
}"""
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("undefined symbol error instead of type cast error") {
val src = """
main {
const ubyte foo = 0
ubyte bar = 0
sub start() {
when foo {
notdefined -> bar = 1
else -> bar = 2
}
bool z = undefined != 0
bool z2 = undefined == 0
if undefined==0
z= true
if undefined!=0
z= false
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 10
errors.errors[1] shouldContain "undefined symbol"
errors.errors[2] shouldContain "undefined symbol"
errors.errors[4] shouldContain "undefined symbol"
errors.errors[6] shouldContain "undefined symbol"
errors.errors[8] shouldContain "undefined symbol"
}
test("return unsigned values for signed results ok if value fits") {
val src = """
main {
sub start() {
void foo()
void bar()
void overflow1()
void overflow2()
}
sub foo() -> byte {
return 42
}
sub bar() -> word {
return 12345
}
sub overflow1() -> byte {
return 200
}
sub overflow2() -> word {
return 44444
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "17:16: return value type ubyte doesn't match subroutine return type byte"
errors.errors[1] shouldContain "20:16: return value type uword doesn't match subroutine return type word"
}
test("if-expression adjusts different value types to common type") {
val src = """
main {
sub start() {
cx16.r0sL = if cx16.r0L < cx16.r1L -1 else 1
}
}"""
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = false)!!
val program = result.compilerAst
val st = program.entrypoint.statements
st.size shouldBe 2
val assign = st[0] as Assignment
assign.target.inferType(program).getOrUndef().base shouldBe BaseDataType.BYTE
val ifexpr = assign.value as IfExpression
ifexpr.truevalue.inferType(program).getOrUndef().base shouldBe BaseDataType.BYTE
ifexpr.falsevalue.inferType(program).getOrUndef().base shouldBe BaseDataType.BYTE
ifexpr.truevalue shouldBe instanceOf<NumericLiteral>()
ifexpr.falsevalue shouldBe instanceOf<NumericLiteral>()
}
test("correct data types of numeric literals in word/byte scenario") {
val src = """
main {
sub start() {
const uword WIDTH = 40
const uword WIDER = 400
cx16.r0 = cx16.r0-1+WIDTH
cx16.r1 = cx16.r1-1+WIDER
cx16.r2 = cx16.r2L * 5 ; byte multiplication
cx16.r3 = cx16.r3L * $0005 ; word multiplication
}
}"""
val result = compileText(C64Target(), false, src, outputDir, writeAssembly = false)!!
val program = result.compilerAst
val st = program.entrypoint.statements
st.size shouldBe 7
val v1 = (st[2] as Assignment).value as BinaryExpression
v1.operator shouldBe "+"
(v1.left as IdentifierReference).nameInSource shouldBe listOf("cx16", "r0")
(v1.right as NumericLiteral).type shouldBe BaseDataType.UWORD
(v1.right as NumericLiteral).number shouldBe 39
val v2 = (st[3] as Assignment).value as BinaryExpression
v2.operator shouldBe "+"
(v2.left as IdentifierReference).nameInSource shouldBe listOf("cx16", "r1")
(v2.right as NumericLiteral).type shouldBe BaseDataType.UWORD
(v2.right as NumericLiteral).number shouldBe 399
val v3 = (st[4] as Assignment).value as TypecastExpression
v3.type shouldBe DataType.UWORD
val v3e = v3.expression as BinaryExpression
v3e.operator shouldBe "*"
(v3e.left as IdentifierReference).nameInSource shouldBe listOf("cx16", "r2L")
(v3e.right as NumericLiteral).type shouldBe BaseDataType.UBYTE
(v3e.right as NumericLiteral).number shouldBe 5
val v4 = (st[5] as Assignment).value as BinaryExpression
v4.operator shouldBe "*"
val v4t = v4.left as TypecastExpression
v4t.type shouldBe DataType.UWORD
(v4t.expression as IdentifierReference).nameInSource shouldBe listOf("cx16", "r3L")
(v4.right as NumericLiteral).type shouldBe BaseDataType.UWORD
(v4.right as NumericLiteral).number shouldBe 5
}
test("allow comparisons against constant values with different type") {
val src = """
main {
sub start() {
const uword MAX_CAVE_WIDTH = 440 ; word here to avoid having to cast to word all the time
if cx16.r0L > MAX_CAVE_WIDTH
return
}
}"""
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("common subtypes") {
BinaryExpression.commonDatatype(DataType.WORD, DataType.UWORD, null, null).first shouldBe DataType.WORD
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.UBYTE, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.UWORD, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.BYTE, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.pointer(BaseDataType.BOOL), DataType.WORD, null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.UBYTE, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.UWORD, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.BYTE, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
BinaryExpression.commonDatatype(DataType.WORD, DataType.pointer(BaseDataType.BOOL), null, null).first shouldBe DataType.pointer(BaseDataType.BOOL)
}
test("bitwise operator on signed values") {
val src = """
main {
sub start() {
word[10] xpos
xpos[4] &= ${'$'}fff8
xpos[5] &= ${'$'}fff8 as word
xpos[6] = xpos[4] & ${'$'}fff8
xpos[7] = xpos[4] & ${'$'}fff8 as word
xpos[4] &= $7000
xpos[5] &= $7000 as word
xpos[6] = xpos[4] & $7000
xpos[7] = xpos[4] & $7000 as word
xpos[4] |= $7000
xpos[5] |= $7000 as word
xpos[6] = xpos[4] | $7000
xpos[7] = xpos[4] | $7000 as word
; the error doesn't occur with other operators:
xpos[4] += $7000
xpos[5] += $7000 as word
xpos[6] = xpos[4] + $7000
xpos[7] = xpos[4] + $7000 as word
}
}"""
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("assign typecasted value to struct field") {
val src = """
main {
struct element {
word y
}
sub start() {
^^element zp_element = 20000
zp_element.y = cx16.r0L as byte
}
}"""
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("negative numbers okay for pokes") {
val src = """
main {
sub start() {
@(4000) = 100
@(4000) = -100
poke(4000, 100)
poke(4000, -100)
pokew(4000, 9999)
pokew(4000, -9999)
pokel(4000, 999999)
pokel(4000, -999999)
}
}"""
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("various long to float") {
val src = """
%import floats
main {
sub start() {
long @shared lv = 1234567
float @shared fl1 = lv as float / 1.234
float @shared fl2 = (cx16.r0r1sl - cx16.r2r3sl) as float
float @shared fl3 = -lv as float
}
}"""
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
})