Homework must be submitted via D2L. All submitted files (3 for this assignment sol5A.e5, hw5A.hs, and sol5B.hs) must be submitted in the appropriate D2L directory in the drop box HW5.It is your responsibility to submit the homework in the proper format with the proper names.
All programs mentioned can be downloaded from the this document
This homework has two parts. First, altering the definitional interpreter for a small functional language, and Second, writing a few small programs in a functional language (Haskell).
prog := exp exp := var | int | char | '(' '=' exp exp ')' | '(' 'if' exp exp exp ')' | '(' 'let' var exp exp ')' | '(' 'letfun' var var exp exp ')' | '(' '@' exp exp ')' | '(' '+' exp exp ')' | '(' '-' exp exp ')' | '(' '*' exp exp ')' | '(' '/' exp exp ')' | '(' '<=' exp exp ')' | '(' 'pair' exp exp ')' | '(' 'fst' exp ')' | '(' 'snd' exp ')' | '(' 'ispair' exp ')' | '(' 'ischar' exp ')' | '(' 'ispair' exp ')' | '(' 'isint' exp ')' var := letter { letter | digit }
As usual, comments may be included by enclosing them between '{' and '}' characters, and they may be nested.
E5 is similar to some of our earlier languages (especially E4), it retains characters and their operations, but lacks imperative features including assignment, while , block , and write. It The local expression has been renamed let (to emphasize that it acts like an Haskell-style immutable binding), and only one variable can be defined (nested let's can be used to define multiple variables).
The most important changes are in the treatment of functions, which are now defined using a locally-scoped letfun expression form. Functions are treated as just another kind of value, and they share the same name space as other values. To evaluate (letfun f x b e ) , first create a function value whose formal argument is x , whose body is b , and whose environment is the current environment; then bind f to that function value and evaluate e in the resulting environment. Applications now take an arbitrary expression in the function position; this must evaluate to a function value. All functions take exactly one argument; pairs can be used to encode multiple arguments (or multiple results). Functions are completely "first-class", i.e., they can be passed as arguments to, or returned as results of, other functions, and can be stored in pairs. Since there is now no need for a separate (fun ...) declaration form, a program is once again just an expression (most probably with some outer let or letfuns defining things that will be known globally).
The web site gives several example E5 programs. Program static.e5 illustrates that nested functions use static scoping rules; program compose.e5 shows how to write a higher-order function that composes two existing functions.
An E5 interpreter in Haskell (only) has been provided ( hw5.hs ).
In the E5 language there are exactly 4 kinds of values. Ints, Characters, Pairs, and Functions. There are 3 predicates on values: ispair, ischar, and isint. With these you can test what kind of value any object takes. In addition, the interpreter for E5 attempts to recognize the convention we have used to encode lists. Recall that convention, it uses right-nested pairs ending in 0. For example (pair 4 (pair 'z' 0)) evaluates to (4.('z'.0)). In the interpreter for E5 if such a right-nested pair is recognized it is printed as [4,'z']. The interpreter also recognizes right-nested pairs of characters, and prints them as strings. For example (pair 'a' (pair 'b' (pair 'c' 0))) prints as "abc". There are still only 4 kinds of values, though we print some of them using the list conventions. Here are some examples
You will need to write some small E5 programs. You will also modify the definitional interpreter. You might try adding the list convention functions to an E5 program. While the idea is the same, the format of definitions is very different in E5 than in E3 and E4. This would be good practice before you start.
Turn in a file named hw5A.hs in to D2L which is your modified interpreter.
Note that this interpreter has some significant changes from the previous one for E4. We no longer use a stack, just an environment and a heap. There is a (single) environment (because both functions and other values can now be stored in the same name space). The environment now maps all identifiers directly to addresses in the heap. But, we use addresses only when we allocate new cells in the heap, We never change values in the heap since there is no assignment (or call by reference).