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 ] }
42 : canonicalize-specializer-3 ( specializer -- specializer' )
43 [ total get object <array> dup <enum> ] dip update ;
45 : canonicalize-specializers ( methods -- methods' hooks )
47 [ [ canonicalize-specializer-0 ] dip ] assoc-map
52 [ [ canonicalize-specializer-1 ] dip ] assoc-map
54 hooks [ natural-sort ] change
56 [ [ canonicalize-specializer-2 ] dip ] assoc-map
58 args get hooks get length + total set
60 [ [ canonicalize-specializer-3 ] dip ] assoc-map
65 : drop-n-quot ( n -- quot ) \ drop <repetition> >quotation ;
67 : prepare-method ( method n -- quot )
68 [ 1quotation ] [ drop-n-quot ] bi* prepend ;
70 : prepare-methods ( methods -- methods' prologue )
71 canonicalize-specializers
72 [ length [ prepare-method ] curry assoc-map ] keep
73 [ [ get ] curry ] map concat [ ] like ;
75 ! Part II: Topologically sorting specializers
76 : maximal-element ( seq quot -- n elt )
78 swapd [ call +lt+ = ] 2curry filter empty?
79 ] 2curry find [ "Topological sort failed" throw ] unless* ;
82 : topological-sort ( seq quot -- newseq )
83 [ >vector [ dup empty? not ] ] dip
84 [ dupd maximal-element [ over delete-nth ] dip ] curry
85 [ ] produce nip ; inline
87 : classes< ( seq1 seq2 -- lt/eq/gt )
90 { [ 2dup eq? ] [ +eq+ ] }
91 { [ 2dup [ class<= ] [ swap class<= ] 2bi and ] [ +eq+ ] }
92 { [ 2dup class<= ] [ +lt+ ] }
93 { [ 2dup swap class<= ] [ +gt+ ] }
96 ] 2map [ +eq+ eq? not ] find nip +eq+ or ;
98 : sort-methods ( alist -- alist' )
99 [ [ first ] bi@ classes< ] topological-sort ;
101 ! PART III: Creating dispatch quotation
102 : picker ( n -- quot )
107 [ 1- picker [ dip swap ] curry ]
110 : (multi-predicate) ( class picker -- quot )
111 swap "predicate" word-prop append ;
113 : multi-predicate ( classes -- quot )
114 dup length <reversed>
115 [ picker 2array ] 2map
116 [ drop object eq? not ] assoc-filter
118 [ (multi-predicate) ] { } assoc>map
119 unclip [ swap [ f ] \ if 3array append [ ] like ] reduce
122 : argument-count ( methods -- n )
123 keys 0 [ length max ] reduce ;
125 ERROR: no-method arguments generic ;
127 : make-default-method ( methods generic -- quot )
128 [ argument-count ] dip [ [ narray ] dip no-method ] 2curry ;
130 : multi-dispatch-quot ( methods generic -- quot )
131 [ make-default-method ]
132 [ drop [ [ multi-predicate ] dip ] assoc-map reverse ]
136 PREDICATE: generic < word
137 "multi-methods" word-prop >boolean ;
139 : methods ( word -- alist )
140 "multi-methods" word-prop >alist ;
142 : make-generic ( generic -- quot )
144 [ methods prepare-methods % sort-methods ] keep
145 multi-dispatch-quot %
148 : update-generic ( word -- )
149 dup make-generic define ;
152 PREDICATE: method-body < word
153 "multi-method-generic" word-prop >boolean ;
155 M: method-body stack-effect
156 "multi-method-generic" word-prop stack-effect ;
158 M: method-body crossref?
159 "forgotten" word-prop not ;
161 : method-word-name ( specializer generic -- string )
162 [ name>> % "-" % unparse % ] "" make ;
164 : method-word-props ( specializer generic -- assoc )
166 "multi-method-generic" set
167 "multi-method-specializer" set
170 : <method> ( specializer generic -- word )
171 [ method-word-props ] 2keep
172 method-word-name f <word>
175 : with-methods ( word quot -- )
177 [ "multi-methods" word-prop ] dip call
178 ] dip update-generic ; inline
180 : reveal-method ( method classes generic -- )
181 [ set-at ] with-methods ;
183 : method ( classes word -- method )
184 "multi-methods" word-prop at ;
186 : create-method ( classes generic -- method )
190 drop [ <method> dup ] 2keep reveal-method
193 : niceify-method ( seq -- seq )
194 [ dup \ f eq? [ drop f ] when ] map ;
197 "Type check error" print
199 "Generic word " write dup generic>> pprint
200 " does not have a method applicable to inputs:" print
201 dup arguments>> short.
203 "Inputs have signature:" print
204 dup arguments>> [ class ] map niceify-method .
206 "Available methods: " print
207 generic>> methods canonicalize-specializers drop sort-methods
208 keys [ niceify-method ] map stack. ;
210 : forget-method ( specializer generic -- )
211 [ delete-at ] with-methods ;
213 : method>spec ( method -- spec )
214 [ "multi-method-specializer" word-prop ]
215 [ "multi-method-generic" word-prop ] bi prefix ;
217 : define-generic ( word -- )
218 dup "multi-methods" word-prop [
221 [ H{ } clone "multi-methods" set-word-prop ]
228 CREATE define-generic ; parsing
230 : parse-method ( -- quot classes generic )
231 parse-definition [ 2 tail ] [ second ] [ first ] tri ;
233 : create-method-in ( specializer generic -- method )
234 create-method dup save-location f set-word ;
236 : CREATE-METHOD ( -- method )
237 scan-word scan-object swap create-method-in ;
239 : (METHOD:) ( -- method def ) CREATE-METHOD parse-definition ;
241 : METHOD: (METHOD:) define ; parsing
245 scan-word 1array scan-word create-method-in
249 ! Definition protocol. We qualify core generics here
252 syntax:M: generic definer drop \ GENERIC: f ;
254 syntax:M: generic definition drop f ;
256 PREDICATE: method-spec < array
257 unclip generic? [ [ class? ] all? ] dip and ;
259 syntax:M: method-spec where
260 dup unclip method [ ] [ first ] ?if where ;
262 syntax:M: method-spec set-where
263 unclip method set-where ;
265 syntax:M: method-spec definer
266 unclip method definer ;
268 syntax:M: method-spec definition
269 unclip method definition ;
271 syntax:M: method-spec synopsis*
272 unclip method synopsis* ;
274 syntax:M: method-spec forget*
275 unclip method forget* ;
277 syntax:M: method-body definer
280 syntax:M: method-body synopsis*
282 [ "multi-method-generic" word-prop pprint-word ]
283 [ "multi-method-specializer" word-prop pprint* ] bi ;