feat: initial attempt at renamer parallelisation
This commit is contained in:
@@ -32,7 +32,10 @@ object ast {
|
|||||||
object StrLiter extends ParserBridgePos1Atom[String, StrLiter]
|
object StrLiter extends ParserBridgePos1Atom[String, StrLiter]
|
||||||
case class PairLiter()(val pos: Position) extends Expr6
|
case class PairLiter()(val pos: Position) extends Expr6
|
||||||
object PairLiter extends ParserBridgePos0[PairLiter]
|
object PairLiter extends ParserBridgePos0[PairLiter]
|
||||||
case class Ident(v: String, var uid: Int = -1)(val pos: Position) extends Expr6 with LValue
|
case class Ident(v: String, var guid: Int = -1, var ty: types.RenamerType = types.?)(
|
||||||
|
val pos: Position
|
||||||
|
) extends Expr6
|
||||||
|
with LValue
|
||||||
object Ident extends ParserBridgePos1Atom[String, Ident] {
|
object Ident extends ParserBridgePos1Atom[String, Ident] {
|
||||||
def apply(v: String)(pos: Position): Ident = new Ident(v)(pos)
|
def apply(v: String)(pos: Position): Ident = new Ident(v)(pos)
|
||||||
}
|
}
|
||||||
@@ -186,7 +189,9 @@ object ast {
|
|||||||
|
|
||||||
/* ============================ STATEMENTS ============================ */
|
/* ============================ STATEMENTS ============================ */
|
||||||
|
|
||||||
sealed trait Stmt
|
sealed trait Stmt {
|
||||||
|
val pos: Position
|
||||||
|
}
|
||||||
case class Skip()(val pos: Position) extends Stmt
|
case class Skip()(val pos: Position) extends Stmt
|
||||||
object Skip extends ParserBridgePos0[Skip]
|
object Skip extends ParserBridgePos0[Skip]
|
||||||
case class VarDecl(varType: Type, name: Ident, value: RValue)(val pos: Position) extends Stmt
|
case class VarDecl(varType: Type, name: Ident, value: RValue)(val pos: Position) extends Stmt
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
package wacc
|
package wacc
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
import cats.effect.IO
|
||||||
|
import cats.syntax.all._
|
||||||
|
import cats.implicits._
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.Foldable
|
||||||
|
import cats.Functor
|
||||||
|
import cats.data.NonEmptyList
|
||||||
|
import parsley.{Failure, Success}
|
||||||
|
import cats.data.NonEmptyChain
|
||||||
|
import cats.NonEmptyParallel
|
||||||
|
|
||||||
|
private val MAIN = "$main"
|
||||||
|
|
||||||
object renamer {
|
object renamer {
|
||||||
import ast._
|
import ast._
|
||||||
@@ -11,167 +24,297 @@ object renamer {
|
|||||||
case Var
|
case Var
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private case class ScopeKey(path: String, name: String, identType: IdentType)
|
||||||
|
private case class ScopeValue(id: Ident, public: Boolean)
|
||||||
|
|
||||||
private class Scope(
|
private class Scope(
|
||||||
val current: mutable.Map[(String, IdentType), Ident],
|
private val current: mutable.Map[ScopeKey, ScopeValue],
|
||||||
val parent: Map[(String, IdentType), Ident]
|
private val parent: Map[ScopeKey, ScopeValue],
|
||||||
|
guidStart: Int = 0,
|
||||||
|
val guidInc: Int = 1
|
||||||
) {
|
) {
|
||||||
|
private var guid = guidStart
|
||||||
|
private var immutable = false
|
||||||
|
|
||||||
|
private def nextGuid(): Int = {
|
||||||
|
val id = guid
|
||||||
|
guid += guidInc
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
private def verifyMutable(): Unit = {
|
||||||
|
if (immutable) throw new IllegalStateException("Cannot modify an immutable scope")
|
||||||
|
}
|
||||||
|
|
||||||
/** Create a new scope with the current scope as its parent.
|
/** Create a new scope with the current scope as its parent.
|
||||||
|
*
|
||||||
|
* To be used for single-threaded applications.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* A new scope with an empty current scope, and this scope flattened into the parent scope.
|
* A new scope with an empty current scope, and this scope flattened into the parent scope.
|
||||||
*/
|
*/
|
||||||
def subscope: Scope =
|
def withSubscope[T](f: Scope => T): T = {
|
||||||
Scope(mutable.Map.empty, Map.empty.withDefault(current.withDefault(parent)))
|
val subscope = Scope(mutable.Map.empty, Map.empty.withDefault(current.withDefault(parent)), guid, guidInc)
|
||||||
|
immutable = true
|
||||||
|
val result = f(subscope)
|
||||||
|
guid = subscope.guid // Sync GUID
|
||||||
|
immutable = false
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create new scopes with the current scope as its parent and GUID numbering adjusted
|
||||||
|
* correctly.
|
||||||
|
*
|
||||||
|
* This will permanently mark the current scope as immutable, for thread safety.
|
||||||
|
*
|
||||||
|
* To be used for multi-threaded applications.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* New scopes with an empty current scope, and this scope flattened into the parent scope.
|
||||||
|
*/
|
||||||
|
def subscopes(n: Int): Seq[Scope] = {
|
||||||
|
verifyMutable()
|
||||||
|
immutable = true
|
||||||
|
(0 until n).map { i =>
|
||||||
|
Scope(
|
||||||
|
mutable.Map.empty,
|
||||||
|
Map.empty.withDefault(current.withDefault(parent)),
|
||||||
|
guid + i * guidInc,
|
||||||
|
guidInc * n
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Attempt to add a new identifier to the current scope. If the identifier already exists in
|
/** 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.
|
* the current scope, add an error to the error list.
|
||||||
*
|
*
|
||||||
* @param ty
|
|
||||||
* The semantic type of the variable identifier, or function identifier type.
|
|
||||||
* @param name
|
* @param name
|
||||||
* The name of the identifier.
|
* The name of the identifier.
|
||||||
* @param globalNames
|
* @return
|
||||||
* The global map of identifiers to semantic types - the identifier will be added to this
|
* An error, if one occurred.
|
||||||
* 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(ty: SemType | FuncType, name: Ident)(using
|
def add(name: Ident, public: Boolean = false): Chain[Error] = {
|
||||||
globalNames: mutable.Map[Ident, SemType],
|
verifyMutable()
|
||||||
globalFuncs: mutable.Map[Ident, FuncType],
|
val path = name.pos.file.getCanonicalPath
|
||||||
globalNumbering: mutable.Map[String, Int],
|
val identType = name.ty match {
|
||||||
errors: mutable.Builder[Error, List[Error]]
|
|
||||||
) = {
|
|
||||||
val identType = ty match {
|
|
||||||
case _: SemType => IdentType.Var
|
case _: SemType => IdentType.Var
|
||||||
case _: FuncType => IdentType.Func
|
case _: FuncType => IdentType.Func
|
||||||
}
|
}
|
||||||
current.get((name.v, identType)) match {
|
val key = ScopeKey(path, name.v, identType)
|
||||||
case Some(Ident(_, uid)) =>
|
current.get(key) match {
|
||||||
errors += Error.DuplicateDeclaration(name)
|
case Some(ScopeValue(Ident(_, id, _), _)) =>
|
||||||
name.uid = uid
|
name.guid = id
|
||||||
|
Chain.one(Error.DuplicateDeclaration(name))
|
||||||
case None =>
|
case None =>
|
||||||
val uid = globalNumbering.getOrElse(name.v, 0)
|
name.guid = nextGuid()
|
||||||
name.uid = uid
|
current(key) = ScopeValue(name, public)
|
||||||
current((name.v, identType)) = name
|
Chain.empty
|
||||||
|
|
||||||
ty match {
|
|
||||||
case semType: SemType =>
|
|
||||||
globalNames(name) = semType
|
|
||||||
case funcType: FuncType =>
|
|
||||||
globalFuncs(name) = funcType
|
|
||||||
}
|
|
||||||
globalNumbering(name.v) = uid + 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def get(name: String, identType: IdentType): Option[Ident] =
|
/** Attempt to add a new identifier as an alias to another to the existing scope.
|
||||||
|
*
|
||||||
|
* @param alias
|
||||||
|
* The (new) alias identifier.
|
||||||
|
* @param orig
|
||||||
|
* The (existing) original identifier.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* An error, if one occurred.
|
||||||
|
*/
|
||||||
|
def addAlias(alias: Ident, orig: ScopeValue, public: Boolean = false): Chain[Error] = {
|
||||||
|
verifyMutable()
|
||||||
|
val path = alias.pos.file.getCanonicalPath
|
||||||
|
val identType = alias.ty match {
|
||||||
|
case _: SemType => IdentType.Var
|
||||||
|
case _: FuncType => IdentType.Func
|
||||||
|
}
|
||||||
|
val key = ScopeKey(path, alias.v, identType)
|
||||||
|
current.get(key) match {
|
||||||
|
case Some(ScopeValue(Ident(_, id, _), _)) =>
|
||||||
|
alias.guid = id
|
||||||
|
Chain.one(Error.DuplicateDeclaration(alias))
|
||||||
|
case None =>
|
||||||
|
alias.guid = nextGuid()
|
||||||
|
current(key) = ScopeValue(orig.id, public)
|
||||||
|
Chain.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get(path: String, name: String, identType: IdentType): Option[ScopeValue] =
|
||||||
// Unfortunately map defaults only work with `.apply()`, which throws an error when the key is not found.
|
// Unfortunately map defaults only work with `.apply()`, which throws an error when the key is not found.
|
||||||
// Neither is there a way to check whether a default exists, so we have to use a try-catch.
|
// Neither is there a way to check whether a default exists, so we have to use a try-catch.
|
||||||
try {
|
try {
|
||||||
Some(current.withDefault(parent)((name, identType)))
|
Some(current.withDefault(parent)(ScopeKey(path, name, identType)))
|
||||||
} catch {
|
} catch {
|
||||||
case _: NoSuchElementException => None
|
case _: NoSuchElementException => None
|
||||||
}
|
}
|
||||||
|
|
||||||
def getVar(name: String): Option[Ident] = get(name, IdentType.Var)
|
def getVar(name: Ident): Option[Ident] = get(name.pos.file.getCanonicalPath, name.v, IdentType.Var).map(_.id)
|
||||||
def getFunc(name: String): Option[Ident] = get(name, IdentType.Func)
|
def getFunc(name: Ident): Option[Ident] = get(name.pos.file.getCanonicalPath, name.v, IdentType.Func).map(_.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check scoping of all variables and functions in the program. Also generate semantic types for
|
private def prepareGlobalScope(partialProg: PartialProgram)(using scope: Scope): IO[(FuncDecl, Chain[FuncDecl], Chain[Error])] = {
|
||||||
* all identifiers.
|
def readImportFile(file: File): IO[String] =
|
||||||
*
|
IO.blocking(os.read(os.Path(file.getCanonicalPath)))
|
||||||
* @param prog
|
|
||||||
* AST of the program
|
def prepareImport(contents: String, file: File)(using scope: Scope): IO[(Chain[FuncDecl], Chain[Error])] = {
|
||||||
* @param errors
|
parser.parse(contents) match {
|
||||||
* List of errors to append to
|
case Failure(msg) =>
|
||||||
* @return
|
IO.pure(Chain.empty, Chain.one(Error.SyntaxError(file, msg)))
|
||||||
* Map of all (renamed) identifies to their semantic types
|
case Success(fn) =>
|
||||||
*/
|
val partialProg = fn(file)
|
||||||
def rename(prog: Program)(using
|
for {
|
||||||
errors: mutable.Builder[Error, List[Error]]
|
(main, chunks, errors) <- prepareGlobalScope(partialProg)
|
||||||
): (Map[Ident, SemType], Map[Ident, FuncType]) = {
|
} yield (main +: chunks, errors)
|
||||||
given globalNames: mutable.Map[Ident, SemType] = mutable.Map.empty
|
}
|
||||||
given globalFuncs: mutable.Map[Ident, FuncType] = mutable.Map.empty
|
|
||||||
given globalNumbering: mutable.Map[String, Int] = mutable.Map.empty
|
}
|
||||||
val scope = Scope(mutable.Map.empty, Map.empty)
|
|
||||||
|
val PartialProgram(imports, prog) = partialProg
|
||||||
|
|
||||||
|
// First prepare this file's functions...
|
||||||
val Program(funcs, main) = prog
|
val Program(funcs, main) = prog
|
||||||
funcs
|
val (funcChunks, funcErrors) = funcs.foldLeft((Chain.empty[FuncDecl], Chain.empty[Error])) {
|
||||||
// First add all function declarations to the scope
|
case ((chunks, errors), func@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)
|
||||||
|
param.name.ty = paramType
|
||||||
paramType
|
paramType
|
||||||
}
|
}
|
||||||
scope.add(FuncType(SemType(retType), paramTypes), name)
|
name.ty = FuncType(SemType(retType), paramTypes)
|
||||||
(params zip paramTypes, body)
|
(chunks :+ func, errors ++ scope.add(name, public = true))
|
||||||
}
|
}
|
||||||
// Only then rename the function bodies
|
// ...and main body.
|
||||||
// (functions can call one-another regardless of order of declaration)
|
val mainBodyIdent = Ident(MAIN, ty = FuncType(?, Nil))(prog.pos)
|
||||||
.foreach { case (params, body) =>
|
val mainBodyErrors = scope.add(mainBodyIdent, public = false)
|
||||||
val functionScope = scope.subscope
|
val mainBodyChunk = FuncDecl(IntType()(prog.pos), mainBodyIdent, Nil, main)(prog.pos)
|
||||||
params.foreach { case (param, paramType) =>
|
|
||||||
functionScope.add(paramType, param.name)
|
// Now handle imports
|
||||||
|
val file = prog.pos.file
|
||||||
|
val preparedImports = imports.foldLeftM[IO, (Chain[FuncDecl], Chain[Error])]((Chain.empty[FuncDecl], Chain.empty[Error])) {
|
||||||
|
case ((chunks, errors), Import(name, funcs)) =>
|
||||||
|
val importFile = File(file.getParent, name.v)
|
||||||
|
for {
|
||||||
|
contents <- readImportFile(importFile)
|
||||||
|
(importChunks, importErrors) <- prepareImport(contents, importFile)
|
||||||
|
importAliasErrors = funcs.foldMap { case ImportedFunc(srcName, aliasName) =>
|
||||||
|
scope.get(importFile.getCanonicalPath, srcName.v, IdentType.Func) match {
|
||||||
|
case Some(src) if src.public => scope.addAlias(aliasName, src)
|
||||||
|
case _ => Chain.one(Error.UndefinedFunction(srcName))
|
||||||
}
|
}
|
||||||
body.toList.foreach(rename(functionScope.subscope)) // body can shadow function params
|
|
||||||
}
|
}
|
||||||
main.toList.foreach(rename(scope))
|
} yield (chunks ++ importChunks, errors ++ importErrors)
|
||||||
(globalNames.toMap, globalFuncs.toMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
(importChunks, importErrors) <- preparedImports
|
||||||
|
allChunks = importChunks ++ funcChunks
|
||||||
|
allErrors = importErrors ++ funcErrors ++ mainBodyErrors
|
||||||
|
} yield (mainBodyChunk, allChunks, allErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check scoping of all variables and flatten a program. Also generates semantic types and parses
|
||||||
|
* any imported files.
|
||||||
|
*
|
||||||
|
* @param partialProg
|
||||||
|
* AST of the program
|
||||||
|
* @return
|
||||||
|
* (flattenedProg, errors)
|
||||||
|
*/
|
||||||
|
def rename(partialProg: PartialProgram): IO[(Program, Chain[Error])] = {
|
||||||
|
given scope: Scope = Scope(mutable.Map.empty, Map.empty)
|
||||||
|
for {
|
||||||
|
(main, chunks, errors) <- prepareGlobalScope(partialProg)
|
||||||
|
toRename = (main +: chunks).toList
|
||||||
|
res = (toRename zip scope.subscopes(toRename.size)).parTraverse { case (func@FuncDecl(retType, name, params, body), subscope) =>
|
||||||
|
val paramErrors = params.foldMap { param => subscope.add(param.name) }
|
||||||
|
val bodyErrors = subscope.withSubscope { s => body.foldMap(rename(s)) }
|
||||||
|
paramErrors ++ bodyErrors
|
||||||
|
}
|
||||||
|
} yield (partialProg.self, errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// /** 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
|
||||||
|
// errors: mutable.Builder[Error, List[Error]]
|
||||||
|
// ): (Map[Ident, SemType], Map[Ident, FuncType]) = {
|
||||||
|
// given globalNames: mutable.Map[Ident, SemType] = mutable.Map.empty
|
||||||
|
// given globalFuncs: mutable.Map[Ident, FuncType] = mutable.Map.empty
|
||||||
|
// given globalNumbering: mutable.Map[String, Int] = mutable.Map.empty
|
||||||
|
// val scope = Scope(mutable.Map.empty, Map.empty)
|
||||||
|
// val Program(funcs, main) = prog
|
||||||
|
// funcs
|
||||||
|
// // First add all function declarations to the scope
|
||||||
|
// .map { case FuncDecl(retType, name, params, body) =>
|
||||||
|
// val paramTypes = params.map { param =>
|
||||||
|
// val paramType = SemType(param.paramType)
|
||||||
|
// paramType
|
||||||
|
// }
|
||||||
|
// scope.add(FuncType(SemType(retType), paramTypes), name)
|
||||||
|
// (params zip paramTypes, body)
|
||||||
|
// }
|
||||||
|
// // Only then rename the function bodies
|
||||||
|
// // (functions can call one-another regardless of order of declaration)
|
||||||
|
// .foreach { case (params, body) =>
|
||||||
|
// val functionScope = scope.subscope
|
||||||
|
// params.foreach { case (param, paramType) =>
|
||||||
|
// functionScope.add(paramType, param.name)
|
||||||
|
// }
|
||||||
|
// body.toList.foreach(rename(functionScope.subscope)) // body can shadow function params
|
||||||
|
// }
|
||||||
|
// main.toList.foreach(rename(scope))
|
||||||
|
// (globalNames.toMap, globalFuncs.toMap)
|
||||||
|
// }
|
||||||
|
|
||||||
/** Check scoping of all identifies in a given AST node.
|
/** Check scoping of all identifies in a given AST node.
|
||||||
*
|
*
|
||||||
* @param scope
|
* @param scope
|
||||||
* The current scope and flattened parent scope.
|
* The current scope and flattened parent scope.
|
||||||
* @param node
|
* @param node
|
||||||
* The AST 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 | Expr): Chain[Error] =
|
||||||
node: Ident | Stmt | LValue | RValue | Expr
|
node match {
|
||||||
)(using
|
// These cases are more interes/globting because the involve making subscopes
|
||||||
globalNames: mutable.Map[Ident, SemType],
|
|
||||||
globalFuncs: mutable.Map[Ident, FuncType],
|
|
||||||
globalNumbering: mutable.Map[String, Int],
|
|
||||||
errors: mutable.Builder[Error, List[Error]]
|
|
||||||
): Unit = node match {
|
|
||||||
// These cases are more interesting because the involve making subscopes
|
|
||||||
// or modifying the current scope.
|
// 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)
|
val errors = rename(scope)(value)
|
||||||
// Attempt to add the new variable to the current scope.
|
// Attempt to add the new variable to the current scope.
|
||||||
scope.add(SemType(synType), name)
|
name.ty = SemType(synType)
|
||||||
|
errors ++ scope.add(name)
|
||||||
}
|
}
|
||||||
case If(cond, thenStmt, elseStmt) => {
|
case If(cond, thenStmt, elseStmt) => {
|
||||||
rename(scope)(cond)
|
val condErrors = rename(scope)(cond)
|
||||||
// then and else both have their own scopes
|
// then and else both have their own scopes
|
||||||
thenStmt.toList.foreach(rename(scope.subscope))
|
val thenErrors = scope.withSubscope(s => thenStmt.foldMap(rename(s)))
|
||||||
elseStmt.toList.foreach(rename(scope.subscope))
|
val elseErrors = scope.withSubscope(s => elseStmt.foldMap(rename(s)))
|
||||||
|
condErrors ++ thenErrors ++ elseErrors
|
||||||
}
|
}
|
||||||
case While(cond, body) => {
|
case While(cond, body) => {
|
||||||
rename(scope)(cond)
|
val condErrors = rename(scope)(cond)
|
||||||
// while bodies have their own scopes
|
// while bodies have their own scopes
|
||||||
body.toList.foreach(rename(scope.subscope))
|
val bodyErrors = scope.withSubscope(s => body.foldMap(rename(s)))
|
||||||
|
condErrors ++ bodyErrors
|
||||||
}
|
}
|
||||||
// begin-end blocks have their own scopes
|
// begin-end blocks have their own scopes
|
||||||
case Block(body) => body.toList.foreach(rename(scope.subscope))
|
case Block(body) => scope.withSubscope(s => body.foldMap(rename(s)))
|
||||||
|
|
||||||
// These cases are simpler, mostly just recursive calls to rename()
|
// 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.
|
// 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)
|
|
||||||
}
|
}
|
||||||
case Read(lhs) => rename(scope)(lhs)
|
case Read(lhs) => rename(scope)(lhs)
|
||||||
case Free(expr) => rename(scope)(expr)
|
case Free(expr) => rename(scope)(expr)
|
||||||
@@ -179,41 +322,49 @@ object renamer {
|
|||||||
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 NewPair(fst, snd) => {
|
case NewPair(fst, snd) => {
|
||||||
rename(scope)(fst)
|
rename(scope)(fst) ++ rename(scope)(snd)
|
||||||
rename(scope)(snd)
|
|
||||||
}
|
}
|
||||||
case Call(name, args) => {
|
case Call(name, args) => {
|
||||||
scope.getFunc(name.v) match {
|
val nameErrors = scope.getFunc(name) match {
|
||||||
case Some(Ident(_, uid)) => name.uid = uid
|
case Some(Ident(_, guid, ty)) =>
|
||||||
|
name.ty = ty
|
||||||
|
name.guid = guid
|
||||||
|
Chain.empty
|
||||||
case None =>
|
case None =>
|
||||||
errors += Error.UndefinedFunction(name)
|
name.ty = FuncType(?, args.map(_ => ?))
|
||||||
scope.add(FuncType(?, args.map(_ => ?)), name)
|
scope.add(name)
|
||||||
|
Chain.one(Error.UndefinedFunction(name))
|
||||||
}
|
}
|
||||||
args.foreach(rename(scope))
|
val argsErrors = args.foldMap(rename(scope))
|
||||||
|
nameErrors ++ argsErrors
|
||||||
}
|
}
|
||||||
case Fst(elem) => rename(scope)(elem)
|
case Fst(elem) => rename(scope)(elem)
|
||||||
case Snd(elem) => rename(scope)(elem)
|
case Snd(elem) => rename(scope)(elem)
|
||||||
case ArrayLiter(elems) => elems.foreach(rename(scope))
|
case ArrayLiter(elems) => elems.foldMap(rename(scope))
|
||||||
case ArrayElem(name, indices) => {
|
case ArrayElem(name, indices) => {
|
||||||
rename(scope)(name)
|
val nameErrors = rename(scope)(name)
|
||||||
indices.toList.foreach(rename(scope))
|
val indicesErrors = indices.foldMap(rename(scope))
|
||||||
|
nameErrors ++ indicesErrors
|
||||||
}
|
}
|
||||||
case Parens(expr) => rename(scope)(expr)
|
case Parens(expr) => rename(scope)(expr)
|
||||||
case op: UnaryOp => rename(scope)(op.x)
|
case op: UnaryOp => rename(scope)(op.x)
|
||||||
case op: BinaryOp => {
|
case op: BinaryOp => {
|
||||||
rename(scope)(op.x)
|
rename(scope)(op.x) ++ rename(scope)(op.y)
|
||||||
rename(scope)(op.y)
|
|
||||||
}
|
}
|
||||||
// Default to variables. Only `call` uses IdentType.Func.
|
// Default to variables. Only `call` uses IdentType.Func.
|
||||||
case id: Ident => {
|
case id: Ident => {
|
||||||
scope.getVar(id.v) match {
|
scope.getVar(id) match {
|
||||||
case Some(Ident(_, uid)) => id.uid = uid
|
case Some(Ident(_, guid, ty)) =>
|
||||||
|
id.ty = ty
|
||||||
|
id.guid = guid
|
||||||
|
Chain.empty
|
||||||
case None =>
|
case None =>
|
||||||
errors += Error.UndeclaredVariable(id)
|
id.ty = ?
|
||||||
scope.add(?, id)
|
scope.add(id)
|
||||||
|
Chain.one(Error.UndeclaredVariable(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// These literals cannot contain identifies, exit immediately.
|
// These literals cannot contain identifies, exit immediately.
|
||||||
case IntLiter(_) | BoolLiter(_) | CharLiter(_) | StrLiter(_) | PairLiter() | Skip() => ()
|
case IntLiter(_) | BoolLiter(_) | CharLiter(_) | StrLiter(_) | PairLiter() | Skip() => Chain.empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package wacc
|
|||||||
object types {
|
object types {
|
||||||
import ast._
|
import ast._
|
||||||
|
|
||||||
sealed trait SemType {
|
sealed trait RenamerType
|
||||||
|
|
||||||
|
sealed trait SemType extends RenamerType {
|
||||||
override def toString(): String = this match {
|
override def toString(): String = this match {
|
||||||
case KnownType.Int => "int"
|
case KnownType.Int => "int"
|
||||||
case KnownType.Bool => "bool"
|
case KnownType.Bool => "bool"
|
||||||
@@ -41,5 +43,5 @@ object types {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class FuncType(returnType: SemType, params: List[SemType])
|
case class FuncType(returnType: SemType, params: List[SemType]) extends RenamerType
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user