expgen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +1 -0
- data/bin/expgen +4 -0
- data/expgen.gemspec +22 -0
- data/lib/expgen.rb +43 -0
- data/lib/expgen/nodes.rb +128 -0
- data/lib/expgen/parser.rb +85 -0
- data/lib/expgen/randomizer.rb +32 -0
- data/lib/expgen/transform.rb +18 -0
- data/lib/expgen/version.rb +3 -0
- data/spec/expgen_spec.rb +243 -0
- data/spec/spec_helper.rb +6 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Jonas Nicklas
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Expgen
|
2
|
+
|
3
|
+
Expgen solves a very simple problem: Given a regular expression, find a string
|
4
|
+
which matches that regular expression. Use it like this:
|
5
|
+
|
6
|
+
``` ruby
|
7
|
+
Expgen.gen(/foo\w+b[a-z]{2,3}/) # => "fooxbdp"
|
8
|
+
```
|
9
|
+
|
10
|
+
For a full list of supported syntax, see the spec file.
|
11
|
+
|
12
|
+
Some things are really difficult to generate accurate expressions for, it's
|
13
|
+
even quite easy to create a regexp which matches *no* strings. For example
|
14
|
+
`/a\bc/` will not match any string, since there can never be a word boundary
|
15
|
+
between characters.
|
16
|
+
|
17
|
+
When given a negative character class, Expgen takes the entire ASCII character
|
18
|
+
set (sans control characters) and removes from it any characters excluded by the
|
19
|
+
character class. In other words, if the character class excludes the *entire*
|
20
|
+
ASCII character set, Expgen will be unable to fill this space.
|
21
|
+
|
22
|
+
The following is a list of things Expgen does *not* support:
|
23
|
+
|
24
|
+
- Anchors (are ignored)
|
25
|
+
- Lookaheads and lookbehinds
|
26
|
+
- Subexpressions
|
27
|
+
- Backreferences
|
28
|
+
|
29
|
+
## Doesn't this already exist?
|
30
|
+
|
31
|
+
There is a gem called Randexp which does much the same thing. Expgen differs
|
32
|
+
from Randexp in two important ways. (1) It actually works. (2) It supports a
|
33
|
+
*much* wider range of regexp syntax.
|
34
|
+
|
35
|
+
The idea behind Expgen is that you should be able to take any reasonable, real
|
36
|
+
world regular expression and be able to generate matching strings. The focus is
|
37
|
+
on finding Strings which match a particular expression, not necessarily using
|
38
|
+
it as a random generator.
|
39
|
+
|
40
|
+
# License
|
41
|
+
|
42
|
+
MIT, see separate LICENSE.txt file
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/expgen
ADDED
data/expgen.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'expgen/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "expgen"
|
8
|
+
gem.version = Expgen::VERSION
|
9
|
+
gem.authors = ["Jonas Nicklas"]
|
10
|
+
gem.email = ["jonas.nicklas@gmail.com"]
|
11
|
+
gem.description = %q{Generate random strings from regular expression}
|
12
|
+
gem.summary = %q{Generate random regular expression}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.add_dependency "parslet"
|
20
|
+
gem.add_development_dependency "pry"
|
21
|
+
gem.add_development_dependency "rspec"
|
22
|
+
end
|
data/lib/expgen.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "parslet"
|
2
|
+
require "expgen/version"
|
3
|
+
require "expgen/parser"
|
4
|
+
require "expgen/transform"
|
5
|
+
require "expgen/randomizer"
|
6
|
+
require "expgen/nodes"
|
7
|
+
|
8
|
+
module Expgen
|
9
|
+
ASCII = (32..126).map(&:chr)
|
10
|
+
LOWER = ("a".."z").to_a
|
11
|
+
UPPER = ("A".."Z").to_a
|
12
|
+
DIGIT = (0..9).map(&:to_s)
|
13
|
+
ALPHA = LOWER + UPPER
|
14
|
+
WORD = ALPHA + DIGIT + ["_"]
|
15
|
+
NEGATIVE_WORD = ASCII - WORD
|
16
|
+
NON_DIGIT = ASCII - DIGIT
|
17
|
+
HEX_DIGIT = ("a".."f").to_a + ("A".."F").to_a + DIGIT
|
18
|
+
NON_HEX_DIGIT = ASCII - HEX_DIGIT
|
19
|
+
SPACE = [" "]
|
20
|
+
NON_SPACE = ASCII.drop(1)
|
21
|
+
CONTROL_CHARS = (0.chr..31.chr).to_a
|
22
|
+
PUNCT = (33..47).map(&:chr) + (58..64).map(&:chr) + (91..96).map(&:chr) + (123..126).map(&:chr)
|
23
|
+
|
24
|
+
ESCAPE_CHARS = { "n" => "\n", "s" => "\s", "r" => "\r", "t" => "\t", "v" => "\v", "f" => "\f", "a" => "\a", "e" => "\e" }
|
25
|
+
|
26
|
+
class ParseError < StandardError; end
|
27
|
+
|
28
|
+
def self.cache
|
29
|
+
@cache ||= {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.clear_cache
|
33
|
+
@cache = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.gen(exp)
|
37
|
+
source = if exp.respond_to?(:source) then exp.source else exp.to_s end
|
38
|
+
cache[source] ||= Transform.new.apply((Parser::Expression.new.parse(source)))
|
39
|
+
Randomizer.randomize(cache[source])
|
40
|
+
rescue Parslet::ParseFailed => e
|
41
|
+
raise Expgen::ParseError, e.message
|
42
|
+
end
|
43
|
+
end
|
data/lib/expgen/nodes.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
module Expgen
|
2
|
+
module Nodes
|
3
|
+
class Node
|
4
|
+
attr_reader :ast
|
5
|
+
|
6
|
+
def initialize(ast)
|
7
|
+
@ast = ast
|
8
|
+
end
|
9
|
+
|
10
|
+
def repeat
|
11
|
+
ast[:repeat]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Group < Node
|
16
|
+
def elements
|
17
|
+
ast[:elements]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Alternation < Node
|
22
|
+
def options
|
23
|
+
ast.map { |option| option[:alt] }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Character < Node; end
|
28
|
+
|
29
|
+
class CharacterClass < Character
|
30
|
+
def groups
|
31
|
+
ast[:groups]
|
32
|
+
end
|
33
|
+
|
34
|
+
def chars
|
35
|
+
chars = groups.map(&:chars).flatten
|
36
|
+
val = if ast[:negative]
|
37
|
+
ASCII - chars
|
38
|
+
else
|
39
|
+
chars
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Literal < Character
|
45
|
+
def chars
|
46
|
+
[ast[:letter].to_s]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Wildcard < Character
|
51
|
+
def chars
|
52
|
+
ASCII
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Shorthand < Character
|
57
|
+
def chars
|
58
|
+
case ast[:letter].to_s
|
59
|
+
when "w" then WORD
|
60
|
+
when "W" then NEGATIVE_WORD
|
61
|
+
when "d" then DIGIT
|
62
|
+
when "D" then NON_DIGIT
|
63
|
+
when "h" then HEX_DIGIT
|
64
|
+
when "H" then NON_HEX_DIGIT
|
65
|
+
when "s" then SPACE
|
66
|
+
when "S" then NON_SPACE
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class BracketExpression < Character
|
72
|
+
def chars
|
73
|
+
case ast[:name].to_s
|
74
|
+
when "alnum" then ALPHA + DIGIT
|
75
|
+
when "alpha" then ALPHA
|
76
|
+
when "blank" then " "
|
77
|
+
when "cntrl" then CONTROL_CHARS
|
78
|
+
when "digit" then DIGIT
|
79
|
+
when "graph" then NON_SPACE
|
80
|
+
when "lower" then LOWER
|
81
|
+
when "print" then ASCII
|
82
|
+
when "punct" then PUNCT
|
83
|
+
when "space" then SPACE
|
84
|
+
when "upper" then UPPER
|
85
|
+
when "xdigit" then HEX_DIGIT
|
86
|
+
when "word" then WORD + ["_"]
|
87
|
+
when "ascii" then ASCII
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Range < Character
|
93
|
+
def chars
|
94
|
+
(ast[:from].to_s..ast[:to].to_s).to_a
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class EscapeCharControl < Character
|
99
|
+
def chars
|
100
|
+
[ESCAPE_CHARS[ast[:letter].to_s]]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class EscapeCharLiteral < Character
|
105
|
+
def chars
|
106
|
+
[ast[:letter].to_s]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class CodePointOctal < Character
|
111
|
+
def chars
|
112
|
+
[ast[:code].to_s.to_i(8).chr]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class CodePointHex < Character
|
117
|
+
def chars
|
118
|
+
[ast[:code].to_s.to_i(16).chr]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class CodePointUnicode < Character
|
123
|
+
def chars
|
124
|
+
[ast[:code].to_s.to_i(16).chr("UTF-8")]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Expgen
|
2
|
+
NON_LITERALS = "[]\^$.|?*+()".split("").map { |l| "\\" + l }.join
|
3
|
+
|
4
|
+
module Parser
|
5
|
+
class Repeat < Parslet::Parser
|
6
|
+
rule(:lcurly) { str('{') }
|
7
|
+
rule(:rcurly) { str('}') }
|
8
|
+
rule(:multiply) { str('*') }
|
9
|
+
rule(:plus) { str('+') }
|
10
|
+
rule(:comma) { str(',') }
|
11
|
+
rule(:questionmark) { str('?') }
|
12
|
+
|
13
|
+
rule(:integer) { match('[0-9]').repeat(1).as(:int) }
|
14
|
+
|
15
|
+
rule(:amount) { lcurly >> integer.as(:repeat) >> (comma >> integer.as(:max).maybe).maybe >> rcurly }
|
16
|
+
rule(:repeat) { plus.as(:repeat) | multiply.as(:repeat) | questionmark.as(:optional) | amount }
|
17
|
+
|
18
|
+
root(:repeat)
|
19
|
+
end
|
20
|
+
|
21
|
+
class EscapeChar < Parslet::Parser
|
22
|
+
rule(:backslash) { str('\\') }
|
23
|
+
|
24
|
+
rule(:code_point_octal) { backslash >> match["0-7"].repeat(3,3).as(:code) >> Repeat.new.maybe }
|
25
|
+
rule(:code_point_hex) { backslash >> str("x") >> match["0-9a-fA-F"].repeat(2,2).as(:code) >> Repeat.new.maybe }
|
26
|
+
rule(:code_point_unicode) { backslash >> str("u") >> match["0-9a-fA-F"].repeat(4,4).as(:code) >> Repeat.new.maybe }
|
27
|
+
rule(:escape_char_control) { backslash >> match["nsrtvfae"].as(:letter) >> Repeat.new.maybe }
|
28
|
+
rule(:escape_char_literal) { backslash >> any.as(:letter) >> Repeat.new.maybe }
|
29
|
+
|
30
|
+
rule(:escape_char) { escape_char_control.as(:escape_char_control) | code_point_octal.as(:code_point_octal) | code_point_hex.as(:code_point_hex) | code_point_unicode.as(:code_point_unicode) | escape_char_literal.as(:escape_char_literal) }
|
31
|
+
root(:escape_char)
|
32
|
+
end
|
33
|
+
|
34
|
+
class ShorthandCharacterClass < Parslet::Parser
|
35
|
+
rule(:backslash) { str('\\') }
|
36
|
+
|
37
|
+
rule(:char_class_shorthand) { (backslash >> match["wWdDhHsS"].as(:letter) >> Repeat.new.maybe).as(:char_class_shorthand) }
|
38
|
+
|
39
|
+
root(:char_class_shorthand)
|
40
|
+
end
|
41
|
+
|
42
|
+
class CharacterClass < Parslet::Parser
|
43
|
+
rule(:dash) { str('-') }
|
44
|
+
rule(:lbracket) { str('[') }
|
45
|
+
rule(:rbracket) { str(']') }
|
46
|
+
|
47
|
+
rule(:alpha) { match["a-z"] }
|
48
|
+
rule(:number) { match["0-9"] }
|
49
|
+
rule(:char) { match["^\\[\\]"].as(:letter) }
|
50
|
+
rule(:wildcard) { str('.').as(:wildcard) }
|
51
|
+
rule(:range) { (alpha.as(:from) >> dash >> alpha.as(:to)) | (number.as(:from) >> dash >> number.as(:to)) }
|
52
|
+
rule(:bracket_expression) { str("[:") >> alpha.repeat(1).as(:name) >> str(":]") }
|
53
|
+
|
54
|
+
rule(:contents) { ShorthandCharacterClass.new | EscapeChar.new | range.as(:char_class_range) | char.as(:char_class_literal) | bracket_expression.as(:bracket_expression) }
|
55
|
+
rule(:negative) { match["\\^"] }
|
56
|
+
|
57
|
+
rule(:char_class) { (lbracket >> negative.maybe.as(:negative) >> contents.repeat.as(:groups) >> rbracket >> Repeat.new.maybe).as(:char_class) }
|
58
|
+
root(:char_class)
|
59
|
+
end
|
60
|
+
|
61
|
+
class Expression < Parslet::Parser
|
62
|
+
rule(:lparen) { str('(') }
|
63
|
+
rule(:rparen) { str(')') }
|
64
|
+
rule(:pipe) { str('|') }
|
65
|
+
rule(:backslash) { str('\\') }
|
66
|
+
|
67
|
+
rule(:literal) { match["^#{NON_LITERALS}"].as(:letter) >> Repeat.new.maybe }
|
68
|
+
|
69
|
+
rule(:wildcard) { str('.').as(:wildcard) >> Repeat.new.maybe }
|
70
|
+
|
71
|
+
rule(:non_capturing) { str('?:') | str("?-mix:") }
|
72
|
+
rule(:group) { lparen >> non_capturing.maybe >> expression.as(:elements) >> rparen >> Repeat.new.maybe }
|
73
|
+
|
74
|
+
rule(:thing) { anchor | ShorthandCharacterClass.new | EscapeChar.new | wildcard.as(:wildcard) | literal.as(:literal) | group.as(:group) | CharacterClass.new }
|
75
|
+
rule(:things) { thing.repeat(1) }
|
76
|
+
|
77
|
+
rule(:anchor) { str("^") | str("$") | backslash >> match["bBAzZ"] }
|
78
|
+
|
79
|
+
rule(:alternation) { things.as(:alt) >> (pipe >> things.as(:alt)).repeat(1) }
|
80
|
+
|
81
|
+
rule(:expression) { alternation.as(:alternation) | things }
|
82
|
+
root(:expression)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Expgen
|
2
|
+
module Randomizer
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def range(number)
|
6
|
+
if number == "*"
|
7
|
+
[0,5]
|
8
|
+
elsif number == "+"
|
9
|
+
[1,5]
|
10
|
+
elsif number
|
11
|
+
[number[:int].to_i, number[:int].to_i]
|
12
|
+
else
|
13
|
+
[1,1]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def repeat(number)
|
18
|
+
first, last = range(number)
|
19
|
+
number = rand(last - first + 1) + first
|
20
|
+
number.times.map { yield }.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def randomize(tree)
|
24
|
+
case tree
|
25
|
+
when Array then tree.map { |el| randomize(el) }.join
|
26
|
+
when Nodes::Alternation then randomize(tree.options.sample)
|
27
|
+
when Nodes::Group then repeat(tree.repeat) { randomize(tree.elements) }
|
28
|
+
when Nodes::Character then repeat(tree.repeat) { tree.chars.sample }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Expgen
|
2
|
+
class Transform < Parslet::Transform
|
3
|
+
rule(:literal => subtree(:x)) { Nodes::Literal.new(x) }
|
4
|
+
rule(:wildcard => subtree(:x)) { Nodes::Wildcard.new(x) }
|
5
|
+
rule(:char_class_range => subtree(:x)) { Nodes::Range.new(x) }
|
6
|
+
rule(:char_class_literal => subtree(:x)) { Nodes::Literal.new(x) }
|
7
|
+
rule(:char_class_shorthand => subtree(:x)) { Nodes::Shorthand.new(x) }
|
8
|
+
rule(:char_class => subtree(:x)) { Nodes::CharacterClass.new(x) }
|
9
|
+
rule(:escape_char_control => subtree(:x)) { Nodes::EscapeCharControl.new(x) }
|
10
|
+
rule(:escape_char_literal => subtree(:x)) { Nodes::EscapeCharLiteral.new(x) }
|
11
|
+
rule(:code_point_octal => subtree(:x)) { Nodes::CodePointOctal.new(x) }
|
12
|
+
rule(:code_point_hex => subtree(:x)) { Nodes::CodePointHex.new(x) }
|
13
|
+
rule(:code_point_unicode => subtree(:x)) { Nodes::CodePointUnicode.new(x) }
|
14
|
+
rule(:bracket_expression => subtree(:x)) { Nodes::BracketExpression.new(x) }
|
15
|
+
rule(:group => subtree(:x)) { Nodes::Group.new(x) }
|
16
|
+
rule(:alternation => subtree(:x)) { Nodes::Alternation.new(x) }
|
17
|
+
end
|
18
|
+
end
|
data/spec/expgen_spec.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Expgen do
|
4
|
+
def self.test(exp)
|
5
|
+
it "can generate expressions which match #{exp.inspect}", :caller => caller do
|
6
|
+
20.times { Expgen.gen(exp).should =~ exp }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "raises an exception if the regexp can't be parsed" do
|
11
|
+
expect { Expgen.gen(/(?!foo)/) } .to raise_error(Expgen::ParseError)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can generate from a string" do
|
15
|
+
Expgen.gen("f\wo").should =~ /f\wo/
|
16
|
+
end
|
17
|
+
|
18
|
+
test(/foo|bar/)
|
19
|
+
|
20
|
+
describe "groups" do
|
21
|
+
test(/(foo)/)
|
22
|
+
test(/(foo|bar)/)
|
23
|
+
test(/f(oo|ba)r/)
|
24
|
+
test(/f(oo|ba){3}r/)
|
25
|
+
test(/f(oo|ba){3,6}r/)
|
26
|
+
test(/f(oo|ba){3,}r/)
|
27
|
+
test(/f(oo|ba)+r/)
|
28
|
+
test(/f(oo|ba)*r/)
|
29
|
+
test(/f(oo|ba)?r/)
|
30
|
+
test(/f(oo|ba|qx|foo)+r/)
|
31
|
+
test(/(oo)|(ba)/)
|
32
|
+
test(/(o(blah|baz)(o|b))|(ba)/)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "character classes" do
|
36
|
+
test(/[abcd]/)
|
37
|
+
test(/f[abcd]b/)
|
38
|
+
test(/f[a-z]b/)
|
39
|
+
test(/f[0-9]b/)
|
40
|
+
test(/f[&a-z%]b/)
|
41
|
+
test(/f[abcd]b/)
|
42
|
+
test(/f[abcd]*b/)
|
43
|
+
test(/f[abcd]+b/)
|
44
|
+
test(/f[abcd]{2}b/)
|
45
|
+
test(/f[abcd]{2,}b/)
|
46
|
+
test(/f[abcd]{2,4}b/)
|
47
|
+
test(/f[abcd]?b/)
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "anchors (are simply ignored)" do
|
51
|
+
test(/^foo$/)
|
52
|
+
test(/\bfoo\b/)
|
53
|
+
test(/f\Bfo/)
|
54
|
+
test(/\Afoo\z/)
|
55
|
+
test(/\Afoo\Z/)
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "shorthand character classes" do
|
59
|
+
test(/f\wo/)
|
60
|
+
test(/f\w+o/)
|
61
|
+
test(/f\w*o/)
|
62
|
+
test(/f\w{2}o/)
|
63
|
+
test(/f\w{3,}o/)
|
64
|
+
test(/f\w{2,4}o/)
|
65
|
+
test(/f\w?o/)
|
66
|
+
test(/f\Wo/)
|
67
|
+
test(/f\do/)
|
68
|
+
test(/f\Do/)
|
69
|
+
test(/f\ho/)
|
70
|
+
test(/f\Ho/)
|
71
|
+
test(/f\so/)
|
72
|
+
test(/f\So/)
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "shorthand character classes inside regular character classes" do
|
76
|
+
test(/f[\w]o/)
|
77
|
+
test(/f[\w%&]o/)
|
78
|
+
test(/f[\w]o/)
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "negated character classes" do
|
82
|
+
test(/[^abcd]/)
|
83
|
+
test(/f[^abcd]b/)
|
84
|
+
test(/f[^a-z]b/)
|
85
|
+
test(/f[^0-9]b/)
|
86
|
+
test(/f[^&a-z%]b/)
|
87
|
+
test(/f[^abcd]b/)
|
88
|
+
test(/f[^abcd]*b/)
|
89
|
+
test(/f[^abcd]+b/)
|
90
|
+
test(/f[^abcd]{2}b/)
|
91
|
+
test(/f[^abcd]{2,}b/)
|
92
|
+
test(/f[^abcd]{2,4}b/)
|
93
|
+
test(/f[^\w]o/)
|
94
|
+
test(/f[^\w%&]o/)
|
95
|
+
test(/f[^\w]o/)
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "control characters" do
|
99
|
+
test(/f\no/)
|
100
|
+
test(/f\so/)
|
101
|
+
test(/f\ro/)
|
102
|
+
test(/f\to/)
|
103
|
+
test(/f\vo/)
|
104
|
+
test(/f\fo/)
|
105
|
+
test(/f\ao/)
|
106
|
+
test(/f\eo/)
|
107
|
+
test(/f\345o/)
|
108
|
+
test(/f\345123o/)
|
109
|
+
test(/f\xAFo/)
|
110
|
+
test(/f\xAF12o/)
|
111
|
+
test(/f\u04AFo/u)
|
112
|
+
test(/f\u04AF00o/u)
|
113
|
+
test(/f\qo/)
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "control characters in character classes" do
|
117
|
+
test(/f[\n]o/)
|
118
|
+
test(/f[\s]o/)
|
119
|
+
test(/f[\r]o/)
|
120
|
+
test(/f[\t]o/)
|
121
|
+
test(/f[\v]o/)
|
122
|
+
test(/f[\f]o/)
|
123
|
+
test(/f[\a]o/)
|
124
|
+
test(/f[\e]o/)
|
125
|
+
test(/f[\345]o/)
|
126
|
+
test(/f[\xAF]o/)
|
127
|
+
test(/f[\u04AF]o/u)
|
128
|
+
test(/f[\q]o/)
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "repeated letters" do
|
132
|
+
test(/fo*o/)
|
133
|
+
test(/fo+o/)
|
134
|
+
test(/fo{2}o/)
|
135
|
+
test(/fo{2,}o/)
|
136
|
+
test(/fo{2,4}o/)
|
137
|
+
test(/fo?o/)
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "repeated escape characters" do
|
141
|
+
test(/f\n*o/)
|
142
|
+
test(/f\n+o/)
|
143
|
+
test(/f\n{2}o/)
|
144
|
+
test(/f\n{2,5}o/)
|
145
|
+
test(/f\n?o/)
|
146
|
+
test(/f\345*o/)
|
147
|
+
test(/f\345+o/)
|
148
|
+
test(/f\345{2}o/)
|
149
|
+
test(/f\345{2,}o/)
|
150
|
+
test(/f\345{2,4}o/)
|
151
|
+
test(/f\345?o/)
|
152
|
+
test(/f\xAF*o/)
|
153
|
+
test(/f\xAF+o/)
|
154
|
+
test(/f\xAF{2}o/)
|
155
|
+
test(/f\xAF{2,}o/)
|
156
|
+
test(/f\xAF{2,4}o/)
|
157
|
+
test(/f\xAF?o/)
|
158
|
+
test(/f\u04AF*o/u)
|
159
|
+
test(/f\u04AF+o/u)
|
160
|
+
test(/f\u04AF{2}o/u)
|
161
|
+
test(/f\u04AF{2,}o/u)
|
162
|
+
test(/f\u04AF{2,4}o/u)
|
163
|
+
test(/f\u04AF?o/u)
|
164
|
+
test(/f\q+o/)
|
165
|
+
test(/f\q*o/)
|
166
|
+
test(/f\q{2}o/)
|
167
|
+
test(/f\q{2,}o/)
|
168
|
+
test(/f\q{2,4}o/)
|
169
|
+
test(/f\q?o/)
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "escaped special characters" do
|
173
|
+
test(/\//)
|
174
|
+
test(/#{Expgen::NON_LITERALS}/)
|
175
|
+
test(/}{/)
|
176
|
+
test(/\{\}/)
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "the wildcard character" do
|
180
|
+
test(/f.*o/)
|
181
|
+
test(/f.+o/)
|
182
|
+
test(/f.{2}o/)
|
183
|
+
test(/f.{2,}o/)
|
184
|
+
test(/f.{2,4}o/)
|
185
|
+
test(/f.?o/)
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "special char inside character class" do
|
189
|
+
test(/f[.]o/)
|
190
|
+
test(/f[\\]o/)
|
191
|
+
test(/f[a^]o/)
|
192
|
+
test(/f[$]o/)
|
193
|
+
test(/f[|]o/)
|
194
|
+
test(/f[?]o/)
|
195
|
+
test(/f[*]o/)
|
196
|
+
test(/f[+]o/)
|
197
|
+
test(/f[(]o/)
|
198
|
+
test(/f[)]o/)
|
199
|
+
test(/f[{]o/)
|
200
|
+
test(/f[}]o/)
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "optional capture groups" do
|
204
|
+
test(/f(?:foo)o/)
|
205
|
+
test(/f(?:foo|bar)+o/)
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "weird interpolation thing" do
|
209
|
+
test(/f#{/oo/}o/)
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "bracket expressions" do
|
213
|
+
test(/f[[:alnum:]]o/)
|
214
|
+
test(/f[[:alpha:]]o/)
|
215
|
+
test(/f[[:blank:]]o/)
|
216
|
+
test(/f[[:cntrl:]]o/)
|
217
|
+
test(/f[[:digit:]]o/)
|
218
|
+
test(/f[[:graph:]]o/)
|
219
|
+
test(/f[[:lower:]]o/)
|
220
|
+
test(/f[[:print:]]o/)
|
221
|
+
test(/f[[:punct:]]o/)
|
222
|
+
test(/f[[:space:]]o/)
|
223
|
+
test(/f[[:upper:]]o/)
|
224
|
+
test(/f[[:xdigit:]]o/)
|
225
|
+
test(/f[[:word:]]o/)
|
226
|
+
test(/f[[:ascii:]]o/)
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "negated bracket expressions" do
|
230
|
+
test(/f[^[:alnum:]]o/)
|
231
|
+
test(/f[^[:alpha:]]o/)
|
232
|
+
test(/f[^[:blank:]]o/)
|
233
|
+
test(/f[^[:cntrl:]]o/)
|
234
|
+
test(/f[^[:digit:]]o/)
|
235
|
+
test(/f[^[:graph:]]o/)
|
236
|
+
test(/f[^[:lower:]]o/)
|
237
|
+
test(/f[^[:punct:]]o/)
|
238
|
+
test(/f[^[:space:]]o/)
|
239
|
+
test(/f[^[:upper:]]o/)
|
240
|
+
test(/f[^[:xdigit:]]o/)
|
241
|
+
test(/f[^[:word:]]o/)
|
242
|
+
end
|
243
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: expgen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jonas Nicklas
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: parslet
|
16
|
+
prerelease: false
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
none: false
|
23
|
+
type: :runtime
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ! '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
none: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pry
|
32
|
+
prerelease: false
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
none: false
|
39
|
+
type: :development
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
none: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
prerelease: false
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
none: false
|
55
|
+
type: :development
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
none: false
|
62
|
+
description: Generate random strings from regular expression
|
63
|
+
email:
|
64
|
+
- jonas.nicklas@gmail.com
|
65
|
+
executables:
|
66
|
+
- expgen
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- bin/expgen
|
76
|
+
- expgen.gemspec
|
77
|
+
- lib/expgen.rb
|
78
|
+
- lib/expgen/nodes.rb
|
79
|
+
- lib/expgen/parser.rb
|
80
|
+
- lib/expgen/randomizer.rb
|
81
|
+
- lib/expgen/transform.rb
|
82
|
+
- lib/expgen/version.rb
|
83
|
+
- spec/expgen_spec.rb
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
homepage: ''
|
86
|
+
licenses: []
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
none: false
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
none: false
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.8.24
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: Generate random regular expression
|
109
|
+
test_files:
|
110
|
+
- spec/expgen_spec.rb
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
has_rdoc:
|