Untitled
unknown
plain_text
8 months ago
14 kB
4
Indexable
Never
package miniscala import miniscala.Ast.* import miniscala.Unparser.unparse import scala.io.StdIn /** * Interpreter for MiniScala. */ object Interpreter { sealed abstract class Val case class IntVal(v: Int) extends Val case class BoolVal(v: Boolean) extends Val case class FloatVal(v: Float) extends Val case class StringVal(v: String) extends Val case class TupleVal(vs: List[Val]) extends Val type VarEnv = Map[Var, Val] def eval(e: Exp, venv: VarEnv): Val = e match { case IntLit(c) => IntVal(c) case BoolLit(c) => BoolVal(c) case FloatLit(c) => FloatVal(c) case StringLit(c) => StringVal(c) case VarExp(x) => val y = venv.getOrElse(x, throw InterpreterError(s"Unknown identifier '$x'", e)) trace(s"Looking up value of $x = $y") y case BinOpExp(leftexp, op, rightexp) => val leftval = eval(leftexp, venv) val rightval = eval(rightexp, venv) val res = op match { case PlusBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => IntVal(v1 + v2) case (FloatVal(v1), FloatVal(v2)) => FloatVal(v1 + v2) case (IntVal(v1), FloatVal(v2)) => FloatVal(v1 + v2) case (FloatVal(v1), IntVal(v2)) => FloatVal(v1 + v2) case (StringVal(v1), StringVal(v2)) => StringVal(v1 + v2) case (StringVal(v1), IntVal(v2)) => StringVal(v1 + v2.toString) case (StringVal(v1), FloatVal(v2)) => StringVal(v1 + v2.toString) case (IntVal(v1), StringVal(v2)) => StringVal(v1.toString + v2) case (FloatVal(v1), StringVal(v2)) => StringVal(v1.toString + v2) case _ => throw InterpreterError(s"Type mismatch at '+', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case MinusBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => IntVal(v1 - v2) case (FloatVal(v1), FloatVal(v2)) => FloatVal(v1 - v2) case (IntVal(v1), FloatVal(v2)) => FloatVal(v1 - v2) case (FloatVal(v1), IntVal(v2)) => FloatVal(v1 - v2) case (StringVal(v1), IntVal(v2)) => IntVal(v1.toInt - v2) case (StringVal(v1), FloatVal(v2)) => FloatVal(v1.toFloat - v2) case (IntVal(v1), StringVal(v2)) => IntVal(v1 - v2.toInt) case (FloatVal(v1), StringVal(v2)) => FloatVal(v1 - v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '-', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case MultBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => IntVal(v1 * v2) case (FloatVal(v1), FloatVal(v2)) => FloatVal(v1 * v2) case (IntVal(v1), FloatVal(v2)) => FloatVal(v1 * v2) case (FloatVal(v1), IntVal(v2)) => FloatVal(v1 * v2) case (StringVal(v1), IntVal(v2)) => IntVal(v1.toInt * v2) case (StringVal(v1), FloatVal(v2)) => FloatVal(v1.toFloat * v2) case (IntVal(v1), StringVal(v2)) => IntVal(v1 * v2.toInt) case (FloatVal(v1), StringVal(v2)) => FloatVal(v1 * v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '*', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case DivBinOp() => if (rightval == IntVal(0) || rightval == FloatVal(0.0f)) throw InterpreterError(s"Division by zero", op) (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => IntVal(v1 / v2) case (FloatVal(v1), FloatVal(v2)) => FloatVal(v1 / v2) case (IntVal(v1), FloatVal(v2)) => FloatVal(v1 / v2) case (FloatVal(v1), IntVal(v2)) => FloatVal(v1 / v2) case (StringVal(v1), IntVal(v2)) => IntVal(v1.toInt / v2) case (StringVal(v1), FloatVal(v2)) => FloatVal(v1.toFloat / v2) case (IntVal(v1), StringVal(v2)) => IntVal(v1 / v2.toInt) case (FloatVal(v1), StringVal(v2)) => FloatVal(v1 / v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '/', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case ModuloBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => IntVal(v1 % v2) case (FloatVal(v1), FloatVal(v2)) => FloatVal(v1 % v2) case (IntVal(v1), FloatVal(v2)) => FloatVal(v1 % v2) case (FloatVal(v1), IntVal(v2)) => FloatVal(v1 % v2) case (StringVal(v1), IntVal(v2)) => IntVal(v1.toInt % v2) case (StringVal(v1), FloatVal(v2)) => FloatVal(v1.toFloat % v2) case (IntVal(v1), StringVal(v2)) => IntVal(v1 % v2.toInt) case (FloatVal(v1), StringVal(v2)) => FloatVal(v1 % v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '%', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case MaxBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => IntVal(v1 max v2) case (FloatVal(v1), FloatVal(v2)) => FloatVal(v1 max v2) case (IntVal(v1), FloatVal(v2)) => FloatVal(v1 max v2) case (FloatVal(v1), IntVal(v2)) => FloatVal(v1 max v2) case (StringVal(v1), IntVal(v2)) => IntVal(v1.toInt max v2) case (StringVal(v1), FloatVal(v2)) => FloatVal(v1.toFloat max v2) case (IntVal(v1), StringVal(v2)) => IntVal(v1 max v2.toInt) case (FloatVal(v1), StringVal(v2)) => FloatVal(v1 max v2.toInt) case _ => throw InterpreterError(s"Type mismatch at 'max', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case EqualBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => BoolVal(v1 == v2) case (FloatVal(v1), FloatVal(v2)) => BoolVal(v1 == v2) case (IntVal(v1), FloatVal(v2)) => BoolVal(v1 == v2) case (FloatVal(v1), IntVal(v2)) => BoolVal(v1 == v2) case (StringVal(v1), StringVal(v2)) => BoolVal (v1 == v2) case (BoolVal(v1), BoolVal(v2)) => BoolVal (v1 == v2) case (TupleVal(vs1), TupleVal(vs2)) => BoolVal(vs1 == vs2) case (StringVal(v1), IntVal(v2)) => BoolVal(v1.toInt == v2) case (StringVal(v1), FloatVal(v2)) => BoolVal(v1.toFloat == v2) case (IntVal(v1), StringVal(v2)) => BoolVal(v1 == v2.toInt) case (FloatVal(v1), StringVal(v2)) => BoolVal(v1 == v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '==', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case LessThanBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => BoolVal(v1 < v2) case (FloatVal(v1), FloatVal(v2)) => BoolVal(v1 < v2) case (IntVal(v1), FloatVal(v2)) => BoolVal(v1 < v2) case (FloatVal(v1), IntVal(v2)) => BoolVal(v1 < v2) case (StringVal(v1), IntVal(v2)) => BoolVal(v1.toInt < v2) case (StringVal(v1), FloatVal(v2)) => BoolVal(v1.toFloat < v2) case (IntVal(v1), StringVal(v2)) => BoolVal(v1 < v2.toInt) case (FloatVal(v1), StringVal(v2)) => BoolVal(v1 < v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '<', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case LessThanOrEqualBinOp() => (leftval, rightval) match { case (IntVal(v1), IntVal(v2)) => BoolVal(v1 <= v2) case (FloatVal(v1), FloatVal(v2)) => BoolVal(v1 <= v2) case (IntVal(v1), FloatVal(v2)) => BoolVal(v1 <= v2) case (FloatVal(v1), IntVal(v2)) => BoolVal(v1 <= v2) case (StringVal(v1), IntVal(v2)) => BoolVal(v1.toInt <= v2) case (StringVal(v1), FloatVal(v2)) => BoolVal(v1.toFloat <= v2) case (IntVal(v1), StringVal(v2)) => BoolVal(v1 <= v2.toInt) case (FloatVal(v1), StringVal(v2)) => BoolVal(v1 <= v2.toInt) case _ => throw InterpreterError(s"Type mismatch at '<=', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case AndBinOp() => (leftval, rightval) match { /*case (IntVal(v1), IntVal(v2)) => BoolVal(v1 && v2) case (FloatVal(v1), FloatVal(v2)) => BoolVal(v1 && v2) case (IntVal(v1), FloatVal(v2)) => BoolVal(v1 && v2) case (FloatVal(v1), IntVal(v2)) => BoolVal(v1 && v2)*/ case (BoolVal(v1), BoolVal(v2)) => BoolVal(v1 && v2) case (StringVal(v1), BoolVal(v2)) => BoolVal(v1.toBoolean && v2) case (BoolVal(v1), StringVal(v2)) => BoolVal(v1 && v2.toBoolean) case (StringVal(v1), StringVal(v2)) => BoolVal(v1.toBoolean && v2.toBoolean) case _ => throw InterpreterError(s"Type mismatch at '&&', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } case OrBinOp() => (leftval, rightval) match { /*case (IntVal(v1), IntVal(v2)) => BoolVal(v1 || v2) case (FloatVal(v1), FloatVal(v2)) => BoolVal(v1 || v2) case (IntVal(v1), FloatVal(v2)) => BoolVal(v1 || v2) case (FloatVal(v1), IntVal(v2)) => BoolVal(v1 || v2)*/ case (BoolVal(v1), BoolVal(v2)) => BoolVal(v1 || v2) case (StringVal(v1), BoolVal(v2)) => BoolVal(v1.toBoolean || v2) case (BoolVal(v1), StringVal(v2)) => BoolVal(v1 || v2.toBoolean) case (StringVal(v1), StringVal(v2)) => BoolVal(v1.toBoolean || v2.toBoolean) case _ => throw InterpreterError(s"Type mismatch at '||', unexpected values ${valueToString(leftval)} and ${valueToString(rightval)}", op) } } trace(s"Evaluating ($leftval${unparse(op)}$rightval) = $res" ) res case UnOpExp(op, exp) => val expval = eval(exp, venv) op match { case NegUnOp() => expval match { case IntVal(v) => IntVal(-v) case FloatVal(v) => FloatVal(-v) case StringVal(v) => FloatVal(-v.toFloat) case _ => throw InterpreterError(s"Type mismatch at '-', unexpected value ${valueToString(expval)}", op) } case NotUnOp() => expval match case BoolVal(v) => BoolVal(!v) case StringVal(v) => BoolVal(!v.toBoolean) case _ => throw InterpreterError(s"Type mismatch at '!', unexpected value ${valueToString(expval)}", op) } case IfThenElseExp(condexp, thenexp, elseexp) => val boolVal = eval(condexp, venv) boolVal match { case BoolVal(true) => eval(thenexp, venv) case BoolVal(false) => eval(elseexp, venv) case IntVal(v) => ??? case FloatVal(v) => ??? case StringVal(v) => ??? case TupleVal(vs) => ??? } case BlockExp(vals, exp) => var venv1 = venv for (d <- vals) trace(s"--- Assigning value of ${d.x} ---") venv1 = venv1 + (d.x -> eval(d.exp, venv1)) trace(s"--- Assigning ${d.x} to above ---") trace("--- Evaluating expression using new VarEnv ---") val e = eval(exp, venv1) trace("--- Done evaluating using VarEnv ---") e case TupleExp(exps) => var vals = List[Val]() for (exp <- exps) vals = eval(exp, venv) :: vals TupleVal(vals.reverse) case MatchExp(exp, cases) => val expval = eval(exp, venv) expval match { case TupleVal(vs) => for (c <- cases) { if (vs.length == c.pattern.length) { ??? } } throw InterpreterError(s"No case matches value ${valueToString(expval)}", e) case _ => throw InterpreterError(s"Tuple expected at match, found ${valueToString(expval)}", e) } } /** * Checks whether value `v` has type `ot` (if present), generates runtime type error otherwise. */ def checkValueType(v: Val, ot: Option[Type], n: AstNode): Unit = ot match { case Some(t) => (v, t) match { case (IntVal(_), IntType()) | (BoolVal(_), BoolType()) | (FloatVal(_), FloatType()) | (IntVal(_), FloatType()) | (StringVal(_), StringType()) => // do nothing case (TupleVal(vs), TupleType(ts)) if vs.length == ts.length => for ((vi, ti) <- vs.zip(ts)) checkValueType(vi, Some(ti), n) case _ => throw InterpreterError(s"Type mismatch: value ${valueToString(v)} does not match type ${unparse(t)}", n) } case None => // do nothing } /** * Converts a value to its string representation (for error messages). */ def valueToString(v: Val): String = v match { case IntVal(c) => c.toString case FloatVal(c) => c.toString case BoolVal(c) => c.toString case StringVal(c) => c case TupleVal(vs) => vs.map(valueToString).mkString("(", ",", ")") } /** * Builds an initial environment, with a value for each free variable in the program. */ def makeInitialVarEnv(program: Exp): VarEnv = { var venv = Map[Var, Val]() for (x <- Vars.freeVars(program)) { print(s"Please provide an integer value for the variable $x: ") venv = venv + (x -> IntVal(StdIn.readInt())) } venv } /** * Prints message if option -trace is used. */ def trace(msg: String): Unit = if (Options.trace) println(msg) /** * Exception thrown in case of MiniScala runtime errors. */ class InterpreterError(msg: String, node: AstNode) extends MiniScalaError(s"Runtime error: $msg", node.pos) }
Leave a Comment