Homework 10
Extending Boom, A Little Language for Arithmetic

Due: Friday, May 2, at 11:59 PM

Introduction

This assignment extends Boom, our little language for doing arithmetic, and the interpreter and other tools that process programs written in the language. The new version of the language supports local variables.

Before going any farther, read the updated Boom language specification. It defines the language, in particular the extensions, and talks about some of the Racket you can use to implement your solution.

Code and Files

For this assignment, you will extend the Boom interpreter to handle local variables, including 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 9.

Here are a few changes specific to this assignment:

We will extend the Boom language and interpreter again on the next homework assignment. Please comment your source code so that you and I can easily find the pieces relevant to this assignment.

A Data Abstraction for Environments

In order to support local variables, our interpreter needs a way to map names onto values. To provide this behavior, we need an abstract data type called an environment. The environment ADT is an example of a finite function.

This data type consists of four operations:

The var-exists? function is an addition to the interface of finite functions we studied in class. It enables us to determine whether an environment contains a particular variable, without ever having to look up a non-existent variable and cause an error.

Problems

  1. Implement the environment ADT as a finite function using association lists.
    An association list is a list of name/value pairs, such as:
    ((foo . 2) (bar . 5) (baz . 15))
    

    For example:

    > (make-bindings)
    ()
    > (bind 'foo 2 (make-bindings))
    ((foo . 2))
    > (bind 'bar 5 (bind 'foo 2 (make-bindings)))
    ((bar . 5) (foo . 2))
    > (define env (bind 'bar 5 (bind 'foo 2 (make-bindings))))
    > (look-up 'foo env)
    2
    > (var-exists? 'bif env)
    #f
    > (look-up 'bif env)
    environment: undefined variable -- baz
    

    You may write your own recursive code to implement the look-up function, or you may use the Racket's primitive assoc function.


  2. Define the syntax procedures for variable references and let/in expressions.
    For each of the new expression types, define a type predicate, a constructor, and the corresponding access procedures.

    Be sure to extend the general type predicate boom-exp? (or whatever you named it) to recognize the new types of expression, too.

    Note: we do not need accessors to retrieve the symbols let, =, or in. They are keywords, not values provided by the programmer.


  3. Extend (preprocess sugared-exp) to handle variable references and let/in expressions.
    Both of the new types of expression are core features of Boom, so preprocess needs only to de-sugar the sub-expressions of a variable declaration: the body and the variable's value.

    For example:

    > (preprocess 'two)
    'two
    
    > (preprocess '(let z = (x @ y) in (- z)))
    '(let z = ((x + y) / 2) in (- z))
    
    > (preprocess '(let square = (sq x) in
                     (let average = (square @ x) in
                       (average << 2))))
    '(let square = (x * x) in
       (let average = ((square + x) / 2) in
         (average * (10 ^ 2))))
    

  4. Extend (eval-exp exp) to handle variable references and let/in expressions.
    eval-exp returns the value of exp, which now may be or contain the new kinds of expression, as specified by the language's semantics.

    eval-exp must now maintain its own environment and look up the value of a variable references in the environment. Have eval-exp do the following:

    • create an initial environment in which to evaluate its argument. This environment should contain the primitive values of Boom
    • pass the initial environment, along with the pre-processed version of its argument, to the helper function that actually evaluates an expression in a given environment

    A let/in expression creates a new variable, which must be added to the environment before its body is evaluated. The helper will pass the extended environment on its recursive call to evaluate the let/in expression's body.

    For example:

    > (eval-exp 'two)
    2
    
    > (eval-exp '(let z = (two * ten) in (- z)))
    -20
    
    > (eval-exp '(let square = (sq ten) in
                   (let half = (square / 2) in
                     (half + (4 * 12)))))
    98
    

  5. Define a function (go-boom) that opens a REPL for your Boom interpreter.
    Have the eval step in your REPL call your eval-exp function. Feel free to use the ideas from the reading on imperative programming, but customize the REPL so that it is unmistakably yours! This is an opportunity to put a bit of your own personality into the language.

    go-boom should validate the input before calling eval-exp. If the user enters an illegal expression, that is, anything that is not a legal Boom program, the REPL should not cause an error. Instead, have it write an error message to the screen and let the user enter another expression.

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.