diff --git a/compiler/src/main/kotlin/ast/ast.kt b/compiler/src/main/kotlin/ast/ast.kt index 286f5ab..c9ec25a 100644 --- a/compiler/src/main/kotlin/ast/ast.kt +++ b/compiler/src/main/kotlin/ast/ast.kt @@ -364,7 +364,7 @@ class NeighbourhoodDecl( */ class FunctionArgument( ctx: SourceContext, - val ident: String, + var ident: String, val type: Type = UncheckedType ) : AST(ctx) diff --git a/compiler/src/main/kotlin/visitors/Uniqueify.kt b/compiler/src/main/kotlin/visitors/Uniqueify.kt new file mode 100644 index 0000000..5bc4c70 --- /dev/null +++ b/compiler/src/main/kotlin/visitors/Uniqueify.kt @@ -0,0 +1,114 @@ +package dk.aau.cs.d409f19.cellumata.visitors + +import com.ibm.icu.text.SymbolTable +import com.sun.xml.internal.fastinfoset.algorithm.BuiltInEncodingAlgorithm +import dk.aau.cs.d409f19.cellumata.ast.* +import dk.aau.cs.d409f19.cellumata.visitors.codegen.InvalidIdentLookupError +import java.util.Stack + +/** + * Makes all identifiers unique. This elements need keep track of scoping in some cases. + * + * Prerequisites: The ScopeChecker must be run prior to this phase. We to make sure that redeclaration doesn't occur. + */ +class Uniqueify(oldSymbolTable: Table, newSymbolTable: Table = Table()): BaseScopedASTVisitor() { + private var viewingSymbolTableSession = ViewingSymbolTableSession(oldSymbolTable) + private var creatingSymbolTabelSession = CreatingSymbolTableSession(newSymbolTable) + private val mappings: Stack> = Stack() + private var labelCounter = 0 + + override fun openScope() { + viewingSymbolTableSession.openScope() + creatingSymbolTabelSession.openScope() + } + + override fun closeScope() { + viewingSymbolTableSession.closeScope() + creatingSymbolTabelSession.closeScope() + } + + init { + mappings.push(mapOf()) // Global scope + } + + private fun nextLabel(): String { + return "label_${labelCounter++}" + } + + /** + * @param ident Identifier + * @return The mapped label for the identifier + */ + private fun addMapping(ident: String): String { + // Change the map object at the top of the stack + val label = nextLabel() + creatingSymbolTabelSession.insertSymbol( + label, + viewingSymbolTableSession.getSymbol(ident)!! + ) + mappings.push(mappings.pop() + Pair(ident, label)) + System.out.println("Uniqueify: mapped $ident to $label") // ToDo use a proper logging system + return label + } + + private fun getMappedLabel(ident: String): String { + for (scope in mappings.reversed()) { + if (scope.containsKey(ident)) { + val label = scope.getValue(ident) + System.out.println("Uniqueify: Found mapping from $ident to $label") + return label + } + } + + System.out.println("Uniqueify: No mapping for $ident") + throw InvalidIdentLookupError() + } + + fun getSymbolTable(): Table { + return creatingSymbolTabelSession.getRootTable() + } + + override fun visit(node: RootNode) { + node.body.forEach { + when (it) { + is ConstDecl -> it.ident = addMapping(it.ident) + is StateDecl -> it.ident = addMapping(it.ident) + is NeighbourhoodDecl -> it.ident = addMapping(it.ident) + is FuncDecl -> it.ident = addMapping(it.ident) + } + } + + super.visit(node) + } + + override fun visit(node: Identifier) { + node.spelling = getMappedLabel(node.spelling) + + super.visit(node) + } + + override fun visit(node: FuncCallExpr) { + // Ignore builtin functions, their names are already unique + if (viewingSymbolTableSession.getSymbol(node.ident) !is BuiltinFunc) { + node.ident = getMappedLabel(node.ident) + } + + super.visit(node) + } + + override fun visit(node: AssignStmt) { + node.ident = if (node.isDeclaration) { + addMapping(node.ident) + } else { + getMappedLabel(node.ident) + } + + super.visit(node) + } + + override fun visit(node: FunctionArgument) { + node.ident = addMapping(node.ident) + + super.visit(node) + } +} \ No newline at end of file diff --git a/compiler/src/main/kotlin/visitors/symbolchecker.kt b/compiler/src/main/kotlin/visitors/symbolchecker.kt index fe7b59c..dcd4089 100644 --- a/compiler/src/main/kotlin/visitors/symbolchecker.kt +++ b/compiler/src/main/kotlin/visitors/symbolchecker.kt @@ -12,7 +12,7 @@ class UndeclaredNameException(ctx: SourceContext, val ident: String) : CompileEr /** * Walks through the abstract syntax tree, extracts symbols, and checks for use-before-declaration. */ -class ScopeCheckVisitor(symbolTable: Table = Table()) : BaseASTVisitor() { +class ScopeCheckVisitor(symbolTable: Table = Table()) : BaseScopedASTVisitor() { private val symbolTableSession: CreatingSymbolTableSession = CreatingSymbolTableSession(symbolTable = symbolTable) /** @@ -22,6 +22,14 @@ class ScopeCheckVisitor(symbolTable: Table = Table()) : BaseASTVisitor() { return symbolTableSession.getRootTable() } + override fun openScope() { + symbolTableSession.openScope() + } + + override fun closeScope() { + symbolTableSession.closeScope() + } + override fun visit(node: RootNode) { // Extract all states, functions, and neighbourhoods names first node.body.filter { it is StateDecl }.forEach { symbolTableSession.insertSymbol((it as StateDecl).ident, it) } @@ -52,18 +60,8 @@ class ScopeCheckVisitor(symbolTable: Table = Table()) : BaseASTVisitor() { symbolTableSession.insertSymbol(node.ident, node) } - override fun visit(node: StateDecl) { - symbolTableSession.openScope() - super.visit(node) - symbolTableSession.closeScope() - } - - override fun visit(node: FuncDecl) { - symbolTableSession.openScope() + override fun visitFuncDeclPreNode(node: FuncDecl) { node.args.forEach { symbolTableSession.insertSymbol(it.ident, it) } - - super.visit(node) - symbolTableSession.closeScope() } override fun visit(node: AssignStmt) { @@ -81,43 +79,10 @@ class ScopeCheckVisitor(symbolTable: Table = Table()) : BaseASTVisitor() { super.visit(node) } - override fun visit(node: IfStmt) { - // conditional blocks open their own scopes - node.conditionals.forEach { visit(it) } - if (node.elseBlock != null) { - symbolTableSession.openScope() - visit(node.elseBlock) - symbolTableSession.closeScope() - } - } - - override fun visit(node: ConditionalBlock) { - visit(node.expr) - symbolTableSession.openScope() - visit(node.block) - symbolTableSession.closeScope() - } - override fun visit(node: FuncCallExpr) { if (symbolTableSession.getSymbol(node.ident) == null) { ErrorLogger += UndeclaredNameException(node.ctx, node.ident) } super.visit(node) } - - override fun visit(node: ForLoopStmt) { - // For-loop adds two layers of scopes. - // In the outer-layer are the init, condition, and post-iteration. - // The inner-layer is the loops body. - // This way any loop-control-variables (those in the init-part) are not remade every iteration, but they are - // removed when the loop finishes. - symbolTableSession.openScope() - node.initPart?.let { visit(it) } - visit(node.condition) - symbolTableSession.openScope() - visit(node.body) - symbolTableSession.closeScope() - node.postIterationPart?.let { visit(it) } - symbolTableSession.closeScope() - } } diff --git a/compiler/src/main/kotlin/visitors/visitor.kt b/compiler/src/main/kotlin/visitors/visitor.kt index edea707..fcbb145 100644 --- a/compiler/src/main/kotlin/visitors/visitor.kt +++ b/compiler/src/main/kotlin/visitors/visitor.kt @@ -424,40 +424,130 @@ abstract class BaseASTVisitor: ASTVisitor { } } -/** - * Walks the tree while opening and closing scopes as they are entered and left. - * - * @see BaseASTVisitor - */ -open class ScopedASTVisitor(symbolTable: Table): BaseASTVisitor() { - protected val symbolTableSession = ViewingSymbolTableSession(symbolTable = symbolTable) +abstract class BaseScopedASTVisitor : BaseASTVisitor() { + abstract fun openScope() + abstract fun closeScope() override fun visit(node: StateDecl) { - symbolTableSession.openScope() + openScope() + visitStateDeclPreNode(node) super.visit(node) - symbolTableSession.closeScope() + visitStateDeclPostNode(node) + closeScope() } + open fun visitStateDeclPreNode(node: StateDecl) {} + + open fun visitStateDeclPostNode(node: StateDecl) {} + override fun visit(node: FuncDecl) { - symbolTableSession.openScope() + openScope() + visitFuncDeclPreNode(node) super.visit(node) - symbolTableSession.closeScope() + visitFuncDeclPostNode(node) + closeScope() + } + + open fun visitFuncDeclPreNode(node: FuncDecl) {} + + open fun visitFuncDeclPostNode(node: FuncDecl) {} + + override fun visit(node: IfStmt) { + // conditional blocks open their own scopes + visitIfStmtPreConditionals() + node.conditionals.forEach { visit(it) } + visitIfStmtPostConditionals() + if (node.elseBlock != null) { + openScope() + visitIfStmtPreElseBlock() + visit(node.elseBlock) + visitIfStmtPostElseBlock() + closeScope() + } } + open fun visitIfStmtPreConditionals() {} + + open fun visitIfStmtPostConditionals() {} + + open fun visitIfStmtPreElseBlock() {} + + open fun visitIfStmtPostElseBlock() {} + override fun visit(node: ConditionalBlock) { - symbolTableSession.openScope() - super.visit(node) - symbolTableSession.closeScope() + visitConditionalBlockPreExpr(node) + visit(node.expr) + visitConditionalBlockPostExpr(node) + openScope() + visitConditionalBlockPreBlock(node) + visit(node.block) + visitConditionalBlockPostBlock(node) + closeScope() } + open fun visitConditionalBlockPreExpr(node: ConditionalBlock) {} + + open fun visitConditionalBlockPostExpr(node: ConditionalBlock) {} + + open fun visitConditionalBlockPreBlock(node: ConditionalBlock) {} + + open fun visitConditionalBlockPostBlock(node: ConditionalBlock) {} + + override fun visit(node: ForLoopStmt) { - symbolTableSession.openScope() + // For-loop adds two layers of scopes. + // In the outer-layer are the init, condition, and post-iteration. + // The inner-layer is the loops body. + // This way any loop-control-variables (those in the init-part) are not remade every iteration, but they are + // removed when the loop finishes. + openScope() + visitForLoopPreInitPart(node) node.initPart?.let { visit(it) } + visitForLoopPostInitPart(node) + visitForLoopPreCondition(node) visit(node.condition) - symbolTableSession.openScope() + visitForLoopPostCondition(node) + openScope() + visitForLoopPreBody(node) visit(node.body) - symbolTableSession.closeScope() + visitForLoopPostBody(node) + closeScope() + visitForLoopPrePostIteration(node) node.postIterationPart?.let { visit(it) } + visitForLoopPostPostIteration(node) + closeScope() + } + + open fun visitForLoopPreInitPart(node: ForLoopStmt) {} + + open fun visitForLoopPostInitPart(node: ForLoopStmt) {} + + open fun visitForLoopPreCondition(node: ForLoopStmt) {} + + open fun visitForLoopPostCondition(node: ForLoopStmt) {} + + open fun visitForLoopPreBody(node: ForLoopStmt) {} + + open fun visitForLoopPostBody(node: ForLoopStmt) {} + + open fun visitForLoopPrePostIteration(node: ForLoopStmt) {} + + open fun visitForLoopPostPostIteration(node: ForLoopStmt) {} +} + +/** + * Walks the tree while opening and closing scopes as they are entered and left. + * + * @see BaseASTVisitor + */ +open class ScopedASTVisitor(symbolTable: Table): BaseScopedASTVisitor() { + protected val symbolTableSession = ViewingSymbolTableSession(symbolTable = symbolTable) + + override fun openScope() { + symbolTableSession.openScope() + } + + override fun closeScope() { symbolTableSession.closeScope() } }