1
9 package regexp2
10
11 import (
12 "errors"
13 "math"
14 "strconv"
15 "sync"
16 "time"
17
18 "github.com/dlclark/regexp2/syntax"
19 )
20
21
22 var DefaultMatchTimeout = time.Duration(math.MaxInt64)
23
24
25
26 type Regexp struct {
27
28
29
30
31
32 MatchTimeout time.Duration
33
34
35 pattern string
36 options RegexOptions
37
38 caps map[int]int
39 capnames map[string]int
40 capslist []string
41 capsize int
42
43 code *syntax.Code
44
45
46 muRun sync.Mutex
47 runner []*runner
48 }
49
50
51
52 func Compile(expr string, opt RegexOptions) (*Regexp, error) {
53
54 tree, err := syntax.Parse(expr, syntax.RegexOptions(opt))
55 if err != nil {
56 return nil, err
57 }
58
59
60 code, err := syntax.Write(tree)
61 if err != nil {
62 return nil, err
63 }
64
65
66 return &Regexp{
67 pattern: expr,
68 options: opt,
69 caps: code.Caps,
70 capnames: tree.Capnames,
71 capslist: tree.Caplist,
72 capsize: code.Capsize,
73 code: code,
74 MatchTimeout: DefaultMatchTimeout,
75 }, nil
76 }
77
78
79
80
81 func MustCompile(str string, opt RegexOptions) *Regexp {
82 regexp, error := Compile(str, opt)
83 if error != nil {
84 panic(`regexp2: Compile(` + quote(str) + `): ` + error.Error())
85 }
86 return regexp
87 }
88
89
90 func Escape(input string) string {
91 return syntax.Escape(input)
92 }
93
94
95 func Unescape(input string) (string, error) {
96 return syntax.Unescape(input)
97 }
98
99
100
101
102 func SetTimeoutCheckPeriod(d time.Duration) {
103 clockPeriod = d
104 }
105
106
107
108 func StopTimeoutClock() {
109 stopClock()
110 }
111
112
113 func (re *Regexp) String() string {
114 return re.pattern
115 }
116
117 func quote(s string) string {
118 if strconv.CanBackquote(s) {
119 return "`" + s + "`"
120 }
121 return strconv.Quote(s)
122 }
123
124
125
126
127 type RegexOptions int32
128
129 const (
130 None RegexOptions = 0x0
131 IgnoreCase = 0x0001
132 Multiline = 0x0002
133 ExplicitCapture = 0x0004
134 Compiled = 0x0008
135 Singleline = 0x0010
136 IgnorePatternWhitespace = 0x0020
137 RightToLeft = 0x0040
138 Debug = 0x0080
139 ECMAScript = 0x0100
140 RE2 = 0x0200
141 Unicode = 0x0400
142 )
143
144 func (re *Regexp) RightToLeft() bool {
145 return re.options&RightToLeft != 0
146 }
147
148 func (re *Regexp) Debug() bool {
149 return re.options&Debug != 0
150 }
151
152
153
154
155
156 func (re *Regexp) Replace(input, replacement string, startAt, count int) (string, error) {
157 data, err := syntax.NewReplacerData(replacement, re.caps, re.capsize, re.capnames, syntax.RegexOptions(re.options))
158 if err != nil {
159 return "", err
160 }
161
162
163 return replace(re, data, nil, input, startAt, count)
164 }
165
166
167
168
169
170 func (re *Regexp) ReplaceFunc(input string, evaluator MatchEvaluator, startAt, count int) (string, error) {
171 return replace(re, nil, evaluator, input, startAt, count)
172 }
173
174
175 func (re *Regexp) FindStringMatch(s string) (*Match, error) {
176
177 return re.run(false, -1, getRunes(s))
178 }
179
180
181 func (re *Regexp) FindRunesMatch(r []rune) (*Match, error) {
182 return re.run(false, -1, r)
183 }
184
185
186 func (re *Regexp) FindStringMatchStartingAt(s string, startAt int) (*Match, error) {
187 if startAt > len(s) {
188 return nil, errors.New("startAt must be less than the length of the input string")
189 }
190 r, startAt := re.getRunesAndStart(s, startAt)
191 if startAt == -1 {
192
193 return nil, errors.New("startAt must align to the start of a valid rune in the input string")
194 }
195
196 return re.run(false, startAt, r)
197 }
198
199
200 func (re *Regexp) FindRunesMatchStartingAt(r []rune, startAt int) (*Match, error) {
201 return re.run(false, startAt, r)
202 }
203
204
205
206 func (re *Regexp) FindNextMatch(m *Match) (*Match, error) {
207 if m == nil {
208 return nil, nil
209 }
210
211
212
213 startAt := m.textpos
214 if m.Length == 0 {
215 if m.textpos == len(m.text) {
216 return nil, nil
217 }
218
219 if re.RightToLeft() {
220 startAt--
221 } else {
222 startAt++
223 }
224 }
225 return re.run(false, startAt, m.text)
226 }
227
228
229
230 func (re *Regexp) MatchString(s string) (bool, error) {
231 m, err := re.run(true, -1, getRunes(s))
232 if err != nil {
233 return false, err
234 }
235 return m != nil, nil
236 }
237
238 func (re *Regexp) getRunesAndStart(s string, startAt int) ([]rune, int) {
239 if startAt < 0 {
240 if re.RightToLeft() {
241 r := getRunes(s)
242 return r, len(r)
243 }
244 return getRunes(s), 0
245 }
246 ret := make([]rune, len(s))
247 i := 0
248 runeIdx := -1
249 for strIdx, r := range s {
250 if strIdx == startAt {
251 runeIdx = i
252 }
253 ret[i] = r
254 i++
255 }
256 if startAt == len(s) {
257 runeIdx = i
258 }
259 return ret[:i], runeIdx
260 }
261
262 func getRunes(s string) []rune {
263 return []rune(s)
264 }
265
266
267
268 func (re *Regexp) MatchRunes(r []rune) (bool, error) {
269 m, err := re.run(true, -1, r)
270 if err != nil {
271 return false, err
272 }
273 return m != nil, nil
274 }
275
276
277 func (re *Regexp) GetGroupNames() []string {
278 var result []string
279
280 if re.capslist == nil {
281 result = make([]string, re.capsize)
282
283 for i := 0; i < len(result); i++ {
284 result[i] = strconv.Itoa(i)
285 }
286 } else {
287 result = make([]string, len(re.capslist))
288 copy(result, re.capslist)
289 }
290
291 return result
292 }
293
294
295 func (re *Regexp) GetGroupNumbers() []int {
296 var result []int
297
298 if re.caps == nil {
299 result = make([]int, re.capsize)
300
301 for i := 0; i < len(result); i++ {
302 result[i] = i
303 }
304 } else {
305 result = make([]int, len(re.caps))
306
307 for k, v := range re.caps {
308 result[v] = k
309 }
310 }
311
312 return result
313 }
314
315
316
317
318 func (re *Regexp) GroupNameFromNumber(i int) string {
319 if re.capslist == nil {
320 if i >= 0 && i < re.capsize {
321 return strconv.Itoa(i)
322 }
323
324 return ""
325 }
326
327 if re.caps != nil {
328 var ok bool
329 if i, ok = re.caps[i]; !ok {
330 return ""
331 }
332 }
333
334 if i >= 0 && i < len(re.capslist) {
335 return re.capslist[i]
336 }
337
338 return ""
339 }
340
341
342
343
344 func (re *Regexp) GroupNumberFromName(name string) int {
345
346 if re.capnames != nil {
347 if k, ok := re.capnames[name]; ok {
348 return k
349 }
350
351 return -1
352 }
353
354
355 result := 0
356 for i := 0; i < len(name); i++ {
357 ch := name[i]
358
359 if ch > '9' || ch < '0' {
360 return -1
361 }
362
363 result *= 10
364 result += int(ch - '0')
365 }
366
367
368 if result >= 0 && result < re.capsize {
369 return result
370 }
371
372 return -1
373 }
374
View as plain text