grammoire 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|