Skip to content

Commit

Permalink
moving this shit to github
Browse files Browse the repository at this point in the history
  • Loading branch information
benburkert committed Jul 3, 2008
0 parents commit f0e4df8
Show file tree
Hide file tree
Showing 10 changed files with 448 additions and 0 deletions.
43 changes: 43 additions & 0 deletions Rakefile
@@ -0,0 +1,43 @@
# -*- ruby -*-

require "rake"
require "rake/clean"
require "rake/gempackagetask"
require 'rake/rdoctask'
require "spec"
require "spec/rake/spectask"

DIR = File.dirname(__FILE__)
NAME = 'randexp'
SUMMARY =<<-EOS
Library for generating random strings.
EOS
GEM_VERSION = "0.1.0"

spec = Gem::Specification.new do |s|
s.name = NAME
s.summary = SUMMARY

s.version = GEM_VERSION
s.platform = Gem::Platform::RUBY

s.has_rdoc = true

s.require_path = "lib"
s.files = %w(Rakefile) + Dir["lib/**/*"]
end

Rake::GemPackageTask.new(spec) do |package|
package.gem_spec = spec
package.need_zip = true
package.need_tar = true
end

##############################################################################
# rSpec & rcov
##############################################################################
desc "Run all specs"
Spec::Rake::SpecTask.new("specs") do |t|
t.spec_opts = ["--format", "specdoc", "--colour"]
t.spec_files = Dir["spec/**/*_spec.rb"].sort
end
5 changes: 5 additions & 0 deletions lib/core_ext/array.rb
@@ -0,0 +1,5 @@
class Array
def pick
at rand(size)
end
end
5 changes: 5 additions & 0 deletions lib/core_ext/integer.rb
@@ -0,0 +1,5 @@
class Integer
def of
(1..self).to_a.map { yield }
end
end
5 changes: 5 additions & 0 deletions lib/core_ext/range.rb
@@ -0,0 +1,5 @@
class Range
def pick
to_a.pick
end
end
7 changes: 7 additions & 0 deletions lib/core_ext/regexp.rb
@@ -0,0 +1,7 @@
class Regexp
def generate
Randexp.new(source).generate
end

alias_method :gen, :generate
end
24 changes: 24 additions & 0 deletions lib/dictionary.rb
@@ -0,0 +1,24 @@
class Dictionary
def self.load_dictionary
if File.exists?("/usr/share/dict/words")
File.read("/usr/share/dict/words").split
elsif File.exists?("/usr/dict/words")
File.read("/usr/dict/words").split
else
raise "words file not found"
end
end

def self.words(options = {})
case
when options.has_key?(:length)
words_by_length[options[:length]]
else
@@words ||= load_dictionary
end
end

def self.words_by_length
@@words_by_length ||= words.inject({}) {|h, w| (h[w.size] ||= []) << w; h }
end
end
170 changes: 170 additions & 0 deletions lib/randexp.rb
@@ -0,0 +1,170 @@
require 'core_ext/array'
require 'core_ext/integer'
require 'core_ext/range'
require 'core_ext/regexp'
require 'dictionary'
require 'random'

class Randexp
def self.parse(source)
case source
when /^\(([^()]*)\)$/ then union(parse($1))
when /(.*)\|(?:([^()]*|\(.*\)))$/ then intersection(parse($1), parse($2))
when /(.*)\(([^()]*)\)(\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(parse($2), $3.to_sym))
when /(.*)\(([^()]*)\)\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(parse($2), ($3.to_i)..($4.to_i)))
when /(.*)\(([^()]*)\)\{(\d+)\}$/ then union(parse($1), quantify(parse($2), $3.to_i))
when /(.*)\(([^()]*)\)$/ then union(parse($1), parse($2))
when /(.*)\[:(\w+):\]$/ then union(parse($1), random($2))
when /(.*)\\([wsdc])(\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(random($2), $3.to_sym))
when /(.*)\\([wsdc])\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(random($2), ($3.to_i)..($4.to_i)))
when /(.*)\\([wsdc])\{(\d+)\}$/ then union(parse($1), quantify(random($2), $3.to_i))
when /(.*)\\([wsdc])$/ then union(parse($1), random($2))
when /(.*)(\S)$/ then union(parse($1), literal($2))
else nil
end
end

def self.quantify(lhs, sym)
if lhs.first == :intersection
lhs << [:quantify, lhs.pop, sym]
else
[:quantify, lhs, sym]
end
end

def self.union(lhs, *rhs)
if lhs.nil?
union(*rhs)
elsif rhs.empty?
lhs
elsif lhs.first == :union
rhs.each {|s| lhs << s}
lhs
else
[:union, lhs, *rhs]
end
end

def self.intersection(lhs, rhs)
if rhs.first == :intersection
[:intersection, lhs] + rhs[1..-1]
else
[:intersection, lhs, rhs]
end
end

def self.random(char)
[:random, char.to_sym]
end

def self.literal(word)
[:literal, word]
end

def initialize(source)
@sexp = Randexp.parse(source)
end

def to_s
generate
end

def generate(sexp = @sexp.dup, quant = nil)
send(sexp.shift, sexp, quant)
end

def quantify(sexp, old_quant)
generate(*sexp)
end

def literal(sexp, quant = nil)
raise "quantifier (#{quant}) cannot be used on a literal (#{sexp * ''})" unless quant.nil?
sexp * ""
end

def random(sexp, quant)
case s = sexp.shift
when :w then char(quant)
when :s then whitespace(quant)
when :d then digit(quant)
else send(s, quant)
end
end

def intersection(sexp, quant)
case quant
when :'?' then ['', sexp.map {|s| generate(s)}.pick].pick
when :+, :'+?' then raise "Sorry, \"((...)|(...))+\" is too vague, try setting a range: \"((...)|(...)){1, 3}\""
when :*, :'*?' then raise "Sorry, \"((...)|(...))*\" is too vague, try setting a range: \"((...)|(...)){0, 3}\""
when Range then quant.pick.of { ssexp.map {|s| generate(s)}.pick } * ''
when Integer then quant.of { sexp.map {|s| generate(s)}.pick } * ''
when nil then sexp.map {|s| generate(s)}.pick
end
end

def union(sexp, quant)
case quant
when :'?' then ['', sexp.map {|s| generate(s)} * ''].pick
when :+, :'+?' then raise "Sorry, \"(...)+\" is too vague, try setting a range: \"(...){1, 3}\""
when :*, :'*?' then raise "Sorry, \"(...)*\" is too vague, try setting a range: \"(...){0, 3}\""
when Range then quant.pick.of { sexp.map {|s| generate(s)} * '' } * ''
when Integer then quant.of { sexp.map {|s| generate(s)} * '' } * ''
when nil then sexp.map {|s| generate(s)} * ''
end
end

def char(quant)
case quant
when :'?' then ['', Random.char].pick
when :+, :'+?' then Random.word
when :*, :'*?' then ['', Random.word].pick
when Range then Random.word(:length => quant.pick)
when 1, nil then Random.char
when Integer then Random.word(:length => quant)
end
end

def whitespace(quant)
case quant
when :'?' then ['', Random.whitespace].pick
when :+, :'+?' then raise "Sorry, \"\s+\" is too vague, try setting a range: \"\s{1, 5}\""
when :*, :'*?' then raise "Sorry, \"\s*\" is too vague, try setting a range: \"\s{0, 5}\""
when Range then quant.pick.of { Random.whitespace } * ''
when Integer then quant.of { Random.whitespace } * ''
when nil then Random.whitespace
end
end

def digit(quant)
case quant
when :'?' then ['', Random.digit].pick
when :+, :'+?' then raise "Sorry, \"\d+\" is too vague, try setting a range: \"\d{1, 5}\""
when :*, :'*?' then raise "Sorry, \"\d*\" is too vague, try setting a range: \"\d{0, 5}\""
when Range then quant.pick.of { Random.digit } * ''
when Integer then quant.of { Random.digit } * ''
when nil then Random.digit
end
end

def word(quantity)
case quantity
when :'?' then ['', Random.word].pick
when :+, :'?' then Random.sentence
when :*, :'*?' then ['', Random.sentence].pick
when Range then Random.sentence(:length => quant.pick)
when nil then Random.word
when Integer then Random.paragraph(:length => quant)
end
end

def sentence(quantity)
case quantity
when :'?' then ['', Random.sentence].pick
when :+, :'+?' then Random.paragraph
when :*, :'*?' then ['', Random.paragraph].pick
when Range then Random.paragraph(:length => quant.pick)
when 1, nil then Random.sentence
when Integer then Random.paragraph(:length => quant)
end
end
end
40 changes: 40 additions & 0 deletions lib/random.rb
@@ -0,0 +1,40 @@
class Random
WORDS_PER_SENTENCE = 3..20
SENTENCES_PER_PARAGRAPH = 3..8

def self.bool
[true, false].pick
end

def self.lchar
('a'..'z').to_a.pick
end

def self.uchar
('A'..'Z').to_a.pick
end

def self.char
[lchar, uchar].pick
end

def self.whitespace
["\t", "\n", "\r", "\f"].pick
end

def self.digit
('0'..'9').to_a.pick
end

def self.word(options = {})
Dictionary.words(options).pick
end

def self.sentence(options = {})
((options[:length] || WORDS_PER_SENTENCE.pick).of { word } * " ").capitalize
end

def self.paragraph(options = {})
((options[:length] || SENTENCES_PER_PARAGRAPH.pick).of { sentence } * ". ") + "."
end
end

0 comments on commit f0e4df8

Please sign in to comment.