-
-
Notifications
You must be signed in to change notification settings - Fork 372
/
RoleHOW.pir
367 lines (268 loc) Β· 7.87 KB
/
RoleHOW.pir
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
## $Id$
=head1 TITLE
RoleHOW - default metaclass for Perl 6 roles
=head1 DESCRIPTION
This is the metaclass for roles.
We use a P6role as our backing store. However, we keep a list of parents
separately - we simply pass these on to the class as an "implementation
detail". We also don't want Parrot's immediate-composition semantics, so
we also have an attribute collecting roles to flatten and compose later
on.
=cut
.namespace ['RoleHOW']
.sub 'onload' :anon :init :load
.local pmc p6meta, rolehowproto
p6meta = get_hll_global ['Mu'], '$!P6META'
rolehowproto = p6meta.'new_class'('RoleHOW', 'parent'=>'Mu', 'attr'=>'parrotclass shortname longname protoobject $!parents $!composees $!requirements $!collisions $!attributes $!done')
.end
=item new()
Creates a new instance of the meta-class.
=cut
.sub 'new' :method
.param pmc name :optional
.local pmc how, p6role
# Create P6role object, which is what we will install in the namespace.
p6role = new ['P6role']
# Stash in metaclass instance, init a couple of other fields,
# and associate it with the P6role object, then hand that back.
have_p6role:
how = new ['RoleHOW']
setattribute how, 'parrotclass', p6role
$P0 = new ['ResizablePMCArray']
setattribute how, '$!parents', $P0
$P0 = new ['ResizablePMCArray']
setattribute how, '$!attributes', $P0
$P0 = new ['ResizablePMCArray']
setattribute how, '$!composees', $P0
$P0 = new ['ResizablePMCArray']
setattribute how, '$!requirements', $P0
$P0 = new ['ResizablePMCArray']
setattribute how, '$!collisions', $P0
setprop p6role, 'metaclass', how
setattribute how, 'protoobject', p6role
.return (p6role)
.end
=item add_parent
Stores the parent; we'll add it to a class at compose time.
=cut
.sub 'add_parent' :method
.param pmc role
.param pmc parent
$P0 = getattribute self, '$!parents'
push $P0, parent
.end
=item add_requirement
Adds the name of a required method to the requirements list for the role.
=cut
.sub 'add_requirement' :method
.param pmc role
.param pmc requirement
$P0 = getattribute self, '$!requirements'
push $P0, requirement
.end
=item add_collision
Adds the name of a colliding method that needs the class or a role to resolve
it to the collisions list for the role.
=cut
.sub 'add_collision' :method
.param pmc role
.param pmc collision
$P0 = getattribute self, '$!collisions'
push $P0, collision
.end
=item add_attribute
Adds an attribute to the role.
=cut
.sub 'add_attribute' :method
.param pmc role
.param pmc attribute
$P0 = getattribute self, '$!attributes'
push $P0, attribute
.end
=item add_composable
Stores something that we will compose (e.g. a role) at class composition time.
=cut
.sub 'add_composable' :method
.param pmc role
.param pmc composee
$P0 = getattribute self, '$!composees'
push $P0, composee
.end
=item add_meta_method(meta, name, code_ref)
Add a metamethod to the given meta.
=cut
.sub 'add_meta_method' :method
.param pmc role
.param string name
.param pmc meth
'&die'("Adding meta-methods to roles is not yet implemented.")
.end
=item add_method(meta, name, code_ref)
Add a method to the given meta.
=cut
.sub 'add_method' :method
.param pmc role
.param string name
.param pmc meth
$P0 = getattribute self, 'parrotclass'
push_eh add_fail
addmethod $P0, name, meth
pop_eh
.return ()
add_fail:
pop_eh
# May be that we need to merge multis.
$P1 = $P0.'methods'()
$P1 = $P1[name]
$I0 = isa $P1, 'MultiSub'
unless $I0 goto error
$I0 = isa meth, 'MultiSub'
unless $I0 goto error
$P1.'incorporate_candidates'(meth)
.return ()
error:
'&die'('Can not add two methods to a role if they are not multis')
.end
=item methods
Gets the list of methods that this role does.
=cut
.sub 'methods' :method
.param pmc role
.local pmc result, it, p6role
result = root_new ['parrot';'ResizablePMCArray']
p6role = getattribute self, 'parrotclass'
$P0 = inspect p6role, 'methods'
it = iter $P0
it_loop:
unless it goto it_loop_end
$S0 = shift it
$P1 = $P0[$S0]
push result, $P1
goto it_loop
it_loop_end:
.return (result)
.end
=item parents
Gets the parents list for this role (e.g. the parents we are passing along for
later being added to the class).
=cut
.sub 'parents' :method
.param pmc role
$P0 = getattribute self, '$!parents'
.return ($P0)
.end
=item requirements
Accessor for list of method names a role requires.
=cut
.sub 'requirements' :method
.param pmc role
$P0 = getattribute self, '$!requirements'
.return ($P0)
.end
=item collisions
Accessor for list of method names in conflict; the class must resolve them.
=cut
.sub 'collisions' :method
.param pmc role
$P0 = getattribute self, '$!collisions'
.return ($P0)
.end
=item attributes
Accessor for list of attributes in the role.
=cut
.sub 'attributes' :method
.param pmc role
$P0 = getattribute self, '$!attributes'
.return ($P0)
.end
=item composees
Returns all of the composees that this role has. With the :trasitive flag
it represents all of those that have been composed in from other roles too.
XXX This is non-spec ATM.
=cut
.sub 'composees' :method
.param pmc role
.param pmc transitive :named('transitive') :optional
if null transitive goto intransitive
unless transitive goto intransitive
$P0 = getattribute self, '$!done'
.return ($P0)
intransitive:
$P0 = getattribute self, '$!composees'
.return ($P0)
.end
=item applier_for
For now, we can't use a class as a composable thing. In the future we can
instead extract a role from the class (or rather, hand back a composer that
knows how to do that).
=cut
.sub 'applier_for' :method
.param pmc role
.param pmc for
$I0 = isa for, 'ClassHOW'
if $I0 goto class_applier
$I0 = isa for, 'RoleHOW'
if $I0 goto role_applier
goto instance_applier
class_applier:
$P0 = get_hll_global ['Perl6';'Metamodel'], 'RoleToClassApplier'
.return ($P0)
role_applier:
$P0 = get_hll_global ['Perl6';'Metamodel'], 'RoleToRoleApplier'
.return ($P0)
instance_applier:
$P0 = get_hll_global ['Perl6';'Metamodel'], 'RoleToInstanceApplier'
.return ($P0)
.end
=item compose(meta)
Completes the creation of the metaclass and return the P6role.
=cut
.sub 'compose' :method
.param pmc role
.local pmc p6role
p6role = getattribute self, 'parrotclass'
# See if we have anything to compose. Also, make sure our composees
# all want the same composer.
.local pmc composees, chosen_applier, composee_it, done
composees = getattribute self, '$!composees'
$I0 = elements composees
if $I0 == 0 goto composition_done
if $I0 == 1 goto one_composee
composee_it = iter composees
composee_it_loop:
unless composee_it goto apply_composees
$P0 = shift composee_it
if null chosen_applier goto first_composee
$P1 = $P0.'HOW'()
$P1 = $P1.'applier_for'($P0, self)
$P2 = chosen_applier.'WHAT'()
$P3 = $P1.'WHAT'()
$I0 = '&infix:<===>'($P2, $P3)
if $I0 goto composee_it_loop
die 'Can not compose multiple composees that want different appliers'
first_composee:
$P1 = $P0.'HOW'()
chosen_applier = $P1.'applier_for'($P0, self)
goto composee_it_loop
one_composee:
$P0 = composees[0]
$P1 = $P0.'HOW'()
chosen_applier = $P1.'applier_for'($P0, self)
apply_composees:
done = chosen_applier.'apply'(role, composees)
composition_done:
unless null done goto done_done
done = root_new ['parrot';'ResizablePMCArray']
done_done:
done.'unshift'(p6role)
setattribute self, '$!done', done
# Associate the metaclass with the p6role.
.return (role)
.end
=back
=cut
# Local Variables:
# mode: pir
# fill-column: 100
# End:
# vim: expandtab shiftwidth=4 ft=pir: