/
regex.go
78 lines (73 loc) · 2.16 KB
/
regex.go
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
package gen
import (
"reflect"
"regexp"
"regexp/syntax"
"strings"
"github.com/leanovate/gopter"
)
// RegexMatch generates matches for a given regular expression
// regexStr is supposed to conform to the perl regular expression syntax
func RegexMatch(regexStr string) gopter.Gen {
regexSyntax, err1 := syntax.Parse(regexStr, syntax.Perl)
regex, err2 := regexp.Compile(regexStr)
if err1 != nil || err2 != nil {
return Fail(reflect.TypeOf(""))
}
return regexMatchGen(regexSyntax.Simplify()).SuchThat(func(v string) bool {
return regex.MatchString(v)
}).WithShrinker(StringShrinker)
}
func regexMatchGen(regex *syntax.Regexp) gopter.Gen {
switch regex.Op {
case syntax.OpLiteral:
return Const(string(regex.Rune))
case syntax.OpCharClass:
gens := make([]gopter.Gen, 0, len(regex.Rune)/2)
for i := 0; i+1 < len(regex.Rune); i += 2 {
gens = append(gens, RuneRange(regex.Rune[i], regex.Rune[i+1]).Map(runeToString))
}
return OneGenOf(gens...)
case syntax.OpAnyChar:
return Rune().Map(runeToString)
case syntax.OpAnyCharNotNL:
return RuneNoControl().Map(runeToString)
case syntax.OpCapture:
return regexMatchGen(regex.Sub[0])
case syntax.OpStar:
elementGen := regexMatchGen(regex.Sub[0])
return SliceOf(elementGen).Map(func(v []string) string {
return strings.Join(v, "")
})
case syntax.OpPlus:
elementGen := regexMatchGen(regex.Sub[0])
return gopter.CombineGens(elementGen, SliceOf(elementGen)).Map(func(vs []interface{}) string {
return vs[0].(string) + strings.Join(vs[1].([]string), "")
})
case syntax.OpQuest:
elementGen := regexMatchGen(regex.Sub[0])
return OneGenOf(Const(""), elementGen)
case syntax.OpConcat:
gens := make([]gopter.Gen, len(regex.Sub))
for i, sub := range regex.Sub {
gens[i] = regexMatchGen(sub)
}
return gopter.CombineGens(gens...).Map(func(v []interface{}) string {
result := ""
for _, str := range v {
result += str.(string)
}
return result
})
case syntax.OpAlternate:
gens := make([]gopter.Gen, len(regex.Sub))
for i, sub := range regex.Sub {
gens[i] = regexMatchGen(sub)
}
return OneGenOf(gens...)
}
return Const("")
}
func runeToString(v rune) string {
return string(v)
}