Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/main/kotlin/ast/ast.kt
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ class NeighbourhoodDecl(
*/
class FunctionArgument(
ctx: SourceContext,
val ident: String,
var ident: String,
val type: Type = UncheckedType
) : AST(ctx)

Expand Down
114 changes: 114 additions & 0 deletions compiler/src/main/kotlin/visitors/Uniqueify.kt
Original file line number Diff line number Diff line change
@@ -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<Map<String, String>> = 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)
}
}
55 changes: 10 additions & 45 deletions compiler/src/main/kotlin/visitors/symbolchecker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

/**
Expand All @@ -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) }
Expand Down Expand Up @@ -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) {
Expand All @@ -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()
}
}
124 changes: 107 additions & 17 deletions compiler/src/main/kotlin/visitors/visitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -424,40 +424,130 @@ abstract class BaseASTVisitor: ASTVisitor<Unit> {
}
}

/**
* 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()
}
}