If you're still struggling to get a good language design, look back at the notes from the "Introduction to Languages" lecture: they discuss how a language consists of data, operations on the data, and control commands for ordering operations. What are each of these for a tax form program? It might be easiest to think in terms of the commands first, and the data/control second:
The commands are things like "request user input", "compute value", "conditional branch (to schedule)", "request multiple inputs" and "print taxes owed" (these are analogous to commands like "display" in the slide show language and "ask", "branch on results" and "display status" in the tutoring system of Lab 5). The language design you turned in should have supported at least all five of these commands, or something close to them.
For each of these commands, what data do they operate on? "Request user input" needs to know what prompt to give the user and where to put the information provided by the user. "Compute value" needs to know which lines are inputs to the computation and which line is the output. And so on. Each of these gives rise to a structure for a different kind of command.
What's the control? A sequence of commands. Do we run them in
order, or do we ever branch (like in timecond
)? Think about the
schedules -- how should you handle those?
Once you have answers to these questions, a data definition emerges. You need one for the commands (corresponding to the lines in the form). Whether you need one for data depends on whether all of the command data consists of simple built-in types (like strings, symbols, and numbers), or whether you need something more complex (like a slide). A tax program should capture how you order the commands.
Finally, think of the boxes to fill in on each line of the tax form as "local variables" analogous to the variables we added to the slide show program in the no-lambda version of the code.
If you still don't have a workable language design that follows the above format, don't start on the implementation until you revise your design. You'll waste your time and not get any closer to a working implementation.
Recall that when we introduced the slide show program, we talked about all the functionality we might want, then started with a smaller portion and built it up over time (as when you added overlays). Take the same approach here. Pick a couple of (easier) commands, and get the interpreter running on those commands. Test it, then expand it to handle more commands.
Why do it this way? Because (a) it makes the project more manageable, and (b) it hopefully gives you something partially working to turn in, rather than a larger jumble of code that doesn't work. The large jumble demonstrates less of what you understand than a working prototype on a smaller language.
Recognize that the top level of your tax form interpreter will have the same basic structure that you have seen twice already, in the slide show and the interactive exam examples, namely:
;; a tax form is (make-form list[cmd]) (define-struct form (cmds)) ;; a cmd is either ;; - (make-cmd-a ...) ;; - (make-cmd-b ...) ;; ... ;; prepare-taxes : form -> void (define (prepare-taxes a-form) (for-each run-cmd (form-cmds a-form))) ;; run-cmd : cmd -> void (define (run-cmd a-cmd) (cond [(cmd-a? a-cmd) ...] [(cmd-b? a-cmd) ...] ...))Finally, you may find the implementation of variables in the slide show interpreter useful.
For I/O in Racket, you will find these commands useful (all documented in the Help Desk):
printf
and format
is that format
returns the
string (but doesn't display it), while printf
displays the string but
doesn't return it.
Example:
(define (age-to-year) (begin (printf "Enter your age: ") (let ([age (read)]) (printf "You were born in ~a or ~a~n" (- 2001 (+ 1 age)) (- 2001 age)))))
Another function that can help on the implementation:
apply : (arg1 ... argn -> alpha) list-of-length-n ->
alpha
apply
takes a function (of any number of arguments)
and a list (with the number of arguments that the function needs) and
calls the function with the arguments in the list as arguments. Here
are some examples:
(apply + (list 1 2 3))
returns 6
(apply (lambda (x y) (+ (* x x) (* y y))) (list 3 4))
returns 25
(apply (lambda (x y) (+ (* x x) (* y y))) (list 3 4
5))
returns an error, since the list has more elements than the
function expects.
apply
is useful if you need to query the user for a
list of arguments and want to pass them to a function, or if you
gather a list of arguments from some data structures and want to pass
them to a function.
Leave yourself enough time to do this assignment! This project is certainly doable with what we've covered this term, but it does require you to pull together all the material we've covered so far. For most of you, that will take a bit more time than you're probably expecting.