package regexp2
import (
"bytes"
"errors"
"fmt"
"math"
"strconv"
"strings"
"time"
"unicode"
"github.com/dlclark/regexp2/syntax"
)
type runner struct {
re *Regexp
code *syntax .Code
runtextstart int
runtext []rune
runtextpos int
runtextend int
runtrack []int
runtrackpos int
runstack []int
runstackpos int
runcrawl []int
runcrawlpos int
runtrackcount int
runmatch *Match
ignoreTimeout bool
timeout time .Duration
deadline fasttime
operator syntax .InstOp
codepos int
rightToLeft bool
caseInsensitive bool
}
func (re *Regexp ) run (quick bool , textstart int , input []rune ) (*Match , error ) {
runner := re .getRunner ()
defer re .putRunner (runner )
if textstart < 0 {
if re .RightToLeft () {
textstart = len (input )
} else {
textstart = 0
}
}
return runner .scan (input , textstart , quick , re .MatchTimeout )
}
func (r *runner ) scan (rt []rune , textstart int , quick bool , timeout time .Duration ) (*Match , error ) {
r .timeout = timeout
r .ignoreTimeout = (time .Duration (math .MaxInt64 ) == timeout )
r .runtextstart = textstart
r .runtext = rt
r .runtextend = len (rt )
stoppos := r .runtextend
bump := 1
if r .re .RightToLeft () {
bump = -1
stoppos = 0
}
r .runtextpos = textstart
initted := false
r .startTimeoutWatch ()
for {
if r .re .Debug () {
fmt .Printf ("\nSearch range: from 0 to %v\n" , r .runtextend )
fmt .Printf ("Firstchar search starting at %v stopping at %v\n" , r .runtextpos , stoppos )
}
if r .findFirstChar () {
if err := r .checkTimeout (); err != nil {
return nil , err
}
if !initted {
r .initMatch ()
initted = true
}
if r .re .Debug () {
fmt .Printf ("Executing engine starting at %v\n\n" , r .runtextpos )
}
if err := r .execute (); err != nil {
return nil , err
}
if r .runmatch .matchcount [0 ] > 0 {
return r .tidyMatch (quick ), nil
}
r .runtrackpos = len (r .runtrack )
r .runstackpos = len (r .runstack )
r .runcrawlpos = len (r .runcrawl )
}
if r .runtextpos == stoppos {
r .tidyMatch (true )
return nil , nil
}
r .runtextpos += bump
}
}
func (r *runner ) execute () error {
r .goTo (0 )
for {
if r .re .Debug () {
r .dumpState ()
}
if err := r .checkTimeout (); err != nil {
return err
}
switch r .operator {
case syntax .Stop :
return nil
case syntax .Nothing :
break
case syntax .Goto :
r .goTo (r .operand (0 ))
continue
case syntax .Testref :
if !r .runmatch .isMatched (r .operand (0 )) {
break
}
r .advance (1 )
continue
case syntax .Lazybranch :
r .trackPush1 (r .textPos ())
r .advance (1 )
continue
case syntax .Lazybranch | syntax .Back :
r .trackPop ()
r .textto (r .trackPeek ())
r .goTo (r .operand (0 ))
continue
case syntax .Setmark :
r .stackPush (r .textPos ())
r .trackPush ()
r .advance (0 )
continue
case syntax .Nullmark :
r .stackPush (-1 )
r .trackPush ()
r .advance (0 )
continue
case syntax .Setmark | syntax .Back , syntax .Nullmark | syntax .Back :
r .stackPop ()
break
case syntax .Getmark :
r .stackPop ()
r .trackPush1 (r .stackPeek ())
r .textto (r .stackPeek ())
r .advance (0 )
continue
case syntax .Getmark | syntax .Back :
r .trackPop ()
r .stackPush (r .trackPeek ())
break
case syntax .Capturemark :
if r .operand (1 ) != -1 && !r .runmatch .isMatched (r .operand (1 )) {
break
}
r .stackPop ()
if r .operand (1 ) != -1 {
r .transferCapture (r .operand (0 ), r .operand (1 ), r .stackPeek (), r .textPos ())
} else {
r .capture (r .operand (0 ), r .stackPeek (), r .textPos ())
}
r .trackPush1 (r .stackPeek ())
r .advance (2 )
continue
case syntax .Capturemark | syntax .Back :
r .trackPop ()
r .stackPush (r .trackPeek ())
r .uncapture ()
if r .operand (0 ) != -1 && r .operand (1 ) != -1 {
r .uncapture ()
}
break
case syntax .Branchmark :
r .stackPop ()
matched := r .textPos () - r .stackPeek ()
if matched != 0 {
r .trackPush2 (r .stackPeek (), r .textPos ())
r .stackPush (r .textPos ())
r .goTo (r .operand (0 ))
} else {
r .trackPushNeg1 (r .stackPeek ())
r .advance (1 )
}
continue
case syntax .Branchmark | syntax .Back :
r .trackPopN (2 )
r .stackPop ()
r .textto (r .trackPeekN (1 ))
r .trackPushNeg1 (r .trackPeek ())
r .advance (1 )
continue
case syntax .Branchmark | syntax .Back2 :
r .trackPop ()
r .stackPush (r .trackPeek ())
break
case syntax .Lazybranchmark :
{
r .stackPop ()
oldMarkPos := r .stackPeek ()
if r .textPos () != oldMarkPos {
if oldMarkPos != -1 {
r .trackPush2 (oldMarkPos , r .textPos ())
} else {
r .trackPush2 (r .textPos (), r .textPos ())
}
} else {
r .stackPush (oldMarkPos )
r .trackPushNeg1 (r .stackPeek ())
}
r .advance (1 )
continue
}
case syntax .Lazybranchmark | syntax .Back :
r .trackPopN (2 )
pos := r .trackPeekN (1 )
r .trackPushNeg1 (r .trackPeek ())
r .stackPush (pos )
r .textto (pos )
r .goTo (r .operand (0 ))
continue
case syntax .Lazybranchmark | syntax .Back2 :
r .stackPop ()
r .trackPop ()
r .stackPush (r .trackPeek ())
break
case syntax .Setcount :
r .stackPush2 (r .textPos (), r .operand (0 ))
r .trackPush ()
r .advance (1 )
continue
case syntax .Nullcount :
r .stackPush2 (-1 , r .operand (0 ))
r .trackPush ()
r .advance (1 )
continue
case syntax .Setcount | syntax .Back :
r .stackPopN (2 )
break
case syntax .Nullcount | syntax .Back :
r .stackPopN (2 )
break
case syntax .Branchcount :
r .stackPopN (2 )
mark := r .stackPeek ()
count := r .stackPeekN (1 )
matched := r .textPos () - mark
if count >= r .operand (1 ) || (matched == 0 && count >= 0 ) {
r .trackPushNeg2 (mark , count )
r .advance (2 )
} else {
r .trackPush1 (mark )
r .stackPush2 (r .textPos (), count +1 )
r .goTo (r .operand (0 ))
}
continue
case syntax .Branchcount | syntax .Back :
r .trackPop ()
r .stackPopN (2 )
if r .stackPeekN (1 ) > 0 {
r .textto (r .stackPeek ())
r .trackPushNeg2 (r .trackPeek (), r .stackPeekN (1 )-1 )
r .advance (2 )
continue
}
r .stackPush2 (r .trackPeek (), r .stackPeekN (1 )-1 )
break
case syntax .Branchcount | syntax .Back2 :
r .trackPopN (2 )
r .stackPush2 (r .trackPeek (), r .trackPeekN (1 ))
break
case syntax .Lazybranchcount :
r .stackPopN (2 )
mark := r .stackPeek ()
count := r .stackPeekN (1 )
if count < 0 {
r .trackPushNeg1 (mark )
r .stackPush2 (r .textPos (), count +1 )
r .goTo (r .operand (0 ))
} else {
r .trackPush3 (mark , count , r .textPos ())
r .advance (2 )
}
continue
case syntax .Lazybranchcount | syntax .Back :
r .trackPopN (3 )
mark := r .trackPeek ()
textpos := r .trackPeekN (2 )
if r .trackPeekN (1 ) < r .operand (1 ) && textpos != mark {
r .textto (textpos )
r .stackPush2 (textpos , r .trackPeekN (1 )+1 )
r .trackPushNeg1 (mark )
r .goTo (r .operand (0 ))
continue
} else {
r .stackPush2 (r .trackPeek (), r .trackPeekN (1 ))
break
}
case syntax .Lazybranchcount | syntax .Back2 :
r .trackPop ()
r .stackPopN (2 )
r .stackPush2 (r .trackPeek (), r .stackPeekN (1 )-1 )
break
case syntax .Setjump :
r .stackPush2 (r .trackpos (), r .crawlpos ())
r .trackPush ()
r .advance (0 )
continue
case syntax .Setjump | syntax .Back :
r .stackPopN (2 )
break
case syntax .Backjump :
r .stackPopN (2 )
r .trackto (r .stackPeek ())
for r .crawlpos () != r .stackPeekN (1 ) {
r .uncapture ()
}
break
case syntax .Forejump :
r .stackPopN (2 )
r .trackto (r .stackPeek ())
r .trackPush1 (r .stackPeekN (1 ))
r .advance (0 )
continue
case syntax .Forejump | syntax .Back :
r .trackPop ()
for r .crawlpos () != r .trackPeek () {
r .uncapture ()
}
break
case syntax .Bol :
if r .leftchars () > 0 && r .charAt (r .textPos ()-1 ) != '\n' {
break
}
r .advance (0 )
continue
case syntax .Eol :
if r .rightchars () > 0 && r .charAt (r .textPos ()) != '\n' {
break
}
r .advance (0 )
continue
case syntax .Boundary :
if !r .isBoundary (r .textPos (), 0 , r .runtextend ) {
break
}
r .advance (0 )
continue
case syntax .Nonboundary :
if r .isBoundary (r .textPos (), 0 , r .runtextend ) {
break
}
r .advance (0 )
continue
case syntax .ECMABoundary :
if !r .isECMABoundary (r .textPos (), 0 , r .runtextend ) {
break
}
r .advance (0 )
continue
case syntax .NonECMABoundary :
if r .isECMABoundary (r .textPos (), 0 , r .runtextend ) {
break
}
r .advance (0 )
continue
case syntax .Beginning :
if r .leftchars () > 0 {
break
}
r .advance (0 )
continue
case syntax .Start :
if r .textPos () != r .textstart () {
break
}
r .advance (0 )
continue
case syntax .EndZ :
rchars := r .rightchars ()
if rchars > 1 {
break
}
if (r .re .options & (RE2 | ECMAScript )) != 0 {
if rchars > 0 {
break
}
} else if rchars == 1 && r .charAt (r .textPos ()) != '\n' {
break
}
r .advance (0 )
continue
case syntax .End :
if r .rightchars () > 0 {
break
}
r .advance (0 )
continue
case syntax .One :
if r .forwardchars () < 1 || r .forwardcharnext () != rune (r .operand (0 )) {
break
}
r .advance (1 )
continue
case syntax .Notone :
if r .forwardchars () < 1 || r .forwardcharnext () == rune (r .operand (0 )) {
break
}
r .advance (1 )
continue
case syntax .Set :
if r .forwardchars () < 1 || !r .code .Sets [r .operand (0 )].CharIn (r .forwardcharnext ()) {
break
}
r .advance (1 )
continue
case syntax .Multi :
if !r .runematch (r .code .Strings [r .operand (0 )]) {
break
}
r .advance (1 )
continue
case syntax .Ref :
capnum := r .operand (0 )
if r .runmatch .isMatched (capnum ) {
if !r .refmatch (r .runmatch .matchIndex (capnum ), r .runmatch .matchLength (capnum )) {
break
}
} else {
if (r .re .options & ECMAScript ) == 0 {
break
}
}
r .advance (1 )
continue
case syntax .Onerep :
c := r .operand (1 )
if r .forwardchars () < c {
break
}
ch := rune (r .operand (0 ))
for c > 0 {
if r .forwardcharnext () != ch {
goto BreakBackward
}
c --
}
r .advance (2 )
continue
case syntax .Notonerep :
c := r .operand (1 )
if r .forwardchars () < c {
break
}
ch := rune (r .operand (0 ))
for c > 0 {
if r .forwardcharnext () == ch {
goto BreakBackward
}
c --
}
r .advance (2 )
continue
case syntax .Setrep :
c := r .operand (1 )
if r .forwardchars () < c {
break
}
set := r .code .Sets [r .operand (0 )]
for c > 0 {
if !set .CharIn (r .forwardcharnext ()) {
goto BreakBackward
}
c --
}
r .advance (2 )
continue
case syntax .Oneloop :
c := r .operand (1 )
if c > r .forwardchars () {
c = r .forwardchars ()
}
ch := rune (r .operand (0 ))
i := c
for ; i > 0 ; i -- {
if r .forwardcharnext () != ch {
r .backwardnext ()
break
}
}
if c > i {
r .trackPush2 (c -i -1 , r .textPos ()-r .bump ())
}
r .advance (2 )
continue
case syntax .Notoneloop :
c := r .operand (1 )
if c > r .forwardchars () {
c = r .forwardchars ()
}
ch := rune (r .operand (0 ))
i := c
for ; i > 0 ; i -- {
if r .forwardcharnext () == ch {
r .backwardnext ()
break
}
}
if c > i {
r .trackPush2 (c -i -1 , r .textPos ()-r .bump ())
}
r .advance (2 )
continue
case syntax .Setloop :
c := r .operand (1 )
if c > r .forwardchars () {
c = r .forwardchars ()
}
set := r .code .Sets [r .operand (0 )]
i := c
for ; i > 0 ; i -- {
if !set .CharIn (r .forwardcharnext ()) {
r .backwardnext ()
break
}
}
if c > i {
r .trackPush2 (c -i -1 , r .textPos ()-r .bump ())
}
r .advance (2 )
continue
case syntax .Oneloop | syntax .Back , syntax .Notoneloop | syntax .Back :
r .trackPopN (2 )
i := r .trackPeek ()
pos := r .trackPeekN (1 )
r .textto (pos )
if i > 0 {
r .trackPush2 (i -1 , pos -r .bump ())
}
r .advance (2 )
continue
case syntax .Setloop | syntax .Back :
r .trackPopN (2 )
i := r .trackPeek ()
pos := r .trackPeekN (1 )
r .textto (pos )
if i > 0 {
r .trackPush2 (i -1 , pos -r .bump ())
}
r .advance (2 )
continue
case syntax .Onelazy , syntax .Notonelazy :
c := r .operand (1 )
if c > r .forwardchars () {
c = r .forwardchars ()
}
if c > 0 {
r .trackPush2 (c -1 , r .textPos ())
}
r .advance (2 )
continue
case syntax .Setlazy :
c := r .operand (1 )
if c > r .forwardchars () {
c = r .forwardchars ()
}
if c > 0 {
r .trackPush2 (c -1 , r .textPos ())
}
r .advance (2 )
continue
case syntax .Onelazy | syntax .Back :
r .trackPopN (2 )
pos := r .trackPeekN (1 )
r .textto (pos )
if r .forwardcharnext () != rune (r .operand (0 )) {
break
}
i := r .trackPeek ()
if i > 0 {
r .trackPush2 (i -1 , pos +r .bump ())
}
r .advance (2 )
continue
case syntax .Notonelazy | syntax .Back :
r .trackPopN (2 )
pos := r .trackPeekN (1 )
r .textto (pos )
if r .forwardcharnext () == rune (r .operand (0 )) {
break
}
i := r .trackPeek ()
if i > 0 {
r .trackPush2 (i -1 , pos +r .bump ())
}
r .advance (2 )
continue
case syntax .Setlazy | syntax .Back :
r .trackPopN (2 )
pos := r .trackPeekN (1 )
r .textto (pos )
if !r .code .Sets [r .operand (0 )].CharIn (r .forwardcharnext ()) {
break
}
i := r .trackPeek ()
if i > 0 {
r .trackPush2 (i -1 , pos +r .bump ())
}
r .advance (2 )
continue
default :
return errors .New ("unknown state in regex runner" )
}
BreakBackward :
;
r .backtrack ()
}
}
func (r *runner ) ensureStorage () {
if r .runstackpos < r .runtrackcount *4 {
doubleIntSlice (&r .runstack , &r .runstackpos )
}
if r .runtrackpos < r .runtrackcount *4 {
doubleIntSlice (&r .runtrack , &r .runtrackpos )
}
}
func doubleIntSlice(s *[]int , pos *int ) {
oldLen := len (*s )
newS := make ([]int , oldLen *2 )
copy (newS [oldLen :], *s )
*pos += oldLen
*s = newS
}
func (r *runner ) crawl (i int ) {
if r .runcrawlpos == 0 {
doubleIntSlice (&r .runcrawl , &r .runcrawlpos )
}
r .runcrawlpos --
r .runcrawl [r .runcrawlpos ] = i
}
func (r *runner ) popcrawl () int {
val := r .runcrawl [r .runcrawlpos ]
r .runcrawlpos ++
return val
}
func (r *runner ) crawlpos () int {
return len (r .runcrawl ) - r .runcrawlpos
}
func (r *runner ) advance (i int ) {
r .codepos += (i + 1 )
r .setOperator (r .code .Codes [r .codepos ])
}
func (r *runner ) goTo (newpos int ) {
if newpos <= r .codepos {
r .ensureStorage ()
}
r .setOperator (r .code .Codes [newpos ])
r .codepos = newpos
}
func (r *runner ) textto (newpos int ) {
r .runtextpos = newpos
}
func (r *runner ) trackto (newpos int ) {
r .runtrackpos = len (r .runtrack ) - newpos
}
func (r *runner ) textstart () int {
return r .runtextstart
}
func (r *runner ) textPos () int {
return r .runtextpos
}
func (r *runner ) trackpos () int {
return len (r .runtrack ) - r .runtrackpos
}
func (r *runner ) trackPush () {
r .runtrackpos --
r .runtrack [r .runtrackpos ] = r .codepos
}
func (r *runner ) trackPush1 (I1 int ) {
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I1
r .runtrackpos --
r .runtrack [r .runtrackpos ] = r .codepos
}
func (r *runner ) trackPush2 (I1 , I2 int ) {
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I1
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I2
r .runtrackpos --
r .runtrack [r .runtrackpos ] = r .codepos
}
func (r *runner ) trackPush3 (I1 , I2 , I3 int ) {
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I1
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I2
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I3
r .runtrackpos --
r .runtrack [r .runtrackpos ] = r .codepos
}
func (r *runner ) trackPushNeg1 (I1 int ) {
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I1
r .runtrackpos --
r .runtrack [r .runtrackpos ] = -r .codepos
}
func (r *runner ) trackPushNeg2 (I1 , I2 int ) {
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I1
r .runtrackpos --
r .runtrack [r .runtrackpos ] = I2
r .runtrackpos --
r .runtrack [r .runtrackpos ] = -r .codepos
}
func (r *runner ) backtrack () {
newpos := r .runtrack [r .runtrackpos ]
r .runtrackpos ++
if r .re .Debug () {
if newpos < 0 {
fmt .Printf (" Backtracking (back2) to code position %v\n" , -newpos )
} else {
fmt .Printf (" Backtracking to code position %v\n" , newpos )
}
}
if newpos < 0 {
newpos = -newpos
r .setOperator (r .code .Codes [newpos ] | syntax .Back2 )
} else {
r .setOperator (r .code .Codes [newpos ] | syntax .Back )
}
if newpos < r .codepos {
r .ensureStorage ()
}
r .codepos = newpos
}
func (r *runner ) setOperator (op int ) {
r .caseInsensitive = (0 != (op & syntax .Ci ))
r .rightToLeft = (0 != (op & syntax .Rtl ))
r .operator = syntax .InstOp (op & ^(syntax .Rtl | syntax .Ci ))
}
func (r *runner ) trackPop () {
r .runtrackpos ++
}
func (r *runner ) trackPopN (framesize int ) {
r .runtrackpos += framesize
}
func (r *runner ) trackPeek () int {
return r .runtrack [r .runtrackpos -1 ]
}
func (r *runner ) trackPeekN (i int ) int {
return r .runtrack [r .runtrackpos -i -1 ]
}
func (r *runner ) stackPush (I1 int ) {
r .runstackpos --
r .runstack [r .runstackpos ] = I1
}
func (r *runner ) stackPush2 (I1 , I2 int ) {
r .runstackpos --
r .runstack [r .runstackpos ] = I1
r .runstackpos --
r .runstack [r .runstackpos ] = I2
}
func (r *runner ) stackPop () {
r .runstackpos ++
}
func (r *runner ) stackPopN (framesize int ) {
r .runstackpos += framesize
}
func (r *runner ) stackPeek () int {
return r .runstack [r .runstackpos -1 ]
}
func (r *runner ) stackPeekN (i int ) int {
return r .runstack [r .runstackpos -i -1 ]
}
func (r *runner ) operand (i int ) int {
return r .code .Codes [r .codepos +i +1 ]
}
func (r *runner ) leftchars () int {
return r .runtextpos
}
func (r *runner ) rightchars () int {
return r .runtextend - r .runtextpos
}
func (r *runner ) bump () int {
if r .rightToLeft {
return -1
}
return 1
}
func (r *runner ) forwardchars () int {
if r .rightToLeft {
return r .runtextpos
}
return r .runtextend - r .runtextpos
}
func (r *runner ) forwardcharnext () rune {
var ch rune
if r .rightToLeft {
r .runtextpos --
ch = r .runtext [r .runtextpos ]
} else {
ch = r .runtext [r .runtextpos ]
r .runtextpos ++
}
if r .caseInsensitive {
return unicode .ToLower (ch )
}
return ch
}
func (r *runner ) runematch (str []rune ) bool {
var pos int
c := len (str )
if !r .rightToLeft {
if r .runtextend -r .runtextpos < c {
return false
}
pos = r .runtextpos + c
} else {
if r .runtextpos -0 < c {
return false
}
pos = r .runtextpos
}
if !r .caseInsensitive {
for c != 0 {
c --
pos --
if str [c ] != r .runtext [pos ] {
return false
}
}
} else {
for c != 0 {
c --
pos --
if str [c ] != unicode .ToLower (r .runtext [pos ]) {
return false
}
}
}
if !r .rightToLeft {
pos += len (str )
}
r .runtextpos = pos
return true
}
func (r *runner ) refmatch (index , len int ) bool {
var c , pos , cmpos int
if !r .rightToLeft {
if r .runtextend -r .runtextpos < len {
return false
}
pos = r .runtextpos + len
} else {
if r .runtextpos -0 < len {
return false
}
pos = r .runtextpos
}
cmpos = index + len
c = len
if !r .caseInsensitive {
for c != 0 {
c --
cmpos --
pos --
if r .runtext [cmpos ] != r .runtext [pos ] {
return false
}
}
} else {
for c != 0 {
c --
cmpos --
pos --
if unicode .ToLower (r .runtext [cmpos ]) != unicode .ToLower (r .runtext [pos ]) {
return false
}
}
}
if !r .rightToLeft {
pos += len
}
r .runtextpos = pos
return true
}
func (r *runner ) backwardnext () {
if r .rightToLeft {
r .runtextpos ++
} else {
r .runtextpos --
}
}
func (r *runner ) charAt (j int ) rune {
return r .runtext [j ]
}
func (r *runner ) findFirstChar () bool {
if 0 != (r .code .Anchors & (syntax .AnchorBeginning | syntax .AnchorStart | syntax .AnchorEndZ | syntax .AnchorEnd )) {
if !r .code .RightToLeft {
if (0 != (r .code .Anchors &syntax .AnchorBeginning ) && r .runtextpos > 0 ) ||
(0 != (r .code .Anchors &syntax .AnchorStart ) && r .runtextpos > r .runtextstart ) {
r .runtextpos = r .runtextend
return false
}
if 0 != (r .code .Anchors &syntax .AnchorEndZ ) && r .runtextpos < r .runtextend -1 {
r .runtextpos = r .runtextend - 1
} else if 0 != (r .code .Anchors &syntax .AnchorEnd ) && r .runtextpos < r .runtextend {
r .runtextpos = r .runtextend
}
} else {
if (0 != (r .code .Anchors &syntax .AnchorEnd ) && r .runtextpos < r .runtextend ) ||
(0 != (r .code .Anchors &syntax .AnchorEndZ ) && (r .runtextpos < r .runtextend -1 ||
(r .runtextpos == r .runtextend -1 && r .charAt (r .runtextpos ) != '\n' ))) ||
(0 != (r .code .Anchors &syntax .AnchorStart ) && r .runtextpos < r .runtextstart ) {
r .runtextpos = 0
return false
}
if 0 != (r .code .Anchors &syntax .AnchorBeginning ) && r .runtextpos > 0 {
r .runtextpos = 0
}
}
if r .code .BmPrefix != nil {
return r .code .BmPrefix .IsMatch (r .runtext , r .runtextpos , 0 , r .runtextend )
}
return true
} else if r .code .BmPrefix != nil {
r .runtextpos = r .code .BmPrefix .Scan (r .runtext , r .runtextpos , 0 , r .runtextend )
if r .runtextpos == -1 {
if r .code .RightToLeft {
r .runtextpos = 0
} else {
r .runtextpos = r .runtextend
}
return false
}
return true
} else if r .code .FcPrefix == nil {
return true
}
r .rightToLeft = r .code .RightToLeft
r .caseInsensitive = r .code .FcPrefix .CaseInsensitive
set := r .code .FcPrefix .PrefixSet
if set .IsSingleton () {
ch := set .SingletonChar ()
for i := r .forwardchars (); i > 0 ; i -- {
if ch == r .forwardcharnext () {
r .backwardnext ()
return true
}
}
} else {
for i := r .forwardchars (); i > 0 ; i -- {
n := r .forwardcharnext ()
if set .CharIn (n ) {
r .backwardnext ()
return true
}
}
}
return false
}
func (r *runner ) initMatch () {
if r .runmatch == nil {
if r .re .caps != nil {
r .runmatch = newMatchSparse (r .re , r .re .caps , r .re .capsize , r .runtext , r .runtextstart )
} else {
r .runmatch = newMatch (r .re , r .re .capsize , r .runtext , r .runtextstart )
}
} else {
r .runmatch .reset (r .runtext , r .runtextstart )
}
if r .runcrawl != nil {
r .runtrackpos = len (r .runtrack )
r .runstackpos = len (r .runstack )
r .runcrawlpos = len (r .runcrawl )
return
}
r .initTrackCount ()
tracksize := r .runtrackcount * 8
stacksize := r .runtrackcount * 8
if tracksize < 32 {
tracksize = 32
}
if stacksize < 16 {
stacksize = 16
}
r .runtrack = make ([]int , tracksize )
r .runtrackpos = tracksize
r .runstack = make ([]int , stacksize )
r .runstackpos = stacksize
r .runcrawl = make ([]int , 32 )
r .runcrawlpos = 32
}
func (r *runner ) tidyMatch (quick bool ) *Match {
if !quick {
match := r .runmatch
r .runmatch = nil
match .tidy (r .runtextpos )
return match
} else {
return r .runmatch
}
}
func (r *runner ) capture (capnum , start , end int ) {
if end < start {
T := end
end = start
start = T
}
r .crawl (capnum )
r .runmatch .addMatch (capnum , start , end -start )
}
func (r *runner ) transferCapture (capnum , uncapnum , start , end int ) {
var start2 , end2 int
if end < start {
T := end
end = start
start = T
}
start2 = r .runmatch .matchIndex (uncapnum )
end2 = start2 + r .runmatch .matchLength (uncapnum )
if start >= end2 {
end = start
start = end2
} else if end <= start2 {
start = start2
} else {
if end > end2 {
end = end2
}
if start2 > start {
start = start2
}
}
r .crawl (uncapnum )
r .runmatch .balanceMatch (uncapnum )
if capnum != -1 {
r .crawl (capnum )
r .runmatch .addMatch (capnum , start , end -start )
}
}
func (r *runner ) uncapture () {
capnum := r .popcrawl ()
r .runmatch .removeMatch (capnum )
}
func (r *runner ) dumpState () {
back := ""
if r .operator &syntax .Back != 0 {
back = " Back"
}
if r .operator &syntax .Back2 != 0 {
back += " Back2"
}
fmt .Printf ("Text: %v\nTrack: %v\nStack: %v\n %s%s\n\n" ,
r .textposDescription (),
r .stackDescription (r .runtrack , r .runtrackpos ),
r .stackDescription (r .runstack , r .runstackpos ),
r .code .OpcodeDescription (r .codepos ),
back )
}
func (r *runner ) stackDescription (a []int , index int ) string {
buf := &bytes .Buffer {}
fmt .Fprintf (buf , "%v/%v" , len (a )-index , len (a ))
if buf .Len () < 8 {
buf .WriteString (strings .Repeat (" " , 8 -buf .Len ()))
}
buf .WriteRune ('(' )
for i := index ; i < len (a ); i ++ {
if i > index {
buf .WriteRune (' ' )
}
buf .WriteString (strconv .Itoa (a [i ]))
}
buf .WriteRune (')' )
return buf .String ()
}
func (r *runner ) textposDescription () string {
buf := &bytes .Buffer {}
buf .WriteString (strconv .Itoa (r .runtextpos ))
if buf .Len () < 8 {
buf .WriteString (strings .Repeat (" " , 8 -buf .Len ()))
}
if r .runtextpos > 0 {
buf .WriteString (syntax .CharDescription (r .runtext [r .runtextpos -1 ]))
} else {
buf .WriteRune ('^' )
}
buf .WriteRune ('>' )
for i := r .runtextpos ; i < r .runtextend ; i ++ {
buf .WriteString (syntax .CharDescription (r .runtext [i ]))
}
if buf .Len () >= 64 {
buf .Truncate (61 )
buf .WriteString ("..." )
} else {
buf .WriteRune ('$' )
}
return buf .String ()
}
func (r *runner ) isBoundary (index , startpos , endpos int ) bool {
return (index > startpos && syntax .IsWordChar (r .runtext [index -1 ])) !=
(index < endpos && syntax .IsWordChar (r .runtext [index ]))
}
func (r *runner ) isECMABoundary (index , startpos , endpos int ) bool {
return (index > startpos && syntax .IsECMAWordChar (r .runtext [index -1 ])) !=
(index < endpos && syntax .IsECMAWordChar (r .runtext [index ]))
}
func (r *runner ) startTimeoutWatch () {
if r .ignoreTimeout {
return
}
r .deadline = makeDeadline (r .timeout )
}
func (r *runner ) checkTimeout () error {
if r .ignoreTimeout || !r .deadline .reached () {
return nil
}
if r .re .Debug () {
}
return fmt .Errorf ("match timeout after %v on input `%v`" , r .timeout , string (r .runtext ))
}
func (r *runner ) initTrackCount () {
r .runtrackcount = r .code .TrackCount
}
func (re *Regexp ) getRunner () *runner {
re .muRun .Lock ()
if n := len (re .runner ); n > 0 {
z := re .runner [n -1 ]
re .runner = re .runner [:n -1 ]
re .muRun .Unlock ()
return z
}
re .muRun .Unlock ()
z := &runner {
re : re ,
code : re .code ,
}
return z
}
func (re *Regexp ) putRunner (r *runner ) {
re .muRun .Lock ()
r .runtext = nil
if r .runmatch != nil {
r .runmatch .text = nil
}
re .runner = append (re .runner , r )
re .muRun .Unlock ()
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .