mirror of
https://github.com/irmen/prog8.git
synced 2025-09-18 08:26:11 +00:00
2555 lines
76 KiB
Kotlin
2555 lines
76 KiB
Kotlin
package prog8tests.compiler
|
|
|
|
import io.kotest.assertions.withClue
|
|
import io.kotest.core.spec.style.FunSpec
|
|
import io.kotest.engine.spec.tempdir
|
|
import io.kotest.matchers.comparables.shouldBeGreaterThanOrEqualTo
|
|
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.expressions.*
|
|
import prog8.ast.statements.*
|
|
import prog8.code.ast.*
|
|
import prog8.code.core.BaseDataType
|
|
import prog8.code.core.DataType
|
|
import prog8.code.core.IMemSizer
|
|
import prog8.code.core.ISubType
|
|
import prog8.code.target.C64Target
|
|
import prog8.code.target.Cx16Target
|
|
import prog8.code.target.VMTarget
|
|
import prog8.vm.VmRunner
|
|
import prog8tests.helpers.ErrorReporterForTests
|
|
import prog8tests.helpers.compileText
|
|
import kotlin.io.path.readText
|
|
|
|
|
|
class TestPointers: FunSpec( {
|
|
|
|
val outputDir = tempdir().toPath()
|
|
|
|
test("basic pointers") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
^^bool g_bp
|
|
^^word g_bw
|
|
^^float g_floats
|
|
|
|
sub start() {
|
|
^^bool l_bp
|
|
^^word l_bw
|
|
^^float l_floats
|
|
|
|
assign_pointers()
|
|
assign_values()
|
|
assign_inplace()
|
|
assign_deref()
|
|
assign_uwords()
|
|
assign_same_ptrs()
|
|
assign_different_ptrs()
|
|
|
|
sub assign_pointers() {
|
|
cx16.r0 = l_bp
|
|
cx16.r1 = l_bw
|
|
cx16.r2 = l_floats
|
|
cx16.r0 = g_bp
|
|
cx16.r1 = g_bw
|
|
cx16.r2 = g_floats
|
|
cx16.r0 = other.g_bp
|
|
cx16.r1 = other.g_bw
|
|
cx16.r2 = other.g_floats
|
|
cx16.r0 = other.func.l_bp
|
|
cx16.r1 = other.func.l_bw
|
|
cx16.r2 = other.func.l_floats
|
|
}
|
|
|
|
sub assign_deref() {
|
|
float f
|
|
bool b
|
|
word w
|
|
b = l_bp^^
|
|
w = l_bw^^
|
|
f = l_floats^^
|
|
b = g_bp^^
|
|
w = g_bw^^
|
|
f = g_floats^^
|
|
b = other.g_bp^^
|
|
w = other.g_bw^^
|
|
f = other.g_floats^^
|
|
b = other.func.l_bp^^
|
|
w = other.func.l_bw^^
|
|
f = other.func.l_floats^^
|
|
}
|
|
|
|
sub assign_values() {
|
|
l_bp^^ = true
|
|
l_bw^^ = -1234
|
|
l_floats^^ = 5.678
|
|
g_bp^^ = true
|
|
g_bw^^ = -1234
|
|
g_floats^^ = 5.678
|
|
other.g_bp^^ = true
|
|
other.g_bw^^ = -1234
|
|
other.g_floats^^ = 5.678
|
|
other.func.l_bp^^ = true
|
|
other.func.l_bw^^ = -1234
|
|
other.func.l_floats^^ = 5.678
|
|
}
|
|
|
|
sub assign_same_ptrs() {
|
|
l_bp = g_bp
|
|
l_bw = g_bw
|
|
l_floats = g_floats
|
|
g_bp = other.g_bp
|
|
g_bw = other.g_bw
|
|
g_floats = other.g_floats
|
|
other.g_bp = other.func.l_bp
|
|
other.g_bw = other.func.l_bw
|
|
other.g_floats = other.func.l_floats
|
|
}
|
|
|
|
sub assign_different_ptrs() {
|
|
l_bp = g_floats as ^^bool
|
|
l_bw = g_floats as ^^word
|
|
l_floats = g_bp as ^^float
|
|
other.g_bp = l_floats as ^^bool
|
|
other.g_bw = l_floats as ^^word
|
|
other.g_floats = l_bp as ^^float
|
|
}
|
|
|
|
sub assign_inplace() {
|
|
bool b
|
|
l_bp^^ = l_bp^^ xor b
|
|
l_bw^^ += -1234
|
|
l_floats^^ += 5.678
|
|
g_bp^^ = g_bp^^ xor b
|
|
g_bw^^ += -1234
|
|
g_floats^^ += 5.678
|
|
other.g_bp^^ = other.g_bp^^ xor b
|
|
other.g_bw^^ += -1234
|
|
other.g_floats^^ += 5.678
|
|
other.func.l_bp^^ = other.func.l_bp^^ xor b
|
|
other.func.l_bw^^ += -1234
|
|
other.func.l_floats^^ += 5.678
|
|
|
|
l_bw^^ /= 3
|
|
l_floats^^ /= 3.0
|
|
g_bw^^ /= 3
|
|
g_floats^^ /= 3.0
|
|
other.g_bw^^ /= 3
|
|
other.g_floats^^ /= 3.0
|
|
other.func.l_bw^^ /= 3
|
|
other.func.l_floats^^ /= 3.0
|
|
}
|
|
|
|
sub assign_uwords() {
|
|
l_bp = $9000
|
|
l_bw = $9000
|
|
l_floats = $9000
|
|
g_bp = $9000
|
|
g_bw = $9000
|
|
g_floats = $9000
|
|
other.g_bp = $9000
|
|
other.g_bw = $9000
|
|
other.g_floats = $9000
|
|
other.func.l_bp = $9000
|
|
other.func.l_bw = $9000
|
|
other.func.l_floats = $9000
|
|
}
|
|
}
|
|
}
|
|
|
|
other {
|
|
^^bool g_bp
|
|
^^word g_bw
|
|
^^float g_floats
|
|
|
|
sub func() {
|
|
^^bool l_bp
|
|
^^word l_bw
|
|
^^float l_floats
|
|
}
|
|
}
|
|
"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("struct pointers") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
struct Node {
|
|
bool bb
|
|
float f
|
|
word w
|
|
^^Node next
|
|
}
|
|
|
|
sub start() {
|
|
^^Node n1 = ^^Node : [false, 1.1, 1111, 0]
|
|
^^Node n2 = ^^Node : [false, 2.2, 2222, 0]
|
|
^^Node n3 = ^^Node : [true, 3.3, 3333, 0]
|
|
|
|
n1.next = n2
|
|
n2.next = n3
|
|
n3.next = 0
|
|
|
|
bool bb = n1.bb
|
|
float f = n1.f
|
|
uword next = n1.next
|
|
word w = n1.w
|
|
|
|
bb = n2.bb
|
|
f = n2.f
|
|
next = n2.next
|
|
w = n2.w
|
|
|
|
n1.next.next.bb = false
|
|
n1.next.next.f = 42.999
|
|
n1.next.next.w = 5555
|
|
n1.next.next.w++
|
|
|
|
bb = n1.next.next.bb
|
|
f = n1.next.next.f
|
|
}
|
|
}
|
|
"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("pointer walking using simple dot notation should be equivalent to explicit dereference chain") {
|
|
val src="""
|
|
main {
|
|
struct State {
|
|
uword c
|
|
^^uword ptr
|
|
^^State next
|
|
}
|
|
|
|
sub start() {
|
|
^^State matchstate
|
|
cx16.r0 = matchstate^^.ptr
|
|
cx16.r1 = matchstate^^.next^^.next^^.ptr
|
|
cx16.r2 = matchstate.ptr
|
|
cx16.r3 = matchstate.next.next.ptr
|
|
cx16.r4 = matchstate.ptr^^
|
|
cx16.r5 = matchstate.next.next.ptr^^
|
|
|
|
matchstate^^.ptr = 2222
|
|
matchstate^^.next^^.next^^.ptr = 2222
|
|
matchstate.ptr = 2222
|
|
matchstate.next.next.ptr = 2222
|
|
}
|
|
}"""
|
|
|
|
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 13
|
|
val a0v = (st[2] as Assignment).value as PtrDereference
|
|
a0v.chain shouldBe listOf("matchstate", "ptr")
|
|
a0v.derefLast shouldBe false
|
|
|
|
val a1v = (st[3] as Assignment).value as PtrDereference
|
|
a1v.chain shouldBe listOf("matchstate", "next", "next", "ptr")
|
|
a1v.derefLast shouldBe false
|
|
|
|
val a2v = (st[4] as Assignment).value as PtrDereference
|
|
a2v.chain shouldBe listOf("matchstate", "ptr")
|
|
a2v.derefLast shouldBe false
|
|
|
|
val a3v = (st[5] as Assignment).value as PtrDereference
|
|
a3v.chain shouldBe listOf("matchstate", "next", "next", "ptr")
|
|
a3v.derefLast shouldBe false
|
|
|
|
val a4v = (st[6] as Assignment).value as PtrDereference
|
|
a4v.chain shouldBe listOf("matchstate", "ptr")
|
|
a4v.derefLast shouldBe true
|
|
|
|
val a5v = (st[7] as Assignment).value as PtrDereference
|
|
a5v.chain shouldBe listOf("matchstate", "next", "next", "ptr")
|
|
a5v.derefLast shouldBe true
|
|
|
|
val t0 = (st[8] as Assignment).target.pointerDereference!!
|
|
t0.derefLast shouldBe false
|
|
t0.chain shouldBe listOf("matchstate", "ptr")
|
|
|
|
val t1 = (st[9] as Assignment).target.pointerDereference!!
|
|
t1.derefLast shouldBe false
|
|
t1.chain shouldBe listOf("matchstate", "next", "next", "ptr")
|
|
|
|
val t2 = (st[10] as Assignment).target.pointerDereference!!
|
|
t2.derefLast shouldBe false
|
|
t2.chain shouldBe listOf("matchstate", "ptr")
|
|
|
|
val t3 = (st[11] as Assignment).target.pointerDereference!!
|
|
t3.derefLast shouldBe false
|
|
t3.chain shouldBe listOf("matchstate", "next", "next", "ptr")
|
|
}
|
|
|
|
test("word size pointer indexing on pointers") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
|
|
struct List {
|
|
^^uword s
|
|
ubyte n
|
|
^^List next
|
|
}
|
|
|
|
sub start() {
|
|
ubyte[10] array
|
|
uword @shared wordptr
|
|
^^bool @shared boolptr
|
|
^^float @shared floatptr
|
|
^^byte @shared byteptr
|
|
^^ubyte @shared ubyteptr
|
|
^^List @shared listptr
|
|
^^List @shared listptr2
|
|
|
|
bool @shared zz
|
|
float @shared fl
|
|
byte @shared bb
|
|
|
|
zz = boolptr[999]
|
|
fl = floatptr[999]
|
|
bb = byteptr[999]
|
|
cx16.r0L = ubyteptr[999]
|
|
cx16.r1L = wordptr[999]
|
|
cx16.r2L = array[9]
|
|
}
|
|
}"""
|
|
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 27
|
|
|
|
val a_zz = (st[20] as Assignment).value
|
|
a_zz shouldBe instanceOf<FunctionCallExpression>()
|
|
val a_fl = (st[21] as Assignment).value
|
|
a_fl shouldBe instanceOf<FunctionCallExpression>()
|
|
val a_bb = (st[22] as Assignment).value
|
|
a_bb shouldBe instanceOf<DirectMemoryRead>()
|
|
val a_r0 = (st[23] as Assignment).value
|
|
a_r0 shouldBe instanceOf<DirectMemoryRead>()
|
|
val a_r1 = (st[24] as Assignment).value
|
|
a_r1 shouldBe instanceOf<DirectMemoryRead>()
|
|
val a_r2 = (st[25] as Assignment).value
|
|
a_r2 shouldBe instanceOf<ArrayIndexedExpression>()
|
|
val a_lptr2 = (st[25] as Assignment).value
|
|
a_lptr2 shouldBe instanceOf<ArrayIndexedExpression>()
|
|
}
|
|
|
|
test("block scoping still parsed correctly") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
readbyte(&thing.name)
|
|
readbyte(&thing.name[1])
|
|
readbyte(&thing.array[1])
|
|
}
|
|
|
|
sub readbyte(uword @requirezp ptr) {
|
|
ptr=0
|
|
}
|
|
}
|
|
|
|
thing {
|
|
str name = "error"
|
|
ubyte[10] array
|
|
}"""
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
val result = compileText(VMTarget(), false, src, outputDir)!!
|
|
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
|
VmRunner().runProgram(virtfile.readText(), true)
|
|
}
|
|
|
|
test("passing struct instances to subroutines and returning a struct instance is not allowed") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
}
|
|
|
|
struct Node {
|
|
bool flag
|
|
}
|
|
|
|
sub faulty(Node arg) -> Node {
|
|
return cx16.r0
|
|
}
|
|
}
|
|
"""
|
|
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors)
|
|
val err = errors.errors
|
|
err.size shouldBe 3
|
|
err[0] shouldContain "uword doesn't match"
|
|
err[1] shouldContain "structs can only be passed via a pointer"
|
|
err[2] shouldContain "structs can only be returned via a pointer"
|
|
}
|
|
|
|
test("pointers in subroutine return values") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
^^thing.Node @shared ptr = thing.new()
|
|
}
|
|
}
|
|
|
|
thing {
|
|
struct Node {
|
|
bool flag
|
|
^^Node next
|
|
}
|
|
|
|
sub new() -> ^^Node {
|
|
cx16.r0++
|
|
^^Node pointer = 2000
|
|
return pointer
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("pointers in subroutine parameters") {
|
|
val src="""
|
|
main {
|
|
struct MNode {
|
|
bool flag
|
|
^^MNode next
|
|
}
|
|
|
|
sub func(^^MNode pointer) -> ^^MNode {
|
|
cx16.r0++
|
|
return pointer.next
|
|
}
|
|
|
|
sub start() {
|
|
^^MNode mn1 = ^^MNode : []
|
|
mn1 = func(mn1)
|
|
|
|
^^thing.Node n1 = ^^thing.Node : []
|
|
n1 = thing.func(n1)
|
|
}
|
|
}
|
|
|
|
thing {
|
|
struct Node {
|
|
bool flag
|
|
^^Node next
|
|
}
|
|
|
|
sub func(^^Node pointer) -> ^^Node {
|
|
cx16.r0++
|
|
return pointer.next
|
|
}
|
|
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("string comparisons still calling string compare routine") {
|
|
val src="""
|
|
main {
|
|
str s1 = "hello"
|
|
^^ubyte @shared ubyteptr
|
|
|
|
sub start() {
|
|
cx16.r0bL = s1=="wob"
|
|
cx16.r1bL = "wob"=="s1"
|
|
cx16.r2bL = "wob"==cx16.r0
|
|
cx16.r3bL = cx16.r0=="wob"
|
|
cx16.r4bL = "wob"==ubyteptr
|
|
cx16.r5bL = ubyteptr=="wob"
|
|
void compare1("wob")
|
|
void compare2("wob")
|
|
}
|
|
|
|
sub compare1(str s2) -> bool {
|
|
return s1==s2
|
|
}
|
|
|
|
sub compare2(str s2) -> bool {
|
|
return s2==s1
|
|
}
|
|
}"""
|
|
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
|
val main = result.compilerAst.allBlocks.first {it.name=="main"}
|
|
val st = main.statements.filterIsInstance<Subroutine>().first {it.name=="start"}.statements
|
|
|
|
fun assertIsStringCompare(expr: BinaryExpression) {
|
|
expr.operator shouldBe "=="
|
|
(expr.right as NumericLiteral).number shouldBe 0.0
|
|
(expr.left as FunctionCallExpression).target.nameInSource shouldBe listOf("prog8_lib_stringcompare")
|
|
}
|
|
|
|
st.size shouldBe 9
|
|
val av1 = (st[0] as Assignment).value as BinaryExpression
|
|
val av2 = (st[1] as Assignment).value as BinaryExpression
|
|
val av3 = (st[2] as Assignment).value as BinaryExpression
|
|
val av4 = (st[3] as Assignment).value as BinaryExpression
|
|
val av5 = (st[4] as Assignment).value as BinaryExpression
|
|
val av6 = (st[5] as Assignment).value as BinaryExpression
|
|
assertIsStringCompare(av1)
|
|
assertIsStringCompare(av2)
|
|
assertIsStringCompare(av3)
|
|
assertIsStringCompare(av4)
|
|
assertIsStringCompare(av5)
|
|
assertIsStringCompare(av6)
|
|
|
|
val compare1 = main.statements.filterIsInstance<Subroutine>().first {it.name=="compare1"}
|
|
val r1v = (compare1.statements.last() as Return).values.single() as BinaryExpression
|
|
assertIsStringCompare(r1v)
|
|
|
|
val compare2 = main.statements.filterIsInstance<Subroutine>().first {it.name=="compare2"}
|
|
val r2v = (compare2.statements.last() as Return).values.single() as BinaryExpression
|
|
assertIsStringCompare(r2v)
|
|
}
|
|
|
|
test("str or ubyte array params or return type replaced by pointer to ubyte") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
test1("zzz")
|
|
test2("zzz")
|
|
}
|
|
|
|
sub test1(str arg) -> str {
|
|
cx16.r0++
|
|
return cx16.r0
|
|
}
|
|
|
|
sub test2(ubyte[] arg) {
|
|
cx16.r0++
|
|
}
|
|
}"""
|
|
|
|
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
|
val main = result.compilerAst.allBlocks.first {it.name=="main"}
|
|
val test1 = main.statements[1] as Subroutine
|
|
val test2 = main.statements[2] as Subroutine
|
|
test1.name shouldBe "test1"
|
|
test1.parameters.single().type shouldBe DataType.pointer(DataType.UBYTE)
|
|
test1.returntypes.single() shouldBe DataType.pointer(DataType.UBYTE)
|
|
test2.name shouldBe "test2"
|
|
test2.parameters.single().type shouldBe DataType.pointer(DataType.UBYTE)
|
|
}
|
|
|
|
test("creating instances") {
|
|
val src="""
|
|
main {
|
|
struct MyNode {
|
|
bool flag
|
|
^^MyNode next
|
|
}
|
|
|
|
sub start() {
|
|
^^MyNode @shared m1 = ^^MyNode : []
|
|
^^MyNode @shared m2 = ^^MyNode : [true, 0]
|
|
|
|
^^thing.Node @shared n1 = ^^thing.Node : []
|
|
^^thing.Node @shared n2 = ^^thing.Node : [true, 0]
|
|
}
|
|
}
|
|
|
|
thing {
|
|
struct Node {
|
|
bool flag
|
|
^^Node next
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("creating instances for unused vars should all be removed") {
|
|
val src="""
|
|
main {
|
|
struct MyNode {
|
|
bool flag
|
|
^^MyNode next
|
|
}
|
|
|
|
sub start() {
|
|
^^MyNode m1 = ^^MyNode : []
|
|
^^MyNode m2 = ^^MyNode : [true, 0]
|
|
|
|
^^thing.Node n1 = ^^thing.Node : []
|
|
^^thing.Node n2 = ^^thing.Node : [true, 0]
|
|
}
|
|
}
|
|
|
|
thing {
|
|
struct Node {
|
|
bool flag
|
|
^^Node next
|
|
}
|
|
}"""
|
|
|
|
var result = compileText(VMTarget(), true, src, outputDir)!!
|
|
withClue("all variables should have been optimized away") {
|
|
val start = result.codegenAst!!.entrypoint()!!
|
|
start.children.size shouldBe 2
|
|
start.children[0] shouldBe instanceOf<PtSubSignature>()
|
|
start.children[1] shouldBe instanceOf<PtReturn>()
|
|
}
|
|
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("creating instances should have correct number of args") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
bool flag
|
|
ubyte value
|
|
^^Node next
|
|
}
|
|
|
|
sub start() {
|
|
^^Node ptr = ^^Node : [true] ; error
|
|
}
|
|
}"""
|
|
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors)
|
|
val err = errors.errors
|
|
err.size shouldBe 1
|
|
err[0] shouldContain("expected 3 or 0 but got 1, missing: value, next")
|
|
}
|
|
|
|
test("pointer uword compatibility") {
|
|
val src="""
|
|
main {
|
|
struct MyNode {
|
|
bool flag
|
|
^^MyNode next
|
|
}
|
|
|
|
sub start() {
|
|
cx16.r0 = ^^MyNode : []
|
|
|
|
^^MyNode @shared ptr1 = cx16.r0
|
|
|
|
ptr1 = 2000
|
|
ptr1 = 20
|
|
ptr1 = 20.2222
|
|
}
|
|
}"""
|
|
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors)
|
|
val err = errors.errors
|
|
err.size shouldBe 1
|
|
err[0] shouldContain("15:16: incompatible value type, can only assign uword or correct pointer")
|
|
}
|
|
|
|
test("unknown field") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
struct Node {
|
|
ubyte weight
|
|
}
|
|
^^Node nodes
|
|
nodes^^.zzz1 = 99
|
|
cx16.r0L = nodes^^.zzz2
|
|
cx16.r0L = nodes[2].zzz3
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
|
|
val err = errors.errors
|
|
err.size shouldBe 5
|
|
err[0] shouldContain("no such field 'zzz1'")
|
|
err[1] shouldContain("invalid assignment")
|
|
err[2] shouldContain("no such field 'zzz2'")
|
|
err[3] shouldContain("invalid assignment")
|
|
err[4] shouldContain("no such field 'zzz3'")
|
|
}
|
|
|
|
|
|
class Struct(override val scopedNameString: String) : ISubType {
|
|
override fun memsize(sizer: IMemSizer): Int {
|
|
TODO("Not yet implemented")
|
|
}
|
|
|
|
override fun sameas(other: ISubType): Boolean {
|
|
return other is Struct && other.scopedNameString == scopedNameString
|
|
}
|
|
|
|
override fun getFieldType(name: String): DataType? = null
|
|
}
|
|
|
|
test("internal type for address-of") {
|
|
DataType.BYTE.typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.BYTE)
|
|
DataType.WORD.typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.WORD)
|
|
DataType.FLOAT.typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.FLOAT)
|
|
DataType.UNDEFINED.typeForAddressOf(false) shouldBe DataType.UWORD
|
|
DataType.UNDEFINED.typeForAddressOf(true) shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
DataType.STR.typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
DataType.arrayFor(BaseDataType.FLOAT, false).typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.FLOAT)
|
|
DataType.arrayFor(BaseDataType.FLOAT, false).typeForAddressOf(true) shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
DataType.arrayFor(BaseDataType.UWORD, false).typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.UWORD)
|
|
DataType.arrayFor(BaseDataType.UWORD, true).typeForAddressOf(false) shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
DataType.arrayFor(BaseDataType.UWORD, false).typeForAddressOf(true) shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
DataType.arrayFor(BaseDataType.UWORD, true).typeForAddressOf(true) shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
|
|
DataType.pointer(Struct("struct")).typeForAddressOf(false) shouldBe DataType.UWORD
|
|
DataType.pointerFromAntlr(listOf("struct")).typeForAddressOf(false) shouldBe DataType.UWORD
|
|
|
|
DataType.pointer(BaseDataType.BOOL).typeForAddressOf(false) shouldBe DataType.UWORD
|
|
}
|
|
|
|
test("untyped and typed address-of operators") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
float f
|
|
cx16.r0 = &f+1
|
|
cx16.r1 = &&f+1
|
|
}
|
|
}"""
|
|
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
val result = compileText(VMTarget(), false, src, outputDir)!!
|
|
val st = result.codegenAst!!.entrypoint()!!.children
|
|
st.size shouldBe 6
|
|
val r0v = (st[3] as PtAssignment).value as PtBinaryExpression
|
|
val r1v = (st[4] as PtAssignment).value as PtBinaryExpression
|
|
r0v.left shouldBe instanceOf<PtAddressOf>()
|
|
r0v.right shouldBe instanceOf<PtNumber>()
|
|
(r0v.right as PtNumber).number shouldBe 1.0
|
|
r1v.left shouldBe instanceOf<PtAddressOf>()
|
|
r1v.right shouldBe instanceOf<PtNumber>()
|
|
(r1v.right as PtNumber).number shouldBe VMTarget.FLOAT_MEM_SIZE
|
|
}
|
|
|
|
test("untyped and typed address-of subroutines") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
cx16.r2 = &start
|
|
cx16.r3 = &&start
|
|
}
|
|
}"""
|
|
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
|
|
errors.errors.size shouldBe 1
|
|
errors.errors[0] shouldContain("5:19: no support for typed pointers to subroutines")
|
|
}
|
|
|
|
test("address-of struct fields") {
|
|
val src="""
|
|
%import floats
|
|
%import textio
|
|
|
|
main {
|
|
struct List {
|
|
uword s
|
|
ubyte n
|
|
float f
|
|
bool b
|
|
^^List next
|
|
}
|
|
sub start() {
|
|
^^List @shared l0 = 3000
|
|
^^List @shared l1 = 2000
|
|
l1.next = l0
|
|
|
|
cx16.r0 = &l1.s
|
|
cx16.r1 = &l1.n
|
|
cx16.r2 = &l1.f
|
|
cx16.r3 = &l1.b
|
|
cx16.r4 = &l1.next
|
|
cx16.r5 = &l1.next.s
|
|
cx16.r6 = &l1.next.n
|
|
cx16.r7 = &l1.next.f
|
|
cx16.r8 = &l1.next.b
|
|
cx16.r9 = &l1.next.next
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("address-of pointer arithmetic on alias") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
ubyte @shared index = 3
|
|
ubyte[10] array
|
|
alias curframe = array
|
|
|
|
cx16.r0 = &curframe
|
|
cx16.r1 = &curframe[3]
|
|
cx16.r2 = &curframe + 3
|
|
cx16.r3 = &curframe[index]
|
|
cx16.r4 = &curframe + index
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
val result = compileText(VMTarget(), false, src, outputDir)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 9
|
|
(st[3] as Assignment).value shouldBe instanceOf<AddressOf>()
|
|
val a1v = (st[4] as Assignment).value as AddressOf
|
|
a1v.identifier?.nameInSource shouldBe listOf("array")
|
|
a1v.arrayIndex?.indexExpr?.constValue(result.compilerAst)?.number shouldBe 3.0
|
|
|
|
val a2v = (st[5] as Assignment).value as BinaryExpression
|
|
a2v.left shouldBe instanceOf<AddressOf>()
|
|
a2v.operator shouldBe "+"
|
|
(a2v.right as NumericLiteral).number shouldBe 3.0
|
|
|
|
val a3v = (st[6] as Assignment).value as AddressOf
|
|
a3v.identifier?.nameInSource shouldBe listOf("array")
|
|
(a3v.arrayIndex?.indexExpr as IdentifierReference).nameInSource shouldBe listOf("index")
|
|
|
|
val a4v = (st[7] as Assignment).value as BinaryExpression
|
|
a4v.left shouldBe instanceOf<AddressOf>()
|
|
a4v.operator shouldBe "+"
|
|
(a4v.right as TypecastExpression).expression shouldBe instanceOf<IdentifierReference>()
|
|
}
|
|
|
|
test("pointer arithmetic") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
^^uword @shared ptr
|
|
|
|
add1()
|
|
add2()
|
|
sub1()
|
|
sub2()
|
|
|
|
sub add1() {
|
|
ptr += 5
|
|
cx16.r0 = ptr + 5
|
|
cx16.r0 = peekw(ptr + 5)
|
|
}
|
|
|
|
sub add2() {
|
|
ptr += cx16.r0L
|
|
cx16.r0 = ptr + cx16.r0L
|
|
cx16.r0 = peekw(ptr + cx16.r0L)
|
|
}
|
|
|
|
sub sub1() {
|
|
ptr -= 5
|
|
cx16.r0 = ptr - 5
|
|
cx16.r0 = peekw(ptr - 5)
|
|
}
|
|
|
|
sub sub2() {
|
|
ptr -= cx16.r0L
|
|
cx16.r0 = ptr - cx16.r0L
|
|
cx16.r0 = peekw(ptr - cx16.r0L)
|
|
}
|
|
}
|
|
}"""
|
|
|
|
val result = compileText(VMTarget(), true, src, outputDir)!!
|
|
val st = result.codegenAst!!.allBlocks().first { it.name=="main" }.children
|
|
st.size shouldBe 5
|
|
val add1 = (st[1] as PtSub).children
|
|
val add2 = (st[2] as PtSub).children
|
|
val sub1 = (st[3] as PtSub).children
|
|
val sub2 = (st[4] as PtSub).children
|
|
add1.size shouldBe 5
|
|
add2.size shouldBe 5
|
|
sub1.size shouldBe 5
|
|
sub2.size shouldBe 5
|
|
|
|
((add1[1] as PtAugmentedAssign).value as PtNumber).number shouldBe 10.0
|
|
(((add1[2] as PtAssignment).value as PtBinaryExpression).right as PtNumber).number shouldBe 10.0
|
|
val add1peek = (add1[3] as PtAssignment).value as PtBuiltinFunctionCall
|
|
((add1peek.args[0] as PtBinaryExpression).right as PtNumber).number shouldBe 10.0
|
|
|
|
val add2expr1 = (add2[1] as PtAugmentedAssign).value as PtBinaryExpression
|
|
add2expr1.operator shouldBe "<<"
|
|
(add2expr1.right as PtNumber).number shouldBe 1.0
|
|
val add2expr2 = ((add2[2] as PtAssignment).value as PtBinaryExpression).right as PtBinaryExpression
|
|
add2expr2.operator shouldBe "<<"
|
|
(add2expr2.right as PtNumber).number shouldBe 1.0
|
|
val add2expr3 = (((add2[3] as PtAssignment).value as PtBuiltinFunctionCall).args[0] as PtBinaryExpression).right as PtBinaryExpression
|
|
add2expr3.operator shouldBe "<<"
|
|
(add2expr3.right as PtNumber).number shouldBe 1.0
|
|
|
|
((sub1[1] as PtAugmentedAssign).value as PtNumber).number shouldBe 10.0
|
|
(((sub1[2] as PtAssignment).value as PtBinaryExpression).right as PtNumber).number shouldBe 10.0
|
|
val sub1peek = (sub1[3] as PtAssignment).value as PtBuiltinFunctionCall
|
|
((sub1peek.args[0] as PtBinaryExpression).right as PtNumber).number shouldBe 10.0
|
|
|
|
val sub2expr1 = (sub2[1] as PtAugmentedAssign).value as PtBinaryExpression
|
|
sub2expr1.operator shouldBe "<<"
|
|
(sub2expr1.right as PtNumber).number shouldBe 1.0
|
|
val sub2expr2 = ((sub2[2] as PtAssignment).value as PtBinaryExpression).right as PtBinaryExpression
|
|
sub2expr2.operator shouldBe "<<"
|
|
(sub2expr2.right as PtNumber).number shouldBe 1.0
|
|
val sub2expr3 = (((sub2[3] as PtAssignment).value as PtBuiltinFunctionCall).args[0] as PtBinaryExpression).right as PtBinaryExpression
|
|
sub2expr3.operator shouldBe "<<"
|
|
(sub2expr3.right as PtNumber).number shouldBe 1.0
|
|
}
|
|
|
|
test("odd pointer arithmetic") {
|
|
val src="""
|
|
main{
|
|
|
|
sub start() {
|
|
^^ubyte @shared ptr = 2000
|
|
cx16.r0L = (cx16.r1 - ptr) as ubyte
|
|
cx16.r1L = (cx16.r1 - (ptr as uword)) as ubyte
|
|
void findstr1("asdf")
|
|
void findstr2("asdf")
|
|
}
|
|
|
|
sub findstr1(str haystack) -> ubyte {
|
|
return (cx16.r3-haystack) as ubyte
|
|
}
|
|
sub findstr2(str haystack) -> ubyte {
|
|
return (cx16.r3-(haystack as uword)) as ubyte
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain("6:31: unclear pointer arithmetic in expression")
|
|
errors.errors[1] shouldContain("13:25: unclear pointer arithmetic in expression")
|
|
}
|
|
|
|
test("uword struct field array indexing") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
struct List {
|
|
uword s
|
|
uword n
|
|
}
|
|
^^List l = ^^List : []
|
|
l.s[2] = 42
|
|
l.s[300] = 42
|
|
l.n[2] = 99
|
|
l.n[300] = 99
|
|
l.s[cx16.r0L] = 42
|
|
l.n[cx16.r0L] = 99
|
|
l.s[cx16.r0L+2] = 42
|
|
l.n[cx16.r0L+2] = 99
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("array indexing on non pointer fields give correct error messages") {
|
|
val src="""
|
|
main {
|
|
struct List {
|
|
bool s
|
|
ubyte n
|
|
uword ptr
|
|
}
|
|
sub start() {
|
|
^^List @shared l1 = ^^List : []
|
|
bool ss = l1.s[1]
|
|
ubyte ub = l1.n[1]
|
|
uword uw = l1.ptr[1]
|
|
l1.s[1] = 4444
|
|
l1.n[1] = true
|
|
l1.ptr[1] = 4444
|
|
}
|
|
}"""
|
|
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 8
|
|
errors.errors[0] shouldContain "invalid assignment value"
|
|
errors.errors[1] shouldContain "cannot array index"
|
|
errors.errors[2] shouldContain "invalid assignment value"
|
|
errors.errors[3] shouldContain "cannot array index"
|
|
errors.errors[4] shouldContain "cannot array index"
|
|
errors.errors[5] shouldContain "cannot array index"
|
|
errors.errors[6] shouldContain "out of range"
|
|
errors.errors[7] shouldContain "cannot assign word to byte"
|
|
}
|
|
|
|
test("dereferences of ptr variables mark those as used in the callgraph") {
|
|
val src="""
|
|
main {
|
|
struct List {
|
|
^^uword s
|
|
ubyte n
|
|
^^List next
|
|
}
|
|
sub start() {
|
|
^^List l1 = ^^List : []
|
|
^^List l2 = ^^List : []
|
|
l1.s[2] = 1
|
|
l2.n=10
|
|
|
|
^^List l3 = ^^List : []
|
|
cx16.r0L = l3.next.n
|
|
}
|
|
}"""
|
|
|
|
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 10
|
|
(st[0] as VarDecl).name shouldBe "l1"
|
|
(st[2] as VarDecl).name shouldBe "l2"
|
|
st[4] shouldBe instanceOf<Assignment>()
|
|
st[5] shouldBe instanceOf<Assignment>()
|
|
(st[6] as VarDecl).name shouldBe "l3"
|
|
st[8] shouldBe instanceOf<Assignment>()
|
|
}
|
|
|
|
test("indexing pointers with index 0 is just a direct pointer dereference except when followed by a struct field lookup") {
|
|
val src="""
|
|
%import floats
|
|
main {
|
|
struct List {
|
|
^^uword s
|
|
ubyte n
|
|
}
|
|
sub start() {
|
|
^^List l1 = ^^List : []
|
|
^^word @shared wptr
|
|
^^float @shared fptr
|
|
float f1,f2
|
|
|
|
cx16.r0 = l1.s^^
|
|
cx16.r1 = l1^^.s^^
|
|
cx16.r2 = l1.s^^
|
|
cx16.r3 = l1.s[0]
|
|
cx16.r4 = l1^^.s[0]
|
|
|
|
l1.s^^ = 4242
|
|
l1^^.s^^ = 4242
|
|
l1.s^^ = 4242
|
|
l1.s[0] = 4242
|
|
;; l1^^.s[0] = 4242 ; TODO fix parse syntax error
|
|
|
|
cx16.r0s = wptr[0]
|
|
cx16.r1s = wptr^^
|
|
wptr^^ = 4242
|
|
wptr[0] = 4242
|
|
|
|
f1 = fptr^^
|
|
f2 = fptr[0]
|
|
fptr^^ = 1.234
|
|
fptr[0] = 1.234
|
|
|
|
; not changed to dereference:
|
|
cx16.r0L = l1[0].n
|
|
cx16.r1L = l1[1].n
|
|
}
|
|
}"""
|
|
|
|
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 30
|
|
val dr0 = (st[10] as Assignment).value as PtrDereference
|
|
val dr1 = (st[11] as Assignment).value as PtrDereference
|
|
val dr2 = (st[12] as Assignment).value as PtrDereference
|
|
val dr3 = (st[13] as Assignment).value as PtrDereference
|
|
val dr4 = (st[14] as Assignment).value as PtrDereference
|
|
|
|
val dr5 = (st[15] as Assignment).target.pointerDereference!!
|
|
val dr6 = (st[16] as Assignment).target.pointerDereference!!
|
|
val dr7 = (st[17] as Assignment).target.pointerDereference!!
|
|
val dr8 = (st[18] as Assignment).target.pointerDereference!!
|
|
|
|
val dr9 = (st[19] as Assignment).value as FunctionCallExpression
|
|
val dr10 = (st[20] as Assignment).value as PtrDereference
|
|
val dr11 = (st[21] as Assignment).target.pointerDereference!!
|
|
(st[22] as FunctionCallStatement).target.nameInSource shouldBe listOf("pokew")
|
|
|
|
val dr13 = (st[23] as Assignment).value as PtrDereference
|
|
((st[24] as Assignment).value as FunctionCallExpression).target.nameInSource shouldBe listOf("peekf")
|
|
val dr15 = (st[25] as Assignment).target.pointerDereference!!
|
|
(st[26] as FunctionCallStatement).target.nameInSource shouldBe listOf("pokef")
|
|
|
|
dr0.chain shouldBe listOf("l1", "s")
|
|
dr0.derefLast shouldBe true
|
|
dr1.chain shouldBe listOf("l1", "s")
|
|
dr1.derefLast shouldBe true
|
|
dr2.chain shouldBe listOf("l1", "s")
|
|
dr2.derefLast shouldBe true
|
|
dr3.chain shouldBe listOf("l1", "s")
|
|
dr3.derefLast shouldBe true
|
|
dr4.chain shouldBe listOf("l1", "s")
|
|
dr4.derefLast shouldBe true
|
|
|
|
dr5.chain shouldBe listOf("l1", "s")
|
|
dr5.derefLast shouldBe true
|
|
dr6.chain shouldBe listOf("l1", "s")
|
|
dr6.derefLast shouldBe true
|
|
dr7.chain shouldBe listOf("l1", "s")
|
|
dr7.derefLast shouldBe true
|
|
dr8.chain shouldBe listOf("l1", "s")
|
|
dr8.derefLast shouldBe true
|
|
|
|
dr9.target.nameInSource shouldBe listOf("peekw")
|
|
dr10.chain shouldBe listOf("wptr")
|
|
dr10.derefLast shouldBe true
|
|
dr11.chain shouldBe listOf("wptr")
|
|
dr11.derefLast shouldBe true
|
|
|
|
dr13.chain shouldBe listOf("fptr")
|
|
dr13.derefLast shouldBe true
|
|
dr15.chain shouldBe listOf("fptr")
|
|
dr15.derefLast shouldBe true
|
|
|
|
val list0 = (st[27] as Assignment).value as BinaryExpression
|
|
val list1 = (st[28] as Assignment).value as BinaryExpression
|
|
|
|
list0.operator shouldBe "."
|
|
(list0.right as IdentifierReference).nameInSource shouldBe listOf("n")
|
|
val list0left = list0.left as ArrayIndexedExpression
|
|
list0left.plainarrayvar!!.nameInSource shouldBe listOf("l1")
|
|
list0left.indexer.constIndex() shouldBe 0
|
|
list1.operator shouldBe "."
|
|
(list1.right as IdentifierReference).nameInSource shouldBe listOf("n")
|
|
val list1left = list0.left as ArrayIndexedExpression
|
|
list1left.plainarrayvar!!.nameInSource shouldBe listOf("l1")
|
|
list1left.indexer.constIndex() shouldBe 0
|
|
}
|
|
|
|
test("indexing pointers to structs") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main{
|
|
struct Country {
|
|
str name
|
|
float population ; millions
|
|
uword area ; 1000 km^2
|
|
ubyte code
|
|
}
|
|
|
|
^^Country[10] countries ; won't be fully filled
|
|
ubyte num_countries
|
|
|
|
sub start() {
|
|
|
|
thing(countries[0])
|
|
thing(countries[1])
|
|
|
|
^^Country @shared fp = 9999
|
|
countries[0] = fp
|
|
countries[1] = fp
|
|
|
|
thing(countries[0])
|
|
thing(countries[1])
|
|
|
|
countries[0] = ^^Country : ["Indonesia", 285.72, 1904, 44]
|
|
countries[1] = ^^Country : ["Congo", 112.83, 2344, 55]
|
|
|
|
thing(countries[0])
|
|
thing(countries[1])
|
|
|
|
float fl
|
|
thing(countries[0].name)
|
|
thing(countries[0].area)
|
|
thingb(countries[0].code)
|
|
fl = countries[0].population
|
|
thing(countries[1].name)
|
|
thing(countries[1].area)
|
|
thingb(countries[1].code)
|
|
fl = countries[1].population
|
|
}
|
|
|
|
sub thing(uword a) {
|
|
a++
|
|
}
|
|
|
|
sub thingb(ubyte a) {
|
|
a++
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("global and local pointer vars") {
|
|
val src="""
|
|
main {
|
|
^^uword g_wptr
|
|
|
|
sub start() {
|
|
^^uword l_wptr
|
|
|
|
cx16.r0 = g_wptr
|
|
cx16.r1 = g_wptr^^
|
|
cx16.r0 = l_wptr
|
|
cx16.r1 = l_wptr^^
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("global struct var deref type") {
|
|
val src="""
|
|
main {
|
|
struct State {
|
|
uword c
|
|
^^uword ptr
|
|
}
|
|
|
|
^^State matchstate
|
|
|
|
sub start() {
|
|
cx16.r0 = matchstate.ptr
|
|
cx16.r1 = matchstate.ptr^^
|
|
cx16.r2 = matchstate^^.ptr^^ ; equivalent to previous
|
|
cx16.r3 = matchstate.c
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("local struct var deref type") {
|
|
val src="""
|
|
main {
|
|
struct State {
|
|
uword c
|
|
^^uword ptr
|
|
}
|
|
|
|
sub start() {
|
|
^^State matchstate
|
|
cx16.r0 = matchstate.ptr
|
|
cx16.r1 = matchstate.ptr^^
|
|
cx16.r2 = matchstate^^.ptr^^ ; equivalent to previous
|
|
cx16.r3 = matchstate.c
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), true, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("assigning pointer dereferences via memcopy") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
struct List {
|
|
bool b
|
|
word w
|
|
float f
|
|
^^List next
|
|
}
|
|
|
|
struct Foo {
|
|
byte bb
|
|
}
|
|
|
|
^^List l1 = 2000
|
|
^^List l2 = 3000
|
|
^^Foo f1 = 4000
|
|
|
|
l1^^ = l2^^ ; memcpy1
|
|
l1[3] = l2^^ ; memcpy2
|
|
l1[3]^^ = l2^^ ; memcpy3
|
|
l1[cx16.r0] = l2^^ ; memcpy4
|
|
l1[cx16.r0]^^ = l2^^ ; memcpy5
|
|
|
|
l2^^ = l1[2] ;memcpy6
|
|
l2^^ = l1[2]^^ ;memcpy7
|
|
l2^^ = l1[cx16.r0L] ;memcpy8
|
|
l2^^ = l1[cx16.r0L]^^ ;memcpy9
|
|
|
|
; TODO add more supported syntax here when they're implemented in the future
|
|
}
|
|
}"""
|
|
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
val result = compileText(VMTarget(), false, src, outputDir)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 18
|
|
val memcpy1 = st[8] as FunctionCallStatement
|
|
val memcpy2 = st[9] as FunctionCallStatement
|
|
val memcpy3 = st[10] as FunctionCallStatement
|
|
val memcpy4 = st[11] as FunctionCallStatement
|
|
val memcpy5 = st[12] as FunctionCallStatement
|
|
val memcpy6 = st[13] as FunctionCallStatement
|
|
val memcpy7 = st[14] as FunctionCallStatement
|
|
val memcpy8 = st[15] as FunctionCallStatement
|
|
val memcpy9 = st[16] as FunctionCallStatement
|
|
|
|
memcpy1.target.nameInSource shouldBe listOf("sys", "memcopy")
|
|
(memcpy1.args[0] as IdentifierReference).nameInSource shouldBe listOf("l2")
|
|
(memcpy1.args[1] as IdentifierReference).nameInSource shouldBe listOf("l1")
|
|
(memcpy1.args[2] as NumericLiteral).number shouldBe 13.0 // sizeof(List)
|
|
|
|
memcpy2.target.nameInSource shouldBe listOf("sys", "memcopy")
|
|
(memcpy2.args[0] as IdentifierReference).nameInSource shouldBe listOf("l2")
|
|
val memcpy2value = memcpy2.args[1] as BinaryExpression
|
|
memcpy2value.operator shouldBe "+"
|
|
(memcpy2value.left as IdentifierReference).nameInSource shouldBe listOf("l1")
|
|
(memcpy2value.right as NumericLiteral).number shouldBe 3.0 // just rely on pointer arithmetic later
|
|
(memcpy2.args[2] as NumericLiteral).number shouldBe 13.0 // sizeof(List)
|
|
|
|
(memcpy3.target isSameAs memcpy2.target) shouldBe true
|
|
memcpy3.args.zip(memcpy2.args).all { it.first isSameAs it.second} shouldBe true
|
|
|
|
(memcpy5.target isSameAs memcpy4.target) shouldBe true
|
|
memcpy5.args.zip(memcpy4.args).all { it.first isSameAs it.second} shouldBe true
|
|
|
|
(memcpy7.target isSameAs memcpy6.target) shouldBe true
|
|
memcpy7.args.zip(memcpy6.args).all { it.first isSameAs it.second} shouldBe true
|
|
|
|
(memcpy9.target isSameAs memcpy8.target) shouldBe true
|
|
memcpy9.args.zip(memcpy8.args).all { it.first isSameAs it.second} shouldBe true
|
|
}
|
|
|
|
test("assigning pointer dereferences should be same type") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
struct List {
|
|
bool b
|
|
word w
|
|
float f
|
|
^^List next
|
|
}
|
|
|
|
struct Foo {
|
|
byte bb
|
|
}
|
|
|
|
^^List l1 = 2000
|
|
^^List l2 = 3000
|
|
^^Foo f1 = 4000
|
|
^^bool bptr = 5000
|
|
|
|
l1^^ = f1^^
|
|
l1^^ = bptr^^
|
|
l1^^ = 4242
|
|
}
|
|
}"""
|
|
|
|
val errors=ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors)
|
|
errors.errors.size shouldBe 4
|
|
errors.errors[0] shouldContain "struct instance by value"
|
|
errors.errors[1] shouldContain "doesn't match"
|
|
errors.errors[2] shouldContain "assigning this value to struct instance not supported"
|
|
errors.errors[3] shouldContain "assigning this value to struct instance not supported"
|
|
}
|
|
|
|
xtest("assigning struct instances") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
struct List {
|
|
bool b
|
|
uword value
|
|
float fv
|
|
} ; sizeof = 11
|
|
|
|
^^List lp1 = 10000
|
|
^^List lp2 = 20000
|
|
|
|
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
|
|
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 11)
|
|
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above) TODO fix astchecker to allow this case
|
|
lp2^^ = lp1[2] ; memcopy(lp1 + 22, lp2, 11)
|
|
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) TODO fix astchecker to allow this case
|
|
lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 99
|
|
}
|
|
|
|
test("a.b.c[i]^^.value as expression where pointer is struct") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
cx16.r0 = other.foo.listarray[2].value
|
|
cx16.r1 = other.foo.listarray[3]^^.value
|
|
other.foo()
|
|
}
|
|
}
|
|
|
|
other {
|
|
sub foo() {
|
|
struct List {
|
|
bool b
|
|
uword value
|
|
}
|
|
|
|
^^List[10] listarray
|
|
cx16.r0 = listarray[2].value
|
|
cx16.r1 = listarray[3]^^.value
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("a.b.c[i]^^ as expression where pointer is primitive type") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
float @shared f2 = other.foo.fptrarray[3]^^
|
|
cx16.r1s = other.foo.wptrarray[3]^^
|
|
cx16.r2L = other.foo.ptrarray[3]^^
|
|
cx16.r3bL = other.foo.bptrarray[3]^^
|
|
other.foo()
|
|
}
|
|
}
|
|
|
|
other {
|
|
sub foo() {
|
|
^^word[10] wptrarray
|
|
^^float[10] fptrarray
|
|
^^ubyte[10] ptrarray
|
|
^^bool[10] bptrarray
|
|
float @shared f1 = fptrarray[3]^^
|
|
cx16.r1s = wptrarray[3]^^
|
|
cx16.r2L = ptrarray[3]^^
|
|
cx16.r3bL = bptrarray[3]^^
|
|
}
|
|
}
|
|
"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("a.b.c[i]^^.value = X where pointer is struct gives good error message") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
other.foo.listarray[3]^^.value = cx16.r0
|
|
other.foo()
|
|
}
|
|
}
|
|
|
|
other {
|
|
sub foo() {
|
|
struct List {
|
|
bool b
|
|
uword value
|
|
}
|
|
|
|
^^List[10] listarray
|
|
listarray[3]^^.value = cx16.r0
|
|
listarray[3]^^ = 999 ; cannot assign word value to struct instance
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
|
errors.errors.size shouldBeGreaterThanOrEqualTo 3
|
|
errors.errors[0] shouldContain "no support for"
|
|
errors.errors[1] shouldContain "no support for"
|
|
errors.errors[2] shouldContain "assigning this value to struct instance not supported"
|
|
}
|
|
|
|
xtest("array indexed assignment parses with and without explicit dereference after struct pointer [IGNORED because it's a parser error right now]") {
|
|
val src="""
|
|
main {
|
|
|
|
sub start() {
|
|
struct Node {
|
|
^^uword s
|
|
}
|
|
|
|
^^Node l1
|
|
|
|
l1.s[0] = 4242
|
|
l1^^.s[0] = 4242 ; TODO fix parse error
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
compileText(VMTarget(), false, src, outputDir, errors = errors) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "no support for"
|
|
errors.errors[1] shouldContain "no support for"
|
|
}
|
|
|
|
xtest("a.b.c[i].value = X where pointer is struct gives good error message [IGNORED because it's a parser error right now]") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
other.foo.listarray[2].value = cx16.r0
|
|
other.foo()
|
|
}
|
|
}
|
|
|
|
other {
|
|
sub foo() {
|
|
struct List {
|
|
bool b
|
|
uword value
|
|
}
|
|
|
|
^^List[10] listarray
|
|
listarray[2].value = cx16.r0
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
compileText(VMTarget(), false, src, outputDir, errors = errors) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "no support for"
|
|
errors.errors[1] shouldContain "no support for"
|
|
}
|
|
|
|
test("a.b.c[i]^^ as assignment target where pointer is primitive type") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
float @shared f2
|
|
other.foo.fptrarray[3]^^ = f2
|
|
other.foo.wptrarray[3]^^ = cx16.r1s
|
|
other.foo.ptrarray[3]^^ = cx16.r2L
|
|
other.foo.bptrarray[3]^^ = cx16.r3bL
|
|
other.foo()
|
|
}
|
|
}
|
|
|
|
other {
|
|
sub foo() {
|
|
^^word[10] wptrarray
|
|
^^float[10] fptrarray
|
|
^^ubyte[10] ptrarray
|
|
^^bool[10] bptrarray
|
|
float @shared f1
|
|
fptrarray[3]^^ = f1
|
|
wptrarray[3]^^ = cx16.r1s
|
|
ptrarray[3]^^ = cx16.r2L
|
|
bptrarray[3]^^ = cx16.r3bL
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("passing arrays to subroutines via typed pointer parameters") {
|
|
val src="""
|
|
%import floats
|
|
%import textio
|
|
|
|
main {
|
|
struct Node {
|
|
bool bb
|
|
float f
|
|
word w
|
|
^^Node next
|
|
}
|
|
|
|
sub start() {
|
|
^^Node[5] node_array
|
|
node_array [0] = node_array[1] = node_array[2] = node_array[3] = node_array[4] = 7777
|
|
bool[5] bool_array = [true, true, true, false, false]
|
|
word[5] @nosplit word_array = [-1111,-2222,3333,4444,5555] ; has to be nosplit
|
|
float[5] float_array = [111.111,222.222,333.333,444.444,555.555]
|
|
|
|
modifyb(bool_array, 2)
|
|
modifyw(word_array, 2)
|
|
modifyf(float_array, 2)
|
|
}
|
|
|
|
sub modifyb(^^bool array, ubyte index) {
|
|
array[index] = false
|
|
}
|
|
|
|
sub modifyw(^^word array, ubyte index) {
|
|
array[index] = 9999
|
|
}
|
|
|
|
sub modifyf(^^float array, ubyte index) {
|
|
array[index] = 9999.999
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("array of pointers as subroutine param are all passed as ^^ubyte because of split word arrays") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
^^ubyte[4] array1
|
|
^^byte[4] array2
|
|
^^bool[4] array3
|
|
^^word[4] array4
|
|
^^uword[4] array5
|
|
^^float[4] array6
|
|
^^long[4] array7
|
|
|
|
ok(array1)
|
|
ok(array2)
|
|
ok(array3)
|
|
ok(array4)
|
|
ok(array5)
|
|
ok(array6)
|
|
ok(array7)
|
|
}
|
|
|
|
sub ok(^^ubyte ptr) {
|
|
cx16.r0 = ptr
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("str replaced by ^^ubyte in subroutine args and return type") {
|
|
val src="""
|
|
main {
|
|
|
|
sub start() {
|
|
void test("hello")
|
|
}
|
|
|
|
sub test(str argument) -> str {
|
|
argument++
|
|
return "bye"
|
|
}
|
|
}"""
|
|
val vmprg = compileText(VMTarget(), false, src, outputDir)!!
|
|
val vmtest = vmprg.codegenAst!!.allBlocks().first { it.name == "main" }.children[1] as PtSub
|
|
vmtest.signature.returns.single() shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
(vmtest.signature.children.single() as PtSubroutineParameter).type shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
val vminc = vmtest.children[2] as PtAugmentedAssign
|
|
vminc.operator shouldBe "+="
|
|
vminc.target.identifier!!.name shouldBe "main.test.argument"
|
|
vminc.target.identifier!!.type shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
(vminc.value as PtNumber).number shouldBe 1.0
|
|
|
|
val c64prg = compileText(C64Target(), false, src, outputDir)!!
|
|
val c64test = c64prg.codegenAst!!.allBlocks().first { it.name == "p8b_main" }.children[1] as PtSub
|
|
c64test.signature.returns.single() shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
(c64test.signature.children.single() as PtSubroutineParameter).type shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
val c64inc = c64test.children[2] as PtAugmentedAssign
|
|
c64inc.operator shouldBe "+="
|
|
c64inc.target.identifier!!.name shouldBe "p8b_main.p8s_test.p8v_argument"
|
|
c64inc.target.identifier!!.type shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
(c64inc.value as PtNumber).number shouldBe 1.0
|
|
}
|
|
|
|
test("hoist variable decl and initializer correctly in case of pointer type variable as well") {
|
|
val src="""
|
|
%import textio
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
txt.print("one\n")
|
|
if true {
|
|
txt.print("two\n")
|
|
^^bool @shared successor = find_successor() ; testcase here
|
|
}
|
|
txt.print("four\n")
|
|
|
|
sub find_successor() -> uword {
|
|
txt.print("three\n")
|
|
cx16.r0++
|
|
return 0
|
|
}
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
val result = compileText(VMTarget(), false, src, outputDir)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 6
|
|
st[0] shouldBe instanceOf<VarDecl>()
|
|
(st[1] as FunctionCallStatement).target.nameInSource shouldBe listOf("txt", "print")
|
|
st[2] shouldBe instanceOf<IfElse>()
|
|
(st[3] as FunctionCallStatement).target.nameInSource shouldBe listOf("txt", "print")
|
|
st[4] shouldBe instanceOf<Return>()
|
|
st[5] shouldBe instanceOf<Subroutine>()
|
|
}
|
|
|
|
test("initialize struct with string fields") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
str question
|
|
str animal
|
|
}
|
|
|
|
sub start() {
|
|
^^Node n = ^^Node : ["question string", "animal name string"]
|
|
^^ubyte @shared q = n.question
|
|
^^ubyte @shared a = n.animal
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("struct initializers in array") {
|
|
val src="""
|
|
%option enable_floats
|
|
main {
|
|
struct Node {
|
|
ubyte id
|
|
str name
|
|
uword array
|
|
bool flag
|
|
float speed
|
|
}
|
|
|
|
sub start() {
|
|
^^Node[] @shared nodes = [
|
|
^^Node:[1,"one", 1000, true, 1.111 ],
|
|
^^Node:[2,"two", 2000, false, 2.222 ],
|
|
^^Node:[3,"three", 3000, true, 3.333 ],
|
|
^^Node:[],
|
|
^^Node:[],
|
|
^^Node:[],
|
|
]
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("type error for invalid field initializer") {
|
|
val src="""
|
|
main {
|
|
struct Enemy {
|
|
ubyte xpos, ypos
|
|
uword health
|
|
bool elite
|
|
}
|
|
|
|
sub start() {
|
|
^^Enemy @shared e1 = ^^Enemy: []
|
|
^^Enemy @shared e2 = ^^Enemy: [1,2,3,true]
|
|
^^Enemy @shared e3 = ^^Enemy: [1,2,3,4]
|
|
^^Enemy @shared e4 = ^^Enemy: [1,2,3,4.444]
|
|
|
|
e3.elite = 99
|
|
e4.elite = 3.444
|
|
}
|
|
}"""
|
|
|
|
val errors=ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 4
|
|
errors.errors[0] shouldContain "value #4 has incompatible type"
|
|
errors.errors[1] shouldContain "value #4 has incompatible type"
|
|
errors.errors[2] shouldContain "doesn't match target type"
|
|
errors.errors[3] shouldContain "doesn't match target type"
|
|
}
|
|
|
|
test("long and short form struct initializers") {
|
|
val src="""
|
|
%option enable_floats
|
|
|
|
main {
|
|
struct Node {
|
|
ubyte id
|
|
str name
|
|
uword array
|
|
bool flag
|
|
float perc
|
|
}
|
|
|
|
sub start() {
|
|
^^Node[] @shared nodeswithtype = [
|
|
^^Node: [1,"one", 1000, true, 1.111],
|
|
^^Node: [],
|
|
]
|
|
|
|
^^Node[] @shared nodeswithout = [
|
|
[2,"two", 2000, false, 2.222],
|
|
[],
|
|
]
|
|
|
|
^^Node @shared nptrwithtype = ^^Node : [1, "one", 1000, false, 3.333]
|
|
^^Node @shared nptrwithouttype = [1, "one", 1000, false, 3.333]
|
|
}
|
|
}"""
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("type error for wrong type in pointer array and assignment") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
ubyte id
|
|
}
|
|
struct Foobar {
|
|
bool thing
|
|
}
|
|
|
|
sub start() {
|
|
^^Node[] onlynodes = [
|
|
^^Node: [],
|
|
^^Foobar: []
|
|
]
|
|
|
|
uword multipleok = [
|
|
^^Node: [],
|
|
^^Foobar: []
|
|
]
|
|
|
|
^^Node node = ^^Foobar: []
|
|
}
|
|
}"""
|
|
val errors=ErrorReporterForTests()
|
|
compileText(C64Target(), false, src, outputDir, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 6
|
|
errors.errors[0] shouldContain "11:30: initialization value for pointer array"
|
|
errors.errors[1] shouldContain "11:30: undefined array type" // a bit redundant but can't be helped
|
|
errors.errors[2] shouldContain "13:13: struct initializer element has invalid type"
|
|
errors.errors[3] shouldContain "16:28: invalid assignment value"
|
|
errors.errors[4] shouldContain "16:28: undefined array type"
|
|
errors.errors[5] shouldContain "21:23: cannot assign different pointer type"
|
|
}
|
|
|
|
test("local and global struct pointer qualified name lookups") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
str question
|
|
str animal
|
|
^^Node negative
|
|
^^Node positive
|
|
}
|
|
|
|
^^Node @shared first
|
|
|
|
sub start() {
|
|
cx16.r1 = first
|
|
cx16.r2 = first.negative
|
|
cx16.r3 = first^^.negative
|
|
cx16.r4 = first.negative.animal
|
|
cx16.r5 = first^^.negative^^.animal
|
|
|
|
cx16.r1 = db.first
|
|
cx16.r2 = db.first.negative
|
|
|
|
cx16.r3 = db.first^^.negative
|
|
cx16.r4 = db.first.negative.animal
|
|
cx16.r5 = db.first^^.negative^^.animal
|
|
|
|
db.first.negative.animal = 0
|
|
db.first.negative = 0
|
|
db.first = 0
|
|
|
|
func(db.first)
|
|
func(db.first.negative)
|
|
func(db.first.negative.animal)
|
|
}
|
|
|
|
sub func(uword arg) {
|
|
cx16.r0 = arg
|
|
}
|
|
}
|
|
|
|
db {
|
|
struct Node {
|
|
str question
|
|
str animal
|
|
^^Node negative
|
|
^^Node positive
|
|
}
|
|
|
|
^^Node @shared first
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("str can be used without explicit cast where ^^ubyte is expected") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
str name = "pjotr"
|
|
^^ubyte @shared ptr = name
|
|
ptr = "hello"
|
|
func(name)
|
|
func("bye")
|
|
}
|
|
|
|
sub func(^^ubyte arg) {
|
|
cx16.r0++
|
|
}
|
|
}"""
|
|
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
|
|
val st = result.compilerAst.entrypoint.statements
|
|
st.size shouldBe 7
|
|
(st[2] as Assignment).value shouldBe instanceOf<AddressOf>()
|
|
(st[3] as Assignment).value shouldBe instanceOf<AddressOf>()
|
|
(st[4] as FunctionCallStatement).args.single() shouldBe instanceOf<AddressOf>()
|
|
(st[5] as FunctionCallStatement).args.single() shouldBe instanceOf<AddressOf>()
|
|
}
|
|
|
|
test("initializing arrays of pointers") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
^^ubyte[4] array1 = [1000, 1100, 1200, 1300]
|
|
^^byte[4] array2 = [1000, 1100, 1200, 1300]
|
|
^^bool[4] array3 = [1000, 1100, 1200, 1300]
|
|
^^word[4] array4 = [1000, 1100, 1200, 1300]
|
|
^^uword[4] array5 = [1000, 1100, 1200, 1300]
|
|
^^float[4] array6 = [1000, 1100, 1200, 1300]
|
|
^^long[4] array7 = [1000, 1100, 1200, 1300]
|
|
|
|
cx16.r0 = array1[2]
|
|
cx16.r1 = array2[2]
|
|
cx16.r2 = array3[2]
|
|
cx16.r3 = array4[2]
|
|
cx16.r4 = array5[2]
|
|
cx16.r5 = array6[2]
|
|
cx16.r6 = array7[2]
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("array indexing a pointer and a pointer array both work") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
ubyte weight
|
|
}
|
|
|
|
sub start() {
|
|
^^Node nodes
|
|
^^Node[5] nodesarray
|
|
|
|
cx16.r0L = nodesarray[2].weight
|
|
cx16.r0L = nodes[2].weight
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("array indexing on a pointer with a word size index works") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
sub start() {
|
|
^^ubyte @shared ptr1 = $4000
|
|
^^uword @shared ptr2 = $4000
|
|
^^float @shared ptr3 = $4000
|
|
^^bool @shared ptr4 = $4000
|
|
uword @shared untyped = $4000
|
|
float @shared fl
|
|
bool @shared bb, bb2
|
|
|
|
untyped[$1000] = 0
|
|
ptr1[$1000] = 0
|
|
ptr2[$1000] = 0
|
|
ptr3[$1000] = 0
|
|
ptr4[$1000] = false
|
|
untyped[$1000] = 99
|
|
ptr1[$1000] = 99
|
|
ptr2[$1000] = 99
|
|
ptr3[$1000] = 99
|
|
ptr4[$1000] = true
|
|
untyped[$1000] = cx16.r0L
|
|
ptr1[$1000] = cx16.r0L
|
|
ptr2[$1000] = cx16.r0L
|
|
ptr3[$1000] = fl
|
|
ptr4[$1000] = bb
|
|
untyped[$1000] = cx16.r0L+1
|
|
ptr1[$1000] = cx16.r0L+1
|
|
ptr2[$1000] = cx16.r0L+1
|
|
ptr3[$1000] = fl+1.1
|
|
ptr4[$1000] = bb xor bb2
|
|
|
|
untyped[$1000 + cx16.r0] = 0
|
|
ptr1[$1000 + cx16.r0] = 0
|
|
ptr2[$1000 + cx16.r0] = 0
|
|
ptr3[$1000 + cx16.r0] = 0
|
|
ptr4[$1000 + cx16.r0] = false
|
|
untyped[$1000 + cx16.r0] = 99
|
|
ptr1[$1000 + cx16.r0] = 99
|
|
ptr2[$1000 + cx16.r0] = 99
|
|
ptr3[$1000 + cx16.r0] = 99
|
|
ptr4[$1000 + cx16.r0] = true
|
|
untyped[$1000 + cx16.r0] = cx16.r0L
|
|
ptr1[$1000 + cx16.r0] = cx16.r0L
|
|
ptr2[$1000 + cx16.r0] = cx16.r0L
|
|
ptr3[$1000 + cx16.r0] = fl
|
|
ptr4[$1000 + cx16.r0] = bb
|
|
untyped[$1000 + cx16.r0] = cx16.r0L+1
|
|
ptr1[$1000 + cx16.r0] = cx16.r0L+1
|
|
ptr2[$1000 + cx16.r0] = cx16.r0L+1
|
|
ptr3[$1000 + cx16.r0] = fl+1.1
|
|
ptr4[$1000 + cx16.r0] = bb xor bb2
|
|
|
|
cx16.r0L = untyped[$1000]
|
|
cx16.r1L = ptr1[$1000]
|
|
cx16.r2 = ptr2[$1000]
|
|
fl = ptr3[$1000]
|
|
bb = ptr4[$1000]
|
|
cx16.r0L = untyped[cx16.r0]
|
|
cx16.r1L = ptr1[cx16.r0]
|
|
cx16.r2 = ptr2[cx16.r0]
|
|
fl = ptr3[cx16.r0]
|
|
bb = ptr4[cx16.r0]
|
|
cx16.r0L = untyped[cx16.r0+1]
|
|
cx16.r1L = ptr1[cx16.r0+1]
|
|
cx16.r2 = ptr2[cx16.r0+1]
|
|
fl = ptr3[cx16.r0+1]
|
|
bb = ptr4[cx16.r0+1]
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("correct type of address of split and nosplit arrays") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
uword[10] @split @shared splitarray
|
|
uword[10] @nosplit @shared nosplitarray
|
|
|
|
^^uword ptr1 = &&splitarray
|
|
^^ubyte ptr2 = &&splitarray
|
|
^^uword ptr3 = &&nosplitarray
|
|
^^ubyte ptr4 = &&nosplitarray
|
|
}
|
|
}"""
|
|
val errors = ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "7:24: cannot assign different pointer type, expected ^^uword or uword but got ^^ubyte"
|
|
errors.errors[1] shouldContain "10:24: cannot assign different pointer type, expected ^^ubyte or uword but got ^^uword"
|
|
}
|
|
|
|
test("passing nosplit array of structpointers to a subroutine in various forms should be param type ptr to struct") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
ubyte weight
|
|
}
|
|
|
|
sub start() {
|
|
^^Node[10] @nosplit nodearray ; not actually possible to store this array but required for the address-ofs below
|
|
func(nodearray[0])
|
|
func(&&nodearray) ; error because datatype internally is registered as a split pointer array
|
|
func(&nodearray)
|
|
func(nodearray) ; error because datatype internally is registered as a split pointer array
|
|
}
|
|
|
|
sub func(^^Node n) {
|
|
cx16.r0++
|
|
}
|
|
}"""
|
|
|
|
val errors=ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
|
|
errors.errors.size shouldBe 3
|
|
errors.warnings.size shouldBe 0
|
|
errors.infos.size shouldBe 0
|
|
errors.errors[0] shouldContain "pointer arrays can only be @split"
|
|
errors.errors[1] shouldContain "was: ^^ubyte (because arg is a @split word array) expected: ^^main.Node"
|
|
errors.errors[2] shouldContain "was: ^^ubyte (because arg is a @split word array) expected: ^^main.Node"
|
|
}
|
|
|
|
test("passing split array of structpointers to a subroutine in various forms should be param type ptr to ubyte (the lsb part of the split array)") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
ubyte weight
|
|
}
|
|
|
|
sub start() {
|
|
^^Node[10] @split nodearray
|
|
func(&&nodearray)
|
|
func(&nodearray)
|
|
func(nodearray)
|
|
}
|
|
|
|
sub func(^^ubyte n) {
|
|
cx16.r0++
|
|
}
|
|
}"""
|
|
val errors=ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
compileText(C64Target(), false, src, outputDir, errors=errors) shouldNotBe null
|
|
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)
|
|
errors.errors.size shouldBe 0
|
|
errors.warnings.size shouldBe 0
|
|
errors.infos.size shouldBe 0
|
|
val st = result!!.codegenAst!!.entrypoint()!!.children
|
|
st.size shouldBe 6
|
|
val f1a = (st[2] as PtFunctionCall).args[0]
|
|
val f2a = (st[3] as PtFunctionCall).args[0]
|
|
val f3a = (st[4] as PtFunctionCall).args[0]
|
|
f1a.type shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
f2a.type shouldBe DataType.UWORD
|
|
f3a.type shouldBe DataType.pointer(BaseDataType.UBYTE)
|
|
}
|
|
|
|
test("pointer cannot be used in conditional expression in shorthand form") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
^^word ptr
|
|
|
|
if ptr cx16.r0++
|
|
if not ptr cx16.r1++
|
|
|
|
while ptr cx16.r0++
|
|
while not ptr cx16.r1++
|
|
|
|
do cx16.r0++ until ptr
|
|
do cx16.r1++ until not ptr
|
|
|
|
cx16.r0 = if ptr 1 else 0
|
|
cx16.r1 = if not ptr 1 else 0
|
|
}
|
|
}"""
|
|
|
|
val errors=ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 8
|
|
errors.errors[0] shouldContain "condition should be a boolean"
|
|
errors.errors[1] shouldContain "pointers don't support prefix operators"
|
|
errors.errors[2] shouldContain "condition should be a boolean"
|
|
errors.errors[3] shouldContain "pointers don't support prefix operators"
|
|
errors.errors[4] shouldContain "condition should be a boolean"
|
|
errors.errors[5] shouldContain "pointers don't support prefix operators"
|
|
errors.errors[6] shouldContain "condition should be a boolean"
|
|
errors.errors[7] shouldContain "pointers don't support prefix operators"
|
|
}
|
|
|
|
test("pointers in if expressions") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
^^word ptr
|
|
|
|
if ptr!=0
|
|
cx16.r0++
|
|
if ptr==0
|
|
cx16.r0++
|
|
|
|
cx16.r0 = if ptr!=0 0 else ptr
|
|
cx16.r1 = if ptr==0 0 else ptr
|
|
cx16.r2 = if ptr!=0 ptr else 0
|
|
cx16.r3 = if ptr==0 ptr else 0
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("boolean field in if statement condition") {
|
|
val src = """
|
|
main {
|
|
struct Enemy {
|
|
ubyte xpos, ypos
|
|
uword health
|
|
bool elite
|
|
}
|
|
|
|
sub start() {
|
|
^^Enemy e1
|
|
if e1.elite
|
|
e1.health += 100
|
|
}
|
|
}"""
|
|
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("^^str is not valid") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
str name = "test"
|
|
^^str ptr = &name
|
|
^^str[4] array
|
|
ptr = foo(&name)
|
|
}
|
|
|
|
sub foo(^^str arg) -> ^^str {
|
|
return arg+2
|
|
}
|
|
}"""
|
|
val errors=ErrorReporterForTests()
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
|
|
errors.errors.size shouldBe 4
|
|
errors.errors[0] shouldContain "^^str is not a valid pointer type"
|
|
errors.errors[1] shouldContain "^^str is not a valid pointer type"
|
|
errors.errors[2] shouldContain "^^str is not a valid pointer type"
|
|
errors.errors[3] shouldContain "^^str is not a valid return type"
|
|
}
|
|
|
|
test("various miscellaneous pointer syntax tests") {
|
|
val src="""
|
|
main {
|
|
struct Node {
|
|
ubyte weight
|
|
}
|
|
^^Node @shared n1, n2
|
|
^^bool @shared bptr1, bptr2
|
|
|
|
sub start() {
|
|
^^Node nodes
|
|
n1^^=n2^^ ; ok
|
|
bptr1^^=bptr2^^ ; ok
|
|
n1^^=nodes[2] ; ok
|
|
n2 = nodes[2]^^ ; cannot assign instance to pointer like this yet
|
|
}
|
|
}"""
|
|
val errors=ErrorReporterForTests(keepMessagesAfterReporting = true)
|
|
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
|
errors.errors.size shouldBe 2
|
|
errors.errors[0] shouldContain "struct instance by value"
|
|
errors.errors[1] shouldContain "no support for getting the target value of pointer array indexing"
|
|
}
|
|
|
|
test("pointer variable usage detection in other block 1") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
other.bptr^^ = true
|
|
cx16.r0bL = other.bptr^^
|
|
}
|
|
}
|
|
|
|
other {
|
|
^^bool bptr
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
|
compileText(VMTarget(), true, src, outputDir, writeAssembly = false) shouldNotBe null
|
|
}
|
|
|
|
test("pointer variable usage detection in other block 2") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
other.func.variable^^ += 3
|
|
}
|
|
}
|
|
|
|
other {
|
|
sub func() {
|
|
^^ubyte variable
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
|
compileText(VMTarget(), true, src, outputDir, writeAssembly = false) shouldNotBe null
|
|
}
|
|
|
|
test("float ptr inplace operations") {
|
|
val src="""
|
|
%import floats
|
|
|
|
main {
|
|
^^float g_floats
|
|
|
|
sub start() {
|
|
^^float l_floats
|
|
|
|
f_add()
|
|
f_sub()
|
|
f_mul()
|
|
f_div()
|
|
|
|
sub f_add() {
|
|
l_floats^^ += 3.0
|
|
g_floats^^ += 3.0
|
|
other.g_floats^^ += 3.0
|
|
other.func.l_floats^^ += 3.0
|
|
}
|
|
|
|
sub f_sub() {
|
|
l_floats^^ -= 3.0
|
|
g_floats^^ -= 3.0
|
|
other.g_floats^^ -= 3.0
|
|
other.func.l_floats^^ -= 3.0
|
|
}
|
|
|
|
sub f_mul() {
|
|
l_floats^^ *= 3.0
|
|
g_floats^^ *= 3.0
|
|
other.g_floats^^ *= 3.0
|
|
other.func.l_floats^^ *= 3.0
|
|
}
|
|
|
|
sub f_div() {
|
|
l_floats^^ /= 3.0
|
|
g_floats^^ /= 3.0
|
|
other.g_floats^^ /= 3.0
|
|
other.func.l_floats^^ /= 3.0
|
|
}
|
|
}
|
|
}
|
|
|
|
other {
|
|
%option force_output
|
|
|
|
^^float g_floats
|
|
|
|
sub func() {
|
|
^^float l_floats
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("pointer comparisons against a value") {
|
|
val src="""
|
|
%option enable_floats
|
|
%zeropage basicsafe
|
|
|
|
main {
|
|
struct Node {
|
|
bool flag
|
|
}
|
|
|
|
^^ubyte @shared ptr1
|
|
^^bool @shared ptr2
|
|
^^uword @shared ptr3
|
|
^^float @shared ptr4
|
|
^^Node @shared ptr5
|
|
|
|
sub start() {
|
|
bool bb1,bb2
|
|
|
|
bb1 = ptr1 == 5000
|
|
bb2 = ptr1 != 5000
|
|
bb1 = ptr2 == 5000
|
|
bb2 = ptr2 != 5000
|
|
bb1 = ptr3 == 5000
|
|
bb2 = ptr3 != 5000
|
|
bb1 = ptr4 == 5000
|
|
bb2 = ptr4 != 5000
|
|
bb1 = ptr5 == 5000
|
|
bb2 = ptr5 != 5000
|
|
|
|
bb1 = ptr1 < 5000
|
|
bb2 = ptr1 > 5000
|
|
bb1 = ptr2 < 5000
|
|
bb2 = ptr2 > 5000
|
|
bb1 = ptr3 < 5000
|
|
bb2 = ptr3 > 5000
|
|
bb1 = ptr4 < 5000
|
|
bb2 = ptr4 > 5000
|
|
bb1 = ptr5 < 5000
|
|
bb2 = ptr5 > 5000
|
|
|
|
bb1 = ptr1 == cx16.r0
|
|
bb2 = ptr1 != cx16.r0
|
|
bb1 = ptr2 == cx16.r0
|
|
bb2 = ptr2 != cx16.r0
|
|
bb1 = ptr3 == cx16.r0
|
|
bb2 = ptr3 != cx16.r0
|
|
bb1 = ptr4 == cx16.r0
|
|
bb2 = ptr4 != cx16.r0
|
|
bb1 = ptr5 == cx16.r0
|
|
bb2 = ptr5 != cx16.r0
|
|
|
|
bb1 = ptr1 < cx16.r0
|
|
bb2 = ptr1 > cx16.r0
|
|
bb1 = ptr2 < cx16.r0
|
|
bb2 = ptr2 > cx16.r0
|
|
bb1 = ptr3 < cx16.r0
|
|
bb2 = ptr3 > cx16.r0
|
|
bb1 = ptr4 < cx16.r0
|
|
bb2 = ptr4 > cx16.r0
|
|
bb1 = ptr5 < cx16.r0
|
|
bb2 = ptr5 > cx16.r0
|
|
|
|
bb1 = ptr1 == cx16.r0+10
|
|
bb2 = ptr1 != cx16.r0+10
|
|
bb1 = ptr2 == cx16.r0+10
|
|
bb2 = ptr2 != cx16.r0+10
|
|
bb1 = ptr3 == cx16.r0+10
|
|
bb2 = ptr3 != cx16.r0+10
|
|
bb1 = ptr4 == cx16.r0+10
|
|
bb2 = ptr4 != cx16.r0+10
|
|
bb1 = ptr5 == cx16.r0+10
|
|
bb2 = ptr5 != cx16.r0+10
|
|
|
|
bb1 = ptr1 < cx16.r0+10
|
|
bb2 = ptr1 > cx16.r0+10
|
|
bb1 = ptr2 < cx16.r0+10
|
|
bb2 = ptr2 > cx16.r0+10
|
|
bb1 = ptr3 < cx16.r0+10
|
|
bb2 = ptr3 > cx16.r0+10
|
|
bb1 = ptr4 < cx16.r0+10
|
|
bb2 = ptr4 > cx16.r0+10
|
|
bb1 = ptr5 < cx16.r0+10
|
|
bb2 = ptr5 > cx16.r0+10
|
|
|
|
if ptr1 == 5000 cx16.r0++
|
|
if ptr1 != 5000 cx16.r0++
|
|
if ptr3 == 5000 cx16.r0++
|
|
if ptr3 != 5000 cx16.r0++
|
|
if ptr1 > 5000 cx16.r0++
|
|
if ptr3 > 5000 cx16.r0++
|
|
|
|
if ptr1 == cx16.r0 cx16.r0++
|
|
if ptr1 != cx16.r0 cx16.r0++
|
|
if ptr3 == cx16.r0 cx16.r0++
|
|
if ptr3 != cx16.r0 cx16.r0++
|
|
if ptr1 > cx16.r0 cx16.r0++
|
|
if ptr3 > cx16.r0 cx16.r0++
|
|
|
|
if ptr1 == cx16.r0+10 cx16.r0++
|
|
if ptr1 != cx16.r0+10 cx16.r0++
|
|
if ptr3 == cx16.r0+10 cx16.r0++
|
|
if ptr3 != cx16.r0+10 cx16.r0++
|
|
if ptr1 > cx16.r0+10 cx16.r0++
|
|
if ptr3 > cx16.r0+10 cx16.r0++
|
|
}
|
|
}"""
|
|
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
|
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
|
}
|
|
|
|
test("struct and pointer aliasing") {
|
|
val src="""
|
|
main {
|
|
sub start() {
|
|
alias1()
|
|
alias2()
|
|
alias3()
|
|
alias4()
|
|
alias5()
|
|
}
|
|
|
|
sub alias1() {
|
|
alias TheNode = structdefs.Node
|
|
^^TheNode node = 20000
|
|
node.value = 100
|
|
}
|
|
|
|
sub alias2() {
|
|
^^structdefs.Node node = 20000
|
|
alias thing = node
|
|
thing.value=200
|
|
}
|
|
|
|
sub alias3() {
|
|
alias TheNode = structdefs.Node
|
|
^^TheNode node = 20000
|
|
node++
|
|
}
|
|
|
|
sub alias4() {
|
|
alias currentElement = structdefs.element
|
|
currentElement = 20000
|
|
|
|
; all 3 should be the same:
|
|
structdefs.element.value = 42
|
|
currentElement.value = 42
|
|
currentElement^^.value = 42
|
|
|
|
; all 3 should be the same:
|
|
structdefs.element.value2 = 4242
|
|
currentElement.value2 = 4242
|
|
currentElement^^.value2 = 4242
|
|
|
|
cx16.r0 = currentElement^^.value2
|
|
cx16.r1 = currentElement.value2
|
|
}
|
|
|
|
sub alias5() {
|
|
alias nid = structdefs.element.value
|
|
nid++
|
|
}
|
|
}
|
|
|
|
structdefs {
|
|
struct Node {
|
|
ubyte value
|
|
uword value2
|
|
}
|
|
|
|
^^Node @shared element
|
|
}
|
|
"""
|
|
|
|
val result = compileText(VMTarget(), false, src, outputDir)!!
|
|
val st = result.compilerAst.allBlocks.single{it.name == "main"}
|
|
val alias4 = (st.statements[4] as Subroutine).statements
|
|
alias4.size shouldBe 10
|
|
val a1t = (alias4[1] as Assignment).target
|
|
val a2t = (alias4[2] as Assignment).target
|
|
val a3t = (alias4[3] as Assignment).target
|
|
val a4t = (alias4[4] as Assignment).target
|
|
val a5t = (alias4[5] as Assignment).target
|
|
val a6t = (alias4[6] as Assignment).target
|
|
a1t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value")
|
|
a2t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value")
|
|
a3t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value")
|
|
a4t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value2")
|
|
a5t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value2")
|
|
a6t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value2")
|
|
}
|
|
}) |