Add /xx regex pattern modifier
authorKarl Williamson <khw@cpan.org>
Thu, 12 Jan 2017 18:07:47 +0000 (11:07 -0700)
committerKarl Williamson <khw@cpan.org>
Fri, 13 Jan 2017 18:44:35 +0000 (11:44 -0700)
This was first proposed in the thread starting at
http://www.nntp.perl.org/group/perl.perl5.porters/2014/09/msg219394.html

18 files changed:
MANIFEST
ext/re/re.pm
ext/re/t/reflags.t
pod/perlcheat.pod
pod/perldelta.pod
pod/perldiag.pod
pod/perlop.pod
pod/perlre.pod
pod/perlrecharclass.pod
pod/perlretut.pod
pod/perlstyle.pod
pod/perluniintro.pod
regcomp.c
regexp.h
t/re/keep_tabs.t [new file with mode: 0644]
t/re/re_tests
t/re/reg_mesg.t
toke.c

index d31ee1a..4745b04 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -5726,6 +5726,7 @@ t/porting/utils.t         Check that utility scripts still compile
 t/re/anyof.t                   See if bracketed char classes [...] compile properly
 t/re/charset.t                 See if regex modifiers like /d, /u work properly
 t/re/fold_grind.t              See if case folding works properly
+t/re/keep_tabs.t               Tests where \t can't be expanded.
 t/re/no_utf8_pm.t              Verify utf8.pm doesn't get loaded unless required
 t/re/overload.t                Test against string corruption in pattern matches on overloaded objects
 t/re/pat.t                     See if esoteric patterns work
index b924fd9..123408c 100644 (file)
@@ -4,7 +4,7 @@ package re;
 use strict;
 use warnings;
 
-our $VERSION     = "0.33";
+our $VERSION     = "0.34";
 our @ISA         = qw(Exporter);
 our @EXPORT_OK   = ('regmust',
                     qw(is_regexp regexp_pattern
@@ -23,6 +23,7 @@ my %reflags = (
     s => 1 << ($PMMOD_SHIFT + 1),
     i => 1 << ($PMMOD_SHIFT + 2),
     x => 1 << ($PMMOD_SHIFT + 3),
+   xx => 1 << ($PMMOD_SHIFT + 4),
     n => 1 << ($PMMOD_SHIFT + 5),
     p => 1 << ($PMMOD_SHIFT + 6),
     strict => 1 << ($PMMOD_SHIFT + 10),
@@ -112,7 +113,6 @@ sub bits {
     my $on = shift;
     my $bits = 0;
     my $turning_all_off = ! @_ && ! $on;
-    my %seen;   # Has flag already been seen?
     if ($turning_all_off) {
 
         # Pretend were called with certain parameters, which are best dealt
@@ -180,6 +180,7 @@ sub bits {
        } elsif ($s =~ s/^\///) {
            my $reflags = $^H{reflags} || 0;
            my $seen_charset;
+            my $x_count = 0;
            while ($s =~ m/( . )/gx) {
                 local $_ = $1;
                if (/[adul]/) {
@@ -225,7 +226,19 @@ sub bits {
                                         && $^H{reflags_charset} == $reflags{$_};
                    }
                } elsif (exists $reflags{$_}) {
-                    $seen{$_}++;
+                    if ($_ eq 'x') {
+                        $x_count++;
+                        if ($x_count > 2) {
+                           require Carp;
+                            Carp::carp(
+                            qq 'The "x" flag may only appear a maximum of twice'
+                            );
+                        }
+                        elsif ($x_count == 2) {
+                            $_ = 'xx';  # First time through got the /x
+                        }
+                    }
+
                     $on
                      ? $reflags |= $reflags{$_}
                      : ($reflags &= ~$reflags{$_});
@@ -247,10 +260,6 @@ sub bits {
                        ")");
        }
     }
-    if (exists $seen{'x'} && $seen{'x'} > 1) {
-        require Carp;
-        Carp::croak("Only one /x regex modifier is allowed");
-    }
 
     if ($turning_all_off) {
         _load_unload(0);
index a481c98..595b4b2 100644 (file)
@@ -11,7 +11,7 @@ BEGIN {
 
 use strict;
 
-use Test::More tests => 67;
+use Test::More tests => 74;
 
 my @flags = qw( a d l u );
 
@@ -24,10 +24,19 @@ ok "Foo" !~ /(??{'foo'})/, 'no re "/i" (??{})';
 use re '/x';
 ok "foo" =~ / foo /, 'use re "/x"';
 ok "foo" =~ / (??{' foo '}) /, 'use re "/x" (??{})';
+like " ", qr/[a b]/, 'use re "/x" [a b]';
 no re '/x';
 ok "foo" !~ / foo /, 'no re "/x"';
 ok "foo" !~ /(??{' foo '})/, 'no re "/x" (??{})';
 ok "foo" !~ / (??{'foo'}) /, 'no re "/x" (??{})';
+use re '/xx';
+ok "foo" =~ / foo /, 'use re "/xx"';
+ok "foo" =~ / (??{' foo '}) /, 'use re "/xx" (??{})';
+unlike " ", qr/[a b]/, 'use re "/xx" [a b] # Space in [] gobbled up';
+no re '/xx';
+ok "foo" !~ / foo /, 'no re "/xx"';
+ok "foo" !~ /(??{' foo '})/, 'no re "/xx" (??{})';
+ok "foo" !~ / (??{'foo'}) /, 'no re "/xx" (??{})';
 use re '/s';
 ok "\n" =~ /./, 'use re "/s"';
 ok "\n" =~ /(??{'.'})/, 'use re "/s" (??{})';
@@ -178,8 +187,8 @@ is qr//, '(?^:)', 'no re "/aai"';
     "warning with eval \"use re \"/amaa\"";
 
   $w = "";
-  eval "use re '/xamax'";
-  like $@, qr/Only one \/x regex modifier is allowed/,
-    "error with eval \"use re \"/xamax\"";
+  eval "use re '/xamaxx'";
+  like $w, qr/The "x" flag may only appear a maximum of twice/,
+    "warning with eval \"use re \"/xamaxx\"";
 
 }
index 6e4e919..99a8dfc 100644 (file)
@@ -41,7 +41,7 @@ already be overwhelming.
   &&              /i case insensitive   ^      string begin
   || //           /m line based ^$      $      str end (bfr \n)
   .. ...          /s . includes \n      +      one or more
-  ?:              /x ignore wh.space    *      zero or more
+  ?:              /x /xx ign. wh.space  *      zero or more
   = += last goto  /p preserve           ?      zero or one
   , =>            /a ASCII    /aa safe  {3,7}  repeat in range
   list ops        /l locale   /d  dual  |      alternation
index 460d118..86b7e9a 100644 (file)
@@ -25,7 +25,14 @@ XXX New core language features go here.  Summarize user-visible core language
 enhancements.  Particularly prominent performance optimisations could go
 here, but most should go in the L</Performance Enhancements> section.
 
-[ List each enhancement as a =head2 entry ]
+=head2 New regular expression modifier C</xx>
+
+Specifying two C<x> characters to modify a regular expression pattern
+does everything that a single one does, but additionally TAB and SPACE
+characters within a bracketed character class are generally ignored and
+can be added to improve readability, like
+S<C</[ ^ A-Z d-f p-x ]/xx>>.  Details are at
+L<perlre/E<sol>x and E<sol>xx>.
 
 =head1 Security
 
index fe5ff9b..7d6675c 100644 (file)
@@ -4230,14 +4230,6 @@ C<sysread()>ing a file, or when seeking past the end of a scalar opened
 for I/O (in anticipation of future reads and to imitate the behavior
 with real files).
 
-=item Only one /x regex modifier is allowed
-
-=item Only one /x regex modifier is allowed in regex; marked by <-- HERE in m/%s/
-
-(F) You used the C</x> regular expression pattern modifier at least twice in a
-string of modifiers.  This has been made illegal, in order to allow future
-extensions to the Perl language.
-
 =item %s() on unopened %s
 
 (W unopened) An I/O operation was attempted on a filehandle that was
index 82dca55..3cf9db6 100644 (file)
@@ -1743,7 +1743,9 @@ Options (specified by the following modifiers) are:
     m  Treat string as multiple lines.
     s  Treat string as single line. (Make . match a newline)
     i  Do case-insensitive pattern matching.
-    x  Use extended regular expressions.
+    x   Use extended regular expressions; specifying two
+        x's means \t and the SPACE character are ignored within
+        square-bracketed character classes
     p  When matching preserve a copy of the matched string so
         that ${^PREMATCH}, ${^MATCH}, ${^POSTMATCH} will be
         defined (ignored starting in v5.20) as these are always
index 0b1ae4c..e3fc62d 100644 (file)
@@ -86,11 +86,11 @@ inverted, which otherwise could be highly confusing.  See
 L<perlrecharclass/Bracketed Character Classes>, and
 L<perlrecharclass/Negation>.
 
-=item B<C<x>>
+=item B<C<x>> and B<C<xx>>
 X</x>
 
 Extend your pattern's legibility by permitting whitespace and comments.
-Details in L</"/x">
+Details in L</E<sol>x and  E<sol>xx>
 
 =item B<C<p>>
 X</p> X<regex, preserve> X<regexp, preserve>
@@ -164,9 +164,9 @@ the C<(?...)> construct, see L</Extended Patterns> below.
 Some of the modifiers require more explanation than given in the
 L</Overview> above.
 
-=head4 /x
+=head4 C</x> and  C</xx>
 
-C</x> tells
+A single C</x> tells
 the regular expression parser to ignore most whitespace that is neither
 backslashed nor within a bracketed character class.  You can use this to
 break up your regular expression into more readable parts.
@@ -193,6 +193,20 @@ A common pitfall is to forget that C<#> characters begin a comment under
 C</x> and are not matched literally.  Just keep that in mind when trying
 to puzzle out why a particular C</x> pattern isn't working as expected.
 
+Starting in Perl v5.26, if the modifier has a second C<x> within it,
+it does everything that a single C</x> does, but additionally
+non-backslashed SPACE and TAB characters within bracketed character
+classes are also generally ignored, and hence can be added to make the
+classes more readable.
+
+    / [d-e g-i 3-7]/xx
+    /[ ! @ " # $ % ^ & * () = ? <> ' ]/xx
+
+may be easier to grasp than the squashed equivalents
+
+    /[d-eg-i3-7]/
+    /[!@"#$%^&*()=?<>']/
+
 Taken together, these features go a long way towards
 making Perl's regular expressions more readable.  Here's an example:
 
@@ -1137,7 +1151,21 @@ same scope containing the same modifier, so that
 
 matches all of C<foobar> case insensitively, but uses C</m> rules for
 only the C<foo> portion.  The C<a> flag overrides C<aa> as well;
-likewise C<aa> overrides C<a>.
+likewise C<aa> overrides C<a>.  The same goes for C<x> and C<xx>.
+Hence, in
+
+    /(?-x)foo/xx
+
+both C</x> and C</xx> are turned off during matching C<foo>.  And in
+
+    /(?x)foo/x
+
+C</x> but NOT C</xx> is turned on for matching C<foo>.  (One might
+mistakenly think that since the inner C<(?x)> is already in the scope of
+C</x>, that the result would effectively be the sum of them, yielding
+C</xx>.  It doesn't work that way.)  Similarly, doing something like
+C<(?xx-x)foo> turns off all C<x> behavior for matching C<foo>, it is not
+that you subtract 1 C<x> from 2 to get 1 C<x> remaining.
 
 Any of these modifiers can be set to apply globally to all regular
 expressions compiled within the scope of a C<use re>.  See
@@ -1193,9 +1221,10 @@ is equivalent to the more verbose
 Note that any C<()> constructs enclosed within this one will still
 capture unless the C</n> modifier is in effect.
 
-
 Like the L</(?adlupimnsx-imnsx)> construct, C<aa> and C<a> override each
-other.
+other, as do C<xx> and C<x>.  They are not additive.  So, doing
+something like C<(?xx-x:foo)> turns off all C<x> behavior for matching
+C<foo>.
 
 Starting in Perl 5.14, a C<"^"> (caret or circumflex accent) immediately
 after the C<"?"> is a shorthand equivalent to C<d-imnsx>.  Any positive
index 93bb2e5..1c07632 100644 (file)
@@ -576,6 +576,29 @@ Examples:
                       #  containing just [, and the character class is
                       #  followed by a ].
 
+=head3 Bracketed Character Classes and the C</xx> pattern modifier
+
+Normally SPACE and TAB characters have no special meaning inside a
+bracketed character class; they are just added to the list of characters
+matched by the class.  But if the L<C</xx>|perlre/E<sol>x and E<sol>xx>
+pattern modifier is in effect, they are generally ignored and can be
+added to improve readability.  They can't be added in the middle of a
+single construct:
+
+ / [ \x{10 FFFF} ] /xx  # WRONG!
+
+The SPACE in the middle of the hex constant is illegal.
+
+To specify a literal SPACE character, you can escape it with a
+backslash, like:
+
+ /[ a e i o u \  ]/xx
+
+This matches the English vowels plus the SPACE character.
+
+For clarity, you should already have been using C<\t> to specify a
+literal tab, and C<\t> is unaffected by C</xx>.
+
 =head3 Character Ranges
 
 It is not uncommon to want to match a range of characters. Luckily, instead
@@ -1016,7 +1039,7 @@ We can extend the example above:
 This matches digits that are in either the Thai or Laotian scripts.
 
 Notice the white space in these examples.  This construct always has
-the C<E<sol>x> modifier turned on within it.
+the C<E<sol>xx> modifier turned on within it.
 
 The available binary operators are:
 
@@ -1061,18 +1084,9 @@ C<\N{...}>, etc.)
 
 This last example shows the use of this construct to specify an ordinary
 bracketed character class without additional set operations.  Note the
-white space within it; a limited version of C<E<sol>x> is turned on even
-within bracketed character classes, with only the SPACE and TAB (C<\t>)
-characters allowed, and no comments.  Hence,
-
- (?[ [#] ])
+white space within it.  This is allowed because C<E<sol>xx> is
+automatically turned on within this construct.
 
-matches the literal character "#".  To specify a literal white space character,
-you can escape it with a backslash, like:
-
- /(?[ [ a e i o u \  ] ])/
-
-This matches the English vowels plus the SPACE character.
 All the other escapes accepted by normal bracketed character classes are
 accepted here as well; but unrecognized escapes that generate warnings
 in normal classes are fatal errors here.
index d72d52d..9c7ab56 100644 (file)
@@ -1516,7 +1516,25 @@ could be factored out:
       ( [eE] [+-]? \d+ )?  # finally, optionally match an exponent
    $/x;
 
-or written in the compact form,
+Starting in Perl v5.26, specifying C</xx> changes the square-bracketed
+portions of a pattern to ignore tabs and space characters unless they
+are escaped by preceding them with a backslash.  So, we could write
+
+   /^
+      [ + - ]?\ *   # first, match an optional sign
+      (             # then match integers or f.p. mantissas:
+          \d+       # start out with a ...
+          (
+              \.\d* # mantissa of the form a.b or a.
+          )?        # ? takes care of integers of the form a
+         |\.\d+     # mantissa of the form .b
+      )
+      ( [ e E ] [ + - ]? \d+ )?  # finally, optionally match an exponent
+   $/xx;
+
+This doesn't really improve the legibility of this example, but it's
+available in case you want it.  Squashing the pattern down to the
+compact form, we have
 
     /^[+-]?\ *(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
 
@@ -2379,7 +2397,7 @@ enclosed in parentheses up to two levels deep.  Then the following
 regexp matches:
 
     $x = "abc(de(fg)h";  # unbalanced parentheses
-    $x =~ /\( ( [^()]+ | \([^()]*\) )+ \)/x;
+    $x =~ /\( ( [ ^ () ]+ | \( [ ^ () ]* \) )+ \)/xx;
 
 The regexp matches an open parenthesis, one or more copies of an
 alternation, and a close parenthesis.  The alternation is two-way, with
@@ -2393,7 +2411,7 @@ was no match possible.  To prevent the exponential blowup, we need to
 prevent useless backtracking at some point.  This can be done by
 enclosing the inner quantifier as an independent subexpression:
 
-    $x =~ /\( ( (?>[^()]+) | \([^()]*\) )+ \)/x;
+    $x =~ /\( ( (?> [ ^ () ]+ ) | \([ ^ () ]* \) )+ \)/xx;
 
 Here, C<< (?>[^()]+) >> breaks the degeneracy of string partitioning
 by gobbling up as much of the string as possible and keeping it.   Then
index 37dfaaf..5c25345 100644 (file)
@@ -210,8 +210,9 @@ function should not be used outside the package that defined it.
 
 =item *
 
-If you have a really hairy regular expression, use the C</x> modifier and
-put in some whitespace to make it look a little less like line noise.
+If you have a really hairy regular expression, use the C</x>  or C</xx>
+modifiers and put in some whitespace to make it look a little less like
+line noise.
 Don't use slash as a delimiter when your regexp has slashes or backslashes.
 
 =item *
index 5b571fb..ef4d07d 100644 (file)
@@ -645,7 +645,7 @@ Starting in v5.22, you can use Unicode code points as the end points of
 regular expression pattern character ranges, and the range will include
 all Unicode code points that lie between those end points, inclusive.
 
- qr/ [\N{U+03]-\N{U+20}] /x
+ qr/ [ \N{U+03} - \N{U+20} ] /xx
 
 includes the code points
 C<\N{U+03}>, C<\N{U+04}>, ..., C<\N{U+20}>.
index 93fde35..2114773 100644 (file)
--- a/regcomp.c
+++ b/regcomp.c
@@ -6516,8 +6516,12 @@ S_compile_runtime_code(pTHX_ RExC_state_t * const pRExC_state,
            *p++ = pat[s];
        }
        *p++ = '\'';
-       if (pRExC_state->pm_flags & RXf_PMf_EXTENDED)
+       if (pRExC_state->pm_flags & RXf_PMf_EXTENDED) {
            *p++ = 'x';
+            if (pRExC_state->pm_flags & RXf_PMf_EXTENDED_MORE) {
+                *p++ = 'x';
+            }
+        }
        *p++ = '\0';
        DEBUG_COMPILE_r({
             Perl_re_printf( aTHX_
@@ -7162,7 +7166,7 @@ Perl_re_op_compile(pTHX_ SV ** const patternp, int pat_count,
                                                    == REG_RUN_ON_COMMENT_SEEN);
        U8 reganch = (U8)((r->extflags & RXf_PMf_STD_PMMOD)
                            >> RXf_PMf_STD_PMMOD_SHIFT);
-       const char *fptr = STD_PAT_MODS;        /*"msixn"*/
+       const char *fptr = STD_PAT_MODS;        /*"msixxn"*/
        char *p;
 
         /* We output all the necessary flags; we never output a minus, as all
@@ -10426,18 +10430,23 @@ S_parse_lparen_question_flags(pTHX_ RExC_state_t *pRExC_state)
                 }
                 flagsp = &negflags;
                 wastedflags = 0;  /* reset so (?g-c) warns twice */
+                x_mod_count = 0;
                 break;
             case ':':
             case ')':
+
+                if ((posflags & (RXf_PMf_EXTENDED|RXf_PMf_EXTENDED_MORE)) == RXf_PMf_EXTENDED) {
+                    negflags |= RXf_PMf_EXTENDED_MORE;
+                }
                 RExC_flags |= posflags;
+
+                if (negflags & RXf_PMf_EXTENDED) {
+                    negflags |= RXf_PMf_EXTENDED_MORE;
+                }
                 RExC_flags &= ~negflags;
                 set_regex_charset(&RExC_flags, cs);
 
-                if (UNLIKELY((x_mod_count) > 1)) {
-                    vFAIL("Only one /x regex modifier is allowed");
-                }
                 return;
-                /*NOTREACHED*/
             default:
               fail_modifiers:
                 RExC_parse += SKIP_IF_CHAR(RExC_parse);
@@ -15788,8 +15797,10 @@ S_regclass(pTHX_ RExC_state_t *pRExC_state, I32 *flagp, U32 depth,
                                        character; used under /i */
     UV n;
     char * stop_ptr = RExC_end;    /* where to stop parsing */
-    const bool skip_white = cBOOL(ret_invlist); /* ignore unescaped white
-                                                   space? */
+
+    /* ignore unescaped whitespace? */
+    const bool skip_white = cBOOL(   ret_invlist
+                                  || (RExC_flags & RXf_PMf_EXTENDED_MORE));
 
     /* Unicode properties are stored in a swash; this holds the current one
      * being parsed.  If this swash is the only above-latin1 component of the
index 7351afd..08b4fc3 100644 (file)
--- a/regexp.h
+++ b/regexp.h
@@ -278,18 +278,26 @@ and check for NULL.
 
 #include "op_reg_common.h"
 
-#define RXf_PMf_STD_PMMOD      (RXf_PMf_MULTILINE|RXf_PMf_SINGLELINE|RXf_PMf_FOLD|RXf_PMf_EXTENDED|RXf_PMf_NOCAPTURE)
+#define RXf_PMf_STD_PMMOD      (RXf_PMf_MULTILINE|RXf_PMf_SINGLELINE|RXf_PMf_FOLD|RXf_PMf_EXTENDED|RXf_PMf_EXTENDED_MORE|RXf_PMf_NOCAPTURE)
 
 #define CASE_STD_PMMOD_FLAGS_PARSE_SET(pmfl, x_count)                       \
     case IGNORE_PAT_MOD:    *(pmfl) |= RXf_PMf_FOLD;       break;           \
     case MULTILINE_PAT_MOD: *(pmfl) |= RXf_PMf_MULTILINE;  break;           \
     case SINGLE_PAT_MOD:    *(pmfl) |= RXf_PMf_SINGLELINE; break;           \
-    case XTENDED_PAT_MOD:   *(pmfl) |= RXf_PMf_EXTENDED; (x_count)++; break;\
+    case XTENDED_PAT_MOD:   if (x_count == 0) {                             \
+                                *(pmfl) |= RXf_PMf_EXTENDED;                \
+                                *(pmfl) &= ~RXf_PMf_EXTENDED_MORE;          \
+                            }                                               \
+                            else {                                          \
+                                *(pmfl) |= RXf_PMf_EXTENDED                 \
+                                          |RXf_PMf_EXTENDED_MORE;           \
+                            }                                               \
+                            (x_count)++; break;                             \
     case NOCAPTURE_PAT_MOD: *(pmfl) |= RXf_PMf_NOCAPTURE; break;
 
 /* Note, includes charset ones, assumes 0 is the default for them */
 #define STD_PMMOD_FLAGS_CLEAR(pmfl)                        \
-    *(pmfl) &= ~(RXf_PMf_FOLD|RXf_PMf_MULTILINE|RXf_PMf_SINGLELINE|RXf_PMf_EXTENDED|RXf_PMf_CHARSET|RXf_PMf_NOCAPTURE)
+    *(pmfl) &= ~(RXf_PMf_FOLD|RXf_PMf_MULTILINE|RXf_PMf_SINGLELINE|RXf_PMf_EXTENDED|RXf_PMf_EXTENDED_MORE|RXf_PMf_CHARSET|RXf_PMf_NOCAPTURE)
 
 /* chars and strings used as regex pattern modifiers
  * Singular is a 'c'har, plural is a "string"
diff --git a/t/re/keep_tabs.t b/t/re/keep_tabs.t
new file mode 100644 (file)
index 0000000..ec986c4
--- /dev/null
@@ -0,0 +1,29 @@
+# This file contains tests where \t characters should not be expanded into
+# spaces.
+
+BEGIN {
+    chdir 't' if -d 't';
+    require './test.pl';
+}
+
+{
+      like("\t", qr/[a b]/x, '\t not ignored under /x');
+    unlike("\t", qr/[a b]/xx, '\t ignored under /xx');
+    like("a", qr/[a    b]/xx, '"a" matches qr/[a       b]/xx');
+    like("b", qr/[a    b]/xx, '"b" matches qr/[a       b]/xx');
+    like("\t", qr/[a\  b]/xx, '"\t" matches qr/[a\     b]/xx');
+    like("a", qr/[a\   b]/xx, '"a" matches qr/[a\      b]/xx');
+    like("b", qr/[a\   b]/xx, '"b" matches qr/[a\      b]/xx');
+
+      like("\t", qr/(?x:[a     b])/, '\t not ignored under /x');
+    unlike("\t", qr/(?xx:[a    b])/, '\t ignored under /xx');
+    like("a", qr/(?xx:[a       b])/, '"a" matches qr/(?xx:[a   b])/');
+    like("b", qr/(?xx:[a       b])/, '"b" matches qr/(?xx:[a   b])/');
+    like("\t", qr/(?xx:[a\     b])/, '"\t" matches qr/(?xx:[a\ b])/');
+    like("a", qr/(?xx:[a\      b])/, '"a" matches qr/(?xx:[a\  b])/');
+    like("b", qr/(?xx:[a\      b])/, '"b" matches qr/(?xx:[a\  b])/');
+}
+
+done_testing;
+
+# ex softtabstop=0 noexpandtab
index e8a7fa9..2653b94 100644 (file)
@@ -1977,5 +1977,17 @@ AB\s+\x{100}     AB \x{100}X     y       -       -
 (^(?:(\d)x)?\d$)       1       y       [$1-$2] [1-]            #  make sure that we reset capture buffers properly (from regtry)
 (X{2,}[-X]{1,4}){3,}X{2,}      XXX-XXX-XXX--   n       -       -       # [perl #130307]
 
+/[a b]/x       \N{SPACE}       yS      $&                      # Note a space char here
+/[a b]/xx      \N{SPACE}       n       -       -
+/[a\ b]/xx     \N{SPACE}       y       $&                      # Note a space char here
+/[ ^ a b ]/xx  a       n       -       -
+/[ ^ a b ]/xx  b       n       -       -
+/[ ^ a b ]/xx  A       y       $&      A
+/(?x:[a b])/xx \N{SPACE}       yS      $&              # Note a space char here
+/(?xx:[a b])/x \N{SPACE}       n       -       -
+/(?x)[a b]/xx  \N{SPACE}       yS      $&              # Note a space char here
+/(?xx)[a b]/x  \N{SPACE}       n       -       -
+/(?-x:[a b])/xx        \N{SPACE}       yS      $&              # Note a space char here
+
 # Keep these lines at the end of the file
 # vim: softtabstop=0 noexpandtab
index 52bec7a..7aa430e 100644 (file)
@@ -274,10 +274,10 @@ my @death =
  '/\A{/' => 'Unescaped left brace in regex is illegal here {#} m/\A{{#}/',
  '/:{4,a}/' => 'Unescaped left brace in regex is illegal here {#} m/:{{#}4,a}/',
  '/xa{3\,4}y/' => 'Unescaped left brace in regex is illegal here {#} m/xa{{#}3\,4}y/',
- '/abc/xix' => 'Only one /x regex modifier is allowed',
- '/(?xmsixp:abc)/' => 'Only one /x regex modifier is allowed {#} m/(?xmsixp{#}:abc)/',
- '/(?xmsixp)abc/' => 'Only one /x regex modifier is allowed {#} m/(?xmsixp{#})abc/',
- '/(?xxxx:abc)/' => 'Only one /x regex modifier is allowed {#} m/(?xxxx{#}:abc)/',
+ '/abc/xix' => "",
+ '/(?xmsixp:abc)/' => "",
+ '/(?xmsixp)abc/' => "",
+ '/(?xxxx:abc)/' => "",
  '/(?<=/' => 'Sequence (?... not terminated {#} m/(?<={#}/',                        # [perl #128170]
 
 );
diff --git a/toke.c b/toke.c
index e6dad0a..3b36404 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -9508,10 +9508,6 @@ S_scan_pat(pTHX_ char *start, I32 type)
                       "Use of /c modifier is meaningless without /g" );
     }
 
-    if (UNLIKELY((x_mod_count) > 1)) {
-        yyerror("Only one /x regex modifier is allowed");
-    }
-
     PL_lex_op = (OP*)pm;
     pl_yylval.ival = OP_MATCH;
     return s;
@@ -9566,10 +9562,6 @@ S_scan_subst(pTHX_ char *start)
        }
     }
 
-    if (UNLIKELY((x_mod_count) > 1)) {
-        yyerror("Only one /x regex modifier is allowed");
-    }
-
     if ((pm->op_pmflags & PMf_CONTINUE)) {
         Perl_ck_warner(aTHX_ packWARN(WARN_REGEXP), "Use of /c modifier is meaningless in s///" );
     }