parslet 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README +1 -1
  3. data/lib/parslet/context.rb +1 -1
  4. data/lib/parslet/parser.rb +1 -1
  5. data/parslet.gemspec +18 -0
  6. data/spec/acceptance/examples_spec.rb +37 -0
  7. data/spec/acceptance/infix_parser_spec.rb +112 -0
  8. data/spec/acceptance/regression_spec.rb +314 -0
  9. data/spec/acceptance/repetition_and_maybe_spec.rb +42 -0
  10. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  11. data/spec/parslet/atom_results_spec.rb +39 -0
  12. data/spec/parslet/atoms/alternative_spec.rb +26 -0
  13. data/spec/parslet/atoms/base_spec.rb +126 -0
  14. data/spec/parslet/atoms/capture_spec.rb +21 -0
  15. data/spec/parslet/atoms/combinations_spec.rb +5 -0
  16. data/spec/parslet/atoms/dsl_spec.rb +25 -0
  17. data/spec/parslet/atoms/entity_spec.rb +77 -0
  18. data/spec/parslet/atoms/infix_spec.rb +5 -0
  19. data/spec/parslet/atoms/lookahead_spec.rb +22 -0
  20. data/spec/parslet/atoms/named_spec.rb +4 -0
  21. data/spec/parslet/atoms/re_spec.rb +14 -0
  22. data/spec/parslet/atoms/repetition_spec.rb +24 -0
  23. data/spec/parslet/atoms/scope_spec.rb +26 -0
  24. data/spec/parslet/atoms/sequence_spec.rb +28 -0
  25. data/spec/parslet/atoms/str_spec.rb +15 -0
  26. data/spec/parslet/atoms/visitor_spec.rb +80 -0
  27. data/spec/parslet/atoms_spec.rb +429 -0
  28. data/spec/parslet/convenience_spec.rb +48 -0
  29. data/spec/parslet/error_reporter/contextual_spec.rb +115 -0
  30. data/spec/parslet/error_reporter/deepest_spec.rb +73 -0
  31. data/spec/parslet/error_reporter/tree_spec.rb +7 -0
  32. data/spec/parslet/export_spec.rb +67 -0
  33. data/spec/parslet/expression/treetop_spec.rb +74 -0
  34. data/spec/parslet/minilisp.citrus +29 -0
  35. data/spec/parslet/minilisp.tt +29 -0
  36. data/spec/parslet/parser_spec.rb +31 -0
  37. data/spec/parslet/parslet_spec.rb +38 -0
  38. data/spec/parslet/pattern_spec.rb +272 -0
  39. data/spec/parslet/position_spec.rb +14 -0
  40. data/spec/parslet/rig/rspec_spec.rb +54 -0
  41. data/spec/parslet/scope_spec.rb +45 -0
  42. data/spec/parslet/slice_spec.rb +144 -0
  43. data/spec/parslet/source/line_cache_spec.rb +74 -0
  44. data/spec/parslet/source_spec.rb +168 -0
  45. data/spec/parslet/transform/context_spec.rb +35 -0
  46. data/spec/parslet/transform_spec.rb +165 -0
  47. data/spec/spec_helper.rb +38 -0
  48. metadata +46 -4
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parslet::Parser do
4
+ include Parslet
5
+ class FooParser < Parslet::Parser
6
+ rule(:foo) { str('foo') }
7
+ root(:foo)
8
+ end
9
+
10
+ describe "<- .root" do
11
+ parser = Class.new(Parslet::Parser)
12
+ parser.root :root_parslet
13
+
14
+ it "should have defined a 'root' method, returning the root" do
15
+ parser_instance = parser.new
16
+ flexmock(parser_instance).should_receive(:root_parslet => :answer)
17
+
18
+ parser_instance.root.should == :answer
19
+ end
20
+ end
21
+ it "should parse 'foo'" do
22
+ FooParser.new.parse('foo').should == 'foo'
23
+ end
24
+ context "composition" do
25
+ let(:parser) { FooParser.new }
26
+ it "should allow concatenation" do
27
+ composite = parser >> str('bar')
28
+ composite.should parse('foobar')
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parslet do
4
+ include Parslet
5
+
6
+ describe Parslet::ParseFailed do
7
+ it "should be caught by an empty rescue" do
8
+ begin
9
+ raise Parslet::ParseFailed
10
+ rescue
11
+ # Success! Ignore this.
12
+ end
13
+ end
14
+ end
15
+ describe "<- .rule" do
16
+ # Rules define methods. This can be easily tested by defining them right
17
+ # here.
18
+ context "empty rule" do
19
+ rule(:empty) { }
20
+
21
+ it "should raise a NotImplementedError" do
22
+ lambda {
23
+ empty.parslet
24
+ }.should raise_error(NotImplementedError)
25
+ end
26
+ end
27
+
28
+ context "containing 'any'" do
29
+ rule(:any_rule) { any }
30
+ subject { any_rule }
31
+
32
+ it { should be_a Parslet::Atoms::Entity }
33
+ it "should memoize the returned instance" do
34
+ any_rule.object_id.should == any_rule.object_id
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,272 @@
1
+ require 'spec_helper'
2
+
3
+ require 'parslet'
4
+
5
+ describe Parslet::Pattern do
6
+ include Parslet
7
+
8
+ # These two factory methods help make the specs more robust to interface
9
+ # changes. They also help to label trees (t) and patterns (p).
10
+ def p(pattern)
11
+ Parslet::Pattern.new(pattern)
12
+ end
13
+ def t(obj)
14
+ obj
15
+ end
16
+
17
+ # Tries to match pattern to the tree, and verifies the bindings hash. Don't
18
+ # use this for new examples.
19
+ #
20
+ RSpec::Matchers.define :match_with_bind do |pattern, exp_bindings|
21
+ unless respond_to?(:failure_message)
22
+ alias_method :failure_message_for_should, :failure_message
23
+ end
24
+
25
+ failure_message do |tree|
26
+ "expected #{pattern.inspect} to match #{tree.inspect}, but didn't. (block wasn't called or not correctly)"
27
+ end
28
+ match do |tree|
29
+ bindings = Parslet::Pattern.new(pattern).match(tree)
30
+ bindings && bindings == exp_bindings
31
+ end
32
+ end
33
+
34
+ # This is the more modern version of verifying a match: (uses 'exp'
35
+ # implicitly). Checks for a match of pattern in +exp+ and yields the
36
+ # matched variables.
37
+ #
38
+ def with_match_locals(pattern, &block)
39
+ bindings = p(pattern).match(exp)
40
+ bindings.should_not be_nil
41
+
42
+ block.call(bindings) if block
43
+ end
44
+
45
+ # Can't use #match here, so I went to the Thesaurus.
46
+ #
47
+ RSpec::Matchers.define :detect do |pattern|
48
+ match do |tree|
49
+ bindings = Parslet::Pattern.new(pattern).match(tree)
50
+
51
+ bindings ? true : false
52
+ end
53
+ end
54
+
55
+ describe "<- #match" do
56
+ context "injecting bindings" do
57
+ let(:pattern) { p(simple(:x)) }
58
+
59
+ it "should not modify the original bindings hash" do
60
+ h = {}
61
+ b=pattern.match('a', h)
62
+ h.size.should == 0
63
+ b.size.should == 1
64
+ end
65
+ it "should return nil when no match succeeds" do
66
+ pattern.match([], :foo => :bar).should be_nil
67
+ end
68
+ context "when matching simple(:x) against 'a'" do
69
+ let(:bindings) { pattern.match(t('a'), :foo => :bar) }
70
+
71
+ before(:each) { bindings.should_not be_nil }
72
+ it "should return the injected bindings" do
73
+ bindings[:foo].should == :bar
74
+ end
75
+ it "should return the new bindings" do
76
+ bindings[:x].should == 'a'
77
+ end
78
+ end
79
+ end
80
+ context "simple strings" do
81
+ let(:exp) { 'aaaa' }
82
+
83
+ it "should match simple strings" do
84
+ exp.should match_with_bind(simple(:x), :x => 'aaaa')
85
+ end
86
+ end
87
+ context "simple hash {:a => 'b'}" do
88
+ attr_reader :exp
89
+ before(:each) do
90
+ @exp = t(:a => 'b')
91
+ end
92
+
93
+ it "should not match {:a => simple(:x), :b => simple(:y)}" do
94
+ exp.should_not detect(:a => simple(:x), :b => simple(:y))
95
+ end
96
+ it "should match {:a => simple(:x)}, binding 'x' to the first argument" do
97
+ exp.should match_with_bind({:a => simple(:x)}, :x => 'b')
98
+ end
99
+ it "should match {:a => 'b'} with no binds" do
100
+ exp.should match_with_bind({:a => 'b'}, {})
101
+ end
102
+ end
103
+ context "a more complex hash {:a => {:b => 'c'}}" do
104
+ attr_reader :exp
105
+ before(:each) do
106
+ @exp = t(:a => {:b => 'c'})
107
+ end
108
+
109
+ it "should match wholly with {:a => {:b => simple(:x)}}" do
110
+ exp.should match_with_bind({:a => {:b => simple(:x)}}, :x => 'c')
111
+ end
112
+ it "should match wholly with {:a => subtree(:t)}" do
113
+ with_match_locals(:a => subtree(:t)) do |dict|
114
+ dict[:t].should == {:b => 'c'}
115
+ end
116
+ end
117
+ it "should not bind subtrees to variables in {:a => simple(:x)}" do
118
+ p(:a => simple(:x)).should_not detect(exp)
119
+ end
120
+ end
121
+ context "a more complex hash {:a => 'a', :b => 'b'}" do
122
+ attr_reader :exp
123
+ before(:each) do
124
+ @exp = t({:a => 'a', :b => 'b'})
125
+ end
126
+
127
+ it "should not match partially" do
128
+ Parslet::Pattern.new(:a => simple(:x)).match(exp).should be_nil
129
+ end
130
+ it "should match completely" do
131
+ exp.should match_with_bind({:a => simple(:x), :b => simple(:y)},
132
+ :x => 'a',
133
+ :y => 'b')
134
+ end
135
+ end
136
+ context "an array of 'a', 'b', 'c'" do
137
+ let(:exp) { ['a', 'b', 'c'] }
138
+
139
+ it "should match all elements at once" do
140
+ exp.should match_with_bind(
141
+ [simple(:x), simple(:y), simple(:z)],
142
+ :x => 'a', :y => 'b', :z => 'c')
143
+ end
144
+ end
145
+ context "{:a => 'a', :b => 'b'}" do
146
+ attr_reader :exp
147
+ before(:each) do
148
+ @exp = t(:a => 'a', :b => 'b')
149
+ end
150
+
151
+ it "should match both elements simple(:x), simple(:y)" do
152
+ exp.should match_with_bind(
153
+ {:a => simple(:x), :b => simple(:y)},
154
+ :x => 'a', :y => 'b')
155
+ end
156
+ it "should not match a constrained match (simple(:x) != simple(:y))" do
157
+ exp.should_not detect({:a => simple(:x), :b => simple(:x)})
158
+ end
159
+ end
160
+ context "{:a => 'a', :b => 'a'}" do
161
+ attr_reader :exp
162
+ before(:each) do
163
+ @exp = t(:a => 'a', :b => 'a')
164
+ end
165
+
166
+ it "should match constrained pattern" do
167
+ exp.should match_with_bind(
168
+ {:a => simple(:x), :b => simple(:x)},
169
+ :x => 'a')
170
+ end
171
+ end
172
+ context "{:sub1 => {:a => 'a'}, :sub2 => {:a => 'a'}}" do
173
+ attr_reader :exp
174
+ before(:each) do
175
+ @exp = t({
176
+ :sub1 => {:a => 'a'},
177
+ :sub2 => {:a => 'a'}
178
+ })
179
+ end
180
+
181
+ it "should verify constraints over several subtrees" do
182
+ exp.should match_with_bind({
183
+ :sub1 => {:a => simple(:x)},
184
+ :sub2 => {:a => simple(:x)}
185
+ }, :x => 'a')
186
+ end
187
+ it "should return both bind variables simple(:x), simple(:y)" do
188
+ exp.should match_with_bind({
189
+ :sub1 => {:a => simple(:x)},
190
+ :sub2 => {:a => simple(:y)}
191
+ }, :x => 'a', :y => 'a')
192
+ end
193
+ end
194
+ context "{:sub1 => {:a => 'a'}, :sub2 => {:a => 'b'}}" do
195
+ attr_reader :exp
196
+ before(:each) do
197
+ @exp = t({
198
+ :sub1 => {:a => 'a'},
199
+ :sub2 => {:a => 'b'}
200
+ })
201
+ end
202
+
203
+ it "should verify constraints over several subtrees" do
204
+ exp.should_not match_with_bind({
205
+ :sub1 => {:a => simple(:x)},
206
+ :sub2 => {:a => simple(:x)}
207
+ }, :x => 'a')
208
+ end
209
+ it "should return both bind variables simple(:x), simple(:y)" do
210
+ exp.should match_with_bind({
211
+ :sub1 => {:a => simple(:x)},
212
+ :sub2 => {:a => simple(:y)}
213
+ }, :x => 'a', :y => 'b')
214
+ end
215
+ end
216
+ context "[{:a => 'x'}, {:a => 'y'}]" do
217
+ attr_reader :exp
218
+ before(:each) do
219
+ @exp = t([{:a => 'x'}, {:a => 'y'}])
220
+ end
221
+
222
+ it "should not match sequence(:x) (as a whole)" do
223
+ exp.should_not detect(sequence(:x))
224
+ end
225
+ end
226
+ context "['x', 'y', 'z']" do
227
+ attr_reader :exp
228
+ before(:each) do
229
+ @exp = t(['x', 'y', 'z'])
230
+ end
231
+
232
+ it "should match [simple(:x), simple(:y), simple(:z)]" do
233
+ with_match_locals([simple(:x), simple(:y), simple(:z)]) do |dict|
234
+ dict[:x].should == 'x'
235
+ dict[:y].should == 'y'
236
+ dict[:z].should == 'z'
237
+ end
238
+ end
239
+ it "should match %w(x y z)" do
240
+ exp.should match_with_bind(%w(x y z), { })
241
+ end
242
+ it "should not match [simple(:x), simple(:y), simple(:x)]" do
243
+ exp.should_not detect([simple(:x), simple(:y), simple(:x)])
244
+ end
245
+ it "should not match [simple(:x), simple(:y)]" do
246
+ exp.should_not detect([simple(:x), simple(:y), simple(:x)])
247
+ end
248
+ it "should match sequence(:x) (as array)" do
249
+ exp.should match_with_bind(sequence(:x), :x => ['x', 'y', 'z'])
250
+ end
251
+ end
252
+ context "{:a => [1,2,3]}" do
253
+ attr_reader :exp
254
+ before(:each) do
255
+ @exp = t(:a => [1,2,3])
256
+ end
257
+
258
+ it "should match :a => sequence(:x) (binding x to the whole array)" do
259
+ exp.should match_with_bind({:a => sequence(:x)}, {:x => [1,2,3]})
260
+ end
261
+ end
262
+ context "with differently ordered hashes" do
263
+ it "should still match" do
264
+ t(:a => 'a', :b => 'b').should detect(:a => 'a', :b => 'b')
265
+ t(:a => 'a', :b => 'b').should detect(:b => 'b', :a => 'a')
266
+
267
+ t(:b => 'b', :a => 'a').should detect(:b => 'b', :a => 'a')
268
+ t(:b => 'b', :a => 'a').should detect(:a => 'a', :b => 'b')
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,14 @@
1
+ # Encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Parslet::Position do
6
+ slet(:position) { described_class.new('öäüö', 4) }
7
+
8
+ it 'should have a charpos of 2' do
9
+ position.charpos.should == 2
10
+ end
11
+ it 'should have a bytepos of 4' do
12
+ position.bytepos.should == 4
13
+ end
14
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ describe 'rspec integration' do
5
+ include Parslet
6
+ subject { str('example') }
7
+
8
+ it { should parse('example') }
9
+ it { should_not parse('foo') }
10
+ it { should parse('example').as('example') }
11
+ it { should_not parse('foo').as('example') }
12
+ it { should_not parse('example').as('foo') }
13
+
14
+ it { str('foo').as(:bar).should parse('foo').as({:bar => 'foo'}) }
15
+ it { str('foo').as(:bar).should_not parse('foo').as({:b => 'f'}) }
16
+
17
+ it 'accepts a block to assert more specific details about the parsing output' do
18
+ str('foo').as(:bar).should(parse('foo').as { |output|
19
+ output.should have_key(:bar)
20
+ output.values.first.should == 'foo'
21
+ })
22
+ end
23
+
24
+ # Uncomment to test error messages manually:
25
+ # it { str('foo').should parse('foo', :trace => true).as('bar') }
26
+ # it { str('foo').should parse('food', :trace => true) }
27
+ # it { str('foo').should_not parse('foo', :trace => true).as('foo') }
28
+ # it { str('foo').should_not parse('foo', :trace => true) }
29
+ # it 'accepts a block to assert more specific details about the parsing output' do
30
+ # str('foo').as(:bar).should(parse('foo', :trace => true).as { |output|
31
+ # output.should_not have_key(:bar)
32
+ # })
33
+ # end
34
+
35
+ end
36
+
37
+ describe 'rspec3 syntax' do
38
+ include Parslet
39
+
40
+ let(:s) { str('example') }
41
+
42
+ it { expect(s).to parse('example') }
43
+ it { expect(s).not_to parse('foo') }
44
+ it { expect(s).to parse('example').as('example') }
45
+ it { expect(s).not_to parse('foo').as('example') }
46
+
47
+ it { expect(s).not_to parse('example').as('foo') }
48
+
49
+ # Uncomment to test error messages manually:
50
+ # it { expect(str('foo')).to parse('foo', :trace => true).as('bar') }
51
+ # it { expect(str('foo')).to parse('food', :trace => true) }
52
+ # it { expect(str('foo')).not_to parse('foo', :trace => true).as('foo') }
53
+ # it { expect(str('foo')).not_to parse('foo', :trace => true) }
54
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parslet::Scope do
4
+ let(:scope) { described_class.new }
5
+
6
+ describe 'simple store/retrieve' do
7
+ before(:each) { scope[:foo] = :bar }
8
+ it "allows storing objects" do
9
+ scope[:obj] = 42
10
+ end
11
+ it "raises on access of empty slots" do
12
+ expect {
13
+ scope[:empty]
14
+ }.to raise_error(Parslet::Scope::NotFound)
15
+ end
16
+ it "allows retrieval of stored values" do
17
+ scope[:foo].should == :bar
18
+ end
19
+ end
20
+
21
+ describe 'scoping' do
22
+ before(:each) { scope[:depth] = 1 }
23
+ before(:each) { scope.push }
24
+
25
+ let(:depth) { scope[:depth] }
26
+ subject { depth }
27
+
28
+ it { should == 1 }
29
+ describe 'after a push' do
30
+ before(:each) { scope.push }
31
+ it { should == 1 }
32
+
33
+ describe 'and reassign' do
34
+ before(:each) { scope[:depth] = 2 }
35
+
36
+ it { should == 2 }
37
+
38
+ describe 'and a pop' do
39
+ before(:each) { scope.pop }
40
+ it { should == 1 }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end