tagged release 0.6.4
[parrot.git] / src / pmc / slice.pmc
bloba52da7496f77bac967067a09fbd9c3980f4e5da8
1 /*
2 Copyright (C) 2004-2008, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/pmc/slice.pmc - Slice PMC
9 =head1 DESCRIPTION
11 These are the vtable functions for the slice PMC class.
13 A Slice PMC isa Key PMC, holding a chain of start and/or end values
14 for slice ranges. Private flags define the meaning of the values:
16   [ s .. e ]    s .. KEY_start_slice_FLAG; e .. KEY_end_slice_FLAG
17   [ x,     ]    KEY_start_slice_FLAG | KEY_end_slice_FLAG
18   [  .. e  ]    KEY_inf_slice_FLAG   | KEY_end_slice_FLAG
19   [ s ..   ]    KEY_start_slice_FLAG | KEY_inf_slice_FLAG
21 Infinite ranges are currently implemented for Array and PerlString only.
23 Run
25   $ parrot -d2000 slice.pasm
27 to see slice constant flags.
29 During initialization above key chain gets converted to parrot_range_t
30 structures.
32 =head2 Methods
34 =over 4
36 =cut
40 #include "parrot/parrot.h"
42 #define VALID_RANGE(r) ((r) && ((INTVAL)(r) != -1))
43 #define RVal_int(u) (u).i
44 #define RVal_str(u) (u).s
48  * create range_t structure
49  *
50  * set the Slice iter state to initial, first position
51  * no backwards iterations for now
52  *
53  * XXX self is a key chain (PMC constant), which shouldn't be modified
54  *     finally this stuff should be call from packfiles thaw
55  *
56  */
57 static void
58 set_slice_start(PARROT_INTERP, PMC *self, PMC *key, PMC *agg)
60     parrot_range_t *range = mem_allocate_typed(parrot_range_t);
62     /*
63      * when this PMC gets created with init(), this flag should get set if and
64      * only if there's a slice range allocated for it -- that is, if this
65      * function gets called
66      */
67     PObj_custom_mark_destroy_SETALL(self);
69     PMC_struct_val(self)  = range;
71 next_range:
72     range->next           = NULL;
73     RVal_int(range->step) = 1;
75     if (key_type(interp, key) & KEY_integer_FLAG) {
76         range->type = enum_type_INTVAL;
78         /* integer key */
79         if ((PObj_get_FLAGS(key) &
80                 (KEY_start_slice_FLAG | KEY_end_slice_FLAG)) ==
81                 (KEY_start_slice_FLAG | KEY_end_slice_FLAG)) {
82             /* start == end */
83             RVal_int(range->start)   =
84                 RVal_int(range->end) = key_integer(interp, key);
85         }
86         else if ((PObj_get_FLAGS(key) &
87                     (KEY_inf_slice_FLAG | KEY_end_slice_FLAG)) ==
88                 (KEY_inf_slice_FLAG | KEY_end_slice_FLAG)) {
89             /*
90              * first range is ".. end"
91              * start at index 0
92              * */
93             RVal_int(range->start) = 0;
94             RVal_int(range->end)   = key_integer(interp, key);
95         }
96         else {
97             /*
98              * else start at range value
99              */
100             RVal_int(range->start) = key_integer(interp, key);
101             if ((PObj_get_FLAGS(key) &
102                 (KEY_inf_slice_FLAG | KEY_start_slice_FLAG)) ==
103                 (KEY_inf_slice_FLAG | KEY_start_slice_FLAG)) {
104                 /* last range "start .." */
105                 RVal_int(range->end) = VTABLE_elements(interp, agg) - 1;
106                 if (PMC_data(key))
107                     real_exception(interp, NULL, 1, "Illegal range after start..");
108             }
109             else {
110                 /* must have end in next key */
111                 key = PMC_data_typed(key, PMC *);
113                 if (!key)
114                     real_exception(interp, NULL, 1, "no end range specified");
116                 RVal_int(range->end) = key_integer(interp, key);
117             }
118         }
120         if (agg->vtable->base_type == enum_class_Slice)
121             --RVal_int(range->end);
122         RVal_int(range->cur) = RVal_int(range->start);
124 range_end:
125         key = PMC_data_typed(key, PMC *);
127         if (key) {
128             parrot_range_t * const n = mem_allocate_typed(parrot_range_t);
129             range->next       = n;
130             range             = n;
131             goto next_range;
132         }
133         return;
134     }
135     else {
136         if (PObj_get_FLAGS(key) & KEY_inf_slice_FLAG) {
137             real_exception(interp, NULL, 1,
138                                "unlimited slice range for hash "
139                                "not implemented");
140         }
141         range->type = enum_type_STRING;
143         /*
144          * string assumed
145          * start at value
146          */
147         RVal_str(range->start)   =
148             RVal_str(range->cur) =
149             key_string(interp, key);
151         if ((PObj_get_FLAGS(key) &
152                     (KEY_start_slice_FLAG | KEY_end_slice_FLAG)) ==
153                     (KEY_start_slice_FLAG | KEY_end_slice_FLAG)) {
154             /* start == end */
155             RVal_str(range->end) = RVal_str(range->start);
156         }
157         else {
158             /* must have end in next key */
159             key = PMC_data_typed(key, PMC *);
161             if (!key)
162                 real_exception(interp, NULL, 1, "no end range specified");
164             RVal_str(range->end) = key_string(interp, key);
165         }
166         goto range_end;
167     }
171  * increment Slice value according to range and/or advance to
172  * next range PMC in Key chain
173  */
174 static void
175 set_slice_next(PARROT_INTERP, PMC *self, PMC *agg)
177     parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(self);
179     if (!VALID_RANGE(r))
180         real_exception(interp, NULL, E_StopIteration, "StopIteration");
182     if (r->type == enum_type_INTVAL) {
183         RVal_int(r->cur) += RVal_int(r->step);
184         if (RVal_int(r->step) > 0) {
185             if (RVal_int(r->cur) > RVal_int(r->end)) {
186                 parrot_range_t *n;
187 next_range:
188                 n = r->next;
189                 mem_sys_free(r);
191                 PMC_struct_val(self) = n;
193                 if (!n)
194                     PMC_int_val(self) = -1;
195             }
196         }
197         else {
198             if (RVal_int(r->cur) < RVal_int(r->end))
199                 goto next_range;
200         }
201     }
202     else {
203         STRING * const cur = RVal_str(r->cur);
204         STRING * const end = RVal_str(r->end);
206         if (string_compare(interp, cur, end) < 0)
207             RVal_str(r->cur) = string_increment(interp, cur);
208         else
209             goto next_range;
210     }
213 pmclass Slice need_ext extends Key {
215     VTABLE void init() {
216         PMC_struct_val(SELF) = NULL;
217         PMC_pmc_val(SELF)    = NULL;
218     }
220     VTABLE void mark() {
221         parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(SELF);
223         if (PMC_pmc_val(SELF))
224             pobject_lives(INTERP, (PObj *)PMC_pmc_val(SELF));
226         /*
227          * the pointer must not be null, or at the end of iteration and it must
228          * point to STRINGs.  Note that r->step is always the integer value 1.
229          */
230         if (VALID_RANGE(r) && r->type == enum_type_STRING) {
231             if (RVal_str(r->start))
232                 pobject_lives(INTERP, (PObj *)RVal_str(r->start));
233             if (RVal_str(r->end))
234                 pobject_lives(INTERP, (PObj *)RVal_str(r->end));
235             if (RVal_str(r->cur))
236                 pobject_lives(INTERP, (PObj *)RVal_str(r->cur));
237         }
238     }
240     VTABLE void init_pmc(PMC *key) {
241         SELF.init();
243         /*
244          * that's actually the KEY_number_FLAG - but I can't hardly
245          * imagine that we get keyed by FLOATVAL slices on
246          * arrays
247          */
248         PObj_get_FLAGS(SELF) |= PObj_private1_FLAG;
249         PObj_custom_mark_destroy_SETALL(SELF);
250         set_slice_start(INTERP, SELF, key, SELF);
251     }
253     VTABLE void destroy() {
254         parrot_range_t *r = (parrot_range_t *)PMC_struct_val(SELF);
256         /* iteration ended - all is freed */
257         if ((INTVAL)r == -1)
258             return;
260         while (r) {
261             parrot_range_t * const n = r->next;
262             mem_sys_free(r);
263             r = n;
264         }
265     }
267     VTABLE PMC *clone() {
268         real_exception(INTERP, NULL, E_NotImplementedError, "Unimplemented");
269     }
273 =item C<INTVAL get_integer()>
275 Get the next integer key from the current slice range.
277 =item C<STRING *get_string()>
279 Get the next string key from the current slice range.
281 =cut
285     VTABLE INTVAL get_integer() {
286         const parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(SELF);
288         if (!VALID_RANGE(r))
289             real_exception(INTERP, NULL, E_StopIteration, "StopIteration");
291         return RVal_int(r->cur);
292     }
294     VTABLE STRING *get_string() {
295         const parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(SELF);
297         return RVal_str(r->cur);
298     }
302 =item C<PMC *slice(PMC *key)>
304 =item C<PMC *get_iter()>
306 A slice can serve as its own iterator, yielding values [start .. end-1].
307 This is used for implementing Pythons xrange()
309 =cut
313     VTABLE PMC *slice(PMC *key, INTVAL f) {
314         PMC *iter;
316         if (f)
317             real_exception(INTERP, NULL, E_ValueError,
318                     "Slice: Unknown slice type");
320         iter                 = pmc_new_init(INTERP, enum_class_Iterator, SELF);
321         PMC_struct_val(iter) = key;
322         return iter;
323     }
325     VTABLE PMC *get_iter() {
326         PMC * const iter     = pmc_new_init(INTERP, enum_class_Iterator, SELF);
327         PMC_struct_val(iter) = SELF;
328         return iter;
329     }
331     VTABLE INTVAL elements() {
332         const parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(SELF);
333         /* only start .. end supported so:
334          * TODO check flags somewhere
335          * */
336         return RVal_int(r->start) - RVal_int(r->end);
337     }
339     VTABLE INTVAL get_integer_keyed(PMC *key) {
340         const parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(key);
341         return RVal_int(r->cur);
342     }
344     VTABLE STRING *get_string_keyed(PMC *key) {
345         const INTVAL v = VTABLE_get_integer(INTERP, key);
346         return string_from_int(INTERP, v);
347     }
349     VTABLE PMC *get_pmc_keyed(PMC *key) {
350         const parrot_range_t * const r = (parrot_range_t *)PMC_struct_val(key);
351         PMC * const res   = PMC_pmc_val(SELF);
352         PMC_int_val(res)  = RVal_int(r->cur);
354         return res;
355     }
358 =item C<PMC *nextkey_keyed(PMC *agg, INTVAL what)>
360 Prepare slice PMC SELF for iteration over the passed aggregate or
361 advance to next position in the range, depending on what.
363 =cut
366     VTABLE PMC *nextkey_keyed(PMC *agg, INTVAL what) {
367         PMC *ret = SELF;
369         switch (what) {
370             case ITERATE_FROM_START:
371             case ITERATE_FROM_START_KEYS:    /* reset key */
373                 /* xrange implementation - the slice PMC itself serves as the
374                  * aggregate. It's already initialized. */
375                 if (!agg)
376                     return SELF;
378                 /*
379                  * need a new Slice PMC that holds the state
380                  * especially PMC_data() must be zero
381                  *
382                  * aggregate call get_integer/get_string on this
383                  * PMC, because it's marked being a Key PMC
384                  */
385                 ret = pmc_new(INTERP, enum_class_Slice);
386                 PObj_get_FLAGS(ret) |= KEY_pmc_FLAG;
388                 /* set start value */
389                 set_slice_start(INTERP, ret, SELF, agg);
390                 break;
392                 /* we are passed now the new PMC we created above */
393             case ITERATE_GET_NEXT:
394                 set_slice_next(INTERP, SELF, agg);
395                 break;
396             default:
397                 real_exception(INTERP, NULL, E_NotImplementedError,
398                         "No backward iteration on slices yet");
399                 break;
400         }
402         return ret;
403     }
408 =back
410 =cut
415  * Local variables:
416  *   c-file-style: "parrot"
417  * End:
418  * vim: expandtab shiftwidth=4:
419  */