CS 3520 Homework 5   - Due October 2

Important: This homework requires much more thinking than typing. The solution to the first exercise consists of roughly 25 lines of code. The solution the the second exercise consists of a few lines of (difficult to read!) code in a toy language. Both exercises require a thorough understanding of the interpreter that is in the book (developed in class).

Exercise 5.1, Interpreter for a New Toy Language

Your task: Implement an interpreter for a new toy language, starting with this code.

The syntax of the language is as follows:
  <expr> = <number>                        ;; an integer
         | @<number>                       ;; looks up a value in the env
         | [<expr>]                        ;; creates a function (1 arg)
         | call(<expr>, <expr>)            ;; applies a function (1 arg)
         | +(<expr>, <expr>)               ;; add
         | ifzero(<expr>, <expr>, <expr>)  ;; branch
         | -(<expr>, <expr>)               ;; subtract
In this language, the only primitive values are integers.

The + and - primitive forms add and subtract numbers:

The ifzero form evaluates its first argument. If it is zero, then the result is the evaluation of the second argument (without evaluating the third), otherwise the result is the evaluation of the third argument (without evaluating the second).

The [ ] form is like lambda, except with only the body specified. No argument name is provided because arguments will be accessed by lexical position. All functions created with [ ] take a single argument.

The call form calls a function, given a single argument.

The @ form accesses a function argument. The number provided after @ indicates the lexical depth of the argument, counting from 0. Thus, @0 obtains the argument of the immediately enclosing function.

Expressed and denoted values both include functions:

This language is very similar to the one presented in EoPL 3.5, so use the book's interpreter as a guide to implementing your own.

Exercise 5.2, Recursive functions

Despite its simplicity, the toy language above is fairly powerful. For example, we can compute the sum of all integers from 0 to, say, 10...

In Scheme, we might write:
(letrec ([sum (lambda (n)
                (if (zero? n)
                    0
                    (+ n (sum (- n 1)))))])
  (sum 10))
If we have let but not letrec, then it's tricker, but still possible:
(let ([summer (lambda (summer n)
                (if (zero? n)
                    0
                    (+ n (summer summer (- n 1)))))])
  (summer summer 10))
The toy language doesn't event provide let, however. The only way to bind a variable is with a function. Fortunately, we can simulate let using functions:
((lambda (summer)
  (summer summer 10))
 ;; This argument gets bound to "summer" in the above function
 (lambda (summer n)
   (if (zero? n)
       0
       (+ n (summer summer (- n 1))))))
One more problem --- the toy language supports only 1-argument functions. In general, we can turn a 2-argument function into a function that takes one argument, then returns another function to get the second argument, like this:
(lambda (x y) (+ x y))  ~~~>  (lambda (x) (lambda (y) (+ x y)))
2-argument calls get changed into nested function calls:
((lambda (x y) (+ x y)) 1 2)  ~~~>  (((lambda (x) (lambda (y) (+ x y))) 1) 2)
Thus, we can write the summing program as
((lambda (summer)
  ((summer summer) 10)) ;; << used to be a 2-arg call
 (lambda (summer) (lambda (n)  ;; << used to be a 2-arg function
                    (if (zero? n)
                        0 ;; v--- used to be a 2-arg call
                        (+ n ((summer summer) (- n 1)))))))
Finally, since this Scheme function relies only on 1-argument functions, addition, subtraction, and zero tests, we can translate it into the toy language:
call([call(call(@0, @0), 10)], 
     [[ifzero(@0,
              0,
              +(@0, call(call(@1, @1), -(@0, 1))))]])
The result will be 55! Of course, we could replace 10 with any other non-negative integer n to compute the sum from 0 to n.

Your task: Use above techniques to compute 6*7 using the toy language. Express your answer as a function answer-for-5.2 that takes no arguments and returns the program as a string. Of course, your program should contain the constants 6 and 7 such that they can be replaced with arbitrary non-negative integers m and n to compute n*m.

As a hint, here is a useful starting point in Scheme:
(letrec ([times (lambda (n m)
                  (if (zero? n)
                      0
                      (+ m (times (- n 1) m))))])
  (times 6 7))
Note that the above function relies on only addition and subtraction.

If you get stuck, send mail to teach-cs3520 for another hint!


Last update: Tuesday, September 25th, 2001
mflatt@cs.utah.edu