1 ! Copyright (C) 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: kernel math sequences vectors classes classes.algebra
4 combinators arrays words assocs parser namespaces make
5 definitions prettyprint prettyprint.backend prettyprint.custom
6 quotations generalizations debugger io compiler.units
7 kernel.private effects accessors hashtables sorting shuffle
11 ! PART I: Converting hook specializers
12 : canonicalize-specializer-0 ( specializer -- specializer' )
21 : canonicalize-specializer-1 ( specializer -- specializer' )
24 [ length <reversed> [ 1+ neg ] map ] keep zip
25 [ length args [ max ] change ] keep
29 [ keys [ hooks get adjoin ] each ] keep
32 : canonicalize-specializer-2 ( specializer -- specializer' )
36 { [ dup integer? ] [ ] }
37 { [ dup word? ] [ hooks get index ] }
41 : canonicalize-specializer-3 ( specializer -- specializer' )
42 >r total get object <array> dup <enum> r> update ;
44 : canonicalize-specializers ( methods -- methods' hooks )
46 [ >r canonicalize-specializer-0 r> ] assoc-map
51 [ >r canonicalize-specializer-1 r> ] assoc-map
53 hooks [ natural-sort ] change
55 [ >r canonicalize-specializer-2 r> ] assoc-map
57 args get hooks get length + total set
59 [ >r canonicalize-specializer-3 r> ] assoc-map
64 : drop-n-quot ( n -- quot ) \ drop <repetition> >quotation ;
66 : prepare-method ( method n -- quot )
67 [ 1quotation ] [ drop-n-quot ] bi* prepend ;
69 : prepare-methods ( methods -- methods' prologue )
70 canonicalize-specializers
71 [ length [ prepare-method ] curry assoc-map ] keep
72 [ [ get ] curry ] map concat [ ] like ;
74 ! Part II: Topologically sorting specializers
75 : maximal-element ( seq quot -- n elt )
77 swapd [ call +lt+ = ] 2curry filter empty?
78 ] 2curry find [ "Topological sort failed" throw ] unless* ;
81 : topological-sort ( seq quot -- newseq )
82 >r >vector [ dup empty? not ] r>
83 [ dupd maximal-element >r over delete-nth r> ] curry
84 [ ] produce nip ; inline
86 : classes< ( seq1 seq2 -- lt/eq/gt )
89 { [ 2dup eq? ] [ +eq+ ] }
90 { [ 2dup [ class<= ] [ swap class<= ] 2bi and ] [ +eq+ ] }
91 { [ 2dup class<= ] [ +lt+ ] }
92 { [ 2dup swap class<= ] [ +gt+ ] }
95 ] 2map [ +eq+ eq? not ] find nip +eq+ or ;
97 : sort-methods ( alist -- alist' )
98 [ [ first ] bi@ classes< ] topological-sort ;
100 ! PART III: Creating dispatch quotation
101 : picker ( n -- quot )
106 [ 1- picker [ >r ] [ r> swap ] surround ]
109 : (multi-predicate) ( class picker -- quot )
110 swap "predicate" word-prop append ;
112 : multi-predicate ( classes -- quot )
113 dup length <reversed>
114 [ picker 2array ] 2map
115 [ drop object eq? not ] assoc-filter
117 [ (multi-predicate) ] { } assoc>map
118 unclip [ swap [ f ] \ if 3array append [ ] like ] reduce
121 : argument-count ( methods -- n )
122 keys 0 [ length max ] reduce ;
124 ERROR: no-method arguments generic ;
126 : make-default-method ( methods generic -- quot )
127 >r argument-count r> [ >r narray r> no-method ] 2curry ;
129 : multi-dispatch-quot ( methods generic -- quot )
130 [ make-default-method ]
131 [ drop [ >r multi-predicate r> ] assoc-map reverse ]
135 PREDICATE: generic < word
136 "multi-methods" word-prop >boolean ;
138 : methods ( word -- alist )
139 "multi-methods" word-prop >alist ;
141 : make-generic ( generic -- quot )
143 [ methods prepare-methods % sort-methods ] keep
144 multi-dispatch-quot %
147 : update-generic ( word -- )
148 dup make-generic define ;
151 PREDICATE: method-body < word
152 "multi-method-generic" word-prop >boolean ;
154 M: method-body stack-effect
155 "multi-method-generic" word-prop stack-effect ;
157 M: method-body crossref?
158 "forgotten" word-prop not ;
160 : method-word-name ( specializer generic -- string )
161 [ name>> % "-" % unparse % ] "" make ;
163 : method-word-props ( specializer generic -- assoc )
165 "multi-method-generic" set
166 "multi-method-specializer" set
169 : <method> ( specializer generic -- word )
170 [ method-word-props ] 2keep
171 method-word-name f <word>
174 : with-methods ( word quot -- )
175 over >r >r "multi-methods" word-prop
176 r> call r> update-generic ; inline
178 : reveal-method ( method classes generic -- )
179 [ set-at ] with-methods ;
181 : method ( classes word -- method )
182 "multi-methods" word-prop at ;
184 : create-method ( classes generic -- method )
188 drop [ <method> dup ] 2keep reveal-method
191 : niceify-method ( seq -- seq )
192 [ dup \ f eq? [ drop f ] when ] map ;
195 "Type check error" print
197 "Generic word " write dup generic>> pprint
198 " does not have a method applicable to inputs:" print
199 dup arguments>> short.
201 "Inputs have signature:" print
202 dup arguments>> [ class ] map niceify-method .
204 "Available methods: " print
205 generic>> methods canonicalize-specializers drop sort-methods
206 keys [ niceify-method ] map stack. ;
208 : forget-method ( specializer generic -- )
209 [ delete-at ] with-methods ;
211 : method>spec ( method -- spec )
212 [ "multi-method-specializer" word-prop ]
213 [ "multi-method-generic" word-prop ] bi prefix ;
215 : define-generic ( word -- )
216 dup "multi-methods" word-prop [
219 [ H{ } clone "multi-methods" set-word-prop ]
226 CREATE define-generic ; parsing
228 : parse-method ( -- quot classes generic )
229 parse-definition [ 2 tail ] [ second ] [ first ] tri ;
231 : create-method-in ( specializer generic -- method )
232 create-method dup save-location f set-word ;
234 : CREATE-METHOD ( -- method )
235 scan-word scan-object swap create-method-in ;
237 : (METHOD:) ( -- method def ) CREATE-METHOD parse-definition ;
239 : METHOD: (METHOD:) define ; parsing
243 scan-word 1array scan-word create-method-in
247 ! Definition protocol. We qualify core generics here
251 syntax:M: generic definer drop \ GENERIC: f ;
253 syntax:M: generic definition drop f ;
255 PREDICATE: method-spec < array
256 unclip generic? >r [ class? ] all? r> and ;
258 syntax:M: method-spec where
259 dup unclip method [ ] [ first ] ?if where ;
261 syntax:M: method-spec set-where
262 unclip method set-where ;
264 syntax:M: method-spec definer
265 unclip method definer ;
267 syntax:M: method-spec definition
268 unclip method definition ;
270 syntax:M: method-spec synopsis*
271 unclip method synopsis* ;
273 syntax:M: method-spec forget*
274 unclip method forget* ;
276 syntax:M: method-body definer
279 syntax:M: method-body synopsis*
281 [ "multi-method-generic" word-prop pprint-word ]
282 [ "multi-method-specializer" word-prop pprint* ] bi ;