Homework 11
Adding State to Huey
Due: Friday, May 8, at 11:59 PM
Introduction
For this assignment, we extend Huey, our RGB color language, one last time. You will modify the interpreter and other tools that process Huey programs so that they support mutable variables and assignment statements.
Before going any farther, read the updated Huey language specification, especially the sections marked new. These sections define the new features being added to the language.
Optional Reading: The instructions below include all the requirements for the assignment, but they assume that you are comfortable with adding new features to our language and interpreter. If you would like a little more guidance on how to approach the assignment, read this outline for adding features to a language and its processors. The reading walks through the process we have used before to extend a language in a bit more detail, offering more explicit instructions and hints for implementation, including some Racket you might want to use.
Code and Files
For this assignment, you will extend the Huey interpreter to handle mutable local variables and assignment statements. These 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 Homework 10.
Here are a few changes specific to this assignment:
-
Make a copy of the
huey-hw10/directory from Homework 10, which will contain all your files for the project. Name the copyhuey-hw11/. Modify and extend these files for the project. -
Put the abstract data type you implement for
Problem 1
in your
datatypes.rktfile. It will now contain two data types. -
Make sure to update the
provideclause in each file to include the new public functions you write. Don't include helpers that you create as a part of implementing your code.
Please comment your source code so that you and I can easily find the various pieces.
A Data Abstraction for Cells
In order to support mutable variables, our interpreter must map names to locations that hold values, rather than to the values themselves. To provide this behavior, we need an abstract data type called a cell. The cell ADT is an example of a mutable object.
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 as a function or as a pair
of procedures that share data.
This means that the cell constructor will return either alambdaor acons.Of course, users of the ADT won't know which implementation you use! For example:
> (define foo (cell 0)) > (cell-value foo) 0 > (cell-set! foo 10) 10 > (cell-value foo) 10 > (cell-set! foo (+ (cell-value foo) 5)) 15 > (cell-value foo) 15
We saw examples of several different implementations for mutable objects in Sessions 24 and 25, including functions and lists.
-
Define the syntax procedures for
doexpressions and for the assignment statements they contain.
For each of the new expression types, define a type predicate, a constructor, and the corresponding access procedures. As always, follow the BNF description for each kind of expression.Reminders and hints:
-
For
doexpressions, you will want two accessors:-
one that returns a list of the 0 or more assignment
statements that immediately follow the keyword
doat the beginning of the expression, and - one that returns expression's value, which is the Huey expression at the end of the list.
-
one that returns a list of the 0 or more assignment
statements that immediately follow the keyword
-
You can use the built-in Racket function
lastto 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
color?(or whatever you named it) to recognize the new types of expression, too. Remember, though: assignment expressions are not legal Huey expressions by themselves. They occur only withindoexpressions.
-
For
-
Extend
(preprocess sugared-exp)to handledoexpressions.
doexpressions are a core feature of Huey, sopreprocessneeds only de-sugar the sub-expressions of thedoand then create a newdoexpression.For example:
> (preprocess '(do (darker (rgb 255 0 0)))) '(do ((rgb 255 0 0) * 0.5)) > (preprocess '(color c = (rgb 0 255 0) in (do (c <= (c mix (rgb 0 0 255))) (c <= (invert c)) (darker (c shift 5))))) '(color c = (rgb 0 255 0) in (do (c <= ((c * 0.5) + ((rgb 0 0 255) * 0.5))) (c <= (invert c)) ((c shift 5) * 0.5)))Reminders and hints:
-
assignmentstatements are not legal Huey expressions, sopreprocesswill not have a case for them. -
The case for preprocessing
doexpressions can call a helper function to preprocess theassignmentstatements it contains. To preprocess a single assignment statement, preprocess the value on its right hand side and return a new assignment statement.
-
-
Modify the evaluator's environment to map variables to
cells that contain values, not values.
Use the cell ADT you implemented for Problem 1.This requires three changes:
-
change the initial environment to bind
whiteandblackto 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 ask the cell it finds for the actual value.
After this step, our evaluator should work exactly as it did after Homework 10, even though the environment now maps variables to cells that hold values. All the same tests should pass. We should not notice any changes!
Run your existing tests to make sure this is true. -
change the initial environment to bind
-
Extend
(eval-exp exp)to handledoexpressions.
eval-expreturns the value ofexp, which now may be or contain the new kind of expression, as specified by the language spec.What Racket will display as a result of
eval-expstill depends on how you represent RGB values internally. If you use lists of size three, for example, it might display values such as these:> (eval-exp 'white) '(255 255 255) > (eval-exp '(do (rgb 255 0 0))) '(255 0 0) > (eval-exp '(color c = (rgb 0 255 0) in (do (c <= (c mix (rgb 0 0 255))) (c <= (invert c)) (darker (c shift 5))))) '(127 66 66) > (eval-exp '(color c = (rgb 0 255 0) in (color d = (rgb 0 0 255) in (do (c <= (c mix d)) (d <= (c mix d)) ((c mix d) shift 5))))) '(5 99 163) > (eval-exp '(color c = (rgb 0 255 0) in (do (c <= (invert d)) ;; notice the 'd' c))) eval-exp: undefined variable dReminders and hints:
-
Again: assignment statements are not legal Huey
expressions, so
eval-expwill not have a case for them. - 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
doexpression,eval-expcan call that helper. You can do that withmapor with another helper function that handles all of the assignment statements.
-
Again: assignment statements are not legal Huey
expressions, so
Extra Credit Opportunities
If you finish the above tasks and would like the chance to
earn a few extra points, try one or more of these exercises.
If you do, be sure to document your attempt clearly
in the header block of your interpreter.rkt file.
-
Modify the REPL with an option to print colors
in hex format.
— 4 points
After we create a useful color, we might want to use it in the CSS file for a website. Huey could display colors in hex format in order to make that easier.One way to support this would be to let the user toggle the display format with a REPL command:
huey! > (hex on) huey! > (rgb 255 0 0) "#ff0000" huey! > ((black mix (rgb 255 0 0)) mix white) "#be7f7f" huey! > (hex off) huey! > (rgb 255 0 0) (255 0 0)
Another option would be to make this an option when we create the REPL, say, by passing a flag as an argument to
run-huey.> (run-huey 'hex) huey! > (rgb 255 0 0) "#ff0000" huey! > ((black mix (rgb 255 0 0)) mix white) "#be7f7f"
If you want to offer even more flexibility, let the user pass any display function as an argument to a REPL generator.
(define run-huey (make-repl display-hex))
-
Define two or more new operators that are syntactic
abstractions. — 4 points
We have a preprocessor, so adding syntactic abstractions to Huey should be straightforward!Update Huey's syntax procedures to recognize your new operators, and then modify the preprocessor to translate them into core Huey syntax.
Offhand, I think adding a unary operator named
lighterwould be a useful bit of sugar in tandem withdarker. Can you think of other combinations of core Huey operations that would be useful shorthand when describing colors?Run your ideas by me before implementing them. That way, we can make sure that they are valid abstractions and that they are worth extra credit points.
-
After completing this assignment,
your Huey REPL
will evaluate programs that have state. Unfortunately,
though, the REPL itself does not support sequences of
stateful expressions at the Huey prompt.
Write a function
run-huey-with-statethat creates a REPL with top-level mutable variables. — 8 pointsUsers should be able to
definea variable and then use and change its value in subsequent expressions:huey! > (define c <= (white mix black)) '(127 127 127) huey! > (c <= (c shift 1)) huey! > c '(128 128 128)
This REPL will need its own helper to evaluate the expressions that the user enters, which can be
definestatements, assignment statements, or Huey expressions. This helper can call the existingeval-expto evaluate the Huey expressions, but it will have to evaluate thedefinestatements 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 interpreter for creating and manipulating colors.
Deliverables
By the due time and date, use the course submission system to submit the following files electronically:
syntax-procs.rktinterpreter.rktdatatypes.rkttests.rktutilities.rkt(optional)
Be sure that your submission follows the submission requirements. As always, use the specified names for your files. This enables the autograder to find and run your code.