]> gitweb.factorcode.org Git - factor.git/blob - core/generic/single/single.factor
bc255e72ee076952c1b76e90cdec0ccf24ce98c1
[factor.git] / core / generic / single / single.factor
1 ! Copyright (C) 2009, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays assocs classes classes.algebra
4 combinators combinators.private definitions effects generic
5 hashtables kernel layouts make math namespaces quotations
6 sequences words ;
7 IN: generic.single
8
9 <PRIVATE
10 PRIMITIVE: inline-cache-miss ( generic methods index cache -- )
11 PRIMITIVE: inline-cache-miss-tail ( generic methods index cache -- )
12 PRIMITIVE: lookup-method ( object methods -- method )
13 PRIMITIVE: mega-cache-lookup ( methods index cache -- )
14 PRIMITIVE: mega-cache-miss ( methods index cache -- method )
15 PRIVATE>
16
17 ERROR: no-method object generic ;
18
19 ERROR: inconsistent-next-method class generic ;
20
21 TUPLE: single-combination ;
22
23 PREDICATE: single-generic < generic
24     "combination" word-prop single-combination? ;
25
26 M: single-generic make-inline cannot-be-inline ;
27
28 GENERIC: dispatch# ( word -- n )
29
30 M: generic dispatch# "combination" word-prop dispatch# ;
31
32 SYMBOL: assumed
33 SYMBOL: default
34 SYMBOL: generic-word
35 SYMBOL: combination
36
37 : with-combination ( combination quot -- )
38     [ combination ] dip with-variable ; inline
39
40 HOOK: picker combination ( -- quot )
41
42 M: single-combination next-method-quot* ( class generic combination -- quot )
43     [
44         2dup next-method dup [
45             [
46                 pick predicate-def %
47                 1quotation ,
48                 [ inconsistent-next-method ] 2curry ,
49                 \ if ,
50             ] [ ] make picker prepend
51         ] [ 3drop f ] if
52     ] with-combination ;
53
54 : method-for-object ( obj word -- method )
55     [
56         [ method-classes [ instance? ] with filter smallest-class ] keep
57         ?lookup-method
58     ] [ "default-method" word-prop ]
59     bi or ;
60
61 M: single-combination make-default-method
62     [ [ picker ] dip [ no-method ] curry append ] with-combination ;
63
64 ! ! ! Build an engine ! ! !
65
66 : find-default ( methods -- default )
67     ! Side-effects methods.
68     [ object bootstrap-word ] dip delete-at* [
69         drop generic-word get "default-method" word-prop
70     ] unless ;
71
72 ! 1. Flatten methods
73 TUPLE: predicate-engine class methods ;
74
75 C: <predicate-engine> predicate-engine
76
77 : push-method ( method class atomic assoc -- )
78     dupd [
79         [ ] [ H{ } clone <predicate-engine> ] ?if
80         [ methods>> set-at ] keep
81     ] change-at ;
82
83 : flatten-method ( method class assoc -- )
84     over flatten-class [ swap push-method ] 2with with each ;
85
86 : flatten-methods ( assoc -- assoc' )
87     H{ } clone [ [ swapd flatten-method ] curry assoc-each ] keep ;
88
89 ! 2. Convert methods
90 : split-methods ( assoc class -- first second )
91     [ [ nip class<= ] curry assoc-reject ]
92     [ [ nip class<=     ] curry assoc-filter ] 2bi ;
93
94 : convert-methods ( assoc class word -- assoc' )
95     over [ split-methods ] 2dip pick assoc-empty?
96     [ 3drop ] [ [ execute ] dip pick set-at ] if ; inline
97
98 ! 2.1 Convert tuple methods
99 TUPLE: echelon-dispatch-engine n methods ;
100
101 C: <echelon-dispatch-engine> echelon-dispatch-engine
102
103 TUPLE: tuple-dispatch-engine echelons ;
104
105 : push-echelon ( class method assoc -- )
106     [ swap dup "layout" word-prop third ] dip
107     [ ?set-at ] change-at ;
108
109 : echelon-sort ( assoc -- assoc' )
110     ! Convert an assoc mapping classes to methods into an
111     ! assoc mapping echelons to assocs. The first echelon
112     ! is always there
113     H{ { 0 f } } clone [ [ push-echelon ] curry assoc-each ] keep ;
114
115 : copy-superclass-methods ( engine superclass assoc -- )
116     at* [ [ methods>> ] bi@ assoc-union! drop ] [ 2drop ] if ;
117
118 : copy-superclasses-methods ( class engine assoc -- )
119     [ superclasses-of ] 2dip
120     [ swapd copy-superclass-methods ] 2curry each ;
121
122 : convert-tuple-inheritance ( assoc -- assoc' )
123     ! A method on a superclass A might have a higher precedence
124     ! than a method on a subclass B, if the methods are
125     ! defined on incomparable classes that happen to contain
126     ! A and B, respectively. Copy A's methods into B's set so
127     ! that they can be sorted and selected properly.
128     dup dup [ copy-superclasses-methods ] curry assoc-each ;
129
130 : <tuple-dispatch-engine> ( methods -- engine )
131     convert-tuple-inheritance echelon-sort
132     [ dupd <echelon-dispatch-engine> ] assoc-map
133     tuple-dispatch-engine boa ;
134
135 : convert-tuple-methods ( assoc -- assoc' )
136     tuple bootstrap-word
137     \ <tuple-dispatch-engine> convert-methods ;
138
139 ! 3 Tag methods
140 TUPLE: tag-dispatch-engine methods ;
141
142 C: <tag-dispatch-engine> tag-dispatch-engine
143
144 : <engine> ( assoc -- engine )
145     flatten-methods
146     convert-tuple-methods
147     <tag-dispatch-engine> ;
148
149 ! ! ! Compile engine ! ! !
150 GENERIC: compile-engine ( engine -- obj )
151
152 : compile-engines ( assoc -- assoc' )
153     [ compile-engine ] assoc-map ;
154
155 : compile-engines* ( assoc -- assoc' )
156     [ over assumed [ compile-engine ] with-variable ] assoc-map ;
157
158 : direct-dispatch-table ( assoc n -- table )
159     default get <array> <enumerated> swap assoc-union! seq>> ;
160
161 : tag-number ( class -- n ) "type" word-prop ;
162
163 M: tag-dispatch-engine compile-engine
164     methods>> compile-engines*
165     [ [ tag-number ] dip ] assoc-map
166     num-types get direct-dispatch-table ;
167
168 : build-fast-hash ( methods -- buckets )
169     >alist V{ } clone [ hashcode 1array ] distribute-buckets
170     [ compile-engines* >alist concat ] map ;
171
172 M: echelon-dispatch-engine compile-engine
173     dup n>> 0 = [
174         methods>> dup assoc-size {
175             { 0 [ drop default get ] }
176             { 1 [ >alist first second compile-engine ] }
177         } case
178     ] [
179         methods>> compile-engines* build-fast-hash
180     ] if ;
181
182 M: tuple-dispatch-engine compile-engine
183     tuple assumed [
184         echelons>> compile-engines
185         dup keys supremum 1 + f <array>
186         <enumerated> swap assoc-union! seq>>
187     ] with-variable ;
188
189 PREDICATE: predicate-engine-word < word "owner-generic" word-prop ;
190
191 SYMBOL: predicate-engines
192
193 : sort-methods ( assoc -- assoc' )
194     >alist [ keys sort-classes ] keep extract-keys ;
195
196 : quote-methods ( assoc -- assoc' )
197     [ 1quotation \ drop prefix ] assoc-map ;
198
199 : find-predicate-engine ( classes -- word )
200     predicate-engines get [ at ] curry map-find drop ;
201
202 : next-predicate-engine ( engine -- word )
203     class>> superclasses-of
204     find-predicate-engine
205     default get or ;
206
207 : methods-with-default ( engine -- assoc )
208     [ methods>> clone ] [ next-predicate-engine ] bi
209     object bootstrap-word pick set-at ;
210
211 : keep-going? ( assoc -- ? )
212     assumed get swap second first class<= ;
213
214 ERROR: unreachable ;
215
216 : prune-redundant-predicates ( assoc -- default assoc' )
217     {
218         { [ dup empty? ] [ drop [ unreachable ] { } ] }
219         { [ dup length 1 = ] [ first second { } ] }
220         { [ dup keep-going? ] [ rest-slice prune-redundant-predicates ] }
221         [ [ first second ] [ rest-slice ] bi ]
222     } cond ;
223
224 : class-predicates ( assoc -- assoc )
225     [ [ predicate-def [ dup ] prepend ] dip ] assoc-map ;
226
227 : <predicate-engine-word> ( -- word )
228     generic-word get name>> "/predicate-engine" append f <word>
229     dup generic-word get "owner-generic" set-word-prop ;
230
231 M: predicate-engine-word stack-effect "owner-generic" word-prop stack-effect ;
232
233 : define-predicate-engine ( alist -- word )
234     [ <predicate-engine-word> ] dip
235     [ define ] [ drop generic-word get "engines" word-prop push ] [ drop ] 2tri ;
236
237 : compile-predicate-engine ( engine -- word )
238     methods-with-default
239     sort-methods
240     quote-methods
241     prune-redundant-predicates
242     class-predicates
243     [ last ] [ alist>quot picker prepend define-predicate-engine ] if-empty ;
244
245 M: predicate-engine compile-engine
246     [ compile-predicate-engine ] [ class>> ] bi
247     [ drop ] [ predicate-engines get set-at ] 2bi ;
248
249 M: word compile-engine ;
250
251 M: f compile-engine ;
252
253 : build-decision-tree ( generic -- methods )
254     [ "engines" word-prop forget-all ]
255     [ V{ } clone "engines" set-word-prop ]
256     [
257         "methods" word-prop clone
258         [ find-default default set ]
259         [ <engine> compile-engine ] bi
260     ] tri ;
261
262 HOOK: inline-cache-quots combination ( word methods -- pic-quot/f pic-tail-quot/f )
263
264 M: single-combination inline-cache-quots 2drop f f ;
265
266 : define-inline-cache-quot ( word methods -- )
267     [ drop ] [ inline-cache-quots ] 2bi
268     [ >>pic-def ] [ >>pic-tail-def ] bi*
269     drop ;
270
271 HOOK: mega-cache-quot combination ( methods -- quot/f )
272
273 M: single-combination perform-combination
274     [
275         H{ } clone predicate-engines set
276         dup generic-word set
277         dup build-decision-tree
278         [ "decision-tree" set-word-prop ]
279         [ mega-cache-quot define ]
280         [ define-inline-cache-quot ]
281         2tri
282     ] with-combination ;