github.com/leanovate/gopter@v0.2.9/gen/regex.go (about) 1 package gen 2 3 import ( 4 "reflect" 5 "regexp" 6 "regexp/syntax" 7 "strings" 8 9 "github.com/leanovate/gopter" 10 ) 11 12 // RegexMatch generates matches for a given regular expression 13 // regexStr is supposed to conform to the perl regular expression syntax 14 func RegexMatch(regexStr string) gopter.Gen { 15 regexSyntax, err1 := syntax.Parse(regexStr, syntax.Perl) 16 regex, err2 := regexp.Compile(regexStr) 17 if err1 != nil || err2 != nil { 18 return Fail(reflect.TypeOf("")) 19 } 20 return regexMatchGen(regexSyntax.Simplify()).SuchThat(func(v string) bool { 21 return regex.MatchString(v) 22 }).WithShrinker(StringShrinker) 23 } 24 25 func regexMatchGen(regex *syntax.Regexp) gopter.Gen { 26 switch regex.Op { 27 case syntax.OpLiteral: 28 return Const(string(regex.Rune)) 29 case syntax.OpCharClass: 30 gens := make([]gopter.Gen, 0, len(regex.Rune)/2) 31 for i := 0; i+1 < len(regex.Rune); i += 2 { 32 gens = append(gens, RuneRange(regex.Rune[i], regex.Rune[i+1]).Map(runeToString)) 33 } 34 return OneGenOf(gens...) 35 case syntax.OpAnyChar: 36 return Rune().Map(runeToString) 37 case syntax.OpAnyCharNotNL: 38 return RuneNoControl().Map(runeToString) 39 case syntax.OpCapture: 40 return regexMatchGen(regex.Sub[0]) 41 case syntax.OpStar: 42 elementGen := regexMatchGen(regex.Sub[0]) 43 return SliceOf(elementGen).Map(func(v []string) string { 44 return strings.Join(v, "") 45 }) 46 case syntax.OpPlus: 47 elementGen := regexMatchGen(regex.Sub[0]) 48 return gopter.CombineGens(elementGen, SliceOf(elementGen)).Map(func(vs []interface{}) string { 49 return vs[0].(string) + strings.Join(vs[1].([]string), "") 50 }) 51 case syntax.OpQuest: 52 elementGen := regexMatchGen(regex.Sub[0]) 53 return OneGenOf(Const(""), elementGen) 54 case syntax.OpConcat: 55 gens := make([]gopter.Gen, len(regex.Sub)) 56 for i, sub := range regex.Sub { 57 gens[i] = regexMatchGen(sub) 58 } 59 return gopter.CombineGens(gens...).Map(func(v []interface{}) string { 60 result := "" 61 for _, str := range v { 62 result += str.(string) 63 } 64 return result 65 }) 66 case syntax.OpAlternate: 67 gens := make([]gopter.Gen, len(regex.Sub)) 68 for i, sub := range regex.Sub { 69 gens[i] = regexMatchGen(sub) 70 } 71 return OneGenOf(gens...) 72 } 73 return Const("") 74 } 75 76 func runeToString(v rune) string { 77 return string(v) 78 }