File Coverage

Util.xs
Criterion Covered Total %
statement 366 448 81.7
branch 210 362 58.0
condition n/a
subroutine n/a
pod n/a
total 576 810 71.1


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include "XSParseKeyword.h"
7              
8 329           static HV *looputil_state_hv(pTHX) {
9 329           SV *sv = get_sv("Loop::Util::_STATE", GV_ADD);
10 329 100         if (!SvOK(sv) || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV) {
    50          
    50          
11 10           HV *hv = newHV();
12 10           sv_setsv(sv, newRV_noinc((SV *)hv));
13             }
14 329           return (HV *)SvRV(sv);
15             }
16              
17 200           static PERL_CONTEXT *looputil_find_loop_cx(pTHX) {
18             I32 i;
19 248 100         for (i = cxstack_ix; i >= 0; i--) {
20 247           PERL_CONTEXT *cx = &cxstack[i];
21 247 100         if (CxTYPE_is_LOOP(cx))
    100          
22 199           return cx;
23             }
24 1           return NULL;
25             }
26              
27 170           static int looputil_is_loop_context(PERL_CONTEXT *cx) {
28 170 50         if ( !cx ) return 0;
29 170 100         if ( CxTYPE_is_LOOP(cx) ) return 1;
    100          
30 90           return 0;
31             }
32              
33 339           static int looputil_state_key_for_cx(PERL_CONTEXT *cx, char prefix, char *kbuf, Size_t kbuf_sz) {
34             UV key;
35              
36 339 50         if ( !cx ) return 0;
37              
38 339           key = PTR2UV(cx);
39 339           return (int)my_snprintf(kbuf, kbuf_sz, "%c%" UVuf, prefix, key);
40             }
41              
42 5           static void looputil_delete_state_for_cx(pTHX_ PERL_CONTEXT *cx) {
43             HV *hv;
44             char kbuf[64];
45             int klen;
46              
47 5 50         if ( !cx ) return;
48              
49 5           hv = looputil_state_hv(aTHX);
50              
51 5           klen = looputil_state_key_for_cx(cx, 'I', kbuf, sizeof(kbuf));
52 5           (void)hv_delete(hv, kbuf, klen, G_DISCARD);
53              
54 5           klen = looputil_state_key_for_cx(cx, 'F', kbuf, sizeof(kbuf));
55 5           (void)hv_delete(hv, kbuf, klen, G_DISCARD);
56              
57 5           klen = looputil_state_key_for_cx(cx, 'L', kbuf, sizeof(kbuf));
58 5           (void)hv_delete(hv, kbuf, klen, G_DISCARD);
59             }
60              
61 297           static void looputil_set_state_iv_for_cx(pTHX_ PERL_CONTEXT *cx, char prefix, IV value) {
62             HV *hv;
63             char kbuf[64];
64             int klen;
65              
66 297 50         if ( !cx ) return;
67              
68 297           hv = looputil_state_hv(aTHX);
69 297           klen = looputil_state_key_for_cx(cx, prefix, kbuf, sizeof(kbuf));
70 297           (void)hv_store(hv, kbuf, klen, newSViv(value), 0);
71             }
72              
73 24           static int looputil_get_state_iv_for_cx(pTHX_ PERL_CONTEXT *cx, char prefix, IV *out) {
74             HV *hv;
75             char kbuf[64];
76             int klen;
77             SV **svp;
78              
79 24 50         if ( !cx ) return 0;
80              
81 24           hv = looputil_state_hv(aTHX);
82 24           klen = looputil_state_key_for_cx(cx, prefix, kbuf, sizeof(kbuf));
83 24           svp = hv_fetch(hv, kbuf, klen, 0);
84 24 50         if ( !svp || !*svp ) return 0;
    50          
85              
86 24           *out = SvIV(*svp);
87 24           return 1;
88             }
89              
90 100           static void looputil_set_iteration_for_cx(pTHX_ PERL_CONTEXT *cx, IV iteration) {
91 100           looputil_set_state_iv_for_cx(aTHX_ cx, 'I', iteration);
92 100           }
93              
94 24           static int looputil_get_iteration_for_cx(pTHX_ PERL_CONTEXT *cx, IV *out) {
95 24           return looputil_get_state_iv_for_cx(aTHX_ cx, 'I', out);
96             }
97              
98 100           static void looputil_set_loopkind_for_cx(pTHX_ PERL_CONTEXT *cx, int is_finite, IV length) {
99 100 50         if ( !cx ) return;
100              
101 100           looputil_set_state_iv_for_cx(aTHX_ cx, 'F', is_finite ? 1 : 0);
102 100 100         if ( is_finite ) {
103 97           looputil_set_state_iv_for_cx(aTHX_ cx, 'L', length);
104             }
105             else {
106 3           HV *hv = looputil_state_hv(aTHX);
107             char kbuf[64];
108 3           int klen = looputil_state_key_for_cx(cx, 'L', kbuf, sizeof(kbuf));
109 3           (void)hv_delete(hv, kbuf, klen, G_DISCARD);
110             }
111             }
112              
113 0           static int looputil_get_loopkind_for_cx(pTHX_ PERL_CONTEXT *cx, int *is_finite, IV *length) {
114             IV finite_iv;
115              
116 0 0         if ( !looputil_get_state_iv_for_cx(aTHX_ cx, 'F', &finite_iv) ) return 0;
117              
118 0           *is_finite = finite_iv ? 1 : 0;
119 0 0         if ( *is_finite ) {
120 0 0         if ( !looputil_get_state_iv_for_cx(aTHX_ cx, 'L', length) ) {
121 0           *length = 0;
122             }
123             }
124             else {
125 0           *length = 0;
126             }
127              
128 0           return 1;
129             }
130              
131 85           static int looputil_iteration_index_for_cx(pTHX_ PERL_CONTEXT *cx, IV *out) {
132              
133             U8 t;
134             U8 flags;
135              
136 85 100         if ( !cx ) return 0;
137              
138 84           t = CxTYPE(cx);
139 84           flags = (U8)(cx->cx_type & ( CXp_FOR_GV | CXp_FOR_PAD ));
140              
141 84 100         if ( !flags ) return 0;
142              
143 56 100         if ( t == CXt_LOOP_ARY ) {
144 26           *out = cx->blk_loop.state_u.ary.ix;
145 26           return 1;
146             }
147              
148 30 100         if ( t == CXt_LOOP_LIST ) {
149 18           *out = cx->blk_loop.state_u.stack.ix - 1;
150 18           return 1;
151             }
152              
153 12 50         if ( t == CXt_LOOP_LAZYIV ) {
154 12           *out = cx->blk_loop.state_u.lazyiv.cur;
155 12           return 1;
156             }
157              
158 0           return 0;
159             }
160              
161              
162 34           static PERL_CONTEXT *looputil_find_labeled_loop_cx(pTHX_ SV *wanted_label) {
163             I32 ix;
164 34           I32 current_ix = -1;
165             STRLEN wanted_len;
166 34           const char *wanted = SvPV(wanted_label, wanted_len);
167              
168 156 100         for ( ix = cxstack_ix; ix >= 0; ix-- ) {
169 140           PERL_CONTEXT *cur = &cxstack[ix];
170 140           STRLEN label_len = 0;
171             const char *label;
172              
173 140 100         if ( !looputil_is_loop_context(cur) ) continue;
174 68 100         if ( current_ix < 0 ) current_ix = ix;
175 68 50         if ( !cur->blk_oldcop ) continue;
176              
177 68           label = CxLABEL_len(cur, &label_len);
178 68 100         if ( label && label_len == wanted_len && memEQ(label, wanted, wanted_len) ) {
    50          
    50          
179 18           return cur;
180             }
181             }
182              
183 16 50         if ( current_ix >= 0 && wanted_len == 5 && memEQ(wanted, "OUTER", 5) ) {
    100          
    50          
184 18 100         for ( ix = current_ix + 1; ix <= cxstack_ix; ix++ ) {
185 6           PERL_CONTEXT *cur = &cxstack[ix];
186 6           IV tmp = -1;
187              
188 6 50         if ( !looputil_is_loop_context(cur) ) continue;
189 0 0         if ( looputil_iteration_index_for_cx(aTHX_ cur, &tmp) ) return cur;
190 0 0         if ( looputil_get_iteration_for_cx(aTHX_ cur, &tmp) ) return cur;
191             }
192              
193 24 50         for ( ix = current_ix - 1; ix >= 0; ix-- ) {
194 24           PERL_CONTEXT *cur = &cxstack[ix];
195 24           IV tmp = -1;
196              
197 24 100         if ( !looputil_is_loop_context(cur) ) continue;
198 24 50         if ( looputil_iteration_index_for_cx(aTHX_ cur, &tmp) ) return cur;
199 12 50         if ( looputil_get_iteration_for_cx(aTHX_ cur, &tmp) ) return cur;
200             }
201             }
202              
203 4           croak("could not find loop label '%" SVf "'", SVfARG(wanted_label));
204             }
205              
206 24           static int looputil_resolve_iteration_for_cx(pTHX_ PERL_CONTEXT *cx, IV *out) {
207 24 100         if ( looputil_iteration_index_for_cx(aTHX_ cx, out) ) return 1;
208 12 50         if ( looputil_get_iteration_for_cx(aTHX_ cx, out) ) return 1;
209 0           return 0;
210             }
211              
212 37           static bool looputil_consume_word(pTHX_ const char *w) {
213             const char *s;
214             STRLEN n;
215              
216 37 50         if (!(PL_parser && PL_parser->bufptr)) return FALSE;
    50          
217              
218 37           lex_read_space(0);
219 37 50         if (!(PL_parser && PL_parser->bufptr)) return FALSE;
    50          
220              
221 37           s = PL_parser->bufptr;
222              
223 37           n = (STRLEN)strlen(w);
224 37 100         if (!(strnEQ(s, w, n) && !isWORDCHAR(s[n]))) return FALSE;
    50          
225              
226 7           (void)lex_read_to((char *)s + (I32)n);
227 7           return TRUE;
228             }
229              
230 37           static SV *looputil_parse_optional_label(pTHX) {
231             const char *s;
232             const char *p;
233              
234 37           lex_read_space(0);
235 37 50         if ( !( PL_parser && PL_parser->bufptr ) ) return NULL;
    50          
236              
237 37           s = PL_parser->bufptr;
238 37 100         if ( !isIDFIRST(*s) ) return NULL;
239              
240 9           p = s + 1;
241 53 100         while ( isWORDCHAR(*p) ) p++;
242              
243 9           (void)lex_read_to((char *)p);
244 9           lex_read_space(0);
245 9 50         if ( !( PL_parser && PL_parser->bufptr ) ) return NULL;
    50          
246 9 50         if ( *PL_parser->bufptr != '{' ) return NULL;
247              
248 9           return newSVpvn(s, (STRLEN)(p - s));
249             }
250              
251 38           static OP *looputil_new_call_0(pTHX_ const char *name) {
252 38           GV *gv = gv_fetchpv(name, GV_ADD, SVt_PVCV);
253 38           OP *cv = newGVOP(OP_GV, 0, gv);
254 38           OP *args = newOP(OP_NULL, 0);
255 38           return newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, args, cv));
256             }
257              
258              
259 9           static OP *looputil_new_call_1_sv(pTHX_ const char *name, SV *arg_sv) {
260 9           GV *gv = gv_fetchpv(name, GV_ADD, SVt_PVCV);
261 9           OP *cv = newGVOP(OP_GV, 0, gv);
262 9           OP *arg = newSVOP(OP_CONST, 0, arg_sv);
263 9           OP *args = op_append_elem(OP_LIST, newOP(OP_NULL, 0), arg);
264 9           return newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, args, cv));
265             }
266              
267 0           static OP *looputil_wrap_if(pTHX_ const char *pred_pv, OP *thenop, OP *elseop) {
268 0           OP *cond = looputil_new_call_0(aTHX_ pred_pv);
269              
270 0 0         if (!elseop) return newCONDOP(0, cond, thenop, newOP(OP_NULL, 0));
271 0           return newCONDOP(0, cond, thenop, elseop);
272             }
273              
274 13           static int looputil_kw_iffirst(pTHX_ OP **op_ptr, void *hookdata) {
275             (void)hookdata;
276              
277 13           SV *label = looputil_parse_optional_label(aTHX);
278 13           OP *thenop = parse_block(0);
279 13           OP *elseop = NULL;
280             OP *cond;
281              
282 13 100         if ( looputil_consume_word(aTHX_ "else") ) {
283 1           elseop = parse_block(0);
284             }
285              
286 13 100         if ( label ) {
287 3           cond = looputil_new_call_1_sv(aTHX_ "Loop::Util::_looputil_is_first_label", label);
288             }
289             else {
290 10           cond = looputil_new_call_0(aTHX_ "Loop::Util::_looputil_is_first");
291             }
292              
293 13 100         if ( !elseop ) {
294 12           *op_ptr = newCONDOP(0, cond, thenop, newOP(OP_NULL, 0));
295             }
296             else {
297 1           *op_ptr = newCONDOP(0, cond, thenop, elseop);
298             }
299 13           return KEYWORD_PLUGIN_STMT;
300             }
301              
302 8           static int looputil_kw_iflast(pTHX_ OP **op_ptr, void *hookdata) {
303             (void)hookdata;
304              
305 8           SV *label = looputil_parse_optional_label(aTHX);
306 8           OP *thenop = parse_block(0);
307 8           OP *elseop = NULL;
308             OP *cond;
309              
310 8 100         if ( looputil_consume_word(aTHX_ "else") ) {
311 1           elseop = parse_block(0);
312             }
313              
314 8 100         if ( label ) {
315 2           cond = looputil_new_call_1_sv(aTHX_ "Loop::Util::_looputil_is_last_label", label);
316             }
317             else {
318 6           cond = looputil_new_call_0(aTHX_ "Loop::Util::_looputil_is_last");
319             }
320              
321 8 100         if ( !elseop ) {
322 7           *op_ptr = newCONDOP(0, cond, thenop, newOP(OP_NULL, 0));
323             }
324             else {
325 1           *op_ptr = newCONDOP(0, cond, thenop, elseop);
326             }
327 8           return KEYWORD_PLUGIN_STMT;
328             }
329              
330 9           static int looputil_kw_ifodd(pTHX_ OP **op_ptr, void *hookdata) {
331             (void)hookdata;
332              
333 9           SV *label = looputil_parse_optional_label(aTHX);
334 9           OP *thenop = parse_block(0);
335 9           OP *elseop = NULL;
336             OP *cond;
337              
338 9 100         if ( looputil_consume_word(aTHX_ "else") ) {
339 2           elseop = parse_block(0);
340             }
341              
342 9 100         if ( label ) {
343 2           cond = looputil_new_call_1_sv(aTHX_ "Loop::Util::_looputil_is_odd_label", label);
344             }
345             else {
346 7           cond = looputil_new_call_0(aTHX_ "Loop::Util::_looputil_is_odd");
347             }
348              
349 9 100         if ( !elseop ) {
350 7           *op_ptr = newCONDOP(0, cond, thenop, newOP(OP_NULL, 0));
351             }
352             else {
353 2           *op_ptr = newCONDOP(0, cond, thenop, elseop);
354             }
355 9           return KEYWORD_PLUGIN_STMT;
356             }
357              
358 7           static int looputil_kw_ifeven(pTHX_ OP **op_ptr, void *hookdata) {
359             (void)hookdata;
360              
361 7           SV *label = looputil_parse_optional_label(aTHX);
362 7           OP *thenop = parse_block(0);
363 7           OP *elseop = NULL;
364             OP *cond;
365              
366 7 100         if ( looputil_consume_word(aTHX_ "else") ) {
367 3           elseop = parse_block(0);
368             }
369              
370 7 100         if ( label ) {
371 2           cond = looputil_new_call_1_sv(aTHX_ "Loop::Util::_looputil_is_even_label", label);
372             }
373             else {
374 5           cond = looputil_new_call_0(aTHX_ "Loop::Util::_looputil_is_even");
375             }
376              
377 7 100         if ( !elseop ) {
378 4           *op_ptr = newCONDOP(0, cond, thenop, newOP(OP_NULL, 0));
379             }
380             else {
381 3           *op_ptr = newCONDOP(0, cond, thenop, elseop);
382             }
383 7           return KEYWORD_PLUGIN_STMT;
384             }
385              
386 10           static int looputil_kw_ix(pTHX_ OP **op_ptr, void *hookdata) {
387             (void)hookdata;
388              
389 10           *op_ptr = looputil_new_call_0(aTHX_ "Loop::Util::_looputil_ix");
390 10           return KEYWORD_PLUGIN_EXPR;
391             }
392              
393 20           static int looputil_parse_parenthesized_text(pTHX_ SV *out) {
394             const char *s;
395             const char *p;
396 20           int depth = 0;
397              
398 20           lex_read_space(0);
399 20 50         if ( !( PL_parser && PL_parser->bufptr ) ) return 0;
    50          
400              
401 20           s = PL_parser->bufptr;
402 20 50         if ( *s != '(' ) return 0;
403              
404 20           p = s;
405 71 50         while ( *p ) {
406 71 50         if ( *p == '\\' ) {
407 0 0         if ( p[1] ) p += 2;
408 0           else p++;
409 0           continue;
410             }
411              
412 71 50         if ( *p == '\'' || *p == '"' ) {
    50          
413 0           char q = *p++;
414 0 0         while ( *p ) {
415 0 0         if ( *p == '\\' && p[1] ) {
    0          
416 0           p += 2;
417 0           continue;
418             }
419 0 0         if ( *p == q ) {
420 0           p++;
421 0           break;
422             }
423 0           p++;
424             }
425 0           continue;
426             }
427              
428 71 100         if ( *p == '(' ) depth++;
429 50 100         else if ( *p == ')' ) {
430 21           depth--;
431 21 100         if ( depth == 0 ) {
432 20           sv_setpvn(out, s + 1, (STRLEN)(p - s - 1));
433 20           (void)lex_read_to((char *)p + 1);
434 20           return 1;
435             }
436             }
437              
438 51           p++;
439             }
440              
441 0           croak("loop count expression is missing closing ')' ");
442             }
443              
444              
445 4           static int looputil_parse_statement_text(pTHX_ SV *out) {
446             const char *start;
447             const char *cursor;
448             OP *expr;
449              
450 4           lex_read_space(0);
451 4 50         if ( !( PL_parser && PL_parser->bufptr ) ) return 0;
    50          
452              
453 4           start = PL_parser->bufptr;
454 4           expr = parse_fullexpr(0);
455 3 50         if ( !expr ) return 0;
456 3           op_free(expr);
457              
458 3           lex_read_space(0);
459 3 50         if ( !( PL_parser && PL_parser->bufptr ) ) return 0;
    50          
460 3           cursor = PL_parser->bufptr;
461              
462 3 100         if ( cursor < start ) {
463 1           STRLEN len = strlen(start);
464 1 50         if ( len == 0 ) return 0;
465 1           sv_setpvn(out, start, len);
466 1           return 1;
467             }
468              
469 2 50         if ( *cursor == ';' ) {
470 2           sv_setpvn(out, start, (STRLEN)(cursor - start + 1));
471 2           (void)lex_read_to((char *)cursor + 1);
472 2           return 1;
473             }
474              
475 0 0         if ( *cursor == '\0' ) {
476 0           sv_setpvn(out, start, (STRLEN)(cursor - start));
477 0           return 1;
478             }
479              
480 0           return 0;
481             }
482              
483 23           static int looputil_kw_loop(pTHX_ OP **op_ptr, void *hookdata) {
484             (void)hookdata;
485              
486              
487 23           SV *count_expr = newSVpvs("");
488 23           SV *rewrite = newSVpvs("");
489              
490 23           lex_read_space(0);
491              
492 23 50         if ( PL_parser && PL_parser->bufptr && *PL_parser->bufptr == '(' ) {
    50          
    100          
493 20 50         if ( !looputil_parse_parenthesized_text(aTHX_ count_expr) ) {
494 0           SvREFCNT_dec(count_expr);
495 0           SvREFCNT_dec(rewrite);
496 0           croak("loop count requires parentheses");
497             }
498              
499 20           sv_catpv(rewrite,
500             "for ( local $Loop::Util::LOOPKIND = 'finite', "
501             "local $Loop::Util::LENGTH = (");
502 20           sv_catsv(rewrite, count_expr);
503 20           sv_catpv(rewrite,
504             "), local $Loop::Util::ITERATION = 0; "
505             "( Loop::Util::_looputil_mark_iteration(), "
506             "$Loop::Util::ITERATION < $Loop::Util::LENGTH ); "
507             "$Loop::Util::ITERATION++ ) ");
508             }
509             else {
510 3           sv_catpv(rewrite,
511             "for ( local $Loop::Util::LOOPKIND = 'infinite', "
512             "local $Loop::Util::LENGTH = undef, "
513             "local $Loop::Util::ITERATION = 0; "
514             "( Loop::Util::_looputil_mark_iteration(), 1 ); "
515             "$Loop::Util::ITERATION++ ) ");
516             }
517              
518 23           lex_read_space(0);
519 23 50         if ( PL_parser && PL_parser->bufptr && *PL_parser->bufptr != '{' ) {
    50          
    100          
520 4           SV *stmt_expr = newSVpvs("");
521 4 50         if ( !looputil_parse_statement_text(aTHX_ stmt_expr) ) {
522 0           SvREFCNT_dec(count_expr);
523 0           SvREFCNT_dec(rewrite);
524 0           SvREFCNT_dec(stmt_expr);
525 0           croak("loop single-statement form requires trailing ';'");
526             }
527 3           sv_catpv(rewrite, "{ ");
528 3           sv_catsv(rewrite, stmt_expr);
529 3           sv_catpv(rewrite, " }");
530 3           SvREFCNT_dec(stmt_expr);
531             }
532              
533 22           lex_stuff_sv(rewrite, 0);
534 22           *op_ptr = parse_fullstmt(0);
535              
536 22           SvREFCNT_dec(count_expr);
537 22           SvREFCNT_dec(rewrite);
538              
539 22           return KEYWORD_PLUGIN_STMT;
540             }
541              
542             MODULE = Loop::Util PACKAGE = Loop::Util
543              
544             PROTOTYPES: DISABLE
545              
546             void
547             _looputil_mark_iteration()
548             CODE:
549 100           PERL_CONTEXT *cx = looputil_find_loop_cx(aTHX);
550 100           SV *iter_sv = get_sv("Loop::Util::ITERATION", 0);
551 100           SV *kind_sv = get_sv("Loop::Util::LOOPKIND", 0);
552 100           SV *len_sv = get_sv("Loop::Util::LENGTH", 0);
553              
554 100 50         if ( cx && iter_sv && SvOK(iter_sv) ) {
    50          
    50          
555 100           looputil_set_iteration_for_cx(aTHX_ cx, SvIV(iter_sv));
556             }
557              
558 100 50         if ( cx && kind_sv && SvOK(kind_sv) ) {
    50          
    50          
559             STRLEN klen;
560 100           const char *kind = SvPV(kind_sv, klen);
561              
562 100 100         if ( strEQ(kind, "finite") ) {
563 97 50         IV n = ( len_sv && SvOK(len_sv) ) ? SvIV(len_sv) : 0;
    50          
564 97           looputil_set_loopkind_for_cx(aTHX_ cx, 1, n);
565             }
566 3 50         else if ( strEQ(kind, "infinite") ) {
567 3           looputil_set_loopkind_for_cx(aTHX_ cx, 0, 0);
568             }
569             }
570              
571             bool
572             _looputil_is_first()
573             CODE:
574 22           PERL_CONTEXT *cx = looputil_find_loop_cx(aTHX);
575 22           SV *kind_sv = get_sv("Loop::Util::LOOPKIND", 0);
576 22           SV *iter_sv = get_sv("Loop::Util::ITERATION", 0);
577 22           IV i = -1;
578              
579 22 50         if ( kind_sv && SvOK(kind_sv) && iter_sv && SvOK(iter_sv) ) {
    100          
    50          
    50          
580 9           RETVAL = ( SvIV(iter_sv) == 0 );
581             }
582 13 100         else if ( looputil_iteration_index_for_cx(aTHX_ cx, &i) ) RETVAL = ( i == 0 );
583 1           else croak("iffirst only works in for/foreach loops over arrays or lists");
584             OUTPUT:
585             RETVAL
586              
587             bool
588             _looputil_is_first_label(wanted_label)
589             SV *wanted_label
590             CODE:
591 13           PERL_CONTEXT *found = looputil_find_labeled_loop_cx(aTHX_ wanted_label);
592 12           IV i = -1;
593              
594 12 50         if ( looputil_resolve_iteration_for_cx(aTHX_ found, &i) ) RETVAL = ( i == 0 );
595 0           else croak("iffirst with label only works in loop/for contexts with iteration indices");
596             OUTPUT:
597             RETVAL
598              
599             bool
600             _looputil_is_last_label(wanted_label)
601             SV *wanted_label
602             CODE:
603 7           PERL_CONTEXT *found = looputil_find_labeled_loop_cx(aTHX_ wanted_label);
604 6           U8 t = CxTYPE(found);
605 6           int is_finite = 0;
606 6           IV length = 0;
607              
608 6           RETVAL = 0;
609              
610 6 50         if ( t == CXt_LOOP_ARY ) {
611 0           AV *ary = found->blk_loop.state_u.ary.ary;
612 0           IV ix = found->blk_loop.state_u.ary.ix;
613 0 0         if ( ary ) {
614 0           IV lastix = (IV)av_len(ary);
615 0           RETVAL = ( ix >= lastix );
616             }
617             }
618 6 50         else if ( t == CXt_LOOP_LIST ) {
619 0           SSize_t basesp = found->blk_loop.state_u.stack.basesp;
620 0           IV ix = found->blk_loop.state_u.stack.ix;
621 0           SV **base = PL_stack_base + basesp;
622 0           SV **top = PL_stack_sp;
623              
624 0 0         if ( top >= base ) {
625 0           IV total = (IV)(top - base + 1);
626 0           RETVAL = ( ix >= ( total - 1 ) );
627             }
628             }
629 6 50         else if ( t == CXt_LOOP_LAZYIV ) {
630 6           IV cur = found->blk_loop.state_u.lazyiv.cur;
631 6           IV end = found->blk_loop.state_u.lazyiv.end;
632 6           RETVAL = ( cur == end );
633             }
634 0 0         else if ( t == CXt_LOOP_LAZYSV ) {
635 0           SV *cur = found->blk_loop.state_u.lazysv.cur;
636 0           SV *end = found->blk_loop.state_u.lazysv.end;
637 0 0         RETVAL = ( cur && end && sv_cmp(cur, end) == 0 );
    0          
    0          
638             }
639 0 0         else if ( looputil_get_loopkind_for_cx(aTHX_ found, &is_finite, &length) ) {
640 0           IV i = -1;
641 0 0         if ( !is_finite ) {
642 0           croak("iflast called outside for loop");
643             }
644 0 0         if ( !looputil_get_iteration_for_cx(aTHX_ found, &i) ) {
645 0           croak("iflast with label only works in loop/for contexts with iteration indices");
646             }
647 0 0         RETVAL = ( length > 0 && i >= ( length - 1 ) );
    0          
648             }
649             else {
650 0           croak("iflast with label only works in loop/for contexts");
651             }
652              
653 6 100         if ( RETVAL ) {
654 2           looputil_delete_state_for_cx(aTHX_ found);
655             }
656             OUTPUT:
657             RETVAL
658              
659             bool
660             _looputil_is_odd_label(wanted_label)
661             SV *wanted_label
662             CODE:
663 7           PERL_CONTEXT *found = looputil_find_labeled_loop_cx(aTHX_ wanted_label);
664 6           IV i = -1;
665              
666 6 50         if ( looputil_resolve_iteration_for_cx(aTHX_ found, &i) ) RETVAL = ( ( i % 2 ) == 0 );
667 0           else croak("ifodd with label only works in loop/for contexts with iteration indices");
668             OUTPUT:
669             RETVAL
670              
671             bool
672             _looputil_is_even_label(wanted_label)
673             SV *wanted_label
674             CODE:
675 7           PERL_CONTEXT *found = looputil_find_labeled_loop_cx(aTHX_ wanted_label);
676 6           IV i = -1;
677              
678 6 50         if ( looputil_resolve_iteration_for_cx(aTHX_ found, &i) ) RETVAL = ( ( i % 2 ) != 0 );
679 0           else croak("ifeven with label only works in loop/for contexts with iteration indices");
680             OUTPUT:
681             RETVAL
682              
683             bool
684             _looputil_is_last()
685             CODE:
686 14           PERL_CONTEXT *cx = looputil_find_loop_cx(aTHX);
687 14           SV *kind_sv = get_sv("Loop::Util::LOOPKIND", 0);
688 14           SV *len_sv = get_sv("Loop::Util::LENGTH", 0);
689 14           SV *iter_sv = get_sv("Loop::Util::ITERATION", 0);
690              
691 14 50         if ( kind_sv && SvOK(kind_sv) ) {
    100          
692             STRLEN klen;
693 4           const char *kind = SvPV(kind_sv, klen);
694              
695 4 100         if ( strEQ(kind, "infinite") ) {
696 1           croak("iflast called outside for loop");
697             }
698              
699 3 50         if ( strEQ(kind, "finite") ) {
700 3 50         IV i = ( iter_sv && SvOK(iter_sv) ) ? SvIV(iter_sv) : 0;
    50          
701 3 50         IV n = ( len_sv && SvOK(len_sv) ) ? SvIV(len_sv) : 0;
    50          
702 3 50         RETVAL = n > 0 && i >= ( n - 1 );
    100          
703             }
704             else {
705 0           croak("iflast called outside for loop");
706             }
707             }
708 10 50         else if (!cx) { croak("iflast called outside for loop"); }
709             else {
710             /*
711             Determine "last" for foreach-style loops by inspecting cx->blk_loop state.
712             We accept:
713             CXt_LOOP_ARY : for (@ary)
714             CXt_LOOP_LIST : for (list) (best-effort via stack size)
715             CXt_LOOP_LAZYIV : for (1..9)
716             CXt_LOOP_LAZYSV : for ('a'..'z')
717             Other loop kinds => false.
718             */
719 10           U8 t = CxTYPE(cx);
720 10           RETVAL = 0;
721              
722 10 100         if (t == CXt_LOOP_ARY) {
723 9           AV *ary = cx->blk_loop.state_u.ary.ary;
724 9           IV ix = cx->blk_loop.state_u.ary.ix;
725 9 50         if (ary) {
726 9           IV lastix = (IV)av_len(ary);
727 9 100         if (ix >= lastix) RETVAL = 1;
728             }
729             }
730 1 50         else if (t == CXt_LOOP_LIST) {
731 0           SSize_t basesp = cx->blk_loop.state_u.stack.basesp;
732 0           IV ix = cx->blk_loop.state_u.stack.ix;
733              
734 0           SV **base = PL_stack_base + basesp;
735 0           SV **top = PL_stack_sp;
736              
737 0 0         if (top >= base) {
738 0           IV total = (IV)(top - base + 1);
739 0 0         if (ix >= (total - 1)) RETVAL = 1;
740             }
741             }
742 1 50         else if (t == CXt_LOOP_LAZYIV) {
743 0           IV cur = cx->blk_loop.state_u.lazyiv.cur;
744 0           IV end = cx->blk_loop.state_u.lazyiv.end;
745 0 0         if (cur == end) RETVAL = 1;
746             }
747 1 50         else if (t == CXt_LOOP_LAZYSV) {
748 0           SV *cur = cx->blk_loop.state_u.lazysv.cur;
749 0           SV *end = cx->blk_loop.state_u.lazysv.end;
750 0 0         if (cur && end && sv_cmp(cur, end) == 0) RETVAL = 1;
    0          
    0          
751             }
752              
753 10 100         if (RETVAL) {
754             /* cleanup state on last iteration to avoid unbounded growth */
755 3           looputil_delete_state_for_cx(aTHX_ cx);
756             }
757             else {
758 7 100         if ( t != CXt_LOOP_ARY && t != CXt_LOOP_LIST &&
    50          
    50          
759 1 50         t != CXt_LOOP_LAZYIV && t != CXt_LOOP_LAZYSV ) {
760 1           croak("iflast called outside for loop");
761             }
762             }
763             }
764             OUTPUT:
765             RETVAL
766              
767             bool
768             _looputil_is_odd()
769             CODE:
770 12           PERL_CONTEXT *cx = looputil_find_loop_cx(aTHX);
771 12           SV *kind_sv = get_sv("Loop::Util::LOOPKIND", 0);
772 12           SV *iter_sv = get_sv("Loop::Util::ITERATION", 0);
773 12           IV i = -1;
774              
775 12 50         if ( kind_sv && SvOK(kind_sv) && iter_sv && SvOK(iter_sv) ) {
    100          
    50          
    50          
776 3           RETVAL = ( ( SvIV(iter_sv) % 2 ) == 0 );
777             }
778 9 100         else if ( looputil_iteration_index_for_cx(aTHX_ cx, &i) ) RETVAL = ( ( i % 2 ) == 0 );
779 1           else croak("ifodd only works in for/foreach loops over arrays or lists");
780             OUTPUT:
781             RETVAL
782              
783             bool
784             _looputil_is_even()
785             CODE:
786 14           PERL_CONTEXT *cx = looputil_find_loop_cx(aTHX);
787 14           SV *kind_sv = get_sv("Loop::Util::LOOPKIND", 0);
788 14           SV *iter_sv = get_sv("Loop::Util::ITERATION", 0);
789 14           IV i = -1;
790              
791 14 50         if ( kind_sv && SvOK(kind_sv) && iter_sv && SvOK(iter_sv) ) {
    100          
    50          
    50          
792 3           RETVAL = ( ( SvIV(iter_sv) % 2 ) != 0 );
793             }
794 11 100         else if ( looputil_iteration_index_for_cx(aTHX_ cx, &i) ) RETVAL = ( ( i % 2 ) != 0 );
795 2           else croak("ifeven only works in for/foreach loops over arrays or lists");
796             OUTPUT:
797             RETVAL
798              
799             SV *
800             _looputil_ix()
801             CODE:
802 38           PERL_CONTEXT *cx = looputil_find_loop_cx(aTHX);
803 38           SV *kind_sv = get_sv("Loop::Util::LOOPKIND", 0);
804 38           SV *iter_sv = get_sv("Loop::Util::ITERATION", 0);
805 38           IV i = -1;
806 38 100         U8 t = cx ? CxTYPE(cx) : 0;
807              
808 38 50         if ( kind_sv && SvOK(kind_sv) && iter_sv && SvOK(iter_sv) && t == CXt_LOOP_PLAIN ) {
    50          
    50          
    50          
    100          
809 22           RETVAL = newSViv(SvIV(iter_sv));
810             }
811 16 100         else if ( looputil_iteration_index_for_cx(aTHX_ cx, &i) ) {
812 15           RETVAL = newSViv(i);
813             }
814             else {
815 1           RETVAL = &PL_sv_undef;
816             }
817             OUTPUT:
818             RETVAL
819              
820             BOOT:
821             /*
822             iffirst/iflast are statement keywords.
823             */
824             static struct XSParseKeywordHooks iffirst_hooks;
825             static struct XSParseKeywordHooks iflast_hooks;
826             static struct XSParseKeywordHooks ifodd_hooks;
827             static struct XSParseKeywordHooks ifeven_hooks;
828             static struct XSParseKeywordHooks loop_hooks;
829             static struct XSParseKeywordHooks ix_hooks;
830              
831 13           iffirst_hooks.flags = XPK_FLAG_STMT | XPK_FLAG_PERMIT_LEXICAL;
832 13           iffirst_hooks.permit_hintkey = "Loop::Util/iffirst";
833 13           iffirst_hooks.parse = looputil_kw_iffirst;
834              
835 13           iflast_hooks.flags = XPK_FLAG_STMT | XPK_FLAG_PERMIT_LEXICAL;
836 13           iflast_hooks.permit_hintkey = "Loop::Util/iflast";
837 13           iflast_hooks.parse = looputil_kw_iflast;
838              
839 13           ifodd_hooks.flags = XPK_FLAG_STMT | XPK_FLAG_PERMIT_LEXICAL;
840 13           ifodd_hooks.permit_hintkey = "Loop::Util/ifodd";
841 13           ifodd_hooks.parse = looputil_kw_ifodd;
842              
843 13           ifeven_hooks.flags = XPK_FLAG_STMT | XPK_FLAG_PERMIT_LEXICAL;
844 13           ifeven_hooks.permit_hintkey = "Loop::Util/ifeven";
845 13           ifeven_hooks.parse = looputil_kw_ifeven;
846              
847 13           loop_hooks.flags = XPK_FLAG_STMT | XPK_FLAG_PERMIT_LEXICAL;
848 13           loop_hooks.permit_hintkey = "Loop::Util/loop";
849 13           loop_hooks.parse = looputil_kw_loop;
850              
851 13           ix_hooks.flags = XPK_FLAG_EXPR | XPK_FLAG_PERMIT_LEXICAL;
852 13           ix_hooks.permit_hintkey = "Loop::Util/__IX__";
853 13           ix_hooks.parse = looputil_kw_ix;
854              
855 13           boot_xs_parse_keyword(0);
856 13           register_xs_parse_keyword("iffirst", &iffirst_hooks, NULL);
857 13           register_xs_parse_keyword("iflast", &iflast_hooks, NULL);
858 13           register_xs_parse_keyword("ifodd", &ifodd_hooks, NULL);
859 13           register_xs_parse_keyword("ifeven", &ifeven_hooks, NULL);
860 13           register_xs_parse_keyword("loop", &loop_hooks, NULL);
861 13           register_xs_parse_keyword("__IX__", &ix_hooks, NULL);