132 lines
4.3 KiB
Scala
132 lines
4.3 KiB
Scala
package wacc
|
|
|
|
import scala.collection.mutable
|
|
|
|
object renamer {
|
|
import ast._
|
|
import types._
|
|
|
|
enum IdentType {
|
|
case Func
|
|
case Var
|
|
}
|
|
|
|
private case class Scope(
|
|
current: mutable.Map[(String, IdentType), Ident],
|
|
parent: Map[(String, IdentType), Ident]
|
|
) {
|
|
def subscope: Scope =
|
|
Scope(mutable.Map.empty, Map.empty.withDefault(current.withDefault(parent)))
|
|
|
|
def add(semType: SemType, name: Ident, identType: IdentType)(using
|
|
globalNames: mutable.Map[Ident, SemType],
|
|
globalNumbering: mutable.Map[String, Int],
|
|
errors: mutable.Builder[Error, List[Error]]
|
|
) = {
|
|
if (current.contains((name.v, identType))) {
|
|
errors += Error.DuplicateDeclaration(name)
|
|
} else {
|
|
val uid = globalNumbering.getOrElse(name.v, 0)
|
|
name.uid = uid
|
|
current((name.v, identType)) = name
|
|
|
|
globalNames(name) = semType
|
|
globalNumbering(name.v) = uid + 1
|
|
}
|
|
}
|
|
}
|
|
|
|
def rename(prog: Program)(using
|
|
errors: mutable.Builder[Error, List[Error]]
|
|
): Map[Ident, SemType] =
|
|
given globalNames: mutable.Map[Ident, SemType] = mutable.Map.empty
|
|
given globalNumbering: mutable.Map[String, Int] = mutable.Map.empty
|
|
rename(Scope(mutable.Map.empty, Map.empty))(prog)
|
|
globalNames.toMap
|
|
|
|
private def rename(scope: Scope)(
|
|
node: Program | FuncDecl | Ident | Stmt | LValue | RValue
|
|
)(using
|
|
globalNames: mutable.Map[Ident, SemType],
|
|
globalNumbering: mutable.Map[String, Int],
|
|
errors: mutable.Builder[Error, List[Error]]
|
|
): Unit = node match {
|
|
case Program(funcs, main) => {
|
|
funcs.foreach(rename(scope))
|
|
main.toList.foreach(rename(scope))
|
|
}
|
|
case FuncDecl(retType, name, params, body) => {
|
|
val functionScope = scope.subscope
|
|
val paramTypes = params.map { param =>
|
|
val paramType = SemType(param.paramType)
|
|
functionScope.add(paramType, param.name, IdentType.Var)
|
|
paramType
|
|
}
|
|
scope.add(KnownType.Func(SemType(retType), paramTypes), name, IdentType.Func)
|
|
body.toList.foreach(rename(functionScope))
|
|
}
|
|
case VarDecl(synType, name, value) => {
|
|
// Order matters here. Variable isn't declared until after the value is evaluated.
|
|
rename(scope)(value)
|
|
scope.add(SemType(synType), name, IdentType.Var)
|
|
}
|
|
case Assign(lhs, value) => {
|
|
rename(scope)(lhs)
|
|
rename(scope)(value)
|
|
}
|
|
case Read(lhs) => rename(scope)(lhs)
|
|
case Free(expr) => rename(scope)(expr)
|
|
case Return(expr) => rename(scope)(expr)
|
|
case Exit(expr) => rename(scope)(expr)
|
|
case Print(expr, _) => rename(scope)(expr)
|
|
case If(cond, thenStmt, elseStmt) => {
|
|
rename(scope)(cond)
|
|
thenStmt.toList.foreach(rename(scope.subscope))
|
|
elseStmt.toList.foreach(rename(scope.subscope))
|
|
}
|
|
case While(cond, body) => {
|
|
rename(scope)(cond)
|
|
body.toList.foreach(rename(scope.subscope))
|
|
}
|
|
case Block(body) => body.toList.foreach(rename(scope.subscope))
|
|
case NewPair(fst, snd) => {
|
|
rename(scope)(fst)
|
|
rename(scope)(snd)
|
|
}
|
|
case Call(name, args) => {
|
|
renameIdent(scope, name, IdentType.Func)
|
|
args.foreach(rename(scope))
|
|
}
|
|
case Fst(elem) => rename(scope)(elem)
|
|
case Snd(elem) => rename(scope)(elem)
|
|
case ArrayLiter(elems) => elems.foreach(rename(scope))
|
|
case ArrayElem(name, indices) => {
|
|
rename(scope)(name)
|
|
indices.toList.foreach(rename(scope))
|
|
}
|
|
case Parens(expr) => rename(scope)(expr)
|
|
case op: UnaryOp => rename(scope)(op.x)
|
|
case op: BinaryOp => {
|
|
rename(scope)(op.x)
|
|
rename(scope)(op.y)
|
|
}
|
|
// Default to variables. Only `call` uses IdentType.Func.
|
|
case id: Ident => renameIdent(scope, id, IdentType.Var)
|
|
case IntLiter(_) | BoolLiter(_) | CharLiter(_) | StrLiter(_) | PairLiter | Skip => ()
|
|
}
|
|
|
|
private def renameIdent(scope: Scope, ident: Ident, identType: IdentType)(using
|
|
globalNames: mutable.Map[Ident, SemType],
|
|
globalNumbering: mutable.Map[String, Int],
|
|
errors: mutable.Builder[Error, List[Error]]
|
|
): Unit = {
|
|
scope.current.withDefault(scope.parent).get((ident.v, identType)) match {
|
|
case Some(Ident(_, uid)) => ident.uid = uid
|
|
case None => {
|
|
errors += Error.UndefinedIdentifier(ident, identType)
|
|
scope.add(?, ident, identType)
|
|
}
|
|
}
|
|
}
|
|
}
|