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>