]> git.wincent.com - walrat.git/blob - spec/walrat/parser_state_spec.rb
Initial import (extraction from Walrus repo, commit 0c9d44c)
[walrat.git] / spec / walrat / parser_state_spec.rb
1 # encoding: utf-8
2 # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are met:
5 #
6 # 1. Redistributions of source code must retain the above copyright notice,
7 #    this list of conditions and the following disclaimer.
8 # 2. Redistributions in binary form must reproduce the above copyright notice,
9 #    this list of conditions and the following disclaimer in the documentation
10 #    and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
13 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
16 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
18 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
19 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
21 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22 # POSSIBILITY OF SUCH DAMAGE.
23
24 require File.expand_path('../spec_helper', File.dirname(__FILE__))
25
26 describe Walrat::ParserState do
27   before do
28     @base_string = 'this is the string to be parsed'
29     @state = Walrat::ParserState.new @base_string
30   end
31
32   it 'raises an ArgumentError if initialized with nil' do
33     expect do
34       Walrat::ParserState.new nil
35     end.to raise_error(ArgumentError, /nil string/)
36   end
37
38   it 'before parsing has started "remainder" should equal the entire string' do
39     @state.remainder.should == @base_string
40   end
41
42   it 'before parsing has started "remainder" should equal the entire string (when string is an empty string)' do
43     Walrat::ParserState.new('').remainder.should == ''
44   end
45
46   it 'before parsing has started "results" should be empty' do
47     @state.results.should be_empty
48   end
49
50   it '"parsed" should complain if passed nil' do
51     lambda { @state.parsed(nil) }.should raise_error(ArgumentError)
52   end
53
54   it '"skipped" should complain if passed nil' do
55     lambda { @state.skipped(nil) }.should raise_error(ArgumentError)
56   end
57
58   it '"parsed" should return the remainder of the string' do
59     @state.parsed('this is the ').should  == 'string to be parsed'
60     @state.parsed('string ').should       == 'to be parsed'
61     @state.parsed('to be parsed').should  == ''
62   end
63
64   it '"skipped" should return the remainder of the string' do
65     @state.skipped('this is the ').should == 'string to be parsed'
66     @state.skipped('string ').should      == 'to be parsed'
67     @state.skipped('to be parsed').should == ''
68   end
69
70   it '"results" should return an unwrapped parsed result (for single results)' do
71     @state.parsed('this')
72     @state.results.should == 'this'
73   end
74
75   it 'skipped substrings should not appear in "results"' do
76     @state.skipped('this')
77     @state.results.should be_empty
78   end
79
80   it 'should return an array of the parsed results (for multiple results)' do
81     @state.parsed('this ')
82     @state.parsed('is ')
83     @state.results.should == ['this ', 'is ']
84   end
85
86   it 'should work when the entire string is consumed in a single operation (using "parsed")' do
87     @state.parsed(@base_string).should == ''
88     @state.results.should == @base_string
89   end
90
91   it 'should work when the entire string is consumed in a single operation (using "skipped")' do
92     @state.skipped(@base_string).should == ''
93     @state.results.should be_empty
94   end
95
96   it '"parsed" should complain if passed something that doesn\'t respond to the "line_end" and "column_end" messages' do
97     # line_end
98     my_mock = mock('mock_which_does_not_implement_line_end', :null_object => true)
99     my_mock.should_receive(:line_end).and_raise(NoMethodError)
100     lambda { @state.parsed(my_mock) }.should raise_error(NoMethodError)
101
102     # column_end
103     my_mock = mock('mock_which_does_not_implement_column_end', :null_object => true)
104     my_mock.should_receive(:column_end).and_raise(NoMethodError)
105     lambda { @state.parsed(my_mock) }.should raise_error(NoMethodError)
106   end
107
108   it '"skipped" should complain if passed something that doesn\'t respond to the "line_end" and "column_end" messages' do
109     # line_end
110     my_mock = mock('mock_which_does_not_implement_line_end', :null_object => true)
111     my_mock.should_receive(:line_end).and_raise(NoMethodError)
112     lambda { @state.skipped(my_mock) }.should raise_error(NoMethodError)
113
114     # column_end
115     my_mock = mock('mock_which_does_not_implement_column_end', :null_object => true)
116     my_mock.should_receive(:column_end).and_raise(NoMethodError)
117     lambda { @state.skipped(my_mock) }.should raise_error(NoMethodError)
118   end
119
120   it 'should be able to mix use of "parsed" and "skipped" methods' do
121     # first example
122     @state.skipped('this is the ').should  == 'string to be parsed'
123     @state.results.should be_empty
124     @state.parsed('string ').should       == 'to be parsed'
125     @state.results.should == 'string '
126     @state.skipped('to be parsed').should  == ''
127     @state.results.should == 'string '
128
129     # second example (add this test to isolate a bug in another specification)
130     state = Walrat::ParserState.new('foo1...')
131     state.skipped('foo').should == '1...'
132     state.remainder.should == '1...'
133     state.results.should be_empty
134     state.parsed('1').should == '...'
135     state.remainder.should == '...'
136     state.results.should == '1'
137   end
138
139   it '"parsed" and "results" methods should work with multi-byte Unicode strings' do
140     # basic test
141     state = Walrat::ParserState.new('400€, foo')
142     state.remainder.should == '400€, foo'
143     state.parsed('40').should == '0€, foo'
144     state.results.should == '40'
145     state.parsed('0€, ').should == 'foo'
146     state.results.should == ['40', '0€, ']
147     state.parsed('foo').should == ''
148     state.results.should == ['40', '0€, ', 'foo']
149
150     # test with newlines before and after multi-byte chars
151     state = Walrat::ParserState.new("400\n or more €...\nfoo")
152     state.remainder.should == "400\n or more €...\nfoo"
153     state.parsed("400\n or more").should == " €...\nfoo"
154     state.results.should == "400\n or more"
155     state.parsed(' €..').should == ".\nfoo"
156     state.results.should == ["400\n or more", ' €..']
157     state.parsed(".\nfoo").should == ''
158     state.results.should == ["400\n or more", ' €..', ".\nfoo"]
159   end
160
161   it '"skipped" and "results" methods should work with multi-byte Unicode strings' do
162     state = Walrat::ParserState.new('400€, foo')
163     state.remainder.should == '400€, foo'
164     state.skipped('4').should == '00€, foo'
165     state.results.should be_empty
166     state.parsed('0').should == '0€, foo'
167     state.results.should == '0'
168     state.skipped('0€, ').should == 'foo'
169     state.results.should == '0'
170     state.parsed('foo').should == ''
171     state.results.should == ['0', 'foo']
172   end
173 end