“Code should run as fast as necessary, but no faster; something important is always traded away to increase speed.”
Your task is to transform the interpreter below into a language that can be automatically converted to C and Java. We will be using ParentheC for this task, so you'll be CPSing and registerizing the interpreter below before making final modifications to make it ready for ParentheC.
You cannot receive a grade for this course until you complete this assignment and demonstrate your understanding with one of the instructors. See the end of this page for more details.
EDIT: A couple of persons pointed out possible improvements in our pc2j file. I've implemented an number of those and uploaded a new version. Let me know if you encounter any difficulties.
The language we will be using for the interpreter in the assignment is characterized by the grammar here:
<expr> ::= (exp_const <integer>) | (exp_var <integer>) | (exp_if <expr> <expr> <expr>) | (exp_mult <expr> <expr>) | (exp_sub1 <expr>) | (exp_zero <expr>) | (exp_letcc <expr>) | (exp_abort-with <expr> <expr>) | (exp_let <expr> <expr>) | (exp_lambda <expr>) | (exp_app <expr> <expr>) | (exp_abort-case <expr> <expr> <expr>)
This is quite simple, and identical to the other languages that we have written interpreters for. However, we now explicitly tag all language constructs. When we refer to a variable (using exp_var) we refer to the variable's lexical address.
We provide a parser that will convert normal Scheme-like syntax into the language we are using on this assignment. For example,
> (load "parser.ss") > (parse '((lambda (a b c) (* a c)) 5 6 7)) (exp_app (exp_app (exp_app (exp_lambda (exp_lambda (exp_lambda (exp_mult (exp_var 2) (exp_var 0))))) (exp_const 5)) (exp_const 6)) (exp_const 7))
Use this to generate tests for your program. Notice that the output is curried for you. Note also that our parser produces quoted output, but your value-of will not take quoted expressions as input.
Here is a fully-functioning interpreter that implements this language for us. It uses the ParentheC union type to represent the language.
(load "parenthec.ss") (define-union exp (const n) (var v) (if test conseq alt) (mult rand1 rand2) (sub1 rand) (zero rand) (letcc body) (abort-with vexp kexp) (let vexp body) (lambda body) (app rator rand) (abort-case vexp k1exp k2exp)) (define value-of (lambda (expr env) (union-case expr exp [(const n) n] [(var v) (apply-env env v)] [(if test conseq alt) (if (value-of test env) (value-of conseq env) (value-of alt env))] [(mult rand1 rand2) (* (value-of rand1 env) (value-of rand2 env))] [(sub1 rand) (- (value-of rand env) 1)] [(zero rand) (zero? (value-of rand env))] [(letcc body) (call/cc (lambda (k) (value-of body (envr_extend k env))))] [(abort-with vexp kexp) ((value-of kexp env) (value-of vexp env))] [(abort-case vexp k1exp k2exp) (let ((v (value-of vexp env))) (if (zero? v) ((value-of k1exp env) v) ((value-of k2exp env) v)))] [(let vexp body) (value-of body (envr_extend (value-of vexp env) env))] [(lambda body) (clos_closure body env)] [(app rator rand) (apply-closure (value-of rator env) (value-of rand env))]))) (define-union envr (empty) (extend arg env)) (define apply-env (lambda (env num) (union-case env envr [(empty) (errorf 'env "unbound variable")] [(extend arg env) (if (zero? num) arg (apply-env env (sub1 num)))]))) (define-union clos (closure code env)) (define apply-closure (lambda (c a) (union-case c clos [(closure code env) (value-of code (envr_extend a env))]))) ; Factorial of 5...should be 120. (pretty-print (value-of (exp_app (exp_lambda (exp_app (exp_app (exp_var 0) (exp_var 0)) (exp_const 5))) (exp_lambda (exp_lambda (exp_if (exp_zero (exp_var 0)) (exp_const 1) (exp_mult (exp_var 0) (exp_app (exp_app (exp_var 1) (exp_var 1)) (exp_sub1 (exp_var 0)))))))) (envr_empty))) ; Test of letcc and abort-with...should evaluate to 24. (pretty-print (value-of (exp_mult (exp_const 2) (exp_letcc (exp_mult (exp_const 5) (exp_abort-with (exp_mult (exp_const 2) (exp_const 6)) (exp_var 0))))) (envr_empty))) ;; (let ([fact (lambda (f) ;; (lambda (n) ;; (if (zero? n) ;; 1 ;; (* n ((f f) (sub1 n))))))]) ;; ((fact fact) 5)) (pretty-print (value-of (exp_let (exp_lambda (exp_lambda (exp_if (exp_zero (exp_var 0)) (exp_const 1) (exp_mult (exp_var 0) (exp_app (exp_app (exp_var 1) (exp_var 1)) (exp_sub1 (exp_var 0))))))) (exp_app (exp_app (exp_var 0) (exp_var 0)) (exp_const 5))) (envr_empty))) ;; first abort-case test (pretty-print (value-of (exp_letcc (exp_mult (exp_const 2) (exp_letcc (exp_abort-case (exp_const 0) (exp_var 1) (exp_var 0))))) (envr_empty))) ;; second abort-case test (pretty-print (value-of (exp_letcc (exp_mult (exp_const 2) (exp_letcc (exp_abort-case (exp_const 1) (exp_var 1) (exp_var 0))))) (envr_empty)))
Note that the programs we pass to value-of do not have a quote in front of them. Why is this? Because the programs are no longer lists. We are actually passing a series of nested unions created by calling these tagged constructors.
Note: ParentheC does not support the pretty-print command, so you will need to use printf in your main function to display the results of the execution.
Your assignment is to turn this interpreter into C and Java programs using ParentheC, and run the factorial and letcc test programs. Here are the steps you will need to accomplish:
(define name (lambda () …)) statements to use define-label.main (of no arguments) that does whatever computation you want your program to accomplish.mount-trampoline and dismount-trampoline.pc2c.ss, generate C code for your interpreter, compile the C program, and run the resulting executable, verifying that you see the correct output. (If you don't know how to do this step, check with the AIs for help.)pc2j.scm, generate Java code for your interpreter, compile the Java program, and run the resulting executable, verifying that you see the correct output. For instructions on using pc2j, see the comments at the top of the file.Save a new copy of your interpreter after you finish every step. We will expect you to have all of these intermediate files available during your demonstration. Also, you will often need to go back to an older version of your interpreter and having copies of all of them will save a lot of time.
interp.pc that contains the exact Scheme code you used to generate your C and Java programs.
Add a callcc form to your interpreter that behaves like Scheme's call/cc. Include at least one test program, as well as any necessary changes to the parser or other files in an email to your instructor.