CSE 526
Spring 2005
Stony Brook
Principles of Programming Languages
Annie Liu
Homework 9
Handout H9
Mar. 22, 2005
Due Apr. 5

Interpreter for an Imperative Language with Aliasing and I/O.

This assignment has two parts, each worth 90% and 10% of the grades, respectively. Suggestions for how to proceed are given at the end. The assignment is due before class on Tuesday April 5. Please submit your file(s) as a single attachment (made using tar or zip if necessary) in an email to cse526ATcsDOTsunysbDOTedu.

Part 1. Implementation.

Write an interpreter for IMP-+Alias+IO, a simple imperative language that has I/O and aliasing. The language is IMP modified as follows.

1. It does not have boolean expressions; nor does it bother to include subtraction and multiplication in arithmetic expressions. Instead, it has two additional kinds of expressions: read and a after c. In another word, expressions have the syntax a ::= n | X | a1+a2 | read | a after c.

Expression read is a function, in the C sense, which returns the next value of the input stream, and moved the input pointer one place forward. It is very much like the C getchar() function.

Expression a after c execute command c and then evaluate expression a.

2. In if and while commands, the conditions are arithmetic expressions, using 0 for false and everything else for true. Also, there are three additional kinds of commands: write a, let X:=a in c, and alias Y to X in C.

Command write a writes the value of a on the output stream.

Command let X:=a in c declares variable X, initializes its value to the value of a and then executes c. All variables must be declared before used.

Command alias Y to X in c declares a local alias of the global variable X inside command c, so an update to Yalso updates the other and vice versa.

Part 2. Related requirements.

Language. You must do the implementation using Python.

Tests. Test your interpreter on the examples below and at least one more small example that does something more interesting. As in Homeworks 2 and 7, you are not asked to do parsing; directly build these examples into your internal representation. For each example, build a particular input stream for it too. For each example, turn in a printout of the example program and the input, output, and error streams.

name   program

err0   write X
err1   let X:=X in write X
err2   alias Y to X in write Y
err3   X:=1; write X

skip   skip

if0    if 0 then write X else write 0      should write 0
if1    if 1 then write 1 else write X      should write 1

while0 while 0 do write X                  should write nothing
while1 while 1 do skip                     should loop forever,
                                           until you kill it

seq    write 1; write 2                    should write 1,2

let    let X:=1 in write(X)                should write 1

assign let X:=12 in (write(X); X:=X+13; write(X))
                                           should write 12, 25

lets   let X:=0 in (write(X); let X:=1 in write(X); X:=X+2; write(X))
                                           should write 0,1,2

alias  let X:=0 in (alias Y to X in (Y:=1; write(X); X:=3; write(Y);
                                     let X:=5 in (write(X); write(Y));
                                     write(X); write(Y)))
                                           should write 1,3,5,3,3,3

after  let X:=0 in (write(X); write(X after(write(X);X:=1)); write(X))
                                           should write 0,0,1,1

read   write(read)                         should copy one value
                                           from input to output

reads  let X:=read; while X do (write(X+X); X:=read)
                                           should double input 
                                           until a 0 is read

How to proceed?

First, define internal representation of expressions and commands, as in Homework 2. Then, define the evaluation and execution operations, similar to those in Homework 2, but with the following two major differences.

1. In IMP, identifiers are directly interpreted as locations, so different identifiers corresponds to different locations, and hence IMP does not support aliasing. Since IMP-+Alias+IO has aliasing, it is reasonable that identifiers be bound to locations, which are addresses of the store in some abstract machine. If X and Y are aliases for each other, then the they are bound to the same location. The binding of identifiers to locations is called an environment. The only commands that change the environment are let and alias. Updating the value of an identifier changes the contents of the store, as in Homework 2, but not which location the identifier is bound to. Looking up the value of an identifier is a two-step process: first look in the environment to see what location the identifier is bound to, and then look at the store what value is in that location.

2. In IMP, the configuration includes only the expression/command and the store. Since IMP-+Alias+IO has I/O, it is reasonable to include I/O stream in the configuration. I/O stream consists of input stream, output stream, and error stream. The input and output stream can be simple a list of numbers, and the error stream can be a list of string.

The evaluation and execution operations can be written to take arguments (Environemnt e, Store s, IOStream io) in addition to the argument expression and command, respectively.

Write your program using objected-oriented styles. As an indication of the amount of work, my ML code for this interpreter is (dense but) less than 100 lines. I'd say that your Java code would be at least two to three times as long.

NOTE: Consult Homework 2 solution to save work and avoid repeating errors.