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:

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:

Problems

  1. Implement the cell ADT interface either as a function or as a pair of procedures that share data.
    This means that the cell constructor will return either a lambda or a cons.

    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
    

  2. 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 new do expressions. Remember, though: assignment expressions are not legal Boom expressions by themselves. They occur only within do expressions.


  3. Extend (preprocess sugared-exp) to handle do expressions.
    do expressions are a core feature of Boom, so preprocess needs only to de-sugar the assignment statements and the final sub-expression, and then return a new do 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. To preprocess 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)))
    

  4. 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, and two 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.


  5. Extend (eval-exp exp) to do expressions.
    eval-exp returns the value of exp, which now may be, or contain, a do 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.

  1. 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.


  2. 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 points

    Users 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 existing eval-exp to evaluate the Boom expressions, but it will have to evaluate the define 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:

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.