sequitur 0.1.13 → 0.1.14
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.
- checksums.yaml +8 -8
- data/.rubocop.yml +5 -4
- data/CHANGELOG.md +7 -2
- data/Rakefile +4 -6
- data/lib/sequitur.rb +0 -1
- data/lib/sequitur/constants.rb +1 -1
- data/lib/sequitur/digram.rb +0 -3
- data/lib/sequitur/dynamic_grammar.rb +0 -3
- data/lib/sequitur/formatter/base_formatter.rb +0 -3
- data/lib/sequitur/formatter/base_text.rb +0 -3
- data/lib/sequitur/formatter/debug.rb +0 -3
- data/lib/sequitur/grammar_visitor.rb +0 -4
- data/lib/sequitur/production.rb +0 -5
- data/lib/sequitur/production_ref.rb +0 -4
- data/lib/sequitur/sequitur_grammar.rb +0 -4
- data/lib/sequitur/symbol_sequence.rb +6 -8
- data/spec/sequitur/digram_spec.rb +0 -8
- data/spec/sequitur/dynamic_grammar_spec.rb +144 -159
- data/spec/sequitur/formatter/base_text_spec.rb +92 -95
- data/spec/sequitur/formatter/debug_spec.rb +111 -114
- data/spec/sequitur/grammar_visitor_spec.rb +80 -88
- data/spec/sequitur/production_ref_spec.rb +102 -111
- data/spec/sequitur/production_spec.rb +361 -376
- data/spec/sequitur/sequitur_grammar_spec.rb +1 -5
- data/spec/sequitur/symbol_sequence_spec.rb +115 -124
- metadata +2 -2
@@ -1,111 +1,102 @@
|
|
1
|
-
require_relative '../spec_helper'
|
2
|
-
|
3
|
-
# Load the class under test
|
4
|
-
require_relative '../../lib/sequitur/production'
|
5
|
-
require_relative '../../lib/sequitur/production_ref'
|
6
|
-
|
7
|
-
module Sequitur # Re-open the module to get rid of qualified names
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
expect(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
expect(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
expect(
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
expect(target.refcount).to eq(0)
|
52
|
-
|
53
|
-
expect(subject).
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
expect(target
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
expect
|
64
|
-
expect(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
subject.bind_to(
|
69
|
-
|
70
|
-
expect(
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'should
|
74
|
-
|
75
|
-
|
76
|
-
expect
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
expect(subject).to eq(
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
end # context
|
106
|
-
|
107
|
-
end # describe
|
108
|
-
|
109
|
-
end # module
|
110
|
-
|
111
|
-
# End of file
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../lib/sequitur/production'
|
5
|
+
require_relative '../../lib/sequitur/production_ref'
|
6
|
+
|
7
|
+
module Sequitur # Re-open the module to get rid of qualified names
|
8
|
+
describe ProductionRef do
|
9
|
+
let(:target) { Production.new }
|
10
|
+
let(:another_target) { Production.new }
|
11
|
+
|
12
|
+
subject { ProductionRef.new(target) }
|
13
|
+
|
14
|
+
context 'Creation & initialization:' do
|
15
|
+
it 'should be created with a production argument' do
|
16
|
+
expect { ProductionRef.new(target) }.not_to raise_error
|
17
|
+
expect(target.refcount).to eq(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should clone with reference count incrementing' do
|
21
|
+
expect(target.refcount).to eq(0)
|
22
|
+
expect(subject.production.refcount).to eq(1)
|
23
|
+
klone = subject.clone
|
24
|
+
expect(klone.production.refcount).to eq(2)
|
25
|
+
duplicate = subject.dup
|
26
|
+
expect(duplicate.production.refcount).to eq(3)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should know its referenced production' do
|
30
|
+
instance = ProductionRef.new(target)
|
31
|
+
expect(instance.production).to eq(target)
|
32
|
+
end
|
33
|
+
end # context
|
34
|
+
|
35
|
+
context 'Provided services:' do
|
36
|
+
it 'should render its referenced production' do
|
37
|
+
expect(subject.to_s).to eq(target.object_id.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should unbind itself from its production' do
|
41
|
+
expect(target.refcount).to eq(0)
|
42
|
+
expect(subject).not_to be_unbound
|
43
|
+
expect(target.refcount).to eq(1)
|
44
|
+
subject.unbind
|
45
|
+
expect(target.refcount).to eq(0)
|
46
|
+
expect(subject.production).to be_nil
|
47
|
+
expect(subject).to be_unbound
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should bind to a production' do
|
51
|
+
expect(target.refcount).to eq(0)
|
52
|
+
|
53
|
+
expect(subject).not_to be_unbound
|
54
|
+
expect(target.refcount).to eq(1)
|
55
|
+
|
56
|
+
# Case: bind again to same production
|
57
|
+
expect { subject.bind_to(target) }.not_to raise_error
|
58
|
+
expect(target.refcount).to eq(1)
|
59
|
+
|
60
|
+
# Case: bind to another production
|
61
|
+
expect(another_target.refcount).to eq(0)
|
62
|
+
subject.bind_to(another_target)
|
63
|
+
expect(target.refcount).to eq(0)
|
64
|
+
expect(another_target.refcount).to eq(1)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should complain when binding to something else than production' do
|
68
|
+
subject.bind_to(target)
|
69
|
+
msg = 'Illegal production type String'
|
70
|
+
expect { subject.bind_to('WRONG') }.to raise_error(StandardError, msg)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should compare to other production (reference)' do
|
74
|
+
same = ProductionRef.new(target)
|
75
|
+
expect(subject).to eq(subject) # Strict identity
|
76
|
+
expect(subject).to eq(same) # 2 references pointing to same production
|
77
|
+
expect(subject).to eq(target)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should return the hash value of its production' do
|
81
|
+
expectation = target.hash
|
82
|
+
expect(subject.hash).to eq(expectation)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should complain when requested for a hash and unbound' do
|
86
|
+
subject.unbind
|
87
|
+
expect { subject.hash }.to raise_error(StandardError)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should accept a visitor' do
|
91
|
+
# Use a mock visitor
|
92
|
+
fake = double('fake_visitor')
|
93
|
+
|
94
|
+
# Visitor should receive a visit message
|
95
|
+
expect(fake).to receive(:visit_prod_ref).once
|
96
|
+
expect { subject.accept(fake) }.not_to raise_error
|
97
|
+
end
|
98
|
+
end # context
|
99
|
+
end # describe
|
100
|
+
end # module
|
101
|
+
|
102
|
+
# End of file
|
@@ -1,376 +1,361 @@
|
|
1
|
-
require_relative '../spec_helper'
|
2
|
-
|
3
|
-
# Load the class under test
|
4
|
-
require_relative '../../lib/sequitur/production'
|
5
|
-
|
6
|
-
module Sequitur # Re-open the module to get rid of qualified names
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
instance
|
17
|
-
instance
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
instance
|
23
|
-
instance.append_symbol('
|
24
|
-
instance
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
expect(subject.
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# Case
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
subject.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
expect(
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
expect(
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
#
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
expect(
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
expect(subject.repeated_digram?).to be_falsey
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'should
|
199
|
-
subject.append_symbol(
|
200
|
-
expect(subject.repeated_digram?).to
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
#
|
213
|
-
cases = %w(
|
214
|
-
cases.each do |word|
|
215
|
-
instance = Production.new
|
216
|
-
word.each_char { |symb| instance.append_symbol(symb) }
|
217
|
-
expect(instance.repeated_digram?).to
|
218
|
-
end
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
subject.reduce_step(p_bc)
|
234
|
-
|
235
|
-
expect(
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
expect(
|
247
|
-
expect(
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
subject.
|
254
|
-
|
255
|
-
|
256
|
-
expect(subject.rhs).to eq(
|
257
|
-
expect(
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
it 'should replace
|
262
|
-
%w(a b c
|
263
|
-
subject.reduce_step(p_bc)
|
264
|
-
|
265
|
-
expect(subject.rhs.size).to eq(
|
266
|
-
expect(subject.rhs).to eq(['a', p_bc,
|
267
|
-
expect(p_bc.refcount).to eq(2)
|
268
|
-
end
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
expect(subject.rhs
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
it 'should replace a production at the
|
289
|
-
[
|
290
|
-
expect(p_bc.refcount).to eq(1)
|
291
|
-
|
292
|
-
|
293
|
-
expect(subject.rhs.size).to eq(3)
|
294
|
-
expect(subject.rhs).to eq(%w(b c
|
295
|
-
expect(p_bc.refcount).to eq(0)
|
296
|
-
end
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
subject.
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
subject.
|
311
|
-
subject.
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
expect(
|
322
|
-
expect(
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
expect(fake).to receive(:
|
334
|
-
expect(fake).to receive(:
|
335
|
-
expect(fake).to receive(:
|
336
|
-
expect(fake).to receive(:
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
expect(fake).to receive(:
|
349
|
-
expect(fake).to receive(:
|
350
|
-
|
351
|
-
expect
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
expect(fake).to receive(:visit_prod_ref).with(p_a).ordered
|
363
|
-
expect(fake).to receive(:visit_prod_ref).with(p_bc).ordered
|
364
|
-
expect(fake).to receive(:end_visit_rhs).once.ordered
|
365
|
-
expect(fake).to receive(:end_visit_production).once.ordered
|
366
|
-
|
367
|
-
expect { subject.accept(fake) }.not_to raise_error
|
368
|
-
end
|
369
|
-
|
370
|
-
end # context
|
371
|
-
|
372
|
-
end # describe
|
373
|
-
|
374
|
-
end # module
|
375
|
-
|
376
|
-
# End of file
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../lib/sequitur/production'
|
5
|
+
|
6
|
+
module Sequitur # Re-open the module to get rid of qualified names
|
7
|
+
describe Production do
|
8
|
+
# Helper method: convert list of digrams into an array
|
9
|
+
# of symbol couples.
|
10
|
+
def to_symbols(theDigrams)
|
11
|
+
return theDigrams.map(&:symbols)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:p_a) do
|
15
|
+
instance = Production.new
|
16
|
+
instance.append_symbol(:a)
|
17
|
+
instance
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:p_bc) do
|
21
|
+
instance = Production.new
|
22
|
+
instance.append_symbol('b')
|
23
|
+
instance.append_symbol('c')
|
24
|
+
instance
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'Creation & initialization:' do
|
28
|
+
it 'should be created without argument' do
|
29
|
+
expect { Production.new }.not_to raise_error
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should not referenced yet' do
|
33
|
+
expect(subject.refcount).to eq(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should be empty at creation' do
|
37
|
+
expect(subject).to be_empty
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should not have digram' do
|
41
|
+
expect(subject.digrams).to be_empty
|
42
|
+
expect(subject.last_digram).to be_nil
|
43
|
+
end
|
44
|
+
end # context
|
45
|
+
|
46
|
+
context 'Provided services:' do
|
47
|
+
it 'should compare to another production' do
|
48
|
+
expect(p_a).to eq(p_a)
|
49
|
+
expect(p_a).not_to eq(p_bc)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should compare to a production reference' do
|
53
|
+
ref_a = ProductionRef.new(p_a)
|
54
|
+
expect(p_a).to eq(ref_a)
|
55
|
+
expect(p_bc).not_to eq(ref_a)
|
56
|
+
|
57
|
+
ref_bc = ProductionRef.new(p_bc)
|
58
|
+
expect(p_a).not_to eq(ref_bc)
|
59
|
+
expect(p_bc).to eq(ref_bc)
|
60
|
+
end
|
61
|
+
end # context
|
62
|
+
|
63
|
+
context 'Knowing its rhs:' do
|
64
|
+
it 'should know the productions in its rhs' do
|
65
|
+
# Case 1: empty production
|
66
|
+
expect(subject.references).to be_empty
|
67
|
+
|
68
|
+
# Case 2: production without references
|
69
|
+
symbols = [:a, :b, :c]
|
70
|
+
symbols.each { |symb| subject.append_symbol(symb) }
|
71
|
+
expect(subject.references).to be_empty
|
72
|
+
expect(subject.references_of(p_a)).to be_empty
|
73
|
+
|
74
|
+
# Case 2: production with one reference
|
75
|
+
subject.append_symbol(p_a)
|
76
|
+
expect(subject.references).to eq([p_a])
|
77
|
+
expect(subject.references_of(p_a).map(&:production)).to eq([p_a])
|
78
|
+
|
79
|
+
|
80
|
+
# Case 3: production with repeated references
|
81
|
+
subject.append_symbol(p_a) # second time
|
82
|
+
expect(subject.references).to eq([p_a, p_a])
|
83
|
+
expect(subject.references_of(p_a).map(&:production)).to eq([p_a, p_a])
|
84
|
+
|
85
|
+
|
86
|
+
# Case 4: production with multiple distinct references
|
87
|
+
subject.append_symbol(p_bc)
|
88
|
+
expect(subject.references).to eq([p_a, p_a, p_bc])
|
89
|
+
expect(subject.references_of(p_bc).map(&:production)).to eq([p_bc])
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should know the position(s) of a given digram' do
|
93
|
+
sequence1 = [:a, :b, :c, :a, :b, :a, :b, :d]
|
94
|
+
sequence1.each { |symb| subject.append_symbol(symb) }
|
95
|
+
positions = [0, 3, 5]
|
96
|
+
expect(subject.positions_of(:a, :b)).to eq(positions)
|
97
|
+
|
98
|
+
subject.clear_rhs
|
99
|
+
# Case of overlapping digrams
|
100
|
+
sequence2 = [:a, :a, :b, :a, :a, :a, :c, :d]
|
101
|
+
sequence2.each { |symb| subject.append_symbol(symb) }
|
102
|
+
positions = [0, 3]
|
103
|
+
expect(subject.positions_of(:a, :a)).to eq(positions)
|
104
|
+
end
|
105
|
+
end # context
|
106
|
+
|
107
|
+
context 'Appending a symbol:' do
|
108
|
+
it 'should append a symbol when empty' do
|
109
|
+
expect { subject.append_symbol(:a) }.not_to raise_error
|
110
|
+
expect(subject.rhs).to eq([:a])
|
111
|
+
expect(subject.last_digram).to be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should append a symbol when has one symbol' do
|
115
|
+
subject.append_symbol(:a)
|
116
|
+
subject.append_symbol(:b)
|
117
|
+
expect(subject.rhs).to eq([:a, :b])
|
118
|
+
expect(subject.last_digram.symbols).to eq([:a, :b])
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should append a symbol when rhs has several symbols' do
|
122
|
+
symbols = [:a, :b, :c, :d, :e, :f]
|
123
|
+
symbols.each { |symb| subject.append_symbol(symb) }
|
124
|
+
expect(subject.rhs).to eq(symbols)
|
125
|
+
expect(subject.last_digram.symbols).to eq([:e, :f])
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should append a production in its rhs' do
|
129
|
+
# Side-effect: refcount of production to append is incremented
|
130
|
+
expect(p_a.refcount).to be(0)
|
131
|
+
|
132
|
+
input = [p_a, :b, :c, :d, p_a, :e, :f] # p_a appears twice
|
133
|
+
input.each { |symb| subject.append_symbol(symb) }
|
134
|
+
expect(p_a.refcount).to be(2)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should append a production ref in its rhs' do
|
138
|
+
# Side-effect: refcount of production to append is incremented
|
139
|
+
ref_a = ProductionRef.new(p_a)
|
140
|
+
expect(p_a.refcount).to be(1)
|
141
|
+
|
142
|
+
input = [ref_a, :b, :c, :d, ref_a] # ref_a appears twice
|
143
|
+
input.each { |symb| subject.append_symbol(symb) }
|
144
|
+
|
145
|
+
# References in rhs should point to p_a...
|
146
|
+
# ...but should be distinct reference objects
|
147
|
+
expect(subject.rhs[0]).to eq(p_a)
|
148
|
+
expect(subject.rhs[0].object_id).not_to eq(ref_a.object_id)
|
149
|
+
expect(subject.rhs[-1]).to eq(p_a)
|
150
|
+
expect(subject.rhs[-1].object_id).not_to eq(ref_a.object_id)
|
151
|
+
|
152
|
+
# Reference count should be updated
|
153
|
+
expect(p_a.refcount).to be(3)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should complain when appending ref to nil production' do
|
157
|
+
# Side-effect: refcount of production to append is incremented
|
158
|
+
ref_a = ProductionRef.new(p_a)
|
159
|
+
expect(p_a.refcount).to be(1)
|
160
|
+
|
161
|
+
# Unbind the reference
|
162
|
+
ref_a.unbind
|
163
|
+
|
164
|
+
expect { subject.append_symbol(ref_a) }.to raise_error(StandardError)
|
165
|
+
end
|
166
|
+
end # context
|
167
|
+
|
168
|
+
|
169
|
+
context 'Text representation of a production rule:' do
|
170
|
+
it 'should emit minimal text when empty' do
|
171
|
+
expectation = "#{subject.object_id} : ."
|
172
|
+
expect(subject.to_string).to eq(expectation)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should emit its text representation' do
|
176
|
+
instance = Production.new
|
177
|
+
symbols = [:a, :b, 'c', :d, :e, 1000, instance]
|
178
|
+
symbols.each { |symb| subject.append_symbol(symb) }
|
179
|
+
expectation = "#{subject.object_id} : "
|
180
|
+
expectation << "a b 'c' d e 1000 #{instance.object_id}."
|
181
|
+
expect(subject.to_string).to eq(expectation)
|
182
|
+
end
|
183
|
+
end # context
|
184
|
+
|
185
|
+
context 'Detecting digram repetition:' do
|
186
|
+
it 'should report no repetition when empty' do
|
187
|
+
expect(subject.repeated_digram?).to be_falsey
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should report no repetition when rhs has less than 3 symbols' do
|
191
|
+
subject.append_symbol(:a)
|
192
|
+
expect(subject.repeated_digram?).to be_falsey
|
193
|
+
|
194
|
+
subject.append_symbol(:a)
|
195
|
+
expect(subject.repeated_digram?).to be_falsey
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'should detect shortest repetition' do
|
199
|
+
'aaa'.each_char { |symb| subject.append_symbol(symb) }
|
200
|
+
expect(subject.repeated_digram?).to be_truthy
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'should detect any repetition pattern' do
|
204
|
+
# Positive cases
|
205
|
+
cases = %w(abab abcdab abcdcd abcdefcd )
|
206
|
+
cases.each do |word|
|
207
|
+
instance = Production.new
|
208
|
+
word.each_char { |symb| instance.append_symbol(symb) }
|
209
|
+
expect(instance.repeated_digram?).to be_truthy
|
210
|
+
end
|
211
|
+
|
212
|
+
# Negative cases
|
213
|
+
cases = %w(abc abb abba abcdef)
|
214
|
+
cases.each do |word|
|
215
|
+
instance = Production.new
|
216
|
+
word.each_char { |symb| instance.append_symbol(symb) }
|
217
|
+
expect(instance.repeated_digram?).to be_falsey
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end # context
|
221
|
+
|
222
|
+
context 'Replacing a digram by a production:' do
|
223
|
+
it 'should have not effect on empty production' do
|
224
|
+
subject.reduce_step(p_bc)
|
225
|
+
expect(subject.rhs).to be_empty
|
226
|
+
expect(p_bc.refcount).to eq(0)
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
it 'should replace two-symbol sequence' do
|
231
|
+
%w(a b c d e b c e).each { |symb| subject.append_symbol(symb) }
|
232
|
+
p_bc_before = p_bc.to_string
|
233
|
+
subject.reduce_step(p_bc)
|
234
|
+
|
235
|
+
expect(subject.rhs.size).to eq(6)
|
236
|
+
expect(subject.rhs).to eq(['a', p_bc, 'd', 'e', p_bc, 'e'])
|
237
|
+
expect(p_bc.refcount).to eq(2)
|
238
|
+
expect(p_bc.to_string).to eq(p_bc_before)
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
it 'should replace a starting two-symbol sequence' do
|
243
|
+
%w(b c d e b c e).each { |symb| subject.append_symbol(symb) }
|
244
|
+
subject.reduce_step(p_bc)
|
245
|
+
|
246
|
+
expect(subject.rhs.size).to eq(5)
|
247
|
+
expect(subject.rhs).to eq([p_bc, 'd', 'e', p_bc, 'e'])
|
248
|
+
expect(p_bc.refcount).to eq(2)
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
it 'should replace an ending two-symbol sequence' do
|
253
|
+
%w(a b c d e b c).each { |symb| subject.append_symbol(symb) }
|
254
|
+
subject.reduce_step(p_bc)
|
255
|
+
|
256
|
+
expect(subject.rhs.size).to eq(5)
|
257
|
+
expect(subject.rhs).to eq(['a', p_bc, 'd', 'e', p_bc])
|
258
|
+
expect(p_bc.refcount).to eq(2)
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'should replace two consecutive two-symbol sequences' do
|
262
|
+
%w(a b c b c d).each { |symb| subject.append_symbol(symb) }
|
263
|
+
subject.reduce_step(p_bc)
|
264
|
+
|
265
|
+
expect(subject.rhs.size).to eq(4)
|
266
|
+
expect(subject.rhs).to eq(['a', p_bc, p_bc, 'd'])
|
267
|
+
expect(p_bc.refcount).to eq(2)
|
268
|
+
end
|
269
|
+
end # context
|
270
|
+
|
271
|
+
context 'Replacing a production occurrence by its rhs:' do
|
272
|
+
it 'should have not effect on empty production' do
|
273
|
+
subject.derive_step(p_bc)
|
274
|
+
expect(subject.rhs).to be_empty
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'should replace a production at the start' do
|
278
|
+
[p_bc, 'd'].each { |symb| subject.append_symbol(symb) }
|
279
|
+
expect(p_bc.refcount).to eq(1)
|
280
|
+
|
281
|
+
subject.derive_step(p_bc)
|
282
|
+
expect(subject.rhs.size).to eq(3)
|
283
|
+
expect(subject.rhs).to eq(%w(b c d))
|
284
|
+
expect(p_bc.refcount).to eq(0)
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
it 'should replace a production at the end' do
|
289
|
+
['d', p_bc].each { |symb| subject.append_symbol(symb) }
|
290
|
+
expect(p_bc.refcount).to eq(1)
|
291
|
+
subject.derive_step(p_bc)
|
292
|
+
|
293
|
+
expect(subject.rhs.size).to eq(3)
|
294
|
+
expect(subject.rhs).to eq(%w(d b c))
|
295
|
+
expect(p_bc.refcount).to eq(0)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'should replace a production as sole symbol' do
|
299
|
+
subject.append_symbol(p_bc)
|
300
|
+
subject.derive_step(p_bc)
|
301
|
+
|
302
|
+
expect(subject.rhs.size).to eq(2)
|
303
|
+
expect(subject.rhs).to eq(%w(b c))
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'should replace a production in the middle' do
|
307
|
+
['a', p_bc, 'd'].each { |symb| subject.append_symbol(symb) }
|
308
|
+
subject.derive_step(p_bc)
|
309
|
+
|
310
|
+
expect(subject.rhs.size).to eq(4)
|
311
|
+
expect(subject.rhs).to eq(%w(a b c d))
|
312
|
+
end
|
313
|
+
end # context
|
314
|
+
|
315
|
+
context 'Visiting:' do
|
316
|
+
it 'should accept a visitor when its rhs is empty' do
|
317
|
+
# Use a mock visitor
|
318
|
+
fake = double('fake_visitor')
|
319
|
+
|
320
|
+
# Empty production: visitor will receive a start and end visit messages
|
321
|
+
expect(fake).to receive(:start_visit_production).once.ordered
|
322
|
+
expect(fake).to receive(:start_visit_rhs).once.ordered
|
323
|
+
expect(fake).to receive(:end_visit_rhs).once.ordered
|
324
|
+
expect(fake).to receive(:end_visit_production).once.ordered
|
325
|
+
|
326
|
+
expect { subject.accept(fake) }.not_to raise_error
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'should accept a visitor when rhs consists of terminals only' do
|
330
|
+
# Use a mock visitor
|
331
|
+
fake = double('fake_visitor')
|
332
|
+
expect(fake).to receive(:start_visit_production).once.ordered
|
333
|
+
expect(fake).to receive(:start_visit_rhs).once.ordered
|
334
|
+
expect(fake).to receive(:visit_terminal).with('b').ordered
|
335
|
+
expect(fake).to receive(:visit_terminal).with('c').ordered
|
336
|
+
expect(fake).to receive(:end_visit_rhs).once.ordered
|
337
|
+
expect(fake).to receive(:end_visit_production).once.ordered
|
338
|
+
|
339
|
+
expect { p_bc.accept(fake) }.not_to raise_error
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'should accept a visitor when rhs consists of non-terminals' do
|
343
|
+
# Add two production references (=non-terminals) to RHS of subject
|
344
|
+
subject.append_symbol(p_a)
|
345
|
+
subject.append_symbol(p_bc)
|
346
|
+
|
347
|
+
fake = double('fake_visitor')
|
348
|
+
expect(fake).to receive(:start_visit_production).once.ordered
|
349
|
+
expect(fake).to receive(:start_visit_rhs).once.ordered
|
350
|
+
expect(fake).to receive(:visit_prod_ref).with(p_a).ordered
|
351
|
+
expect(fake).to receive(:visit_prod_ref).with(p_bc).ordered
|
352
|
+
expect(fake).to receive(:end_visit_rhs).once.ordered
|
353
|
+
expect(fake).to receive(:end_visit_production).once.ordered
|
354
|
+
|
355
|
+
expect { subject.accept(fake) }.not_to raise_error
|
356
|
+
end
|
357
|
+
end # context
|
358
|
+
end # describe
|
359
|
+
end # module
|
360
|
+
|
361
|
+
# End of file
|