package syntax

import (
	
	
	
	
)

func ( *RegexTree) (*Code, error) {
	 := newWriter(nil)
	,  := .codeFromTree()
	if  != nil {
		return nil, 
	}

	if slices.Contains(.CaptureSlotInUse, false) {
		 := newWriter(.CaptureSlotInUse)
		,  := .codeFromTree()
		if  != nil {
			return nil, 
		}
		if !dispatchTablesEqual(.Dispatches, .Dispatches) {
			return nil, fmt.Errorf("full and quick dispatch tables differ")
		}
		for  := range .Dispatches {
			.Dispatches[].Sets = .Dispatches[].Sets
			.Dispatches[].ASCII = .Dispatches[].ASCII
			.Dispatches[].ASCIIMasks = .Dispatches[].ASCIIMasks
		}
		.QuickCodes = .Codes
		.QuickDispatches = .Dispatches
	}
	return , nil
}

func newWriter( []bool) writer {
	return writer{
		intStack:          make([]int, 0, 32),
		emitted:           make([]int, 2),
		stringhash:        make(map[string]int),
		sethash:           make(map[string]int),
		preconsumed:       make(map[*RegexNode]bool),
		dispatchInfo:      make(map[*RegexNode]dispatchInfo),
		dispatchCodes:     make(map[*RegexNode]int),
		dispatchGotos:     make(map[*RegexNode][]int),
		quickCaptureSlots: ,
	}
}

type writer struct {
	emitted []int

	intStack          []int
	curpos            int
	stringhash        map[string]int
	stringtable       [][]rune
	sethash           map[string]int
	settable          []*CharSet
	dispatchtable     []DispatchTable
	preconsumed       map[*RegexNode]bool
	dispatchInfo      map[*RegexNode]dispatchInfo
	dispatchCodes     map[*RegexNode]int
	dispatchGotos     map[*RegexNode][]int
	counting          bool
	count             int
	trackcount        int
	caps              map[int]int
	quickCaptureSlots []bool
}

type dispatchInfo struct {
	sets     []*CharSet
	leaders  []*RegexNode
	complete []bool
	ok       bool
}

func dispatchTablesEqual(,  []DispatchTable) bool {
	if len() != len() {
		return false
	}
	for  := range  {
		if !slices.Equal([].Sets, [].Sets) ||
			!slices.Equal([].ASCIIMasks, [].ASCIIMasks) ||
			([].ASCII == nil) != ([].ASCII == nil) {
			return false
		}
		if [].ASCII != nil && *[].ASCII != *[].ASCII {
			return false
		}
	}
	return true
}

const (
	BeforeChild NodeType = 64
	AfterChild  NodeType = 128
	//MaxPrefixSize is the largest number of runes we'll use for a BoyerMoyer prefix
	MaxPrefixSize = 50
)

// The top level RegexCode generator. It does a depth-first walk
// through the tree and calls EmitFragment to emits code before
// and after each child of an interior node, and at each leaf.
//
// It runs two passes, first to count the size of the generated
// code, and second to generate the code.
//
// We should time it against the alternative, which is
// to just generate the code and grow the array as we go.
func ( *writer) ( *RegexTree) (*Code, error) {
	var (
		  *RegexNode
		 int
		  int
	)
	// construct sparse capnum mapping if some numbers are unused

	if .Capnumlist == nil || .Captop == len(.Capnumlist) {
		 = .Captop
		.caps = nil
	} else {
		 = len(.Capnumlist)
		.caps = .Caps
		for  := 0;  < len(.Capnumlist); ++ {
			.caps[.Capnumlist[]] = 
		}
	}

	.counting = true

	for {
		if !.counting {
			.emitted = make([]int, .count)
			.dispatchCodes = make(map[*RegexNode]int)
			.dispatchGotos = make(map[*RegexNode][]int)
		}

		 = .Root
		 = 0

		.emit1(Lazybranch, 0)

		for {
			if len(.Children) == 0 {
				if  := .emitFragment(.T, , 0);  != nil {
					return nil, 
				}
			} else if  < len(.Children) {
				if  := .emitFragment(.T|BeforeChild, , );  != nil {
					return nil, 
				}

				 = .Children[]

				.pushInt()
				 = 0
				continue
			}

			if .emptyStack() {
				break
			}

			 = .popInt()
			 = .Parent

			if  := .emitFragment(.T|AfterChild, , );  != nil {
				return nil, 
			}
			++
		}

		.patchJump(0, .curPos())
		.emit(Stop)

		if !.counting {
			break
		}

		.counting = false
	}
	if .quickCaptureSlots != nil {
		return &Code{Codes: .emitted, Dispatches: .dispatchtable, TrackCount: .trackcount}, nil
	}

	 := getFirstCharsPrefix()
	 := getPrefix()
	 := (.Options & RightToLeft) != 0

	var  *BmPrefix
	//TODO: benchmark string prefixes
	if  != nil && len(.PrefixStr) > 0 && MaxPrefixSize > 0 {
		if len(.PrefixStr) > MaxPrefixSize {
			// limit prefix changes to 10k
			.PrefixStr = .PrefixStr[:MaxPrefixSize]
		}
		 = newBmPrefix(.PrefixStr, .CaseInsensitive, )
	} else {
		 = nil
	}

	return &Code{
		Codes:             .emitted,
		Strings:           .stringtable,
		Sets:              .settable,
		Dispatches:        .dispatchtable,
		TrackCount:        .trackcount,
		Caps:              .caps,
		Capsize:           ,
		CaptureSlotInUse:  captureSlotsInUse(.emitted, ),
		FcPrefix:          ,
		BmPrefix:          ,
		Anchors:           getAnchors(),
		RightToLeft:       ,
		FindOptimizations: .FindOptimizations,
	}, nil
}

// The main RegexCode generator. It does a depth-first walk
// through the tree and calls EmitFragment to emits code before
// and after each child of an interior node, and at each leaf.
func ( *writer) ( NodeType,  *RegexNode,  int) error {
	 := InstOp(0)

	if (.Options & RightToLeft) != 0 {
		 |= Rtl
	}
	if (.Options & IgnoreCase) != 0 {
		 |= Ci
	}

	 := NodeType()

	switch  {
	case NtConcatenate | BeforeChild, NtConcatenate | AfterChild, NtEmpty:

	case NtAlternate | BeforeChild:
		if , , ,  := .getDispatchCandidates();  {
			if  == 0 {
				.dispatchCodes[] = .dispatchCode()
				.emit1(Dispatch|, .dispatchCodes[])
			}
			.preconsumed[[]] = true
			.patchDispatchBranch(, , .curPos())
		} else if  < len(.Children)-1 {
			.pushInt(.curPos())
			.emit1(Lazybranch, 0)
		}

	case NtAlternate | AfterChild:
		if , , ,  := .getDispatchCandidates();  {
			if  < len(.Children)-1 && ![] {
				 := .curPos()
				.emit1(Goto, 0)
				.dispatchGotos[] = append(.dispatchGotos[], )
			}
			if  == len(.Children)-1 {
				 := .curPos()
				for ,  := range .dispatchGotos[] {
					.patchJump(, )
				}
				for ,  := range  {
					if  {
						.patchDispatchBranch(, , )
					}
				}
			}
		} else if  < len(.Children)-1 {
			 := .popInt()
			.pushInt(.curPos())
			.emit1(Goto, 0)
			.patchJump(, .curPos())
		} else {
			for  := 0;  < ; ++ {
				.patchJump(.popInt(), .curPos())
			}
		}

	case NtBackRefCond | BeforeChild:
		if  == 0 {
			.emit(Setjump)
			.pushInt(.curPos())
			.emit1(Lazybranch, 0)
			.emit1(Testref, .mapCapnum(.M))
			.emit(Forejump)
		}

	case NtBackRefCond | AfterChild:
		switch  {
		case 0:
			 := .popInt()
			.pushInt(.curPos())
			.emit1(Goto, 0)
			.patchJump(, .curPos())
			.emit(Forejump)
			if len(.Children) <= 1 {
				.patchJump(.popInt(), .curPos())
			}
		case 1:
			.patchJump(.popInt(), .curPos())
		}

	case NtExprCond | BeforeChild:
		if  == 0 {
			.emit(Setjump)
			.emit(Setmark)
			.pushInt(.curPos())
			.emit1(Lazybranch, 0)
		}

	case NtExprCond | AfterChild:
		switch  {
		case 0:
			.emit(Getmark)
			.emit(Forejump)
		case 1:
			 := .popInt()
			.pushInt(.curPos())
			.emit1(Goto, 0)
			.patchJump(, .curPos())
			.emit(Getmark)
			.emit(Forejump)
			if len(.Children) <= 2 {
				.patchJump(.popInt(), .curPos())
			}
		case 2:
			.patchJump(.popInt(), .curPos())
		}

	case NtLoop | BeforeChild, NtLazyloop | BeforeChild:

		if .N < math.MaxInt32 || .M > 1 {
			if .M == 0 {
				.emit1(Nullcount, 0)
			} else {
				.emit1(Setcount, 1-.M)
			}
		} else if .M == 0 {
			.emit(Nullmark)
		} else {
			.emit(Setmark)
		}

		if .M == 0 {
			.pushInt(.curPos())
			.emit1(Goto, 0)
		}
		.pushInt(.curPos())

	case NtLoop | AfterChild, NtLazyloop | AfterChild:

		 := .curPos()
		 := InstOp( - (NtLoop | AfterChild))

		if .N < math.MaxInt32 || .M > 1 {
			if .N == math.MaxInt32 {
				.emit2(Branchcount+, .popInt(), math.MaxInt32)
			} else {
				.emit2(Branchcount+, .popInt(), .N-.M)
			}
		} else {
			.emit1(Branchmark+, .popInt())
		}

		if .M == 0 {
			.patchJump(.popInt(), )
		}

	case NtGroup | BeforeChild, NtGroup | AfterChild:

	case NtCapture | BeforeChild:
		if .emitCapture() {
			.emit(Setmark)
		}

	case NtCapture | AfterChild:
		if .emitCapture() {
			.emit2(Capturemark, .mapCapnum(.M), .mapCapnum(.N))
		}

	case NtPosLook | BeforeChild:
		// NOTE: the following line causes lookahead/lookbehind to be
		// NON-BACKTRACKING. It can be commented out with (*)
		.emit(Setjump)

		.emit(Setmark)

	case NtPosLook | AfterChild:
		.emit(Getmark)

		// NOTE: the following line causes lookahead/lookbehind to be
		// NON-BACKTRACKING. It can be commented out with (*)
		.emit(Forejump)

	case NtNegLook | BeforeChild:
		.emit(Setjump)
		.pushInt(.curPos())
		.emit1(Lazybranch, 0)

	case NtNegLook | AfterChild:
		.emit(Backjump)
		.patchJump(.popInt(), .curPos())
		.emit(Forejump)

	case NtAtomic | BeforeChild:
		.emit(Setjump)

	case NtAtomic | AfterChild:
		.emit(Forejump)

	case NtOne, NtNotone:
		if !.preconsumed[] {
			.emit1(InstOp(.T|), int(.Ch))
		}

	case NtNotoneloop, NtNotoneloopatomic, NtNotonelazy, NtOneloop, NtOneloopatomic, NtOnelazy:
		if .M > 0 {
			if .T == NtOneloop || .T == NtOnelazy || .T == NtOneloopatomic {
				.emit2(Onerep|, int(.Ch), .M)
			} else {
				.emit2(Notonerep|, int(.Ch), .M)
			}
		}
		if .N > .M {
			if .N == math.MaxInt32 {
				.emit2(InstOp(.T|), int(.Ch), math.MaxInt32)
			} else {
				.emit2(InstOp(.T|), int(.Ch), .N-.M)
			}
		}

	case NtSetloop, NtSetlazy, NtSetloopatomic:
		if .M > 0 {
			.emit2(Setrep|, .setCode(.Set), .M)
		}
		if .N > .M {
			if .N == math.MaxInt32 {
				.emit2(InstOp(.T|), .setCode(.Set), math.MaxInt32)
			} else {
				.emit2(InstOp(.T|), .setCode(.Set), .N-.M)
			}
		}

	case NtMulti:
		 := .Str
		if .preconsumed[] {
			if &Rtl != 0 {
				 = [:len()-1]
			} else {
				 = [1:]
			}
		}
		if len() > 0 {
			.emit1(InstOp(.T|), .stringCode())
		}

	case NtSet:
		if !.preconsumed[] {
			.emit1(InstOp(.T|), .setCode(.Set))
		}

	case NtRef:
		.emit1(InstOp(.T|), .mapCapnum(.M))

	case NtNothing, NtBol, NtEol, NtBoundary, NtNonboundary, NtECMABoundary, NtNonECMABoundary, NtBeginning, NtStart, NtEndZ, NtEnd, NtUpdateBumpalong:
		.emit(InstOp(.T))

	case NtGrapheme:
		.emit(InstOp(.T | ))

	default:
		return fmt.Errorf("unexpected opcode in regular expression generation: %v", )
	}

	return nil
}

func ( *writer) ( *RegexNode) bool {
	if .quickCaptureSlots == nil {
		return true
	}
	,  := .mapCapnum(.M), .mapCapnum(.N)
	if  != -1 {
		return true
	}
	return  >= 0 && ( >= len(.quickCaptureSlots) || .quickCaptureSlots[])
}

// To avoid recursion, we use a simple integer stack.
// This is the push.
func ( *writer) ( int) {
	.intStack = append(.intStack, )
}

// Returns true if the stack is empty.
func ( *writer) () bool {
	return len(.intStack) == 0
}

// This is the pop.
func ( *writer) () int {
	//get our item
	 := len(.intStack) - 1
	 := .intStack[]
	//trim our slice
	.intStack = .intStack[:]
	return 
}

// Returns the current position in the emitted code.
func ( *writer) () int {
	return .curpos
}

// Fixes up a jump instruction at the specified offset
// so that it jumps to the specified jumpDest.
func ( *writer) (,  int) {
	.emitted[+1] = 
}

func ( *writer) ( *RegexNode) ([]*CharSet, []*RegexNode, []bool, bool) {
	if ,  := .dispatchInfo[];  {
		return .sets, .leaders, .complete, .ok
	}
	, , ,  := .dispatchCandidates()
	.dispatchInfo[] = dispatchInfo{sets: , leaders: , complete: , ok: }
	return , , , 
}

func ( *writer) ( []*CharSet) int {
	if .counting {
		return 0
	}
	 := DispatchTable{
		Sets:     make([]int, len()),
		Branches: make([]int, len()),
	}
	if len() >= 4 {
		.ASCII = &[128]uint16{}
	} else {
		.ASCIIMasks = make([]uint64, len()*2)
	}
	for ,  := range  {
		.Sets[] = .setCode()
		for  := rune(0);  < 128; ++ {
			if .CharIn() {
				if .ASCII != nil {
					.ASCII[] = uint16( + 1)
				} else {
					.ASCIIMasks[*2+int(>>6)] |= uint64(1) << ( & 63)
				}
			}
		}
	}
	 := len(.dispatchtable)
	.dispatchtable = append(.dispatchtable, )
	return 
}

func ( *writer) ( *RegexNode, ,  int) {
	if .counting {
		return
	}
	 := .dispatchCodes[]
	.dispatchtable[].Branches[] = 
}

// Returns an index in the set table for a charset
// uses a map to eliminate duplicates.
func ( *writer) ( *CharSet) int {
	if .counting {
		return 0
	}

	 := &bytes.Buffer{}

	.mapHashFill()
	 := .String()
	,  := .sethash[]
	if ! {
		 = len(.sethash)
		.sethash[] = 
		.settable = append(.settable, )
	}
	return 
}

// Returns an index in the string table for a string.
// uses a map to eliminate duplicates.
func ( *writer) ( []rune) int {
	if .counting {
		return 0
	}

	 := string()
	,  := .stringhash[]
	if ! {
		 = len(.stringhash)
		.stringhash[] = 
		.stringtable = append(.stringtable, )
	}

	return 
}

// When generating code on a regex that uses a sparse set
// of capture slots, we hash them to a dense set of indices
// for an array of capture slots. Instead of doing the hash
// at match time, it's done at compile time, here.
func ( *writer) ( int) int {
	if  == -1 {
		return -1
	}

	if .caps != nil {
		return .caps[]
	}

	return 
}

// Emits a zero-argument operation. Note that the emit
// functions all run in two modes: they can emit code, or
// they can just count the size of the code.
func ( *writer) ( InstOp) {
	if .counting {
		.count++
		if opcodeBacktracks() {
			.trackcount++
		}
		return
	}
	.emitted[.curpos] = int()
	.curpos++
}

// Emits a one-argument operation.
func ( *writer) ( InstOp,  int) {
	if .counting {
		.count += 2
		if opcodeBacktracks() {
			.trackcount++
		}
		return
	}
	.emitted[.curpos] = int()
	.curpos++
	.emitted[.curpos] = 
	.curpos++
}

// Emits a two-argument operation.
func ( *writer) ( InstOp, ,  int) {
	if .counting {
		.count += 3
		if opcodeBacktracks() {
			.trackcount++
		}
		return
	}
	.emitted[.curpos] = int()
	.curpos++
	.emitted[.curpos] = 
	.curpos++
	.emitted[.curpos] = 
	.curpos++
}