On 17/03/2011 20:27, Chap Harrison wrote:
On Mar 17, 2011, at 1:49 PM, C.DeRykus wrote:
On Mar 16, 9:58 am, c...@pobox.com (Chap Harrison) wrote:

[snip]

my $line = 'min=2 max = 12 weighted total= 20';
$line = 'min=2 max, = 12 weighted total= 20';
say $line;
my %param;

if ( $line and
      ($line !~
            s/
                 \G            # Begin where prev. left off
                 (?:           # Either a parameter...
                     (?:            # Keyword clause:
                         (\w+)      # KEYWORD (captured $1)
                         (?:        # Value clause:
                             \s*    #
                             =      # equal sign
                             \s*    #
                             (\w+)  # VALUE (captured $2)
                         )?         # Value clause is optional
                     )
                     \s*            # eat up any trailing ws
                 )             ###<-- moved
                 |             # ... or ...
                     $         # End of line.
             /                 # use captured to set %param
                 $param{uc $1} = ( $2 ? $2 : 1 ) if defined $1;
        /xeg
    ) ) {
     say "Syntax error: '$line'";
     while (my ($x, $y) = each %param) {say "$x='$y'";}
     exit;}

while (my ($x, $y) = each %param) {say "$x='$y'";}

I believe the problem is the "?   # Value clause is optional"
since, in the case of your badline with a ",", the regex will
consume 'max' and then ignore the , since ? means 0 or 1
instance.  Therefore the regex will still succeed and $2 will
be undefined. So the VALUE gets set to 1.


I agree - encountering the ',' causes the regex to think it's
encountered a keyword without a value. But why doesn't the *next*
iteration of the global substitution (which would begin at the ',')
fail, causing the if-statement to succeed and print "Syntax error"?

Perhaps I don't fully understand how the /g option works.... I
thought it would continue to "iterate" until either it reached the
end of the string (in which case the s/// would be considered to have
succeeded) or it could not match anything further (in which case it
would be considered to have failed).

Hi Chap

A s///g is 'successful' if it performs at least one substitution, in which case it will return the number of substitutions made. In your code, it will find as many key=value substrings as possible and replace them with just the value string.

The \G pattern is documented only in the case of m//g, which makes sense as it is defined in terms of a character position (pos) within the string where the last match ended. If a substitution is being made then it will also affect character positions, and so is similar to adding to or deleting from an array while iterating over it.

It is bad form to use the /e modifier to generate side-effects (just as it is wrong to do so with the map operator).

I believe a while loop is the proper way to go, but if you want to experiment with m//g I suggest something like this

  my %matches = $line =~ /\G\s*(\w+)(?:\s*=\s*(\w+))?\s*/gc;

which will pass all the 'key' and 'key=value' pairs to %matches. An invalid input will cause the match to terminate before the end of the string, so

  if (pos $line < length $line) {
    # code to handle bad input
  }

If a key has no corresponding value in the string it will appear in the hash with a value of undef, which should be defaulted to 1 like this

  foreach (values %matches) {
    $_ = 1 if not defined;
  }

I hope that helps a little.

Rob



--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to