Untitled
unknown
plain_text
2 years ago
14 kB
22
Indexable
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)
}
Editor is loading...
Leave a Comment