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