package prog8tests.compiler import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe 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.ParentSentinel import prog8.ast.expressions.NumericLiteral import prog8.ast.statements.* import prog8.code.target.C64Target import prog8tests.helpers.ErrorReporterForTests 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)!! val module = result.compilerAst.toplevelModule module.parent shouldBe instanceOf() module.program shouldBeSameInstanceAs result.compilerAst module.parent.parent shouldBe instanceOf() } test("anon scope vars moved into subroutine scope") { val src = """ main { sub start() { repeat 10 { ubyte xx = 99 rol(xx) } } } """ val result = compileText(C64Target(), false, src, writeAssembly = false)!! val module = result.compilerAst.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? NumericLiteral)?.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 10 { 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)!! val module = result.compilerAst.toplevelModule val mainBlock = module.statements.single() as Block val start = mainBlock.statements.single() as Subroutine val labels = start.statements.filterIsInstance