;;;************************************************************ ;;; Homework 4. Solution ;;; ;;; This Solution is suggested by stelo@cs. ;;; ;;;************************************************************ ;;; ;;; original: Tue Oct 18 11:02:26 1988 ;;; revised Fri Oct 23 10:30:16 1992 ;;; revised to use sllgen Tue Sep 28 17:09:43 1993 ;;; Assumes sllgen.scm is loaded (load "/.xinuserver/u57/hylee/pub/source/sllgen.scm") ;;; ********************************************************** ;;; ;;; Top-level interface (define run (lambda (string) (eval-exp (scan&parse string) init-env))) ;;; *********************************************************** ;;; Lexical Specification ; MODIFIED add new keywords, no identifier starts with ':' (define automaton-5 '((proc if then else let set! in print begin end cond ==> =) (start-state ((#\space #\tab #\newline) #f) ((alphabetic #\* #\+ #\- #\/ #\! #\=) (arbno (numeric alphabetic #\* #\+ #\- #\/ #\! #\= #\: #\>)) identifier) ((numeric) (arbno numeric) number) (#\( lparen) (#\) rparen) (#\^ end-marker) (#\: colon) (#\; semicolon) (#\% comment-state)) (comment-state (#\newline #f) (any comment-state)))) ;;; **************************************************************** ;;; Grammar (define grammar-5 '((expression (number) lit-exp) (expression (identifier) var-exp) (expression (set! identifier expression) assign-exp) (expression (if expression then expression else expression) if-exp) (expression (let (arbno declaration) in expression) let-exp) ; MODIFIED procedure with optional parameters (expression (proc lparen (arbno identifier) (arbno colon declaration) rparen expression) proc-exp) (expression (lparen expression (arbno operand) rparen) app-exp) (operand (colon declaration) operand-key) (operand (expression) operand-exp) ; MODIFIED print (expression (print expression) print-exp) ; MODIFIED begin (expression (begin (arbno expression semicolon) end) begin-end-exp) ; MODIFIED cond (expression (cond (arbno expression ==> expression) end) cond-exp) (declaration (identifier = expression) decl))) (define scan&parse (sllgen:string->tree automaton-5 grammar-5)) ;;; End of syntactic specification ;;; **************************************************************** ;;; Data Structure Definitions for Interpreter ;;; **************************************************************** ;;; Cells (define make-cell (lambda (value) (cons '*cell value))) (define deref-cell cdr) (define set-cell! set-cdr!) ; danger! ;;; **************************************************************** ;; Finite functions: ribcage (list of frames) ; empty-ribcage ==> nil ; (extend-ribcage names vals ff) ==> ((names . vals) . ff) (define the-empty-ribcage '()) (define extend-ribcage (lambda (names vals ribcage) (if (= (length names) (length vals)) (cons (cons names vals) ribcage) (error 'extend-ribcage "wrong number of values. names: ~s values: ~s" names vals)))) (define apply-ribcage (lambda (ribcage z) (if (null? ribcage) (error 'apply-ribcage "identifier ~s not found" z) (let ((names (caar ribcage))(vals (cdar ribcage))(f (cdr ribcage))) (if (memq z names) (letrec ;; can assume z will be found in names ((loop (lambda (names vals) (if (eqv? z (car names)) (car vals) (loop (cdr names) (cdr vals)))))) (loop names vals)) (apply-ribcage f z)))))) ;;; **************************************************************** ;;; Building environments from ribcages: (define the-empty-env the-empty-ribcage) (define extend-env (lambda (names values env) (extend-ribcage names (map make-cell values) env))) (define apply-env apply-ribcage) ;;; ***************************************************************** ;;; Declarations (define-record decl (var exp)) (define-record key-decl (var exp)) ;;; Closures and procedures (define-record closure (formals body env)) (define build-user-proc make-closure) ;;; ***************************************************************** ;;; ***************************************************************** ;;; The Interpreter Proper (define eval-exp (lambda (exp env) ; (newline) ; (display "eval-exp(exp): ") ; (display exp) ; (newline) (record-case exp (lit-exp (constant) constant) (var-exp (id) (deref-cell (apply-env env id))) (assign-exp (ident rhs-exp) (set-cell! (apply-env env ident) (eval-exp rhs-exp env))) (if-exp (test-exp true-exp false-exp) (if (zero? (eval-exp test-exp env)) (eval-exp false-exp env) (eval-exp true-exp env))) (let-exp (decls body) (let ((ids (map decl->var decls)) (exps (map decl->exp decls))) (let ((new-env (extend-env ids (eval-rands exps env) env))) (eval-exp body new-env)))) (app-exp (rator rands) (let ((proc (eval-exp rator env)) (args (eval-rands rands env))) (apply-proc proc args))) ; MODIFIED procedure with optional parameters (proc-exp (formals keys body) (let ((keys-value (eval-rands keys env))) (let ((keys-id (map decl->var keys-value)) (keys-ex (map decl->exp keys-value))) (let ((new-env (extend-env keys-id keys-ex env))) (build-user-proc formals body new-env))))) (operand-exp (rator) (eval-exp rator env)) (operand-key (rator) (eval-exp rator env)) (decl (var exp) (make-decl var (eval-exp exp env))) ; MODIFIED print (print-exp (print-body) (print (eval-exp print-body env)) 1) ; MODIFIED begin end (begin-end-exp (stmnts) (if (null? stmnts) 'unspecified (car (reverse (eval-rands stmnts env))))) ; MODIFIED cond end (cond-exp (caseslist) (resolve-cond caseslist env)) (else (error 'eval-exp "Bad abstract syntax: ~s" exp))))) (define resolve-cond (lambda (cl env) (if (null? cl) 'unspecified (if (eq? (eval-exp (car cl) env) 0) (resolve-cond (cddr cl) env) (eval-exp (cadr cl) env))))) (define eval-rands (lambda (rands env) (map (lambda (exp) (eval-exp exp env)) rands))) (define apply-proc (lambda (proc args) (record-case proc (primitive-proc (primop) (apply-primop primop args)) (closure (formals body env) (let ((actuals (filter-out list? args)) ; MODIFIED (key-actuals (filter-in list? args))) (let ((new-env (extend-env (map decl->var key-actuals) (map decl->exp key-actuals) env))) (eval-exp body (extend-env formals actuals new-env))))) (else (error 'apply-proc "Bad Procedure ~s" proc))))) ; MODIFIED helper function (define filter-in (lambda (p lst) (apply append (map (lambda (x) (if (p x) (list x) '())) lst)))) ; MODIFIED helper function (define filter-out (lambda (p lst) (apply append (map (lambda (x) (if (p x) '() (list x))) lst)))) ;;; ***************************************************************** ;;; Primops (define-record primitive-proc (primop)) (define apply-primop (lambda (primop args) (case primop ((+-op) (+ (car args) (cadr args))) ((--op) (- (car args) (cadr args))) ((*-op) (* (car args) (cadr args))) ((==-op) (if (eq? (car args) (cadr args)) 1 0)) ; MODIFIED new op == ((+1-op) (+ (car args) 1)) ((-1-op) (- (car args) 1)) (else (error 'apply-primop "Unknown Primop: ~s" primop))))) ;;; ***************************************************************** ;;; The Initial Environment (define build-init-env (lambda (pairs) (extend-env (map car pairs) (map make-primitive-proc (map cadr pairs)) the-empty-env))) (define init-pairs '((+ +-op) (- --op) (* *-op) (== ==-op) ; MODIFIED new op == (add1 +1-op) (sub1 -1-op))) (define init-env (build-init-env init-pairs)) ;;; ***************************************************************