Garrett
Garrett

Reputation: 11

Scala Machine Emulator

I'm building a stack machine emulator and I'm having trouble figuring out how to update an environment/map in both Load and Store cases. My goals here are figuring out how to update a map and also iterating to get the correct value to be used to update a map.

case class LoadI(s: String) extends StackMachineInstruction
case class  StoreI(s: String) extends StackMachineInstruction
    def emulateSingleInstruction(stack: List[Double],
                                 env: Map[String, Double],
                                 ins: StackMachineInstruction): (List[Double], Map[String, Double]) = {
        ins match{
            case AddI => stack match{
                case i1 :: i2 :: tail => (i1 + i2 :: tail, env)
                case _ => throw new IllegalArgumentException()
            }
            case PushI(f) => (f :: stack,env)

            //broken
            case LoadI(s) => stack match {
                case Nil => throw new IllegalArgumentException()
                case i1 :: tail => (tail,env) match {
                    case (top,env) => (top, env + s -> top ) // Not clear on how to update an environment
                }
            }
            //broken
            case StoreI(s) => stack match {
                case Nil => throw new IllegalArgumentException()
                case i1 :: tail => // Need to take the value that s maps to in the environment. Let the value be v. Push v onto the top of the stack.
            }

            case PopI => stack match{
                case Nil => throw new IllegalArgumentException()
                case i1 :: tail => {
                    (tail,env)
                }
            }
        }
    }

Here is an example of a test case I have in a different file

test("stack machine test 3") {
        val lst1 = List(PushI(3.5), PushI(2.5), PushI(4.5), PushI(5.2), AddI, LoadI("x"), LoadI("y"), LoadI("z"), StoreI("y"), LoadI("w"))
        val fenv = StackMachineEmulator.emulateStackMachine(lst1)
        assert(fenv contains "x")
        assert(fenv contains "y")
        assert(fenv contains "z")
        assert( math.abs(fenv("x") - 9.7 ) <= 1e-05 )
        assert( math.abs(fenv("y") - 2.5 ) <= 1e-05 )
        assert( math.abs(fenv("z") - 3.5 ) <= 1e-05 )
    }

Upvotes: 0

Views: 216

Answers (1)

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27595

You can use .updated to update a map. Or add a tuple, but Scala has to know that something is a tuple (map + a -> b is seen as (map + a) -> b, so you have to use map + (a -> b)).

You can use .get, .getOrElse or .apply to extract value from map. They will: return Option, returns value or default value on missing, or extract value or throw if missing. So the last one kind of requires checking.

If you also remember that patterns can be composed in pattern matching you can write your interpreter as:

def emulateSingleInstruction(stack: List[Double],
                             env: Map[String, Double],
                             ins: StackMachineInstruction): (List[Double], Map[String, Double]) =
  (ins, stack) match {
    case (AddI, i1 :: i2 :: tail)          => ((i1 + i2) :: tail) -> env
    case (PushI(f), _)                     => (f :: stack) -> env
    case (LoadI(s), i1 :: tail)            => tail -> env.updated(s, i1)
    case (StoreI(s), _) if env.contains(s) => (env(s) :: stack) -> env
    case (PopI, _ :: tail)                 => tail -> env
    case _                                 => throw new IllegalArgumentException()
  }

Upvotes: 1

Related Questions