-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprotocols.scm
92 lines (85 loc) · 3.95 KB
/
protocols.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
(use srfi-69)
(use lolevel)
(define-record protocol name impls sigs)
(define (protocol-dispatch-type obj)
(cond
((record-instance? obj) (record-instance-type obj))
((extended-procedure? obj) (protocol-dispatch-type (procedure-data obj)))
((pair? obj) 'pair)
((vector? obj) 'vector)
((string? obj) 'string)
(else (abort (conc "Unsupported protocol dispatch type: " obj)))))
(define (protocol-dispatch-fn protocol obj mname)
(let* ((type (protocol-dispatch-type obj))
(impls-table (protocol-impls protocol))
(impl (hash-table-ref impls-table type
(lambda ()
(abort
(conc "Protocol " (protocol-name protocol)
" is not implemented for type "
type)))))
(method (hash-table-ref impl mname
(lambda ()
(abort
(conc (protocol-name protocol) "/" mname
" is not implemented for type " type))))))
method))
(define-syntax defprotocol
(ir-macro-transformer
(lambda (form ren cmp)
(if (null? (cdr form)) (syntax-error "Protocol must have a name"))
(let ((pname (cadr form))
(sigs (cddr form)))
(if (not (symbol? pname)) (syntax-error "Name must be an unquoted symbol"))
(do ((sigs sigs (cdr sigs)))
((null? sigs))
(##sys#check-syntax 'defprotocol (car sigs) '(symbol . #((symbol . lambda-list) 1))))
`(begin
(##core#set! ,(ren pname) (make-protocol ',pname (make-hash-table) ',sigs))
,@(map (lambda (sig)
`(##core#set! ,(ren (car sig))
(case-lambda
,@(map (lambda (s)
(list s
`((protocol-dispatch-fn ,(ren pname) ,(car s) ',(car sig))
(if (extended-procedure? ,(car s))
(procedure-data ,(car s))
,(car s))
,@(cdr s))))
(cdr sig)))))
sigs))))))
(define (protocol-add-impl protocol type mname impl)
(let ((type-impls (hash-table-ref (protocol-impls protocol) type)))
(hash-table-set! type-impls mname impl)))
(define-syntax extend-type
(ir-macro-transformer
(lambda (form ren cmp)
(if (null? (cdr form)) (syntax-error "Type required"))
(if (null? (cddr form)) (syntax-error "At least one protocol must be specified"))
(let* ((type (cadr form))
(specs (cddr form)))
(let loop ((prot (car specs))
(rest (cdr specs))
(emits '()))
(if (not (symbol? prot)) (syntax-error "Protocol name must be a symbol"))
(cond
((null? rest)
`(begin
(hash-table-set! (protocol-impls ,(ren prot)) ',type (make-hash-table))
,@emits))
((symbol? (car rest))
(loop (car rest)
(cdr rest)
emits))
(else
(##sys#check-syntax 'extend-type (car rest) '(symbol list . _))
(let* ((impl (car rest))
(mname (car impl))
(bodies (cdr impl))
(bodies (if (symbol? (caar bodies))
(list bodies)
bodies)))
(##sys#check-syntax 'extend-type bodies '#(((symbol . lambda-list) _ . _) 1))
(loop prot (cdr rest) (cons `(protocol-add-impl ,(ren prot) ',type ',(caar rest)
(case-lambda ,@bodies))
emits))))))))))