2024-01-07 17:48:18 +00:00
|
|
|
package prog8tests.ast
|
2021-12-15 00:24:25 +00:00
|
|
|
|
|
|
|
import io.kotest.core.spec.style.FunSpec
|
|
|
|
import io.kotest.matchers.shouldBe
|
2022-03-07 20:41:12 +00:00
|
|
|
import io.kotest.matchers.shouldNotBe
|
2021-12-15 00:24:25 +00:00
|
|
|
import io.kotest.matchers.string.shouldContain
|
2022-03-11 19:35:25 +00:00
|
|
|
import prog8.code.target.C64Target
|
2023-12-05 00:34:41 +00:00
|
|
|
import prog8.code.target.Cx16Target
|
|
|
|
import prog8.code.target.VMTarget
|
2021-12-15 00:24:25 +00:00
|
|
|
import prog8tests.helpers.ErrorReporterForTests
|
|
|
|
import prog8tests.helpers.compileText
|
|
|
|
|
|
|
|
|
|
|
|
class TestAstChecks: FunSpec({
|
|
|
|
|
2023-07-15 13:40:51 +00:00
|
|
|
test("conditional expression w/float works") {
|
2021-12-15 00:24:25 +00:00
|
|
|
val text = """
|
|
|
|
%import floats
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
uword xx
|
|
|
|
if xx+99.99 == xx+1.234 {
|
|
|
|
xx++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors) shouldNotBe null
|
2021-12-15 00:24:25 +00:00
|
|
|
errors.errors.size shouldBe 0
|
|
|
|
errors.warnings.size shouldBe 2
|
|
|
|
errors.warnings[0] shouldContain "converted to float"
|
|
|
|
errors.warnings[1] shouldContain "converted to float"
|
|
|
|
}
|
2022-01-23 01:23:30 +00:00
|
|
|
|
|
|
|
test("can't assign label or subroutine without using address-of") {
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
|
|
|
|
label:
|
|
|
|
uword @shared addr
|
|
|
|
addr = label
|
|
|
|
addr = thing
|
|
|
|
addr = &label
|
|
|
|
addr = &thing
|
|
|
|
}
|
|
|
|
|
|
|
|
sub thing() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests()
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors) shouldBe null
|
2022-01-23 01:23:30 +00:00
|
|
|
errors.errors.size shouldBe 2
|
|
|
|
errors.warnings.size shouldBe 0
|
2022-11-26 13:39:03 +00:00
|
|
|
errors.errors[0] shouldContain ":7:28: invalid assignment value, maybe forgot '&'"
|
|
|
|
errors.errors[1] shouldContain ":8:28: invalid assignment value, maybe forgot '&'"
|
2022-01-23 01:23:30 +00:00
|
|
|
}
|
2022-01-24 22:30:15 +00:00
|
|
|
|
|
|
|
test("can't do str or array expression without using address-of") {
|
|
|
|
val text = """
|
|
|
|
%import textio
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte[] array = [1,2,3,4]
|
|
|
|
str s1 = "test"
|
|
|
|
ubyte ff = 1
|
|
|
|
txt.print(s1+ff)
|
|
|
|
txt.print(array+ff)
|
|
|
|
txt.print_uwhex(s1+ff, true)
|
|
|
|
txt.print_uwhex(array+ff, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests()
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
2022-01-24 22:30:15 +00:00
|
|
|
errors.errors.filter { it.contains("missing &") }.size shouldBe 4
|
|
|
|
}
|
|
|
|
|
|
|
|
test("str or array expression with address-of") {
|
|
|
|
val text = """
|
|
|
|
%import textio
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte[] array = [1,2,3,4]
|
|
|
|
str s1 = "test"
|
2024-02-04 22:41:01 +00:00
|
|
|
bool bb1, bb2
|
2022-01-24 22:30:15 +00:00
|
|
|
ubyte ff = 1
|
|
|
|
txt.print(&s1+ff)
|
|
|
|
txt.print(&array+ff)
|
|
|
|
txt.print_uwhex(&s1+ff, true)
|
|
|
|
txt.print_uwhex(&array+ff, true)
|
|
|
|
; also good:
|
2024-02-04 22:41:01 +00:00
|
|
|
bb1 = (s1 == "derp")
|
|
|
|
bb2 = (s1 != "derp")
|
2022-01-24 22:30:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), false, text, writeAssembly = false) shouldNotBe null
|
2022-01-24 22:30:15 +00:00
|
|
|
}
|
2023-03-10 22:46:13 +00:00
|
|
|
|
|
|
|
test("const is not allowed on arrays") {
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
const ubyte[5] a = 5
|
|
|
|
a[2]=42
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
|
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors)
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.warnings.size shouldBe 0
|
2023-11-16 23:37:12 +00:00
|
|
|
errors.errors[0] shouldContain "const can only be used"
|
2023-03-10 22:46:13 +00:00
|
|
|
}
|
2023-03-10 23:26:19 +00:00
|
|
|
|
|
|
|
test("array indexing is not allowed on a memory mapped variable") {
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
&ubyte a = 10000
|
|
|
|
uword z = 500
|
|
|
|
a[4] = (z % 3) as ubyte
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
|
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors)
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.warnings.size shouldBe 0
|
|
|
|
errors.errors[0] shouldContain "indexing requires"
|
|
|
|
}
|
2023-03-10 23:43:30 +00:00
|
|
|
|
|
|
|
test("array decl with expression as size can be initialized with a single value") {
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
const ubyte n = 40
|
|
|
|
const ubyte half = n / 2
|
|
|
|
ubyte[half] @shared a = 5
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
|
|
compileText(C64Target(), true, text, writeAssembly = true, errors=errors) shouldNotBe null
|
|
|
|
errors.errors.size shouldBe 0
|
|
|
|
errors.warnings.size shouldBe 0
|
|
|
|
}
|
2023-12-05 00:34:41 +00:00
|
|
|
|
|
|
|
test("unicode in identifier names is working") {
|
|
|
|
val text = """
|
|
|
|
%import floats
|
|
|
|
|
|
|
|
main {
|
|
|
|
ubyte приблизительно = 99
|
2023-12-05 16:38:23 +00:00
|
|
|
ubyte นี่คือตัวอักษรภาษาไท = 42
|
2023-12-05 00:34:41 +00:00
|
|
|
|
|
|
|
sub start() {
|
2023-12-05 16:38:23 +00:00
|
|
|
str knäckebröd = "crunchy" ; with composed form
|
|
|
|
prt(knäckebröd) ; with decomposed form
|
2023-12-05 00:34:41 +00:00
|
|
|
printf(2*floats.π)
|
|
|
|
}
|
|
|
|
|
|
|
|
sub prt(str message) {
|
|
|
|
приблизительно++
|
|
|
|
}
|
|
|
|
|
|
|
|
sub printf(float fl) {
|
2023-12-05 16:38:23 +00:00
|
|
|
นี่คือตัวอักษรภาษาไท++
|
2023-12-05 00:34:41 +00:00
|
|
|
}
|
|
|
|
}"""
|
|
|
|
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
|
|
|
compileText(Cx16Target(), false, text, writeAssembly = true) shouldNotBe null
|
|
|
|
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
|
|
|
}
|
2024-01-31 20:36:39 +00:00
|
|
|
|
|
|
|
test("return with a statement instead of a value is a syntax error") {
|
|
|
|
val src="""
|
|
|
|
main {
|
|
|
|
|
|
|
|
sub invalid() {
|
|
|
|
return cx16.r0++
|
|
|
|
}
|
|
|
|
|
|
|
|
sub start() {
|
|
|
|
invalid()
|
|
|
|
}
|
|
|
|
}"""
|
|
|
|
val errors=ErrorReporterForTests()
|
|
|
|
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.errors[0] shouldContain "statement"
|
|
|
|
}
|
2024-09-20 17:55:32 +00:00
|
|
|
|
|
|
|
test("redefined variable name in single declaration is reported") {
|
|
|
|
val src="""
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
const ubyte count=11
|
|
|
|
cx16.r0++
|
|
|
|
ubyte count = 88 ; redefinition
|
|
|
|
cx16.r0 = count
|
|
|
|
}
|
|
|
|
}"""
|
|
|
|
val errors=ErrorReporterForTests()
|
|
|
|
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.errors[0] shouldContain "name conflict"
|
|
|
|
|
|
|
|
errors.clear()
|
|
|
|
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.errors[0] shouldContain "name conflict"
|
|
|
|
}
|
|
|
|
|
|
|
|
test("redefined variable name in multi declaration is reported") {
|
|
|
|
val src="""
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte i
|
|
|
|
i++
|
|
|
|
ubyte i, j ; redefinition
|
|
|
|
i++
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val errors=ErrorReporterForTests()
|
|
|
|
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.errors[0] shouldContain "name conflict"
|
|
|
|
|
|
|
|
errors.clear()
|
|
|
|
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
|
|
|
errors.errors.size shouldBe 1
|
|
|
|
errors.errors[0] shouldContain "name conflict"
|
|
|
|
}
|
2021-12-15 00:24:25 +00:00
|
|
|
})
|