Homework 11
Extending Boom with Assignment Statements
Due: Friday, May 9, at 11:59 PM
Introduction
For this assignment, we extend Boom, our little arithmetic language, one last time. You will modify the interpreter and other tools that process Boom programs that include mutable variables and assignment statements.
Before going any farther, read the updated Boom language specification, especially the sections marked new. The new sections define the features being added to the language for this assignment.
Code and Files
For this assignment, you will extend your Boom interpreter to handle mutable local variables and assignments. The changes will affect the syntax procedures that define the new expressions, the pre-processor, and the evaluator. You will organize your code in the same way as you did for Homework 10.
Here are a few changes specific to this assignment:
-
Make a copy of the
boom-hw10/
directory from Homework 10, which will contain all your files for the project. Name the copyboom-hw11/
. Modify and extend these files for the project. -
Put the abstract data type you implement for
Problem 1 in the file
datatypes.rkt
. -
Make sure to update the
provide
clause in each file to include the new public functions you write.
Please comment your source code so that you and I can easily find the pieces relevant to this assignment.
A Data Abstraction for Cells
In order to support mutable variables, our interpreter must map names not to values but to locations that hold values. To provide this behavior, we need an abstract data type called a cell. The cell ADT is an example of a mutable object. We explored several alternative implementations for mutable objects in Sessions 24-26: various bank accounts, a counter, and an add function with memory. All depend on a closure.
The interface of the cell ADT consists of three operations:
-
(cell init-value)
, which creates a new cell containinginit-value
-
(cell-value a-cell)
, which returns the value of the cell -
(cell-set! a-cell new-value)
, which changes the value of the cell tonew-value
Problems
-
Implement the cell ADT interface either as a function or as
a pair of procedures that share data.
This means that thecell
constructor will return either alambda
or acons
.Of course, users of the ADT won't know which implementation you use! For example:
> (define var (cell 0)) > (cell-value var) 0 > (cell-set! var 10) 10 > (cell-value var) 10 > (cell-set! var (+ (cell-value var) 5)) 15 > (cell-value var) 15
-
Define syntax procedures for
do
expressions and for the assignment statements they contain.
For each of these, define a type predicate, a constructor, and the corresponding access procedures. As always, follow the BNF description for each kind of expression.For
do
expressions, you will want two accessors:-
one that returns a list of the 0 or more assignment
statements, that immediately follow the keyword
do
at the beginning of the expression, and - one that returns expression's value, which is the Boom expression at the end of the list.
You can use the built-in Racket function
last
to implement the value accessor. You will want to write a helper function to implement the assignments accessor.Be sure to extend the general type predicate
boom-exp?
to recognize the newdo
expressions. Remember, though: assignment expressions are not legal Boom expressions by themselves. They occur only withindo
expressions. -
one that returns a list of the 0 or more assignment
statements, that immediately follow the keyword
-
Extend
(preprocess sugared-exp)
to handledo
expressions.
do
expressions are a core feature of Boom, sopreprocess
needs only to de-sugar the assignment statements and the final sub-expression, and then return a newdo
expression.The assignment statement is not a legal Boom expression by itself, so
preprocess
will not have a case for assignment statements. Create a helper function to preprocess a list of assignments. Topreprocess
a single assignment statement, preprocess the value on its right hand side and return a new assignment statement.For example:
> (preprocess '(do (2 @ 4))) '(do ((2 + 4) / 2)) > (preprocess '(let c = 10 in (do (c := ((sq c) + c)) (c - 1)))) '(let c = 10 in (do (c := ((c * c) + c)) (c - 1))) > (preprocess '(let c = 10 in (do (c := ((sq c) + c)) (c := (c << 1)) (c - 1)))) '(let c = 10 in (do (c := ((c * c) + c)) (c := (c * (10 ^ 1))) (c - 1)))
-
Modify the evaluator's environment to map variables to cells
that contain values, not to values.
Use the cell ADT you implemented for Problem 1.This requires three changes:
-
change the initial environment to bind the primitive
variables
zero
,two
, andtwo
to cells that hold their values, - change the part of the evaluator that extends an environment to bind the new variable to a cell that holds its value, and
- change the part of the evaluator that looks up the value of a variable in the environment to access the cell it finds to get the actual value.
After this step, our evaluator should work exactly as it did after Homework 10. We should not notice any changes!
Run your existing tests to make sure this is true. -
change the initial environment to bind the primitive
variables
-
Extend
(eval-exp exp)
todo
expressions.
eval-exp
returns the value ofexp
, which now may be, or contain, ado
expression, as specified by the language spec.For example:
> (eval-exp '(do (2 @ 4))) 3 > (eval-exp '(let x = 26 in (do (x := (x * 2)) (x @ 30)))) 41 > (eval-exp '(let c = 10 in (do (c := ((sq c) + c)) (c := (c / 10)) (c - 1)))) 10 > (eval-exp '(let left = 0 in (let right = 12 in (do (left := (left @ right)) (right := (left @ right)) (left * right))))) 54 > (eval-exp '(let x = 8 in (let square = (x * x) in (do (square := (square / 2)) (square := (square / 2)) (square := (square / 2)) ((x * 4) + square))))) 40
Write a new helper function that evaluates a single assignment statement. It updates the value of the variable in the current environment. It does not add a new variable to the environment.
For each assignment statement in the
do
expression,eval-exp
's helper can call the new helper function that evaluates an assignment statement.
Extra Credit
If you complete all the previous problems, you may attempt one or both of these extra credit problems. Do not try to solve them until your code handles essentially all of Problems 1-5.
-
Add a new piece of sugar to the language.
— 4 points
- Define a new operator that is a syntactic abstraction of Boom's current behavior.
- Update the syntax procedures to recognize and handle the new operator.
-
Add an arm to
preprocess
that translates the abstraction into underlying form.
No change to
eval-exp
is needed. -
After completing this assignment,
your Boom REPL will
evaluate programs that have state. Unfortunately, though, the
REPL itself does not support sequences of stateful expressions
at the Boom prompt.
Write a function
go-boom!
that creates a REPL with top-level mutable variables. — 8 pointsUsers should be able to
define
a variable and then use it and change it in subsequent expressions:boom! > (define c := (two @ ten)) boom! > (c := (sq c)) boom! > c 36
This REPL will need its on helper to evaluate the expressions that the user enters, which can be
define
statements, assignment statements, or Boom expressions. This helper can call the existingeval-exp
to evaluate the Boom expressions, but it will have to evaluate thedefine
statements and assignment statements on its own — with calls to existing helper functions where appropriate.This is a big extension, but the result is a handy little REPL for working with numbers.
Deliverables
By the due time and date, use the course submission system to submit the following files electronically:
syntax-procs.rkt
interpreter.rkt
datatypes.rkt
tests.rkt
utilities.rkt
(optional)
Be sure that your submission follows the submission requirements. As always, use the specified names for your files. This enables the auto-grader to find and run your code.