mirror of
				https://github.com/irmen/prog8.git
				synced 2025-11-03 19:16:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			618 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Kotlin
		
	
	
	
	
	
			
		
		
	
	
			618 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Kotlin
		
	
	
	
	
	
package prog8tests.codegeneration
 | 
						|
 | 
						|
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.string.shouldStartWith
 | 
						|
import io.kotest.matchers.types.instanceOf
 | 
						|
import prog8.code.ast.*
 | 
						|
import prog8.code.core.BaseDataType
 | 
						|
import prog8.code.core.DataType
 | 
						|
import prog8.code.target.*
 | 
						|
import prog8tests.helpers.ErrorReporterForTests
 | 
						|
import prog8tests.helpers.compileText
 | 
						|
import kotlin.io.path.readText
 | 
						|
 | 
						|
class TestVariousCodeGen: FunSpec({
 | 
						|
 | 
						|
    val outputDir = tempdir().toPath()
 | 
						|
 | 
						|
    test("nested scoping") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        testscope.duplicate()
 | 
						|
        cx16.r0L = testscope.duplicate2()
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
testscope {
 | 
						|
 | 
						|
    sub sub1() {
 | 
						|
        ubyte @shared duplicate
 | 
						|
        ubyte @shared duplicate2
 | 
						|
    }
 | 
						|
 | 
						|
    sub duplicate() {
 | 
						|
        ; do nothing
 | 
						|
    }
 | 
						|
 | 
						|
    sub duplicate2() -> ubyte {
 | 
						|
        return cx16.r0L
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("word array indexing") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        uword[3] seed
 | 
						|
        cx16.r0 = seed[0] + seed[1] + seed[2]
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("ast result from compileText") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        uword[3] seed
 | 
						|
        cx16.r0 = seed[0] + seed[1] + seed[2]
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        val result = compileText(C64Target(), false, text, outputDir, writeAssembly = true)!!
 | 
						|
        result.compilerAst.name shouldStartWith  "on_the_fly"
 | 
						|
        val ast = result.codegenAst!!
 | 
						|
        ast.name shouldBe result.compilerAst.name
 | 
						|
        ast.children.size shouldBeGreaterThan 2
 | 
						|
        val start = ast.entrypoint()!!
 | 
						|
        start.name shouldBe "p8s_start"
 | 
						|
        start.children.size shouldBeGreaterThan 2
 | 
						|
        val seed = start.children[1] as PtVariable
 | 
						|
        seed.name shouldBe "p8v_seed"
 | 
						|
        seed.value shouldBe null
 | 
						|
        seed.type shouldBe DataType.arrayFor(BaseDataType.UWORD)
 | 
						|
        val assign = start.children[2] as PtAssignment
 | 
						|
        assign.target.identifier!!.name shouldBe "cx16.r0"
 | 
						|
        assign.value shouldBe instanceOf<PtBinaryExpression>()
 | 
						|
    }
 | 
						|
 | 
						|
    test("peek and poke argument types") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        uword[3] arr
 | 
						|
        ubyte i = 42
 | 
						|
        uword ww = peekw(arr[i])
 | 
						|
        ubyte xx = peek(arr[i])
 | 
						|
        xx = @(arr[i])
 | 
						|
        
 | 
						|
        @(arr[i]) = 42
 | 
						|
        poke(arr[i], 42)
 | 
						|
        pokew(arr[i], 4242)
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("assigning memory byte into arrays works") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        uword factor1
 | 
						|
        ubyte[3] bytearray
 | 
						|
        uword[3] wordarray
 | 
						|
        @(factor1) = bytearray[0]
 | 
						|
        bytearray[0] = @(factor1)
 | 
						|
        @(factor1) = lsb(wordarray[0])
 | 
						|
        wordarray[0] = @(factor1)
 | 
						|
        @(5000) = bytearray[0]
 | 
						|
        @(5000) = lsb(wordarray[0])
 | 
						|
        bytearray[0] = @(5000)
 | 
						|
        wordarray[0] = @(5000)
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("reading memory from unknown var gives proper error") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        cx16.r0L = @(doesnotexist)
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        val errors = ErrorReporterForTests()
 | 
						|
        compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors = errors)
 | 
						|
        errors.errors.size shouldBe 2
 | 
						|
        errors.errors[0] shouldContain "invalid address type"
 | 
						|
        errors.errors[1] shouldContain "undefined symbol: doesnotexist"
 | 
						|
    }
 | 
						|
 | 
						|
    test("shifting by word value is ok") {
 | 
						|
        val text="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        ubyte @shared c = 1
 | 
						|
        @(15000 + c<<${'$'}0003) = 42
 | 
						|
        @(15000 + (c<<${'$'}0003)) = 42
 | 
						|
        @(15000 + c*${'$'}0008) = 42       ; *8 becomes a shift after opt
 | 
						|
 | 
						|
        uword @shared qq = 15000 + c<<${'$'}0003
 | 
						|
        qq = 15000 + (c<<${'$'}0003)
 | 
						|
        qq = 16000 + c*${'$'}0008
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), true, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(VMTarget(), true, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("builtin func in float expression") {
 | 
						|
        val src="""
 | 
						|
%import floats
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        float @shared fl =25.1
 | 
						|
        fl = abs(fl)+0.5
 | 
						|
        fl = sqrt(fl)+0.5
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("string vars in inlined subroutines are ok") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        void block2.curdir()
 | 
						|
        void block2.other()
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
block2 {
 | 
						|
    str result="zzzz"
 | 
						|
    sub curdir() -> str {
 | 
						|
        return result
 | 
						|
    }
 | 
						|
 | 
						|
    sub other() -> str {
 | 
						|
        return "other"
 | 
						|
    }
 | 
						|
}"""
 | 
						|
 | 
						|
        compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("array with pointers") {
 | 
						|
        val src = """
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        str localstr = "hello"
 | 
						|
        ubyte[] otherarray = [1,2,3]
 | 
						|
        uword[] words = [1111,2222,"three",&localstr,&otherarray]
 | 
						|
        uword @shared zz = &words
 | 
						|
        bool @shared result = 2222 in words
 | 
						|
        zz = words[2]
 | 
						|
        zz++
 | 
						|
        zz = words[3]
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        val othertarget = Cx16Target()
 | 
						|
        compileText(othertarget, true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("case sensitive symbols") {
 | 
						|
        val src = """
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        ubyte bytevar = 11      ; var at 0
 | 
						|
        ubyte byteVAR = 22      ; var at 1
 | 
						|
        ubyte ByteVar = 33      ; var at 2
 | 
						|
        ubyte @shared total = bytevar+byteVAR+ByteVar   ; var at 3
 | 
						|
        goto skipLABEL
 | 
						|
SkipLabel:
 | 
						|
        return
 | 
						|
skipLABEL:
 | 
						|
        bytevar = 42
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        val target = Cx16Target()
 | 
						|
        compileText(target, true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("addresses from labels/subroutines") {
 | 
						|
        val src = """
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
 | 
						|
mylabel:
 | 
						|
        ubyte @shared variable
 | 
						|
        uword @shared pointer1 = &main.start
 | 
						|
        uword @shared pointer2 = &start
 | 
						|
        uword @shared pointer3 = &main.start.mylabel
 | 
						|
        uword @shared pointer4 = &mylabel
 | 
						|
        uword[] @shared ptrs = [&variable, &start, &main.start, &mylabel, &main.start.mylabel]
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
"""
 | 
						|
        compileText(Cx16Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("duplicate symbols okay other block and variable") {
 | 
						|
        val src = """
 | 
						|
main {
 | 
						|
    ubyte derp
 | 
						|
    sub start() {
 | 
						|
        derp++
 | 
						|
        foo.bar()
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
foo {
 | 
						|
    sub bar() {
 | 
						|
        derp.print()
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
derp {
 | 
						|
    sub print() {
 | 
						|
        cx16.r0++
 | 
						|
        cx16.r1++
 | 
						|
    }
 | 
						|
}"""
 | 
						|
 | 
						|
        compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("ambiguous symbol name variable vs block") {
 | 
						|
        val src = """
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        uword module
 | 
						|
        module++
 | 
						|
        module.test++
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
module {
 | 
						|
    ubyte @shared test
 | 
						|
}
 | 
						|
"""
 | 
						|
        val errors=ErrorReporterForTests()
 | 
						|
        compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
 | 
						|
        errors.errors.size shouldBe 1
 | 
						|
        errors.errors[0] shouldContain "ambiguous symbol"
 | 
						|
    }
 | 
						|
 | 
						|
    test("prefix expressions with typecasting") {
 | 
						|
        val src="""
 | 
						|
main
 | 
						|
{
 | 
						|
    sub start()
 | 
						|
    {
 | 
						|
        uword uw = 54321
 | 
						|
        ubyte ub = 123
 | 
						|
        word sw = -12345
 | 
						|
        byte sb = -123
 | 
						|
 | 
						|
        func_uw(~ub as uword) 
 | 
						|
        func_ub(~uw as ubyte) 
 | 
						|
        func_uw(~sb as uword) 
 | 
						|
        func_ub(~sw as ubyte) 
 | 
						|
        func_w(-sb as word)   
 | 
						|
        func_b(-sw as byte)   
 | 
						|
    }
 | 
						|
    
 | 
						|
    sub func_uw(uword arg) {
 | 
						|
    }
 | 
						|
    sub func_w(word arg) {
 | 
						|
    }
 | 
						|
    sub func_ub(ubyte arg) {
 | 
						|
    }
 | 
						|
    sub func_b(byte arg) {
 | 
						|
    }
 | 
						|
}"""
 | 
						|
 | 
						|
        compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("inlining sub with 2 statements") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        init()
 | 
						|
    }
 | 
						|
 | 
						|
    sub init() {
 | 
						|
        init_handler()
 | 
						|
        return
 | 
						|
    }
 | 
						|
 | 
						|
    sub init_handler() {
 | 
						|
        cx16.r0++
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(VMTarget(), true, src, outputDir, writeAssembly = false) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("push pop are inlined also with noopt") {
 | 
						|
        val text = """
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        sys.push(11)
 | 
						|
        sys.pushw(2222)
 | 
						|
        sys.push_returnaddress(3333)
 | 
						|
        cx16.r2++
 | 
						|
        cx16.r1 = sys.popw()
 | 
						|
        cx16.r0L = sys.pop()
 | 
						|
    } 
 | 
						|
}"""
 | 
						|
        val result = compileText(C64Target(), false, text, outputDir, writeAssembly = true)!!
 | 
						|
        val assemblyFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".asm")
 | 
						|
        val assembly = assemblyFile.readText()
 | 
						|
        assembly shouldContain "inlined routine follows: push"
 | 
						|
        assembly shouldContain "inlined routine follows: pushw"
 | 
						|
        assembly shouldContain "inlined routine follows: push_returnaddress"
 | 
						|
        assembly shouldContain "inlined routine follows: pop"
 | 
						|
        assembly shouldContain "inlined routine follows: popw"
 | 
						|
    }
 | 
						|
 | 
						|
    test("syslib correctly available for raw outputs") {
 | 
						|
        val text = """
 | 
						|
%output raw
 | 
						|
%launcher none
 | 
						|
%address ${'$'}2000
 | 
						|
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        cx16.r0++
 | 
						|
        sys.clear_carry()
 | 
						|
    }
 | 
						|
}
 | 
						|
"""
 | 
						|
        compileText(Cx16Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(C64Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(C128Target(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(PETTarget(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(VMTarget(), false, text, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("block start address must be greater than program load address") {
 | 
						|
        val src = """
 | 
						|
%output raw
 | 
						|
%launcher none
 | 
						|
%address ${'$'}2000
 | 
						|
 | 
						|
main $2000 {
 | 
						|
    sub start() {
 | 
						|
        sys.clear_carry()
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
otherblock ${'$'}2013 {
 | 
						|
    %option force_output
 | 
						|
}
 | 
						|
 | 
						|
thirdblock ${'$'}2014 {
 | 
						|
    %option force_output
 | 
						|
}"""
 | 
						|
        val errors = ErrorReporterForTests()
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
 | 
						|
        errors.errors.size shouldBe 2
 | 
						|
        errors.errors[0] shouldContain "6:1: block address must be at least program load address + 20"
 | 
						|
        errors.errors[1] shouldContain "12:1: block address must be at least program load address + 20"
 | 
						|
    }
 | 
						|
 | 
						|
    test("for loops with just 1 iteration") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        for cx16.r0L in 100 downto 100 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
        for cx16.r0L in 100 to 100 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
        for cx16.r0 in 2222 downto 2222 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
        for cx16.r0 in 2222 to 2222 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
 | 
						|
        for cx16.r0L in 100 downto 100 step -5 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
        for cx16.r0L in 100 to 100 step 5 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
        for cx16.r0 in 2222 downto 2222 step -5 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
        for cx16.r0 in 2222 to 2222 step 5 {
 | 
						|
            cx16.r1++
 | 
						|
        }
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("multiple status flags return values from asmsub") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    extsub 5000 = carryAndNegativeAndByteAndWord() -> bool @Pc, bool @Pn, ubyte @X, uword @AY
 | 
						|
 | 
						|
    sub start() {
 | 
						|
        ubyte @shared x
 | 
						|
        uword @shared w
 | 
						|
        bool @shared flag1
 | 
						|
        bool @shared flag2
 | 
						|
 | 
						|
        flag1, flag2, x, w = carryAndNegativeAndByteAndWord()
 | 
						|
        flag1, void, void, w = carryAndNegativeAndByteAndWord()
 | 
						|
        void, void, void, void = carryAndNegativeAndByteAndWord()
 | 
						|
        void carryAndNegativeAndByteAndWord()
 | 
						|
    }    
 | 
						|
}"""
 | 
						|
        compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("missing rts in asmsub") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        test()
 | 
						|
        test2()
 | 
						|
    }
 | 
						|
 | 
						|
    asmsub test() {
 | 
						|
        %asm {{
 | 
						|
            nop
 | 
						|
            nop
 | 
						|
        }}
 | 
						|
    }
 | 
						|
 | 
						|
    inline asmsub test2() {
 | 
						|
        %asm {{
 | 
						|
            nop
 | 
						|
            nop
 | 
						|
        }}
 | 
						|
    }
 | 
						|
}"""
 | 
						|
 | 
						|
        val errors = ErrorReporterForTests()
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = true, errors = errors) shouldBe null
 | 
						|
        errors.errors.size shouldBe 1
 | 
						|
        errors.errors[0] shouldContain "asmsub seems to never return"
 | 
						|
    }
 | 
						|
 | 
						|
    test("missing rts in asmsub suppressed") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        test()
 | 
						|
    }
 | 
						|
 | 
						|
    asmsub test() {
 | 
						|
        %asm {{
 | 
						|
            nop
 | 
						|
            nop
 | 
						|
            ; !notreached!
 | 
						|
        }}
 | 
						|
    }
 | 
						|
}"""
 | 
						|
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
 | 
						|
    test("if not without else is not swapped") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        if not thing()
 | 
						|
            cx16.r0++
 | 
						|
    }
 | 
						|
 | 
						|
    sub thing() -> bool {
 | 
						|
        cx16.r0++
 | 
						|
        return false
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
 | 
						|
        val st = result.codegenAst!!.entrypoint()!!.children
 | 
						|
        st.size shouldBe 3
 | 
						|
        val ifelse = st[1] as PtIfElse
 | 
						|
        ifelse.hasElse() shouldBe false
 | 
						|
        (ifelse.condition as PtPrefix).operator shouldBe "not"
 | 
						|
    }
 | 
						|
 | 
						|
    test("if not with else is swapped") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
        if not thing()
 | 
						|
            cx16.r0++
 | 
						|
        else
 | 
						|
            cx16.r1++
 | 
						|
    }
 | 
						|
 | 
						|
    sub thing() -> bool {
 | 
						|
        cx16.r0++
 | 
						|
        return false
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = true)!!
 | 
						|
        val st = result.codegenAst!!.entrypoint()!!.children
 | 
						|
        st.size shouldBe 3
 | 
						|
        val ifelse = st[1] as PtIfElse
 | 
						|
        ifelse.hasElse() shouldBe true
 | 
						|
        ifelse.condition shouldBe instanceOf<PtFunctionCall>()
 | 
						|
    }
 | 
						|
 | 
						|
    test("bit instruction is used for testing bits 6 and 7 of a byte") {
 | 
						|
        val text = """
 | 
						|
main {
 | 
						|
    sub start() {   
 | 
						|
        if cx16.r0L & ${'$'}80 != 0
 | 
						|
            return
 | 
						|
        if cx16.r1L & ${'$'}80 == 0
 | 
						|
            return
 | 
						|
        if cx16.r2L & ${'$'}40 != 0
 | 
						|
            return
 | 
						|
        if cx16.r3L & ${'$'}40 == 0
 | 
						|
            return
 | 
						|
        cx16.r9L = if cx16.r4L & ${'$'}80 != 0  11 else 22
 | 
						|
        cx16.r10L = if cx16.r5L & ${'$'}40 == 0  11 else 22
 | 
						|
    } 
 | 
						|
}"""
 | 
						|
        val result = compileText(C64Target(), true, text, outputDir, writeAssembly = true)!!
 | 
						|
        val assemblyFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".asm")
 | 
						|
        val assembly = assemblyFile.readText()
 | 
						|
        assembly shouldContain "bit  cx16.r0L"
 | 
						|
        assembly shouldContain "bit  cx16.r1L"
 | 
						|
        assembly shouldContain "bit  cx16.r2L"
 | 
						|
        assembly shouldContain "bit  cx16.r3L"
 | 
						|
        assembly shouldContain "bit  cx16.r4L"
 | 
						|
        assembly shouldContain "bit  cx16.r5L"
 | 
						|
 | 
						|
        val resultIR = compileText(VMTarget(), true, text, outputDir, writeAssembly = true)!!
 | 
						|
        val irFile = resultIR.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
 | 
						|
        val ir = irFile.readText()
 | 
						|
        ir shouldContain "bit.b ${'$'}ff02"     // r0
 | 
						|
        ir shouldContain "bit.b ${'$'}ff04"     // r1
 | 
						|
        ir shouldContain "bit.b ${'$'}ff06"     // f2
 | 
						|
        ir shouldContain "bit.b ${'$'}ff08"     // r3
 | 
						|
        ir shouldContain "bit.b ${'$'}ff0a"     // r4
 | 
						|
        ir shouldContain "bit.b ${'$'}ff0c"     // r5
 | 
						|
    }
 | 
						|
 | 
						|
    test("strings in if expression") {
 | 
						|
        val src="""
 | 
						|
main {
 | 
						|
    sub start() {
 | 
						|
       str foo = "foo"
 | 
						|
       str bar = "bar"
 | 
						|
       bool flag = true
 | 
						|
       uword @shared foobar = if flag foo else bar
 | 
						|
    }
 | 
						|
}"""
 | 
						|
        compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
        compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
 | 
						|
    }
 | 
						|
}) |