panini 1.0.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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +28 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +151 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/examples/arithmetic_expression.rb +51 -0
- data/lib/derivation_strategy/base.rb +13 -0
- data/lib/derivation_strategy/leftmost.rb +69 -0
- data/lib/derivation_strategy/random_dampened.rb +115 -0
- data/lib/grammar.rb +31 -0
- data/lib/hash.rb +11 -0
- data/lib/nonterminal.rb +35 -0
- data/lib/panini.rb +6 -0
- data/panini.gemspec +73 -0
- data/spec/derivation_strategy/dampened_probability_production_choice_proxy_spec.rb +194 -0
- data/spec/derivation_strategy/leftmost_spec.rb +90 -0
- data/spec/derivation_strategy/random_dampened_spec.rb +90 -0
- data/spec/grammar_spec.rb +59 -0
- data/spec/nonterminal_spec.rb +79 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/basic_derivation_strategy_shared_example.rb +85 -0
- metadata +146 -0
data/lib/grammar.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Panini
|
2
|
+
|
3
|
+
|
4
|
+
# The Grammar stores the start symbol and nonterminals.
|
5
|
+
class Grammar
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@nonterminals = []
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the grammar's start symbol. This will always be the first
|
12
|
+
# nonterminal added to the grammar.
|
13
|
+
def start
|
14
|
+
@nonterminals[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add a nonterminal to the grammar.
|
18
|
+
def add_nonterminal(name = nil)
|
19
|
+
Panini::Nonterminal.new(name).tap do |new_nonterminal|
|
20
|
+
@nonterminals << new_nonterminal
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The list of nonterminals in the grammar.
|
25
|
+
def nonterminals
|
26
|
+
@nonterminals.dup
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/hash.rb
ADDED
data/lib/nonterminal.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Panini
|
2
|
+
|
3
|
+
class Nonterminal
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
# Initialize a nonterminal. Optionally specify a name.
|
8
|
+
def initialize(name=nil)
|
9
|
+
@productions = []
|
10
|
+
@name = name
|
11
|
+
end
|
12
|
+
|
13
|
+
# Add a production to the nonterminal. It must be an array, but the array can
|
14
|
+
# contain any type of Ruby object.
|
15
|
+
#
|
16
|
+
# nonterminal.add_production([1, 'a', lambda { ...} ])
|
17
|
+
#
|
18
|
+
def add_production(production)
|
19
|
+
raise ArgumentError, "The production must be an Array." unless production.class == Array
|
20
|
+
@productions << production.dup
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# The productions for the nonterminal.
|
25
|
+
def productions
|
26
|
+
@productions.dup
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
name.nil? ? super : @name
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/panini.rb
ADDED
data/panini.gemspec
ADDED
@@ -0,0 +1,73 @@
|
|
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{panini}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["mjbellantoni"]
|
12
|
+
s.date = %q{2011-05-16}
|
13
|
+
s.description = %q{Panini allows you to generate sentences from a context-free grammar, also known as a CFG.}
|
14
|
+
s.email = %q{mjbellantoni@yahoo.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"examples/arithmetic_expression.rb",
|
29
|
+
"lib/derivation_strategy/base.rb",
|
30
|
+
"lib/derivation_strategy/leftmost.rb",
|
31
|
+
"lib/derivation_strategy/random_dampened.rb",
|
32
|
+
"lib/grammar.rb",
|
33
|
+
"lib/hash.rb",
|
34
|
+
"lib/nonterminal.rb",
|
35
|
+
"lib/panini.rb",
|
36
|
+
"panini.gemspec",
|
37
|
+
"spec/derivation_strategy/dampened_probability_production_choice_proxy_spec.rb",
|
38
|
+
"spec/derivation_strategy/leftmost_spec.rb",
|
39
|
+
"spec/derivation_strategy/random_dampened_spec.rb",
|
40
|
+
"spec/grammar_spec.rb",
|
41
|
+
"spec/nonterminal_spec.rb",
|
42
|
+
"spec/spec_helper.rb",
|
43
|
+
"spec/support/basic_derivation_strategy_shared_example.rb"
|
44
|
+
]
|
45
|
+
s.homepage = %q{http://github.com/mjbellantoni/panini}
|
46
|
+
s.licenses = ["MIT"]
|
47
|
+
s.require_paths = ["lib"]
|
48
|
+
s.rubygems_version = %q{1.3.7}
|
49
|
+
s.summary = %q{Create sentences from a context-free grammar (CFG)}
|
50
|
+
|
51
|
+
if s.respond_to? :specification_version then
|
52
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
57
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
58
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.0"])
|
59
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
62
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
63
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
64
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
68
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
69
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.0"])
|
70
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
|
4
|
+
describe Panini::DerivationStrategy::DampenedProbabilityProductionChoiceProxy do
|
5
|
+
|
6
|
+
it "responds to #production" do
|
7
|
+
described_class.new(Panini::Nonterminal.new).should respond_to(:production)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
describe Panini::DerivationStrategy::DampenedProbabilityProductionChoiceProxy, "#production" do
|
14
|
+
|
15
|
+
context "with damping 0.50" do
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@damping = 0.50
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with a production N -> 'a' and rand() -> 0.25" do
|
22
|
+
|
23
|
+
before(:each) do
|
24
|
+
n = Panini::Nonterminal.new
|
25
|
+
n.add_production(['a'])
|
26
|
+
@proxy = described_class.new(n, @damping)
|
27
|
+
Kernel::stub(:rand).and_return(0.25)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns ['a'] after one call" do
|
31
|
+
@proxy.production.should == ['a']
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns ['a'] after two calls" do
|
35
|
+
@proxy.production
|
36
|
+
@proxy.production.should == ['a']
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns ['a'] after three calls" do
|
40
|
+
@proxy.production
|
41
|
+
@proxy.production
|
42
|
+
@proxy.production.should == ['a']
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with a production N -> 'a' | 'b' and rand() -> 0.25" do
|
48
|
+
|
49
|
+
before(:each) do
|
50
|
+
n = Panini::Nonterminal.new
|
51
|
+
n.add_production(['a'])
|
52
|
+
n.add_production(['b'])
|
53
|
+
@proxy = described_class.new(n, @damping)
|
54
|
+
Kernel::stub(:rand).and_return(0.25)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns ['a'] after one call" do
|
58
|
+
@proxy.production.should == ['a']
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns ['a'] after two calls" do
|
62
|
+
@proxy.production
|
63
|
+
@proxy.production.should == ['a']
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns ['b'] after three calls" do
|
67
|
+
@proxy.production
|
68
|
+
@proxy.production
|
69
|
+
@proxy.production.should == ['b']
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "with a production N -> 'a' | 'b' | 'c' and rand() -> 0.3" do
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
n = Panini::Nonterminal.new
|
78
|
+
n.add_production(['a'])
|
79
|
+
n.add_production(['b'])
|
80
|
+
n.add_production(['c'])
|
81
|
+
@proxy = described_class.new(n, @damping)
|
82
|
+
Kernel::stub(:rand).and_return(0.3)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns ['a'] after one call" do
|
86
|
+
@proxy.production.should == ['a']
|
87
|
+
end
|
88
|
+
|
89
|
+
it "returns ['b'] after two calls" do
|
90
|
+
@proxy.production
|
91
|
+
@proxy.production.should == ['b']
|
92
|
+
end
|
93
|
+
|
94
|
+
it "returns ['b'] after three calls" do
|
95
|
+
@proxy.production
|
96
|
+
@proxy.production
|
97
|
+
@proxy.production.should == ['b']
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns ['b'] after four calls" do
|
101
|
+
@proxy.production
|
102
|
+
@proxy.production
|
103
|
+
@proxy.production
|
104
|
+
@proxy.production.should == ['b']
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns ['a'] after five calls" do
|
108
|
+
@proxy.production
|
109
|
+
@proxy.production
|
110
|
+
@proxy.production
|
111
|
+
@proxy.production
|
112
|
+
@proxy.production.should == ['a']
|
113
|
+
end
|
114
|
+
|
115
|
+
it "returns ['c'] after six calls" do
|
116
|
+
@proxy.production
|
117
|
+
@proxy.production
|
118
|
+
@proxy.production
|
119
|
+
@proxy.production
|
120
|
+
@proxy.production
|
121
|
+
@proxy.production.should == ['c']
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
describe Panini::DerivationStrategy::DampenedProbabilityProductionChoiceProxy, "#clone" do
|
133
|
+
|
134
|
+
context "with damping 0.50, a production N -> 'a' | 'b' | 'c' and rand() -> 0.3" do
|
135
|
+
|
136
|
+
before(:each) do
|
137
|
+
n = Panini::Nonterminal.new
|
138
|
+
n.add_production(['a'])
|
139
|
+
n.add_production(['b'])
|
140
|
+
n.add_production(['c'])
|
141
|
+
@proxy = described_class.new(n, 0.50)
|
142
|
+
Kernel::stub(:rand).and_return(0.3)
|
143
|
+
end
|
144
|
+
|
145
|
+
context "and a clone" do
|
146
|
+
|
147
|
+
before(:each) do
|
148
|
+
@clone_proxy = @proxy.clone
|
149
|
+
@clone_proxy.stub(:rand).and_return(0.3)
|
150
|
+
end
|
151
|
+
|
152
|
+
context "the original" do
|
153
|
+
|
154
|
+
it "returns ['a'] after one call" do
|
155
|
+
@proxy.production.should == ['a']
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns ['b'] after two calls" do
|
159
|
+
@proxy.production
|
160
|
+
@proxy.production.should == ['b']
|
161
|
+
end
|
162
|
+
|
163
|
+
it "returns ['b'] after three calls" do
|
164
|
+
@proxy.production
|
165
|
+
@proxy.production
|
166
|
+
@proxy.production.should == ['b']
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
context "the clone" do
|
172
|
+
|
173
|
+
it "returns ['a'] after one call" do
|
174
|
+
@clone_proxy.production.should == ['a']
|
175
|
+
end
|
176
|
+
|
177
|
+
it "returns ['b'] after two calls" do
|
178
|
+
@clone_proxy.production
|
179
|
+
@clone_proxy.production.should == ['b']
|
180
|
+
end
|
181
|
+
|
182
|
+
it "returns ['b'] after three calls" do
|
183
|
+
@clone_proxy.production
|
184
|
+
@clone_proxy.production
|
185
|
+
@clone_proxy.production.should == ['b']
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
|
4
|
+
describe Panini::DerivationStrategy::Leftmost do
|
5
|
+
it_behaves_like "basic derivation strategy"
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
describe "Grammar with the production S -> AAB, A -> 'a' | 'x', B -> 'b'" do
|
11
|
+
|
12
|
+
before (:each) do
|
13
|
+
@g = Panini::Grammar.new
|
14
|
+
|
15
|
+
@n_s = @g.add_nonterminal
|
16
|
+
@n_a = @g.add_nonterminal
|
17
|
+
@n_b = @g.add_nonterminal
|
18
|
+
|
19
|
+
@n_s.add_production([@n_a, @n_a, @n_b])
|
20
|
+
@n_a.add_production(['a'])
|
21
|
+
@n_a.add_production(['x'])
|
22
|
+
@n_b.add_production(['b'])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "generates the sentence ['a', 'x', 'b']" do
|
26
|
+
d = Panini::DerivationStrategy::Leftmost.new(@g)
|
27
|
+
d.sentence.should == ['a', 'x', 'b']
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
describe "Grammar with the production S -> 'a' | 'b'" do
|
35
|
+
|
36
|
+
before (:each) do
|
37
|
+
@g = Panini::Grammar.new
|
38
|
+
@n = @g.add_nonterminal
|
39
|
+
@n.add_production(['a'])
|
40
|
+
@n.add_production(['b'])
|
41
|
+
end
|
42
|
+
|
43
|
+
it "generates the sentence ['a']" do
|
44
|
+
d = Panini::DerivationStrategy::Leftmost.new(@g)
|
45
|
+
d.sentence.should == ['a']
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
describe "Grammar with the production S -> S | 'a' | 'b'" do
|
53
|
+
|
54
|
+
before (:each) do
|
55
|
+
@g = Panini::Grammar.new
|
56
|
+
@n = @g.add_nonterminal
|
57
|
+
@n.add_production([@n])
|
58
|
+
@n.add_production(['a'])
|
59
|
+
@n.add_production(['b'])
|
60
|
+
|
61
|
+
@deriver = Panini::DerivationStrategy::Leftmost.new(@g)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "generates the sentence ['a'] first" do
|
65
|
+
@deriver.sentence.should == ['a']
|
66
|
+
end
|
67
|
+
|
68
|
+
it "generates the sentence ['b'] second" do
|
69
|
+
@deriver.sentence.should
|
70
|
+
@deriver.sentence.should == ['b']
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
describe "with the production S -> 'a' | 'b'" do
|
77
|
+
|
78
|
+
before (:each) do
|
79
|
+
@g = Panini::Grammar.new
|
80
|
+
@n = @g.add_nonterminal
|
81
|
+
@n.add_production(['a'])
|
82
|
+
@n.add_production(['b'])
|
83
|
+
end
|
84
|
+
|
85
|
+
it "generates the sentence ['a']" do
|
86
|
+
d = Panini::DerivationStrategy::Leftmost.new(@g)
|
87
|
+
d.sentence.should == ['a']
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|