On this page:
What to Do
Submitting
Testing
From Dupe+   to Fraud+
From Binary to Variadic Addition
Generalizing Let
Back-Referencing Let
8.13

Assignment 4: Let There Be (Many) Variables🔗

Due: Monday, June 24, 11:59PM EST

The goal of this assignment is to extend a compiler with binding forms and primitives that can take any number of arguments. The new language is called Fraud+.

The Fraud+ langauge features the following additions to Fraud:

What to Do🔗

You must extend the interpreter and compiler to implement Fraud+. You are given a file fraud-plus.zip on ELMS with a starter compiler based on the Fraud: local binding, variables, and binary operations language we studied in class.

Note that, unlike Assignment 3, the following files have already been updated for you and should not be changed by you:

So you will only need to modify:
  • interp.rkt

  • interp-prim.rkt

  • compile.rkt

  • compile-ops.rkt

to correctly implement the new features. These features are described below.

Note that you should not alter your ast.rkt or parse.rkt files. Part of Assignment 3 was learning to design your own structures, but part of Assignment 4 is learning to work within the constraints of an existing design! You must update your previous solutions to work within this framework.

Submitting🔗

Submit a zip file containing your work to Gradescope. Use make submit.zip from within the fraud-plus directory to create a zip file with the proper structure.

Testing🔗

You can test your code in several ways:

Note that only a small number of tests are given to you, so you should write additional test cases.

From Dupe+ to Fraud+🔗

Implement the abs, unary -, and not operations and the cond and case forms from Assignment 3 by modifying interp.rkt, interp-prim.rkt, compile.rkt, and compile-ops.rkt. You can start from your previous code, but you will need to update it to work for the structures provided. What’s essentially left for you to do is to make sure to correctly signal an error ('err) when these constructs are applied to the wrong type of argument.

While you’re at it, implement the predicates integer? and boolean? for checking the type of an argument, modeled by the char? predicate that was covered in the lectures.

From Binary to Variadic Addition🔗

In Fraud, we implemented a binary operation for addition. However, Racket supports an arbitrary number of arguments for +. Your job is to extend the interpreter and compiler to behave similarly.

Hint: Try using + in the REPL with various arguments to see what the expected behavior is.

Generalizing Let🔗

The Fraud language has a let form that binds a single variable in the scope of some expression. This is a restriction of the more general form of let that binds any number of expressions. So, for example,

(let ((x 1) (y 2) (z 3))
  e)

simultaneously binds x, y, and z in the scope of e.

The syntax of a let expression allows any number of binders to occur, so (let () e) is valid syntax and is equivalent to e.

The binding of each variable is only in-scope within the body, not in the right-hand sides of any of the let. So, for example,

(let ((x 1) (y x)) 0)

is a syntax error because the occurrence of x is not bound. We will only test syntactically valid inputs, so you do not need to handle this kind of error specially.

Back-Referencing Let🔗

Similar to let, there is also let* that can also bind any number of expressions. The difference is that previous bindings are available in the right-hand sides of subsequent bindings. For example,

(let* ((x 1) (y 2) (z (add1 y)))
  e)

binds x to 1, y to 2, and z to 3 in the scope of e.

The syntax of a let* expression allows any number of binders to occur, so (let* () e) is valid syntax and is equivalent to e.

Unlike let,

(let* ((x 1) (y x)) 0)

is not a syntax error. However, bindings are only available forward, so

(let* ((x y) (y 1)) 0)

is a syntax error.

HINT: Think about what a lazy compiler writer would do.