;; This file contains excerpts from the textbook Concrete ;; Abstractions: An Introduction to Computer Science Using Scheme, by ;; Max Hailperin, Barbara Kaiser, and Karl Knight, Copyright (c) 1998 ;; by the authors. Full text is available for free at ;; http://www.gustavus.edu/+max/concrete-abstractions.html ;; Before this explanatory version of mini-scheme will work, you need ;; to do the following: ;; ;; (1) Enhance matches? and write substitutions-in-to-match as described ;; in Section 7.6. ;; ;; (2) Write all-are from Review Problem 7.49. ;; ;; (3) Write name? (and keyword?) from Exercise 10.1. ;; ;; (4) Flesh out make-initial-global-environment. (Exercise 10.21.) ;; ;; (5) Modify each AST constructor other than make-application-ast ;; (a) so as to support the unparse operation (Exercise 10.22) and ;; (b) to support and use evaluate-in-at. (Exercise 10.24) ;; ;; (6) Modify read-eval-print-loop to evaluate at level 1. (Exercise 10.25) ;; ;; (7) Modify make-procedure, make-application-ast, and ;; make-mini-scheme-version-of, as described in Exercise 10.26. ;; ;; (8) Find 3 uses for evaluate-additional-in-at. (Exercise 10.27) ;; ;; (9) Call blank-line-at in make-mini-scheme-version-of. (Exercise 10.28) ;; ;; Note that of these, (1)-(4) are also neeed for the normal, ;; non-explanatory version of mini-scheme. ;; Chapter 7: Lists ;; 7.6 An Application: A Movie Query System (define make-pattern/action (lambda (pattern action) (cons pattern action))) (define pattern car) (define action cdr) ;; The definition of matches? given below (the second one in Chapter 7) ;; will not work for micro-scheme -- you still need to add the _ wildcard, ;; as described in Exercise 7.29. (define matches? (lambda (pattern question) (cond ((null? pattern) (null? question)) ((null? question) #f) ((list? (car pattern)) (if (member (car question) (car pattern)) (matches? (cdr pattern) (cdr question)) #f)) ((equal? (car pattern) '...) #t) ((equal? (car pattern) (car question)) (matches? (cdr pattern) (cdr question))) (else #f)))) (define substitutions-in-to-match "You need to write this procedure (Exercises 7.26, 7.30).") ;; Review Problems (define all-are "You need to write this procedure (Exercise 7.49).") ;; Chapter 10: Implementing Programming Languages ;; 10.2 Syntax (define keyword? "You need to write this procedure (Exercise 10.1).") (define name? "You need to write this procedure (Exercise 10.1).") ;; 10.3 Micro-Scheme (define parse (lambda (expression) (define loop (lambda (p/a-list) (cond ((null? p/a-list) (error "invalid expression" expression)) ((matches? (pattern (car p/a-list)) expression) (apply (action (car p/a-list)) (substitutions-in-to-match (pattern (car p/a-list)) expression))) (else (loop (cdr p/a-list)))))) ;end of loop (cond ((name? expression) ;start of main parse procedure (make-name-ast expression)) ((or (number? expression) (string? expression) (boolean? expression)) (make-constant-ast expression)) ((list? expression) (loop micro-scheme-parsing-p/a-list)) (else (error "invalid expression" expression))))) (define micro-scheme-parsing-p/a-list (list (make-pattern/action '(if _ _ _) (lambda (test if-true if-false) (make-conditional-ast (parse test) (parse if-true) (parse if-false)))) (make-pattern/action '(lambda _ _) (lambda (parameters body) (if (and (list? parameters) ((all-are name?) parameters)) (make-abstraction-ast parameters (parse body)) (error "invalid expression" (list 'lambda parameters body))))) (make-pattern/action '(quote _) (lambda (value) (make-constant-ast value))) (make-pattern/action '(...) ; note that this *must* come last (lambda (operator&operands) (let ((asts (map parse operator&operands))) (make-application-ast (car asts) (cdr asts))))))) (define substitute-for-in (lambda (value name ast) ((ast 'substitute-for) value name))) ;; 10.4 Global Definitions: Turning Micro-Scheme into Mini-Scheme (define read-eval-print-loop (lambda () (define loop (lambda (global-environment) (display ";Enter Mini-Scheme expr. or definition:") (newline) (let ((expression-or-definition (read))) (if (definition? expression-or-definition) (let ((name (definition-name expression-or-definition)) (value (evaluate-in (parse (definition-expression expression-or-definition)) global-environment))) (display ";Mini-scheme defined: ") (write name) (newline) (loop (extend-global-environment-with-naming global-environment name value))) (let ((value (evaluate-in (parse expression-or-definition) global-environment))) (display ";Mini-scheme value: ") (write value) (newline) (loop global-environment)))))) (loop (make-initial-global-environment)))) (define definition? (lambda (x) (and (list? x) (matches? '(define _ _) x)))) (define definition-name cadr) (define definition-expression caddr) (define make-name-ast (lambda (name) (define the-ast (lambda (message) (cond ((equal? message 'evaluate-in) (lambda (global-environment) (look-up-value-in name global-environment))) ((equal? message 'substitute-for) (lambda (value name-to-substitute-for) (if (equal? name name-to-substitute-for) (make-constant-ast value) the-ast))) (else (error "unknown operation on a name AST" message))))) the-ast)) (define make-constant-ast (lambda (value) (define the-ast (lambda (message) (cond ((equal? message 'evaluate-in) (lambda (global-environment) value)) ((equal? message 'substitute-for) (lambda (value name) the-ast)) (else (error "unknown operation on a constant AST" message))))) the-ast)) (define make-conditional-ast (lambda (test-ast if-true-ast if-false-ast) (lambda (message) (cond ((equal? message 'evaluate-in) (lambda (global-environment) (if (evaluate-in test-ast global-environment) (evaluate-in if-true-ast global-environment) (evaluate-in if-false-ast global-environment)))) ((equal? message 'substitute-for) (lambda (value name) (make-conditional-ast (substitute-for-in value name test-ast) (substitute-for-in value name if-true-ast) (substitute-for-in value name if-false-ast)))) (else (error "unknown operation on a conditional AST" message)))))) (define make-abstraction-ast (lambda (parameters body-ast) (define the-ast (lambda (message) (cond ((equal? message 'evaluate-in) (lambda (global-environment) (make-procedure parameters body-ast))) ((equal? message 'substitute-for) (lambda (value name) (if (member name parameters) the-ast (make-abstraction-ast parameters (substitute-for-in value name body-ast))))) (else (error "unknown operation on an abstraction AST" message))))) the-ast)) (define make-procedure (lambda (parameters body-ast) (lambda global-environment&arguments (let ((global-environment (car global-environment&arguments)) (arguments (cdr global-environment&arguments))) (define loop (lambda (parameters arguments body-ast) (cond ((null? parameters) (if (null? arguments) (evaluate-in body-ast global-environment) (error "too many arguments"))) ((null? arguments) (error "too few arguments")) (else (loop (cdr parameters) (cdr arguments) (substitute-for-in (car arguments) (car parameters) body-ast)))))) (loop parameters arguments body-ast))))) (define look-up-value-in (lambda (name global-environment) (global-environment name))) (define extend-global-environment-with-naming (lambda (old-environment name value) (lambda (n) (if (equal? n name) value (old-environment n))))) (define make-mini-scheme-version-of (lambda (procedure) (lambda global-environment&arguments (let ((global-environment (car global-environment&arguments)) (arguments (cdr global-environment&arguments))) (apply procedure arguments))))) (define make-initial-global-environment (lambda () (let ((ms+ (make-mini-scheme-version-of +)) (ms- (make-mini-scheme-version-of -)) ;; the rest get similarly converted in here ) (lambda (name) (cond ((equal? name '+) ms+) ((equal? name '-) ms-) ;; the rest get similarly selected in here (else (error "Unrecognized name" name))))))) ;; 10.5 An Application: Adding Explanatory Output to Mini-Scheme (define unparse (lambda (ast) (ast 'unparse))) (define display-times (lambda (output count) (if (= count 0) 'done (begin (display output) (display-times output (- count 1)))))) (define make-application-ast (lambda (operator-ast operand-asts) (lambda (message) (cond ((equal? message 'unparse) (cons (unparse operator-ast) (map unparse operand-asts))) ((equal? message 'evaluate-in-at) (lambda (global-environment level) (let ((procedure (evaluate-in-at operator-ast global-environment (+ level 1))) (arguments (map (lambda (ast) (evaluate-in-at ast global-environment (+ level 1))) operand-asts))) (apply procedure (cons global-environment arguments))))) ((equal? message 'substitute-for) (lambda (value name) (make-application-ast (substitute-for-in value name operator-ast) (map (lambda (operand-ast) (substitute-for-in value name operand-ast)) operand-asts)))) (else (error "unknown operation on an application AST" message)))))) (define write-with-at (lambda (thing indicator level) (display-times "| " (- level 1)) (display "+-") (display indicator) (display " ") (write thing) (newline))) (define blank-line-at (lambda (level) (display-times "| " level) (newline))) (define evaluate-in-at (lambda (ast global-environment level) (blank-line-at (- level 1)) (write-with-at (unparse ast) "<" level) (let ((value ((ast 'evaluate-in-at) global-environment level))) (write-with-at value ">" level) value))) (define evaluate-additional-in-at (lambda (ast global-environment level) (blank-line-at level) (write-with-at (unparse ast) "-" level) ((ast 'evaluate-in-at) global-environment level)))