Untitled

mail@pastecode.io avatar
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