xref: /openbsd-src/gnu/usr.bin/perl/t/op/for-many.t (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
1#!./perl
2
3BEGIN {
4    chdir 't' if -d 't';
5    require "./test.pl";
6    set_up_inc('../lib');
7}
8
9use strict;
10use warnings;
11use utf8;
12
13my @have;
14
15@have = ();
16
17# Simplest case is an explicit list:
18for my ($q, $r) ('A', 'B', 'C', 'D') {
19    push @have, "$q;$r";
20}
21is("@have", 'A;B C;D', 'explicit list');
22
23@have = ();
24
25for my ($q, $r) (reverse 'A', 'B', 'C', 'D') {
26    push @have, "$q;$r";
27}
28is("@have", 'D;C B;A', 'explicit list reversed');
29
30@have = ();
31
32for my ($q, $r) ('A', 'B', 'C', 'D', 'E', 'F') {
33    push @have, "$q;$r";
34}
35is("@have", 'A;B C;D E;F', 'explicit list three iterations');
36
37@have = ();
38
39for my ($q, $r, $s) ('A', 'B', 'C', 'D', 'E', 'F') {
40    push @have, "$q;$r;$s";
41}
42is("@have", 'A;B;C D;E;F', 'explicit list triplets');
43
44@have = ();
45
46for my ($q, $r, $s,) ('A', 'B', 'C', 'D', 'E', 'F') {
47    push @have, "$q;$r;$s";
48}
49is("@have", 'A;B;C D;E;F', 'trailing comma n-fold');
50
51@have = ();
52
53for my ($q, $r, $s) ('A', 'B', 'C', 'D', 'E') {
54    push @have, join ';', map { $_ // 'undef' } $q, $r, $s;
55}
56
57is("@have", 'A;B;C D;E;undef', 'incomplete explicit list');
58
59@have = ();
60
61for my ($q, $r, $s) (reverse 'A', 'B', 'C', 'D', 'E') {
62    push @have, join ';', map { $_ // 'undef' } $q, $r, $s;
63}
64
65is("@have", 'E;D;C B;A;undef', 'incomplete explicit list reversed');
66
67# This two are legal syntax and actually indistinguishable from for my $q () ...
68@have = ();
69
70for my ($q,) ('A', 'B', 'C', 'D', 'E', 'F') {
71    push @have, $q;
72}
73is("@have", 'A B C D E F', 'trailing comma one-at-a-time');
74
75@have = ();
76
77for my ($q) ('A', 'B', 'C', 'D', 'E', 'F') {
78    push @have, $q;
79}
80is("@have", 'A B C D E F', 'one-at-a-time');
81
82
83# Arrays have an optimised case in pp_iter:
84{
85    no strict 'vars';
86
87    @array = split ' ', 'Dogs have owners, cats have staff.';
88
89    my $count = scalar @array;
90
91    @have = ();
92
93    for my ($q, $r, $s) (@array) {
94        push @have, "$q;$r;$s";
95    }
96    is("@have", 'Dogs;have;owners, cats;have;staff.', 'package array');
97    is(scalar @array, $count, 'package array size unchanged');
98
99    @have = ();
100
101    for my ($q, $r, $s) (reverse @array) {
102        push @have, "$q;$r;$s";
103    }
104    is("@have", 'staff.;have;cats owners,;have;Dogs', 'package array reversed');
105    is(scalar @array, $count, 'package array reversed size unchanged');
106
107    @have = ();
108
109    for my ($q, $r, $s, $t) (@array) {
110        push @have, join ';', map { $_ // '!' } $q, $r, $s, $t;
111    }
112    is("@have", 'Dogs;have;owners,;cats have;staff.;!;!', 'incomplete package array');
113
114    @have = ();
115
116    for my ($q, $r, $s, $t) (reverse @array) {
117        push @have, join ';', map { $_ // '!' } $q, $r, $s, $t;
118    }
119    is("@have", 'staff.;have;cats;owners, have;Dogs;!;!', 'incomplete package array reversed');
120    is(scalar @array, $count, 'incomplete package array size unchanged');
121
122    # And for our last test, we trash @array
123    for my ($q, $r) (@array) {
124        ($q, $r) = ($r, $q);
125    }
126    is("@array", 'have Dogs cats owners, staff. have', 'package array aliased');
127    is(scalar @array, $count, 'incomplete package array reversed size unchanged');
128}
129
130my @array = split ' ', 'God is real, unless declared integer.';
131
132my $count = scalar @array;
133
134@have = ();
135
136for my ($q, $r, $s) (@array) {
137    push @have, "$q;$r;$s";
138}
139is("@have", 'God;is;real, unless;declared;integer.', 'lexical array');
140is(scalar @array, $count, 'lexical array size unchanged');
141
142@have = ();
143
144for my ($q, $r, $s) (reverse @array) {
145    push @have, "$q;$r;$s";
146}
147is("@have", 'integer.;declared;unless real,;is;God', 'lexical array reversed');
148is(scalar @array, $count, 'lexical array reversed size unchanged');
149
150@have = ();
151
152for my ($q, $r, $s, $t) (@array) {
153    push @have, join ';', map { $_ // '!' } $q, $r, $s, $t;
154}
155is("@have", 'God;is;real,;unless declared;integer.;!;!', 'incomplete lexical array');
156is(scalar @array, $count, 'incomplete lexical array size unchanged');
157
158@have = ();
159
160for my ($q, $r, $s, $t) (reverse @array) {
161    push @have, join ';', map { $_ // '!' } $q, $r, $s, $t;
162}
163is("@have", 'integer.;declared;unless;real, is;God;!;!', 'incomplete lexical array reversed');
164is(scalar @array, $count, 'incomplete lexical array reversed size unchanged');
165
166for my ($q, $r) (@array) {
167    $q = uc $q;
168    $r = ucfirst $r;
169}
170is("@array", 'GOD Is REAL, Unless DECLARED Integer.', 'lexical array aliased');
171
172# Integer ranges have an optimised case in pp_iter:
173@have = ();
174
175for my ($q, $r, $s) (0..5) {
176    push @have, "$q;$r;$s";
177}
178
179is("@have", '0;1;2 3;4;5', 'integer list');
180
181@have = ();
182
183for my ($q, $r, $s) (reverse 0..5) {
184    push @have, "$q;$r;$s";
185}
186
187is("@have", '5;4;3 2;1;0', 'integer list reversed');
188
189@have = ();
190
191for my ($q, $r, $s) (1..5) {
192    push @have, join ';', map { $_ // 'undef' } $q, $r, $s;
193}
194
195is("@have", '1;2;3 4;5;undef', 'incomplete integer list');
196
197@have = ();
198
199for my ($q, $r, $s) (reverse 1..5) {
200    push @have, join ';', map { $_ // 'Thunderbirds are go' } $q, $r, $s;
201}
202
203is("@have", '5;4;3 2;1;Thunderbirds are go', 'incomplete integer list reversed');
204
205# String ranges have an optimised case in pp_iter:
206@have = ();
207
208for my ($q, $r, $s) ('A'..'F') {
209    push @have, "$q;$r;$s";
210}
211
212is("@have", 'A;B;C D;E;F', 'string list');
213
214@have = ();
215
216for my ($q, $r, $s) (reverse 'A'..'F') {
217    push @have, "$q;$r;$s";
218}
219
220is("@have", 'F;E;D C;B;A', 'string list reversed');
221
222@have = ();
223
224for my ($q, $r, $s) ('B'..'F') {
225    push @have, join ';', map { $_ // 'undef' } $q, $r, $s;
226}
227
228is("@have", 'B;C;D E;F;undef', 'incomplete string list');
229
230@have = ();
231
232for my ($q, $r, $s) (reverse 'B'..'F') {
233    push @have, join ';', map { $_ // 'undef' } $q, $r, $s;
234}
235
236is("@have", 'F;E;D C;B;undef', 'incomplete string list reversed');
237
238# Hashes are expanded as regular lists, so there's nothing particularly
239# special here:
240{
241    no strict;
242
243    %hash = (
244        perl => 'rules',
245        beer => 'foamy',
246    );
247
248    @have = ();
249
250    for my ($key, $value) (%hash) {
251        push @have, "$key;$value";
252    }
253
254    my $got = "@have";
255    if ($got =~ /^perl/) {
256        is($got, 'perl;rules beer;foamy', 'package hash key/value iteration');
257    }
258    else {
259        is($got, 'beer;foamy perl;rules', 'package hash key/value iteration');
260    }
261
262    @have = ();
263
264    for my ($value, $key) (reverse %hash) {
265        push @have, "$key;$value";
266    }
267
268    $got = "@have";
269    if ($got =~ /^perl/) {
270        is($got, 'perl;rules beer;foamy', 'package hash key/value reverse iteration');
271    }
272    else {
273        is($got, 'beer;foamy perl;rules', 'package hash key/value reverse iteration');
274    }
275
276    # values are aliases. As ever. Keys are copies.
277
278    for my ($key, $value) (%hash) {
279        $key = ucfirst $key;
280        $value = uc $value;
281    }
282
283    $got = join ';', %hash;
284
285    if ($got =~ /^perl/i) {
286        is($got, 'perl;RULES;beer;FOAMY', 'package hash value iteration aliases');
287    }
288    else {
289        is($got, 'beer;FOAMY;perl;RULES', 'package hash value iteration aliases');
290    }
291}
292
293my %hash = (
294    beer => 'street',
295    gin => 'lane',
296);
297
298
299@have = ();
300
301for my ($key, $value) (%hash) {
302    push @have, "$key;$value";
303}
304
305my $got = "@have";
306if ($got =~ /^gin/) {
307    is($got, 'gin;lane beer;street', 'lexical hash key/value iteration');
308}
309else {
310    is($got, 'beer;street gin;lane', 'lexical hash key/value iteration');
311}
312
313@have = ();
314
315for my ($value, $key) (reverse %hash) {
316    push @have, "$key;$value";
317}
318
319$got = "@have";
320if ($got =~ /^gin/) {
321    is($got, 'gin;lane beer;street', 'lexical hash key/value reverse iteration');
322}
323else {
324    is($got, 'beer;street gin;lane', 'lexical hash key/value reverse iteration');
325}
326
327# values are aliases, keys are copies, so this is a daft thing to do:
328
329for my ($key, $value) (%hash) {
330    ($key, $value) = ($value, $key);
331}
332
333$got = join ';', %hash;
334
335if ($got =~ /^gin/i) {
336    is($got, 'gin;gin;beer;beer', 'lexical hash value iteration aliases');
337}
338else {
339    is($got, 'beer;beer;gin;gin', 'lexical hash value iteration aliases');
340}
341
342my $code = 'for my ($q, $r) (6, 9) {}; 42';
343
344$got = eval $code;
345
346is($@, "", 'test code generated no error');
347is($got, 42, 'test code ran');
348
349$code =~ s/my/our/;
350
351like($code, qr/for our \(/, 'for our code set up correctly');
352$got = eval $code;
353
354like($@, qr/^Missing \$ on loop variable /, 'for our code generated error');
355is($got, undef, 'for our did not run');
356
357$code =~ s/ our//;
358
359like($code, qr/for \(/, 'for () () code set up correctly');
360$got = eval "no strict 'vars'; $code";
361
362like($@, qr/^syntax error /, 'for () () code generated error');
363is($got, undef, 'for () () did not run');
364
365# Yes, I looked these up:
366my @Quercus = qw(robor petraea cerris);
367# I should be able to sneak this past the children for some years...
368my @Allium = qw(cepa sativum ampeloprasum);
369
370for my ($left, $right) (@Quercus, @Allium) {
371    $left = uc $left;
372    $right = reverse $right;
373}
374
375is("@Quercus", 'ROBOR aeartep CERRIS', 'for () () aliases 1');
376is("@Allium", 'apec SATIVUM musarpolepma', 'for () () aliases 2');
377
378is(eval {
379    for my ($left, $right) (@Allium, undef, @Quercus) {
380        $left = reverse $left;
381        $right = lc($right // "");
382    }
383    54;
384}, undef, 'aliased rvalue');
385like($@, qr/^Modification of a read-only value attempted/,
386     'aliased rvalue threw the correct exception');
387
388is("@Allium", 'cepa sativum ampeloprasum', 'for () () aliases 3');
389is("@Quercus", 'ROBOR aeartep CERRIS', 'for () () aliases 4');
390
391is(eval {
392    for my ($left, $right) (@Quercus) {
393        $left = lc $left;
394        $right = reverse($right // "");
395    }
396    54;
397}, undef, 'padded with literal undef');
398like($@, qr/^Modification of a read-only value attempted/,
399     'padded with literal undef threw the correct exception');
400is("@Quercus", 'robor petraea cerris', 'side effects observed');
401
402my @numbers = (3, 2, 1, 0);
403my $redo;
404my $next;
405my $done;
406my $continue;
407
408for my ($left, $right) (@numbers) {
409    $left *= 3;
410    ++$right;
411    redo
412        unless $redo++;
413    ++$done;
414    next
415        unless $next++;
416    $left *= 5;
417    $right *= 7;
418} continue {
419    $continue .= 'x';
420}
421
422is("@numbers", '27 4 15 7', 'expected result');
423is($redo, 3, 'redo reached thrice');
424is($next, 2, 'next reached twice');
425is($continue, 'xx', 'continue reached twice');
426
427{
428    no strict 'vars';
429    # Important that this is a package variable, so that we test that the parser
430    # ends the scope of the my at the ')' and generates the correct ops to read
431    # from the symbol table, not the pad.
432
433    @Lamini = qw(alpaca guanaco llama vicuña);
434
435    @have = ();
436    for my ($domestic, $wild) (@Lamini) {
437        push @have, "$domestic;$wild";
438    }
439    is("@have", 'alpaca;guanaco llama;vicuña', 'comma test 0');
440
441    @have = ();
442    for my ($domestic, $wild,) (@Lamini) {
443        push @have, "$domestic;$wild";
444    }
445    is("@have", 'alpaca;guanaco llama;vicuña', 'comma test 1');
446
447    @have = ();
448    for my ($domestic,, $wild) (@Lamini) {
449        push @have, "$domestic;$wild";
450    }
451    is("@have", 'alpaca;guanaco llama;vicuña', 'comma test 2');
452
453    @have = ();
454    for my ($domestic,, $wild,) (@Lamini) {
455        push @have, "$domestic;$wild";
456    }
457    is("@have", 'alpaca;guanaco llama;vicuña', 'comma test 3');
458
459    @have = ();
460    for my ($domestic,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, $wild) (@Lamini) {
461        push @have, "$domestic;$wild";
462    }
463    is("@have", 'alpaca;guanaco llama;vicuña', 'comma test 42');
464}
465
466# Spaces shouldn't trigger parsing errors:
467{
468    my @correct = ('Pointy', 'Up', 'Flamey', 'Down');
469
470    @have = ();
471
472    for my                                          ($one) (@correct) {
473        push @have, $one;
474    }
475    is("@have", "@correct", 'for my ($one)');
476
477    @have = ();
478
479    for my($one) (@correct) {
480        push @have, $one;
481    }
482    is("@have", "@correct", 'for my($one)');
483
484    @have = ();
485
486    # This is lots of lovely whitespace:
487    for my
488        ($end, $orientation) (@correct) {
489        push @have, "$end end $orientation";
490    }
491    is("@have", "Pointy end Up Flamey end Down", 'for my ($one, $two)');
492
493    @have = ();
494
495    for my($end, $orientation) (@correct) {
496        push @have, "$end end $orientation";
497    }
498    is("@have", "Pointy end Up Flamey end Down", 'for my ($one, $two)');
499}
500
501done_testing();
502