grammoire 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +10 -0
- data/Gemfile.lock +34 -0
- data/LICENSE +20 -0
- data/README.md +4 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/examples/features/conditions.rb +21 -0
- data/examples/features/custom_context.rb +24 -0
- data/examples/features/data_points.rb +9 -0
- data/examples/features/probability.rb +12 -0
- data/examples/features/single_rule.rb +11 -0
- data/grammoire.gemspec +65 -0
- data/lib/grammoire.rb +15 -0
- data/lib/grammoire/evaluation_context.rb +26 -0
- data/lib/grammoire/grammar.rb +34 -0
- data/lib/grammoire/rule.rb +34 -0
- data/lib/grammoire/rule_chooser.rb +25 -0
- data/lib/grammoire/util/random_generator.rb +9 -0
- data/spec/grammoire/evaluation_context_spec.rb +32 -0
- data/spec/grammoire/grammar_spec.rb +77 -0
- data/spec/grammoire/rule_chooser_spec.rb +32 -0
- data/spec/grammoire/rule_spec.rb +62 -0
- data/spec/spec_helper.rb +20 -0
- metadata +95 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.8.3)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rdoc
|
11
|
+
json (1.6.5)
|
12
|
+
metaclass (0.0.1)
|
13
|
+
mocha (0.11.3)
|
14
|
+
metaclass (~> 0.0.1)
|
15
|
+
rake (0.9.2.2)
|
16
|
+
rdoc (3.10)
|
17
|
+
json (~> 1.4)
|
18
|
+
rspec (2.9.0)
|
19
|
+
rspec-core (~> 2.9.0)
|
20
|
+
rspec-expectations (~> 2.9.0)
|
21
|
+
rspec-mocks (~> 2.9.0)
|
22
|
+
rspec-core (2.9.0)
|
23
|
+
rspec-expectations (2.9.1)
|
24
|
+
diff-lcs (~> 1.1.3)
|
25
|
+
rspec-mocks (2.9.0)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
bundler (~> 1.0.0)
|
32
|
+
jeweler (~> 1.8.3)
|
33
|
+
mocha
|
34
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Paulo Schneider
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'bundler'
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
require 'rake'
|
12
|
+
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |gem|
|
15
|
+
gem.name = "grammoire"
|
16
|
+
gem.homepage = "http://github.com/moonpxi/grammoire"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{A context-sensitive grammar library for producing randomised objects}
|
19
|
+
gem.description = %Q{A context-sensitive grammar library for producing randomised objects}
|
20
|
+
gem.email = "paulo.schneider@gmail.com"
|
21
|
+
gem.authors = ["moonpxi"]
|
22
|
+
end
|
23
|
+
Jeweler::RubygemsDotOrgTasks.new
|
24
|
+
|
25
|
+
desc 'Run specs'
|
26
|
+
RSpec::Core::RakeTask.new do |t|
|
27
|
+
t.rspec_opts = %w{--format s --color}
|
28
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/grammoire")
|
2
|
+
|
3
|
+
grammar = Grammoire.define do
|
4
|
+
rule(:conditionals) do
|
5
|
+
pre_condition { data(:variable) == 5 }
|
6
|
+
produce {'You got 5!'}
|
7
|
+
end
|
8
|
+
rule(:conditionals) do
|
9
|
+
pre_condition { data(:variable) < 5 }
|
10
|
+
produce {'You got less then 5. :('}
|
11
|
+
end
|
12
|
+
rule(:conditionals) do
|
13
|
+
pre_condition { data(:variable) > 5 }
|
14
|
+
produce {'You got more then 5. :)'}
|
15
|
+
end
|
16
|
+
rule(:conditionals) { produce {'I am always valid.'} }
|
17
|
+
end
|
18
|
+
|
19
|
+
[5, 2, 8].each do |number|
|
20
|
+
puts "Variable #{number} gives #{grammar.evaluate(:conditionals, :variable => number)}"
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/grammoire")
|
2
|
+
|
3
|
+
class ArrayEvaluationContext < Grammoire::EvaluationContext
|
4
|
+
def one_of(*productions)
|
5
|
+
production = productions[rand(productions.size)]
|
6
|
+
evaluated_results = production.collect do |element|
|
7
|
+
if element.kind_of? Symbol
|
8
|
+
eval(element)
|
9
|
+
else
|
10
|
+
element
|
11
|
+
end
|
12
|
+
end
|
13
|
+
return evaluated_results.join
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
grammar = Grammoire.define do
|
19
|
+
context ArrayEvaluationContext
|
20
|
+
|
21
|
+
rule(:s) { produce { one_of([:s, :s], ['(', :s, ')'], ['()']) } }
|
22
|
+
end
|
23
|
+
|
24
|
+
10.times { puts grammar.evaluate(:s) + "\n\n" }
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/grammoire")
|
2
|
+
|
3
|
+
grammar = Grammoire.define do
|
4
|
+
rule(:full_name) { produce { eval(:first_name) + ' ' + eval(:last_name) } }
|
5
|
+
rule(:first_name) { produce { data(:first) } }
|
6
|
+
rule(:last_name) { produce { data(:last) } }
|
7
|
+
end
|
8
|
+
|
9
|
+
puts grammar.evaluate(:full_name, :first => "Jonny", :last => "Data")
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/grammoire")
|
2
|
+
|
3
|
+
grammar = Grammoire.define do
|
4
|
+
rule(:s) { produce { 'a' + eval(:s) }; weights 4 }
|
5
|
+
rule(:s) { produce { 'b' + eval(:s) } }
|
6
|
+
rule(:s) { produce { eval(:end) } }
|
7
|
+
|
8
|
+
rule(:end) { produce { 'x' }; weights 10 }
|
9
|
+
rule(:end) { produce { 'y' } }
|
10
|
+
end
|
11
|
+
|
12
|
+
10.times { puts grammar.evaluate(:s) + "\n\n" }
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/grammoire")
|
2
|
+
|
3
|
+
grammar = Grammoire.define do
|
4
|
+
rule(:s) { produce { eval(:s) + eval(:s) } }
|
5
|
+
rule(:s) { produce { "(#{eval(:s)})" } }
|
6
|
+
rule(:s) { produce { '()' } }
|
7
|
+
rule(:s) { produce { "[#{eval(:s)}]" } }
|
8
|
+
rule(:s) { produce { '[]' } }
|
9
|
+
end
|
10
|
+
|
11
|
+
10.times { puts grammar.evaluate(:s) + "\n\n" }
|
data/grammoire.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{grammoire}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = [%q{moonpxi}]
|
12
|
+
s.date = %q{2012-04-28}
|
13
|
+
s.description = %q{A context-sensitive grammar library for producing randomised objects}
|
14
|
+
s.email = %q{paulo.schneider@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"Gemfile",
|
21
|
+
"Gemfile.lock",
|
22
|
+
"LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"examples/features/conditions.rb",
|
27
|
+
"examples/features/custom_context.rb",
|
28
|
+
"examples/features/data_points.rb",
|
29
|
+
"examples/features/probability.rb",
|
30
|
+
"examples/features/single_rule.rb",
|
31
|
+
"grammoire.gemspec",
|
32
|
+
"lib/grammoire.rb",
|
33
|
+
"lib/grammoire/evaluation_context.rb",
|
34
|
+
"lib/grammoire/grammar.rb",
|
35
|
+
"lib/grammoire/rule.rb",
|
36
|
+
"lib/grammoire/rule_chooser.rb",
|
37
|
+
"lib/grammoire/util/random_generator.rb",
|
38
|
+
"spec/grammoire/evaluation_context_spec.rb",
|
39
|
+
"spec/grammoire/grammar_spec.rb",
|
40
|
+
"spec/grammoire/rule_chooser_spec.rb",
|
41
|
+
"spec/grammoire/rule_spec.rb",
|
42
|
+
"spec/spec_helper.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/moonpxi/grammoire}
|
45
|
+
s.licenses = [%q{MIT}]
|
46
|
+
s.require_paths = [%q{lib}]
|
47
|
+
s.rubygems_version = %q{1.8.6}
|
48
|
+
s.summary = %q{A context-sensitive grammar library for producing randomised objects}
|
49
|
+
|
50
|
+
if s.respond_to? :specification_version then
|
51
|
+
s.specification_version = 3
|
52
|
+
|
53
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
55
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
58
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
59
|
+
end
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
62
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/grammoire.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/grammoire/util/random_generator')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/grammoire/grammar')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/grammoire/rule')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/grammoire/evaluation_context')
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/grammoire/rule_chooser')
|
6
|
+
|
7
|
+
class GrammarError < StandardError; end
|
8
|
+
|
9
|
+
module Grammoire
|
10
|
+
def self.define(&block)
|
11
|
+
grammar = Grammar.new
|
12
|
+
grammar.instance_eval &block
|
13
|
+
grammar
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Grammoire
|
2
|
+
|
3
|
+
class EvaluationContext
|
4
|
+
|
5
|
+
def initialize(grammar)
|
6
|
+
@grammar = grammar
|
7
|
+
with_data_points({})
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_data_points(data)
|
11
|
+
@data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def data(name)
|
15
|
+
raise GrammarError.new("Data point '#{name}' doesn't exist in the evaluation context.") unless @data.has_key? name
|
16
|
+
|
17
|
+
return @data[name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(rule)
|
21
|
+
@grammar.evaluate(rule, @data)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Grammoire
|
2
|
+
class Grammar
|
3
|
+
|
4
|
+
def initialize(random_generator = RandomGenerator.new)
|
5
|
+
@rules = []
|
6
|
+
@chooser = RuleChooser.new(random_generator)
|
7
|
+
context(EvaluationContext)
|
8
|
+
end
|
9
|
+
|
10
|
+
def context(context_class)
|
11
|
+
@context = context_class.new(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def rule(name, &action)
|
15
|
+
@rules << Rule.new(name, &action)
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate(rule_name, data = {})
|
19
|
+
@context.with_data_points(data)
|
20
|
+
|
21
|
+
return @chooser.select_from(rules_for(rule_name)).evaluate(@context)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def rules_for(name)
|
27
|
+
rules = @rules.select { |rule| rule.name == name && rule.applies?(@context) }
|
28
|
+
raise GrammarError.new("Rule '#{name}' doesn't exist in the grammar or don't have valid pre-conditions.") if rules.empty?
|
29
|
+
|
30
|
+
return rules
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Grammoire
|
2
|
+
class Rule
|
3
|
+
attr_reader :name, :weight
|
4
|
+
|
5
|
+
def initialize(name, &setup)
|
6
|
+
@name = name
|
7
|
+
weights(1)
|
8
|
+
pre_condition { true }
|
9
|
+
|
10
|
+
instance_eval &setup if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
def weights(weight)
|
14
|
+
@weight = weight
|
15
|
+
end
|
16
|
+
|
17
|
+
def pre_condition(&condition)
|
18
|
+
@condition = condition
|
19
|
+
end
|
20
|
+
|
21
|
+
def applies?(context)
|
22
|
+
context.instance_eval &@condition
|
23
|
+
end
|
24
|
+
|
25
|
+
def produce(&action)
|
26
|
+
@action = action
|
27
|
+
end
|
28
|
+
|
29
|
+
def evaluate(context)
|
30
|
+
context.instance_eval &@action
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class RuleChooser
|
2
|
+
|
3
|
+
def initialize(random_generator = RandomGenerator.new)
|
4
|
+
@random_generator = random_generator
|
5
|
+
end
|
6
|
+
|
7
|
+
def select_from(rules)
|
8
|
+
return nil if rules.empty?
|
9
|
+
|
10
|
+
selected_weight_index = @random_generator.number_up_to(total_weight_from(rules))
|
11
|
+
max_weight_index = 0
|
12
|
+
|
13
|
+
rules.each do |rule|
|
14
|
+
max_weight_index += rule.weight
|
15
|
+
return rule if selected_weight_index < max_weight_index
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def total_weight_from(rules)
|
22
|
+
rules.inject(0) { |sum, rule| sum + rule.weight }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EvaluationContext do
|
4
|
+
let(:grammar) { mock() }
|
5
|
+
let(:context) { EvaluationContext.new(grammar) }
|
6
|
+
|
7
|
+
it 'should evaluate rule in context by evaluating from the grammar' do
|
8
|
+
grammar.expects(:evaluate).with(:some_rule, {}).returns("some result")
|
9
|
+
|
10
|
+
context.eval(:some_rule).should == "some result"
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should raise error when access inexistent data point' do
|
14
|
+
lambda { context.data(:dont_exist) }.should raise_error(GrammarError, "Data point 'dont_exist' doesn't exist in the evaluation context.")
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should access data point' do
|
18
|
+
context.with_data_points(:point_a => "abc", :point_b => 123)
|
19
|
+
|
20
|
+
context.data(:point_a).should == "abc"
|
21
|
+
context.data(:point_b).should == 123
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should forward data point when producing rule' do
|
25
|
+
data = {:should => 'keep', :this => 'data'}
|
26
|
+
context.with_data_points(data)
|
27
|
+
|
28
|
+
grammar.expects(:evaluate).with(:rule_with_data, data)
|
29
|
+
|
30
|
+
context.eval(:rule_with_data)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grammar do
|
4
|
+
let(:grammar) { Grammar.new }
|
5
|
+
|
6
|
+
describe '(validation)' do
|
7
|
+
it 'should raise an exception when trying to evaluate output for non existent rule' do
|
8
|
+
lambda { grammar.evaluate(:no_such_rule) }.should raise_error(GrammarError, "Rule 'no_such_rule' doesn't exist in the grammar or don't have valid pre-conditions.")
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should raise an exception when evaluating a rule without valid productions' do
|
12
|
+
grammar.rule(:invalid) { pre_condition { false }; produce { 'not valid' } }
|
13
|
+
|
14
|
+
lambda { grammar.evaluate(:invalid) }.should raise_error(GrammarError, "Rule 'invalid' doesn't exist in the grammar or don't have valid pre-conditions.")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '(producing)' do
|
19
|
+
it 'should evaluate terminal symbol of a rule' do
|
20
|
+
grammar.rule(:terminal) { produce { 'terminal' } }
|
21
|
+
|
22
|
+
grammar.evaluate(:terminal).should == 'terminal'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should evaluate and substitute symbols when producing a non-terminal rule' do
|
26
|
+
grammar.rule(:non_terminal) { produce { 'non-' + eval(:terminal) } }
|
27
|
+
grammar.rule(:terminal) { produce { 'terminal' } }
|
28
|
+
|
29
|
+
grammar.evaluate(:non_terminal).should == 'non-terminal'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should select one of the production rules' do
|
33
|
+
random_generator = StubRandomGenerator.should_produce(0, 1, 1)
|
34
|
+
grammar = Grammar.new(random_generator)
|
35
|
+
|
36
|
+
grammar.rule(:two_choices) { produce { 'terminal x' } }
|
37
|
+
grammar.rule(:two_choices) { produce { 'terminal y' } }
|
38
|
+
|
39
|
+
grammar.evaluate(:two_choices).should == 'terminal x'
|
40
|
+
grammar.evaluate(:two_choices).should == 'terminal y'
|
41
|
+
grammar.evaluate(:two_choices).should == 'terminal y'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should evaluate rules that pass the pre condition check' do
|
45
|
+
grammar.rule(:condition) { pre_condition { false }; produce { 'not running' } }
|
46
|
+
grammar.rule(:condition) { pre_condition { true }; produce { 'I am true!' } }
|
47
|
+
grammar.rule(:condition) { pre_condition { false }; produce { 'I am false' }; weights 1000 }
|
48
|
+
|
49
|
+
grammar.evaluate(:condition).should == 'I am true!'
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '(data points)' do
|
55
|
+
it 'should evaluate with data points' do
|
56
|
+
grammar.rule(:with_data) { produce { data(:some_data) } }
|
57
|
+
|
58
|
+
grammar.evaluate(:with_data, :some_data => 123).should == 123
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '(context)' do
|
63
|
+
it 'should define custom context for productions to be executed' do
|
64
|
+
class CustomContext < EvaluationContext
|
65
|
+
def custom_method
|
66
|
+
"hello there"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
grammar.context(CustomContext)
|
71
|
+
grammar.rule(:custom) { produce { custom_method } }
|
72
|
+
|
73
|
+
grammar.evaluate(:custom).should == "hello there"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RuleChooser do
|
4
|
+
let(:generator) { StubRandomGenerator.new }
|
5
|
+
let(:chooser) { RuleChooser.new(generator) }
|
6
|
+
|
7
|
+
it 'should return nil if no rule is given' do
|
8
|
+
chooser.select_from([]).should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should select only rule if only one rule is given' do
|
12
|
+
generator.returning_results(0, 1)
|
13
|
+
|
14
|
+
rule = Rule.new(:name)
|
15
|
+
|
16
|
+
chooser.select_from([rule]).should == rule
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should select rule based on the probability over the weightings' do
|
20
|
+
generator.returning_results(0, 1, 2, 3)
|
21
|
+
|
22
|
+
heavier_rule = Rule.new(:heavy) { weights 3 }
|
23
|
+
lighter_rule = Rule.new(:heavy) { weights 1 }
|
24
|
+
rules = [heavier_rule, lighter_rule]
|
25
|
+
|
26
|
+
chooser.select_from(rules).should == heavier_rule
|
27
|
+
chooser.select_from(rules).should == heavier_rule
|
28
|
+
chooser.select_from(rules).should == heavier_rule
|
29
|
+
chooser.select_from(rules).should == lighter_rule
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rule do
|
4
|
+
let(:context) { mock() }
|
5
|
+
let(:rule) { Rule.new(:rule_name) }
|
6
|
+
|
7
|
+
it 'should construct rule with a setup block' do
|
8
|
+
rule = Rule.new(:my_name) do
|
9
|
+
weights 5
|
10
|
+
produce { 'production' }
|
11
|
+
end
|
12
|
+
|
13
|
+
rule.name.should == :my_name
|
14
|
+
rule.weight == 5
|
15
|
+
rule.evaluate('any context').should == 'production'
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '(weighting)' do
|
19
|
+
it 'should have default weight of 1' do
|
20
|
+
rule.weight.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should allow changing the weight' do
|
24
|
+
rule.weights(3)
|
25
|
+
|
26
|
+
rule.weight.should == 3
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '(conditions)' do
|
31
|
+
it 'should always applies when pre conditions not specified' do
|
32
|
+
rule.applies?(context).should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should apply only if pre condition is valid within context' do
|
36
|
+
context.expects(:data).with(:number).returns(30)
|
37
|
+
|
38
|
+
rule.pre_condition { data(:number) == 30 }
|
39
|
+
|
40
|
+
rule.applies?(context).should be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should not apply if pre condition is invalid within context' do
|
44
|
+
context.expects(:data).with(:number).returns(42)
|
45
|
+
|
46
|
+
rule.pre_condition { data(:number) == 30 }
|
47
|
+
|
48
|
+
rule.applies?(context).should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '(evaluation)' do
|
53
|
+
it 'should evaluate production in the given context' do
|
54
|
+
context.expects(:inside_context).returns('context response')
|
55
|
+
|
56
|
+
rule.produce { inside_context }
|
57
|
+
|
58
|
+
rule.evaluate(context).should == 'context response'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'mocha'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/grammoire")
|
4
|
+
include Grammoire
|
5
|
+
|
6
|
+
class StubRandomGenerator < RandomGenerator
|
7
|
+
|
8
|
+
def returning_results(*results)
|
9
|
+
@results = results
|
10
|
+
return self
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.should_produce(*results)
|
14
|
+
StubRandomGenerator.new.returning_results(*results)
|
15
|
+
end
|
16
|
+
|
17
|
+
def number_up_to(max)
|
18
|
+
@results.shift
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grammoire
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- moonpxi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-28 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &70237364121240 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70237364121240
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: jeweler
|
27
|
+
requirement: &70237364120520 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.8.3
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70237364120520
|
36
|
+
description: A context-sensitive grammar library for producing randomised objects
|
37
|
+
email: paulo.schneider@gmail.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.md
|
43
|
+
files:
|
44
|
+
- Gemfile
|
45
|
+
- Gemfile.lock
|
46
|
+
- LICENSE
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- examples/features/conditions.rb
|
51
|
+
- examples/features/custom_context.rb
|
52
|
+
- examples/features/data_points.rb
|
53
|
+
- examples/features/probability.rb
|
54
|
+
- examples/features/single_rule.rb
|
55
|
+
- grammoire.gemspec
|
56
|
+
- lib/grammoire.rb
|
57
|
+
- lib/grammoire/evaluation_context.rb
|
58
|
+
- lib/grammoire/grammar.rb
|
59
|
+
- lib/grammoire/rule.rb
|
60
|
+
- lib/grammoire/rule_chooser.rb
|
61
|
+
- lib/grammoire/util/random_generator.rb
|
62
|
+
- spec/grammoire/evaluation_context_spec.rb
|
63
|
+
- spec/grammoire/grammar_spec.rb
|
64
|
+
- spec/grammoire/rule_chooser_spec.rb
|
65
|
+
- spec/grammoire/rule_spec.rb
|
66
|
+
- spec/spec_helper.rb
|
67
|
+
homepage: http://github.com/moonpxi/grammoire
|
68
|
+
licenses:
|
69
|
+
- MIT
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
hash: 443293151484266560
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 1.8.6
|
92
|
+
signing_key:
|
93
|
+
specification_version: 3
|
94
|
+
summary: A context-sensitive grammar library for producing randomised objects
|
95
|
+
test_files: []
|