This exercise is based on one from CS 173 at Brown University. Some differences: it was their first assignment of the semester, not the third (they were expected to learn Scheme on their own, with some tutorial exercises), and they were given a week to do it rather than two weeks. Think about that before begging for an extension.
For every function you write, you must include a contract and a well-chosen set of test cases.
For this assignment, switch languages in Racket to "Use Language
Declared in Source", and replace the #lang racket
with
#lang plai
.
AE
data type
and the parse
function. Make sure the parse
function
passes all of its tests. Make up some more test cases of your own, and make sure
it passes them.calc
function.
Make sure it passes all of its tests. Make up some more test cases of your own, and make sure
it passes them.WAE
data
type. Modify your parse
function so it handles
the full WAE syntax. Make sure it passes all its test cases.
subst
and calc
functions. Make up a few additional test cases of your
own, and make sure it passes all of them.
(I'm going to stop saying that now; take it as read.)
At present, there are separate rules for +
and
-
, and if we wanted to add *
or /
,
we would need to change the parser, change the subst
function,
and change the interpreter. This is a pain.
BWAE
, like WAE
but with
a single case named binop
to handle all binary operators.
Modify the parser so it produces BWAE
parse trees.
'+
, '-
, etc.) and returns the corresponding built-in
function. (Yes, a Scheme function can easily return a function as its value.)
If the given symbol isn't the name of a built-in function, your function should
return false
. subst
and calc
functions so they
work correctly on BWAE
parse trees.*
and /
operators. You shouldn't need
to change the definitions of BWAE
,
subst
, or calc
at all -- only the lookup
function, and perhaps a trivial change to parse
.with
At present, each with
binds exactly one new variable.
Modify the language to allow a with
to bind zero or more
variables simultaneously (that is, not depending on one
another). If the same identifier is bound more than once in the bindings
list of the same with
, your parser should halt with an error
message.
For example,
{with {{x 2} {y 3}} {with {{z {+ x y}}} {+ x z}}}should evaluate to
7
, while
{with {{x 2} {x 3}} {+ x 2}}should halt with an error message. And
{with {{x 2}} {with {{x {+ x 1}} {y {+ x 2}}} {* x y}}}should evaluate to
12
(not 15!).
This may require defining a new abstract syntax (MBWAE
?),
modifying the parser, modifying the subst
function, and/or
modifying the interpreter.
Your parser and interpreter must detect errors and explicitly signal them by
calling (error ...)
. (Look it up in the Help Desk.)
I shall consider an error raised internally
by Scheme to be a bug in your code.
For example, the parser should detect a with
that binds
the same identifier more than once, and signal an error before
calc
ever sees it. If the parser does not signal an error in such
cases, it's a bug.
Scheme signals a "divide by zero" error if you attempt
to evaluate (/ 1 0)
. You may be tempted to
have your calc
function just use Scheme's division operator
to detect and signal this error. This will be considered a bug: you
need to detect division-by-zero yourself, and signal it yourself,
before calling Scheme's division procedure.
To test whether your function has correctly raised an error, look up
the test/exn
function in the Help Desk. Note that it only
catches errors that you signal, not those raised by built-in functions.
Turn in four Racket source files:
ae.rkt
, wae.rkt
, bwae.rkt
, and
mbwae.rkt
containing the source code for the four
stages of the assignment. If you don't get them all working,
turn in as much as you've got, with an explanation of where you
got stuck and what you've tried.