refactor: add comments to renamer
This commit is contained in:
parent
74f62ea933
commit
0e2d1af878
@ -15,9 +15,33 @@ object renamer {
|
|||||||
current: mutable.Map[(String, IdentType), Ident],
|
current: mutable.Map[(String, IdentType), Ident],
|
||||||
parent: Map[(String, IdentType), Ident]
|
parent: Map[(String, IdentType), Ident]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/** Create a new scope with the current scope as its parent.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new scope with an empty current scope, and this scope flattened into the parent scope.
|
||||||
|
*/
|
||||||
def subscope: Scope =
|
def subscope: Scope =
|
||||||
Scope(mutable.Map.empty, Map.empty.withDefault(current.withDefault(parent)))
|
Scope(mutable.Map.empty, Map.empty.withDefault(current.withDefault(parent)))
|
||||||
|
|
||||||
|
/** Attempt to add a new identifier to the current scope. If the identifier already exists in
|
||||||
|
* the current scope, add an error to the error list.
|
||||||
|
*
|
||||||
|
* @param semType
|
||||||
|
* The semantic type of the identifier.
|
||||||
|
* @param name
|
||||||
|
* The name of the identifier.
|
||||||
|
* @param identType
|
||||||
|
* The identifier type (function or variable).
|
||||||
|
* @param globalNames
|
||||||
|
* The global map of identifiers to semantic types - the identifier will be added to this
|
||||||
|
* map.
|
||||||
|
* @param globalNumbering
|
||||||
|
* The global map of identifier names to the number of times they have been declared - will
|
||||||
|
* used to rename this identifier, and will be incremented.
|
||||||
|
* @param errors
|
||||||
|
* The list of errors to append to.
|
||||||
|
*/
|
||||||
def add(semType: SemType, name: Ident, identType: IdentType)(using
|
def add(semType: SemType, name: Ident, identType: IdentType)(using
|
||||||
globalNames: mutable.Map[Ident, SemType],
|
globalNames: mutable.Map[Ident, SemType],
|
||||||
globalNumbering: mutable.Map[String, Int],
|
globalNumbering: mutable.Map[String, Int],
|
||||||
@ -36,6 +60,16 @@ object renamer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check scoping of all variables and functions in the program. Also generate semantic types for
|
||||||
|
* all identifiers.
|
||||||
|
*
|
||||||
|
* @param prog
|
||||||
|
* AST of the program
|
||||||
|
* @param errors
|
||||||
|
* List of errors to append to
|
||||||
|
* @return
|
||||||
|
* Map of all (renamed) identifies to their semantic types
|
||||||
|
*/
|
||||||
def rename(prog: Program)(using
|
def rename(prog: Program)(using
|
||||||
errors: mutable.Builder[Error, List[Error]]
|
errors: mutable.Builder[Error, List[Error]]
|
||||||
): Map[Ident, SemType] =
|
): Map[Ident, SemType] =
|
||||||
@ -44,6 +78,7 @@ object renamer {
|
|||||||
val scope = Scope(mutable.Map.empty, Map.empty)
|
val scope = Scope(mutable.Map.empty, Map.empty)
|
||||||
val Program(funcs, main) = prog
|
val Program(funcs, main) = prog
|
||||||
funcs
|
funcs
|
||||||
|
// First add all function declarations to the scope
|
||||||
.map { case FuncDecl(retType, name, params, body) =>
|
.map { case FuncDecl(retType, name, params, body) =>
|
||||||
val paramTypes = params.map { param =>
|
val paramTypes = params.map { param =>
|
||||||
val paramType = SemType(param.paramType)
|
val paramType = SemType(param.paramType)
|
||||||
@ -52,6 +87,8 @@ object renamer {
|
|||||||
scope.add(KnownType.Func(SemType(retType), paramTypes), name, IdentType.Func)
|
scope.add(KnownType.Func(SemType(retType), paramTypes), name, IdentType.Func)
|
||||||
(params zip paramTypes, body)
|
(params zip paramTypes, body)
|
||||||
}
|
}
|
||||||
|
// Only then rename the function bodies
|
||||||
|
// (functions can call one-another regardless of order of declaration)
|
||||||
.foreach { case (params, body) =>
|
.foreach { case (params, body) =>
|
||||||
val functionScope = scope.subscope
|
val functionScope = scope.subscope
|
||||||
params.foreach { case (param, paramType) =>
|
params.foreach { case (param, paramType) =>
|
||||||
@ -62,19 +99,52 @@ object renamer {
|
|||||||
main.toList.foreach(rename(scope))
|
main.toList.foreach(rename(scope))
|
||||||
globalNames.toMap
|
globalNames.toMap
|
||||||
|
|
||||||
|
/** Check scoping of all identifies in a given AST node.
|
||||||
|
*
|
||||||
|
* @param scope
|
||||||
|
* The current scope and flattened parent scope.
|
||||||
|
* @param node
|
||||||
|
* The AST node.
|
||||||
|
* @param globalNames
|
||||||
|
* The global map of identifiers to semantic types - renamed identifiers will be added to this
|
||||||
|
* map.
|
||||||
|
* @param globalNumbering
|
||||||
|
* The global map of identifier names to the number of times they have been declared - used and
|
||||||
|
* updated during identifier renaming.
|
||||||
|
* @param errors
|
||||||
|
*/
|
||||||
private def rename(scope: Scope)(
|
private def rename(scope: Scope)(
|
||||||
node: Ident | Stmt | LValue | RValue
|
node: Ident | Stmt | LValue | RValue | Expr
|
||||||
)(using
|
)(using
|
||||||
globalNames: mutable.Map[Ident, SemType],
|
globalNames: mutable.Map[Ident, SemType],
|
||||||
globalNumbering: mutable.Map[String, Int],
|
globalNumbering: mutable.Map[String, Int],
|
||||||
errors: mutable.Builder[Error, List[Error]]
|
errors: mutable.Builder[Error, List[Error]]
|
||||||
): Unit = node match {
|
): Unit = node match {
|
||||||
|
// These cases are more interesting because the involve making subscopes
|
||||||
|
// or modifying the current scope.
|
||||||
case VarDecl(synType, name, value) => {
|
case VarDecl(synType, name, value) => {
|
||||||
// Order matters here. Variable isn't declared until after the value is evaluated.
|
// Order matters here. Variable isn't declared until after the value is evaluated.
|
||||||
rename(scope)(value)
|
rename(scope)(value)
|
||||||
|
// Attempt to add the new variable to the current scope.
|
||||||
scope.add(SemType(synType), name, IdentType.Var)
|
scope.add(SemType(synType), name, IdentType.Var)
|
||||||
}
|
}
|
||||||
|
case If(cond, thenStmt, elseStmt) => {
|
||||||
|
rename(scope)(cond)
|
||||||
|
// then and else both have their own scopes
|
||||||
|
thenStmt.toList.foreach(rename(scope.subscope))
|
||||||
|
elseStmt.toList.foreach(rename(scope.subscope))
|
||||||
|
}
|
||||||
|
case While(cond, body) => {
|
||||||
|
rename(scope)(cond)
|
||||||
|
// while bodies have their own scopes
|
||||||
|
body.toList.foreach(rename(scope.subscope))
|
||||||
|
}
|
||||||
|
// begin-end blocks have their own scopes
|
||||||
|
case Block(body) => body.toList.foreach(rename(scope.subscope))
|
||||||
|
|
||||||
|
// These cases are simpler, mostly just recursive calls to rename()
|
||||||
case Assign(lhs, value) => {
|
case Assign(lhs, value) => {
|
||||||
|
// Variables may be reassigned with their value in the rhs, so order doesn't matter here.
|
||||||
rename(scope)(lhs)
|
rename(scope)(lhs)
|
||||||
rename(scope)(value)
|
rename(scope)(value)
|
||||||
}
|
}
|
||||||
@ -83,16 +153,6 @@ object renamer {
|
|||||||
case Return(expr) => rename(scope)(expr)
|
case Return(expr) => rename(scope)(expr)
|
||||||
case Exit(expr) => rename(scope)(expr)
|
case Exit(expr) => rename(scope)(expr)
|
||||||
case Print(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) => {
|
case NewPair(fst, snd) => {
|
||||||
rename(scope)(fst)
|
rename(scope)(fst)
|
||||||
rename(scope)(snd)
|
rename(scope)(snd)
|
||||||
@ -116,9 +176,26 @@ object renamer {
|
|||||||
}
|
}
|
||||||
// Default to variables. Only `call` uses IdentType.Func.
|
// Default to variables. Only `call` uses IdentType.Func.
|
||||||
case id: Ident => renameIdent(scope, id, IdentType.Var)
|
case id: Ident => renameIdent(scope, id, IdentType.Var)
|
||||||
|
// These literals cannot contain identifies, exit immediately.
|
||||||
case IntLiter(_) | BoolLiter(_) | CharLiter(_) | StrLiter(_) | PairLiter() | Skip() => ()
|
case IntLiter(_) | BoolLiter(_) | CharLiter(_) | StrLiter(_) | PairLiter() | Skip() => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Lookup an identifier in the current scope and rename it. If the identifier is not found, add
|
||||||
|
* an error to the error list and add it to the current scope with an unknown type.
|
||||||
|
*
|
||||||
|
* @param scope
|
||||||
|
* The current scope and flattened parent scope.
|
||||||
|
* @param ident
|
||||||
|
* The identifier to rename.
|
||||||
|
* @param identType
|
||||||
|
* The type of the identifier (function or variable).
|
||||||
|
* @param globalNames
|
||||||
|
* Used to add not-found identifiers to scope.
|
||||||
|
* @param globalNumbering
|
||||||
|
* Used to add not-found identifiers to scope.
|
||||||
|
* @param errors
|
||||||
|
* The list of errors to append to.
|
||||||
|
*/
|
||||||
private def renameIdent(scope: Scope, ident: Ident, identType: IdentType)(using
|
private def renameIdent(scope: Scope, ident: Ident, identType: IdentType)(using
|
||||||
globalNames: mutable.Map[Ident, SemType],
|
globalNames: mutable.Map[Ident, SemType],
|
||||||
globalNumbering: mutable.Map[String, Int],
|
globalNumbering: mutable.Map[String, Int],
|
||||||
|
@ -21,6 +21,7 @@ object types {
|
|||||||
case BoolType() => KnownType.Bool
|
case BoolType() => KnownType.Bool
|
||||||
case CharType() => KnownType.Char
|
case CharType() => KnownType.Char
|
||||||
case StringType() => KnownType.String
|
case StringType() => KnownType.String
|
||||||
|
// For semantic types it is easier to work with recursion rather than a fixed size
|
||||||
case ArrayType(elemType, dimension) =>
|
case ArrayType(elemType, dimension) =>
|
||||||
(0 until dimension).foldLeft(SemType(elemType))((acc, _) => KnownType.Array(acc))
|
(0 until dimension).foldLeft(SemType(elemType))((acc, _) => KnownType.Array(acc))
|
||||||
case PairType(fst, snd) => KnownType.Pair(SemType(fst), SemType(snd))
|
case PairType(fst, snd) => KnownType.Pair(SemType(fst), SemType(snd))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user