From: Nick Ing-Simmons Date: Sat, 25 Apr 1998 13:58:17 +0000 (+0000) Subject: Auto-insert defined() test in while when test expression is X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=1f5fef3ecc6ec9818895ca2466f0939fac0e8ba9;p=p5sagit%2Fp5-mst-13.2.git Auto-insert defined() test in while when test expression is readline (i.e. <>), glob, readdir, or each. p4raw-id: //depot/ansiperl@900 --- diff --git a/op.c b/op.c index 7459ae6..2dd64f8 100644 --- a/op.c +++ b/op.c @@ -2669,7 +2669,7 @@ new_logop(I32 type, I32 flags, OP** firstp, OP** otherp) case OP_NULL: if (k2 && k2->op_type == OP_READLINE && (k2->op_flags & OPf_STACKED) - && (k1->op_type == OP_RV2SV || k1->op_type == OP_PADSV)) + && ((k1->op_flags & OPf_WANT) == OPf_WANT_SCALAR)) warnop = k2->op_type; break; @@ -2831,8 +2831,26 @@ newLOOPOP(I32 flags, I32 debuggable, OP *expr, OP *block) || (expr->op_type == OP_NULL && expr->op_targ == OP_GLOB)) { expr = newUNOP(OP_DEFINED, 0, newASSIGNOP(0, newDEFSVOP(), 0, expr) ); + } else if (expr->op_flags & OPf_KIDS) { + OP *k1 = ((UNOP*)expr)->op_first; + OP *k2 = (k1) ? k1->op_sibling : NULL; + switch (expr->op_type) { + case OP_NULL: + if (k2 && k2->op_type == OP_READLINE + && (k2->op_flags & OPf_STACKED) + && ((k1->op_flags & OPf_WANT) == OPf_WANT_SCALAR)) + expr = newUNOP(OP_DEFINED, 0, expr); + break; + + case OP_SASSIGN: + if (k1->op_type == OP_READDIR + || k1->op_type == OP_GLOB + || k1->op_type == OP_EACH) + expr = newUNOP(OP_DEFINED, 0, expr); + break; + } } - } + } listop = append_elem(OP_LINESEQ, block, newOP(OP_UNSTACK, 0)); o = new_logop(OP_AND, 0, &expr, &listop); @@ -2866,8 +2884,27 @@ newWHILEOP(I32 flags, I32 debuggable, LOOP *loop, I32 whileline, OP *expr, OP *b || (expr->op_type == OP_NULL && expr->op_targ == OP_GLOB))) { expr = newUNOP(OP_DEFINED, 0, newASSIGNOP(0, newDEFSVOP(), 0, expr) ); + } else if (expr && (expr->op_flags & OPf_KIDS)) { + OP *k1 = ((UNOP*)expr)->op_first; + OP *k2 = (k1) ? k1->op_sibling : NULL; + switch (expr->op_type) { + case OP_NULL: + if (k2 && k2->op_type == OP_READLINE + && (k2->op_flags & OPf_STACKED) + && ((k1->op_flags & OPf_WANT) == OPf_WANT_SCALAR)) + expr = newUNOP(OP_DEFINED, 0, expr); + break; + + case OP_SASSIGN: + if (k1->op_type == OP_READDIR + || k1->op_type == OP_GLOB + || k1->op_type == OP_EACH) + expr = newUNOP(OP_DEFINED, 0, expr); + break; + } } + if (!block) block = newOP(OP_NULL, 0); diff --git a/pod/perlop.pod b/pod/perlop.pod index 5d1aae7..e8818f7 100644 --- a/pod/perlop.pod +++ b/pod/perlop.pod @@ -1054,17 +1054,35 @@ Ordinarily you must assign that value to a variable, but there is one situation where an automatic assignment happens. I the input symbol is the only thing inside the conditional of a C or C loop, the value is automatically assigned to the variable -C<$_>. The assigned value is then tested to see if it is defined. -(This may seem like an odd thing to you, but you'll use the construct -in almost every Perl script you write.) Anyway, the following lines -are equivalent to each other: +C<$_>. In these loop constructs, the assigned value (whether assignment +is automatic or explcit) is then tested to see if it is defined. +The defined test avoids problems where line has a string value +that would be treated as false by perl e.g. "" or "0" with no trailing +newline. (This may seem like an odd thing to you, but you'll use the +construct in almost every Perl script you write.) Anyway, the following +lines are equivalent to each other: while (defined($_ = )) { print; } + while ($_ = ) { print; } while () { print; } for (;;) { print; } print while defined($_ = ); + print while ($_ = ); print while ; +and this also behaves similarly, but avoids the use of $_ : + + while (my $line = ) { print $line } + +If you really mean such values to terminate the loop they should be +tested for explcitly: + + while (($_ = ) ne '0') { ... } + while () { last unless $_; ... } + +In other boolean contexts CIE> without explcit C +test or comparison will solicit a warning if C<-w> is in effect. + The filehandles STDIN, STDOUT, and STDERR are predefined. (The filehandles C, C, and C will also work except in packages, where they would be interpreted as local identifiers rather @@ -1124,9 +1142,9 @@ Getopts modules or put a loop on the front like this: ... # code for each line } -The EE symbol will return FALSE only once. If you call it again after -this it will assume you are processing another @ARGV list, and if you -haven't set @ARGV, will input from STDIN. +The EE symbol will return C for end-of-file only once. +If you call it again after this it will assume you are processing another +@ARGV list, and if you haven't set @ARGV, will input from STDIN. If the string inside the angle brackets is a reference to a scalar variable (e.g., E$fooE), then that variable contains the name of the @@ -1174,9 +1192,12 @@ A glob evaluates its (embedded) argument only when it is starting a new list. All values must be read before it will start over. In a list context this isn't important, because you automatically get them all anyway. In a scalar context, however, the operator returns the next value -each time it is called, or a FALSE value if you've just run out. Again, -FALSE is returned only once. So if you're expecting a single value from -a glob, it is much better to say +each time it is called, or a C value if you've just run out. As +for filehandles an automatic C is generated when the glob +occurs in the test part of a C or C - because legal glob returns +(e.g. a file called F<0>) would otherwise terminate the loop. +Again, C is returned only once. So if you're expecting a single value +from a glob, it is much better to say ($file) = ; diff --git a/t/op/defins.t b/t/op/defins.t new file mode 100755 index 0000000..5dd614d --- /dev/null +++ b/t/op/defins.t @@ -0,0 +1,144 @@ +#!./perl -w + +# +# test auto defined() test insertion +# + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + $SIG{__WARN__} = sub { $warns++; warn $_[0] }; + print "1..14\n"; +} + +print "not " if $warns; +print "ok 1\n"; + +open(FILE,">./0"); +print FILE "1\n"; +print FILE "0"; +close(FILE); + +open(FILE,"<./0"); +my $seen = 0; +my $dummy; +while (my $name = ) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 2\n"; + +seek(FILE,0,0); +$seen = 0; +my $line = ''; +do + { + $seen++ if $line eq '0'; + } while ($line = ); + +print "not " unless $seen; +print "ok 3\n"; + + +seek(FILE,0,0); +$seen = 0; +while (($seen ? $dummy : $name) = ) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 4\n"; + +seek(FILE,0,0); +$seen = 0; +my %where; +while ($where{$seen} = ) + { + $seen++ if $where{$seen} eq '0'; + } +print "not " unless $seen; +print "ok 5\n"; + +opendir(DIR,'.'); +$seen = 0; +while (my $name = readdir(DIR)) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 6\n"; + +rewinddir(DIR); +$seen = 0; +$dummy = ''; +while (($seen ? $dummy : $name) = readdir(DIR)) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 7\n"; + +rewinddir(DIR); +$seen = 0; +while ($where{$seen} = readdir(DIR)) + { + $seen++ if $where{$seen} eq '0'; + } +print "not " unless $seen; +print "ok 8\n"; + +$seen = 0; +while (my $name = glob('*')) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 9\n"; + +$seen = 0; +$dummy = ''; +while (($seen ? $dummy : $name) = glob('*')) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 10\n"; + +$seen = 0; +while ($where{$seen} = glob('*')) + { + $seen++ if $where{$seen} eq '0'; + } +print "not " unless $seen; +print "ok 11\n"; + +unlink("./0"); + +my %hash = (0 => 1, 1 => 2); + +$seen = 0; +while (my $name = each %hash) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 12\n"; + +$seen = 0; +$dummy = ''; +while (($seen ? $dummy : $name) = each %hash) + { + $seen++ if $name eq '0'; + } +print "not " unless $seen; +print "ok 13\n"; + +$seen = 0; +while ($where{$seen} = each %hash) + { + $seen++ if $where{$seen} eq '0'; + } +print "not " unless $seen; +print "ok 14\n"; +