127 lines
3.7 KiB
Scala
127 lines
3.7 KiB
Scala
package wacc
|
|
|
|
import wacc.ast.Position
|
|
import wacc.types._
|
|
import java.io.File
|
|
|
|
private val SYNTAX_ERROR = 100
|
|
private val SEMANTIC_ERROR = 200
|
|
|
|
/** Error types for semantic errors
|
|
*/
|
|
enum Error {
|
|
case DuplicateDeclaration(ident: ast.Ident)
|
|
case UndeclaredVariable(ident: ast.Ident)
|
|
case UndefinedFunction(ident: ast.Ident)
|
|
|
|
case FunctionParamsMismatch(ident: ast.Ident, expected: Int, got: Int, funcType: FuncType)
|
|
case SemanticError(pos: Position, msg: String)
|
|
case TypeMismatch(pos: Position, expected: SemType, got: SemType, msg: String)
|
|
case InternalError(pos: Position, msg: String)
|
|
|
|
case SyntaxError(file: File, msg: String)
|
|
}
|
|
|
|
extension (e: Error) {
|
|
def exitCode: Int = e match {
|
|
case Error.SyntaxError(_, _) => SYNTAX_ERROR
|
|
case _ => SEMANTIC_ERROR
|
|
}
|
|
}
|
|
|
|
/** Function to handle printing the details of a given semantic error
|
|
*
|
|
* @param error
|
|
* Error object
|
|
* @param errorContent
|
|
* Contents of the file to generate code snippets
|
|
*/
|
|
def formatError(error: Error): String = {
|
|
val sb = new StringBuilder()
|
|
|
|
/** Format the file of an error
|
|
*
|
|
* @param file
|
|
* File of the error
|
|
*/
|
|
def formatFile(file: File): Unit = {
|
|
sb.append(s"File: ${file.getCanonicalPath}\n")
|
|
}
|
|
|
|
/** Function to format the position of an error
|
|
*
|
|
* @param pos
|
|
* Position of the error
|
|
*/
|
|
def formatPosition(pos: Position): Unit = {
|
|
formatFile(pos.file)
|
|
sb.append(s"(line ${pos.line}, column ${pos.column}):\n")
|
|
}
|
|
|
|
/** Function to highlight a section of code for an error message
|
|
*
|
|
* @param pos
|
|
* Position of the error
|
|
* @param size
|
|
* Size(in chars) of section to highlight
|
|
*/
|
|
def formatHighlight(pos: Position, size: Int): Unit = {
|
|
val lines = os.read(os.Path(pos.file.getCanonicalPath)).split("\n")
|
|
val preLine = if (pos.line > 1) lines(pos.line - 2) else ""
|
|
val midLine = lines(pos.line - 1)
|
|
val postLine = if (pos.line < lines.size) lines(pos.line) else ""
|
|
val linePointer = " " * (pos.column + 2) + ("^" * (size)) + "\n"
|
|
|
|
sb.append(
|
|
s" >$preLine\n >$midLine\n$linePointer >$postLine\netscape"
|
|
)
|
|
}
|
|
|
|
error match {
|
|
case Error.SyntaxError(_, _) =>
|
|
sb.append("Syntax error:\n")
|
|
case _ =>
|
|
sb.append("Semantic error:\n")
|
|
}
|
|
|
|
error match {
|
|
case Error.DuplicateDeclaration(ident) =>
|
|
formatPosition(ident.pos)
|
|
sb.append(s"Duplicate declaration of identifier ${ident.v}\n")
|
|
formatHighlight(ident.pos, ident.v.length)
|
|
case Error.UndeclaredVariable(ident) =>
|
|
formatPosition(ident.pos)
|
|
sb.append(s"Undeclared variable ${ident.v}\n")
|
|
formatHighlight(ident.pos, ident.v.length)
|
|
case Error.UndefinedFunction(ident) =>
|
|
formatPosition(ident.pos)
|
|
sb.append(s"Undefined function ${ident.v}\n")
|
|
formatHighlight(ident.pos, ident.v.length)
|
|
case Error.FunctionParamsMismatch(id, expected, got, funcType) =>
|
|
formatPosition(id.pos)
|
|
sb.append(s"Function expects $expected parameters, got $got\n")
|
|
sb.append(
|
|
s"(function ${id.v} has type (${funcType.params.mkString(", ")}) -> ${funcType.returnType})\n"
|
|
)
|
|
formatHighlight(id.pos, 1)
|
|
case Error.TypeMismatch(pos, expected, got, msg) =>
|
|
formatPosition(pos)
|
|
sb.append(s"Type mismatch: $msg\nExpected: $expected\nGot: $got\n")
|
|
formatHighlight(pos, 1)
|
|
case Error.SemanticError(pos, msg) =>
|
|
formatPosition(pos)
|
|
sb.append(msg + "\n")
|
|
formatHighlight(pos, 1)
|
|
case wacc.Error.InternalError(pos, msg) =>
|
|
formatPosition(pos)
|
|
sb.append(s"Internal error: $msg\n")
|
|
formatHighlight(pos, 1)
|
|
case Error.SyntaxError(file, msg) =>
|
|
formatFile(file)
|
|
sb.append(msg + "\n")
|
|
sb.append("\n")
|
|
}
|
|
|
|
sb.toString()
|
|
}
|