-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathregex.go
More file actions
85 lines (76 loc) · 2 KB
/
regex.go
File metadata and controls
85 lines (76 loc) · 2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package sham
import (
"math/rand"
"regexp/syntax"
)
const maxRepeats int = 10
// NewRegex parses a regular expression. Regular expressions are of the Go flavor
// and use Perl flags.
func NewRegex(pattern string) (Regex, error) {
s, err := syntax.Parse(pattern, syntax.Perl)
if err != nil {
return Regex{}, err
}
return Regex{Pattern: pattern, regex: s.Simplify()}, nil
}
// Regex holds a compiled regex. While any valid regular expression can be
// provided, only a subset will actually generate data. Every node in a
// parsed regex leads to a possible choice. During data generation, a
// random path through the parsed expression is taken. Therefore, a complciated
// expression has the potential to lead to wildly different performance on
// repeated generations.
//
// TODO: fully document nodes that can generate data
type Regex struct {
Pattern string
regex *syntax.Regexp
}
// Generate traverses a parsed regular expression and generates data where
// applicable.
func (r Regex) Generate() interface{} {
return string(r.gen(r.regex))
}
func (r Regex) gen(re *syntax.Regexp) []rune {
rs := make([]rune, 0)
switch re.Op {
case syntax.OpLiteral:
return re.Rune
case syntax.OpStar:
n := rand.Intn(maxRepeats)
for i := 0; i < n; i++ {
rs = append(rs, r.gen(re.Sub0[0])...)
}
case syntax.OpPlus:
n := rand.Intn(maxRepeats-1) + 1
for i := 0; i < n; i++ {
rs = append(rs, r.gen(re.Sub0[0])...)
}
case syntax.OpConcat:
for _, s := range re.Sub {
rs = append(rs, r.gen(s)...)
}
case syntax.OpAlternate:
return r.gen(re.Sub[rand.Intn(len(re.Sub))])
case syntax.OpCapture:
return r.gen(re.Sub0[0])
case syntax.OpEmptyMatch:
return nil
case syntax.OpCharClass:
r := fromCharClass(re.Rune)
rs = append(rs, r)
case syntax.OpQuest:
if rand.Float64() < 0.75 {
return r.gen(re.Sub0[0])
}
return nil
}
return rs
}
func fromCharClass(class []rune) rune {
if len(class) == 0 {
return 0
}
min := class[0]
max := class[len(class)-1]
return rune(rand.Int31n(max-min) + min)
}