Lecture 6 — 2018-02-01

WhileNZ

We started defining a simple imperative language, which we called WhileNZ. Here’s its syntax and semantics:

``````n in Nat
v in Var (some set of variables)
e in Expr    ::= n | e1 plus e2 | e1 times e2 | neg e | x
c in Command ::= SKIP | c1 ; c2 | x := e | WHILENZ e DO c END

sigma in Store, the set of total functions assigning ints to variables
i.e. Var -> Z

(sigma[x |-> z])(y) = / z           if x = y
\ sigma(y)    otherwise

eval : Store -> Expr -> Z
eval(sigma, n)           = n
eval(sigma, e1 plus  e2) = eval(sigma, e1) + eval(sigma, e2)
eval(sigma, e1 times e2) = eval(sigma, e1) * eval(sigma, e2)
eval(sigma, neg e)       = -eval(sigma, e)
eval(sigma, x)           = sigma(x)

interp : Store -> Command -> Store
interp(sigma, SKIP)    = sigma
interp(sigma, c1 ; c2) = interp(interp(sigma, c1), c2)
interp(sigma, x := e)  = sigma[x|->eval(sigma, e)]
interp(sigma, WHILENZ e DO c END) =
if eval(sigma, e) = 0
then sigma
else interp(interp(sigma, c), WHILENZ e DO c)``````

We went over an example or two in class, running some loops and observing that sometimes `interp` wasn’t well defined, i.e., sometimes it ran forever, which isn’t the sort of thing that math is supposed to do.

Try running `interp` on the following programs… what do they do? Do they always terminate? If so, what values will you get out?

``````Y := 5;
X := 1;
WHILENZ Y DO
X := Y * X;
Y := Y - 1
END``````

What will `X` be? What about `Y`?

``````A := X;
WHILENZ A DO
B := B + 1
END``````

What values will `A`, `B` and `X` have?

``````X := 1;
WHILENZ X DO
X := X + 1
END``````

A simple interpreter

Here’s how to write an interpreter for our simple language consisting of arithmetic expressions, assignments, and loops.

The first order of business is thinking about variables: how should we represent them? Haskell’s map type seems like a good bet.

``````import qualified Data.Map as Map
import Data.Map (Map, (!))

type VarName = String

type Store = Map VarName Int

update :: VarName -> Int -> Store -> Store
update v n st = Map.insert v n st``````

Arithmetic expressions

We return to our old friend, `ArithExp`, adding a notion of variable.

``````data ArithExp =
Num Int
| Var VarName
| Plus ArithExp ArithExp
| Times ArithExp ArithExp
| Neg ArithExp
deriving (Show, Eq)

evalA :: Store -> ArithExp -> Int
evalA st (Var x) = st ! x -- Map.findWithDefault ?
evalA _  (Num n) = n
evalA st (Plus e1 e2) = evalA st e1 + evalA st e2
evalA st (Times e1 e2) = evalA st e1 * evalA st e2
evalA st (Neg e) = - (evalA st e)``````

The interpreter is pretty similar to our old one, but note that we take an extra argument, `st :: Store`. The store is where we look for variables. If a variable isn’t in the store, then `Map.!` will complain with an error.

Commands in `WhileNZ`

Having converted the mathematical grammar:

``s ::= skip | x := a | s1;s2 | while a s``

``````data WhileNZ =
Skip
| Assign VarName ArithExp
| Seq WhileNZ WhileNZ
| WhileNZ ArithExp WhileNZ
deriving Show``````

Let’s set about writing our interpreter. While `evalA :: Store -> ArithExp -> Int`, here `eval` takes a `Store` and a statement and returns a new store.

``````eval :: Store -> WhileNZ -> Store
eval st Skip = st
eval st (Assign x e) = update x (evalA st e) st
eval st (Seq c1 c2) = eval (eval st c1) c2
eval st eLoop@(WhileNZ e c) =
if evalA st e == 0
then st
else eval (eval st c) eLoop``````

We can try writing a program or two…

``````p1, p2 :: WhileNZ
p1 = Seq (Assign "x" (Plus (Var "x") (Num 1)))
(Assign "y" (Plus (Var "y") (Num (-1))))
p2 = Seq (Assign "y" (Num 5))
(Seq (Assign "x" (Num 0))
(WhileNZ (Var "y") p1))``````

…and then we can run them in a sample store:

``````stXY :: Store
stXY = Map.fromList [("x",0),("y",0)]``````