diff --git a/src/main/wacc/Error.scala b/src/main/wacc/Error.scala index 89e03d9..ba810f5 100644 --- a/src/main/wacc/Error.scala +++ b/src/main/wacc/Error.scala @@ -35,7 +35,7 @@ def printError(error: Error)(using errorContent: String): Unit = { highlight(pos, 1) case Error.TypeMismatch(pos, expected, got, msg) => printPosition(pos) - println(msg) + println(s"Type mismatch: $msg\nExpected: $expected\nGot: $got") highlight(pos, 1) case Error.SemanticError(pos, msg) => printPosition(pos) diff --git a/src/main/wacc/ast.scala b/src/main/wacc/ast.scala index 8ea99d7..f885843 100644 --- a/src/main/wacc/ast.scala +++ b/src/main/wacc/ast.scala @@ -60,35 +60,45 @@ object ast { object Chr extends ParserBridgePos1[Expr6, Chr] // Binary operators - sealed trait BinaryOp extends Expr { + sealed trait BinaryOp(val name: String) extends Expr { val x: Expr val y: Expr } - case class Add(x: Expr4, y: Expr5)(val pos: Position) extends Expr4 with BinaryOp + case class Add(x: Expr4, y: Expr5)(val pos: Position) extends Expr4 with BinaryOp("addition") object Add extends ParserBridgePos2[Expr4, Expr5, Add] - case class Sub(x: Expr4, y: Expr5)(val pos: Position) extends Expr4 with BinaryOp + case class Sub(x: Expr4, y: Expr5)(val pos: Position) extends Expr4 with BinaryOp("subtraction") object Sub extends ParserBridgePos2[Expr4, Expr5, Sub] - case class Mul(x: Expr5, y: Expr6)(val pos: Position) extends Expr5 with BinaryOp + case class Mul(x: Expr5, y: Expr6)(val pos: Position) + extends Expr5 + with BinaryOp("multiplication") object Mul extends ParserBridgePos2[Expr5, Expr6, Mul] - case class Div(x: Expr5, y: Expr6)(val pos: Position) extends Expr5 with BinaryOp + case class Div(x: Expr5, y: Expr6)(val pos: Position) extends Expr5 with BinaryOp("division") object Div extends ParserBridgePos2[Expr5, Expr6, Div] - case class Mod(x: Expr5, y: Expr6)(val pos: Position) extends Expr5 with BinaryOp + case class Mod(x: Expr5, y: Expr6)(val pos: Position) extends Expr5 with BinaryOp("modulus") object Mod extends ParserBridgePos2[Expr5, Expr6, Mod] - case class Greater(x: Expr4, y: Expr4)(val pos: Position) extends Expr3 with BinaryOp + case class Greater(x: Expr4, y: Expr4)(val pos: Position) + extends Expr3 + with BinaryOp("strictly greater than") object Greater extends ParserBridgePos2[Expr4, Expr4, Greater] - case class GreaterEq(x: Expr4, y: Expr4)(val pos: Position) extends Expr3 with BinaryOp + case class GreaterEq(x: Expr4, y: Expr4)(val pos: Position) + extends Expr3 + with BinaryOp("greater than or equal to") object GreaterEq extends ParserBridgePos2[Expr4, Expr4, GreaterEq] - case class Less(x: Expr4, y: Expr4)(val pos: Position) extends Expr3 with BinaryOp + case class Less(x: Expr4, y: Expr4)(val pos: Position) + extends Expr3 + with BinaryOp("strictly less than") object Less extends ParserBridgePos2[Expr4, Expr4, Less] - case class LessEq(x: Expr4, y: Expr4)(val pos: Position) extends Expr3 with BinaryOp + case class LessEq(x: Expr4, y: Expr4)(val pos: Position) + extends Expr3 + with BinaryOp("less than or equal to") object LessEq extends ParserBridgePos2[Expr4, Expr4, LessEq] - case class Eq(x: Expr3, y: Expr3)(val pos: Position) extends Expr2 with BinaryOp + case class Eq(x: Expr3, y: Expr3)(val pos: Position) extends Expr2 with BinaryOp("equality") object Eq extends ParserBridgePos2[Expr3, Expr3, Eq] - case class Neq(x: Expr3, y: Expr3)(val pos: Position) extends Expr2 with BinaryOp + case class Neq(x: Expr3, y: Expr3)(val pos: Position) extends Expr2 with BinaryOp("inequality") object Neq extends ParserBridgePos2[Expr3, Expr3, Neq] - case class And(x: Expr2, y: Expr1)(val pos: Position) extends Expr1 with BinaryOp + case class And(x: Expr2, y: Expr1)(val pos: Position) extends Expr1 with BinaryOp("logical and") object And extends ParserBridgePos2[Expr2, Expr1, And] - case class Or(x: Expr1, y: Expr)(val pos: Position) extends Expr with BinaryOp + case class Or(x: Expr1, y: Expr)(val pos: Position) extends Expr with BinaryOp("logical or") object Or extends ParserBridgePos2[Expr1, Expr, Or] // Types diff --git a/src/main/wacc/typeChecker.scala b/src/main/wacc/typeChecker.scala index 38b34e3..8c01379 100644 --- a/src/main/wacc/typeChecker.scala +++ b/src/main/wacc/typeChecker.scala @@ -159,24 +159,25 @@ object typeChecker { ctx.typeOf(id).satisfies(constraint, id.pos) case ArrayElem(id, indices) => val arrayTy = ctx.typeOf(id) - val elemTy = indices.toList.foldRight(arrayTy) { (elem, acc) => + val elemTy = indices.foldLeftM(arrayTy) { (acc, elem) => checkValue(elem, Constraint.Is(KnownType.Int, "array index must be an int")) acc match { - case KnownType.Array(innerTy) => innerTy - case _ => + case KnownType.Array(innerTy) => Some(innerTy) + case nonArrayTy => ctx.error( Error.TypeMismatch(elem.pos, KnownType.Array(?), acc, "cannot index into a non-array") ) + None } } - elemTy.satisfies(constraint, id.pos) + elemTy.getOrElse(?).satisfies(constraint, id.pos) case Parens(expr) => checkValue(expr, constraint) case l @ ArrayLiter(elems) => KnownType - .Array(elems.foldRight[SemType](?) { case (elem, acc) => + .Array(elems.foldLeft[SemType](?) { case (acc, elem) => checkValue( elem, - Constraint.IsSymmetricCompatible(acc, "array elements must have the same type") + Constraint.IsSymmetricCompatible(acc, s"array elements must have the same type") ) }) .satisfies(constraint, l.pos) @@ -233,13 +234,16 @@ object typeChecker { // Binary operators case op: (Add | Sub | Mul | Div | Mod) => - val operand = Constraint.Is(KnownType.Int, "binary operator must be applied to an int") + val operand = Constraint.Is(KnownType.Int, s"${op.name} operator must be applied to an int") checkValue(op.x, operand) checkValue(op.y, operand) KnownType.Int.satisfies(constraint, op.pos) case op: (Eq | Neq) => val xTy = checkValue(op.x, Constraint.Unconstrained) - checkValue(op.y, Constraint.Is(xTy, "equality must be applied to values of the same type")) + checkValue( + op.y, + Constraint.Is(xTy, s"${op.name} operator must be applied to values of the same type") + ) KnownType.Bool.satisfies(constraint, op.pos) case op: (Less | LessEq | Greater | GreaterEq) => val xTy = checkValue( @@ -247,13 +251,16 @@ object typeChecker { Constraint.IsEither( KnownType.Int, KnownType.Char, - "comparison must be applied to an int or char" + s"${op.name} operator must be applied to an int or char" ) ) - checkValue(op.y, Constraint.Is(xTy, "comparison must be applied to values of the same type")) + checkValue( + op.y, + Constraint.Is(xTy, s"${op.name} operator must be applied to values of the same type") + ) KnownType.Bool.satisfies(constraint, op.pos) case op: (And | Or) => - val operand = Constraint.Is(KnownType.Bool, "logical operator must be applied to a bool") + val operand = Constraint.Is(KnownType.Bool, s"${op.name} operator must be applied to a bool") checkValue(op.x, operand) checkValue(op.y, operand) KnownType.Bool.satisfies(constraint, op.pos) diff --git a/src/main/wacc/types.scala b/src/main/wacc/types.scala index 41d4124..549d8a1 100644 --- a/src/main/wacc/types.scala +++ b/src/main/wacc/types.scala @@ -9,9 +9,11 @@ object types { case KnownType.Bool => "bool" case KnownType.Char => "char" case KnownType.String => "string" + case KnownType.Array(?) => "array" case KnownType.Array(elem) => s"$elem[]" + case KnownType.Pair(?, ?) => "pair" case KnownType.Pair(left, right) => s"pair($left, $right)" - case ? => "?" + case ? => "<unknown-type>" } }