mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
225 lines
7.7 KiB
Kotlin
225 lines
7.7 KiB
Kotlin
package prog8tests.compiler
|
|
|
|
import io.kotest.core.spec.style.FunSpec
|
|
import io.kotest.engine.spec.tempdir
|
|
import io.kotest.inspectors.shouldForAll
|
|
import io.kotest.matchers.shouldBe
|
|
import io.kotest.matchers.shouldNotBe
|
|
import io.kotest.matchers.string.shouldContain
|
|
import io.kotest.matchers.string.shouldNotContain
|
|
import prog8.ast.expressions.NumericLiteral
|
|
import prog8.ast.statements.Assignment
|
|
import prog8.ast.statements.FunctionCallStatement
|
|
import prog8.code.core.BaseDataType
|
|
import prog8.code.core.BuiltinFunctions
|
|
import prog8.code.core.RegisterOrPair
|
|
import prog8.code.core.isNumeric
|
|
import prog8.code.target.C64Target
|
|
import prog8.code.target.Cx16Target
|
|
import prog8.code.target.VMTarget
|
|
import prog8tests.helpers.ErrorReporterForTests
|
|
import prog8tests.helpers.compileText
|
|
|
|
class TestBuiltinFunctions: FunSpec({
|
|
|
|
val outputDir = tempdir().toPath()
|
|
|
|
test("pure func with fixed type") {
|
|
val func = BuiltinFunctions.getValue("sgn")
|
|
func.parameters.size shouldBe 1
|
|
func.parameters[0].name shouldBe "value"
|
|
func.parameters[0].possibleDatatypes. shouldForAll { it.isNumeric }
|
|
func.pure shouldBe true
|
|
func.returnTypes shouldBe arrayOf(BaseDataType.BYTE)
|
|
|
|
val conv = func.callConvention(listOf(BaseDataType.UBYTE))
|
|
conv.params.size shouldBe 1
|
|
conv.params[0].dt shouldBe BaseDataType.UBYTE
|
|
conv.params[0].reg shouldBe RegisterOrPair.A
|
|
conv.params[0].variable shouldBe false
|
|
conv.returns.dt shouldBe BaseDataType.BYTE
|
|
conv.returns.reg shouldBe RegisterOrPair.A
|
|
}
|
|
|
|
test("not-pure func with varying result value type") {
|
|
val func = BuiltinFunctions.getValue("cmp")
|
|
func.parameters.size shouldBe 2
|
|
func.pure shouldBe false
|
|
func.returnTypes.size shouldBe 0
|
|
|
|
val conv = func.callConvention(listOf(BaseDataType.UWORD, BaseDataType.UWORD))
|
|
conv.params.size shouldBe 2
|
|
conv.returns.dt shouldBe null
|
|
conv.returns.reg shouldBe null
|
|
}
|
|
|
|
test("func without return type") {
|
|
val func = BuiltinFunctions.getValue("poke")
|
|
func.parameters.size shouldBe 2
|
|
func.parameters[0].name shouldBe "address"
|
|
func.parameters[0].possibleDatatypes shouldBe arrayOf(BaseDataType.UWORD)
|
|
func.parameters[1].name shouldBe "value"
|
|
func.parameters[1].possibleDatatypes shouldBe arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
|
func.pure shouldBe false
|
|
func.returnTypes.size shouldBe 0
|
|
}
|
|
|
|
test("certain builtin functions should be compile time evaluated") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
uword[] array = [1,2,3]
|
|
str name = "hello"
|
|
cx16.r0L = len(array)
|
|
cx16.r1L = len(name)
|
|
cx16.r2L = sizeof(array)
|
|
cx16.r4 = mkword(200,100)
|
|
test(sizeof(array))
|
|
}
|
|
sub test(uword value) {
|
|
value++
|
|
}
|
|
}"""
|
|
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)
|
|
val statements = result!!.compilerAst.entrypoint.statements
|
|
statements.size shouldBe 8
|
|
val a1 = statements[2] as Assignment
|
|
val a2 = statements[3] as Assignment
|
|
val a3 = statements[4] as Assignment
|
|
val a4 = statements[5] as Assignment
|
|
val a5 = statements[6] as FunctionCallStatement
|
|
(a1.value as NumericLiteral).number shouldBe 3.0
|
|
(a2.value as NumericLiteral).number shouldBe 5.0
|
|
(a3.value as NumericLiteral).number shouldBe 6.0
|
|
(a4.value as NumericLiteral).number shouldBe 200*256+100
|
|
(a5.args[0] as NumericLiteral).number shouldBe 6.0
|
|
}
|
|
|
|
test("warning for return value discarding of pure functions") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
word @shared ww = 2222
|
|
|
|
abs(ww)
|
|
sgn(ww)
|
|
sqrt(ww)
|
|
min(ww, 0)
|
|
max(ww, 0)
|
|
clamp(ww, 0, 319)
|
|
}
|
|
}"""
|
|
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
compileText(Cx16Target(), true, src, outputDir, errors=errors, writeAssembly = false) shouldNotBe null
|
|
errors.warnings.size shouldBe 6
|
|
errors.warnings[0] shouldContain "statement has no effect"
|
|
errors.warnings[1] shouldContain "statement has no effect"
|
|
errors.warnings[2] shouldContain "statement has no effect"
|
|
errors.warnings[3] shouldContain "statement has no effect"
|
|
errors.warnings[4] shouldContain "statement has no effect"
|
|
errors.warnings[5] shouldContain "statement has no effect"
|
|
}
|
|
|
|
test("all peeks and pokes") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
uword[5] a
|
|
float f
|
|
|
|
sub start() {
|
|
pokebool(3000, true)
|
|
pokew(3100, 12345)
|
|
pokef(3200, 3.1415927)
|
|
poke(3300, 222)
|
|
pokebool(cx16.r0, true)
|
|
pokew(cx16.r0, 12345)
|
|
pokef(cx16.r0, 3.1415927)
|
|
poke(cx16.r0, 222)
|
|
pokebool(a[2], true)
|
|
pokew(a[2], 12345)
|
|
pokef(a[2], 3.1415927)
|
|
poke(a[2], 222)
|
|
pokebool(cx16.r0+cx16.r1, true)
|
|
pokew(cx16.r0+cx16.r1, 12345)
|
|
pokef(cx16.r0+cx16.r1, 3.1415927)
|
|
poke(cx16.r0+cx16.r1, 222)
|
|
|
|
|
|
cx16.r0bL = peekbool(3000)
|
|
cx16.r1 = peekw(3100)
|
|
f = peekf(3200)
|
|
cx16.r2L = peek(3300)
|
|
cx16.r0bL = peekbool(cx16.r0)
|
|
cx16.r1 = peekw(cx16.r0)
|
|
f = peekf(cx16.r0)
|
|
cx16.r2L = peek(cx16.r0)
|
|
cx16.r0bL = peekbool(a[2])
|
|
cx16.r1 = peekw(a[2])
|
|
f = peekf(a[2])
|
|
cx16.r2L = peek(a[2])
|
|
cx16.r0bL = peekbool(cx16.r0+cx16.r1)
|
|
cx16.r1 = peekw(cx16.r0+cx16.r1)
|
|
f = peekf(cx16.r0+cx16.r1)
|
|
cx16.r2L = peek(cx16.r0+cx16.r1)
|
|
}
|
|
}
|
|
"""
|
|
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
|
}
|
|
|
|
test("divmod and lmh should be compile time folded") {
|
|
val src="""
|
|
main {
|
|
ubyte @shared d, r, l, m, h
|
|
sub start() {
|
|
d, r = divmod(100, 13)
|
|
l, m, h = lmh(123456)
|
|
}
|
|
}"""
|
|
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = false)!!
|
|
val statements = result.compilerAst.entrypoint.statements
|
|
|
|
val assignments = statements.filterIsInstance<Assignment>()
|
|
val d = assignments.find { it.target.identifier?.nameInSource?.last() == "d" }!!
|
|
val r = assignments.find { it.target.identifier?.nameInSource?.last() == "r" }!!
|
|
val l = assignments.find { it.target.identifier?.nameInSource?.last() == "l" }!!
|
|
val m = assignments.find { it.target.identifier?.nameInSource?.last() == "m" }!!
|
|
val h = assignments.find { it.target.identifier?.nameInSource?.last() == "h" }!!
|
|
|
|
(d.value as NumericLiteral).number shouldBe 7.0
|
|
(r.value as NumericLiteral).number shouldBe 9.0
|
|
(l.value as NumericLiteral).number shouldBe 64.0
|
|
(m.value as NumericLiteral).number shouldBe 226.0
|
|
(h.value as NumericLiteral).number shouldBe 1.0
|
|
|
|
statements.forEach {
|
|
it.toString() shouldNotContain "divmod("
|
|
it.toString() shouldNotContain "lmh("
|
|
}
|
|
}
|
|
|
|
test("sgn") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
byte @shared b1 = -100
|
|
word @shared w1 = -1000
|
|
long @shared l1 = -1000000
|
|
|
|
cx16.r0sL = sgn(b1)
|
|
cx16.r1sL = sgn(w1)
|
|
cx16.r2sL = sgn(l1)
|
|
}
|
|
}"""
|
|
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
|
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
|
}
|
|
})
|
|
|