feat: semantic error messages make use of msg strings passed from typeChecker

This commit is contained in:
Guy C 2025-02-07 12:46:47 +00:00 committed by Barf-Vader
parent c798fdf416
commit b6d8eb31e3
2 changed files with 15 additions and 29 deletions

View File

@ -6,7 +6,6 @@ import wacc.types._
enum Error { enum Error {
case DuplicateDeclaration(ident: ast.Ident) case DuplicateDeclaration(ident: ast.Ident)
case UndefinedIdentifier(ident: ast.Ident, identType: renamer.IdentType) case UndefinedIdentifier(ident: ast.Ident, identType: renamer.IdentType)
case FunctionParamsMismatch(pos: Position, expected: Int, got: Int) case FunctionParamsMismatch(pos: Position, expected: Int, got: Int)
case SemanticError(pos: Position, msg: String) case SemanticError(pos: Position, msg: String)
case TypeMismatch(pos: Position, expected: SemType, got: SemType, msg: String) case TypeMismatch(pos: Position, expected: SemType, got: SemType, msg: String)
@ -18,25 +17,23 @@ def printError(error: Error)(using errorContent: String): Unit = {
error match { error match {
case Error.DuplicateDeclaration(ident) => case Error.DuplicateDeclaration(ident) =>
printPosition(ident.getPosition) printPosition(ident.getPosition)
println( println(s"Duplicate declaration of identifier ${ident.v}")
s"Duplicate declaration of identifier ${ident.v}"
)
highlight(ident.getPosition, ident.v.length) highlight(ident.getPosition, ident.v.length)
case Error.UndefinedIdentifier(ident, identType) => case Error.UndefinedIdentifier(ident, identType) =>
printPosition(ident.getPosition) printPosition(ident.getPosition)
println( println(s"Undefined ${identType.toString.toLowerCase()} ${ident.v}")
s"Undefined ${identType.toString.toLowerCase()} ${ident.v}"
)
highlight(ident.getPosition, ident.v.length) highlight(ident.getPosition, ident.v.length)
case Error.FunctionParamsMismatch(pos, expected, got) => case Error.FunctionParamsMismatch(pos, expected, got) =>
printPosition(pos) printPosition(pos)
println(s"Function expects $expected parameters, got $got") println(s"Function expects $expected parameters, got $got")
highlight(pos, 1) highlight(pos, 1)
case Error.TypeMismatch(pos, expected, got, msg) => case Error.TypeMismatch(pos, expected, got, msg) =>
println(s"Type mismatch: expected $expected, got $got") printPosition(pos)
println(msg)
highlight(pos, 1)
case Error.SemanticError(pos, msg) => case Error.SemanticError(pos, msg) =>
printPosition(pos) printPosition(pos)
println(s"$msg at line: ${pos.line} column: ${pos.column}") println(msg)
highlight(pos, 1) highlight(pos, 1)
case wacc.Error.InternalError(pos, msg) => case wacc.Error.InternalError(pos, msg) =>
printPosition(pos) printPosition(pos)

View File

@ -12,8 +12,6 @@ import cats.data.NonEmptyList
import parsley.errors.DefaultErrorBuilder import parsley.errors.DefaultErrorBuilder
import parsley.errors.ErrorBuilder import parsley.errors.ErrorBuilder
import parsley.errors.tokenextractors.LexToken import parsley.errors.tokenextractors.LexToken
import parsley.errors.patterns.VerifiedErrors
import parsley.character.char
object parser { object parser {
import lexer.implicits.implicitSymbol import lexer.implicits.implicitSymbol
@ -50,9 +48,6 @@ object parser {
case Expr case Expr
case Pair case Pair
val _parensCheck =
char('(').verifiedExplain("use keyword 'call' to call functions")
implicit val builder: ErrorBuilder[String] = new DefaultErrorBuilder with LexToken { implicit val builder: ErrorBuilder[String] = new DefaultErrorBuilder with LexToken {
def tokens = errTokens def tokens = errTokens
} }
@ -71,8 +66,8 @@ object parser {
GreaterEq from ">=" GreaterEq from ">="
) +: ) +:
SOps(InfixL)( SOps(InfixL)(
((Add from "+").label("binary operator") | _parensCheck), (Add from "+").label("binary operator"),
((Sub from "-").label("binary operator") | _parensCheck) (Sub from "-").label("binary operator")
) +: ) +:
SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +: SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +:
SOps(Prefix)( SOps(Prefix)(
@ -115,16 +110,13 @@ object parser {
private lazy val `<pair-elem-type>` = private lazy val `<pair-elem-type>` =
(`<base-type>` <**> (`<array-type>` </> identity)) | (`<base-type>` <**> (`<array-type>` </> identity)) |
((UntypedPairType from `<pair-type>`) <**> ((UntypedPairType from `<pair-type>`) <**>
((`<pair-elems-type>` <**> `<array-type>`.explain( ((`<pair-elems-type>` <**> `<array-type>`)
"non-erased pair types cannot be nested" .map(arr => (_: UntypedPairType) => arr) </> identity))
)).map(arr => (_: UntypedPairType) => arr) </> identity))
// Statements // Statements
private lazy val `<program>` = Program( private lazy val `<program>` = Program(
"begin" ~> many( "begin" ~> many(
atomic( atomic(`<type>`.label("function declaration") <~> `<ident>` <~ "(") <**> `<partial-func-decl>`
(`<type>`.label("function declaration") <~> `<ident>` <~ "(")
) <**> `<partial-func-decl>`
).label("function declaration"), ).label("function declaration"),
(atomic(`<ident>` <~ "(") ~> fail("function is missing return type") | `<stmt>`.label( (atomic(`<ident>` <~ "(") ~> fail("function is missing return type") | `<stmt>`.label(
"main program body" "main program body"
@ -132,9 +124,9 @@ object parser {
) )
private lazy val `<partial-func-decl>` = private lazy val `<partial-func-decl>` =
FuncDecl( FuncDecl(
sepBy(`<param>`, ",") <~ ")".label("parenthesis") <~ "is", sepBy(`<param>`, ",") <~ ")" <~ "is",
`<stmt>`.guardAgainst { `<stmt>`.guardAgainst {
case stmts if !stmts.isReturning => Seq("all functions must end in a returning statement") case stmts if !stmts.isReturning => Seq("All functions must end in a returning statement")
} <~ "end" } <~ "end"
) )
private lazy val `<param>` = Param(`<type>`, `<ident>`) private lazy val `<param>` = Param(`<type>`, `<ident>`)
@ -159,11 +151,8 @@ object parser {
) )
| While("while" ~> `<expr>`.labelWithType(LabelType.Expr) <~ "do", `<stmt>` <~ "done") | While("while" ~> `<expr>`.labelWithType(LabelType.Expr) <~ "do", `<stmt>` <~ "done")
| Block("begin" ~> `<stmt>` <~ "end") | Block("begin" ~> `<stmt>` <~ "end")
| VarDecl( | VarDecl(`<type>`, `<ident>` <~ "=", `<rvalue>`.label("valid initial value for variable"))
`<type>`, // TODO: Can we inline the name of the variable in the message
`<ident>` <~ "=".explain("functions must be defined on top of the main block"),
`<rvalue>`.label("valid initial value for variable")
)
| Assign(`<lvalue>` <~ "=", `<rvalue>`) | Assign(`<lvalue>` <~ "=", `<rvalue>`)
private lazy val `<lvalue>`: Parsley[LValue] = private lazy val `<lvalue>`: Parsley[LValue] =
`<pair-elem>` | `<ident-or-array-elem>` `<pair-elem>` | `<ident-or-array-elem>`