summaryrefslogtreecommitdiffstats
path: root/ps03_evalapply/ghelper.scm
blob: 7b8613d4fec9feb51d3adc237e092f8bc86a016b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
;;;;           Most General Generic-Operator Dispatch

(declare (usual-integrations))

;;; Generic-operator dispatch is implemented here by a discrimination
;;; list, where the arguments passed to the operator are examined by
;;; predicates that are supplied at the point of attachment of a
;;; handler (by ASSIGN-OPERATION).

;;; To be the correct branch all arguments must be accepted by
;;; the branch predicates, so this makes it necessary to
;;; backtrack to find another branch where the first argument
;;; is accepted if the second argument is rejected.  Here
;;; backtracking is implemented by OR.

(define (make-generic-operator arity default-operation)
  (let ((record (make-operator-record arity)))

    (define (operator . arguments)
      (if (not (= (length arguments) arity))
          (error:wrong-number-of-arguments operator arity arguments))
      (let ((succeed
	     (lambda (handler)
	       (apply handler arguments))))
	(let per-arg
	    ((tree (operator-record-tree record))
	     (args arguments)
	     (fail
	      (lambda ()
		(error:no-applicable-methods operator arguments))))
	  (let per-pred ((tree tree) (fail fail))
	    (cond ((pair? tree)
		   (if ((caar tree) (car args))
		       (if (pair? (cdr args))
			   (per-arg (cdar tree)
				    (cdr args)
				    (lambda ()
				      (per-pred (cdr tree) fail)))
			   (succeed (cdar tree)))
		       (per-pred (cdr tree) fail)))
		  ((null? tree)
		   (fail))
		  (else
		   (succeed tree)))))))

    (hash-table/put! *generic-operator-table* operator record)
    (if default-operation
	(assign-operation operator default-operation))
    operator))

(define *generic-operator-table*
  (make-eq-hash-table))

(define (make-operator-record arity) (cons arity '()))
(define (operator-record-arity record) (car record))
(define (operator-record-tree record) (cdr record))
(define (set-operator-record-tree! record tree) (set-cdr! record tree))

(define (assign-operation operator handler . argument-predicates)
  (let ((record
         (let ((record (hash-table/get *generic-operator-table* operator #f))
               (arity (length argument-predicates)))
           (if record
               (begin
                 (if (not (<= arity (operator-record-arity record)))
                     (error "Incorrect operator arity:" operator))
                 record)
               (let ((record (make-operator-record arity)))
                 (hash-table/put! *generic-operator-table* operator record)
                 record)))))
    (set-operator-record-tree! record
                               (bind-in-tree argument-predicates
                                             handler
                                             (operator-record-tree record))))
  operator)

(define defhandler assign-operation)

(define (bind-in-tree keys handler tree)
  (let loop ((keys keys) (tree tree))
    (if (pair? keys)
	(let find-key ((tree* tree))
	  (if (pair? tree*)
	      (if (eq? (caar tree*) (car keys))
		  (begin
		    (set-cdr! (car tree*)
			      (loop (cdr keys) (cdar tree*)))
		    tree)
		  (find-key (cdr tree*)))
	      (cons (cons (car keys)
			  (loop (cdr keys) '()))
		    tree)))
	(if (pair? tree)
	    (let ((p (last-pair tree)))
	      (if (not (null? (cdr p)))
		  (warn "Replacing a handler:" (cdr p) handler))
	      (set-cdr! p handler)
	      tree)
	    (begin
	      (if (not (null? tree))
		  (warn "Replacing top-level handler:" tree handler))
	      handler)))))