fastpastebin/vendor/github.com/dlclark/regexp2/match.go
Stanislav N. aka pztrn 48d43ca097 Pagination, readable error messages to user, syntax highlighting started.
Pagination now works. Temporary hardcoded 10 pastes per page, will be put
in configuration later. Maybe.

From now user will receive readable error message if error occured.

Started to work on syntax highlighting, tried to make lexers detection
work but apparently to no avail.
2018-05-01 02:37:51 +05:00

348 lines
8.6 KiB
Go

package regexp2
import (
"bytes"
"fmt"
)
// Match is a single regex result match that contains groups and repeated captures
// -Groups
// -Capture
type Match struct {
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 pattern
type Group struct {
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 string
type Capture struct {
// the original string
text []rune
// the position in the original string where the first character of
// captured substring was found.
Index int
// the length of the captured substring.
Length int
}
// String returns the captured text as a String
func (c *Capture) String() string {
return string(c.text[c.Index : c.Index+c.Length])
}
// Runes returns the captured text as a rune slice
func (c *Capture) Runes() []rune {
return c.text[c.Index : c.Index+c.Length]
}
func newMatch(regex *Regexp, capcount int, text []rune, startpos int) *Match {
m := Match{
regex: regex,
matchcount: make([]int, capcount),
matches: make([][]int, capcount),
textstart: startpos,
balancing: false,
}
m.Name = "0"
m.text = text
m.matches[0] = make([]int, 2)
return &m
}
func newMatchSparse(regex *Regexp, caps map[int]int, capcount int, text []rune, startpos int) *Match {
m := newMatch(regex, capcount, text, startpos)
m.sparseCaps = caps
return m
}
func (m *Match) reset(text []rune, textstart int) {
m.text = text
m.textstart = textstart
for i := 0; i < len(m.matchcount); i++ {
m.matchcount[i] = 0
}
m.balancing = false
}
func (m *Match) tidy(textpos int) {
interval := m.matches[0]
m.Index = interval[0]
m.Length = interval[1]
m.textpos = textpos
m.capcount = m.matchcount[0]
//copy our root capture to the list
m.Group.Captures = []Capture{m.Group.Capture}
if m.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 cap := 0; cap < len(m.matchcount); cap++ {
limit := m.matchcount[cap] * 2
matcharray := m.matches[cap]
var i, j int
for i = 0; i < limit; i++ {
if matcharray[i] < 0 {
break
}
}
for j = i; i < limit; i++ {
if matcharray[i] < 0 {
// skip negative values
j--
} else {
// but if we find something positive (an actual capture), copy it back to the last
// unbalanced position.
if i != j {
matcharray[j] = matcharray[i]
}
j++
}
}
m.matchcount[cap] = j / 2
}
m.balancing = false
}
}
// isMatched tells if a group was matched by capnum
func (m *Match) isMatched(cap int) bool {
return cap < len(m.matchcount) && m.matchcount[cap] > 0 && m.matches[cap][m.matchcount[cap]*2-1] != (-3+1)
}
// matchIndex returns the index of the last specified matched group by capnum
func (m *Match) matchIndex(cap int) int {
i := m.matches[cap][m.matchcount[cap]*2-2]
if i >= 0 {
return i
}
return m.matches[cap][-3-i]
}
// matchLength returns the length of the last specified matched group by capnum
func (m *Match) matchLength(cap int) int {
i := m.matches[cap][m.matchcount[cap]*2-1]
if i >= 0 {
return i
}
return m.matches[cap][-3-i]
}
// Nonpublic builder: add a capture to the group specified by "c"
func (m *Match) addMatch(c, start, l int) {
if m.matches[c] == nil {
m.matches[c] = make([]int, 2)
}
capcount := m.matchcount[c]
if capcount*2+2 > len(m.matches[c]) {
oldmatches := m.matches[c]
newmatches := make([]int, capcount*8)
copy(newmatches, oldmatches[:capcount*2])
m.matches[c] = newmatches
}
m.matches[c][capcount*2] = start
m.matches[c][capcount*2+1] = l
m.matchcount[c] = capcount + 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 (m *Match) balanceMatch(c int) {
m.balancing = true
// we'll look at the last capture first
capcount := m.matchcount[c]
target := capcount*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 m.matches[c][target] < 0 {
target = -3 - m.matches[c][target]
}
// move back to the previous capture
target -= 2
// if the previous capture is a reference, just copy that reference to the end. Otherwise, point to it.
if target >= 0 && m.matches[c][target] < 0 {
m.addMatch(c, m.matches[c][target], m.matches[c][target+1])
} else {
m.addMatch(c, -3-target, -4-target /* == -3 - (target + 1) */)
}
}
// Nonpublic builder: removes a group match by capnum
func (m *Match) removeMatch(c int) {
m.matchcount[c]--
}
// GroupCount returns the number of groups this match has matched
func (m *Match) GroupCount() int {
return len(m.matchcount)
}
// GroupByName returns a group based on the name of the group, or nil if the group name does not exist
func (m *Match) GroupByName(name string) *Group {
num := m.regex.GroupNumberFromName(name)
if num < 0 {
return nil
}
return m.GroupByNumber(num)
}
// GroupByNumber returns a group based on the number of the group, or nil if the group number does not exist
func (m *Match) GroupByNumber(num int) *Group {
// check our sparse map
if m.sparseCaps != nil {
if newNum, ok := m.sparseCaps[num]; ok {
num = newNum
}
}
if num >= len(m.matchcount) || num < 0 {
return nil
}
if num == 0 {
return &m.Group
}
m.populateOtherGroups()
return &m.otherGroups[num-1]
}
// Groups returns all the capture groups, starting with group 0 (the full match)
func (m *Match) Groups() []Group {
m.populateOtherGroups()
g := make([]Group, len(m.otherGroups)+1)
g[0] = m.Group
copy(g[1:], m.otherGroups)
return g
}
func (m *Match) populateOtherGroups() {
// Construct all the Group objects first time called
if m.otherGroups == nil {
m.otherGroups = make([]Group, len(m.matchcount)-1)
for i := 0; i < len(m.otherGroups); i++ {
m.otherGroups[i] = newGroup(m.regex.GroupNameFromNumber(i+1), m.text, m.matches[i+1], m.matchcount[i+1])
}
}
}
func (m *Match) groupValueAppendToBuf(groupnum int, buf *bytes.Buffer) {
c := m.matchcount[groupnum]
if c == 0 {
return
}
matches := m.matches[groupnum]
index := matches[(c-1)*2]
last := index + matches[(c*2)-1]
for ; index < last; index++ {
buf.WriteRune(m.text[index])
}
}
func newGroup(name string, text []rune, caps []int, capcount int) Group {
g := Group{}
g.text = text
if capcount > 0 {
g.Index = caps[(capcount-1)*2]
g.Length = caps[(capcount*2)-1]
}
g.Name = name
g.Captures = make([]Capture, capcount)
for i := 0; i < capcount; i++ {
g.Captures[i] = Capture{
text: text,
Index: caps[i*2],
Length: caps[i*2+1],
}
}
//log.Printf("newGroup! capcount %v, %+v", capcount, g)
return g
}
func (m *Match) dump() string {
buf := &bytes.Buffer{}
buf.WriteRune('\n')
if len(m.sparseCaps) > 0 {
for k, v := range m.sparseCaps {
fmt.Fprintf(buf, "Slot %v -> %v\n", k, v)
}
}
for i, g := range m.Groups() {
fmt.Fprintf(buf, "Group %v (%v), %v caps:\n", i, g.Name, len(g.Captures))
for _, c := range g.Captures {
fmt.Fprintf(buf, " (%v, %v) %v\n", c.Index, c.Length, c.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 buf.String()
}