From 743c8b44a25921672240516fdbf67b734a9277c1 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 26 Oct 2021 19:10:48 +0200 Subject: [PATCH] AnonymousScope refactor: it's no longer a INameScope because it doesn't contain scoped variables (these are moved to the subroutine's scope) --- .../compiler/BeforeAsmGenerationAstChanger.kt | 37 +--- compiler/src/prog8/compiler/Compiler.kt | 1 + .../compiler/astprocessing/AstChecker.kt | 5 +- .../compiler/astprocessing/AstExtensions.kt | 8 + .../compiler/astprocessing/AstPreprocessor.kt | 48 +++++ .../compiler/astprocessing/VariousCleanups.kt | 13 +- .../compiler/target/cpu6502/codegen/AsmGen.kt | 12 +- .../src/prog8/optimizer/BinExprSplitter.kt | 4 +- compiler/src/prog8/optimizer/CallGraph.kt | 2 +- .../optimizer/ConstantIdentifierReplacer.kt | 27 +-- .../src/prog8/optimizer/StatementOptimizer.kt | 21 +- .../src/prog8/optimizer/UnusedCodeRemover.kt | 14 +- compilerAst/src/prog8/ast/AstToplevel.kt | 192 +++++++----------- .../src/prog8/ast/statements/AstStatements.kt | 31 ++- compilerAst/src/prog8/ast/walk/AstWalker.kt | 10 +- examples/test.p8 | 47 +---- 16 files changed, 208 insertions(+), 264 deletions(-) create mode 100644 compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index ad65fe714..f5e8d9655 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -1,11 +1,8 @@ package prog8.compiler -import prog8.ast.IFunctionCall -import prog8.ast.Node -import prog8.ast.Program +import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.internedStringsModuleName import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification @@ -25,7 +22,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // This allows you to restart the program and have the same starting values of the variables if(decl.allowInitializeWithZero) { - val nextAssign = decl.definingScope.nextSibling(decl) as? Assignment + val nextAssign = decl.nextSibling() as? Assignment if (nextAssign != null && nextAssign.target isSameAs IdentifierReference(listOf(decl.name), Position.DUMMY)) decl.value = null else { @@ -55,14 +52,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // use the other part of the expression to split. val assignRight = Assignment(assignment.target, binExpr.right, assignment.position) return listOf( - IAstModification.InsertBefore(assignment, assignRight, assignment.definingScope), + IAstModification.InsertBefore(assignment, assignRight, parent as IStatementContainer), IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr), IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr)) } } else { val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position) return listOf( - IAstModification.InsertBefore(assignment, assignLeft, assignment.definingScope), + IAstModification.InsertBefore(assignment, assignLeft, parent as IStatementContainer), IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr)) } } @@ -101,29 +98,11 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I } override fun after(scope: AnonymousScope, parent: Node): Iterable { + if(scope.statements.any { it is VarDecl || it is IStatementContainer }) + throw FatalAstException("anonymousscope may no longer contain any vardecls or subscopes") + val decls = scope.statements.filterIsInstance().filter { it.type == VarDeclType.VAR } subroutineVariables.addAll(decls.map { it.name to it }) - - val sub = scope.definingSubroutine - if (sub != null) { - // move any remaining vardecls of the scope into the upper scope. Make sure the position remains the same! - val replacements = mutableListOf() - val movements = mutableListOf() - - for(decl in decls) { - if(decl.value!=null && decl.datatype in NumericDatatypes) { - val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) - val assign = Assignment(target, decl.value!!, decl.position) - replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) - decl.value = null - decl.allowInitializeWithZero = false - } else { - replacements.add(IAstModification.Remove(decl, scope)) - } - movements.add(IAstModification.InsertFirst(decl, sub)) - } - return replacements + movements - } return noModifications } @@ -372,7 +351,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I // assign the indexing expression to the helper variable, but only if that hasn't been done already val target = AssignTarget(IdentifierReference(listOf("cx16", register), expr.indexer.position), null, null, expr.indexer.position) val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position) - modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope)) + modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer)) modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer)) return modifications } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index ad87d68a6..0492089ca 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -278,6 +278,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) { // perform initial syntax checks and processings println("Processing for target ${compilerOptions.compTarget.name}...") + programAst.preprocessAst() programAst.checkIdentifiers(errors, compilerOptions) errors.report() // TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 6dd78fcf3..06768adb2 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1,6 +1,7 @@ package prog8.compiler.astprocessing import prog8.ast.INameScope +import prog8.ast.IStatementContainer import prog8.ast.Module import prog8.ast.Program import prog8.ast.base.* @@ -196,7 +197,7 @@ internal class AstChecker(private val program: Program, is Label, is VarDecl, is InlineAssembly, - is INameScope, + is IStatementContainer, is NopStatement -> true else -> false } @@ -217,7 +218,7 @@ internal class AstChecker(private val program: Program, super.visit(label) } - private fun hasReturnOrJump(scope: INameScope): Boolean { + private fun hasReturnOrJump(scope: IStatementContainer): Boolean { class Searcher: IAstVisitor { var count=0 diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 8f9da912e..f3c7545a5 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -113,6 +113,14 @@ internal fun Program.verifyFunctionArgTypes() { fixer.visit(this) } +internal fun Program.preprocessAst() { + val transforms = AstPreprocessor() + transforms.visit(this) + var mods = transforms.applyModifications() + while(mods>0) + mods = transforms.applyModifications() +} + internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) { val checker2 = AstIdentifiersChecker(this, errors, options.compTarget) diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt new file mode 100644 index 000000000..34ae6a320 --- /dev/null +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -0,0 +1,48 @@ +package prog8.compiler.astprocessing + +import prog8.ast.Node +import prog8.ast.base.NumericDatatypes +import prog8.ast.base.VarDeclType +import prog8.ast.expressions.IdentifierReference +import prog8.ast.statements.AnonymousScope +import prog8.ast.statements.AssignTarget +import prog8.ast.statements.Assignment +import prog8.ast.statements.VarDecl +import prog8.ast.walk.AstWalker +import prog8.ast.walk.IAstModification + + +class AstPreprocessor : AstWalker() { + + override fun before(scope: AnonymousScope, parent: Node): Iterable { + + // move vardecls in Anonymous scope up to the containing subroutine + // and add initialization assignment in its place if needed + val vars = scope.statements.filterIsInstance() + if(vars.any() && scope.definingScope !== parent) { + val parentscope = scope.definingScope + val movements = mutableListOf() + val replacements = mutableListOf() + + for(decl in vars) { + if(decl.type != VarDeclType.VAR) { + movements.add(IAstModification.InsertFirst(decl, parentscope)) + replacements.add(IAstModification.Remove(decl, scope)) + } else { + if(decl.value!=null && decl.datatype in NumericDatatypes) { + val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) + val assign = Assignment(target, decl.value!!, decl.position) + replacements.add(IAstModification.ReplaceNode(decl, assign, scope)) + decl.value = null + decl.allowInitializeWithZero = false + } else { + replacements.add(IAstModification.Remove(decl, scope)) + } + movements.add(IAstModification.InsertFirst(decl, parentscope)) + } + } + return movements + replacements + } + return noModifications + } +} diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 688e29021..0ce7e88ce 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -1,9 +1,6 @@ package prog8.compiler.astprocessing -import prog8.ast.IFunctionCall -import prog8.ast.INameScope -import prog8.ast.Node -import prog8.ast.Program +import prog8.ast.* import prog8.ast.base.FatalAstException import prog8.ast.base.Position import prog8.ast.expressions.* @@ -16,17 +13,17 @@ import prog8.compiler.IErrorReporter internal class VariousCleanups(val program: Program, val errors: IErrorReporter): AstWalker() { override fun before(nopStatement: NopStatement, parent: Node): Iterable { - return listOf(IAstModification.Remove(nopStatement, parent as INameScope)) + return listOf(IAstModification.Remove(nopStatement, parent as IStatementContainer)) } override fun before(scope: AnonymousScope, parent: Node): Iterable { - return if(parent is INameScope) - listOf(ScopeFlatten(scope, parent as INameScope)) + return if(parent is IStatementContainer) + listOf(ScopeFlatten(scope, parent as IStatementContainer)) else noModifications } - class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification { + class ScopeFlatten(val scope: AnonymousScope, val into: IStatementContainer) : IAstModification { override fun perform() { val idx = into.statements.indexOf(scope) if(idx>=0) { diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index 2697adc9e..41879ba1a 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -20,7 +20,6 @@ import java.time.LocalDate import java.time.LocalDateTime import java.util.* import kotlin.io.path.Path -import kotlin.io.path.absolute import kotlin.math.absoluteValue @@ -49,7 +48,7 @@ internal class AsmGen(private val program: Program, internal val loopEndLabels = ArrayDeque() private val blockLevelVarInits = mutableMapOf>() internal val slabs = mutableMapOf() - internal val removals = mutableListOf>() + internal val removals = mutableListOf>() override fun compileToAssembly(): IAssemblyProgram { assemblyLines.clear() @@ -991,8 +990,7 @@ internal class AsmGen(private val program: Program, // if(!booleanCondition.left.isSimple || !booleanCondition.right.isSimple) // throw AssemblyError("both operands for if comparison expression should have been simplified") - if (stmt.elsepart.containsNoCodeNorVars) { - // empty else + if (stmt.elsepart.isEmpty()) { val endLabel = makeLabel("if_end") expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel) translate(stmt.truepart) @@ -1245,7 +1243,7 @@ $repeatLabel lda $counterVar } private fun translate(stmt: BranchStatement) { - if(stmt.truepart.containsNoCodeNorVars && stmt.elsepart.containsCodeOrVars) + if(stmt.truepart.isEmpty() && stmt.elsepart.isNotEmpty()) throw AssemblyError("only else part contains code, shoud have been switched already") val jump = stmt.truepart.statements.first() as? Jump @@ -1257,7 +1255,7 @@ $repeatLabel lda $counterVar } else { val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break - if(stmt.elsepart.containsNoCodeNorVars) { + if(stmt.elsepart.isEmpty()) { if(truePartIsJustBreak) { // branch with just a break (jump out of loop) val instruction = branchInstruction(stmt.condition, false) @@ -1310,7 +1308,7 @@ $repeatLabel lda $counterVar } inits.add(stmt) } else { - val next = (stmt.parent as INameScope).nextSibling(stmt) + val next = (stmt.parent as IStatementContainer).nextSibling(stmt) if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) { assignInitialValueToVar(stmt, listOf(stmt.name)) } diff --git a/compiler/src/prog8/optimizer/BinExprSplitter.kt b/compiler/src/prog8/optimizer/BinExprSplitter.kt index 4eada5f08..0eb519797 100644 --- a/compiler/src/prog8/optimizer/BinExprSplitter.kt +++ b/compiler/src/prog8/optimizer/BinExprSplitter.kt @@ -1,6 +1,6 @@ package prog8.optimizer -import prog8.ast.INameScope +import prog8.ast.IStatementContainer import prog8.ast.Node import prog8.ast.Program import prog8.ast.expressions.BinaryExpression @@ -65,7 +65,7 @@ X = BinExpr X = LeftExpr val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position) return listOf( IAstModification.ReplaceNode(binExpr, augExpr, assignment), - IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as INameScope) + IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer) ) } } diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index 12ede4ae7..f19197250 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -188,7 +188,7 @@ class CallGraph(private val program: Program) : IAstVisitor { inline fun unused(label: Label) = false // just always output labels - fun unused(stmt: ISymbolStatement): Boolean { + fun unused(stmt: INamedStatement): Boolean { return when(stmt) { is Subroutine -> unused(stmt) is Block -> unused(stmt) diff --git a/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt index fca8e6355..39fddf54a 100644 --- a/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -1,6 +1,5 @@ package prog8.optimizer -import prog8.ast.INameScope import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.* @@ -18,6 +17,10 @@ import prog8.compiler.target.ICompilationTarget internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() { override fun after(decl: VarDecl, parent: Node): Iterable { + + if(decl.parent is AnonymousScope) + throw FatalAstException("vardecl may no longer occur in anonymousscope") + try { val declConstValue = decl.value?.constValue(program) if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST) @@ -31,28 +34,6 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat errors.err(x.message, x.position) } - // move vardecl to the containing subroutine and add initialization assignment in its place if needed - if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) { - val subroutine = decl.definingSubroutine as? INameScope - if(subroutine!=null && subroutine!==parent) { - val declValue = decl.value - decl.value = null - decl.allowInitializeWithZero = false - return if (declValue == null) { - listOf( - IAstModification.Remove(decl, parent as INameScope), - IAstModification.InsertFirst(decl, subroutine) - ) - } else { - val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position) - val assign = Assignment(target, declValue, decl.position) - listOf( - IAstModification.ReplaceNode(decl, assign, parent), - IAstModification.InsertFirst(decl, subroutine) - ) - } - } - } return noModifications } diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index c2ce8452d..ecde494fb 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -1,9 +1,6 @@ package prog8.optimizer -import prog8.ast.IBuiltinFunctions -import prog8.ast.INameScope -import prog8.ast.Node -import prog8.ast.Program +import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.statements.* @@ -23,7 +20,7 @@ internal class StatementOptimizer(private val program: Program, private val functions: IBuiltinFunctions, private val compTarget: ICompilationTarget) : AstWalker() { - private val subsThatNeedReturnVariable = mutableSetOf>() + private val subsThatNeedReturnVariable = mutableSetOf>() override fun after(subroutine: Subroutine, parent: Node): Iterable { @@ -117,7 +114,7 @@ internal class StatementOptimizer(private val program: Program, functionCallStatement.void, pos ) return listOf( - IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope), + IAstModification.InsertBefore(functionCallStatement, chrout1, parent as IStatementContainer), IAstModification.ReplaceNode(functionCallStatement, chrout2, parent) ) } @@ -152,11 +149,11 @@ internal class StatementOptimizer(private val program: Program, override fun after(ifStatement: IfStatement, parent: Node): Iterable { // remove empty if statements - if(ifStatement.truepart.containsNoCodeNorVars && ifStatement.elsepart.containsNoCodeNorVars) + if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty()) return listOf(IAstModification.Remove(ifStatement, ifStatement.definingScope)) // empty true part? switch with the else part - if(ifStatement.truepart.containsNoCodeNorVars && ifStatement.elsepart.containsCodeOrVars) { + if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) { val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position) val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position) val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position) @@ -184,7 +181,7 @@ internal class StatementOptimizer(private val program: Program, } override fun after(forLoop: ForLoop, parent: Node): Iterable { - if(forLoop.body.containsNoCodeNorVars) { + if(forLoop.body.isEmpty()) { errors.warn("removing empty for loop", forLoop.position) return listOf(IAstModification.Remove(forLoop, forLoop.definingScope)) } else if(forLoop.body.statements.size==1) { @@ -277,7 +274,7 @@ internal class StatementOptimizer(private val program: Program, override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable { val iter = repeatLoop.iterations if(iter!=null) { - if(repeatLoop.body.containsNoCodeNorVars) { + if(repeatLoop.body.isEmpty()) { errors.warn("empty loop removed", repeatLoop.position) return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope)) } @@ -445,7 +442,7 @@ internal class StatementOptimizer(private val program: Program, val assign = Assignment(tgt, value, returnStmt.position) val returnReplacement = Return(returnValueIntermediary2, returnStmt.position) return listOf( - IAstModification.InsertBefore(returnStmt, assign, parent as INameScope), + IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer), IAstModification.ReplaceNode(returnStmt, returnReplacement, parent) ) } @@ -469,7 +466,7 @@ internal class StatementOptimizer(private val program: Program, return super.after(returnStmt, parent) } - private fun hasBreak(scope: INameScope): Boolean { + private fun hasBreak(scope: IStatementContainer): Boolean { class Searcher: IAstVisitor { diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index 45dbec5b3..15b67ab25 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -28,27 +28,27 @@ internal class UnusedCodeRemover(private val program: Program, } override fun before(breakStmt: Break, parent: Node): Iterable { - reportUnreachable(breakStmt, parent as INameScope) + reportUnreachable(breakStmt, parent as IStatementContainer) return emptyList() } override fun before(jump: Jump, parent: Node): Iterable { - reportUnreachable(jump, parent as INameScope) + reportUnreachable(jump, parent as IStatementContainer) return emptyList() } override fun before(returnStmt: Return, parent: Node): Iterable { - reportUnreachable(returnStmt, parent as INameScope) + reportUnreachable(returnStmt, parent as IStatementContainer) return emptyList() } override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable { if(functionCallStatement.target.nameInSource.last() == "exit") - reportUnreachable(functionCallStatement, parent as INameScope) + reportUnreachable(functionCallStatement, parent as IStatementContainer) return emptyList() } - private fun reportUnreachable(stmt: Statement, parent: INameScope) { + private fun reportUnreachable(stmt: Statement, parent: IStatementContainer) { when(val next = parent.nextSibling(stmt)) { null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {} else -> errors.warn("unreachable code", next.position) @@ -65,11 +65,11 @@ internal class UnusedCodeRemover(private val program: Program, if (block.containsNoCodeNorVars) { if(block.name != internedStringsModuleName) errors.warn("removing unused block '${block.name}'", block.position) - return listOf(IAstModification.Remove(block, parent as INameScope)) + return listOf(IAstModification.Remove(block, parent as IStatementContainer)) } if(callgraph.unused(block)) { errors.warn("removing unused block '${block.name}'", block.position) - return listOf(IAstModification.Remove(block, parent as INameScope)) + return listOf(IAstModification.Remove(block, parent as IStatementContainer)) } } diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 9144ab75a..015272ac0 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -21,117 +21,12 @@ interface IFunctionCall { var args: MutableList } -interface INameScope { - val name: String +interface IStatementContainer { val position: Position - val statements: MutableList val parent: Node - + val statements: MutableList fun linkParents(parent: Node) - fun subScope(name: String): INameScope? { - for(stmt in statements) { - when(stmt) { - // NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here! - is ForLoop -> if(stmt.body.name==name) return stmt.body - is UntilLoop -> if(stmt.body.name==name) return stmt.body - is WhileLoop -> if(stmt.body.name==name) return stmt.body - is BranchStatement -> { - if(stmt.truepart.name==name) return stmt.truepart - if(stmt.elsepart.containsCodeOrVars && stmt.elsepart.name==name) return stmt.elsepart - } - is IfStatement -> { - if(stmt.truepart.name==name) return stmt.truepart - if(stmt.elsepart.containsCodeOrVars && stmt.elsepart.name==name) return stmt.elsepart - } - is WhenStatement -> { - val scope = stmt.choices.firstOrNull { it.statements.name==name } - if(scope!=null) - return scope.statements - } - is INameScope -> if(stmt.name==name) return stmt - else -> {} - } - } - return null - } - - fun getLabelOrVariable(name: String): Statement? { - // this is called A LOT and could perhaps be optimized a bit more, - // but adding a memoization cache didn't make much of a practical runtime difference - for (stmt in statements) { - if (stmt is VarDecl && stmt.name==name) return stmt - if (stmt is Label && stmt.name==name) return stmt - if (stmt is AnonymousScope) { - val sub = stmt.getLabelOrVariable(name) - if(sub!=null) - return sub - } - } - return null - } - - val allDefinedSymbols: List> - get() { - return statements.mapNotNull { - when (it) { - is Label -> it.name to it - is VarDecl -> it.name to it - is Subroutine -> it.name to it - is Block -> it.name to it - else -> null - } - } - } - - fun lookup(scopedName: List, localContext: Node) : Statement? { - if(scopedName.size>1) { - // a scoped name refers to a name in another module. - // it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program) - for(module in localContext.definingModule.program.modules) { - var scope: INameScope? = module - for(name in scopedName.dropLast(1)) { - scope = scope?.subScope(name) - if(scope==null) - break - } - if(scope!=null) { - val result = scope.getLabelOrVariable(scopedName.last()) - if(result!=null) - return result - return scope.subScope(scopedName.last()) as Statement? - } - } - return null - } else { - // unqualified name - // special case: the do....until statement can also look INSIDE the anonymous scope - if(localContext.parent.parent is UntilLoop) { - val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.getLabelOrVariable(scopedName[0]) - if(symbolFromInnerScope!=null) - return symbolFromInnerScope - } - - // find the scope the localContext is in, look in that first - var statementScope = localContext - while(statementScope !is ParentSentinel) { - val localScope = statementScope.definingScope - val result = localScope.getLabelOrVariable(scopedName[0]) - if (result != null) - return result - val subscope = localScope.subScope(scopedName[0]) as Statement? - if (subscope != null) - return subscope - // not found in this scope, look one higher up - statementScope = statementScope.parent - } - return null - } - } - - val containsCodeOrVars get() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm" } - val containsNoCodeNorVars get() = !containsCodeOrVars - fun remove(stmt: Statement) { if(!statements.remove(stmt)) throw FatalAstException("stmt to remove wasn't found in scope") @@ -140,11 +35,11 @@ interface INameScope { fun getAllLabels(label: String): List