diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index 20acd19..ad43479 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -53,14 +53,15 @@ def frontend( if (errors.result.isEmpty) IO.pure(Right(typedProg)) else { + given errorContent: String = contents val exitCode = errors.result.view.map { case _: Error.InternalError => 201 case _ => 200 }.max - logger.error(s"Semantic errors:\n${errors.result.mkString("\n")}") *> IO.pure( - Left(exitCode) - ) + val formattedErrors = errors.result.map(formatError).mkString("\n") + + logger.error(s"Semantic errors:\n$formattedErrors") *> IO.pure(Left(exitCode)) } } } diff --git a/src/main/wacc/frontend/Error.scala b/src/main/wacc/frontend/Error.scala index 9c02a60..b991421 100644 --- a/src/main/wacc/frontend/Error.scala +++ b/src/main/wacc/frontend/Error.scala @@ -2,7 +2,6 @@ package wacc import wacc.ast.Position import wacc.types._ -import java.io.PrintStream /** Error types for semantic errors */ @@ -24,71 +23,72 @@ enum Error { * @param errorContent * Contents of the file to generate code snippets */ -def printError(error: Error)(using errorContent: String, stdout: PrintStream): Unit = { - stdout.println("Semantic error:") - error match { - case Error.DuplicateDeclaration(ident) => - printPosition(ident.pos) - stdout.println(s"Duplicate declaration of identifier ${ident.v}") - highlight(ident.pos, ident.v.length) - case Error.UndeclaredVariable(ident) => - printPosition(ident.pos) - stdout.println(s"Undeclared variable ${ident.v}") - highlight(ident.pos, ident.v.length) - case Error.UndefinedFunction(ident) => - printPosition(ident.pos) - stdout.println(s"Undefined function ${ident.v}") - highlight(ident.pos, ident.v.length) - case Error.FunctionParamsMismatch(id, expected, got, funcType) => - printPosition(id.pos) - stdout.println(s"Function expects $expected parameters, got $got") - stdout.println( - s"(function ${id.v} has type (${funcType.params.mkString(", ")}) -> ${funcType.returnType})" - ) - highlight(id.pos, 1) - case Error.TypeMismatch(pos, expected, got, msg) => - printPosition(pos) - stdout.println(s"Type mismatch: $msg\nExpected: $expected\nGot: $got") - highlight(pos, 1) - case Error.SemanticError(pos, msg) => - printPosition(pos) - stdout.println(msg) - highlight(pos, 1) - case wacc.Error.InternalError(pos, msg) => - printPosition(pos) - stdout.println(s"Internal error: $msg") - highlight(pos, 1) +def formatError(error: Error)(using errorContent: String): String = { + val sb = new StringBuilder() + sb.append("Semantic error:\n") + + /** Function to format the position of an error + * + * @param pos + * Position of the error + */ + def formatPosition(pos: Position): Unit = { + 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 - * @param errorContent - * Contents of the file to generate code snippets - */ -def highlight(pos: Position, size: Int)(using errorContent: String, stdout: PrintStream): Unit = { - val lines = errorContent.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" - - stdout.println( - s" >$preLine\n >$midLine\n$linePointer >$postLine" - ) -} - -/** Function to print the position of an error - * - * @param pos - * Position of the error - */ -def printPosition(pos: Position)(using stdout: PrintStream): Unit = { - stdout.println(s"(line ${pos.line}, column ${pos.column}):") + /** 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 = errorContent.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.DuplicateDeclaration(ident) => + formatPosition(ident.pos) + sb.append(s"Duplicate declaration of identifier ${ident.v}") + formatHighlight(ident.pos, ident.v.length) + case Error.UndeclaredVariable(ident) => + formatPosition(ident.pos) + sb.append(s"Undeclared variable ${ident.v}") + formatHighlight(ident.pos, ident.v.length) + case Error.UndefinedFunction(ident) => + formatPosition(ident.pos) + sb.append(s"Undefined function ${ident.v}") + 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") + sb.append( + s"(function ${id.v} has type (${funcType.params.mkString(", ")}) -> ${funcType.returnType})" + ) + formatHighlight(id.pos, 1) + case Error.TypeMismatch(pos, expected, got, msg) => + formatPosition(pos) + sb.append(s"Type mismatch: $msg\nExpected: $expected\nGot: $got") + formatHighlight(pos, 1) + case Error.SemanticError(pos, msg) => + formatPosition(pos) + sb.append(msg) + formatHighlight(pos, 1) + case wacc.Error.InternalError(pos, msg) => + formatPosition(pos) + sb.append(s"Internal error: $msg") + formatHighlight(pos, 1) + } + + sb.toString() + }