package prog8tests import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.types.instanceOf import io.kotest.matchers.types.shouldBeSameInstanceAs import prog8.ast.GlobalNamespace import prog8.ast.base.ParentSentinel import prog8.ast.expressions.NumericLiteralValue import prog8.ast.statements.* import prog8.codegen.target.C64Target import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.assertFailure import prog8tests.helpers.assertSuccess import prog8tests.helpers.compileText class TestScoping: FunSpec({ test("modules parent is global namespace") { val src = """ main { sub start() { } } """ val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess() val module = result.program.toplevelModule module.parent shouldBe instanceOf() module.program shouldBeSameInstanceAs result.program module.parent.parent shouldBe instanceOf() } test("anon scope vars moved into subroutine scope") { val src = """ main { sub start() { repeat { ubyte xx = 99 xx++ } } } """ val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess() val module = result.program.toplevelModule val mainBlock = module.statements.single() as Block val start = mainBlock.statements.single() as Subroutine val repeatbody = start.statements.filterIsInstance().single().body withClue("no vars moved to main block") { mainBlock.statements.any { it is VarDecl } shouldBe false } val subroutineVars = start.statements.filterIsInstance() withClue("var from repeat anonscope must be moved up to subroutine") { subroutineVars.size shouldBe 1 } subroutineVars[0].name shouldBe "xx" withClue("var should have been removed from repeat anonscope") { repeatbody.statements.any { it is VarDecl } shouldBe false } val initassign = repeatbody.statements[0] as? Assignment withClue("vardecl in repeat should be replaced by init assignment") { initassign?.target?.identifier?.nameInSource shouldBe listOf("xx") } withClue("vardecl in repeat should be replaced by init assignment") { (initassign?.value as? NumericLiteralValue)?.number?.toInt() shouldBe 99 } repeatbody.statements[1] shouldBe instanceOf() } test("labels with anon scopes") { val src = """ main { sub start() { uword addr goto labeloutside if true { if true { addr = &iflabel addr = &labelinside addr = &labeloutside addr = &main.start.nested.nestedlabel goto labeloutside goto iflabel goto main.start.nested.nestedlabel } iflabel: } repeat { addr = &iflabel addr = &labelinside addr = &labeloutside addr = &main.start.nested.nestedlabel goto iflabel goto labelinside goto main.start.nested.nestedlabel labelinside: } sub nested () { nestedlabel: addr = &nestedlabel goto nestedlabel goto main.start.nested.nestedlabel } labeloutside: addr = &iflabel addr = &labelinside addr = &labeloutside addr = &main.start.nested.nestedlabel goto main.start.nested.nestedlabel } } """ val result = compileText(C64Target(), false, src, writeAssembly = true).assertSuccess() val module = result.program.toplevelModule val mainBlock = module.statements.single() as Block val start = mainBlock.statements.single() as Subroutine val labels = start.statements.filterIsInstance