Reputation: 11
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
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