Fun with AAQZ5, CSC430, Fall 2024
1 Goal
Add a few primitive functions to your language to allow you to write interactive programs.
The focus of this assignment is to be a bit more creative. Therefore, you are not required to have full test case coverage. Also, we would like you to submit a short program, written in AAQZ5, that uses the new features.
2 Guidelines
For this and all remaining assignments, every function you develop must come with the following things:
A commented header line that expresses the result of the function in terms of its inputs, written in English. Be as precise as you can within the space of a line or two.
A type declaration (possibly inline), specifying the input and output types.
Test cases. A function without test cases is incomplete. Write the test cases first, please.
For this assignment, you must develop your solutions using the typed/racket language. If you haven’t seen them, you might be interested in these Hints on Using Typed Racket in CPE 430.
Your test cases must use the check-equal?, check-=, or check-exn forms.
Your solution should take the form of a single file.
Hand in your solution using the handin server. For help with the handin server, please see the course web page.
2.1 Handling Errors
All of your error messages must contain the string "AAQZ". Essentially, this allows my test cases to distinguish errors correctly signaled by your implementation from errors in your implementation. To be more specific: any error message that doesn’t contain the string "AAQZ" will be considered to be an error in your implementation.
Additionally, your error messages should be actually helpful. Since you are the primary consumer of your own error messages, making these error messages good in the first place should reduce your overall development time. There are two parts to this: first, the error message should include text that actually indicates what the programmer did wrong. Second, include the text of the user’s program, so they (actually you) can figure out how to fix it. See lab 3 for an example of how to do this. (Apologies in advance if I renumber the labs and fail to update this paragraph....)
2.2 Progress Toward Goal comment
Graders are happier when they know what to expect. Your final submission should start with a short one- or two-line comment indicating how far you got through the project. Ideally, this would just be: “Full project implemented.” But if you only implemented, say, squazz and blotz, and didn’t get to frob or dringo, please indicate this in the comment, so that we don’t spend all our time searching for bits that aren’t there.
3 Mutation
There’s no need for mutation in any of the first five assignments in this class. Don’t mutate bindings, and don’t mutate structure fields. You don’t have to use hash tables at all, but if you do use hash tables use immutable hash tables only; no hash-set!.
4 New Library Functions
Here are the new library functions you need to add to your language.
Note that as in the previous assignment, these are the "types" as they might appear in the AAQZ5 manual; this does not say anything about how the language implementor (you) should represent them.
procedure
(println s) → boolean
s : str
procedure
(read-num) → real
procedure
(read-str) → str
procedure
(seq a b ...) → any
a : any b : any*
{seq {println "What is your favorite integer between 6 and 7?"} {bind [your-number = {read-num}] {println {++ "Interesting, you picked " your-number ". Bold choice!"}}}}
procedure
(++ a b ...) → string
a : any b : any*
Note that the last two functions are of “variable arity”. That is, you can call them with a variable number of arguments. Accommodating this may require some adjustments to the design of your primitives.
You are welcome to use any racket primitives that are part of the default Racket environment in order to implement these functions. Some of the following may be useful to you:
printf displayln newline read-line string->number number->string real? |
Here’s a short playthrough of a thrilling action-adventure game that I wrote in AAQZ5 . I call it "Nightfall":
% racket ./aaqz-cmdline.rkt ./aaqz1.rkt |
You are in the Space Shuttle. It is very cold. |
You have 10 units of fuel left. |
How many units of fuel do you burn? |
> 8 |
You burned 8 units of fuel. You are slightly warmer. |
You are in the Space Shuttle. It is very cold. |
You have 2 units of fuel left. |
How many units of fuel do you burn? |
> 2 |
You burned 2 units of fuel. You are slightly warmer. |
You are out of fuel. You have died. |
Exciting, right?
Also, here’s an example of a AAQZ5 program that shows how you might simulate lists (and recursion) in this language:
{bind [empty = 15] {bind [empty? = {(x) => {equal? x empty}}] [cons = {(f r) => {(key) => {if {equal? key 0} f r}}}] [first = {(pair) => {pair 0}}] [rest = {(pair) => {pair 1}}] {bind [sum-list = {(l self) => {if {empty? l} 0 {+ {first l} {self {rest l} self}}}}] [my-list = {cons 3 {cons 24 {cons 8 empty}}}] {println {++ "The sum of the list is " {sum-list my-list sum-list} "."}}}}}
5 Be Creative
Using your new functions and some ideas from the previous section and of your own, we’d like you to write an original AAQZ5 program of your own. What should it do? That’s up to you. However, it does not need to be long. Games are often good choices.
You should include your program as part of your submission. It should be at the end of the file, and it should take the form of quoted s-expression with the name ‘example-program‘.
For instance:
(define example-program '{seq {println "this program will be really nifty"} {println "after I write it"}})
You should also include the text that is generated in a sample run of the program, as a comment.
It may be that implementing your program requires additional AAQZ5 primitive functions; for instance, a substring function. You are welcome to add these to your AAQZ5 language. These should not involve changing interp, just adding a function to the table of primitives.
6 Command-line, anyone?
The following is optional. However, if you want to be able to run your programs at the command line, it’s not hard to do that.
Here’s one way. Add this to your implementation file:
(provide file-interp) (define-type Simple-Sexp (U Symbol Number String (Listof Simple-Sexp))) (define-predicate sexp? Simple-Sexp) (define (file-interp [f : Path-String]) (define v (file->value f)) (cond [(sexp? v) (top-interp v)] [else (error 'file-interp "expected file to contain s-expression, got: ~e" v)]))
Then, add another file in the same directory containing this code:
#lang typed/racket (require "impl.rkt") (define dont-care (file-interp (vector-ref (current-command-line-arguments) 0)))
... where “impl.rkt” is the name of the file containing your AAQZ5 implementation.
Now, you should be able to run this at the command-line, as for instance:
% racket ./aaqz-cmdline.rkt ./aaqz2.rkt |
The sum of the list is 35. |
Again, this is entirely optional, you’ll only be handing in the main file.