package syntax

import (
	
	
	
	
	
	
)

type RegexOptions int32

const (
	IgnoreCase              RegexOptions = 0x0001 // "i"
	Multiline                            = 0x0002 // "m"
	ExplicitCapture                      = 0x0004 // "n"
	Compiled                             = 0x0008 // "c"
	Singleline                           = 0x0010 // "s"
	IgnorePatternWhitespace              = 0x0020 // "x"
	RightToLeft                          = 0x0040 // "r"
	Debug                                = 0x0080 // "d"
	ECMAScript                           = 0x0100 // "e"
	RE2                                  = 0x0200 // RE2 compat mode
	Unicode                              = 0x0400 // "u"
)

func optionFromCode( rune) RegexOptions {
	// case-insensitive
	switch  {
	case 'i', 'I':
		return IgnoreCase
	case 'r', 'R':
		return RightToLeft
	case 'm', 'M':
		return Multiline
	case 'n', 'N':
		return ExplicitCapture
	case 's', 'S':
		return Singleline
	case 'x', 'X':
		return IgnorePatternWhitespace
	case 'd', 'D':
		return Debug
	case 'e', 'E':
		return ECMAScript
	case 'u', 'U':
		return Unicode
	default:
		return 0
	}
}

// An Error describes a failure to parse a regular expression
// and gives the offending expression.
type Error struct {
	Code ErrorCode
	Expr string
	Args []interface{}
}

func ( *Error) () string {
	if len(.Args) == 0 {
		return "error parsing regexp: " + .Code.String() + " in `" + .Expr + "`"
	}
	return "error parsing regexp: " + fmt.Sprintf(.Code.String(), .Args...) + " in `" + .Expr + "`"
}

// An ErrorCode describes a failure to parse a regular expression.
type ErrorCode string

const (
	// internal issue
	ErrInternalError ErrorCode = "regexp/syntax: internal error"
	// Parser errors
	ErrUnterminatedComment        = "unterminated comment"
	ErrInvalidCharRange           = "invalid character class range"
	ErrInvalidRepeatSize          = "invalid repeat count"
	ErrInvalidUTF8                = "invalid UTF-8"
	ErrCaptureGroupOutOfRange     = "capture group number out of range"
	ErrUnexpectedParen            = "unexpected )"
	ErrMissingParen               = "missing closing )"
	ErrMissingBrace               = "missing closing }"
	ErrInvalidRepeatOp            = "invalid nested repetition operator"
	ErrMissingRepeatArgument      = "missing argument to repetition operator"
	ErrConditionalExpression      = "illegal conditional (?(...)) expression"
	ErrTooManyAlternates          = "too many | in (?()|)"
	ErrUnrecognizedGrouping       = "unrecognized grouping construct: (%v"
	ErrInvalidGroupName           = "invalid group name: group names must begin with a word character and have a matching terminator"
	ErrCapNumNotZero              = "capture number cannot be zero"
	ErrUndefinedBackRef           = "reference to undefined group number %v"
	ErrUndefinedNameRef           = "reference to undefined group name %v"
	ErrAlternationCantCapture     = "alternation conditions do not capture and cannot be named"
	ErrAlternationCantHaveComment = "alternation conditions cannot be comments"
	ErrMalformedReference         = "(?(%v) ) malformed"
	ErrUndefinedReference         = "(?(%v) ) reference to undefined group"
	ErrIllegalEndEscape           = "illegal \\ at end of pattern"
	ErrMalformedSlashP            = "malformed \\p{X} character escape"
	ErrIncompleteSlashP           = "incomplete \\p{X} character escape"
	ErrUnknownSlashP              = "unknown unicode category, script, or property '%v'"
	ErrUnrecognizedEscape         = "unrecognized escape sequence \\%v"
	ErrMissingControl             = "missing control character"
	ErrUnrecognizedControl        = "unrecognized control character"
	ErrTooFewHex                  = "insufficient hexadecimal digits"
	ErrInvalidHex                 = "hex values may not be larger than 0x10FFFF"
	ErrMalformedNameRef           = "malformed \\k<...> named back reference"
	ErrBadClassInCharRange        = "cannot include class \\%v in character range"
	ErrUnterminatedBracket        = "unterminated [] set"
	ErrSubtractionMustBeLast      = "a subtraction must be the last element in a character class"
	ErrReversedCharRange          = "[%c-%c] range in reverse order"
)

func ( ErrorCode) () string {
	return string()
}

type parser struct {
	stack         *regexNode
	group         *regexNode
	alternation   *regexNode
	concatenation *regexNode
	unit          *regexNode

	patternRaw string
	pattern    []rune

	currentPos  int
	specialCase *unicode.SpecialCase

	autocap  int
	capcount int
	captop   int
	capsize  int

	caps     map[int]int
	capnames map[string]int

	capnumlist  []int
	capnamelist []string

	options         RegexOptions
	optionsStack    []RegexOptions
	ignoreNextParen bool
}

const (
	maxValueDiv10 int = math.MaxInt32 / 10
	maxValueMod10     = math.MaxInt32 % 10
)

// Parse converts a regex string into a parse tree
func ( string,  RegexOptions) (*RegexTree, error) {
	 := parser{
		options: ,
		caps:    make(map[int]int),
	}
	.setPattern()

	if  := .countCaptures();  != nil {
		return nil, 
	}

	.reset()
	,  := .scanRegex()

	if  != nil {
		return nil, 
	}
	 := &RegexTree{
		root:       ,
		caps:       .caps,
		capnumlist: .capnumlist,
		captop:     .captop,
		Capnames:   .capnames,
		Caplist:    .capnamelist,
		options:    ,
	}

	if .options&Debug > 0 {
		os.Stdout.WriteString(.Dump())
	}

	return , nil
}

func ( *parser) ( string) {
	.patternRaw = 
	.pattern = make([]rune, 0, len())

	//populate our rune array to handle utf8 encoding
	for ,  := range  {
		.pattern = append(.pattern, )
	}
}
func ( *parser) ( ErrorCode,  ...interface{}) error {
	return &Error{Code: , Expr: .patternRaw, Args: }
}

func ( *parser) (,  int) {
	if ,  := .caps[]; ! {
		// the rhs of the hashtable isn't used in the parser
		.caps[] = 
		.capcount++

		if .captop <=  {
			if  == math.MaxInt32 {
				.captop = 
			} else {
				.captop =  + 1
			}
		}
	}
}

func ( *parser) ( string,  int) {
	if .capnames == nil {
		.capnames = make(map[string]int)
	}

	if ,  := .capnames[]; ! {
		.capnames[] = 
		.capnamelist = append(.capnamelist, )
	}
}

func ( *parser) () {
	if .capnames != nil {
		for ,  := range .capnamelist {
			for .isCaptureSlot(.autocap) {
				.autocap++
			}
			 := .capnames[]
			.capnames[] = .autocap
			.noteCaptureSlot(.autocap, )

			.autocap++
		}
	}

	// if the caps array has at least one gap, construct the list of used slots
	if .capcount < .captop {
		.capnumlist = make([]int, .capcount)
		 := 0

		for  := range .caps {
			.capnumlist[] = 
			++
		}

		sort.Ints(.capnumlist)
	}

	// merge capsnumlist into capnamelist
	if .capnames != nil || .capnumlist != nil {
		var  []string
		var  int
		var  int

		if .capnames == nil {
			 = nil
			.capnames = make(map[string]int)
			.capnamelist = []string{}
			 = -1
		} else {
			 = .capnamelist
			.capnamelist = []string{}
			 = .capnames[[0]]
		}

		for  := 0;  < .capcount; ++ {
			 := 
			if .capnumlist != nil {
				 = .capnumlist[]
			}

			if  ==  {
				.capnamelist = append(.capnamelist, [])
				++

				if  == len() {
					 = -1
				} else {
					 = .capnames[[]]
				}

			} else {
				//feature: culture?
				 := strconv.Itoa()
				.capnamelist = append(.capnamelist, )
				.capnames[] = 
			}
		}
	}
}

func ( *parser) () int {
	 := .autocap
	.autocap++
	return 
}

// CountCaptures is a prescanner for deducing the slots used for
// captures by doing a partial tokenization of the pattern.
func ( *parser) () error {
	var  rune

	.noteCaptureSlot(0, 0)

	.autocap = 1

	for .charsRight() > 0 {
		 := .textpos()
		 = .moveRightGetChar()
		switch  {
		case '\\':
			if .charsRight() > 0 {
				.scanBackslash(true)
			}

		case '#':
			if .useOptionX() {
				.moveLeft()
				.scanBlank()
			}

		case '[':
			.scanCharSet(false, true)

		case ')':
			if !.emptyOptionsStack() {
				.popOptions()
			}

		case '(':
			if .charsRight() >= 2 && .rightChar(1) == '#' && .rightChar(0) == '?' {
				.moveLeft()
				.scanBlank()
			} else {
				.pushOptions()
				if .charsRight() > 0 && .rightChar(0) == '?' {
					// we have (?...
					.moveRight(1)

					if .charsRight() > 1 && (.rightChar(0) == '<' || .rightChar(0) == '\'') {
						// named group: (?<... or (?'...

						.moveRight(1)
						 = .rightChar(0)

						if  != '0' && IsWordChar() {
							if  >= '1' &&  <= '9' {
								,  := .scanDecimal()
								if  != nil {
									return 
								}
								.noteCaptureSlot(, )
							} else {
								.noteCaptureName(.scanCapname(), )
							}
						}
					} else if .useRE2() && .charsRight() > 2 && (.rightChar(0) == 'P' && .rightChar(1) == '<') {
						// RE2-compat (?P<)
						.moveRight(2)
						 = .rightChar(0)
						if IsWordChar() {
							.noteCaptureName(.scanCapname(), )
						}

					} else {
						// (?...

						// get the options if it's an option construct (?cimsx-cimsx...)
						.scanOptions()

						if .charsRight() > 0 {
							if .rightChar(0) == ')' {
								// (?cimsx-cimsx)
								.moveRight(1)
								.popKeepOptions()
							} else if .rightChar(0) == '(' {
								// alternation construct: (?(foo)yes|no)
								// ignore the next paren so we don't capture the condition
								.ignoreNextParen = true

								// break from here so we don't reset ignoreNextParen
								continue
							}
						}
					}
				} else {
					if !.useOptionN() && !.ignoreNextParen {
						.noteCaptureSlot(.consumeAutocap(), )
					}
				}
			}

			.ignoreNextParen = false

		}
	}

	.assignNameSlots()
	return nil
}

func ( *parser) ( RegexOptions) {
	.currentPos = 0
	.autocap = 1
	.ignoreNextParen = false

	if len(.optionsStack) > 0 {
		.optionsStack = .optionsStack[:0]
	}

	.options = 
	.stack = nil
}

func ( *parser) () (*regexNode, error) {
	 := '@' // nonspecial ch, means at beginning
	 := false

	.startGroup(newRegexNodeMN(ntCapture, .options, 0, -1))

	for .charsRight() > 0 {
		 := 
		 = false

		if  := .scanBlank();  != nil {
			return nil, 
		}

		 := .textpos()

		// move past all of the normal characters.  We'll stop when we hit some kind of control character,
		// or if IgnorePatternWhiteSpace is on, we'll stop when we see some whitespace.
		if .useOptionX() {
			for .charsRight() > 0 {
				 = .rightChar(0)
				//UGLY: clean up, this is ugly
				if !(!isStopperX() || ( == '{' && !.isTrueQuantifier())) {
					break
				}
				.moveRight(1)
			}
		} else {
			for .charsRight() > 0 {
				 = .rightChar(0)
				if !(!isSpecial() ||  == '{' && !.isTrueQuantifier()) {
					break
				}
				.moveRight(1)
			}
		}

		 := .textpos()

		.scanBlank()

		if .charsRight() == 0 {
			 = '!' // nonspecial, means at end
		} else if  = .rightChar(0); isSpecial() {
			 = isQuantifier()
			.moveRight(1)
		} else {
			 = ' ' // nonspecial, means at ordinary char
		}

		if  <  {
			 :=  - 
			if  {
				--
			}
			 = false

			if  > 0 {
				.addToConcatenate(, , false)
			}

			if  {
				.addUnitOne(.charAt( - 1))
			}
		}

		switch  {
		case '!':
			goto 

		case ' ':
			goto 

		case '[':
			,  := .scanCharSet(.useOptionI(), false)
			if  != nil {
				return nil, 
			}
			.addUnitSet()

		case '(':
			.pushOptions()

			if ,  := .scanGroupOpen();  != nil {
				return nil, 
			} else if  == nil {
				.popKeepOptions()
			} else {
				.pushGroup()
				.startGroup()
			}

			continue

		case '|':
			.addAlternate()
			goto 

		case ')':
			if .emptyStack() {
				return nil, .getErr(ErrUnexpectedParen)
			}

			if  := .addGroup();  != nil {
				return nil, 
			}
			if  := .popGroup();  != nil {
				return nil, 
			}
			.popOptions()

			if .unit == nil {
				goto 
			}

		case '\\':
			,  := .scanBackslash(false)
			if  != nil {
				return nil, 
			}
			.addUnitNode()

		case '^':
			if .useOptionM() {
				.addUnitType(ntBol)
			} else {
				.addUnitType(ntBeginning)
			}

		case '$':
			if .useOptionM() {
				.addUnitType(ntEol)
			} else {
				.addUnitType(ntEndZ)
			}

		case '.':
			if .useOptionS() {
				.addUnitSet(AnyClass())
			} else if .useOptionE() {
				.addUnitSet(ECMAAnyClass())
			} else {
				.addUnitNotone('\n')
			}

		case '{', '*', '+', '?':
			if .unit == nil {
				if  {
					return nil, .getErr(ErrInvalidRepeatOp)
				} else {
					return nil, .getErr(ErrMissingRepeatArgument)
				}
			}
			.moveLeft()

		default:
			return nil, .getErr(ErrInternalError)
		}

		if  := .scanBlank();  != nil {
			return nil, 
		}

		if .charsRight() > 0 {
			 = .isTrueQuantifier()
		}
		if .charsRight() == 0 || ! {
			//maintain odd C# assignment order -- not sure if required, could clean up?
			.addConcatenate()
			goto 
		}

		 = .moveRightGetChar()

		// Handle quantifiers
		for .unit != nil {
			var ,  int
			var  bool

			switch  {
			case '*':
				 = 0
				 = math.MaxInt32

			case '?':
				 = 0
				 = 1

			case '+':
				 = 1
				 = math.MaxInt32

			case '{':
				{
					var  error
					 = .textpos()
					if ,  = .scanDecimal();  != nil {
						return nil, 
					}
					 = 
					if  < .textpos() {
						if .charsRight() > 0 && .rightChar(0) == ',' {
							.moveRight(1)
							if .charsRight() == 0 || .rightChar(0) == '}' {
								 = math.MaxInt32
							} else {
								if ,  = .scanDecimal();  != nil {
									return nil, 
								}
							}
						}
					}

					if  == .textpos() || .charsRight() == 0 || .moveRightGetChar() != '}' {
						.addConcatenate()
						.textto( - 1)
						goto 
					}
				}

			default:
				return nil, .getErr(ErrInternalError)
			}

			if  := .scanBlank();  != nil {
				return nil, 
			}

			if .charsRight() == 0 || .rightChar(0) != '?' {
				 = false
			} else {
				.moveRight(1)
				 = true
			}

			if  >  {
				return nil, .getErr(ErrInvalidRepeatSize)
			}

			.addConcatenate3(, , )
		}

	:
	}

:
	;

	if !.emptyStack() {
		return nil, .getErr(ErrMissingParen)
	}

	if  := .addGroup();  != nil {
		return nil, 
	}

	return .unit, nil

}

/*
 * Simple parsing for replacement patterns
 */
func ( *parser) () (*regexNode, error) {
	var ,  int

	.concatenation = newRegexNode(ntConcatenate, .options)

	for {
		 = .charsRight()
		if  == 0 {
			break
		}

		 = .textpos()

		for  > 0 && .rightChar(0) != '$' {
			.moveRight(1)
			--
		}

		.addToConcatenate(, .textpos()-, true)

		if  > 0 {
			if .moveRightGetChar() == '$' {
				,  := .scanDollar()
				if  != nil {
					return nil, 
				}
				.addUnitNode()
			}
			.addConcatenate()
		}
	}

	return .concatenation, nil
}

/*
 * Scans $ patterns recognized within replacement patterns
 */
func ( *parser) () (*regexNode, error) {
	if .charsRight() == 0 {
		return newRegexNodeCh(ntOne, .options, '$'), nil
	}

	 := .rightChar(0)
	 := false
	 := .textpos()
	 := 

	// Note angle

	if  == '{' && .charsRight() > 1 {
		 = true
		.moveRight(1)
		 = .rightChar(0)
	}

	// Try to parse backreference: \1 or \{1} or \{cap}

	if  >= '0' &&  <= '9' {
		if ! && .useOptionE() {
			 := -1
			 := int( - '0')
			.moveRight(1)
			if .isCaptureSlot() {
				 = 
				 = .textpos()
			}

			for .charsRight() > 0 {
				 = .rightChar(0)
				if  < '0' ||  > '9' {
					break
				}
				 := int( - '0')
				if  > maxValueDiv10 || ( == maxValueDiv10 &&  > maxValueMod10) {
					return nil, .getErr(ErrCaptureGroupOutOfRange)
				}

				 = *10 + 

				.moveRight(1)
				if .isCaptureSlot() {
					 = 
					 = .textpos()
				}
			}
			.textto()
			if  >= 0 {
				return newRegexNodeM(ntRef, .options, ), nil
			}
		} else {
			,  := .scanDecimal()
			if  != nil {
				return nil, 
			}
			if ! || .charsRight() > 0 && .moveRightGetChar() == '}' {
				if .isCaptureSlot() {
					return newRegexNodeM(ntRef, .options, ), nil
				}
			}
		}
	} else if  && IsWordChar() {
		 := .scanCapname()

		if .charsRight() > 0 && .moveRightGetChar() == '}' {
			if .isCaptureName() {
				return newRegexNodeM(ntRef, .options, .captureSlotFromName()), nil
			}
		}
	} else if ! {
		 := 1

		switch  {
		case '$':
			.moveRight(1)
			return newRegexNodeCh(ntOne, .options, '$'), nil
		case '&':
			 = 0
		case '`':
			 = replaceLeftPortion
		case '\'':
			 = replaceRightPortion
		case '+':
			 = replaceLastGroup
		case '_':
			 = replaceWholeString
		}

		if  != 1 {
			.moveRight(1)
			return newRegexNodeM(ntRef, .options, ), nil
		}
	}

	// unrecognized $: literalize

	.textto()
	return newRegexNodeCh(ntOne, .options, '$'), nil
}

// scanGroupOpen scans chars following a '(' (not counting the '('), and returns
// a RegexNode for the type of group scanned, or nil if the group
// simply changed options (?cimsx-cimsx) or was a comment (#...).
func ( *parser) () (*regexNode, error) {
	var  rune
	var  nodeType
	var  error
	 := '>'
	 := .textpos()

	// just return a RegexNode if we have:
	// 1. "(" followed by nothing
	// 2. "(x" where x != ?
	// 3. "(?)"
	if .charsRight() == 0 || .rightChar(0) != '?' || (.rightChar(0) == '?' && (.charsRight() > 1 && .rightChar(1) == ')')) {
		if .useOptionN() || .ignoreNextParen {
			.ignoreNextParen = false
			return newRegexNode(ntGroup, .options), nil
		}
		return newRegexNodeMN(ntCapture, .options, .consumeAutocap(), -1), nil
	}

	.moveRight(1)

	for {
		if .charsRight() == 0 {
			break
		}

		switch  = .moveRightGetChar();  {
		case ':':
			 = ntGroup

		case '=':
			.options &= ^RightToLeft
			 = ntRequire

		case '!':
			.options &= ^RightToLeft
			 = ntPrevent

		case '>':
			 = ntGreedy

		case '\'':
			 = '\''
			fallthrough

		case '<':
			if .charsRight() == 0 {
				goto 
			}

			switch  = .moveRightGetChar();  {
			case '=':
				if  == '\'' {
					goto 
				}

				.options |= RightToLeft
				 = ntRequire

			case '!':
				if  == '\'' {
					goto 
				}

				.options |= RightToLeft
				 = ntPrevent

			default:
				.moveLeft()
				 := -1
				 := -1
				 := false

				// grab part before -

				if  >= '0' &&  <= '9' {
					if ,  = .scanDecimal();  != nil {
						return nil, 
					}

					if !.isCaptureSlot() {
						 = -1
					}

					// check if we have bogus characters after the number
					if .charsRight() > 0 && !(.rightChar(0) ==  || .rightChar(0) == '-') {
						return nil, .getErr(ErrInvalidGroupName)
					}
					if  == 0 {
						return nil, .getErr(ErrCapNumNotZero)
					}
				} else if IsWordChar() {
					 := .scanCapname()

					if .isCaptureName() {
						 = .captureSlotFromName()
					}

					// check if we have bogus character after the name
					if .charsRight() > 0 && !(.rightChar(0) ==  || .rightChar(0) == '-') {
						return nil, .getErr(ErrInvalidGroupName)
					}
				} else if  == '-' {
					 = true
				} else {
					// bad group name - starts with something other than a word character and isn't a number
					return nil, .getErr(ErrInvalidGroupName)
				}

				// grab part after - if any

				if ( != -1 ||  == true) && .charsRight() > 0 && .rightChar(0) == '-' {
					.moveRight(1)

					//no more chars left, no closing char, etc
					if .charsRight() == 0 {
						return nil, .getErr(ErrInvalidGroupName)
					}

					 = .rightChar(0)
					if  >= '0' &&  <= '9' {
						if ,  = .scanDecimal();  != nil {
							return nil, 
						}

						if !.isCaptureSlot() {
							return nil, .getErr(ErrUndefinedBackRef, )
						}

						// check if we have bogus characters after the number
						if .charsRight() > 0 && .rightChar(0) !=  {
							return nil, .getErr(ErrInvalidGroupName)
						}
					} else if IsWordChar() {
						 := .scanCapname()

						if !.isCaptureName() {
							return nil, .getErr(ErrUndefinedNameRef, )
						}
						 = .captureSlotFromName()

						// check if we have bogus character after the name
						if .charsRight() > 0 && .rightChar(0) !=  {
							return nil, .getErr(ErrInvalidGroupName)
						}
					} else {
						// bad group name - starts with something other than a word character and isn't a number
						return nil, .getErr(ErrInvalidGroupName)
					}
				}

				// actually make the node

				if ( != -1 ||  != -1) && .charsRight() > 0 && .moveRightGetChar() ==  {
					return newRegexNodeMN(ntCapture, .options, , ), nil
				}
				goto 
			}

		case '(':
			// alternation construct (?(...) | )

			 := .textpos()
			if .charsRight() > 0 {
				 = .rightChar(0)

				// check if the alternation condition is a backref
				if  >= '0' &&  <= '9' {
					var  int
					if ,  = .scanDecimal();  != nil {
						return nil, 
					}
					if .charsRight() > 0 && .moveRightGetChar() == ')' {
						if .isCaptureSlot() {
							return newRegexNodeM(ntTestref, .options, ), nil
						}
						return nil, .getErr(ErrUndefinedReference, )
					}

					return nil, .getErr(ErrMalformedReference, )

				} else if IsWordChar() {
					 := .scanCapname()

					if .isCaptureName() && .charsRight() > 0 && .moveRightGetChar() == ')' {
						return newRegexNodeM(ntTestref, .options, .captureSlotFromName()), nil
					}
				}
			}
			// not a backref
			 = ntTestgroup
			.textto( - 1)   // jump to the start of the parentheses
			.ignoreNextParen = true // but make sure we don't try to capture the insides

			 := .charsRight()
			if  >= 3 && .rightChar(1) == '?' {
				 := .rightChar(2)
				// disallow comments in the condition
				if  == '#' {
					return nil, .getErr(ErrAlternationCantHaveComment)
				}

				// disallow named capture group (?<..>..) in the condition
				if  == '\'' {
					return nil, .getErr(ErrAlternationCantCapture)
				}

				if  >= 4 && ( == '<' && .rightChar(3) != '!' && .rightChar(3) != '=') {
					return nil, .getErr(ErrAlternationCantCapture)
				}
			}

		case 'P':
			if .useRE2() {
				// support for P<name> syntax
				if .charsRight() < 3 {
					goto 
				}

				 = .moveRightGetChar()
				if  != '<' {
					goto 
				}

				 = .moveRightGetChar()
				.moveLeft()

				if IsWordChar() {
					 := -1
					 := .scanCapname()

					if .isCaptureName() {
						 = .captureSlotFromName()
					}

					// check if we have bogus character after the name
					if .charsRight() > 0 && .rightChar(0) != '>' {
						return nil, .getErr(ErrInvalidGroupName)
					}

					// actually make the node

					if  != -1 && .charsRight() > 0 && .moveRightGetChar() == '>' {
						return newRegexNodeMN(ntCapture, .options, , -1), nil
					}
					goto 

				} else {
					// bad group name - starts with something other than a word character and isn't a number
					return nil, .getErr(ErrInvalidGroupName)
				}
			}
			// if we're not using RE2 compat mode then
			// we just behave like normal
			fallthrough

		default:
			.moveLeft()

			 = ntGroup
			// disallow options in the children of a testgroup node
			if .group.t != ntTestgroup {
				.scanOptions()
			}
			if .charsRight() == 0 {
				goto 
			}

			if  = .moveRightGetChar();  == ')' {
				return nil, nil
			}

			if  != ':' {
				goto 
			}

		}

		return newRegexNode(, .options), nil
	}

:

	// break Recognize comes here

	return nil, .getErr(ErrUnrecognizedGrouping, string(.pattern[:.textpos()]))
}

// scans backslash specials and basics
func ( *parser) ( bool) (*regexNode, error) {

	if .charsRight() == 0 {
		return nil, .getErr(ErrIllegalEndEscape)
	}

	switch  := .rightChar(0);  {
	case 'b', 'B', 'A', 'G', 'Z', 'z':
		.moveRight(1)
		return newRegexNode(.typeFromCode(), .options), nil

	case 'w':
		.moveRight(1)
		if .useOptionE() || .useRE2() {
			return newRegexNodeSet(ntSet, .options, ECMAWordClass()), nil
		}
		return newRegexNodeSet(ntSet, .options, WordClass()), nil

	case 'W':
		.moveRight(1)
		if .useOptionE() || .useRE2() {
			return newRegexNodeSet(ntSet, .options, NotECMAWordClass()), nil
		}
		return newRegexNodeSet(ntSet, .options, NotWordClass()), nil

	case 's':
		.moveRight(1)
		if .useOptionE() {
			return newRegexNodeSet(ntSet, .options, ECMASpaceClass()), nil
		} else if .useRE2() {
			return newRegexNodeSet(ntSet, .options, RE2SpaceClass()), nil
		}
		return newRegexNodeSet(ntSet, .options, SpaceClass()), nil

	case 'S':
		.moveRight(1)
		if .useOptionE() {
			return newRegexNodeSet(ntSet, .options, NotECMASpaceClass()), nil
		} else if .useRE2() {
			return newRegexNodeSet(ntSet, .options, NotRE2SpaceClass()), nil
		}
		return newRegexNodeSet(ntSet, .options, NotSpaceClass()), nil

	case 'd':
		.moveRight(1)
		if .useOptionE() || .useRE2() {
			return newRegexNodeSet(ntSet, .options, ECMADigitClass()), nil
		}
		return newRegexNodeSet(ntSet, .options, DigitClass()), nil

	case 'D':
		.moveRight(1)
		if .useOptionE() || .useRE2() {
			return newRegexNodeSet(ntSet, .options, NotECMADigitClass()), nil
		}
		return newRegexNodeSet(ntSet, .options, NotDigitClass()), nil

	case 'p', 'P':
		.moveRight(1)
		,  := .parseProperty()
		if  != nil {
			return nil, 
		}
		 := &CharSet{}
		.addCategory(, ( != 'p'), .useOptionI(), .patternRaw)
		if .useOptionI() {
			.addLowercase()
		}

		return newRegexNodeSet(ntSet, .options, ), nil

	default:
		return .scanBasicBackslash()
	}
}

// Scans \-style backreferences and character escapes
func ( *parser) ( bool) (*regexNode, error) {
	if .charsRight() == 0 {
		return nil, .getErr(ErrIllegalEndEscape)
	}
	 := false
	 := false
	 := '\x00'

	 := .textpos()
	 := .rightChar(0)

	// Allow \k<foo> instead of \<foo>, which is now deprecated.

	// According to ECMAScript specification, \k<name> is only parsed as a named group reference if
	// there is at least one group name in the regexp.
	// See https://www.ecma-international.org/ecma-262/#sec-isvalidregularexpressionliteral, step 7.
	// Note, during the first (scanOnly) run we may not have all group names scanned, but that's ok.
	if  == 'k' && (!.useOptionE() || len(.capnames) > 0) {
		if .charsRight() >= 2 {
			.moveRight(1)
			 = .moveRightGetChar()

			if  == '<' || (!.useOptionE() &&  == '\'') { // No support for \k'name' in ECMAScript
				 = true
				if  == '\'' {
					 = '\''
				} else {
					 = '>'
				}
			}
		}

		if ! || .charsRight() <= 0 {
			return nil, .getErr(ErrMalformedNameRef)
		}

		 = .rightChar(0)
		 = true

	} else if !.useOptionE() && ( == '<' ||  == '\'') && .charsRight() > 1 { // Note angle without \g
		 = true
		if  == '\'' {
			 = '\''
		} else {
			 = '>'
		}

		.moveRight(1)
		 = .rightChar(0)
	}

	// Try to parse backreference: \<1> or \<cap>

	if  &&  >= '0' &&  <= '9' {
		,  := .scanDecimal()
		if  != nil {
			return nil, 
		}

		if .charsRight() > 0 && .moveRightGetChar() ==  {
			if .isCaptureSlot() {
				return newRegexNodeM(ntRef, .options, ), nil
			}
			return nil, .getErr(ErrUndefinedBackRef, )
		}
	} else if ! &&  >= '1' &&  <= '9' { // Try to parse backreference or octal: \1
		,  := .scanDecimal()
		if  != nil {
			return nil, 
		}

		if  {
			return nil, nil
		}

		if .isCaptureSlot() {
			return newRegexNodeM(ntRef, .options, ), nil
		}
		if  <= 9 && !.useOptionE() {
			return nil, .getErr(ErrUndefinedBackRef, )
		}

	} else if  {
		 := .scanCapname()

		if  != "" && .charsRight() > 0 && .moveRightGetChar() ==  {

			if  {
				return nil, nil
			}

			if .isCaptureName() {
				return newRegexNodeM(ntRef, .options, .captureSlotFromName()), nil
			}
			return nil, .getErr(ErrUndefinedNameRef, )
		} else {
			if  {
				return nil, .getErr(ErrMalformedNameRef)
			}
		}
	}

	// Not backreference: must be char code

	.textto()
	,  := .scanCharEscape()
	if  != nil {
		return nil, 
	}

	if  {
		return nil, nil
	}

	if .useOptionI() {
		 = unicode.ToLower()
	}

	return newRegexNodeCh(ntOne, .options, ), nil
}

// Scans X for \p{X} or \P{X}
func ( *parser) () (string, error) {
	// RE2 and PCRE supports \pX syntax (no {} and only 1 letter unicode cats supported)
	// since this is purely additive syntax it's not behind a flag
	if .charsRight() >= 1 && .rightChar(0) != '{' {
		 := string(.moveRightGetChar())
		// check if it's a valid cat
		if !isValidUnicodeCat() {
			return "", .getErr(ErrUnknownSlashP, )
		}
		return , nil
	}

	if .charsRight() < 3 {
		return "", .getErr(ErrIncompleteSlashP)
	}
	 := .moveRightGetChar()
	if  != '{' {
		return "", .getErr(ErrMalformedSlashP)
	}

	 := .textpos()
	for .charsRight() > 0 {
		 = .moveRightGetChar()
		if !(IsWordChar() ||  == '-') {
			.moveLeft()
			break
		}
	}
	 := string(.pattern[:.textpos()])

	if .charsRight() == 0 || .moveRightGetChar() != '}' {
		return "", .getErr(ErrIncompleteSlashP)
	}

	if !isValidUnicodeCat() {
		return "", .getErr(ErrUnknownSlashP, )
	}

	return , nil
}

// Returns ReNode type for zero-length assertions with a \ code.
func ( *parser) ( rune) nodeType {
	switch  {
	case 'b':
		if .useOptionE() {
			return ntECMABoundary
		}
		return ntBoundary
	case 'B':
		if .useOptionE() {
			return ntNonECMABoundary
		}
		return ntNonboundary
	case 'A':
		return ntBeginning
	case 'G':
		return ntStart
	case 'Z':
		return ntEndZ
	case 'z':
		return ntEnd
	default:
		return ntNothing
	}
}

// Scans whitespace or x-mode comments.
func ( *parser) () error {
	if .useOptionX() {
		for {
			for .charsRight() > 0 && isSpace(.rightChar(0)) {
				.moveRight(1)
			}

			if .charsRight() == 0 {
				break
			}

			if .rightChar(0) == '#' {
				for .charsRight() > 0 && .rightChar(0) != '\n' {
					.moveRight(1)
				}
			} else if .charsRight() >= 3 && .rightChar(2) == '#' &&
				.rightChar(1) == '?' && .rightChar(0) == '(' {
				for .charsRight() > 0 && .rightChar(0) != ')' {
					.moveRight(1)
				}
				if .charsRight() == 0 {
					return .getErr(ErrUnterminatedComment)
				}
				.moveRight(1)
			} else {
				break
			}
		}
	} else {
		for {
			if .charsRight() < 3 || .rightChar(2) != '#' ||
				.rightChar(1) != '?' || .rightChar(0) != '(' {
				return nil
			}

			for .charsRight() > 0 && .rightChar(0) != ')' {
				.moveRight(1)
			}
			if .charsRight() == 0 {
				return .getErr(ErrUnterminatedComment)
			}
			.moveRight(1)
		}
	}
	return nil
}

func ( *parser) () string {
	 := .textpos()

	for .charsRight() > 0 {
		if !IsWordChar(.moveRightGetChar()) {
			.moveLeft()
			break
		}
	}

	return string(.pattern[:.textpos()])
}

// Scans contents of [] (not including []'s), and converts to a set.
func ( *parser) (,  bool) (*CharSet, error) {
	 := '\x00'
	 := '\x00'
	 := false
	 := true
	 := false

	var  *CharSet
	if ! {
		 = &CharSet{}
	}

	if .charsRight() > 0 && .rightChar(0) == '^' {
		.moveRight(1)
		if ! {
			.negate = true
		}
	}

	for ; .charsRight() > 0;  = false {
		 := false
		 = .moveRightGetChar()
		if  == ']' {
			if ! {
				 = true
				break
			} else if .useOptionE() {
				if ! {
					.addRanges(NoneClass().ranges)
				}
				 = true
				break
			}

		} else if  == '\\' && .charsRight() > 0 {
			switch  = .moveRightGetChar();  {
			case 'D', 'd':
				if ! {
					if  {
						return nil, .getErr(ErrBadClassInCharRange, )
					}
					.addDigit(.useOptionE() || .useRE2(),  == 'D', .patternRaw)
				}
				continue

			case 'S', 's':
				if ! {
					if  {
						return nil, .getErr(ErrBadClassInCharRange, )
					}
					.addSpace(.useOptionE(), .useRE2(),  == 'S')
				}
				continue

			case 'W', 'w':
				if ! {
					if  {
						return nil, .getErr(ErrBadClassInCharRange, )
					}

					.addWord(.useOptionE() || .useRE2(),  == 'W')
				}
				continue

			case 'p', 'P':
				if ! {
					if  {
						return nil, .getErr(ErrBadClassInCharRange, )
					}
					,  := .parseProperty()
					if  != nil {
						return nil, 
					}
					.addCategory(, ( != 'p'), , .patternRaw)
				} else {
					.parseProperty()
				}

				continue

			case '-':
				if ! {
					.addRange(, )
				}
				continue

			default:
				.moveLeft()
				var  error
				,  = .scanCharEscape() // non-literal character
				if  != nil {
					return nil, 
				}
				 = true
				break // this break will only break out of the switch
			}
		} else if  == '[' {
			// This is code for Posix style properties - [:Ll:] or [:IsTibetan:].
			// It currently doesn't do anything other than skip the whole thing!
			if .charsRight() > 0 && .rightChar(0) == ':' && ! {
				 := .textpos()

				.moveRight(1)
				 := false
				if .charsRight() > 1 && .rightChar(0) == '^' {
					 = true
					.moveRight(1)
				}

				 := .scanCapname() // snag the name
				if ! && .useRE2() {
					// look up the name since these are valid for RE2
					// add the group based on the name
					if  := .addNamedASCII(, ); ! {
						return nil, .getErr(ErrInvalidCharRange)
					}
				}
				if .charsRight() < 2 || .moveRightGetChar() != ':' || .moveRightGetChar() != ']' {
					.textto()
				} else if .useRE2() {
					// move on
					continue
				}
			}
		}

		if  {
			 = false
			if ! {
				if  == '[' && ! && ! {
					// We thought we were in a range, but we're actually starting a subtraction.
					// In that case, we'll add chPrev to our char class, skip the opening [, and
					// scan the new character class recursively.
					.addChar()
					,  := .(, false)
					if  != nil {
						return nil, 
					}
					.addSubtraction()

					if .charsRight() > 0 && .rightChar(0) != ']' {
						return nil, .getErr(ErrSubtractionMustBeLast)
					}
				} else {
					// a regular range, like a-z
					if  >  {
						return nil, .getErr(ErrReversedCharRange, , )
					}
					.addRange(, )
				}
			}
		} else if .charsRight() >= 2 && .rightChar(0) == '-' && .rightChar(1) != ']' {
			// this could be the start of a range
			 = 
			 = true
			.moveRight(1)
		} else if .charsRight() >= 1 &&  == '-' && ! && .rightChar(0) == '[' && ! {
			// we aren't in a range, and now there is a subtraction.  Usually this happens
			// only when a subtraction follows a range, like [a-z-[b]]
			if ! {
				.moveRight(1)
				,  := .(, false)
				if  != nil {
					return nil, 
				}
				.addSubtraction()

				if .charsRight() > 0 && .rightChar(0) != ']' {
					return nil, .getErr(ErrSubtractionMustBeLast)
				}
			} else {
				.moveRight(1)
				.(, true)
			}
		} else {
			if ! {
				.addRange(, )
			}
		}
	}

	if ! {
		return nil, .getErr(ErrUnterminatedBracket)
	}

	if ! &&  {
		.addLowercase()
	}

	return , nil
}

// Scans any number of decimal digits (pegs value at 2^31-1 if too large)
func ( *parser) () (int, error) {
	 := 0
	var  int

	for .charsRight() > 0 {
		 = int(.rightChar(0) - '0')
		if  < 0 ||  > 9 {
			break
		}
		.moveRight(1)

		if  > maxValueDiv10 || ( == maxValueDiv10 &&  > maxValueMod10) {
			return 0, .getErr(ErrCaptureGroupOutOfRange)
		}

		 *= 10
		 += 
	}

	return int(), nil
}

// Returns true for options allowed only at the top level
func isOnlyTopOption( RegexOptions) bool {
	return  == RightToLeft ||  == ECMAScript ||  == RE2
}

// Scans cimsx-cimsx option string, stops at the first unrecognized char.
func ( *parser) () {

	for  := false; .charsRight() > 0; .moveRight(1) {
		 := .rightChar(0)

		if  == '-' {
			 = true
		} else if  == '+' {
			 = false
		} else {
			 := optionFromCode()
			if  == 0 || isOnlyTopOption() {
				return
			}

			if  {
				.options &= ^
			} else {
				.options |= 
			}
		}
	}
}

// Scans \ code for escape codes that map to single unicode chars.
func ( *parser) () ( rune,  error) {

	 := .moveRightGetChar()

	if  >= '0' &&  <= '7' {
		.moveLeft()
		return .scanOctal(), nil
	}

	 := .textpos()

	switch  {
	case 'x':
		// support for \x{HEX} syntax from Perl and PCRE
		if .charsRight() > 0 && .rightChar(0) == '{' {
			if .useOptionE() {
				return , nil
			}
			.moveRight(1)
			return .scanHexUntilBrace()
		} else {
			,  = .scanHex(2)
		}
	case 'u':
		// ECMAscript suppot \u{HEX} only if `u` is also set
		if .useOptionE() && .useOptionU() && .charsRight() > 0 && .rightChar(0) == '{' {
			.moveRight(1)
			return .scanHexUntilBrace()
		} else {
			,  = .scanHex(4)
		}
	case 'a':
		return '\u0007', nil
	case 'b':
		return '\b', nil
	case 'e':
		return '\u001B', nil
	case 'f':
		return '\f', nil
	case 'n':
		return '\n', nil
	case 'r':
		return '\r', nil
	case 't':
		return '\t', nil
	case 'v':
		return '\u000B', nil
	case 'c':
		,  = .scanControl()
	default:
		if !.useOptionE() && !.useRE2() && IsWordChar() {
			return 0, .getErr(ErrUnrecognizedEscape, string())
		}
		return , nil
	}
	if  != nil && .useOptionE() {
		.textto()
		return , nil
	}
	return
}

// Grabs and converts an ascii control character
func ( *parser) () (rune, error) {
	if .charsRight() <= 0 {
		return 0, .getErr(ErrMissingControl)
	}

	 := .moveRightGetChar()

	// \ca interpreted as \cA

	if  >= 'a' &&  <= 'z' {
		 = ( - ('a' - 'A'))
	}
	 = ( - '@')
	if  >= 0 &&  < ' ' {
		return , nil
	}

	return 0, .getErr(ErrUnrecognizedControl)

}

// Scan hex digits until we hit a closing brace.
// Non-hex digits, hex value too large for UTF-8, or running out of chars are errors
func ( *parser) () (rune, error) {
	// PCRE spec reads like unlimited hex digits are allowed, but unicode has a limit
	// so we can enforce that
	 := 0
	 := false

	for .charsRight() > 0 {
		 := .moveRightGetChar()
		if  == '}' {
			// hit our close brace, we're done here
			// prevent \x{}
			if ! {
				return 0, .getErr(ErrTooFewHex)
			}
			return rune(), nil
		}
		 = true
		// no brace needs to be hex digit
		 := hexDigit()
		if  < 0 {
			return 0, .getErr(ErrMissingBrace)
		}

		 *= 0x10
		 += 

		if  > unicode.MaxRune {
			return 0, .getErr(ErrInvalidHex)
		}
	}

	// we only make it here if we run out of digits without finding the brace
	return 0, .getErr(ErrMissingBrace)
}

// Scans exactly c hex digits (c=2 for \xFF, c=4 for \uFFFF)
func ( *parser) ( int) (rune, error) {

	 := 0

	if .charsRight() >=  {
		for  > 0 {
			 := hexDigit(.moveRightGetChar())
			if  < 0 {
				break
			}
			 *= 0x10
			 += 
			--
		}
	}

	if  > 0 {
		return 0, .getErr(ErrTooFewHex)
	}

	return rune(), nil
}

// Returns n <= 0xF for a hex digit.
func hexDigit( rune) int {

	if  := uint( - '0');  <= 9 {
		return int()
	}

	if  := uint( - 'a');  <= 5 {
		return int( + 0xa)
	}

	if  := uint( - 'A');  <= 5 {
		return int( + 0xa)
	}

	return -1
}

// Scans up to three octal digits (stops before exceeding 0377).
func ( *parser) () rune {
	// Consume octal chars only up to 3 digits and value 0377

	 := 3

	if  > .charsRight() {
		 = .charsRight()
	}

	//we know the first char is good because the caller had to check
	 := 0
	 := int(.rightChar(0) - '0')
	for  > 0 &&  <= 7 &&  >= 0 {
		if  >= 0x20 && .useOptionE() {
			break
		}
		 *= 8
		 += 
		--

		.moveRight(1)
		if !.rightMost() {
			 = int(.rightChar(0) - '0')
		}
	}

	// Octal codes only go up to 255.  Any larger and the behavior that Perl follows
	// is simply to truncate the high bits.
	 &= 0xFF

	return rune()
}

// Returns the current parsing position.
func ( *parser) () int {
	return .currentPos
}

// Zaps to a specific parsing position.
func ( *parser) ( int) {
	.currentPos = 
}

// Returns the char at the right of the current parsing position and advances to the right.
func ( *parser) () rune {
	 := .pattern[.currentPos]
	.currentPos++
	return 
}

// Moves the current position to the right.
func ( *parser) ( int) {
	// default would be 1
	.currentPos += 
}

// Moves the current parsing position one to the left.
func ( *parser) () {
	.currentPos--
}

// Returns the char left of the current parsing position.
func ( *parser) ( int) rune {
	return .pattern[]
}

// Returns the char i chars right of the current parsing position.
func ( *parser) ( int) rune {
	// default would be 0
	return .pattern[.currentPos+]
}

// Number of characters to the right of the current parsing position.
func ( *parser) () int {
	return len(.pattern) - .currentPos
}

func ( *parser) () bool {
	return .currentPos == len(.pattern)
}

// Looks up the slot number for a given name
func ( *parser) ( string) int {
	return .capnames[]
}

// True if the capture slot was noted
func ( *parser) ( int) bool {
	if .caps != nil {
		,  := .caps[]
		return 
	}

	return ( >= 0 &&  < .capsize)
}

// Looks up the slot number for a given name
func ( *parser) ( string) bool {
	if .capnames == nil {
		return false
	}

	,  := .capnames[]
	return 
}

// option shortcuts

// True if N option disabling '(' autocapture is on.
func ( *parser) () bool {
	return (.options & ExplicitCapture) != 0
}

// True if I option enabling case-insensitivity is on.
func ( *parser) () bool {
	return (.options & IgnoreCase) != 0
}

// True if M option altering meaning of $ and ^ is on.
func ( *parser) () bool {
	return (.options & Multiline) != 0
}

// True if S option altering meaning of . is on.
func ( *parser) () bool {
	return (.options & Singleline) != 0
}

// True if X option enabling whitespace/comment mode is on.
func ( *parser) () bool {
	return (.options & IgnorePatternWhitespace) != 0
}

// True if E option enabling ECMAScript behavior on.
func ( *parser) () bool {
	return (.options & ECMAScript) != 0
}

// true to use RE2 compatibility parsing behavior.
func ( *parser) () bool {
	return (.options & RE2) != 0
}

// True if U option enabling ECMAScript's Unicode behavior on.
func ( *parser) () bool {
	return (.options & Unicode) != 0
}

// True if options stack is empty.
func ( *parser) () bool {
	return len(.optionsStack) == 0
}

// Finish the current quantifiable (when a quantifier is not found or is not possible)
func ( *parser) () {
	// The first (| inside a Testgroup group goes directly to the group
	.concatenation.addChild(.unit)
	.unit = nil
}

// Finish the current quantifiable (when a quantifier is found)
func ( *parser) ( bool, ,  int) {
	.concatenation.addChild(.unit.makeQuantifier(, , ))
	.unit = nil
}

// Sets the current unit to a single char node
func ( *parser) ( rune) {
	if .useOptionI() {
		 = unicode.ToLower()
	}

	.unit = newRegexNodeCh(ntOne, .options, )
}

// Sets the current unit to a single inverse-char node
func ( *parser) ( rune) {
	if .useOptionI() {
		 = unicode.ToLower()
	}

	.unit = newRegexNodeCh(ntNotone, .options, )
}

// Sets the current unit to a single set node
func ( *parser) ( *CharSet) {
	.unit = newRegexNodeSet(ntSet, .options, )
}

// Sets the current unit to a subtree
func ( *parser) ( *regexNode) {
	.unit = 
}

// Sets the current unit to an assertion of the specified type
func ( *parser) ( nodeType) {
	.unit = newRegexNode(, .options)
}

// Finish the current group (in response to a ')' or end)
func ( *parser) () error {
	if .group.t == ntTestgroup || .group.t == ntTestref {
		.group.addChild(.concatenation.reverseLeft())
		if (.group.t == ntTestref && len(.group.children) > 2) || len(.group.children) > 3 {
			return .getErr(ErrTooManyAlternates)
		}
	} else {
		.alternation.addChild(.concatenation.reverseLeft())
		.group.addChild(.alternation)
	}

	.unit = .group
	return nil
}

// Pops the option stack, but keeps the current options unchanged.
func ( *parser) () {
	 := len(.optionsStack) - 1
	.optionsStack = .optionsStack[:]
}

// Recalls options from the stack.
func ( *parser) () {
	 := len(.optionsStack) - 1
	// get the last item on the stack and then remove it by reslicing
	.options = .optionsStack[]
	.optionsStack = .optionsStack[:]
}

// Saves options on a stack.
func ( *parser) () {
	.optionsStack = append(.optionsStack, .options)
}

// Add a string to the last concatenate.
func ( *parser) (,  int,  bool) {
	var  *regexNode

	if  == 0 {
		return
	}

	if  > 1 {
		 := make([]rune, )
		copy(, .pattern[:+])

		if .useOptionI() && ! {
			// We do the ToLower character by character for consistency.  With surrogate chars, doing
			// a ToLower on the entire string could actually change the surrogate pair.  This is more correct
			// linguistically, but since Regex doesn't support surrogates, it's more important to be
			// consistent.
			for  := 0;  < len(); ++ {
				[] = unicode.ToLower([])
			}
		}

		 = newRegexNodeStr(ntMulti, .options, )
	} else {
		 := .charAt()

		if .useOptionI() && ! {
			 = unicode.ToLower()
		}

		 = newRegexNodeCh(ntOne, .options, )
	}

	.concatenation.addChild()
}

// Push the parser state (in response to an open paren)
func ( *parser) () {
	.group.next = .stack
	.alternation.next = .group
	.concatenation.next = .alternation
	.stack = .concatenation
}

// Remember the pushed state (in response to a ')')
func ( *parser) () error {
	.concatenation = .stack
	.alternation = .concatenation.next
	.group = .alternation.next
	.stack = .group.next

	// The first () inside a Testgroup group goes directly to the group
	if .group.t == ntTestgroup && len(.group.children) == 0 {
		if .unit == nil {
			return .getErr(ErrConditionalExpression)
		}

		.group.addChild(.unit)
		.unit = nil
	}
	return nil
}

// True if the group stack is empty.
func ( *parser) () bool {
	return .stack == nil
}

// Start a new round for the parser state (in response to an open paren or string start)
func ( *parser) ( *regexNode) {
	.group = 
	.alternation = newRegexNode(ntAlternate, .options)
	.concatenation = newRegexNode(ntConcatenate, .options)
}

// Finish the current concatenation (in response to a |)
func ( *parser) () {
	// The | parts inside a Testgroup group go directly to the group

	if .group.t == ntTestgroup || .group.t == ntTestref {
		.group.addChild(.concatenation.reverseLeft())
	} else {
		.alternation.addChild(.concatenation.reverseLeft())
	}

	.concatenation = newRegexNode(ntConcatenate, .options)
}

// For categorizing ascii characters.

const (
	Q byte = 5 // quantifier
	S      = 4 // ordinary stopper
	Z      = 3 // ScanBlank stopper
	X      = 2 // whitespace
	E      = 1 // should be escaped
)

var _category = []byte{
	//01  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
	0, 0, 0, 0, 0, 0, 0, 0, 0, X, X, X, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	// !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /  0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
	X, 0, 0, Z, S, 0, 0, 0, S, S, Q, Q, 0, 0, S, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Q,
	//@A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^  _
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, S, 0,
	//'a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z  {  |  }  ~
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Q, S, 0, 0, 0,
}

func isSpace( rune) bool {
	return ( <= ' ' && _category[] == X)
}

// Returns true for those characters that terminate a string of ordinary chars.
func isSpecial( rune) bool {
	return ( <= '|' && _category[] >= S)
}

// Returns true for those characters that terminate a string of ordinary chars.
func isStopperX( rune) bool {
	return ( <= '|' && _category[] >= X)
}

// Returns true for those characters that begin a quantifier.
func isQuantifier( rune) bool {
	return ( <= '{' && _category[] >= Q)
}

func ( *parser) () bool {
	 := .charsRight()
	if  == 0 {
		return false
	}

	 := .textpos()
	 := .charAt()
	if  != '{' {
		return  <= '{' && _category[] >= Q
	}

	//UGLY: this is ugly -- the original code was ugly too
	 := 
	for {
		--
		if  <= 0 {
			break
		}
		++
		 = .charAt()
		if  < '0' ||  > '9' {
			break
		}
	}

	if  == 0 || - == 1 {
		return false
	}
	if  == '}' {
		return true
	}
	if  != ',' {
		return false
	}
	for {
		--
		if  <= 0 {
			break
		}
		++
		 = .charAt()
		if  < '0' ||  > '9' {
			break
		}
	}

	return  > 0 &&  == '}'
}