mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 00:31:56 +00:00
add unit test for @dirty variables
This commit is contained in:
parent
64164c1c72
commit
3ba1d00a7c
@ -19,28 +19,10 @@ import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
class TestVariousCompilerAst: FunSpec({
|
||||
test("symbol names in inline assembly blocks") {
|
||||
val names1 = InlineAssembly("""
|
||||
|
||||
""", false, Position.DUMMY).names
|
||||
names1 shouldBe emptySet()
|
||||
context("arrays") {
|
||||
|
||||
val names2 = InlineAssembly("""
|
||||
label: lda #<value
|
||||
sta ${'$'}ea
|
||||
sta 123
|
||||
label2:
|
||||
sta othervalue ; but not these in the comments
|
||||
; also not these
|
||||
;; ...or these
|
||||
// valid words 123456
|
||||
""", false, Position.DUMMY).names
|
||||
|
||||
names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words")
|
||||
}
|
||||
|
||||
test("array literals") {
|
||||
val text="""
|
||||
test("array literals") {
|
||||
val text="""
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -57,38 +39,138 @@ main {
|
||||
uword[] @shared addresses5 = [1111, 2222]
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("array init size mismatch error") {
|
||||
val text="""
|
||||
test("array init size mismatch error") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[10] uba = [1,2,3]
|
||||
bool[10] bba = [true, false, true]
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "size mismatch"
|
||||
errors.errors[1] shouldContain "size mismatch"
|
||||
}
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "size mismatch"
|
||||
errors.errors[1] shouldContain "size mismatch"
|
||||
}
|
||||
|
||||
test("invalid && operator") {
|
||||
val text="""
|
||||
test("returning array as uword") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
uword b1
|
||||
uword b2
|
||||
uword b3 = b1 && b2 ; invalid syntax: '&&' is not an operator, 'and' should be used instead
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = false) shouldBe null
|
||||
cx16.r0 = getarray()
|
||||
}
|
||||
|
||||
test("string comparisons") {
|
||||
val src="""
|
||||
sub getarray() -> uword {
|
||||
return [11,22,33]
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("split arrays back to normal when address is taken") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0L=0
|
||||
if cx16.r0L==0 {
|
||||
uword[] addresses = [scores2, start]
|
||||
uword[] @split scores1 = [10, 25, 50, 100]
|
||||
uword[] @split scores2 = [100, 250, 500, 1000]
|
||||
|
||||
cx16.r0 = &scores1
|
||||
cx16.r1 = &scores2
|
||||
cx16.r2 = &addresses
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null
|
||||
errors.errors.size shouldBe 0
|
||||
errors.warnings.size shouldBe 2
|
||||
errors.warnings[0] shouldContain("address")
|
||||
errors.warnings[1] shouldContain("address")
|
||||
errors.warnings[0] shouldContain("split")
|
||||
errors.warnings[1] shouldContain("split")
|
||||
}
|
||||
}
|
||||
|
||||
context("alias") {
|
||||
test("aliases ok") {
|
||||
val src="""
|
||||
main {
|
||||
alias print = txt.print
|
||||
alias width = txt.DEFAULT_WIDTH
|
||||
|
||||
sub start() {
|
||||
alias print2 = txt.print
|
||||
alias width2 = txt.DEFAULT_WIDTH
|
||||
print("one")
|
||||
print2("two")
|
||||
txt.print_ub(width)
|
||||
txt.print_ub(width2)
|
||||
|
||||
; chained aliases
|
||||
alias chained = print2
|
||||
chained("chained")
|
||||
}
|
||||
}
|
||||
|
||||
txt {
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
sub print_ub(ubyte value) {
|
||||
; nothing
|
||||
}
|
||||
sub print(str msg) {
|
||||
; nothing
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("wrong alias gives correct error") {
|
||||
val src="""
|
||||
main {
|
||||
alias print = txt.print2222
|
||||
alias width = txt.DEFAULT_WIDTH
|
||||
|
||||
sub start() {
|
||||
alias print2 = txt.print
|
||||
alias width2 = txt.DEFAULT_WIDTH_XXX
|
||||
print("one")
|
||||
print2("two")
|
||||
txt.print_ub(width)
|
||||
txt.print_ub(width2)
|
||||
}
|
||||
}
|
||||
|
||||
txt {
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
sub print_ub(ubyte value) {
|
||||
; nothing
|
||||
}
|
||||
sub print(str msg) {
|
||||
; nothing
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "undefined symbol: txt.print2222"
|
||||
errors.errors[1] shouldContain "undefined symbol: txt.DEFAULT_WIDTH_XXX"
|
||||
}
|
||||
}
|
||||
|
||||
context("strings") {
|
||||
test("string comparisons") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
@ -118,16 +200,16 @@ main {
|
||||
return 0
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 17
|
||||
val result2 = compileText(VMTarget(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts2 = result2.compilerAst.entrypoint.statements
|
||||
stmts2.size shouldBe 17
|
||||
}
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 17
|
||||
val result2 = compileText(VMTarget(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts2 = result2.compilerAst.entrypoint.statements
|
||||
stmts2.size shouldBe 17
|
||||
}
|
||||
|
||||
test("string concatenation and repeats") {
|
||||
val src="""
|
||||
test("string concatenation and repeats") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
str @shared name = "part1" + "part2"
|
||||
@ -137,19 +219,276 @@ main {
|
||||
rept = "xyz" * (times+1)
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
val name1 = stmts[0] as VarDecl
|
||||
val rept1 = stmts[1] as VarDecl
|
||||
(name1.value as StringLiteral).value shouldBe "part1part2"
|
||||
(rept1.value as StringLiteral).value shouldBe "reprepreprep"
|
||||
val name2strcopy = stmts[3] as IFunctionCall
|
||||
val rept2strcopy = stmts[4] as IFunctionCall
|
||||
val name2 = name2strcopy.args.first() as IdentifierReference
|
||||
val rept2 = rept2strcopy.args.first() as IdentifierReference
|
||||
(name2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xx1xx2"
|
||||
(rept2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 6
|
||||
val name1 = stmts[0] as VarDecl
|
||||
val rept1 = stmts[1] as VarDecl
|
||||
(name1.value as StringLiteral).value shouldBe "part1part2"
|
||||
(rept1.value as StringLiteral).value shouldBe "reprepreprep"
|
||||
val name2strcopy = stmts[3] as IFunctionCall
|
||||
val rept2strcopy = stmts[4] as IFunctionCall
|
||||
val name2 = name2strcopy.args.first() as IdentifierReference
|
||||
val rept2 = rept2strcopy.args.first() as IdentifierReference
|
||||
(name2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xx1xx2"
|
||||
(rept2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
|
||||
}
|
||||
|
||||
test("char as str param is error") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
print('@')
|
||||
}
|
||||
|
||||
sub print(str message) {
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.single() shouldContain "cannot use byte value"
|
||||
}
|
||||
}
|
||||
|
||||
context("return value") {
|
||||
test("missing return value is a syntax error") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = runit1()
|
||||
cx16.r1 = runit2()
|
||||
}
|
||||
|
||||
sub runit1() -> uword {
|
||||
repeat {
|
||||
cx16.r0++
|
||||
goto runit1
|
||||
}
|
||||
}
|
||||
|
||||
sub runit2() -> uword {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "has result value"
|
||||
errors.errors[1] shouldContain "has result value"
|
||||
}
|
||||
|
||||
test("missing return value is not a syntax error if there's an external goto") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = runit1()
|
||||
runit2()
|
||||
}
|
||||
|
||||
sub runit1() -> uword {
|
||||
repeat {
|
||||
cx16.r0++
|
||||
goto runit2
|
||||
}
|
||||
}
|
||||
|
||||
sub runit2() {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
}
|
||||
|
||||
context("variable declarations") {
|
||||
test("multi-var decls in scope with initializer") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte w
|
||||
|
||||
for w in 0 to 20 {
|
||||
ubyte @zp x,y,z=13
|
||||
ubyte q,r,s
|
||||
x++
|
||||
y++
|
||||
z++
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val result = compileText(VMTarget(), optimize = false, src, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
/*
|
||||
sub start () {
|
||||
ubyte s
|
||||
s = 0
|
||||
ubyte r
|
||||
r = 0
|
||||
ubyte q
|
||||
q = 0
|
||||
ubyte @zp z
|
||||
ubyte @zp y
|
||||
ubyte @zp x
|
||||
ubyte w
|
||||
for w in 0 to 20 step 1 {
|
||||
z = 13
|
||||
y = 13
|
||||
x = 13
|
||||
x++
|
||||
y++
|
||||
z++
|
||||
}
|
||||
}
|
||||
*/
|
||||
val vars = st.filterIsInstance<VarDecl>()
|
||||
vars.size shouldBe 7
|
||||
vars.all { it.names.size<=1 } shouldBe true
|
||||
vars.map { it.name }.toSet() shouldBe setOf("s","r","q","z","y","x","w")
|
||||
val forloop = st.single { it is ForLoop } as ForLoop
|
||||
forloop.body.statements[0] shouldBe instanceOf<Assignment>()
|
||||
forloop.body.statements[1] shouldBe instanceOf<Assignment>()
|
||||
forloop.body.statements[2] shouldBe instanceOf<Assignment>()
|
||||
}
|
||||
|
||||
test("multi vardecls smart desugaring") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared x,y,z
|
||||
ubyte @shared k,l,m = 42
|
||||
uword @shared r,s,t = sys.progend()
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), optimize=true, src, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 18
|
||||
st[0] shouldBe instanceOf<VarDecl>() // x
|
||||
st[2] shouldBe instanceOf<VarDecl>() // y
|
||||
st[4] shouldBe instanceOf<VarDecl>() // z
|
||||
st[6] shouldBe instanceOf<VarDecl>() // k
|
||||
st[8] shouldBe instanceOf<VarDecl>() // l
|
||||
st[10] shouldBe instanceOf<VarDecl>() // m
|
||||
st[12] shouldBe instanceOf<VarDecl>() // r
|
||||
st[14] shouldBe instanceOf<VarDecl>() // s
|
||||
st[16] shouldBe instanceOf<VarDecl>() // t
|
||||
val valX = (st[1] as Assignment).value
|
||||
(valX as NumericLiteral).number shouldBe 0.0
|
||||
val valY = (st[3] as Assignment).value
|
||||
(valY as NumericLiteral).number shouldBe 0.0
|
||||
val valZ = (st[5] as Assignment).value
|
||||
(valZ as NumericLiteral).number shouldBe 0.0
|
||||
val valK = (st[7] as Assignment).value
|
||||
(valK as NumericLiteral).number shouldBe 42.0
|
||||
val valL = (st[9] as Assignment).value
|
||||
(valL as NumericLiteral).number shouldBe 42.0
|
||||
val valM = (st[11] as Assignment).value
|
||||
(valM as NumericLiteral).number shouldBe 42.0
|
||||
val valR = (st[13] as Assignment).value
|
||||
(valR as FunctionCallExpression).target.nameInSource shouldBe listOf("sys", "progend")
|
||||
val valS = (st[15] as Assignment).value
|
||||
(valS as IdentifierReference).nameInSource shouldBe listOf("r")
|
||||
val valT = (st[17] as Assignment).value
|
||||
(valT as IdentifierReference).nameInSource shouldBe listOf("r")
|
||||
}
|
||||
|
||||
test("various multi var decl symbol lookups") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
uword @shared a,b
|
||||
b = a
|
||||
cx16.r1L = lsb(a)
|
||||
funcw(a)
|
||||
funcb(lsb(a))
|
||||
}
|
||||
|
||||
sub funcw(uword arg) {
|
||||
arg++
|
||||
}
|
||||
|
||||
sub funcb(ubyte arg) {
|
||||
arg++
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src) shouldNotBe null
|
||||
}
|
||||
|
||||
test("@dirty variables") {
|
||||
val src="""
|
||||
%import floats
|
||||
|
||||
main {
|
||||
uword @shared @dirty globw
|
||||
uword @shared globwi = 4444
|
||||
float @shared @dirty globf
|
||||
float @shared globfi = 4
|
||||
ubyte[5] @shared @dirty globarr1
|
||||
ubyte[] @shared globarr2 = [11,22,33,44,55]
|
||||
|
||||
sub start() {
|
||||
uword @shared @dirty locw
|
||||
uword @shared locwi = 4444
|
||||
float @shared @dirty locf
|
||||
float @shared locfi = 4.0
|
||||
ubyte[5] @shared @dirty locarr1
|
||||
ubyte[] @shared locarr2 = [11,22,33,44,55]
|
||||
|
||||
sys.clear_carry()
|
||||
}
|
||||
}"""
|
||||
|
||||
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||
val result = compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors)!!
|
||||
errors.errors.size shouldBe 0
|
||||
errors.infos.size shouldBe 6
|
||||
errors.infos.all { "dirty variable" in it } shouldBe true
|
||||
val start = result.compilerAst.entrypoint
|
||||
val st = start.statements
|
||||
st.size shouldBe 9
|
||||
val assignments = st.filterIsInstance<Assignment>()
|
||||
assignments.size shouldBe 2
|
||||
assignments[0].target.identifier?.nameInSource shouldBe listOf("locwi")
|
||||
assignments[1].target.identifier?.nameInSource shouldBe listOf("locfi")
|
||||
val blockst = start.definingBlock.statements
|
||||
blockst.size shouldBe(9)
|
||||
val blockassignments = blockst.filterIsInstance<Assignment>()
|
||||
blockassignments.size shouldBe 2
|
||||
blockassignments[0].target.identifier?.nameInSource shouldBe listOf("globwi")
|
||||
blockassignments[1].target.identifier?.nameInSource shouldBe listOf("globfi")
|
||||
}
|
||||
}
|
||||
|
||||
context("various") {
|
||||
test("symbol names in inline assembly blocks") {
|
||||
val names1 = InlineAssembly("""
|
||||
|
||||
""", false, Position.DUMMY).names
|
||||
names1 shouldBe emptySet()
|
||||
|
||||
val names2 = InlineAssembly("""
|
||||
label: lda #<value
|
||||
sta ${'$'}ea
|
||||
sta 123
|
||||
label2:
|
||||
sta othervalue ; but not these in the comments
|
||||
; also not these
|
||||
;; ...or these
|
||||
// valid words 123456
|
||||
""", false, Position.DUMMY).names
|
||||
|
||||
names2 shouldBe setOf("label", "lda", "sta", "ea", "value", "label2", "othervalue", "valid", "words")
|
||||
}
|
||||
|
||||
test("invalid && operator") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
uword b1
|
||||
uword b2
|
||||
uword b3 = b1 && b2 ; invalid syntax: '&&' is not an operator, 'and' should be used instead
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = false) shouldBe null
|
||||
}
|
||||
|
||||
test("pointervariable indexing allowed with >255") {
|
||||
@ -324,20 +663,6 @@ other {
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("returning array as uword") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = getarray()
|
||||
}
|
||||
|
||||
sub getarray() -> uword {
|
||||
return [11,22,33]
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("when on booleans") {
|
||||
val src = """
|
||||
main
|
||||
@ -358,21 +683,6 @@ main
|
||||
errors.errors[0] shouldContain "use if"
|
||||
}
|
||||
|
||||
test("char as str param is error") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
print('@')
|
||||
}
|
||||
|
||||
sub print(str message) {
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.single() shouldContain "cannot use byte value"
|
||||
}
|
||||
|
||||
test("sizeof number const evaluation in vardecl") {
|
||||
val src="""
|
||||
main {
|
||||
@ -384,55 +694,6 @@ main {
|
||||
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("multi-var decls in scope with initializer") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte w
|
||||
|
||||
for w in 0 to 20 {
|
||||
ubyte @zp x,y,z=13
|
||||
ubyte q,r,s
|
||||
x++
|
||||
y++
|
||||
z++
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val result = compileText(VMTarget(), optimize = false, src, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
/*
|
||||
sub start () {
|
||||
ubyte s
|
||||
s = 0
|
||||
ubyte r
|
||||
r = 0
|
||||
ubyte q
|
||||
q = 0
|
||||
ubyte @zp z
|
||||
ubyte @zp y
|
||||
ubyte @zp x
|
||||
ubyte w
|
||||
for w in 0 to 20 step 1 {
|
||||
z = 13
|
||||
y = 13
|
||||
x = 13
|
||||
x++
|
||||
y++
|
||||
z++
|
||||
}
|
||||
}
|
||||
*/
|
||||
val vars = st.filterIsInstance<VarDecl>()
|
||||
vars.size shouldBe 7
|
||||
vars.all { it.names.size<=1 } shouldBe true
|
||||
vars.map { it.name }.toSet() shouldBe setOf("s","r","q","z","y","x","w")
|
||||
val forloop = st.single { it is ForLoop } as ForLoop
|
||||
forloop.body.statements[0] shouldBe instanceOf<Assignment>()
|
||||
forloop.body.statements[1] shouldBe instanceOf<Assignment>()
|
||||
forloop.body.statements[2] shouldBe instanceOf<Assignment>()
|
||||
}
|
||||
|
||||
test("'not in' operator parsing") {
|
||||
val src="""
|
||||
main {
|
||||
@ -552,69 +813,6 @@ main {
|
||||
tc.expression shouldBe instanceOf<ArrayIndexedExpression>()
|
||||
}
|
||||
|
||||
test("multi vardecls smart desugaring") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared x,y,z
|
||||
ubyte @shared k,l,m = 42
|
||||
uword @shared r,s,t = sys.progend()
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), optimize=true, src, writeAssembly=false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 18
|
||||
st[0] shouldBe instanceOf<VarDecl>() // x
|
||||
st[2] shouldBe instanceOf<VarDecl>() // y
|
||||
st[4] shouldBe instanceOf<VarDecl>() // z
|
||||
st[6] shouldBe instanceOf<VarDecl>() // k
|
||||
st[8] shouldBe instanceOf<VarDecl>() // l
|
||||
st[10] shouldBe instanceOf<VarDecl>() // m
|
||||
st[12] shouldBe instanceOf<VarDecl>() // r
|
||||
st[14] shouldBe instanceOf<VarDecl>() // s
|
||||
st[16] shouldBe instanceOf<VarDecl>() // t
|
||||
val valX = (st[1] as Assignment).value
|
||||
(valX as NumericLiteral).number shouldBe 0.0
|
||||
val valY = (st[3] as Assignment).value
|
||||
(valY as NumericLiteral).number shouldBe 0.0
|
||||
val valZ = (st[5] as Assignment).value
|
||||
(valZ as NumericLiteral).number shouldBe 0.0
|
||||
val valK = (st[7] as Assignment).value
|
||||
(valK as NumericLiteral).number shouldBe 42.0
|
||||
val valL = (st[9] as Assignment).value
|
||||
(valL as NumericLiteral).number shouldBe 42.0
|
||||
val valM = (st[11] as Assignment).value
|
||||
(valM as NumericLiteral).number shouldBe 42.0
|
||||
val valR = (st[13] as Assignment).value
|
||||
(valR as FunctionCallExpression).target.nameInSource shouldBe listOf("sys", "progend")
|
||||
val valS = (st[15] as Assignment).value
|
||||
(valS as IdentifierReference).nameInSource shouldBe listOf("r")
|
||||
val valT = (st[17] as Assignment).value
|
||||
(valT as IdentifierReference).nameInSource shouldBe listOf("r")
|
||||
}
|
||||
|
||||
test("various multi var decl symbol lookups") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
uword @shared a,b
|
||||
b = a
|
||||
cx16.r1L = lsb(a)
|
||||
funcw(a)
|
||||
funcb(lsb(a))
|
||||
}
|
||||
|
||||
sub funcw(uword arg) {
|
||||
arg++
|
||||
}
|
||||
|
||||
sub funcb(ubyte arg) {
|
||||
arg++
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src) shouldNotBe null
|
||||
}
|
||||
|
||||
test("void assignment is invalid") {
|
||||
val src="""
|
||||
main {
|
||||
@ -640,54 +838,6 @@ main {
|
||||
errors.errors[2] shouldEndWith "cannot assign to 'void', perhaps a void function call was intended"
|
||||
}
|
||||
|
||||
test("missing return value is a syntax error") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = runit1()
|
||||
cx16.r1 = runit2()
|
||||
}
|
||||
|
||||
sub runit1() -> uword {
|
||||
repeat {
|
||||
cx16.r0++
|
||||
goto runit1
|
||||
}
|
||||
}
|
||||
|
||||
sub runit2() -> uword {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "has result value"
|
||||
errors.errors[1] shouldContain "has result value"
|
||||
}
|
||||
|
||||
test("missing return value is not a syntax error if there's an external goto") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = runit1()
|
||||
runit2()
|
||||
}
|
||||
|
||||
sub runit1() -> uword {
|
||||
repeat {
|
||||
cx16.r0++
|
||||
goto runit2
|
||||
}
|
||||
}
|
||||
|
||||
sub runit2() {
|
||||
cx16.r0++
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("defer syntactic sugaring") {
|
||||
val src="""
|
||||
main {
|
||||
@ -727,99 +877,7 @@ main {
|
||||
val handler = sub.children[7] as PtSub
|
||||
handler.name shouldBe "p8s_prog8_invoke_defers"
|
||||
}
|
||||
|
||||
test("aliases ok") {
|
||||
val src="""
|
||||
main {
|
||||
alias print = txt.print
|
||||
alias width = txt.DEFAULT_WIDTH
|
||||
|
||||
sub start() {
|
||||
alias print2 = txt.print
|
||||
alias width2 = txt.DEFAULT_WIDTH
|
||||
print("one")
|
||||
print2("two")
|
||||
txt.print_ub(width)
|
||||
txt.print_ub(width2)
|
||||
|
||||
; chained aliases
|
||||
alias chained = print2
|
||||
chained("chained")
|
||||
}
|
||||
}
|
||||
|
||||
txt {
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
sub print_ub(ubyte value) {
|
||||
; nothing
|
||||
}
|
||||
sub print(str msg) {
|
||||
; nothing
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("wrong alias gives correct error") {
|
||||
val src="""
|
||||
main {
|
||||
alias print = txt.print2222
|
||||
alias width = txt.DEFAULT_WIDTH
|
||||
|
||||
sub start() {
|
||||
alias print2 = txt.print
|
||||
alias width2 = txt.DEFAULT_WIDTH_XXX
|
||||
print("one")
|
||||
print2("two")
|
||||
txt.print_ub(width)
|
||||
txt.print_ub(width2)
|
||||
}
|
||||
}
|
||||
|
||||
txt {
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
sub print_ub(ubyte value) {
|
||||
; nothing
|
||||
}
|
||||
sub print(str msg) {
|
||||
; nothing
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "undefined symbol: txt.print2222"
|
||||
errors.errors[1] shouldContain "undefined symbol: txt.DEFAULT_WIDTH_XXX"
|
||||
}
|
||||
|
||||
test("split arrays back to normal when address is taken") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0L=0
|
||||
if cx16.r0L==0 {
|
||||
uword[] addresses = [scores2, start]
|
||||
uword[] @split scores1 = [10, 25, 50, 100]
|
||||
uword[] @split scores2 = [100, 250, 500, 1000]
|
||||
|
||||
cx16.r0 = &scores1
|
||||
cx16.r1 = &scores2
|
||||
cx16.r2 = &addresses
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=true, errors=errors) shouldNotBe null
|
||||
errors.errors.size shouldBe 0
|
||||
errors.warnings.size shouldBe 2
|
||||
errors.warnings[0] shouldContain("address")
|
||||
errors.warnings[1] shouldContain("address")
|
||||
errors.warnings[0] shouldContain("split")
|
||||
errors.warnings[1] shouldContain("split")
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -81,7 +81,9 @@ Foreign function interface (external/ROM calls)
|
||||
-----------------------------------------------
|
||||
- You can use the ``extsub`` keyword to define the call signature of foreign functions (ROM routines or external routines elsewhere in RAM) in a natural way.
|
||||
Calling those generates code that is as efficient or even more efficient as calling regular subroutines.
|
||||
No additional stubs are needed. You can even specify the memory bank the routine is in and the compiler takes care of bank switching when calling it.
|
||||
No additional stubs are needed. Y
|
||||
- High level support of memory banking: an ``extsub`` can be defined with the memory bank number (constant or variable) where the routine is located in,
|
||||
and then when you call it as usual, the compiler takes care of the required bank switching.
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
|
@ -239,10 +239,16 @@ when assembling the rest of the code). Example::
|
||||
**uninitialized variables:**
|
||||
All variables will be initialized by prog8 at startup, they'll get their assigned initialization value, or be cleared to zero.
|
||||
This (re)initialization is also done on each subroutine entry for the variables declared in the subroutine.
|
||||
There may be certain scenarios where this initialization is redundant and/or where you want to avoid the overhead of it
|
||||
You can do so by using the ``@dirty`` tag on the variable declaration.
|
||||
This means that the variable will *not* be (re)initialized by Prog8 and that its value is undefined.
|
||||
You have to assign it a value yourself first, before using the variable. If you don't do that, the value can be anything, so beware.
|
||||
|
||||
There may be certain scenarios where this initialization is redundant and/or where you want to avoid the overhead of it.
|
||||
In some cases, Prog8 itself can detect that a variable doesn't need a separate automatic initialization to zero, if
|
||||
it's trivial that it is not being read between the variable's declaration and the first assignment. For instance, when
|
||||
you declare a variable immediately before a for loop where it is the loop variable. However Prog8 is not yet very smart
|
||||
at detecting these redundant initializations. If you want to be sure, check the generated assembly output.
|
||||
|
||||
In any case, you can use the ``@dirty`` tag on the variable declaration to make the variable *not* being (re)initialized by Prog8.
|
||||
This means its value will be undefined (it can be anything) until you assign a value yourself! Don't use such
|
||||
a variable before you have done so. 🦶🔫 Footgun warning.
|
||||
|
||||
|
||||
**memory alignment:**
|
||||
|
@ -1,28 +1,25 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- add unit tests for @dirty variables
|
||||
|
||||
|
||||
for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed.
|
||||
|
||||
regenerate symbol dump files
|
||||
|
||||
improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
|
||||
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
|
||||
Add a -library $xxxx command line option to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
|
||||
Need to add some way to generate a stable jump table at a given address.
|
||||
Why are blocks without an addr moved BEHIND a block with an address? That's done in the StatementReorderer.
|
||||
|
||||
|
||||
Improve register load order in subroutine call args assignments:
|
||||
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
|
||||
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
|
||||
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
- Why are blocks without an addr moved BEHIND a block with an address? That's done in the StatementReorderer.
|
||||
- Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
|
||||
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
|
||||
Add a -library $xxxx command line option to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?
|
||||
Need to add some way to generate a stable jump table at a given address.
|
||||
- Improve register load order in subroutine call args assignments:
|
||||
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
|
||||
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
|
||||
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
|
||||
- remove 'extsub' as a recognised alternative for 'extsub'
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast , get rid of the statements there -> expressions that have Void data type
|
||||
|
Loading…
Reference in New Issue
Block a user