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