CS558 Homework #2
Due Due 8:00 AM, Monday, Jan 18, 2016

Homework must be submitted via D2L. All submitted files (four for this assignment sol2A.e2, sol2B.py, sol2B.hs, sol2C.txt) must be submitted in the appropriate D2L directory in the drop box HW2. It is your responsibility to submit the homework in the proper format with the proper names. For example.

-- Homework 2  Tom Smith   tom_smith@gmail.com

All programs mentioned can be downloaded from the this document

Imperative Language interpretor.

Consider a simple language of imperative expressions, which we'll call E2. We will store programs in this language in files with extension *.e2. It's concretized abstract syntax is given by the following extended grammar:
prog := exp
exp := var
| int
| '(' ':=' var exp ')'
| '(' 'while' exp exp ')'
| '(' 'if' exp exp exp ')'
| '(' 'write' exp ')'
| '(' 'block' { exp } ')'
| '(' '+' exp exp ')'
| '(' '-' exp exp ')'
| '(' '*' exp exp ')'
| '(' '/' exp exp ')'
| '(' '<=' exp exp ')'
var := string of letters
As before, comments may be included by enclosing them between comment braces '{-' and '-}' and they may be nested.

An informal semantics for E2 is as follows. The evaluation of each expression (and hence of a program) yields a single integer result.

An example program primes.e2 written in this language is available for you to view.

Two E2 interpreters have been provided, one in Python ( Hw2.py ) and the other in Haskell ( Hw2.hs ).

Each interpreter reads a file containing an E2 program in the syntax described above, echoes the program (to confirm correct parsing), evaluates the program (possibly producing output from write expressions), and displays the evaluation result.

The Python program interprets the abstract syntax directly. The Haskell program first translates it into stack machine code (for an extended and somewhat different version of the stack machine from Homework 1), prints out the stack code (for debugging purposes), and then executes it. For example, here is a trace from inside GHCI on the file count.e2

*Main> interp "count.e2"
Expression:
(block (:= n 1)
       (while (<= n 2) (block (write n) (:= n (+ n 1)) ))
       )
Compiles to:
[Const 1, Dup, Store n, Pop, Label 0:, Const 2, Load n, Swap, Leq,
 Branchz 1, Load n, Dup, Print, Pop, Const 1, Load n, Plus, Dup,
 Store n, Pop, Branch 0, Label 1:, Const 0]
Executing:

1
2

Evaluates to: 0

For more debugging help, you can trace the behavior of the stack machine by typing "trace" at the GHCI prompt, This commnad flips the state of the tracing flag. Here is a transcript of turing the trace on and reexecuting count.e2. One should do this before executing the Haskell function interp to start the read-eval-print function.

One can also control tracing programatically by using the IO action writeIORef trace_on True inside a Haskell function with type IO.

What you need to do.

  1. One of the interpreters evaluates the operands to binary operators ( + , - , * , / , and <= ) from left-to-right; the other from right-to-left. Write an example E2 program, not involving write statements, that produces diffierent answers depending on the order of evaluation of these operands, and hence diffierent for the two interpreters. Put your program in file sol2A.e2 and and submit this file. Your program must include a comment stating: which interpreter has which behavior; and based on this program, what (if anything) can be deduced about the order of evaluation of operands in the Python and Haskell languages themselves. (20 points)

  2. Modify each interpreter to support a new kind of expression, as specified below. Some of the code for parsing these expressions is already present; what you need to do is extend the abstract syntax, add printing code, and add evaluation code (in Python) or compilation code (in Haskell). When modifying the Haskell version don't change or add to the stack machine.

    The for expression has the following syntax: exp := '(' 'for' var exp exp exp ')'

    Evaluating (for x e1 e2 e3 ) first evaluates e1 to a value v1 and stores v1 into x . It then repeats the following steps: evaluate e2 to a value v2 ; fetch x ; if x > v2 then terminate evaluation of the for, yielding the value 0; otherwise, evaluate e3 and discard the yielded result, fetch x , add one to it, store back into x , and repeat.

    When modifying the code, study carefully how the "while" statement is implemented. Look for how "while" statements are parsed, how they are evaluated (in python) and how they are translated to stack machine code (in Haskell). This will be a good guide for modifying the code to add "for" statements. Searching for "while" and 'While' in the source will help identify places in the code where things neeed to be added or modified.

    For example,

    (for i 1 10 (write i))

    prints the numbers from 1 to 10, and yields the value 0. Make sure you get the order of evaluation right. For example, the bizarre program

    (for i i (block (:= i (+ i 2)) 10) (block (write i) (:= i (+ i 3))))

    prints out the numbers 2,8 and yields the value 0. Hints: Model your code on the existing code for while. The Haskell version may require some tricky stack manipulation to get the order of evaluation right; once again, you may find the SWAP instruction useful.

    Put your revised Python interpreter in sol2B.py (30 points), and your revised Haskell interpreter in sol2B.hs (30 points), and submit both these files.

    Axiomatic Semantics

    Consider the very simple language used to illustrate axiomatic semantics in lecture. Suppose we add a non-deterministic guarded if statement to our language. The syntax of this statement is
    gif E1 -> S1
    | E2 -> S2
    ...
    | En -> Sn
    end
    

    where the Ei are boolean expressions. This statement is executed by non-deterministically choosing any one i such that Ei evaluates to true, and executing the corresponding Si . Among other things, this statement gives an elegant, symmetric way to express functions like max , e.g.

    gif x1 >= x2 -> max := x1
    |   x2 >= x1 -> max := x2
    end
    

    Note that if x1 = x2 then either branch might be chosen non-deterministically. In this example, the resulting value of max will be the same either way, but we can also write guarded gif's that are intentionally non-determinstic, e.g., to simulate a coin toss:

    gif true -> coin := 0
    | true -> coin := 1
    end  { coin }
    
    This question has two parts. Type your answers to both parts into a file sol2C.txt, and submit this file.
    1. Extend the axiomatic semantics given in lecture by writing down an appropriate rule of inference (GIF) for gif statements. (5 points)
    2. Derive the following triple, by presenting a formal proof tree for it (as shown in lecture).
      { z < 0 }
      while (x > -5) do
      gif x <= 0 -> y := -x
      | x >= 0 -> y := x
      end;
      z := z - y
      end
      { z < 0 }
      
      (15 points)

    Remember to label each node in the tree with the appropriate axiom or rule of inference. You'll need one (WHILE), one (COMP), one (GIF), three (ASSIGN), and four (CONSEQ) steps. Don't worry about formatting your answer beautifully, so long as it is clear.