func (n *regexNode) reduceAlternation() *regexNode {
	if len(n.children) == 0 {
		return newRegexNode(ntNothing, n.options)
	}
	wasLastSet := false
	lastNodeCannotMerge := false
	var optionsLast RegexOptions
	var i, j int
	for i, j = 0, 0; i < len(n.children); i, j = i+1, j+1 {
		at := n.children[i]
		if j < i {
			n.children[j] = at
		}
		for {
			if at.t == ntAlternate {
				for k := 0; k < len(at.children); k++ {
					at.children[k].next = n
				}
				n.insertChildren(i+1, at.children)
				j--
			} else if at.t == ntSet || at.t == ntOne {
				optionsAt := at.options & (RightToLeft | IgnoreCase)
				if at.t == ntSet {
					if !wasLastSet || optionsLast != optionsAt || lastNodeCannotMerge || !at.set.IsMergeable() {
						wasLastSet = true
						lastNodeCannotMerge = !at.set.IsMergeable()
						optionsLast = optionsAt
						break
					}
				} else if !wasLastSet || optionsLast != optionsAt || lastNodeCannotMerge {
					wasLastSet = true
					lastNodeCannotMerge = false
					optionsLast = optionsAt
					break
				}
				j--
				prev := n.children[j]
				var prevCharClass *CharSet
				if prev.t == ntOne {
					prevCharClass = &CharSet{}
					prevCharClass.addChar(prev.ch)
				} else {
					prevCharClass = prev.set
				}
				if at.t == ntOne {
					prevCharClass.addChar(at.ch)
				} else {
					prevCharClass.addSet(*at.set)
				}
				prev.t = ntSet
				prev.set = prevCharClass
			} else if at.t == ntNothing {
				j--
			} else {
				wasLastSet = false
				lastNodeCannotMerge = false
			}
			break
		}
	}
	if j < i {
		n.removeChildren(j, i)
	}
	return n.stripEnation(ntNothing)
}
Basic optimization. Single-letter alternations can be replaced
by faster set specifications, and nested alternations with no
intervening operators can be flattened:

a|b|c|def|g|h -> [a-c]|def|[gh]
apple|(?:orange|pear)|grape -> apple|orange|pear|grape
package syntax
import (
	"bytes"
	"fmt"
	"math"
	"strconv"
)
type RegexTree struct {
	root       *regexNode
	caps       map[int]int
	capnumlist []int
	captop     int
	Capnames   map[string]int
	Caplist    []string
	options    RegexOptions
}
type regexNode struct {
	t        nodeType
	children []*regexNode
	str      []rune
	set      *CharSet
	ch       rune
	m        int
	n        int
	options  RegexOptions
	next     *regexNode
}
type nodeType int32
const (
	ntOnerep      nodeType = 0   
	ntNotonerep            = 1   
	ntSetrep               = 2   
	ntOneloop              = 3   
	ntNotoneloop           = 4   
	ntSetloop              = 5   
	ntOnelazy              = 6   
	ntNotonelazy           = 7   
	ntSetlazy              = 8   
	ntOne                  = 9   
	ntNotone               = 10  
	ntSet                  = 11  
	ntMulti                = 12  
	ntRef                  = 13  
	ntBol                  = 14  
	ntEol                  = 15  
	ntBoundary             = 16  
	ntNonboundary          = 17  
	ntBeginning            = 18  
	ntStart                = 19  
	ntEndZ                 = 20  
	ntEnd                  = 21  
	ntNothing     = 22  
	ntEmpty       = 23  
	ntAlternate   = 24  
	ntConcatenate = 25  
	ntLoop        = 26  
	ntLazyloop    = 27  
	ntCapture     = 28  
	ntGroup       = 29  
	ntRequire     = 30  
	ntPrevent     = 31  
	ntGreedy      = 32  
	ntTestref     = 33  
	ntTestgroup   = 34  
	ntECMABoundary    = 41  
	ntNonECMABoundary = 42  
)
func newRegexNode(t nodeType, opt RegexOptions) *regexNode {
	return &regexNode{
		t:       t,
		options: opt,
	}
}
func newRegexNodeCh(t nodeType, opt RegexOptions, ch rune) *regexNode {
	return &regexNode{
		t:       t,
		options: opt,
		ch:      ch,
	}
}
func newRegexNodeStr(t nodeType, opt RegexOptions, str []rune) *regexNode {
	return &regexNode{
		t:       t,
		options: opt,
		str:     str,
	}
}
func newRegexNodeSet(t nodeType, opt RegexOptions, set *CharSet) *regexNode {
	return &regexNode{
		t:       t,
		options: opt,
		set:     set,
	}
}
func newRegexNodeM(t nodeType, opt RegexOptions, m int) *regexNode {
	return &regexNode{
		t:       t,
		options: opt,
		m:       m,
	}
}
func newRegexNodeMN(t nodeType, opt RegexOptions, m, n int) *regexNode {
	return &regexNode{
		t:       t,
		options: opt,
		m:       m,
		n:       n,
	}
}
func (n *regexNode) writeStrToBuf(buf *bytes.Buffer) {
	for i := 0; i < len(n.str); i++ {
		buf.WriteRune(n.str[i])
	}
}
func (n *regexNode) addChild(child *regexNode) {
	reduced := child.reduce()
	n.children = append(n.children, reduced)
	reduced.next = n
}
func (n *regexNode) insertChildren(afterIndex int, nodes []*regexNode) {
	newChildren := make([]*regexNode, 0, len(n.children)+len(nodes))
	n.children = append(append(append(newChildren, n.children[:afterIndex]...), nodes...), n.children[afterIndex:]...)
}
func (n *regexNode) removeChildren(startIndex, endIndex int) {
	n.children = append(n.children[:startIndex], n.children[endIndex:]...)
}
func (n *regexNode) makeRep(t nodeType, min, max int) {
	n.t += (t - ntOne)
	n.m = min
	n.n = max
}
func (n *regexNode) reduce() *regexNode {
	switch n.t {
	case ntAlternate:
		return n.reduceAlternation()
	case ntConcatenate:
		return n.reduceConcatenation()
	case ntLoop, ntLazyloop:
		return n.reduceRep()
	case ntGroup:
		return n.reduceGroup()
	case ntSet, ntSetloop:
		return n.reduceSet()
	default:
		return n
	}
}