From ac5f45d2d4c5aa2dd3600e30ece17d0f6a1f118a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 27 Oct 2021 02:30:58 +0200 Subject: [PATCH] fix nested label lookups in anon scopes (partly) --- .../astprocessing/AstIdentifiersChecker.kt | 2 +- compiler/test/TestScoping.kt | 112 ++++++++++++++++++ compiler/test/TestSubroutines.kt | 30 ----- compilerAst/src/prog8/ast/AstToplevel.kt | 84 ++++++++++--- compilerAst/test/TestProg8Parser.kt | 31 +++++ examples/test.p8 | 30 +++-- 6 files changed, 232 insertions(+), 57 deletions(-) create mode 100644 compiler/test/TestScoping.kt diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index 4e4657ec5..caa12e98b 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -75,7 +75,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e val paramNames = subroutine.parameters.map { it.name }.toSet() val paramsToCheck = paramNames.intersect(namesInSub) for(name in paramsToCheck) { - val labelOrVar = subroutine.getLabelOrVariable(name) + val labelOrVar = subroutine.searchLabelOrVariableNotSubscoped(name) if(labelOrVar!=null && labelOrVar.position != subroutine.position) nameError(name, labelOrVar.position, subroutine) val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name} diff --git a/compiler/test/TestScoping.kt b/compiler/test/TestScoping.kt new file mode 100644 index 000000000..08901e109 --- /dev/null +++ b/compiler/test/TestScoping.kt @@ -0,0 +1,112 @@ +package prog8tests + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import prog8.ast.expressions.NumericLiteralValue +import prog8.ast.statements.* +import prog8.compiler.target.C64Target +import prog8tests.helpers.assertSuccess +import prog8tests.helpers.compileText +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TestScoping { + + @Test + fun testAnonScopeVarsMovedIntoSubroutineScope() { + val src = """ + main { + sub start() { + repeat { + ubyte xx = 99 + xx++ + } + } + } + """ + + val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess() + val module = result.programAst.toplevelModule + val mainBlock = module.statements.single() as Block + val start = mainBlock.statements.single() as Subroutine + val repeatbody = start.statements.filterIsInstance().single().body + assertFalse(mainBlock.statements.any { it is VarDecl }, "no vars moved to main block") + val subroutineVars = start.statements.filterIsInstance() + assertEquals(1, subroutineVars.size, "var from repeat anonscope must be moved up to subroutine") + assertEquals("xx", subroutineVars[0].name) + assertFalse(repeatbody.statements.any { it is VarDecl }, "var should have been removed from repeat anonscope") + val initassign = repeatbody.statements[0] as? Assignment + assertEquals(listOf("xx"), initassign?.target?.identifier?.nameInSource, "vardecl in repeat should be replaced by init assignment") + assertEquals(99, (initassign?.value as? NumericLiteralValue)?.number?.toInt(), "vardecl in repeat should be replaced by init assignment") + assertTrue(repeatbody.statements[1] is PostIncrDecr) + } + + @Test + fun testLabelsWithAnonScopes() { + val src = """ + main { + sub start() { + uword addr + goto labeloutside + + if true { + if true { + addr = &iflabel + addr = &labelinside + addr = &labeloutside + addr = &main.start.nested.nestedlabel + ; addr = &nested.nestedlabel ; TODO should also work!! + goto labeloutside + goto iflabel + goto main.start.nested.nestedlabel + ; goto nested.nestedlabel ; TODO should also work!! + } + iflabel: + } + + repeat { + addr = &iflabel + addr = &labelinside + addr = &labeloutside + addr = &main.start.nested.nestedlabel + ; addr = &nested.nestedlabel ; TODO should also work!! + goto iflabel + goto labelinside + goto main.start.nested.nestedlabel + ; goto nested.nestedlabel ; TODO should also work!! + 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 + ; addr = &nested.nestedlabel ; TODO should also work!! + goto main.start.nested.nestedlabel + ; goto nested.nestedlabel ; TODO should also work!! + } + } + """ + + val result = compileText(C64Target, false, src, writeAssembly = true).assertSuccess() + val module = result.programAst.toplevelModule + val mainBlock = module.statements.single() as Block + val start = mainBlock.statements.single() as Subroutine + val labels = start.statements.filterIsInstance