Due date: Wednesday, September 27th, 11:59PM
You must update the course software to get the support code for this assignment. From the terminal:
$ opam update $ opam intall cs691f.1.5.0
This assignment has two parts. In the first part, you will implement an evaluator for a simple language that makes continuations explicit. In the second part, you modify your evaluator so that it can run on the Web.
Submit a file named Eval.ml with the following signature.
(* Required interface *)
type context
val empty_context : context
val is_empty_context : context -> bool
val is_value : M_syntax.exp -> bool
val step : M_syntax.exp -> context -> M_syntax.exp * context
(** [serve port exp] *)
val serve : int -> M_syntax.exp -> unit
As the signature suggests, you need to define the structure of control strings yourself. We provide the module M_syntax , which defines the abstract syntax of the language you must use.
The provided M_util module defines parsers and
pretty-printers that you'll need to implement the read
and
write
expressions. You may assume that the user will never
try to read or write a function or a data structure that contains a function.
(But, feel free to support reading and writing functions if you wish.)
We recommend hacking up your solution incrementally. First, follow all
the steps below just for binary operators and constants, then for
data-structures, then functions, and then finally tackle read
and write
last.
First, pick the representation of contexts. If you want to build a substitution-based evaluator (i.e., a CK machine), the contexts are just control strings. If you want to build an environment-based evaluator (i.e., a CEK machine), then the contexts must have both a control string and an environment. The lecture notes have implementations for CK and CEK machines for a trivial language to get you started.
is_value
This should be straightforward. No directions needed.
step
function.Use the lecture notes for guidance. Since this language has several kinds
of values, you should use the is_value
function to make
step
shorter. (i.e., you don't want to specify how
functions are applied repeatedly, for every type of argument.)
You may simply use the loop below, though it may be instructive to insert debugging code.
let eval (e : exp) : exp =
let rec loop ((e, cxt) : exp * context) =
if is_value e && is_empty_context cxt then
e
else
loop (step (e, cxt)) in
loop (e, empty_context)
read
and write
These functions are meant to read and write values from standard I/O. Writing
is easy to do with print_exp . To read values, you
can use parse_exp , but that function accepts
arbitrary expressions. But, you can use the is_value
function you
defined above to reject those.
You must either complete this part of the assignment, or the Unification Revisited assignment.
You've implemented a CK (or CEK) machine for a simple language that supports console I/O. The next step is to run these programs on the Web. Your task is to write an OCaml program, called Server.ml with the following command-line interface:
cs691f run Server PORT FILENAME
This command must parse the program in FILENAME and launch a Web server on PORT that runs it. All I/O should occur via the Web browser instead of the console. You'll need to modify (or functorize) your CK/CEK machine to do so. You should only need to change the cases for evaluating read and write.
The Web is a very different environment from desktops. You will need to address several concerns in this assignment:
You cannot trust the end-users to actually run any program. When JavaScript code is delivered to a browser, it is trivial for a user to modify it or replace it completely. In general, you cannot trust the result of any computation (i.e., JavaScript code) that runs in the browser. For this assignment, assume that all computations are critical, so do not run any code in the browser. Use the browser for I/O only.
For example, suppose you're running the following program:
write "Enter first number"; let x = read<int> in write "Enter second number"; let y = read<int> in write "The sum is": write (x + y)
The addition operation at the end must be performed on the server. Therefore, the program should be transformed into three separate forms: (1) a form that displays "Enter first number" that reads in x, (2) a form that displays "Enter second number" that reads in y, and (3) a page that displays the "The sum is" and the result. These forms must be constructed on the server.
A Web server may have to handle several concurrent sessions. Moreover, sessions typically don't terminate gracefully: users just close their browsers, which gives no notification to Web servers. For example, suppose a user is running the program above. Consider the state where the user has already entered a value for x and thus the program is now suspended waiting for y on the second form. The server could remember the x value in memory, but the user may never submit the second form. The server would either leak memory or cleanup state after some (arbitrary) timer elapses.
Instead, you should serialize the control string, send it to the client, and deserialize it to resume the computation. This will ensure that the server is truly stateless and doesn't have arbitrary timeouts to clean up state.
Finally, users may have windows open at a site to run several computations concurrently. E.g., the user may be adding several distinct pairs of numbers in each window. These windows shouldn't interfere with each other. The easiest way to ensure you don't have any interference is to not use cookies or DOM storage at all, since they are shared between all windows at a site. Instead, store the serialized control string in a hidden form field.
You may have realized that a dedicated, malicious user can easily manipulate the serializing control string. Have the server either sign or encrypt the string.
Array.to_list Sys.argvThis expression returns the list of arguments as a list of strings. You can trivially pattern-match against the list to parse arguments.
opam install cohttp asyncThe following book chapter covers these topics: