package syntax

import (
	
	
	
	
	
	
)

type RegexOptions int32

const (
	IgnoreCase              RegexOptions = 0x0001 // "i"
	Multiline               RegexOptions = 0x0002 // "m"
	ExplicitCapture         RegexOptions = 0x0004 // "n"
	Singleline              RegexOptions = 0x0010 // "s"
	IgnorePatternWhitespace RegexOptions = 0x0020 // "x"
	RightToLeft             RegexOptions = 0x0040 // "r"
	ECMAScript              RegexOptions = 0x0100 // "e"
	RE2                     RegexOptions = 0x0200 // RE2 compat mode
	Unicode                 RegexOptions = 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 '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"
	ErrInvalidECMAGroupName       = "invalid capture group name"
	ErrDuplicateGroupName         = "duplicate capture group name"
	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"
	ErrShorthandClassInCharRange  = "cannot create range with shorthand escape sequence \\%v"
	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

	autocap  int
	capcount int
	captop   int
	capsize  int

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

	maintainCaptureOrder bool

	capnumlist  []int
	capnamelist []string

	options         RegexOptions
	optionsStack    []RegexOptions
	ignoreNextParen bool
}

type ParseOptions struct {
	RegexOptions         RegexOptions
	MaintainCaptureOrder bool
	CodeGen              bool
}

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

// Parse converts a regex string into a parse tree
func ( string,  ParseOptions) (*RegexTree, error) {
	 := parser{
		options:              .RegexOptions,
		caps:                 make(map[int]int),
		maintainCaptureOrder: .MaintainCaptureOrder || (.RegexOptions&ECMAScript) != 0,
	}
	.setPattern()

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

	.reset(.RegexOptions)
	,  := .scanRegex()

	if  != nil {
		return nil, 
	}
	 := &RegexTree{
		Root:       ,
		Caps:       .caps,
		Capnumlist: .capnumlist,
		Captop:     .captop,
		Capnames:   .capnames,
		Caplist:    .capnamelist,
		Options:    .RegexOptions,
	}
	.FindOptimizations = newFindOptimizations(, )

	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) error {
	if .capnames == nil {
		.capnames = make(map[string]int)
	}

	if ,  := .capnames[]; ! {
		if .maintainCaptureOrder {
			 := .consumeAutocap()
			.capnames[] = 
			.noteCaptureSlot(, )
		} else {
			.capnames[] = 
		}
		.capnamelist = append(.capnamelist, )
	} else if .useOptionE() {
		return .getErr(ErrDuplicateGroupName)
	}
	return nil
}

func ( *parser) () {
	if .maintainCaptureOrder {
		.assignOrderedNameSlots()
		return
	}

	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) () {
	if !.useOptionE() && .capnames == nil && .capcount == .captop {
		return
	}

	if .capcount < .captop {
		.capnumlist = make([]int, .capcount)
		 := 0
		for  := range .caps {
			.capnumlist[] = 
			++
		}
		sort.Ints(.capnumlist)
	}

	 := .capnamelist
	.capnamelist = make([]string, .capcount)
	if .capnames == nil {
		.capnames = make(map[string]int, .capcount)
	}

	for ,  := range  {
		 := .capnames[]
		 := 
		if .capnumlist != nil {
			for ,  := range .capnumlist {
				if  ==  {
					 = 
					break
				}
			}
		}
		.capnamelist[] = 
	}

	for  := 0;  < .capcount; ++ {
		 := 
		if .capnumlist != nil {
			 = .capnumlist[]
		}
		if .useOptionE() {
			continue
		}
		if .capnamelist[] == "" {
			.capnamelist[] = strconv.Itoa()
		}
		if ,  := .capnames[.capnamelist[]]; ! {
			.capnames[.capnamelist[]] = 
		}
	}
}

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

func ( *parser) ( int) {
	if .maintainCaptureOrder &&  == .autocap {
		.consumeAutocap()
	}
}

// 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' && .isGroupNameStartChar() {
							if  >= '1' &&  <= '9' && !.useOptionE() {
								,  := .scanDecimal()
								if  != nil {
									return 
								}
								if .maintainCaptureOrder {
									if  = .noteCaptureName(strconv.Itoa(), );  != nil {
										return 
									}
								} else {
									.noteCaptureSlot(, )
								}
							} else {
								,  := .scanCapname()
								if  != nil {
									return 
								}
								if  = .noteCaptureName(, );  != nil {
									return 
								}
							}
						}
					} else if .useRE2() && .charsRight() > 2 && (.rightChar(0) == 'P' && .rightChar(1) == '<') {
						// RE2-compat (?P<)
						.moveRight(2)
						 = .rightChar(0)
						if IsWordChar() {
							,  := .scanCapname()
							if  != nil {
								return 
							}
							if  = .noteCaptureName(, );  != nil {
								return 
							}
						}

					} 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) {
	var  rune // 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)
				if isStopperX() && ( != '{' || .isTrueQuantifier()) {
					break
				}
				.moveRight(1)
			}
		} else {
			for .charsRight() > 0 {
				 = .rightChar(0)
				if isSpecial() && ( != '{' || .isTrueQuantifier()) {
					break
				}
				.moveRight(1)
			}
		}

		 := .textpos()

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

		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 '(':
			if .useRE2() && .charsRight() >= 3 && .rightChar(0) == '?' && .rightChar(1) == 'P' && .rightChar(2) == '=' {
				,  := .scanPythonNamedBackref()
				if  != nil {
					return nil, 
				}
				.addUnitNode()
				break
			}

			.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 '\\':
			 := !.useOptionE() && .charsRight() > 0 && .rightChar(0) == 'Q'
			,  := .scanBackslash(false)
			if  != nil {
				return nil, 
			}
			if  {
				.addQuotedUnit()
			} else {
				.addUnitNode()
			}

		case '^':
			if .useOptionM() {
				.addUnitType(NtBol)
			} else {
				.addUnitType(NtBeginning)
			}

		case '$':
			if .useOptionM() {
				.addUnitType(NtEol)
			} else if .useRE2() || .useOptionE() {
				// RE2 and EcmaScript define $ as "asserts position at the end of the string"
				// PCRE/.NET adds "or before the line terminator right at the end of the string (if any)"
				// which is NtEnd, not NtEndZ
				.addUnitType(NtEnd)
			} 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) == '?' {
				.moveRight(1)
				 = true
			} else if .charsRight() > 0 && .rightChar(0) == '+' && !.useOptionE() {
				.moveRight(1)
				 = true
			}

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

			.addConcatenate3(, , , )
		}

	:
	}

:
	;

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

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

	return .unit.finalOptimize(), 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  && .isGroupNameStartChar() {
		,  := .scanCapname()
		if  != nil {
			return nil, 
		}

		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
}

func ( *parser) ( rune) bool {
	if .useOptionE() {
		return IsECMAIdentifierStartChar() ||  == '\\'
	}
	return IsWordChar()
}

func ( *parser) () (*RegexNode, error) {
	.moveRight(3)
	if .charsRight() == 0 {
		return nil, .getErr(ErrMalformedNameRef)
	}

	 := .rightChar(0)
	if !.isGroupNameStartChar() {
		return nil, .getErr(ErrInvalidGroupName)
	}

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

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

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

	return newRegexNodeM(NtRef, .options, .captureSlotFromName()), 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 .charsRight() > 0 {
		switch  = .moveRightGetChar();  {
		case ':':
			 = NtGroup

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

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

		case '>':
			 = NtAtomic

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

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

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

				.options |= RightToLeft
				 = NtPosLook

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

				.options |= RightToLeft
				 = NtNegLook

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

				// grab part before -

				if  >= '0' &&  <= '9' && !.useOptionE() {
					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 .isGroupNameStartChar() {
					,  := .scanCapname()
					if  != nil {
						return nil, 
					}

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

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

				// grab part after - if any

				if !.useOptionE() && ( != -1 || ) && .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  != nil {
							return nil, 
						}

						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() ==  {
					.consumeCaptureSlot()
					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(NtBackRefCond, .options, ), nil
						}
						return nil, .getErr(ErrUndefinedReference, )
					}

					return nil, .getErr(ErrMalformedReference, )

				} else if IsWordChar() {
					,  := .scanCapname()
					if  != nil {
						return nil, 
					}

					if .isCaptureName() && .charsRight() > 0 && .moveRightGetChar() == ')' {
						return newRegexNodeM(NtBackRefCond, .options, .captureSlotFromName()), nil
					}
				}
			}
			// not a backref
			 = NtExprCond
			.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  != nil {
						return nil, 
					}

					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() == '>' {
						.consumeCaptureSlot()
						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 != NtExprCond {
				.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 'Q':
		if .useOptionE() {
			return .scanBasicBackslash()
		}
		.moveRight(1)
		 := .scanQuoted()
		if  {
			return nil, nil
		}
		return newRegexNodeStr(NtMulti, .options&^IgnoreCase, ), nil

	case 'R':
		if .useOptionE() || .useRE2() {
			return .scanBasicBackslash()
		}
		.moveRight(1)
		if  {
			return nil, nil
		}
		return newUnicodeNewlineNode(.options), nil

	case 'X':
		if .useOptionE() || .useRE2() {
			return .scanBasicBackslash()
		}
		.moveRight(1)
		if  {
			return nil, nil
		}
		return newRegexNode(NtGrapheme, .options), nil

	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':
		// skip if we're in ECMAScript mode WITHOUT unicode flag
		if .useOptionE() && !.useOptionU() {
			return .scanBasicBackslash()
		}

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

		return newRegexNodeSet(NtSet, .options, ), nil

	default:
		return .scanBasicBackslash()
	}
}

// newUnicodeNewlineNode constructs \R from existing general-purpose nodes.
// Its branches have disjoint starting sets, and the optional LF is atomic, so
// reduction can remove the outer atomic wrapper without changing semantics.
func newUnicodeNewlineNode( RegexOptions) *RegexNode {
	 &^= IgnoreCase

	,  := '\r', '\n'
	if &RightToLeft != 0 {
		// Stored concatenations are already in execution order. In RTL, consume
		// LF first and then the optional preceding CR.
		,  = '\n', '\r'
	}

	 := &CharSet{}
	for ,  := range []rune{'\n', '\v', '\f', '\u0085', '\u2028', '\u2029'} {
		if  !=  {
			.addChar()
		}
	}
	if '\r' !=  {
		.addChar('\r')
	}

	 := newRegexNode(NtConcatenate, )
	.addChild(newRegexNodeCh(NtOne, , ))
	.addChild(newRegexNodeCh(NtOne, , ).makeQuantifier(false, 0, 1))

	 := newRegexNode(NtAlternate, )
	.addChild(newRegexNodeSet(NtSet, , ))
	.addChild()

	 := newRegexNode(NtAtomic, )
	.addChild()
	return 
}

// scanQuoted scans the literal text after \Q through the next \E, or through
// the end of the pattern when there is no terminator.
func ( *parser) () []rune {
	 := .textpos()
	var  []rune
	for .charsRight() > 0 {
		if .rightChar(0) == '\\' && .charsRight() > 1 && .rightChar(1) == 'E' {
			 = append(, .pattern[:.textpos()]...)
			.moveRight(2)
			return 
		}
		.moveRight(1)
	}
	return append(, .pattern[:.textpos()]...)
}

// 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() || .useOptionU() || 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  != nil {
			return nil, 
		}

		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) != '{' && (!.useOptionE() || !.useOptionU()) {
		 := string(.moveRightGetChar())
		// check if it's a valid cat
		,  := canonicalUnicodeCatName()
		if ! {
			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)
	}

	,  := canonicalUnicodeCatName()
	if ! {
		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, error) {
	 := .textpos()
	var  strings.Builder
	 := false
	 := 0

	for .charsRight() > 0 {
		 := .textpos()
		 := .moveRightGetChar()
		var  error
		 := false

		if  == '\\' {
			if .charsRight() == 0 || .rightChar(0) != 'u' {
				return "", .getErr(ErrInvalidECMAGroupName)
			}
			 = true
			.moveRight(1)
			if .charsRight() > 0 && .rightChar(0) == '{' {
				if !.useOptionU() {
					return "", .getErr(ErrInvalidECMAGroupName)
				}
				.moveRight(1)
				,  = .scanHexUntilBrace()
			} else {
				,  = .scanHex(4)
			}
			if  != nil {
				return "", 
			}
			if ! {
				.WriteString(string(.pattern[:]))
				 = true
			}
		}

		 := IsECMAIdentifierChar()
		if  == 0 {
			 = IsECMAIdentifierStartChar()
		}
		if ! {
			if  {
				return "", .getErr(ErrInvalidECMAGroupName)
			}
			.textto()
			break
		}
		if  {
			.WriteRune()
		}
		++
	}

	if  {
		return .String(), nil
	}
	return string(.pattern[:.textpos()]), nil
}

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

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

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

func ( *parser) () (string, error) {
	if .useOptionE() {
		return .scanECMACapname()
	}

	return .scanWord(), nil
}

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

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

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

	for ; .charsRight() > 0 || len() > 0;  = false {
		 := false
		for {
			if len() > 0 {
				 = [0]
				 = [1:]
				 = true
				break
			}
			 = .moveRightGetChar()
			if  == '\\' && !.useOptionE() && .charsRight() > 0 && .rightChar(0) == 'Q' {
				.moveRight(1)
				 = .scanQuoted()
				if len() == 0 {
					if .charsRight() == 0 {
						break
					}
					continue
				}
				continue
			}
			break
		}
		if  == ']' {
			if  {
				// A quoted closing bracket is an ordinary class member.
			} else if ! {
				 = true
				break
			} else if .useOptionE() {
				if ! {
					.addRanges(NoneClass().ranges)
				}
				 = true
				break
			}

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

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

			case 'W', 'w':
				if ! {
					if  {
						if !.useOptionE() {
							return nil, .getErr(ErrBadClassInCharRange, )
						}
						.addChar()
						.addChar('-')
						 = false
					}

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

			case 'p', 'P':
				if .useOptionE() && !.useOptionU() &&  == 'P' &&  {
					return nil, .getErr(ErrShorthandClassInCharRange, string())
				}
				if .useOptionE() && !.useOptionU() &&  == 'p' {
					if ! {
						if  {
							if  >  {
								return nil, .getErr(ErrReversedCharRange, , )
							}
							.addRange(, )
							 = false
						} else if .charsRight() >= 2 && .rightChar(0) == '-' && .rightChar(1) != ']' {
							.addChar('-')
							.moveRight(1)
							 := .moveRightGetChar()
							if  >  {
								return nil, .getErr(ErrReversedCharRange, , )
							}
							.addRange(, )
						} else {
							.addChar()
						}
					}
					continue
				}
				if ! {
					,  := .parseProperty()
					if  != nil {
						return nil, 
					}
					if  {
						return nil, .getErr(ErrShorthandClassInCharRange, string())
					}
					.addCategory(, ( != 'p'), )
				} else {
					if ,  := .parseProperty();  != nil {
						return nil, 
					}
				}

				continue

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

			default:
				.moveLeft()
				var  error
				,  = .scanCharEscape() // non-literal character
				if  != nil {
					return nil, 
				}
				 = true
			}
		} 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)
				}

				 := .scanWord() // 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 imnsxu-imnsxu option string, stops at the first unrecognized char.
func ( *parser) () {

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

		switch  {
		case '-':
			 = true
		case '+':
			 = false
		default:
			 := 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) {
	 := .unit.makeQuantifier(, , )
	if  {
		 := newRegexNode(NtAtomic, .options)
		.addChild()
		 = 
	}
	.concatenation.addChild()
	.unit = nil
}

// addQuotedUnit adds the literal characters scanned by \Q...\E. All but the
// final character are completed immediately so a following quantifier applies
// to the final literal character, just as if each character had been escaped.
func ( *parser) ( *RegexNode) {
	 := .Str
	if len() == 0 {
		.restoreLastUnit()
		if .unit == nil {
			.unit = newRegexNode(NtEmpty, .options)
		}
		return
	}
	for ,  := range [:len()-1] {
		.concatenation.addChild(newRegexNodeCh(NtOne, .options, ))
	}
	.unit = newRegexNodeCh(NtOne, .options, [len()-1])
}

// restoreLastUnit makes an immediately preceding literal quantifiable again
// when an empty \Q\E appears between it and its quantifier.
func ( *parser) () {
	if len(.concatenation.Children) == 0 {
		return
	}
	 := len(.concatenation.Children) - 1
	 := .concatenation.Children[]
	.concatenation.Children = .concatenation.Children[:]
	.Parent = nil
	if .T == NtMulti && len(.Str) > 1 {
		 := append([]rune(nil), .Str[:len(.Str)-1]...)
		.concatenation.addChild(newRegexNodeStr(NtMulti, .Options, ))
		.unit = newRegexNodeCh(NtOne, .options, .Str[len(.Str)-1])
		return
	}
	.unit = 
}

// Sets the current unit to a single char node
func ( *parser) ( rune) {
	.unit = newRegexNodeCh(NtOne, .options, )
}

// Sets the current unit to a single inverse-char node
func ( *parser) ( rune) {
	.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 == NtExprCond || .group.T == NtBackRefCond {
		.group.addChild(.concatenation.reverseLeft())
		if (.group.T == NtBackRefCond && 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) {
	if  == 0 {
		return
	}
	if  == 1 {
		var  *RegexNode

		if  {
			 = newRegexNodeCh(NtOne, .options&^IgnoreCase, .pattern[])
		} else {
			 = newRegexNodeCh(NtOne, .options, .pattern[])
		}
		.concatenation.addChild()
		return
	}

	if  > 1 && (!.useOptionI() ||  || !anyParticipateInCaseConversion(.pattern[:+])) {
		 := make([]rune, )
		copy(, .pattern[:+])
		.concatenation.addChild(newRegexNodeStr(NtMulti, .options&^IgnoreCase, ))
		return
	}

	// each letter gets an upper-lower range
	for  := ;  < +; ++ {
		.concatenation.addChild(newRegexNodeCh(NtOne, .options, .pattern[]))
	}
}

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

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

	// The first () inside a Testgroup group goes directly to the group
	if .group.T == NtExprCond && 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 == NtExprCond || .group.T == NtBackRefCond {
		.group.addChild(.concatenation.reverseLeft())
	} else {
		.alternation.addChild(.concatenation.reverseLeft())
	}

	.concatenation = newRegexNode(NtConcatenate, .options)
}

// For categorizing ascii characters.

const (
	Q byte = 5 // quantifier
	S byte = 4 // ordinary stopper
	Z byte = 3 // ScanBlank stopper
	X byte = 2 // whitespace
	E byte = 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 &&  == '}'
}