-
Notifications
You must be signed in to change notification settings - Fork 2
/
class.scm
147 lines (125 loc) · 4.65 KB
/
class.scm
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
;;; -*- Mode: Scheme; scheme48-package: soosy -*-
;;;
;;; A class-based single-dispatch OO system
;;;
(define-record-type class
(%make-class %name superclass subclasses variables methods)
class?
(%name class-%name)
(superclass class-superclass)
(subclasses class-subclasses set-class-subclasses!)
(variables class-variables set-class-variables!)
(methods class-methods))
(define-record-type object
(%make-object class variables)
object?
(class object-class)
(variables object-variables))
(define (make-class name superclass variables)
(let ((class (name->class name))
(all-variables (if superclass
(append (class-variables superclass) variables)
variables))
(subclasses '()))
(let ((make-class
(lambda ()
(%make-class name
superclass
subclasses
all-variables
(if superclass
(hash-table-copy (class-methods superclass))
(make-hash-table))))))
(if (not class)
;; if class has not been defined before
(let ((class (make-class)))
(add-class! name class)
(class-add-subclass! superclass class)
class)
;; if the given superclass does not match the known superclass
(cond ((not (eq? (class-superclass class) superclass))
(class-remove-subclass! (class-superclass class) class)
(let ((class (make-class)))
(add-class! name class)
(class-add-subclass! superclass class)
class))
(else class))))))
(define (class-add-subclass! class subclass)
(if (class? class)
(set-class-subclasses! class (cons subclass (class-subclasses class)))
#f))
(define (class-remove-subclass! class subclass)
(if (class? class)
(set-class-subclasses! class (delete subclass (class-subclasses class)))
#f))
(define (class-method class name)
(class-methods/ref (class-methods class) name))
(define (class-name class)
(if (class? class)
(class-%name class)
class))
(define (class-methods/ref methods name)
(or (method-lookup methods name)
(error "unknown method" name)))
(define (method-lookup methods name)
(hash-table-ref/default methods name #f))
(define (class-method-define class name method)
(cond
((not (class? class)) (error "not a class" class))
((not (procedure? method)) (error "not a procedure" method))
(else
(let ((methods (class-methods class)))
(hash-table-set! methods name method)
(map (lambda (subclass)
(let ((methods (class-methods subclass)))
(hash-table-set! methods name method)))
(class-subclasses class)))))
name)
(define (base-class? class)
(eq? (class-superclass class) #f))
(define (subclass? class class*)
(and (class? class)
(class? class*)
(or (eq? class class*)
(subclass? class (class-superclass class*)))))
(define (make-object class)
(if (not (class? class))
(error "wrong type argument" class)
(%make-object class
(make-vector (length (class-variables class))))))
(define (object-of-class? object class)
(and (object? object)
(class? class)
(subclass? class (object-class object))))
(define (object-methods object)
(if (object? object)
(class-methods (object-class object))
#f))
(define (object-method object name)
(if (object? object)
(class-method (object-class object) name)
#f))
(define (offset-of variable class)
(list-index (lambda (item) (eq? item variable))
(class-variables class)))
(define (object-variable object variable)
(let* ((variables (object-variables object))
(class (object-class object))
(offset (offset-of variable class)))
(if (not offset)
(error "variable is not defined" variable)
(vector-ref variables offset))))
(define (set-object-variable! object variable value)
(let* ((variables (object-variables object))
(class (object-class object))
(offset (offset-of variable class)))
(if (not offset)
(error "variable is not defined" variable)
(vector-set! variables offset value))))
(define (send object operation . args)
(let ((method (object-method object operation)))
(apply method object args)))
(define (usual-method class name)
(class-method (class-superclass class) name))
(define (send-usual object operation . args)
(apply (usual-method (object-class object) operation) object args))