feat: reduce appearances of unknown-type, catch illegal function calls
Merge request lab2425_spring/WACC_37!19 Co-authored-by: Barf-Vader <47476490+Barf-Vader@users.noreply.github.com>
This commit is contained in:
commit
585ba6958a
@ -10,7 +10,7 @@ enum Error {
|
||||
case UndeclaredVariable(ident: ast.Ident)
|
||||
case UndefinedFunction(ident: ast.Ident)
|
||||
|
||||
case FunctionParamsMismatch(pos: Position, expected: Int, got: Int)
|
||||
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)
|
||||
@ -38,10 +38,13 @@ def printError(error: Error)(using errorContent: String): Unit = {
|
||||
printPosition(ident.pos)
|
||||
println(s"Undefined function ${ident.v}")
|
||||
highlight(ident.pos, ident.v.length)
|
||||
case Error.FunctionParamsMismatch(pos, expected, got) =>
|
||||
printPosition(pos)
|
||||
case Error.FunctionParamsMismatch(id, expected, got, funcType) =>
|
||||
printPosition(id.pos)
|
||||
println(s"Function expects $expected parameters, got $got")
|
||||
highlight(pos, 1)
|
||||
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)
|
||||
println(s"Type mismatch: $msg\nExpected: $expected\nGot: $got")
|
||||
|
@ -2,7 +2,7 @@ package wacc
|
||||
|
||||
import parsley.Result
|
||||
import parsley.Parsley
|
||||
import parsley.Parsley.{atomic, many, notFollowedBy, pure}
|
||||
import parsley.Parsley.{atomic, many, notFollowedBy, pure, unit}
|
||||
import parsley.combinator.{countSome, sepBy}
|
||||
import parsley.expr.{precedence, SOps, InfixL, InfixN, InfixR, Prefix, Atoms}
|
||||
import parsley.errors.combinator._
|
||||
@ -95,7 +95,9 @@ object parser {
|
||||
private val `<ident>` =
|
||||
Ident(ident) | some("*" | "&").verifiedExplain("pointer operators are not allowed")
|
||||
private lazy val `<ident-or-array-elem>` =
|
||||
`<ident>` <**> (`<array-indices>` </> identity)
|
||||
(`<ident>` <~ ("(".verifiedExplain(
|
||||
"functions can only be called using 'call' keyword"
|
||||
) | unit)) <**> (`<array-indices>` </> identity)
|
||||
private val `<array-indices>` = ArrayElem(some("[" ~> `<expr>` <~ "]"))
|
||||
|
||||
// Types
|
||||
@ -175,7 +177,15 @@ object parser {
|
||||
`<rvalue>`.label("valid initial value for variable")
|
||||
)
|
||||
// TODO: Can we inline the name of the variable in the message
|
||||
| Assign(`<lvalue>` <~ "=", `<rvalue>`)
|
||||
| Assign(
|
||||
`<lvalue>` <~ ("=" | "(".verifiedExplain(
|
||||
"function calls must use the 'call' keyword and the result must be assigned to a variable"
|
||||
)),
|
||||
`<rvalue>`
|
||||
) |
|
||||
("call" ~> `<ident>`).verifiedExplain(
|
||||
"function calls' results must be assigned to a variable"
|
||||
)
|
||||
private lazy val `<lvalue>`: Parsley[LValue] =
|
||||
`<pair-elem>` | `<ident-or-array-elem>`
|
||||
private lazy val `<rvalue>`: Parsley[RValue] =
|
||||
|
@ -95,28 +95,27 @@ object typeChecker {
|
||||
)
|
||||
case Assign(lhs, rhs) =>
|
||||
val lhsTy = checkValue(lhs, Constraint.Unconstrained)
|
||||
checkValue(rhs, Constraint.Is(lhsTy, s"assignment must have type $lhsTy")) match {
|
||||
case ? =>
|
||||
(lhsTy, checkValue(rhs, Constraint.Is(lhsTy, s"assignment must have type $lhsTy"))) match {
|
||||
case (?, ?) =>
|
||||
ctx.error(
|
||||
Error.SemanticError(lhs.pos, "assignment with both sides of unknown type is illegal")
|
||||
)
|
||||
case _ => ()
|
||||
}
|
||||
case Read(lhs) =>
|
||||
val lhsTy = checkValue(lhs, Constraint.Unconstrained)
|
||||
lhsTy match {
|
||||
case Read(dest) =>
|
||||
checkValue(dest, Constraint.Unconstrained) match {
|
||||
case ? =>
|
||||
ctx.error(
|
||||
Error.SemanticError(lhs.pos, "cannot read into a destination with an unknown type")
|
||||
Error.SemanticError(dest.pos, "cannot read into a destination with an unknown type")
|
||||
)
|
||||
case _ =>
|
||||
lhsTy.satisfies(
|
||||
case destTy =>
|
||||
destTy.satisfies(
|
||||
Constraint.IsEither(
|
||||
KnownType.Int,
|
||||
KnownType.Char,
|
||||
"read must be applied to an int or char"
|
||||
),
|
||||
lhs.pos
|
||||
dest.pos
|
||||
)
|
||||
}
|
||||
case Free(lhs) =>
|
||||
@ -125,7 +124,7 @@ object typeChecker {
|
||||
Constraint.IsEither(
|
||||
KnownType.Array(?),
|
||||
KnownType.Pair(?, ?),
|
||||
"free must be an array or pair"
|
||||
"free must be applied to an array or pair"
|
||||
)
|
||||
)
|
||||
case Return(expr) =>
|
||||
@ -163,6 +162,7 @@ object typeChecker {
|
||||
checkValue(elem, Constraint.Is(KnownType.Int, "array index must be an int"))
|
||||
acc match {
|
||||
case KnownType.Array(innerTy) => Some(innerTy)
|
||||
case ? => Some(?)
|
||||
case nonArrayTy =>
|
||||
ctx.error(
|
||||
Error.TypeMismatch(elem.pos, KnownType.Array(?), acc, "cannot index into a non-array")
|
||||
@ -189,9 +189,9 @@ object typeChecker {
|
||||
)
|
||||
.satisfies(constraint, l.pos)
|
||||
case Call(id, args) =>
|
||||
val FuncType(retTy, paramTys) = ctx.funcType(id)
|
||||
val funcTy @ FuncType(retTy, paramTys) = ctx.funcType(id)
|
||||
if (args.length != paramTys.length) {
|
||||
ctx.error(Error.FunctionParamsMismatch(id.pos, paramTys.length, args.length))
|
||||
ctx.error(Error.FunctionParamsMismatch(id, paramTys.length, args.length, funcTy))
|
||||
}
|
||||
args.zip(paramTys).foreach { case (arg, paramTy) =>
|
||||
checkValue(arg, Constraint.Is(paramTy, s"argument type mismatch in function ${id.v}"))
|
||||
@ -246,18 +246,17 @@ object typeChecker {
|
||||
)
|
||||
KnownType.Bool.satisfies(constraint, op.pos)
|
||||
case op: (Less | LessEq | Greater | GreaterEq) =>
|
||||
val xTy = checkValue(
|
||||
op.x,
|
||||
Constraint.IsEither(
|
||||
val xConstraint = Constraint.IsEither(
|
||||
KnownType.Int,
|
||||
KnownType.Char,
|
||||
s"${op.name} operator must be applied to an int or char"
|
||||
)
|
||||
)
|
||||
checkValue(
|
||||
op.y,
|
||||
val yConstraint = checkValue(op.x, xConstraint) match {
|
||||
case ? => xConstraint
|
||||
case xTy =>
|
||||
Constraint.Is(xTy, s"${op.name} operator must be applied to values of the same type")
|
||||
)
|
||||
}
|
||||
checkValue(op.y, yConstraint)
|
||||
KnownType.Bool.satisfies(constraint, op.pos)
|
||||
case op: (And | Or) =>
|
||||
val operand = Constraint.Is(KnownType.Bool, s"${op.name} operator must be applied to a bool")
|
||||
|
Loading…
x
Reference in New Issue
Block a user