You must do this assignment solo. We know that some of you are not yet comfortable with Scheme; for that reason, we will weight this assignment very, very low in the overall score. Doing poorly on it will not at all damage your course grade. But, you should exploit this opportunity to become familiar with the style of programming you will do in the rest of the semester.
In each part of the assignment, implement the function parse
,
which consumes an expression in the language’s concrete syntax and returns
the abstract syntax representation of that expression. parse
must
accept only expressions in the grammar of the language.
In addition to parse
, you must implement the function
interp
, which consumes an abstract syntax expression (as
returned by the parse
function) and returns a
CFWAE-value
. Please include a contract for every function that you
write and include test cases that amply exercise all of the code you’ve
written.
To save the trouble of having to add boolean values and operators over them,
create the construct if0
using the syntax described by the EBNF below.
Note that if0
has three branches:
Evaluation should signal an error for non-numeric test values.
fun
Extend the fun
language feature described in lecture so
that functions can accept a list of zero or more arguments instead of
just one. All arguments to the function must evaluate with the
same deferred substitutions. An example of a multi-argument fun:
{{fun {x y} {* x y}} 2 3}
This evaluates to 6.
As you did for multi-armed with
, you must ensure that the
arguments to a function have distinct names.
The syntax of the CFWAE language with these additional features can be captured with the following EBNF:
<CFWAE> ::= <num> | {+ <CFWAE> <CFWAE>} | {- <CFWAE> <CFWAE>} | {* <CFWAE> <CFWAE>} | {/ <CFWAE> <CFWAE>} | <id> | {if0 <CFWAE> <CFWAE> <CFWAE>} | {with {{<id> <CFWAE>} ...} <CFWAE>} | {fun {<id> ...} <CFWAE>} | {<CFWAE> <CFWAE> ...} where an id is not +, -, *, /, with, if0 or fun.
In this grammar, the ellipsis (...
) means that the previous
non-terminal is present zero or more times.
If a fun
or a with
expression has duplicate
identifiers, we consider it a syntax error. Therefore, such errors must be
detected in parse
. For example, parsing the following
expressions must signal errors:
{with {{x 10} {x 20}} 50} {fun {x x} 10}
You must include a contract for every function that you write and include test cases that amply exercise all of the code you’ve written. We will not give full credit to untested functionality, even if it is correct!
Your parser and interpreter must detect errors and explicitly signal them by
calling (error ...)
. We will consider an error raised internally
by Scheme to be a bug in your code.
For example, Scheme signals a "divide by zero" error if you
attempt to evaluate (/ 1 0)
. Since we use Scheme's division
function to implement division in CFWAE, you may be tempted to leave it to
Scheme to signal division by zero errors for you. However, you must signal the
error yourself by explicitly testing for division by zero before calling
Scheme's division procedure.
test/exn
. test/exn
tests
for errors, but only succeeds on errors that you explicitly signal.
Please once again use the "PLAI Scheme" language. Your code must adhere to the following template, without any changes:
(define-type Binding [binding (name symbol?) (named-expr CFWAE?)]) (define-type CFWAE [num (n number?)] [binop (op procedure?) (lhs CFWAE?) (rhs CFWAE?)] [with (lob (listof Binding?)) (body CFWAE?)] [id (name symbol?)] [if0 (c CFWAE?) (t CFWAE?) (e CFWAE?)] [fun (args (listof symbol?)) (body CFWAE?)] [app (f CFWAE?) (args (listof CFWAE?))]) (define-type Env [mtEnv] [anEnv (name symbol?) (value CFWAE-Value?) (env Env?)]) (define-type CFWAE-Value [numV (n number?)] [closureV (params (listof symbol?)) (body CFWAE?) (env Env?)]) ;; parse : expression -> CFWAE ;; This procedure parses an expression into a CFWAE (define (parse sexp) ...) ;; interp : CFWAE Env -> CFWAE-Value ;; This procedure interprets the given CFWAE in the environment ;; and produces a result in the form of a CFWAE-Value ;; (either a closureV or a numV) (define (interp expr env) ...)
However, for the second part of the assignment (lazy
application), you will need to add an exprV
variant to
CFWAE-Value
. That is, CFWAE-Value
for
xinterp-lazy.ss
should read:
(define-type CFWAE-Value [numV (n number?)] [closureV (params (listof symbol?)) (body CFWAE?) (env Env?)] [exprV (expr CFWAE?) (env Env?)]))
You should turn in two Scheme programs where each contains all of the code needed to run your parser and interpreter. One should be for the eager evaluation, called "xinterp.ss", and the other for the lazy evaluation, called "xinterp-lazy.ss". You can also include a README or other relevant files.