2 Copyright (C) 2003-2009, Parrot Foundation.
7 src/multidispatch.c - Multimethod dispatch for binary opcode functions
11 This system is set up to handle type-based dispatching for binary (two
12 argument) functions. This includes, though isn't necessarily limited to, binary
13 operators such as addition or subtraction.
17 The MMD system is straightforward, and currently must be explicitly invoked,
18 for example by a vtable function. (We reserve the right to use MMD in all
19 circumstances, but currently do not).
23 For the purposes of the API, each MMD-able function is assigned a unique
24 number which is used to find the correct function table. This is the
25 C<func_num> parameter in the following functions. While Parrot isn't
26 restricted to a predefined set of functions, it I<does> set things up so
27 that all the binary vtable functions have a MMD table preinstalled for
28 them, with default behaviour.
40 #include "parrot/compiler.h"
41 #include "parrot/parrot.h"
42 #include "parrot/multidispatch.h"
43 #include "parrot/oplib/ops.h"
44 #include "multidispatch.str"
45 #include "pmc/pmc_nci.h"
46 #include "pmc/pmc_sub.h"
47 #include "pmc/pmc_context.h"
49 /* HEADERIZER HFILE: include/parrot/multidispatch.h */
51 /* HEADERIZER BEGIN: static */
52 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
54 static INTVAL
distance_cmp(SHIM_INTERP
, INTVAL a
, INTVAL b
);
55 static void mmd_add_multi_global(PARROT_INTERP
,
56 ARGIN(STRING
*sub_name
),
58 __attribute__nonnull__(1)
59 __attribute__nonnull__(2)
60 __attribute__nonnull__(3);
62 static void mmd_add_multi_to_namespace(PARROT_INTERP
,
63 ARGIN(STRING
*ns_name
),
64 ARGIN(STRING
*sub_name
),
66 __attribute__nonnull__(1)
67 __attribute__nonnull__(2)
68 __attribute__nonnull__(3)
69 __attribute__nonnull__(4);
71 PARROT_CANNOT_RETURN_NULL
72 PARROT_WARN_UNUSED_RESULT
73 static PMC
* mmd_build_type_tuple_from_long_sig(PARROT_INTERP
,
74 ARGIN(STRING
*long_sig
))
75 __attribute__nonnull__(1)
76 __attribute__nonnull__(2);
78 PARROT_CANNOT_RETURN_NULL
79 PARROT_WARN_UNUSED_RESULT
80 static PMC
* mmd_build_type_tuple_from_type_list(PARROT_INTERP
,
81 ARGIN(PMC
*type_list
))
82 __attribute__nonnull__(1)
83 __attribute__nonnull__(2);
85 PARROT_WARN_UNUSED_RESULT
86 PARROT_CAN_RETURN_NULL
87 static STRING
* mmd_cache_key_from_types(PARROT_INTERP
,
88 ARGIN(const char *name
),
90 __attribute__nonnull__(1)
91 __attribute__nonnull__(2)
92 __attribute__nonnull__(3);
94 PARROT_WARN_UNUSED_RESULT
95 PARROT_CAN_RETURN_NULL
96 static STRING
* mmd_cache_key_from_values(PARROT_INTERP
,
97 ARGIN(const char *name
),
99 __attribute__nonnull__(1)
100 __attribute__nonnull__(2)
101 __attribute__nonnull__(3);
103 PARROT_WARN_UNUSED_RESULT
104 PARROT_CAN_RETURN_NULL
105 static PMC
* mmd_cvt_to_types(PARROT_INTERP
, ARGIN(PMC
*multi_sig
))
106 __attribute__nonnull__(1)
107 __attribute__nonnull__(2);
109 static UINTVAL
mmd_distance(PARROT_INTERP
,
111 ARGIN(PMC
*arg_tuple
))
112 __attribute__nonnull__(1)
113 __attribute__nonnull__(2)
114 __attribute__nonnull__(3);
116 static void mmd_search_by_sig_obj(PARROT_INTERP
,
119 ARGIN(PMC
*candidates
))
120 __attribute__nonnull__(1)
121 __attribute__nonnull__(2)
122 __attribute__nonnull__(3)
123 __attribute__nonnull__(4);
125 static void mmd_search_global(PARROT_INTERP
,
128 __attribute__nonnull__(1)
129 __attribute__nonnull__(2)
130 __attribute__nonnull__(3);
132 static int mmd_search_local(PARROT_INTERP
,
134 ARGIN(PMC
*candidates
))
135 __attribute__nonnull__(1)
136 __attribute__nonnull__(2)
137 __attribute__nonnull__(3);
139 PARROT_WARN_UNUSED_RESULT
140 PARROT_CAN_RETURN_NULL
141 static PMC
* Parrot_mmd_get_cached_multi_sig(PARROT_INTERP
,
143 __attribute__nonnull__(1)
144 __attribute__nonnull__(2);
146 static int Parrot_mmd_maybe_candidate(PARROT_INTERP
,
149 __attribute__nonnull__(1)
150 __attribute__nonnull__(2)
151 __attribute__nonnull__(3);
153 PARROT_CANNOT_RETURN_NULL
154 PARROT_WARN_UNUSED_RESULT
155 static PMC
* Parrot_mmd_search_scopes(PARROT_INTERP
, ARGIN(STRING
*meth
))
156 __attribute__nonnull__(1)
157 __attribute__nonnull__(2);
159 PARROT_CANNOT_RETURN_NULL
160 static PMC
* Parrot_mmd_sort_candidates(PARROT_INTERP
,
161 ARGIN(PMC
*arg_tuple
),
163 __attribute__nonnull__(1)
164 __attribute__nonnull__(2)
165 __attribute__nonnull__(3);
167 #define ASSERT_ARGS_distance_cmp __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
168 #define ASSERT_ARGS_mmd_add_multi_global __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
169 PARROT_ASSERT_ARG(interp) \
170 , PARROT_ASSERT_ARG(sub_name) \
171 , PARROT_ASSERT_ARG(sub_obj))
172 #define ASSERT_ARGS_mmd_add_multi_to_namespace __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
173 PARROT_ASSERT_ARG(interp) \
174 , PARROT_ASSERT_ARG(ns_name) \
175 , PARROT_ASSERT_ARG(sub_name) \
176 , PARROT_ASSERT_ARG(sub_obj))
177 #define ASSERT_ARGS_mmd_build_type_tuple_from_long_sig \
178 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
179 PARROT_ASSERT_ARG(interp) \
180 , PARROT_ASSERT_ARG(long_sig))
181 #define ASSERT_ARGS_mmd_build_type_tuple_from_type_list \
182 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
183 PARROT_ASSERT_ARG(interp) \
184 , PARROT_ASSERT_ARG(type_list))
185 #define ASSERT_ARGS_mmd_cache_key_from_types __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
186 PARROT_ASSERT_ARG(interp) \
187 , PARROT_ASSERT_ARG(name) \
188 , PARROT_ASSERT_ARG(types))
189 #define ASSERT_ARGS_mmd_cache_key_from_values __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
190 PARROT_ASSERT_ARG(interp) \
191 , PARROT_ASSERT_ARG(name) \
192 , PARROT_ASSERT_ARG(values))
193 #define ASSERT_ARGS_mmd_cvt_to_types __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
194 PARROT_ASSERT_ARG(interp) \
195 , PARROT_ASSERT_ARG(multi_sig))
196 #define ASSERT_ARGS_mmd_distance __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
197 PARROT_ASSERT_ARG(interp) \
198 , PARROT_ASSERT_ARG(pmc) \
199 , PARROT_ASSERT_ARG(arg_tuple))
200 #define ASSERT_ARGS_mmd_search_by_sig_obj __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
201 PARROT_ASSERT_ARG(interp) \
202 , PARROT_ASSERT_ARG(name) \
203 , PARROT_ASSERT_ARG(sig_obj) \
204 , PARROT_ASSERT_ARG(candidates))
205 #define ASSERT_ARGS_mmd_search_global __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
206 PARROT_ASSERT_ARG(interp) \
207 , PARROT_ASSERT_ARG(name) \
208 , PARROT_ASSERT_ARG(cl))
209 #define ASSERT_ARGS_mmd_search_local __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
210 PARROT_ASSERT_ARG(interp) \
211 , PARROT_ASSERT_ARG(name) \
212 , PARROT_ASSERT_ARG(candidates))
213 #define ASSERT_ARGS_Parrot_mmd_get_cached_multi_sig \
214 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
215 PARROT_ASSERT_ARG(interp) \
216 , PARROT_ASSERT_ARG(sub_pmc))
217 #define ASSERT_ARGS_Parrot_mmd_maybe_candidate __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
218 PARROT_ASSERT_ARG(interp) \
219 , PARROT_ASSERT_ARG(pmc) \
220 , PARROT_ASSERT_ARG(cl))
221 #define ASSERT_ARGS_Parrot_mmd_search_scopes __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
222 PARROT_ASSERT_ARG(interp) \
223 , PARROT_ASSERT_ARG(meth))
224 #define ASSERT_ARGS_Parrot_mmd_sort_candidates __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
225 PARROT_ASSERT_ARG(interp) \
226 , PARROT_ASSERT_ARG(arg_tuple) \
227 , PARROT_ASSERT_ARG(cl))
228 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
229 /* HEADERIZER END: static */
237 =item C<PMC* Parrot_mmd_find_multi_from_sig_obj(PARROT_INTERP, STRING *name, PMC
240 Collect a list of possible candidates for a given sub name and call signature.
241 Rank the possible candidates by Manhattan Distance, and return the best
242 matching candidate. The candidate list is cached in the CallSignature object,
243 to allow for iterating through it.
245 Currently this only looks in the global "MULTI" namespace.
252 PARROT_WARN_UNUSED_RESULT
253 PARROT_CANNOT_RETURN_NULL
255 Parrot_mmd_find_multi_from_sig_obj(PARROT_INTERP
, ARGIN(STRING
*name
), ARGIN(PMC
*invoke_sig
))
257 ASSERT_ARGS(Parrot_mmd_find_multi_from_sig_obj
)
258 PMC
* const candidate_list
= pmc_new(interp
, enum_class_ResizablePMCArray
);
260 mmd_search_by_sig_obj(interp
, name
, invoke_sig
, candidate_list
);
261 mmd_search_global(interp
, name
, candidate_list
);
263 return Parrot_mmd_sort_manhattan_by_sig_pmc(interp
, candidate_list
, invoke_sig
);
268 =item C<void Parrot_mmd_multi_dispatch_from_c_args(PARROT_INTERP, const char
269 *name, const char *sig, ...)>
271 Dispatches to a MultiSub from a variable-sized list of C arguments. The
272 multiple dispatch system will figure out which sub should be called based on
273 the types of the arguments passed in.
275 Return arguments must be passed as a reference to the PMC, string, number, or
276 integer, so the result can be set.
283 PARROT_CAN_RETURN_NULL
285 Parrot_mmd_multi_dispatch_from_c_args(PARROT_INTERP
,
286 ARGIN(const char *name
), ARGIN(const char *sig
), ...)
288 ASSERT_ARGS(Parrot_mmd_multi_dispatch_from_c_args
)
289 PMC
*sig_object
, *sub
;
293 sig_object
= Parrot_pcc_build_sig_object_from_varargs(interp
, PMCNULL
, sig
, args
);
296 /* Check the cache. */
297 sub
= Parrot_mmd_cache_lookup_by_types(interp
, interp
->op_mmd_cache
, name
,
298 VTABLE_get_pmc(interp
, sig_object
));
300 if (PMC_IS_NULL(sub
)) {
301 sub
= Parrot_mmd_find_multi_from_sig_obj(interp
,
302 Parrot_str_new_constant(interp
, name
), sig_object
);
304 if (!PMC_IS_NULL(sub
))
305 Parrot_mmd_cache_store_by_types(interp
, interp
->op_mmd_cache
, name
,
306 VTABLE_get_pmc(interp
, sig_object
), sub
);
309 if (PMC_IS_NULL(sub
))
310 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_METHOD_NOT_FOUND
,
311 "Multiple Dispatch: No suitable candidate found for '%s',"
312 " with signature '%s'", name
, sig
);
315 Parrot_io_eprintf(interp
, "candidate found for '%s', with signature '%s'\n",
317 Parrot_io_eprintf(interp
, "type of candidate found: %Ss\n",
318 VTABLE_name(interp
, sub
));
321 Parrot_pcc_invoke_from_sig_object(interp
, sub
, sig_object
);
327 =item C<PMC * Parrot_mmd_find_multi_from_long_sig(PARROT_INTERP, STRING *name,
330 Find the best candidate multi for a given sub name and signature. The signature
331 is a string containing a comma-delimited list of type names.
333 Currently only searches the global MULTI namespace.
340 PARROT_CAN_RETURN_NULL
341 PARROT_WARN_UNUSED_RESULT
343 Parrot_mmd_find_multi_from_long_sig(PARROT_INTERP
, ARGIN(STRING
*name
),
344 ARGIN(STRING
*long_sig
))
346 ASSERT_ARGS(Parrot_mmd_find_multi_from_long_sig
)
347 STRING
* const multi_str
= CONST_STRING(interp
, "MULTI");
348 PMC
* const ns
= Parrot_make_namespace_keyed_str(interp
,
349 interp
->root_namespace
, multi_str
);
350 PMC
* const multi_sub
= Parrot_get_global(interp
, ns
, name
);
352 if (PMC_IS_NULL(multi_sub
)) {
356 PMC
* const type_tuple
= mmd_build_type_tuple_from_long_sig(interp
, long_sig
);
357 return Parrot_mmd_sort_candidates(interp
, type_tuple
, multi_sub
);
364 =item C<PMC * Parrot_mmd_sort_manhattan_by_sig_pmc(PARROT_INTERP, PMC
365 *candidates, PMC *invoke_sig)>
367 Given an array PMC (usually a MultiSub) and a CallSignature PMC, sorts the mmd
368 candidates by their manhattan distance to the signature args and returns the
376 PARROT_CAN_RETURN_NULL
377 PARROT_WARN_UNUSED_RESULT
379 Parrot_mmd_sort_manhattan_by_sig_pmc(PARROT_INTERP
, ARGIN(PMC
*candidates
),
380 ARGIN(PMC
*invoke_sig
))
382 ASSERT_ARGS(Parrot_mmd_sort_manhattan_by_sig_pmc
)
383 const INTVAL n
= VTABLE_elements(interp
, candidates
);
388 return Parrot_mmd_sort_candidates(interp
,
389 VTABLE_get_pmc(interp
, invoke_sig
), candidates
);
396 =item C<static INTVAL distance_cmp(PARROT_INTERP, INTVAL a, INTVAL b)>
398 Compare distance values C<a> and C<b>. Return 1 if C<a> is larger, -1 if
406 distance_cmp(SHIM_INTERP
, INTVAL a
, INTVAL b
)
408 ASSERT_ARGS(distance_cmp
)
409 short da
= (short)(a
& 0xffff);
410 short db
= (short)(b
& 0xffff);
412 /* sort first by distance */
419 /* end then by index in candidate list */
420 da
= (short)(a
>> 16);
421 db
= (short)(b
>> 16);
423 return da
> db
? 1 : da
< db
? -1 : 0;
429 =item C<static PMC* mmd_build_type_tuple_from_type_list(PARROT_INTERP, PMC
432 Construct a FixedIntegerArray of type numbers from an array of
433 type names. Used for multiple dispatch.
439 PARROT_CANNOT_RETURN_NULL
440 PARROT_WARN_UNUSED_RESULT
442 mmd_build_type_tuple_from_type_list(PARROT_INTERP
, ARGIN(PMC
*type_list
))
444 ASSERT_ARGS(mmd_build_type_tuple_from_type_list
)
445 PMC
*multi_sig
= constant_pmc_new(interp
, enum_class_FixedIntegerArray
);
446 INTVAL param_count
= VTABLE_elements(interp
, type_list
);
449 VTABLE_set_integer_native(interp
, multi_sig
, param_count
);
451 for (i
= 0; i
< param_count
; i
++) {
452 STRING
*type_name
= VTABLE_get_string_keyed_int(interp
, type_list
, i
);
455 if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "DEFAULT")))
456 type
= enum_type_PMC
;
457 else if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "STRING")))
458 type
= enum_type_STRING
;
459 else if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "INTVAL")))
460 type
= enum_type_INTVAL
;
461 else if (Parrot_str_equal(interp
, type_name
, CONST_STRING(interp
, "FLOATVAL")))
462 type
= enum_type_FLOATVAL
;
464 type
= pmc_type(interp
, type_name
);
466 VTABLE_set_integer_keyed_int(interp
, multi_sig
, i
, type
);
475 =item C<static PMC* mmd_build_type_tuple_from_long_sig(PARROT_INTERP, STRING
478 Construct a FixedIntegerArray of type numbers from a comma-delimited string of
479 type names. Used for multiple dispatch.
485 PARROT_CANNOT_RETURN_NULL
486 PARROT_WARN_UNUSED_RESULT
488 mmd_build_type_tuple_from_long_sig(PARROT_INTERP
, ARGIN(STRING
*long_sig
))
490 ASSERT_ARGS(mmd_build_type_tuple_from_long_sig
)
491 PMC
*type_list
= Parrot_str_split(interp
, CONST_STRING(interp
, ","), long_sig
);
493 return mmd_build_type_tuple_from_type_list(interp
, type_list
);
499 =item C<PMC* Parrot_mmd_build_type_tuple_from_sig_obj(PARROT_INTERP, PMC
502 Construct a FixedIntegerArray of type numbers from the arguments of a Call
503 Signature object. Used for multiple dispatch.
510 PARROT_CANNOT_RETURN_NULL
511 PARROT_WARN_UNUSED_RESULT
513 Parrot_mmd_build_type_tuple_from_sig_obj(PARROT_INTERP
, ARGIN(PMC
*sig_obj
))
515 ASSERT_ARGS(Parrot_mmd_build_type_tuple_from_sig_obj
)
516 PMC
* const type_tuple
= pmc_new(interp
, enum_class_ResizableIntegerArray
);
517 STRING
*string_sig
= VTABLE_get_string(interp
, sig_obj
);
518 INTVAL tuple_size
= 0;
519 INTVAL args_ended
= 0;
520 INTVAL i
, seen_invocant
= 0;
523 if (STRING_IS_NULL(string_sig
)) {
524 Parrot_ex_throw_from_c_args(interp
, NULL
, 1,
525 "Call has no signature, unable to dispatch.\n");
528 sig_len
= Parrot_str_byte_length(interp
, string_sig
);
530 for (i
= 0; i
< sig_len
; ++i
) {
531 INTVAL type
= Parrot_str_indexed(interp
, string_sig
, i
+ seen_invocant
);
535 /* Regular arguments just set the value */
538 VTABLE_set_integer_keyed_int(interp
, type_tuple
,
539 i
, enum_type_INTVAL
);
542 VTABLE_set_integer_keyed_int(interp
, type_tuple
,
543 i
, enum_type_FLOATVAL
);
547 INTVAL type_lookahead
= Parrot_str_indexed(interp
, string_sig
, (i
+ 1));
548 if (type_lookahead
== 'n') {
552 VTABLE_set_integer_keyed_int(interp
, type_tuple
,
553 i
, enum_type_STRING
);
558 INTVAL type_lookahead
= Parrot_str_indexed(interp
, string_sig
, (i
+ 1));
559 if (type_lookahead
== 'i') {
561 Parrot_ex_throw_from_c_args(interp
, NULL
,
562 EXCEPTION_INVALID_OPERATION
,
563 "Multiple Dispatch: only the first argument can be an invocant");
566 else if (type_lookahead
== 'f') {
571 PMC
*pmc_arg
= VTABLE_get_pmc_keyed_int(interp
, sig_obj
, i
);
572 if (PMC_IS_NULL(pmc_arg
))
573 VTABLE_set_integer_keyed_int(interp
, type_tuple
,
576 VTABLE_set_integer_keyed_int(interp
, type_tuple
, i
,
577 VTABLE_type(interp
, pmc_arg
));
586 Parrot_ex_throw_from_c_args(interp
, NULL
,
587 EXCEPTION_INVALID_OPERATION
,
588 "Multiple Dispatch: invalid argument type %c!", type
);
598 =item C<static PMC* mmd_cvt_to_types(PARROT_INTERP, PMC *multi_sig)>
600 Given a ResizablePMCArray PMC containing some form of type identifier (either
601 the string name of a class or a PMC representing the type), resolves all type
602 references to type IDs, if possible. If that's not possible, returns PMCNULL.
603 In that case you can't dispatch to the multi variant with this type signature,
604 as Parrot doesn't yet know about the respective types requested -- you have to
607 Otherwise, returns a ResizableIntegerArray PMC full of type IDs representing
608 the signature of a multi variant to which you may be able to dispatch.
616 PARROT_WARN_UNUSED_RESULT
617 PARROT_CAN_RETURN_NULL
619 mmd_cvt_to_types(PARROT_INTERP
, ARGIN(PMC
*multi_sig
))
621 ASSERT_ARGS(mmd_cvt_to_types
)
623 const INTVAL n
= VTABLE_elements(interp
, multi_sig
);
626 for (i
= 0; i
< n
; ++i
) {
627 PMC
* const sig_elem
= VTABLE_get_pmc_keyed_int(interp
, multi_sig
, i
);
630 if (sig_elem
->vtable
->base_type
== enum_class_String
) {
631 STRING
* const sig
= VTABLE_get_string(interp
, sig_elem
);
636 type
= pmc_type(interp
, sig
);
638 if (type
== enum_type_undef
)
641 else if (sig_elem
->vtable
->base_type
== enum_class_Integer
) {
642 type
= VTABLE_get_integer(interp
, sig_elem
);
645 type
= pmc_type_p(interp
, sig_elem
);
647 /* create destination PMC only as necessary */
648 if (PMC_IS_NULL(ar
)) {
649 ar
= pmc_new(interp
, enum_class_FixedIntegerArray
);
650 VTABLE_set_integer_native(interp
, ar
, n
);
653 VTABLE_set_integer_keyed_int(interp
, ar
, i
, type
);
662 =item C<static PMC * Parrot_mmd_get_cached_multi_sig(PARROT_INTERP, PMC
665 Get the cached multisig of the given sub, if one exists. The cached signature
666 might be in different formats, so put it into a type tuple like the rest of the
673 PARROT_WARN_UNUSED_RESULT
674 PARROT_CAN_RETURN_NULL
676 Parrot_mmd_get_cached_multi_sig(PARROT_INTERP
, ARGIN(PMC
*sub_pmc
))
678 ASSERT_ARGS(Parrot_mmd_get_cached_multi_sig
)
679 if (VTABLE_isa(interp
, sub_pmc
, CONST_STRING(interp
, "Sub"))) {
680 Parrot_Sub_attributes
*sub
;
683 PMC_get_sub(interp
, sub_pmc
, sub
);
684 multi_sig
= sub
->multi_signature
;
686 if (multi_sig
->vtable
->base_type
== enum_class_FixedPMCArray
) {
687 PMC
*converted_sig
= mmd_cvt_to_types(interp
, multi_sig
);
689 if (PMC_IS_NULL(converted_sig
))
692 multi_sig
= sub
->multi_signature
= converted_sig
;
702 #define MMD_BIG_DISTANCE 0x7fff
706 =item C<static UINTVAL mmd_distance(PARROT_INTERP, PMC *pmc, PMC *arg_tuple)>
708 Create Manhattan Distance of sub C<pmc> against given argument types.
709 0xffff is the maximum distance
716 mmd_distance(PARROT_INTERP
, ARGIN(PMC
*pmc
), ARGIN(PMC
*arg_tuple
))
718 ASSERT_ARGS(mmd_distance
)
719 PMC
*multi_sig
, *mro
;
720 Parrot_Sub_attributes
*sub
;
721 INTVAL args
, dist
, i
, j
, n
, m
;
723 /* has to be a builtin multi method */
724 if (pmc
->vtable
->base_type
== enum_class_NCI
) {
725 GETATTR_NCI_multi_sig(interp
, pmc
, multi_sig
);
726 if (PMC_IS_NULL(multi_sig
)) {
729 GETATTR_NCI_long_signature(interp
, pmc
, long_sig
);
730 multi_sig
= mmd_build_type_tuple_from_long_sig(interp
, long_sig
);
731 SETATTR_NCI_multi_sig(interp
, pmc
, multi_sig
);
735 /* not a multi; no distance */
736 PMC_get_sub(interp
, pmc
, sub
);
737 if (!sub
->multi_signature
)
740 multi_sig
= Parrot_mmd_get_cached_multi_sig(interp
, pmc
);
743 if (PMC_IS_NULL(multi_sig
))
744 return MMD_BIG_DISTANCE
;
746 n
= VTABLE_elements(interp
, multi_sig
);
747 args
= VTABLE_elements(interp
, arg_tuple
);
750 * arg_tuple may have more arguments - only the
751 * n multi_sig invocants are counted
754 return MMD_BIG_DISTANCE
;
759 dist
= PARROT_MMD_MAX_CLASS_DEPTH
;
761 /* now go through args */
762 for (i
= 0; i
< n
; ++i
) {
763 const INTVAL type_sig
= VTABLE_get_integer_keyed_int(interp
, multi_sig
, i
);
764 const INTVAL type_call
= VTABLE_get_integer_keyed_int(interp
, arg_tuple
, i
);
765 if (type_sig
== type_call
)
768 /* promote primitives to their PMC equivalents, as PCC will autobox
769 * the distance penalty makes primitive variants look cheaper */
771 case enum_type_INTVAL
:
772 if (type_sig
== enum_class_Integer
) { dist
++; continue; }
774 case enum_type_FLOATVAL
:
775 if (type_sig
== enum_class_Float
) { dist
++; continue; }
777 case enum_type_STRING
:
778 if (type_sig
== enum_class_String
) { dist
++; continue; }
785 * different native types are very different, except a PMC
786 * which matches any PMC
788 if (type_call
<= 0 && type_sig
== enum_type_PMC
) {
793 if ((type_sig
<= 0 && type_sig
!= enum_type_PMC
) || type_call
<= 0) {
794 dist
= MMD_BIG_DISTANCE
;
799 * now consider MRO of types the signature type has to be somewhere
800 * in the MRO of the type_call
802 mro
= interp
->vtables
[type_call
]->mro
;
803 m
= VTABLE_elements(interp
, mro
);
805 for (j
= 0; j
< m
; ++j
) {
806 PMC
* const cl
= VTABLE_get_pmc_keyed_int(interp
, mro
, j
);
808 if (cl
->vtable
->base_type
== type_sig
)
810 if (VTABLE_type(interp
, cl
) == type_sig
)
817 * if the type wasn't in MRO check, if any PMC matches
818 * in that case use the distance + 1 (of an any PMC parent)
820 if (j
== m
&& type_sig
!= enum_type_PMC
) {
821 dist
= MMD_BIG_DISTANCE
;
831 s1
= Parrot_get_datatype_name(interp
, type_sig
);
833 s1
= interp
->vtables
[type_sig
]->whoami
;
836 s2
= Parrot_get_datatype_name(interp
, type_call
);
838 s2
= interp
->vtables
[type_call
]->whoami
;
840 Parrot_io_eprintf(interp
, "arg %d: dist %d sig %Ss arg %Ss\n",
852 =item C<static PMC * Parrot_mmd_sort_candidates(PARROT_INTERP, PMC *arg_tuple,
855 Sort the candidate list C<cl> by Manhattan Distance, returning the best
862 PARROT_CANNOT_RETURN_NULL
864 Parrot_mmd_sort_candidates(PARROT_INTERP
, ARGIN(PMC
*arg_tuple
), ARGIN(PMC
*cl
))
866 ASSERT_ARGS(Parrot_mmd_sort_candidates
)
867 PMC
*best_candidate
= PMCNULL
;
868 INTVAL best_distance
= MMD_BIG_DISTANCE
;
869 const INTVAL n
= VTABLE_elements(interp
, cl
);
872 for (i
= 0; i
< n
; ++i
) {
873 PMC
* const pmc
= VTABLE_get_pmc_keyed_int(interp
, cl
, i
);
874 const INTVAL d
= mmd_distance(interp
, pmc
, arg_tuple
);
875 if (d
< best_distance
) {
876 best_candidate
= pmc
;
881 return best_candidate
;
887 =item C<static PMC* Parrot_mmd_search_scopes(PARROT_INTERP, STRING *meth)>
889 Search all scopes for MMD candidates matching the arguments given in
896 PARROT_CANNOT_RETURN_NULL
897 PARROT_WARN_UNUSED_RESULT
899 Parrot_mmd_search_scopes(PARROT_INTERP
, ARGIN(STRING
*meth
))
901 ASSERT_ARGS(Parrot_mmd_search_scopes
)
902 PMC
* const candidates
= pmc_new(interp
, enum_class_ResizablePMCArray
);
903 const int stop
= mmd_search_local(interp
, meth
, candidates
);
906 mmd_search_global(interp
, meth
, candidates
);
914 =item C<static int Parrot_mmd_maybe_candidate(PARROT_INTERP, PMC *pmc, PMC *cl)>
916 If the candidate C<pmc> is a Sub PMC, push it on the candidate list and
917 return TRUE to stop further search.
919 If the candidate is a MultiSub remember all matching Subs and return FALSE
920 to continue searching outer scopes.
927 Parrot_mmd_maybe_candidate(PARROT_INTERP
, ARGIN(PMC
*pmc
), ARGIN(PMC
*cl
))
929 ASSERT_ARGS(Parrot_mmd_maybe_candidate
)
930 STRING
* const _sub
= CONST_STRING(interp
, "Sub");
931 STRING
* const _multi_sub
= CONST_STRING(interp
, "MultiSub");
935 if (VTABLE_isa(interp
, pmc
, _sub
)) {
936 /* a plain sub stops outer searches */
937 /* RT #45959 check arity of sub */
939 VTABLE_push_pmc(interp
, cl
, pmc
);
943 /* not a Sub or MultiSub - ignore */
944 if (!VTABLE_isa(interp
, pmc
, _multi_sub
))
947 /* ok we have a multi sub pmc, which is an array of candidates */
948 n
= VTABLE_elements(interp
, pmc
);
950 for (i
= 0; i
< n
; ++i
) {
951 PMC
* const multi_sub
= VTABLE_get_pmc_keyed_int(interp
, pmc
, i
);
952 VTABLE_push_pmc(interp
, cl
, multi_sub
);
961 =item C<static int mmd_search_local(PARROT_INTERP, STRING *name, PMC
964 Search the current package namespace for matching candidates. Return
965 TRUE if the MMD search should stop.
972 mmd_search_local(PARROT_INTERP
, ARGIN(STRING
*name
), ARGIN(PMC
*candidates
))
974 ASSERT_ARGS(mmd_search_local
)
975 PMC
* const multi_sub
= Parrot_find_global_cur(interp
, name
);
977 return multi_sub
&& Parrot_mmd_maybe_candidate(interp
, multi_sub
, candidates
);
983 =item C<static void mmd_search_by_sig_obj(PARROT_INTERP, STRING *name, PMC
984 *sig_obj, PMC *candidates)>
986 Search the namespace of the first argument to the sub call for matching
994 mmd_search_by_sig_obj(PARROT_INTERP
, ARGIN(STRING
*name
),
995 ARGIN(PMC
*sig_obj
), ARGIN(PMC
*candidates
))
997 ASSERT_ARGS(mmd_search_by_sig_obj
)
998 PMC
*first_arg
= VTABLE_get_pmc_keyed_int(interp
, sig_obj
, 0);
1001 if (PMC_IS_NULL(first_arg
))
1004 ns
= VTABLE_get_namespace(interp
, first_arg
);
1006 if (PMC_IS_NULL(ns
))
1009 multi_sub
= Parrot_get_global(interp
, ns
, name
);
1011 if (PMC_IS_NULL(multi_sub
))
1014 Parrot_mmd_maybe_candidate(interp
, multi_sub
, candidates
);
1020 =item C<static void mmd_search_global(PARROT_INTERP, STRING *name, PMC *cl)>
1022 Search the builtin namespace for matching candidates.
1029 mmd_search_global(PARROT_INTERP
, ARGIN(STRING
*name
), ARGIN(PMC
*cl
))
1031 ASSERT_ARGS(mmd_search_global
)
1032 STRING
* const multi_str
= CONST_STRING(interp
, "MULTI");
1033 PMC
* const ns
= Parrot_get_namespace_keyed_str(interp
,
1034 interp
->root_namespace
, multi_str
);
1035 PMC
*multi_sub
= Parrot_get_global(interp
, ns
, name
);
1037 if (PMC_IS_NULL(multi_sub
))
1040 Parrot_mmd_maybe_candidate(interp
, multi_sub
, cl
);
1046 =item C<static void mmd_add_multi_global(PARROT_INTERP, STRING *sub_name, PMC
1049 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
1050 stored in the global MULTI namespace.
1057 mmd_add_multi_global(PARROT_INTERP
, ARGIN(STRING
*sub_name
), ARGIN(PMC
*sub_obj
))
1059 ASSERT_ARGS(mmd_add_multi_global
)
1060 STRING
* const multi_str
= CONST_STRING(interp
, "MULTI");
1061 PMC
* const ns
= Parrot_make_namespace_keyed_str(interp
,
1062 interp
->root_namespace
, multi_str
);
1063 PMC
*multi_sub
= Parrot_get_global(interp
, ns
, sub_name
);
1065 if (PMC_IS_NULL(multi_sub
)) {
1066 multi_sub
= constant_pmc_new(interp
, enum_class_MultiSub
);
1067 Parrot_set_global(interp
, ns
, sub_name
, multi_sub
);
1070 PARROT_ASSERT(multi_sub
->vtable
->base_type
== enum_class_MultiSub
);
1071 VTABLE_push_pmc(interp
, multi_sub
, sub_obj
);
1077 =item C<static void mmd_add_multi_to_namespace(PARROT_INTERP, STRING *ns_name,
1078 STRING *sub_name, PMC *sub_obj)>
1080 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
1081 added as a method to a class.
1088 mmd_add_multi_to_namespace(PARROT_INTERP
, ARGIN(STRING
*ns_name
),
1089 ARGIN(STRING
*sub_name
), ARGIN(PMC
*sub_obj
))
1091 ASSERT_ARGS(mmd_add_multi_to_namespace
)
1092 PMC
* const hll_ns
= VTABLE_get_pmc_keyed_int(interp
,
1093 interp
->HLL_namespace
,
1094 Parrot_pcc_get_HLL(interp
, CURRENT_CONTEXT(interp
)));
1095 PMC
* const ns
= Parrot_make_namespace_keyed_str(interp
, hll_ns
, ns_name
);
1096 PMC
*multi_sub
= Parrot_get_global(interp
, ns
, sub_name
);
1098 if (PMC_IS_NULL(multi_sub
)) {
1099 multi_sub
= constant_pmc_new(interp
, enum_class_MultiSub
);
1100 Parrot_set_global(interp
, ns
, sub_name
, multi_sub
);
1103 PARROT_ASSERT(multi_sub
->vtable
->base_type
== enum_class_MultiSub
);
1104 VTABLE_push_pmc(interp
, multi_sub
, sub_obj
);
1110 =item C<void Parrot_mmd_add_multi_from_long_sig(PARROT_INTERP, STRING *sub_name,
1111 STRING *long_sig, PMC *sub_obj)>
1113 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
1114 stored in the global MULTI namespace.
1122 Parrot_mmd_add_multi_from_long_sig(PARROT_INTERP
,
1123 ARGIN(STRING
*sub_name
), ARGIN(STRING
*long_sig
), ARGIN(PMC
*sub_obj
))
1125 ASSERT_ARGS(Parrot_mmd_add_multi_from_long_sig
)
1126 Parrot_Sub_attributes
*sub
;
1127 STRING
*sub_str
= CONST_STRING(interp
, "Sub");
1128 STRING
*closure_str
= CONST_STRING(interp
, "Closure");
1129 PMC
*type_list
= Parrot_str_split(interp
, CONST_STRING(interp
, ","), long_sig
);
1130 STRING
*ns_name
= VTABLE_get_string_keyed_int(interp
, type_list
, 0);
1132 /* Attach a type tuple array to the sub for multi dispatch */
1133 PMC
*multi_sig
= mmd_build_type_tuple_from_type_list(interp
, type_list
);
1135 if (sub_obj
->vtable
->base_type
== enum_class_NCI
) {
1136 SETATTR_NCI_multi_sig(interp
, sub_obj
, multi_sig
);
1138 else if (VTABLE_isa(interp
, sub_obj
, sub_str
)
1139 || VTABLE_isa(interp
, sub_obj
, closure_str
)) {
1140 PMC_get_sub(interp
, sub_obj
, sub
);
1141 sub
->multi_signature
= multi_sig
;
1144 mmd_add_multi_to_namespace(interp
, ns_name
, sub_name
, sub_obj
);
1145 mmd_add_multi_global(interp
, sub_name
, sub_obj
);
1151 =item C<void Parrot_mmd_add_multi_from_c_args(PARROT_INTERP, const char
1152 *sub_name, const char *short_sig, const char *long_sig, funcptr_t
1155 Create a MultiSub, or add a variant to an existing MultiSub. The MultiSub is
1156 stored in the specified namespace.
1164 Parrot_mmd_add_multi_from_c_args(PARROT_INTERP
,
1165 ARGIN(const char *sub_name
), ARGIN(const char *short_sig
),
1166 ARGIN(const char *long_sig
), ARGIN(funcptr_t multi_func_ptr
))
1168 ASSERT_ARGS(Parrot_mmd_add_multi_from_c_args
)
1169 STRING
*comma
= CONST_STRING(interp
, ",");
1170 STRING
*sub_name_str
= Parrot_str_new_constant(interp
, sub_name
);
1171 STRING
*long_sig_str
= Parrot_str_new_constant(interp
, long_sig
);
1172 STRING
*short_sig_str
= Parrot_str_new_constant(interp
, short_sig
);
1173 PMC
*type_list
= Parrot_str_split(interp
, comma
, long_sig_str
);
1174 STRING
*ns_name
= VTABLE_get_string_keyed_int(interp
, type_list
, 0);
1176 /* Create an NCI sub for the C function */
1177 PMC
*sub_obj
= constant_pmc_new(interp
, enum_class_NCI
);
1178 PMC
*multi_sig
= mmd_build_type_tuple_from_long_sig(interp
,
1181 VTABLE_set_pointer_keyed_str(interp
, sub_obj
, short_sig_str
,
1182 F2DPTR(multi_func_ptr
));
1184 /* Attach a type tuple array to the NCI sub for multi dispatch */
1185 SETATTR_NCI_multi_sig(interp
, sub_obj
, multi_sig
);
1187 mmd_add_multi_to_namespace(interp
, ns_name
, sub_name_str
, sub_obj
);
1188 mmd_add_multi_global(interp
, sub_name_str
, sub_obj
);
1193 =item C<void Parrot_mmd_add_multi_list_from_c_args(PARROT_INTERP, const
1194 multi_func_list *mmd_info, INTVAL elements)>
1196 Create a collection of multiple dispatch subs from a C structure of
1197 information. Iterate through the list of details passed in. For each entry
1198 create a MultiSub or add a variant to an existing MultiSub. MultiSubs are
1199 created in the global 'MULTI' namespace in the Parrot HLL.
1201 Typically used to create all the multiple dispatch routines
1202 declared in a PMC from the PMC's class initialization function.
1210 Parrot_mmd_add_multi_list_from_c_args(PARROT_INTERP
,
1211 ARGIN(const multi_func_list
*mmd_info
), INTVAL elements
)
1213 ASSERT_ARGS(Parrot_mmd_add_multi_list_from_c_args
)
1215 for (i
= 0; i
< elements
; ++i
) {
1216 funcptr_t func_ptr
= mmd_info
[i
].func_ptr
;
1218 STRING
*sub_name
= mmd_info
[i
].multi_name
;
1219 STRING
*long_sig
= mmd_info
[i
].full_sig
;
1220 STRING
*short_sig
= mmd_info
[i
].short_sig
;
1221 STRING
*ns_name
= mmd_info
[i
].ns_name
;
1223 /* Create an NCI sub for the C function */
1224 PMC
*sub_obj
= constant_pmc_new(interp
, enum_class_NCI
);
1226 VTABLE_set_pointer_keyed_str(interp
, sub_obj
, short_sig
,
1229 /* Attach a type tuple array to the NCI sub for multi dispatch */
1230 SETATTR_NCI_long_signature(interp
, sub_obj
, long_sig
);
1232 mmd_add_multi_to_namespace(interp
, ns_name
, sub_name
, sub_obj
);
1233 mmd_add_multi_global(interp
, sub_name
, sub_obj
);
1240 =item C<MMD_Cache * Parrot_mmd_cache_create(PARROT_INTERP)>
1242 Creates and returns a new MMD cache.
1249 PARROT_CANNOT_RETURN_NULL
1251 Parrot_mmd_cache_create(PARROT_INTERP
)
1253 ASSERT_ARGS(Parrot_mmd_cache_create
)
1255 Hash
*cache
= parrot_new_hash(interp
);
1262 =item C<static STRING * mmd_cache_key_from_values(PARROT_INTERP, const char
1263 *name, PMC *values)>
1265 Generates an MMD cache key from an array of values.
1271 PARROT_WARN_UNUSED_RESULT
1272 PARROT_CAN_RETURN_NULL
1274 mmd_cache_key_from_values(PARROT_INTERP
, ARGIN(const char *name
),
1277 ASSERT_ARGS(mmd_cache_key_from_values
)
1278 /* Build array of type IDs, which we'll then use as a string to key into
1280 const INTVAL num_values
= VTABLE_elements(interp
, values
);
1281 const INTVAL name_len
= name
? strlen(name
) + 1: 0;
1282 const size_t id_size
= num_values
* sizeof (INTVAL
) + name_len
;
1283 INTVAL
*type_ids
= (INTVAL
*)mem_sys_allocate(id_size
);
1287 for (i
= 0; i
< num_values
; i
++) {
1288 const INTVAL id
= VTABLE_type(interp
, VTABLE_get_pmc_keyed_int(interp
, values
, i
));
1290 mem_sys_free(type_ids
);
1298 strcpy((char *)(type_ids
+ num_values
), name
);
1300 key
= Parrot_str_new(interp
, (char *)type_ids
, id_size
);
1301 mem_sys_free(type_ids
);
1309 =item C<PMC * Parrot_mmd_cache_lookup_by_values(PARROT_INTERP, MMD_Cache *cache,
1310 const char *name, PMC *values)>
1312 Takes an array of values for the call and does a lookup in the MMD cache.
1319 PARROT_WARN_UNUSED_RESULT
1320 PARROT_CAN_RETURN_NULL
1322 Parrot_mmd_cache_lookup_by_values(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1323 ARGIN(const char *name
), ARGIN(PMC
*values
))
1325 ASSERT_ARGS(Parrot_mmd_cache_lookup_by_values
)
1326 STRING
* const key
= mmd_cache_key_from_values(interp
, name
, values
);
1329 return (PMC
*)parrot_hash_get(interp
, cache
, key
);
1337 =item C<void Parrot_mmd_cache_store_by_values(PARROT_INTERP, MMD_Cache *cache,
1338 const char *name, PMC *values, PMC *chosen)>
1340 Takes an array of values for the call along with a chosen candidate and puts
1349 Parrot_mmd_cache_store_by_values(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1350 ARGIN(const char *name
), ARGIN(PMC
*values
), ARGIN(PMC
*chosen
))
1352 ASSERT_ARGS(Parrot_mmd_cache_store_by_values
)
1353 STRING
* const key
= mmd_cache_key_from_values(interp
, name
, values
);
1356 parrot_hash_put(interp
, cache
, key
, chosen
);
1362 =item C<static STRING * mmd_cache_key_from_types(PARROT_INTERP, const char
1365 Generates an MMD cache key from an array of types.
1371 PARROT_WARN_UNUSED_RESULT
1372 PARROT_CAN_RETURN_NULL
1374 mmd_cache_key_from_types(PARROT_INTERP
, ARGIN(const char *name
),
1377 ASSERT_ARGS(mmd_cache_key_from_types
)
1378 /* Build array of type IDs, which we'll then use as a string to key into
1380 const INTVAL num_types
= VTABLE_elements(interp
, types
);
1381 const INTVAL name_len
= name
? strlen(name
) + 1: 0;
1382 const size_t id_size
= num_types
* sizeof (INTVAL
) + name_len
;
1383 INTVAL
* const type_ids
= (INTVAL
*)mem_sys_allocate(id_size
);
1388 for (i
= 0; i
< num_types
; i
++) {
1389 const INTVAL id
= VTABLE_get_integer_keyed_int(interp
, types
, i
);
1392 mem_sys_free(type_ids
);
1400 strcpy((char *)(type_ids
+ num_types
), name
);
1402 key
= Parrot_str_new(interp
, (char *)type_ids
, id_size
);
1404 mem_sys_free(type_ids
);
1411 =item C<PMC * Parrot_mmd_cache_lookup_by_types(PARROT_INTERP, MMD_Cache *cache,
1412 const char *name, PMC *types)>
1414 Takes an array of types for the call and does a lookup in the MMD cache.
1421 PARROT_WARN_UNUSED_RESULT
1422 PARROT_CAN_RETURN_NULL
1424 Parrot_mmd_cache_lookup_by_types(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1425 ARGIN(const char *name
), ARGIN(PMC
*types
))
1427 ASSERT_ARGS(Parrot_mmd_cache_lookup_by_types
)
1428 const STRING
* const key
= mmd_cache_key_from_types(interp
, name
, types
);
1431 return (PMC
*)parrot_hash_get(interp
, cache
, key
);
1439 =item C<void Parrot_mmd_cache_store_by_types(PARROT_INTERP, MMD_Cache *cache,
1440 const char *name, PMC *types, PMC *chosen)>
1442 Takes an array of types for the call along with a chosen candidate and puts
1443 it into the cache. The name parameter is optional, and if the cache is already
1444 tied to an individual multi can be null.
1452 Parrot_mmd_cache_store_by_types(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
),
1453 ARGIN(const char *name
), ARGIN(PMC
*types
), ARGIN(PMC
*chosen
))
1455 ASSERT_ARGS(Parrot_mmd_cache_store_by_types
)
1456 STRING
* const key
= mmd_cache_key_from_types(interp
, name
, types
);
1459 parrot_hash_put(interp
, cache
, key
, chosen
);
1465 =item C<void Parrot_mmd_cache_mark(PARROT_INTERP, MMD_Cache *cache)>
1467 GC-marks an MMD cache.
1475 Parrot_mmd_cache_mark(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
))
1477 ASSERT_ARGS(Parrot_mmd_cache_mark
)
1478 /* As a small future optimization, note that we only *really* need to mark
1479 * keys - the candidates will be referenced outside the cache, provided it's
1480 * invalidated properly. */
1481 parrot_mark_hash(interp
, cache
);
1487 =item C<void Parrot_mmd_cache_destroy(PARROT_INTERP, MMD_Cache *cache)>
1489 Destroys an MMD cache.
1497 Parrot_mmd_cache_destroy(PARROT_INTERP
, ARGMOD(MMD_Cache
*cache
))
1499 ASSERT_ARGS(Parrot_mmd_cache_destroy
)
1500 parrot_hash_destroy(interp
, cache
);
1510 F<include/parrot/multidispatch.h>,
1511 F<http://svn.perl.org/perl6/doc/trunk/design/apo/A12.pod>,
1512 F<http://svn.perl.org/perl6/doc/trunk/design/syn/S12.pod>
1521 * c-file-style: "parrot"
1523 * vim: expandtab shiftwidth=4: