It doesn't seem very useful to have +, -, and * to be defined if we can't apply them. We will remedy this shortcoming of Mini-Scheme-B with our specification for Mini-Scheme-C:
<exp> ::= <number> lit (datum)
| <varref> varref (var)
| (<exp> {<exp>}*) app (rator rands)
Finally, we have a language that can do something. It will not be very hard to derive Mini-Scheme-C from Mini-Scheme-B. We need to define a function that applies primitive operations. Luckily for us, +, * and - are already defined in Scheme. Here is the code we will need:
(define apply-prim-op
(lambda (prim-op args)
(case prim-op
((+) (+ (car args) (cadr args)))
((-) (- (car args) (cadr args)))
((*) (* (car args) (cadr args)))
(else (error "Invalid prim-op name:" prim-op)))))
Note that in our implementation, (+ 2 3 4) equals 5, not 9. We may change this later.
Here is the first installment of a general routine that applies functions which we will add to as we go along. Eventually, we will update this routine to include user-defined functions. For now, only primitive operations will be implemented.
(define apply-proc
(lambda (proc args)
(cond
((prim-proc? proc) (apply-prim-op (prim-proc-prim-op proc) args))
(else (error 'apply-proc "Invalid procedure: " proc)))))
Our new interpreter and parser can be found in interp-c.ss and parse-c.ss.
> (load "parse-c.ss") > (load "interp-c.ss") > (read-eval-print) MS> (* 2 3) 6 MS> (+ 2 4) 6 MS> (* 2 (+ 1 2)) 6 MS> (* 2 (- 3 (+ 4 (* 7 (+ 2 3))))) -72 MS> (+ 2 3 4 5 6) 5 MS> exit returning-to-Scheme-proper >
Write Mini-Scheme-D, an extension of Mini-Scheme-C, that supports three new primitive procedures: add1, sub1, and minus.
Mini-Scheme-D should behave as follows:
> (read-eval-print)
MS> minus
#[#{prim-proc btkejzjhs1b2wx3pgmot7k-1} minus]
MS> add1
#[#{prim-proc btkejzjhs1b2wx3pgmot7k-1} add1]
MS> sub1
#[#{prim-proc btkejzjhs1b2wx3pgmot7k-1} sub1]
MS> (minus (+ (add1 4) (sub1 (sub1 3))))
-6
MS> (add1 (* 3 4))
13
MS> exit
returning-to-Scheme-proper
>
Save the files as "parse-d.ss" and "interp-d.ss".
doesn't have list processing functions?!?!? Write Mini-Scheme-E, which implements attach, head, tail, list, and nth which should each behave like cons, car, cdr and list respectively, while nth behaves like list-ref except that it takes its arguments in reverse order. Whereas to find the 3rd member of (list 1 2 3 4 5) you would in Scheme type (list-ref (list 1 2 3 4 5) 2) in Mini-Scheme-E you will call (nth 2 (list 1 2 3 4 5)). The initial environment should also include a new variable, emptylist. Mini-Scheme-E should behave as follows:
> (load "interp-e.ss")
> (parse '(attach 1 emptylist))
#[#{app btkejzjhs1b2wx3pgmot7k-3} #[#{varref btkejzjhs1b2wx3pgmot7k-4} attach] (#[#{lit btkejzjhs1b2wx3pgmot7k-5} 1] #[#{varref btkejzjhs1b2wx3pgmot7k-4} emptylist])]
> (eval-exp (parse '(attach 1 emptylist)))
(1)
> (read-eval-print)
MS> emptylist
()
MS> (attach 2 emptylist)
(2)
MS> list
#[#{prim-proc btkejzjhs1b2wx3pgmot7k-7} list]
MS> (list 1 2 3 4 5)
(1 2 3 4 5)
MS> (nth 2 (list 1 2 3 4 5))
3
MS> (minus (head (attach 3 (attach 4 emptylist))))
-3
MS>