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  }