Homework must be submitted via D2L. All submitted files (5 for this assignment sol4A1.e3 , sol4A2.e3, sol4A3.e3, sol4A.hs, and sol4B.e4) must be submitted in the appropriate D2L directory in the drop box HW4.It is your responsibility to submit the homework in the proper format with the proper names. For example.
-- Homework 4 Tom Smith tom_smith@gmail.com
All programs mentioned can be downloaded from the this document
Questions in this homework are based upon two different languages. The first part (A) is based upon the E3 language from Homework 3. The second part (B) is based upon the language E4 (which is like E3 with 'local'; constructs for characters; and constructs for raising and handling exceptions). Its "concretized" abstract syntax is given by the following grammar:
prog := '(' { def } ')' exp def := globaldef | fundef globaldef := '(' 'global' var exp ')' fundef := '(' 'fun' fname '(' { var } ')' exp ')' exp := var | int | '(' ':=' var exp ')' | '(' 'while' exp exp ')' | '(' 'if' exp exp exp ')' | '(' 'write' exp ')' | '(' 'block' { exp } ')' | '(' '@' fname { exp } ')' | '(' '+' exp exp ')' | '(' '-' exp exp ')' | '(' '*' exp exp ')' | '(' '/' exp exp ')' | '(' '<=' exp exp ')' | '(' 'pair' exp exp ')' | '(' 'fst' exp ')' | '(' 'snd' exp ')' | '(' 'ispair' exp ')' | '(' 'local' '(' { var exp } ')' exp ')' | '(' 'try' exp { '(' '@' hname { exp } exp ')' } ')' | '(' 'raise' hname { exp } ')' | char | '(' '=' exp exp ')' | '(' 'ischar' exp ')' fname := letter { letter | digit } var := letter { letter | digit } hname := letter { letter | digit }
As before, comments may be included by enclosing them between comment braces '{-' and '-}' characters, and they may be nested.
Part A has two parts. The first where you write E3 programs that distinguish the different parameter passing mechanisms. The second where you alter the call by reference interpreter to pass reference for expressions of the form (fst e) and (snd e).
There are three different pairings of parameter passing mechanisms
For each of the pairings write a single *.e3 program whose main body gives a different answer when run under the two interpreters. Name your programs as indicated in the pairings above, and submit these files in D2L.
To test your files you must download all three interpreters, and run each of your programs through the appropriate pair to see that your E3 program gets a different result.
Your goal for this part is to alter the call by reference interpreter so that parameters to functions of the form form (fst e) and (snd e) are passed by reference. For example consider the program.
(global x 5) (global p (pair 8 2)) (fun swap (x y) (local (temp x) (block (:= temp x) (:= x y) (:= y temp))))Now observe a session for the read eval print loop. Note the call (@ swap x (fst p)), both paremeters to swap are locations on the heap. One is a variable, and the other is the first part of a pair.
enter Exp> x 5 enter Exp> p (8.2)@2 enter Exp> (@ swap x (fst p)) 5 enter Exp> x 8 enter Exp> p (5.2)@2The interpreter already special cases arguments in function calls that are variables (like x). Your job is to special case expressions of the form (fst e) and (snd e). The code has some commented out stubs in the right place to get you started.
The file Hw4.hs is a Haskell interpreter for the E4 language. The E4 language is much like the E3 language but it adds the following constructs to the concretized abstract syntax.
exp := ... | '(' 'local' '(' { var exp } ')' exp ')' | '(' 'try' exp { '(' '@' hname { exp } exp ')' } ')' | '(' 'raise' hname { exp } ')' | char | '(' '=' exp exp ')' | '(' 'ischar' exp ')'
The language E4 adds character constants like 'c' and 'z' and '5' to the atoms of the language. One can compare characters for equality by using the = operation. For example (= 'c' 'd') would return 0 (for false) and (= 'c' 'c') would return 1 (for true). It is an error to apply = to non-characters. In particular = does not work on integer atoms.
The other constructs of E4 concern raising and handling exceptions. The raise construct takes a exception name (using the same syntax as variables and function names) and an arbitrary number of arguments. For example one might write (+ 4 (raise nogood 4 'x')). Here the second argument to + raises the nogood exception with the arguments 4 and 'x'. Raising an exception will cause the whole expression to abort. For example if we type this expression in the read-eval-print loop we get the following transcrition.
enter Exp> (+ 4 (raise nogood 4 'x')) Uncaught exception: nogood 4 'x'
If we turn tracing on, and type an expression, we can watch the execution of the expression as the exception is raised.
enter Exp> trace Tracing is on. enter Exp> (+ 4 (* 3 (raise nogood 4 'x'))) > (+ 4 (* 3 (raise nogood 4 'x'))) Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] |> 4 Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] |< 4 |> (* 3 (raise nogood 4 'x')) Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] | |> 3 Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] | |< 3 | |> (raise nogood 4 'x') Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] | | |> 4 Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] | | |< 4 | | |> 'x' Stack 0@[('c'.4),('a'.9),('b'.12)] 1@[3,'a',6,'d'] | | |< 'x' | |< Exception nogood 4 'x' |< Exception nogood 4 'x' < Exception nogood 4 'x' Uncaught exception: nogood 4 'x'
Notice everything proceeds normally until the raise inside the deeply nested call to *. Once the raise is executed all the nested computations are aborted (we sometime say they unwind).
Exceptions would be of little use, except for error reporting, if we could not catch a raised exception. That is done with the try construct. The form of the try construct is (try test handle1 handle2 ... handleN). The semantics is to execute the test. If that executes normally, then the value of the try is that value. If the test raises an exception then the handles are tried from left to right. The form of a handle is (@ name param1 ... paramN body). The first handle with the matching exception name, and the correct number of parameters wins. The body of that handle is executed and its value is returned by the try. Note that the parameters of the handle are bound to the arguments of the exception. If no handle matches, the exception is propogated.
For example, we might extend our traced example by typing.
enter Exp> (+ 4 (try (* 3 (raise nogood 4 'x')) (@ nogood n c (+ n 3)))) 11 enter Exp> (+ 4 (try (* 3 (raise nogood 4 'x')) (@ nogoode n c (+ n 3)))) Uncaught exception: nogood 4 'x'Here, the trys have only one handle. In the first example, it matches the raised exception, so the body of handle (+ n 3) is returned as the value of the try (rather than the * expression which aborted). Note that the parameters n and c are bound to the exception arguments 4 and 'x'. In the second example, the raised exception and the handler have different names to the exception is just passed on.
Note that in these examples the raise expression is statically inside the try statement, but this need not be case. In fact the raise could be deeply nested inside some function calls used in the test of the try.
The goal of this part of the homework is to write the function sum that sums up all the elements in a value. For example, if the global variable x was the deeply nested set of pairs ((3.5).((6.7).(8.1))) the expression (@ sum x) would evaluate to 30. The complicated part is to handle deeply nested pairs that contain characters like: ((3.('a'.'c')).((8.12).(3.1))). To handle characters we will use a table that maps Char to Int like: (('c'.4).(('a'.9).(('b'.12).0))). Such tables will use the encoding of lists we saw in homework 3.
enter Exp> (@ find 'c' table) 4 enter Exp> (@ find 'z' table) Uncaught exception: notfound 'z'
The argument n counts the nesting depth of the argument x. This is easy to do, every time sum2 makes a recursive call, add 1 to n, i.e. (@ sum (+ n 1) ...) . The purpose of this nesting counter, is that whenever an exception is handled, the handler should return the value of the nesting depth as the value of the handle (In sum, we always returned 0, when we handled an exception).
Here is an example transcript demonstrating how sum2 should work.
enter Exp> (@ sum2 0 'z') 0 enter Exp> (@ sum2 0 (pair 0 'z')) 1 enter Exp> (@ sum2 0 (pair 0 (pair 0 'z'))) 2 enter Exp> (@ sum2 0 (pair 0 (pair 0 (pair 0 'z')))) 3