package regexp2import ()// Match is a single regex result match that contains groups and repeated captures//// -Groups// -CapturetypeMatchstruct {Group//embeded group 0 regex *Regexp otherGroups []Group// input to the match textpos int textstart int capcount int caps []int sparseCaps map[int]int// output from the match matches [][]int matchcount []int// whether we've done any balancing with this match. If we // have done balancing, we'll need to do extra work in Tidy(). balancing bool}// Group is an explicit or implit (group 0) matched group within the patterntypeGroupstruct {Capture// the last capture of this group is embeded for ease of use Name string// group name Captures []Capture// captures of this group}// Capture is a single capture of text within the larger original stringtypeCapturestruct {// the original string text []rune// Index is the position in the underlying rune slice where the first character of // captured substring was found. Even if you pass in a string this will be in Runes. Index int// Length is the number of runes in the captured substring. Length int}// String returns the captured text as a Stringfunc ( *Capture) () string {returnstring(.text[.Index : .Index+.Length])}// Runes returns the captured text as a rune slicefunc ( *Capture) () []rune {return .text[.Index : .Index+.Length]}func newMatch( *Regexp, int, []rune, int) *Match { := Match{regex: ,matchcount: make([]int, ),matches: make([][]int, ),textstart: ,balancing: false, } .Name = "0" .text = .matches[0] = make([]int, 2)return &}func newMatchSparse( *Regexp, map[int]int, int, []rune, int) *Match { := newMatch(, , , ) .sparseCaps = return}func ( *Match) ( []rune, int) { .text = .textstart = for := 0; < len(.matchcount); ++ { .matchcount[] = 0 } .balancing = false}func ( *Match) ( int) { := .matches[0] .Index = [0] .Length = [1] .textpos = .capcount = .matchcount[0]//copy our root capture to the list .Group.Captures = []Capture{.Group.Capture}if .balancing {// The idea here is that we want to compact all of our unbalanced captures. To do that we // use j basically as a count of how many unbalanced captures we have at any given time // (really j is an index, but j/2 is the count). First we skip past all of the real captures // until we find a balance captures. Then we check each subsequent entry. If it's a balance // capture (it's negative), we decrement j. If it's a real capture, we increment j and copy // it down to the last free position.for := 0; < len(.matchcount); ++ { := .matchcount[] * 2 := .matches[]var , intfor = 0; < ; ++ {if [] < 0 {break } }for = ; < ; ++ {if [] < 0 {// skip negative values -- } else {// but if we find something positive (an actual capture), copy it back to the last // unbalanced position.if != { [] = [] } ++ } } .matchcount[] = / 2 } .balancing = false }}// isMatched tells if a group was matched by capnumfunc ( *Match) ( int) bool {return < len(.matchcount) && .matchcount[] > 0 && .matches[][.matchcount[]*2-1] != (-3+1)}// matchIndex returns the index of the last specified matched group by capnumfunc ( *Match) ( int) int { := .matches[][.matchcount[]*2-2]if >= 0 {return }return .matches[][-3-]}// matchLength returns the length of the last specified matched group by capnumfunc ( *Match) ( int) int { := .matches[][.matchcount[]*2-1]if >= 0 {return }return .matches[][-3-]}// Nonpublic builder: add a capture to the group specified by "c"func ( *Match) (, , int) {if .matches[] == nil { .matches[] = make([]int, 2) } := .matchcount[]if *2+2 > len(.matches[]) { := .matches[] := make([]int, *8)copy(, [:*2]) .matches[] = } .matches[][*2] = .matches[][*2+1] = .matchcount[] = + 1//log.Printf("addMatch: c=%v, i=%v, l=%v ... matches: %v", c, start, l, m.matches)}// Nonpublic builder: Add a capture to balance the specified group. This is used by the//// balanced match construct. (?<foo-foo2>...)//// If there were no such thing as backtracking, this would be as simple as calling RemoveMatch(c).// However, since we have backtracking, we need to keep track of everything.func ( *Match) ( int) { .balancing = true// we'll look at the last capture first := .matchcount[] := *2 - 2// first see if it is negative, and therefore is a reference to the next available // capture group for balancing. If it is, we'll reset target to point to that capture.if .matches[][] < 0 { = -3 - .matches[][] }// move back to the previous capture -= 2// if the previous capture is a reference, just copy that reference to the end. Otherwise, point to it.if >= 0 && .matches[][] < 0 { .addMatch(, .matches[][], .matches[][+1]) } else { .addMatch(, -3-, -4- /* == -3 - (target + 1) */) }}// Nonpublic builder: removes a group match by capnumfunc ( *Match) ( int) { .matchcount[]--}// GroupCount returns the number of groups this match has matchedfunc ( *Match) () int {returnlen(.matchcount)}// GroupByName returns a group based on the name of the group, or nil if the group name does not existfunc ( *Match) ( string) *Group { := .regex.GroupNumberFromName()if < 0 {returnnil }return .GroupByNumber()}// GroupByNumber returns a group based on the number of the group, or nil if the group number does not existfunc ( *Match) ( int) *Group {// check our sparse mapif .sparseCaps != nil {if , := .sparseCaps[]; { = } }if >= len(.matchcount) || < 0 {returnnil }if == 0 {return &.Group } .populateOtherGroups()return &.otherGroups[-1]}// Groups returns all the capture groups, starting with group 0 (the full match)func ( *Match) () []Group { .populateOtherGroups() := make([]Group, len(.otherGroups)+1) [0] = .Groupcopy([1:], .otherGroups)return}func ( *Match) () {// Construct all the Group objects first time calledif .otherGroups == nil { .otherGroups = make([]Group, len(.matchcount)-1)for := 0; < len(.otherGroups); ++ { .otherGroups[] = newGroup(.regex.GroupNameFromNumber(+1), .text, .matches[+1], .matchcount[+1]) } }}func ( *Match) ( int, *bytes.Buffer) { := .matchcount[]if == 0 {return } := .matches[] := [(-1)*2] := + [(*2)-1]for ; < ; ++ { .WriteRune(.text[]) }}func newGroup( string, []rune, []int, int) Group { := Group{} .text = if > 0 { .Index = [(-1)*2] .Length = [(*2)-1] } .Name = .Captures = make([]Capture, )for := 0; < ; ++ { .Captures[] = Capture{text: ,Index: [*2],Length: [*2+1], } }//log.Printf("newGroup! capcount %v, %+v", capcount, g)return}func ( *Match) () string { := &bytes.Buffer{} .WriteRune('\n')iflen(.sparseCaps) > 0 {for , := range .sparseCaps {fmt.Fprintf(, "Slot %v -> %v\n", , ) } }for , := range .Groups() {fmt.Fprintf(, "Group %v (%v), %v caps:\n", , .Name, len(.Captures))for , := range .Captures {fmt.Fprintf(, " (%v, %v) %v\n", .Index, .Length, .String()) } }/* for i := 0; i < len(m.matchcount); i++ { fmt.Fprintf(buf, "\nGroup %v (%v):\n", i, m.regex.GroupNameFromNumber(i)) for j := 0; j < m.matchcount[i]; j++ { text := "" if m.matches[i][j*2] >= 0 { start := m.matches[i][j*2] text = m.text[start : start+m.matches[i][j*2+1]] } fmt.Fprintf(buf, " (%v, %v) %v\n", m.matches[i][j*2], m.matches[i][j*2+1], text) } } */return .String()}
The pages are generated with Goldsv0.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.