llip 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.
Files changed (42) hide show
  1. data/History.txt +4 -0
  2. data/MIT-LICENSE +21 -0
  3. data/Manifest.txt +45 -0
  4. data/README.txt +148 -0
  5. data/Rakefile +66 -0
  6. data/examples/ariteval/ariteval.rb +132 -0
  7. data/examples/ariteval/evaluator.rb +61 -0
  8. data/examples/ariteval/exp.rb +104 -0
  9. data/lib/llip.rb +6 -0
  10. data/lib/llip/abstract_parser.rb +170 -0
  11. data/lib/llip/abstract_scanner.rb +83 -0
  12. data/lib/llip/buffer.rb +35 -0
  13. data/lib/llip/llip_error.rb +43 -0
  14. data/lib/llip/parser.rb +93 -0
  15. data/lib/llip/production_compiler.rb +168 -0
  16. data/lib/llip/production_specification.rb +79 -0
  17. data/lib/llip/recursive_production_compiler.rb +35 -0
  18. data/lib/llip/regexp_abstract_scanner.rb +116 -0
  19. data/lib/llip/regexp_parser.rb +197 -0
  20. data/lib/llip/regexp_scanner.rb +33 -0
  21. data/lib/llip/regexp_specification.rb +210 -0
  22. data/lib/llip/token.rb +47 -0
  23. data/lib/llip/visitable.rb +37 -0
  24. data/spec/ariteval/ariteval_spec.rb +111 -0
  25. data/spec/ariteval/evaluator_spec.rb +106 -0
  26. data/spec/ariteval/exp_spec.rb +232 -0
  27. data/spec/llip/abstract_parser_spec.rb +273 -0
  28. data/spec/llip/abstract_scanner_spec.rb +152 -0
  29. data/spec/llip/buffer_spec.rb +60 -0
  30. data/spec/llip/llip_error_spec.rb +77 -0
  31. data/spec/llip/parser_spec.rb +163 -0
  32. data/spec/llip/production_compiler_spec.rb +271 -0
  33. data/spec/llip/production_specification_spec.rb +75 -0
  34. data/spec/llip/recursive_production_compiler_spec.rb +86 -0
  35. data/spec/llip/regexp_abstract_scanner_spec.rb +320 -0
  36. data/spec/llip/regexp_parser_spec.rb +265 -0
  37. data/spec/llip/regexp_scanner_spec.rb +40 -0
  38. data/spec/llip/regexp_specification_spec.rb +734 -0
  39. data/spec/llip/token_spec.rb +70 -0
  40. data/spec/llip/visitable_spec.rb +38 -0
  41. data/spec/spec_helper.rb +10 -0
  42. metadata +110 -0
@@ -0,0 +1,265 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'regexp_parser'
3
+ require 'regexp_specification'
4
+ require 'token'
5
+
6
+ module RegexpMockScannerBuilder
7
+
8
+ def mock_scanner(*tokens)
9
+ @scanner = mock "Scanner"
10
+ tokens.map! do |t|
11
+ if t =~ /[\.\+\*\|\(\)\\]/
12
+ Token.new(:symbol,t)
13
+ else
14
+ Token.new(:char,t)
15
+ end
16
+ end
17
+ @tokens = tokens
18
+ @tokens << nil unless tokens[-1].nil?
19
+ t = nil
20
+ @scanner.should_receive(:next).exactly(tokens.size).and_return { t = @tokens.shift }
21
+ @scanner.should_receive(:current).any_number_of_times.and_return { t }
22
+ @scanner
23
+ end
24
+
25
+ end
26
+
27
+ describe "A RegexpParser should parse" do
28
+
29
+ include RegexpMockScannerBuilder
30
+
31
+ before(:each) do
32
+ @parser = RegexpParser.new
33
+ @parser.should respond_to(:parse)
34
+ end
35
+
36
+ it "'a'" do
37
+ @scanner = mock_scanner('a')
38
+ regexp = @parser.parse(@scanner)
39
+
40
+ regexp.should_not be_nil
41
+ regexp.should be_kind_of(RegexpSpecification)
42
+ regexp.init.should_not be_nil
43
+ regexp.init.should_not be_final
44
+ regexp.init.should be_kind_of(RegexpSpecification::State)
45
+ regexp.init['a'].should be_kind_of(RegexpSpecification::State)
46
+ regexp.init['a'].final?.should == true
47
+ end
48
+
49
+ it "'abcde'" do
50
+ @scanner = mock_scanner('a','b','c','d','e')
51
+ regexp = @parser.parse(@scanner)
52
+
53
+ lambda { regexp.init['a']['b']['c']['d']['e'].should be_final }.should_not raise_error
54
+ end
55
+
56
+ it "'a.bcde'" do
57
+ @scanner = mock_scanner('a','.','b','c','d','e')
58
+ regexp = @parser.parse(@scanner)
59
+
60
+ lambda { regexp.init['a']['b']['c']['d']['e'].should be_final }.should_not raise_error
61
+ lambda { regexp.init['a']['f']['b']['c']['d']['e'].should be_final }.should_not raise_error
62
+ lambda { regexp.init['a']['f']['e'].should == :error }.should_not raise_error
63
+
64
+ regexp.init['a']['b'].should be_kind_of(RegexpSpecification::State)
65
+ regexp.init['a']['d']['b'].should == regexp.init['a']['b']
66
+ regexp.init['a'].error.should be_kind_of(RegexpSpecification::State)
67
+ end
68
+
69
+ it "'a...bcde'" do
70
+ @scanner = mock_scanner('a','.','.','.','b','c','d','e')
71
+ regexp = @parser.parse(@scanner)
72
+
73
+ regexp.init['a']['b']['c']['d']['e'].should be_final
74
+ regexp.init['a']['f']['b']['c']['d']['e'].should be_final
75
+ regexp.init['a']['f']['g']['b']['c']['d']['e'].should be_final
76
+ regexp.init['a']['f']['g']['h']['i'].should == :error
77
+ regexp.init['a']['f']['g']['h']['b']['c']['d']['e'].should be_final
78
+
79
+ regexp.init['a']['b'].should be_kind_of(RegexpSpecification::State)
80
+ regexp.init['a']['d']['b'].should == regexp.init['a']['b']
81
+ regexp.init['a'].error.should be_kind_of(RegexpSpecification::State)
82
+ regexp.init['a'].error.error.should be_kind_of(RegexpSpecification::State)
83
+ regexp.init['a'].error.error.error.should be_kind_of(RegexpSpecification::State)
84
+ regexp.init['a'].error.error.error['b'].should == regexp.init['a']['b']
85
+ end
86
+
87
+ it "'a\\.b'" do
88
+ @scanner = mock_scanner('a','\\','.','b')
89
+ regexp = @parser.parse(@scanner)
90
+
91
+ regexp.init['a']['.']['b'].should be_final
92
+ end
93
+
94
+ it "'a\\nb" do
95
+ @scanner = mock_scanner('a','\\','n','b')
96
+ regexp = @parser.parse(@scanner)
97
+
98
+ regexp.init['a']["\n"]['b'].should be_final
99
+ end
100
+
101
+ it "ab*c" do
102
+ @scanner = mock_scanner('a','b','*','c')
103
+ regexp = @parser.parse(@scanner)
104
+
105
+ regexp.init['a']['c'].should be_final
106
+ regexp.init['a']['b']['c'].should be_final
107
+ regexp.init['a']['b']['b'].should == regexp.init['a']['b']
108
+ end
109
+
110
+ it "ab+c" do
111
+ @scanner = mock_scanner('a','b','+','c')
112
+ regexp = @parser.parse(@scanner)
113
+
114
+ regexp.init['a']['c'].should == :error
115
+ regexp.init['a']['b']['c'].should be_final
116
+ regexp.init['a']['b']['b'].should == regexp.init['a']['b']
117
+ end
118
+
119
+ it "a.*c" do
120
+ @scanner = mock_scanner('a','.','*','c')
121
+ regexp = @parser.parse(@scanner)
122
+
123
+ regexp.init['a']['c'].should be_final
124
+ regexp.init['a']['e']['c'].should be_final
125
+ regexp.init['a']['f']['e']['c'].should == regexp.init['a']['c']
126
+ regexp.init['a'].error.error.should == regexp.init['a'].error
127
+ end
128
+
129
+ it "ac*" do
130
+ @scanner = mock_scanner('a','c','*')
131
+ regexp = @parser.parse(@scanner)
132
+
133
+ regexp.init['a']['c'].should be_final
134
+ regexp.init['a'].should be_final
135
+ regexp.init['a']['c']['c'].should be_final
136
+ regexp.init['a']['c']['c']['c'].should == regexp.init['a']['c']
137
+ end
138
+
139
+
140
+ it "a.+c" do
141
+ @scanner = mock_scanner('a','.','+','c')
142
+ regexp = @parser.parse(@scanner)
143
+
144
+ regexp.init['a']['c'].should_not be_final
145
+ regexp.init['a']['e']['c'].should be_final
146
+ regexp.init['a']['f']['e']['c'].should == regexp.init['a']['b']['c']
147
+ regexp.init['a'].error.error.error.should == regexp.init['a'].error.error
148
+ end
149
+
150
+ it "a|b|c" do
151
+ @scanner = mock_scanner('a','|','b','|','c')
152
+ regexp = @parser.parse(@scanner)
153
+
154
+ regexp.init.keys.should == ['a','b','c']
155
+ regexp.init.error.should == :error
156
+
157
+ regexp.init['a'].should be_final
158
+ regexp.init['b'].should be_final
159
+ regexp.init['c'].should be_final
160
+ end
161
+
162
+ it "a(b|c)" do
163
+ @scanner = mock_scanner("a","(","b","|","c",")")
164
+ regexp = @parser.parse(@scanner)
165
+
166
+ regexp.last.should == [regexp.init['a']['b'],regexp.init['a']['c']]
167
+
168
+ regexp.init.keys.should == ['a']
169
+ regexp.init['a'].keys.should == ['b','c']
170
+ regexp.init['a']['b'].should be_final
171
+ regexp.init['a']['c'].should be_final
172
+ regexp.init['a']['b'].should == regexp.init['a']['c']
173
+ end
174
+
175
+ it "a(b|c)d" do
176
+ @scanner = mock_scanner("a","(","b","|","c",")",'d')
177
+ regexp = @parser.parse(@scanner)
178
+
179
+ regexp.init['a'].keys.should == ['b','c']
180
+ regexp.init['a']['b']['d'].should be_final
181
+ regexp.init['a']['c']['d'].should be_final
182
+
183
+ regexp.last.should == [regexp.init['a']['b']['d']]
184
+ end
185
+
186
+ it "..(b|c)" do
187
+ @scanner = mock_scanner(".",".","(","b","|","c",")")
188
+ regexp = @parser.parse(@scanner)
189
+
190
+ regexp.last.should == [regexp.init.error.error['b'],regexp.error.error['c']]
191
+
192
+ regexp.init.error.error.keys.should == ['b','c']
193
+ regexp.init['a']['3']['b'].should be_final
194
+ regexp.init['a']['f']['c'].should be_final
195
+ regexp.init['e']['f']['b'].should == regexp.init['a']['h']['c']
196
+
197
+ regexp.last.should == [regexp.init.error.error['b'],regexp.error.error['c']]
198
+ end
199
+
200
+ it "a(b|ce)*d" do
201
+ @scanner = mock_scanner("a","(","b","|","c","e",")","*","d")
202
+ regexp = @parser.parse(@scanner)
203
+
204
+ regexp.init['a'].keys.should == ['b','c','d']
205
+
206
+ regexp['a']['b'].keys.should == ['b','c','d']
207
+ regexp['a']['c'].keys.should == ['e']
208
+ regexp['a']['c']['e'].keys.should == ['b','c','d']
209
+
210
+ regexp.init['a']['b']['d'].should be_final
211
+ regexp.init['a']['c']['e']['d'].should be_final
212
+ regexp.init['a']['d'].should be_final
213
+
214
+ regexp.init['a'].should == regexp.init['a']['b']
215
+ regexp.init['a']['b']['b'].should == regexp.init['a']['b']
216
+ regexp.init['a'].should == regexp.init['a']['c']['e']
217
+ regexp.init['a']['d'].should == regexp.init['a']['b']['d']
218
+
219
+ regexp['a']['b'].final?.should == false
220
+ regexp.init['a']['c'].final?.should == false
221
+ regexp.init['a']['c']['e'].final?.should == false
222
+ end
223
+
224
+ it "a(b|ce)+d" do
225
+ @scanner = mock_scanner("a","(","b","|","c","e",")","+","d")
226
+ regexp = @parser.parse(@scanner)
227
+
228
+ regexp.init['a'].keys.should == ['b','c']
229
+
230
+ regexp['a']['b'].keys.should == ['b','c','d']
231
+ regexp['a']['c'].keys.should == ['e']
232
+ regexp['a']['c']['e'].keys.should == ['b','c','d']
233
+
234
+ regexp.init['a']['b']['d'].should be_final
235
+ regexp.init['a']['c']['e']['d'].should be_final
236
+
237
+ regexp.init['a'].should_not == regexp.init['a']['b']
238
+ regexp.init['a'].should_not == regexp.init['a']['c']['e']
239
+
240
+ regexp.init['a']['b']['b'].should == regexp.init['a']['b']
241
+ regexp.init['a']['c']['e'].should == regexp.init['a']['b']
242
+ regexp.init['a']['c']['e']['c']['e'].should == regexp['a']['c']['e']
243
+
244
+ regexp['a']['b'].final?.should == false
245
+ regexp.init['a']['c'].final?.should == false
246
+ regexp.init['a']['c']['e'].final?.should == false
247
+ end
248
+
249
+ end
250
+
251
+ describe "A RegexpParser should not parse" do
252
+
253
+ include RegexpMockScannerBuilder
254
+
255
+ before(:each) do
256
+ @parser = RegexpParser.new
257
+ @parser.should respond_to(:parse)
258
+ end
259
+
260
+ it "a(cdef" do
261
+ @scanner = mock_scanner("a","(","b","c","d","e","f")
262
+ lambda { @parser.parse(@scanner) }.should raise_error(RuntimeError)
263
+ end
264
+
265
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'regexp_scanner'
3
+ require 'token'
4
+ require 'stringio'
5
+
6
+ describe "A RegexpScanner should scan" do
7
+
8
+ before(:each) do
9
+ @scanner = RegexpScanner.new
10
+ end
11
+
12
+ it "'abc'" do
13
+ lambda { @scanner.scan('abc') }.should_not raise_error
14
+ token = @scanner.next
15
+ token.should == 'a'
16
+ token.should == :char
17
+ @scanner.next.should == 'b'
18
+ @scanner.next.should == 'c'
19
+ end
20
+
21
+ it "'.*abc'"do
22
+ @scanner.scan('.*abc(d|e)+\\.')
23
+ token = @scanner.next
24
+ token.should == '.'
25
+ token.should == :symbol
26
+ @scanner.next.should == :symbol
27
+ @scanner.next.should == 'a'
28
+ @scanner.next.should == 'b'
29
+ @scanner.next.should == 'c'
30
+ @scanner.next.should == :symbol
31
+ @scanner.next.should == 'd'
32
+ @scanner.next.should == :symbol
33
+ @scanner.next.should == 'e'
34
+ @scanner.next.should == :symbol
35
+ @scanner.next.should == :symbol
36
+ @scanner.next.should == :symbol
37
+ @scanner.next.should == :symbol
38
+ @scanner.next.should be_nil
39
+ end
40
+ end
@@ -0,0 +1,734 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'regexp_specification'
3
+
4
+ describe "A RegexpSpecification" do
5
+
6
+ before(:each) do
7
+ @instance = RegexpSpecification.new(:a_name)
8
+ end
9
+
10
+ it "should store the initialization parameter in the attribute name" do
11
+ @instance.name.should == :a_name
12
+ end
13
+
14
+ it "should have an read-write attribute name and its default should be nil" do
15
+ @instance.should respond_to(:name)
16
+ @instance.should respond_to(:name=)
17
+
18
+ @instance.name= :pippo
19
+ @instance.name.should == :pippo
20
+
21
+ lambda { @instance = RegexpSpecification.new }.should_not raise_error
22
+ @instance.name.should be_nil
23
+ end
24
+
25
+ it "should send :to_sym to the initialization parameter" do
26
+ m = mock("symbol")
27
+ m.should_receive(:to_sym).and_return(:a_sym)
28
+ @instance = RegexpSpecification.new(m)
29
+ @instance.name.should == :a_sym
30
+ end
31
+
32
+ it "should have a states attribute which must be an hash" do
33
+ @instance.should respond_to(:states)
34
+ @instance.states.should == {}
35
+ end
36
+
37
+ it "should redirect its (:[],:[]=,:keys,:values,:each,:error,:error=,:final?,:final=) methods to the #init ones" do
38
+ m = mock("State")
39
+ m.should_receive(:name).and_return(:a_name)
40
+ m.should_receive(:kind_of?).with(RegexpSpecification::State).and_return(true)
41
+ m.should_receive(:[]).with(:a).and_return(:uh)
42
+ m.should_receive(:[]=).with(:a,:buh).and_return(:buh)
43
+ m.should_receive(:keys)
44
+ m.should_receive(:values)
45
+ m.should_receive(:each)
46
+ m.should_receive(:error)
47
+ m.should_receive(:error=)
48
+ m.should_receive(:final=)
49
+ m.should_receive(:final?)
50
+ m.should_receive(:regexp=).with(@instance)
51
+ @instance.add_state(m)
52
+
53
+ @instance[:a].should == :uh
54
+ (@instance[:a] = :buh).should == :buh
55
+
56
+ lambda {
57
+ @instance.keys
58
+ @instance.values
59
+ @instance.each
60
+ @instance.error
61
+ @instance.error=
62
+ @instance.final=
63
+ @instance.final?
64
+ }.should_not raise_error
65
+
66
+ 2+2 # stupid hack because of stupid rcov
67
+ end
68
+
69
+ it "should have an :add_state method which add the value in the hash with a key that correspond at the value's name." do
70
+ m = mock("State")
71
+ m.should_receive(:name).and_return(1)
72
+ m.should_receive(:kind_of?).with(RegexpSpecification::State).and_return(false)
73
+ m.should_receive(:kind_of?).with(RegexpSpecification).and_return(true)
74
+ m.should_receive(:regexp=).with(@instance)
75
+ @instance.should respond_to(:add_state)
76
+ @instance.add_state(m).should == m
77
+ @instance.states[1].should == m
78
+ end
79
+
80
+ it "should have an :add_state method which create a new State based on the hash passed as a parameter" do
81
+ m = mock("hash")
82
+ m.should_receive(:[]).with(:final).and_return(true)
83
+ m.should_receive(:[]).with(:error).and_return(:self)
84
+
85
+ result = @instance.add_state(m)
86
+ result.should be_kind_of(RegexpSpecification::State)
87
+ result.regexp.should == @instance
88
+
89
+ lambda { @instance.add_state.should be_kind_of(RegexpSpecification::State) }.should_not raise_error
90
+ end
91
+
92
+ it "should have a :starting_chars method which returns the init State keys" do
93
+ @instance.should respond_to(:starting_chars)
94
+ @instance.starting_chars.should == []
95
+
96
+ @instance.add_state["a"] = :try
97
+ @instance.starting_chars.should == ["a"]
98
+ end
99
+
100
+ it "should have a :starting_chars method which returns :everything if the init State has an error which isn't a State" do
101
+ @instance.add_state.error = @instance.add_state
102
+ @instance.starting_chars.should == :everything
103
+ end
104
+ end
105
+
106
+ describe "A RegexpSpecificatio should have a :last method which is able to discover the last State of" do
107
+
108
+ before(:each) do
109
+ @instance = RegexpSpecification.new
110
+ @instance.should respond_to(:last)
111
+ @instance.last.should == []
112
+ end
113
+
114
+ it "'abc'" do
115
+ @a = @instance.add_state
116
+ @b = @instance.add_state
117
+ @c = @instance.add_state
118
+ @d = @instance.add_state(:final => true)
119
+ @a['a'] = @b
120
+ @b['b'] = @c
121
+ @c['c'] = @d
122
+
123
+ @instance.last.should == [@d]
124
+ end
125
+
126
+ it "'a(b|cd)*'" do
127
+ @a = @instance.add_state
128
+
129
+ @b = @instance.add_state(:final => true)
130
+
131
+ @c = @instance.add_state
132
+
133
+ @a['a'] = @b
134
+ @b['b'] = @b
135
+ @b['c'] = @c
136
+ @c['d'] = @b
137
+
138
+ @instance.last.should == [@b]
139
+ end
140
+
141
+ it "'a(b|ce)*d'" do
142
+ @a = @instance.add_state
143
+ @b = @instance.add_state
144
+ @c = @instance.add_state
145
+ @d = @instance.add_state
146
+ @e = @instance.add_state
147
+ @g = @instance.add_state(:final => true)
148
+
149
+ @a['a'] = @b
150
+
151
+ @b['b'] = @d
152
+ @b['c'] = @c
153
+ @b['d'] = @g
154
+
155
+ @d['b'] = @d
156
+ @d['c'] = @c
157
+ @d['d'] = @g
158
+
159
+ @c['e'] = @e
160
+
161
+ @e['b'] = @d
162
+ @e['c'] = @c
163
+ @e['d'] = @g
164
+
165
+ @instance.last.should == [@g]
166
+ end
167
+
168
+ it "'a(b|cd)'" do
169
+ @a = @instance.add_state
170
+ @b = @instance.add_state
171
+ @c = @instance.add_state
172
+ @d = @instance.add_state(:final => true)
173
+
174
+ @a['a'] = @b
175
+ @b['b'] = @d
176
+ @b['c'] = @c
177
+ @c['d'] = @d
178
+
179
+ @instance.last.should == [@d]
180
+ end
181
+
182
+ it "'a(.+|cd)b" do
183
+ @a = @instance.add_state
184
+ @f = @instance.add_state(:error => :self)
185
+ @b = @instance.add_state(:error => @f)
186
+ @c = @instance.add_state
187
+ @d = @instance.add_state
188
+ @e = @instance.add_state(:final => true)
189
+
190
+ @a['a'] = @b
191
+ @b['c'] = @c
192
+ @c['d'] = @d
193
+ @d['b'] = @e
194
+ @f['b'] = @e
195
+
196
+ @instance.last.should == [@e]
197
+ end
198
+
199
+ it "'a.b'" do
200
+ @a = @instance.add_state
201
+ @c = @instance.add_state
202
+ @b = @instance.add_state(:error => @c)
203
+ @d = @instance.add_state(:final => true)
204
+
205
+ @a['a'] = @b
206
+ @c['b'] = @d
207
+
208
+ @instance.last.should == [@d]
209
+ end
210
+
211
+ it "'a.*b'" do
212
+ @a = @instance.add_state
213
+ @c = @instance.add_state(:error => :self)
214
+ @b = @instance.add_state(:error => @c)
215
+ @d = @instance.add_state(:final => true)
216
+
217
+ @a['a'] = @b
218
+ @b['b'] = @d
219
+ @c['b'] = @d
220
+
221
+ @instance.last.should == [@d]
222
+ end
223
+
224
+ it "'ac*'" do
225
+ @a = @instance.add_state
226
+ @b = @instance.add_state(:final => true)
227
+
228
+ @a['c'] = @b
229
+ @b['c'] = @b
230
+
231
+ @instance.last.should == [@b]
232
+ end
233
+
234
+ it "'a.'" do
235
+ @a = @instance.add_state
236
+ @c = @instance.add_state(:final => true)
237
+ @b = @instance.add_state(:error => @c)
238
+
239
+ @a['a'] = @b
240
+
241
+ @instance.last.should == [@c]
242
+ end
243
+
244
+ it "'.a'" do
245
+ @a = @instance.add_state
246
+ @b = @instance.add_state
247
+ @c = @instance.add_state(:final => true)
248
+ @a.error = @b
249
+ @b['a'] = @c
250
+
251
+ @instance.last.should == [@c]
252
+ end
253
+
254
+ it "'..a'" do
255
+ @a = @instance.add_state
256
+ @b = @instance.add_state
257
+ @c = @instance.add_state
258
+ @d = @instance.add_state(:final => true)
259
+ @a.error = @b
260
+ @b.error = @c
261
+ @c['a'] = @d
262
+
263
+ @instance.last.should == [@d]
264
+ end
265
+
266
+ end
267
+
268
+ describe "A RegexpSpecification::State" do
269
+
270
+ before(:each) do
271
+ @instance = RegexpSpecification::State.new
272
+ end
273
+
274
+ it "should be a kind of hash" do
275
+ @instance.should be_kind_of(Hash)
276
+ end
277
+
278
+ it "should return :error if it's requested an unknown key" do
279
+ @instance[:unknown_key].should == :error
280
+ end
281
+
282
+ it "should admit to specify the error code" do
283
+ m = mock("sym")
284
+ m.should_receive(:to_sym).and_return(:a_different_error_code)
285
+ m.should_receive(:respond_to?).with(:to_sym).and_return(true)
286
+ @instance = RegexpSpecification::State.new(:error => m)
287
+ @instance[:unknown_key].should == :a_different_error_code
288
+ end
289
+
290
+ it "should set the error code to its name if the error code specified is :self" do
291
+ @instance = RegexpSpecification::State.new(:error => :self)
292
+ @instance[:unknown_key].should == @instance
293
+ end
294
+
295
+ it "should have a name which is a Numeric identifier" do
296
+ @instance.should respond_to(:name)
297
+ @instance.name.should_not be_nil
298
+ @instance.name.should be_kind_of(Numeric)
299
+
300
+ second = RegexpSpecification::State.new
301
+ second.name.should > @instance.name
302
+ end
303
+
304
+ it "should have a :final? method which is initialized by an hash-like initialization argument" do
305
+ @instance.should respond_to(:final?)
306
+ @instance.final?.should == false
307
+
308
+ lambda { @instance = RegexpSpecification::State.new(:final => true) }.should_not raise_error
309
+ @instance.final?.should == true
310
+ end
311
+
312
+ it "should have a :final= method which sets :final" do
313
+ @instance.should respond_to(:final=)
314
+ @instance.final= true
315
+ @instance.should be_final
316
+ end
317
+
318
+ it "should have as hash the hash of its name" do
319
+ @instance.hash.should == @instance.name.hash
320
+ end
321
+
322
+ it "should allow to set the error code through :error=" do
323
+ @instance.should respond_to(:error)
324
+ @instance.should respond_to(:error=)
325
+ @instance.error.should == @instance.default
326
+ @instance.error= :ghgh
327
+ @instance.error.should == :ghgh
328
+ @instance.error.should == @instance.default
329
+ @instance["huhu"].should == :ghgh
330
+ end
331
+
332
+ it "should not be equal to another State with same content but different error" do
333
+ @another = RegexpSpecification::State.new
334
+ @instance.error = RegexpSpecification::State.new
335
+
336
+ @instance.should_not == @another
337
+
338
+ @another.error = RegexpSpecification::State.new
339
+
340
+ @instance.should == @another
341
+
342
+ @another.error['p'] = :pluto
343
+
344
+ @instance.should_not == @another
345
+
346
+ @another.error = @instance.error
347
+ @instance.should == @another
348
+ end
349
+
350
+ it "should memorize its regexp internally" do
351
+ @instance.should respond_to(:regexp)
352
+ @instance.should respond_to(:regexp=)
353
+ @instance.regexp.should be_nil
354
+ @instance.regexp = :regexp
355
+ @instance.regexp.should == :regexp
356
+ end
357
+ end
358
+
359
+ describe "A RegexpSpecification should be able to represent" do
360
+
361
+ before(:each) do
362
+ @instance = RegexpSpecification.new(:a_regexp)
363
+ @state = RegexpSpecification::State
364
+ end
365
+
366
+ it "'abc'" do
367
+ @a = @instance.add_state
368
+ @b = @instance.add_state
369
+ @c = @instance.add_state
370
+ @d = @instance.add_state(:final => true)
371
+ @a['a'] = @b
372
+ @b['b'] = @c
373
+ @c['c'] = @d
374
+
375
+ scan(["a","b","c"]).should == true
376
+ scan(["a","b","c","d"]).should == false
377
+ end
378
+
379
+ it "'a.b'" do
380
+ @a = @instance.add_state
381
+ @c = @instance.add_state
382
+ @b = @instance.add_state(:error => @c)
383
+ @d = @instance.add_state(:final => true)
384
+
385
+ @a['a'] = @b
386
+ @c['b'] = @d
387
+
388
+ scan(["a","b"]).should == false
389
+ scan(["a","c","b"]).should == true
390
+ scan(["a","c","d"]).should == false
391
+ scan(["a","c","d","b"]).should == false
392
+ end
393
+
394
+ it "'a.+b'" do
395
+ @a = @instance.add_state
396
+ @c = @instance.add_state(:error => :self)
397
+ @b = @instance.add_state(:error => @c)
398
+ @d = @instance.add_state(:final => true)
399
+
400
+ @a['a'] = @b
401
+ @c['b'] = @d
402
+
403
+ scan(["a","b"]).should == false
404
+ scan(["a","c","b"]).should == true
405
+ scan(["a","c","d"]).should == false
406
+ scan(["a","c","d","b"]).should == true
407
+ end
408
+
409
+ it "'a.*b'" do
410
+ @a = @instance.add_state
411
+ @c = @instance.add_state(:error => :self)
412
+ @b = @instance.add_state(:error => @c)
413
+ @d = @instance.add_state(:final => true)
414
+
415
+ @a['a'] = @b
416
+ @b['b'] = @d
417
+ @c['b'] = @d
418
+
419
+ scan(["a","b"]).should == true
420
+ scan(["a","c","b"]).should == true
421
+ scan(["a","c","d"]).should == false
422
+ scan(["a","c","d","b"]).should == true
423
+ end
424
+
425
+ it "'a(b|cd)'" do
426
+ @a = @instance.add_state
427
+ @b = @instance.add_state
428
+ @c = @instance.add_state
429
+ @d = @instance.add_state(:final => true)
430
+
431
+ @a['a'] = @b
432
+ @b['b'] = @d
433
+ @b['c'] = @c
434
+ @c['d'] = @d
435
+
436
+ scan(["a","b"]).should == true
437
+ scan(["a","c","d"]).should == true
438
+ scan(["a","b","c","d"]).should == false
439
+ end
440
+
441
+ it "'a(b|cd)*'" do
442
+ @a = @instance.add_state
443
+
444
+ @b = @instance.add_state(:final => true)
445
+
446
+ @c = @instance.add_state
447
+
448
+ @a['a'] = @b
449
+ @b['b'] = @b
450
+ @b['c'] = @c
451
+ @c['d'] = @b
452
+
453
+
454
+ scan(["a"]).should == true
455
+ scan(["a","b"]).should == true
456
+ scan(["a","c","d"]).should == true
457
+ scan(["a","b","c","d"]).should == true
458
+ scan(["a","b","c","e"]).should == false
459
+ scan(["a","b","c","d","b"]).should == true
460
+ end
461
+
462
+ it "'a(b|cd)+'" do
463
+ @a = @instance.add_state
464
+ @b = @instance.add_state
465
+ @c = @instance.add_state
466
+ @d = @instance.add_state(:final => true)
467
+
468
+ @e = @instance.add_state
469
+ @f = @instance.add_state
470
+
471
+ @a['a'] = @b
472
+ @b['b'] = @d
473
+ @b['c'] = @c
474
+ @c['d'] = @d
475
+
476
+ @d['b'] = @d
477
+ @d['c'] = @e
478
+ @e['d'] = @d
479
+
480
+ scan(["a","b"]).should == true
481
+ scan(["a","c","d"]).should == true
482
+ scan(["a","b","c","d"]).should == true
483
+ scan(["a","b","c","d","b"]).should == true
484
+ end
485
+
486
+ it "'a(.+|cd)b" do
487
+ @a = @instance.add_state
488
+ @f = @instance.add_state(:error => :self)
489
+ @b = @instance.add_state(:error => @f)
490
+ @c = @instance.add_state
491
+ @d = @instance.add_state
492
+ @e = @instance.add_state(:final => true)
493
+
494
+ @a['a'] = @b
495
+ @b['c'] = @c
496
+ @c['d'] = @d
497
+ @d['b'] = @e
498
+ @f['b'] = @e
499
+
500
+
501
+ scan(["a","c","d","b"]).should == true
502
+ scan(["a","b","b"]).should == true
503
+ scan(["a","e","f","b"]).should == true
504
+ end
505
+
506
+ it "'a(b|ce)*d'" do
507
+ @a = @instance.add_state
508
+ @b = @instance.add_state
509
+ @c = @instance.add_state
510
+ @d = @instance.add_state
511
+ @e = @instance.add_state
512
+ @g = @instance.add_state(:final => true)
513
+
514
+ @a['a'] = @b
515
+
516
+ @b['b'] = @d
517
+ @b['c'] = @c
518
+ @b['d'] = @g
519
+
520
+ @d['b'] = @d
521
+ @d['c'] = @c
522
+ @d['d'] = @g
523
+
524
+ @c['e'] = @e
525
+
526
+ @e['b'] = @d
527
+ @e['c'] = @c
528
+ @e['d'] = @g
529
+
530
+ scan("a","d").should == true
531
+ scan("a",'b',"d").should == true
532
+ scan("a","b","b","d").should == true
533
+ scan("a","b","b","b","c","e","b","d").should == true
534
+ scan("a","b","c","e","b","c","e","b","d").should == true
535
+ scan("a","b","c","e","b","c","e","b").should == false
536
+ end
537
+
538
+ def scan(*chars)
539
+ chars.flatten!
540
+ next_state = @instance.init
541
+ chars.each do |c|
542
+ next_state = next_state[c]
543
+ return false if next_state == :error
544
+ end
545
+ next_state.final?
546
+ end
547
+ end
548
+
549
+ describe "The :mix method of the RegexpSpecification class" do
550
+
551
+ before(:each) do
552
+ RegexpSpecification.should respond_to(:mix)
553
+ end
554
+
555
+ it "should be able to mix: 'ab' and 'ac'" do
556
+ r1 = RegexpSpecification.new("first")
557
+ r2 = RegexpSpecification.new("second")
558
+
559
+ s1 = r1.add_state
560
+ s2 = r1.add_state
561
+ s3 = r1.add_state(:final => true)
562
+ s1['a'] = s2
563
+ s2['b'] = s3
564
+
565
+ s4 = r2.add_state
566
+ s5 = r2.add_state
567
+ s6 = r2.add_state(:final => true)
568
+ s4['a'] = s5
569
+ s5['c'] = s6
570
+
571
+ result = RegexpSpecification.mix(r1,r2)
572
+ result.name.should == :"mix between 'first' and 'second'"
573
+ second = result['a']
574
+ second.should_not == :error
575
+ second['b'].should == s3
576
+ second['c'].should == s6
577
+ end
578
+
579
+ it "should be able to mix: '.b' and '.c'" do
580
+ r1 = RegexpSpecification.new("first")
581
+ r2 = RegexpSpecification.new("second")
582
+
583
+ s1 = r1.add_state
584
+ s2 = r1.add_state
585
+ s3 = r1.add_state(:final => true)
586
+ s1.error = s2
587
+ s2['b'] = s3
588
+
589
+ s4 = r2.add_state
590
+ s5 = r2.add_state
591
+ s6 = r2.add_state(:final => true)
592
+ s4.error = s5
593
+ s5['c'] = s6
594
+
595
+ result = RegexpSpecification.mix(r1,r2)
596
+ result.name.should == :"mix between 'first' and 'second'"
597
+ second = result.init.error
598
+ second.should_not == :error
599
+ second['b'].should == s3
600
+ second['c'].should == s6
601
+ end
602
+
603
+ it "should be able to mix: 'a.b' and 'ac'" do
604
+ r1 = RegexpSpecification.new("first")
605
+ r2 = RegexpSpecification.new("second")
606
+
607
+ s1 = r1.add_state
608
+ s2 = r1.add_state
609
+ s3 = r1.add_state
610
+ s4 = r1.add_state(:final => true)
611
+ s1['a'] = s2
612
+ s2.error = s3
613
+ s3['b'] = s4
614
+
615
+ s4 = r2.add_state
616
+ s5 = r2.add_state
617
+ s6 = r2.add_state(:final => true)
618
+ s4['a'] = s5
619
+ s5['c'] = s6
620
+
621
+ result = RegexpSpecification.mix(r1,r2)
622
+ result.name.should == :"mix between 'first' and 'second'"
623
+ second = result.init
624
+ second.should_not == :error
625
+ second['a'].should_not == :error
626
+ second['a']['c'].should == s6
627
+ second['a'].error.should_not == :error
628
+ second['a'].error.should == s3
629
+
630
+ result = RegexpSpecification.mix(r2,r1)
631
+ result.name.should == :"mix between 'second' and 'first'"
632
+ second = result.init
633
+ second.should_not == :error
634
+ second['a'].should_not == :error
635
+ second['a']['c'].should == s6
636
+ second['a'].error.should_not == :error
637
+ second['a'].error.should == s3
638
+ end
639
+
640
+ it "should raise if trying to mix: 'ab' and 'abc'" do
641
+ r1 = RegexpSpecification.new("first")
642
+ r2 = RegexpSpecification.new("second")
643
+
644
+ s1 = r1.add_state
645
+ s2 = r1.add_state
646
+ s3 = r1.add_state(:final => true)
647
+ s1['a'] = s2
648
+ s2['b'] = s3
649
+
650
+ s4 = r2.add_state
651
+ s5 = r2.add_state
652
+ s6 = r2.add_state
653
+ s7 = r2.add_state(:final => true)
654
+ s4['a'] = s5
655
+ s5['b'] = s6
656
+ s6['c'] = s7
657
+
658
+ lambda { RegexpSpecification.mix(r1,r2) }.should raise_error(RuntimeError)
659
+ end
660
+
661
+ it "should be able to mix: '(a|b)c' and '(b|e)d'" do
662
+ r1 = RegexpSpecification.new("first")
663
+ r2 = RegexpSpecification.new("second")
664
+
665
+ s1 = r1.add_state
666
+ s2 = r1.add_state
667
+ s3 = r1.add_state(:final => true)
668
+ s4 = r1.add_state
669
+
670
+ s1['a'] = s2
671
+ s2['c'] = s3
672
+ s1['b'] = s4
673
+ s4['c'] = s3
674
+
675
+ s5 = r2.add_state
676
+ s6 = r2.add_state
677
+ s7 = r2.add_state(:final => true)
678
+ s8 = r2.add_state
679
+
680
+ s5['e'] = s6
681
+ s6['d'] = s7
682
+ s5['b'] = s8
683
+ s8['d'] = s7
684
+
685
+ result = RegexpSpecification.mix(r1,r2)
686
+ result.name.should == :"mix between 'first' and 'second'"
687
+ result['a'].should == s2
688
+ result['e'].should == s6
689
+ result['b'].should_not == :error
690
+ result['b']['c'].should == s3
691
+ result['b']['d'].should == s7
692
+ end
693
+
694
+ it "should be able to mix: '(a|b)+c' and '(b|e)+d'" do
695
+ r1 = RegexpSpecification.new("first")
696
+ r2 = RegexpSpecification.new("second")
697
+
698
+ s1 = r1.add_state
699
+ s2 = r1.add_state
700
+ s3 = r1.add_state(:final => true)
701
+
702
+ s1['a'] = s2
703
+ s1['b'] = s2
704
+ s2['a'] = s2
705
+ s2['b'] = s2
706
+ s2['c'] = s3
707
+
708
+ s4 = r2.add_state
709
+ s5 = r2.add_state
710
+ s6 = r2.add_state(:final => true)
711
+
712
+ s4['e'] = s5
713
+ s4['b'] = s5
714
+ s5['e'] = s5
715
+ s5['b'] = s5
716
+ s5['d'] = s6
717
+
718
+ result = RegexpSpecification.mix(r1,r2)
719
+ result.name.should == :"mix between 'first' and 'second'"
720
+ result['a'].should == s2
721
+ result['e'].should == s5
722
+ result['b'].should_not == :error
723
+ result['b'].should_not == s2
724
+ result['b'].should_not == s5
725
+ result['b']['b'].should_not == :error
726
+ result['b']['b'].should == result['b']['b']['b']
727
+ result['b']['a'].should == s2
728
+ result['b']['e'].should == s5
729
+ result['b']['b'].should_not == s2
730
+ result['b']['b'].should_not == s5
731
+ result['b']['b']['a'].should == s2
732
+ result['b']['b']['e'].should == s5
733
+ end
734
+ end