@@ -310,7 +310,7 @@ def checkgroupname(self, name, offset):
310310 msg = "bad character in group name %r" % name
311311 raise self .error (msg , len (name ) + offset )
312312
313- def _property_escape (source , escape , in_set = False ):
313+ def _property_escape (source , escape ):
314314 # handle \p{...} and \P{...} (UTS #18 1.2.4, "Property Syntax")
315315 from . import _properties
316316 if not source .match ('{' ):
@@ -320,10 +320,6 @@ def _property_escape(source, escape, in_set=False):
320320 if code is None :
321321 raise source .error ("unknown property name %r" % name ,
322322 len (name ) + len (r'\p{}' ))
323- if in_set and code [1 ][0 ] == (NEGATE , None ):
324- # A negated multi-range property cannot be a member of a set.
325- raise source .error ("bad escape %s in character class" % escape ,
326- len (name ) + len (r'\p{}' ))
327323 return code
328324
329325def _class_escape (source , escape ):
@@ -369,7 +365,7 @@ def _class_escape(source, escape):
369365 len (charname ) + len (r'\N{}' )) from None
370366 return LITERAL , c
371367 elif c in "pP" and source .istext :
372- return _property_escape (source , escape , in_set = True )
368+ return _property_escape (source , escape )
373369 elif c in OCTDIGITS :
374370 # octal escape (up to three digits)
375371 escape += source .getwhile (2 , OCTDIGITS )
@@ -569,11 +565,15 @@ def _difference(left, right, state):
569565# with the next operand.
570566_SETOPS = {'||' : _union , '&&' : _intersect , '--' : _difference }
571567
572- def _operand_elements (set , compound ):
573- # The operand's elements: a standalone nested set, else the member union.
568+ def _operand_elements (set , compound , negated , state ):
569+ # The operand's elements: a standalone nested set, else the member union,
570+ # with any negated-property members alternated in (see addmember).
574571 if compound is not None :
575572 return compound
576- return [_charset_node (_uniq (set ))]
573+ result = [_charset_node (_uniq (set ))] if set or not negated else None
574+ for neg in negated :
575+ result = [neg ] if result is None else _union (result , [neg ], state )
576+ return result
577577
578578def _parse_operand (source , state , nested , here , allow_nested ):
579579 # Read one operand, stopping at a set operator or the closing ']'. An
@@ -586,10 +586,15 @@ def _parse_operand(source, state, nested, here, allow_nested):
586586 sourcematch = source .match
587587 set = []
588588 setappend = set .append
589+ negated = [] # \P{...} negated-range props, alternated in at the end
589590 def addmember (code ):
590- # Flatten a \p{...} property's IN into the member set.
591+ # Flatten a \p{...} property's IN into the member set; a negated one is a
592+ # complemented charset, set aside to _union in (it can't join the union).
591593 if code [0 ] is IN :
592- set .extend (code [1 ])
594+ if code [1 ][0 ][0 ] is NEGATE :
595+ negated .append (code )
596+ else :
597+ set .extend (code [1 ])
593598 else :
594599 setappend (code )
595600 compound = None # elements of a standalone nested-set operand
@@ -602,9 +607,9 @@ def addmember(code):
602607 if this is None :
603608 raise source .error ("unterminated character set" ,
604609 source .tell () - here )
605- if set or compound is not None :
610+ if set or compound is not None or negated :
606611 if this == "]" :
607- return _operand_elements (set , compound ), None
612+ return _operand_elements (set , compound , negated , state ), None
608613 if this in '-&|~' and source .next == this :
609614 if this == '~' :
610615 import warnings
@@ -616,7 +621,7 @@ def addmember(code):
616621 else :
617622 # '--', '&&' or '||' ends this operand and starts the next.
618623 sourceget () # consume the second operator character
619- return _operand_elements (set , compound ), this + this
624+ return _operand_elements (set , compound , negated , state ), this + this
620625 if this [0 ] == "\\ " :
621626 code1 = _class_escape (source , this )
622627 else :
@@ -636,12 +641,12 @@ def addmember(code):
636641 # A trailing '-' is a literal.
637642 addmember (code1 )
638643 setappend ((LITERAL , _ord ("-" )))
639- return [ _charset_node ( _uniq ( set ))] , None
644+ return _operand_elements ( set , None , negated , state ) , None
640645 if that == "-" :
641646 # 'X--': difference, not a range. '--' after a single member
642647 # lands here because the range probe consumed the first '-'.
643648 addmember (code1 )
644- return [ _charset_node ( _uniq ( set ))] , "--"
649+ return _operand_elements ( set , None , negated , state ) , "--"
645650 if that [0 ] == "\\ " :
646651 code2 = _class_escape (source , that )
647652 else :
0 commit comments