This is a tiny cave with entrances west and north, and a dark, forbidding staircase leading down.

> wait

Time passes…

A gust of wind blows out your candles!

> go down

Code for this episode can be found in https://github.com/ericlippert/flathead/tree/blog12.

Last time we saw how to find the default values for locals in a routine activation. Now let’s actually create the local variable store.

The operations we need to perform on a local store (so far) are (1) initialize all the locals to their default values, (2) read a local, and (3) write a local. Of course the first and third operations return a new local store; a local store is immutable, like all the data structures in this purely functional interpreter will be.

The store maps integer local numbers – from 1 to 15 – onto unsigned word values. Our old friend the integer map seems like a good candidate.

For reasons which will become clear later, we also need to keep track of two numbers. First, how many locals are there? And second, a routine call causes the arguments of the call to be copied to the locals of the newly-activated routine; we will later need to know how many arguments were provided vs how many locals there were.

type t = { locals : int IntMap.t; count : int; arguments_supplied : int; }

I like to have an empty object for cases where it makes sense.

let empty = { locals = IntMap.empty; count = 0; arguments_supplied = 0 }

Reading is straightforward. When writing we ensure that we have an unsigned value. (In an earlier version of my interpreter I was accidentally writing negative numbers as local values and things got very messed up!)

let read_local local_store (Local local) = IntMap.find local local_store.locals let write_local local_store (Local local) value = let value = unsigned_word value in let locals = IntMap.add local value local_store.locals in { local_store with locals }

Here’s where we deal with creating a new local store given a routine.

let add local_store (Local n) default_value = let locals = IntMap.add n default_value local_store.locals in let count = max local_store.count n in { local_store with locals; count } let create_default_locals story routine_address = let count = Routine.locals_count story routine_address in let rec aux acc i = if i > count then acc else let default_value = Routine.local_default_value story routine_address i in let new_store = add acc (Local i) default_value in aux new_store (i + 1) in aux empty 1

And finally I like to have a helper method for displaying my work:

let display local_store = let to_string local value = Printf.sprintf "local%01x=%04x " (local - 1) value in let folder local value acc = acc ^ (to_string local value) in let locals = local_store.locals in IntMap.fold folder locals ""

Note the use of `IntMap.fold`

, which is the logical equivalent of `List.fold_left`

.

Now we can pick a routine address and create a local store with the default values:

let () = let story = Story.load "minizork.z3" in let locals = Local_store.create_default_locals story (Routine 0x3b36) in let text = Local_store.display locals in Printf.printf "%s\n" text

which outputs

local0=0000 local1=0000 local2=0000

All right, maybe not very interesting there, and now we see why in version 5 they simply initialized every local to zero. The majority of the time that’s how it goes.

We said that interpreter state is a stack of frames, each frame consists of a local store and an evaluation stack. We’ve got the store. **Next time on FAIC:** we’ll implement the evaluation stack. It’ll be pretty straightforward.

I’m really enjoying this blog series. I’ve been following along to try and learn some Haskell, but have picked up a bit more on the way. I hope that you are able to find some time to pick up again where you have currently left off.

Anyway, this is probably not a big deal for others, but I got stuck for a bit based on this sentence:

“Note the use of IntMap.fold, which is the logical equivalent of List.fold_left.”

I think this should be:

“Note the use of IntMap.fold, which is the logical equivalent of List.fold_right”

The type signature for fold is (http://caml.inria.fr/pub/docs/manual-ocaml/libref/Map.Make.html):

val fold : (key -> ‘a -> ‘b -> ‘b) -> ‘a t -> ‘b -> ‘b

The type signature for fold_left is (http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html):

val fold_left : (‘a -> ‘b -> ‘a) -> ‘a -> ‘b list -> ‘a

But compare that to fold_right (http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html):

fold_right : (‘a -> ‘b -> ‘b) -> ‘a list -> ‘b -> ‘b

FWIW, I ended up using (in Haskell’s Data.IntMap.Lazy):

foldrWithKey :: (Key -> a -> b -> b) -> b -> IntMap a -> b