sequitur 0.1.07 → 0.1.09
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/CHANGELOG.md +9 -0
- data/README.md +6 -6
- data/lib/sequitur/constants.rb +1 -1
- data/lib/sequitur/grammar_visitor.rb +12 -3
- data/lib/sequitur/production.rb +12 -40
- data/lib/sequitur/sequitur_grammar.rb +12 -10
- data/lib/sequitur/symbol_sequence.rb +150 -0
- data/spec/sequitur/dynamic_grammar_spec.rb +6 -3
- data/spec/sequitur/grammar_visitor_spec.rb +0 -10
- data/spec/sequitur/production_spec.rb +6 -0
- data/spec/sequitur/symbol_sequence_spec.rb +95 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
M2JmMGMzOGFjNjg3YmQzZDNlMWNjODI2ZmIzNjA4MTM4ZDNkMzUzZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZTAyMGI0MDYzMDBkMDJlNWFhNjE2Yjc5NTUyZjhiNDEzZjEzYzg1ZQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OGQ1YjVhY2M2MmM2YWNiODYyYzQxMmRjNmFmYWY2NjYyMGYyMWM5YjVjNzFh
|
10
|
+
OTVmMDhhZTE2YTRhMWNmODVhYTJjMDUyMDNlYjk0NmJjMjRkN2Q0ODcyYTRj
|
11
|
+
YzAyZDA2ZDJiY2E1YWE5ZTYyYWExNmJkMzI3ODYyZDgwZGUyMzI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2EyZmU3NTNiMTc1YmZkZjk4ODNmMzYyY2ExYzI0YTNmNTFlYWY4OWUxZTQ4
|
14
|
+
NjJmMWUzYzM3Yjc4ZDU5YzIyNTEwZTI5OWI1ZDlhY2RiYzQ3Yzc5OTgxZDZk
|
15
|
+
MzY1MzEwMDBjYTliYmQxYTEwYzQ4MjQzYzFjYjVhNDgxMGE4YTA=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
### 0.1.09 / 2014-10-03
|
2
|
+
* [NEW] Class `SymbolSequence`. Part of code refactoring that reduces code complexity reported by CodeClimate.
|
3
|
+
* [CHANGE] Class `Production` refactored to use a SymbolSequence instance as its rhs.
|
4
|
+
* [CHANGE] File `README.md`: Minor cosmetic enhancements.
|
5
|
+
|
6
|
+
### 0.1.08 / 2014-09-30
|
7
|
+
* [CHANGE] Method `SequiturGrammar#restore_unicity` Code refactored to reduce code complexity reported by CodeClimate.
|
8
|
+
* [CHANGE] File `README.md`: Minor cosmetic enhancements.
|
9
|
+
|
1
10
|
### 0.1.07 / 2014-09-29
|
2
11
|
* [CHANGE] File `README.md`: Fixed bad Markdown syntax in badge part.
|
3
12
|
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ _Ruby gem implementing the Sequitur algorithm_
|
|
8
8
|
[](http://badge.fury.io/rb/sequitur)
|
9
9
|
[](https://gemnasium.com/famished-tiger/Sequitur)
|
10
10
|
[](http://inch-ci.org/github/famished-tiger/Sequitur)
|
11
|
-
|
11
|
+
[](https://codeclimate.com/github/famished-tiger/Sequitur)
|
12
12
|
|
13
13
|
### What is the Sequitur algorithm? ###
|
14
14
|
The following are good entry points to learn about the algorithm:
|
@@ -23,7 +23,7 @@ in a very compact way.
|
|
23
23
|
Of interest is the fact that the algorithm runs in time linear in the length of the input sequence.
|
24
24
|
|
25
25
|
**Can you give a simple example?**
|
26
|
-
Sure. Let's begin with a very basic case. Assume that 'abcabcabc' is our input string.
|
26
|
+
Sure. Let's begin with a very basic case. Assume that *'abcabcabc'* is our input string.
|
27
27
|
Notice that it is the same as the text 'abc' repeated three times. The Sequitur algorithm captures
|
28
28
|
this repetition and will generate the two following rules:
|
29
29
|
|
@@ -32,13 +32,13 @@ start : P1 P1 P1.
|
|
32
32
|
P1 : a b c.
|
33
33
|
```
|
34
34
|
|
35
|
-
In plain English:
|
35
|
+
In plain English:
|
36
36
|
-The first rule (named start) always represents the whole input. Here, it indicates that the input
|
37
|
-
is three time the pattern encoded by the rule called P1.
|
38
|
-
-The second rule (named P1) represents the sequence a b c.
|
37
|
+
is three time the pattern encoded by the rule called P1.
|
38
|
+
-The second rule (named P1) represents the sequence a b c.
|
39
39
|
|
40
40
|
**Can you give another example?**
|
41
|
-
Yep. Assume this time that the input is 'ababcabcdabcde'
|
41
|
+
Yep. Assume this time that the input is *'ababcabcdabcde'*.
|
42
42
|
Then Sequitur algorithm will generate the rule set:
|
43
43
|
```
|
44
44
|
start : P1 P2 P3 P3 e.
|
data/lib/sequitur/constants.rb
CHANGED
@@ -47,7 +47,12 @@ class GrammarVisitor
|
|
47
47
|
# @param aProduction [Production] the production to visit.
|
48
48
|
def start_visit_production(aProduction)
|
49
49
|
broadcast(:before_production, aProduction)
|
50
|
-
|
50
|
+
end
|
51
|
+
|
52
|
+
# Visit event. The visitor is about to visit the given rhs of production.
|
53
|
+
# @param rhs [SymbolSequence] the rhs of a production to visit.
|
54
|
+
def start_visit_rhs(rhs)
|
55
|
+
broadcast(:before_rhs, rhs)
|
51
56
|
end
|
52
57
|
|
53
58
|
# Visit event. The visitor is visiting the
|
@@ -66,13 +71,17 @@ class GrammarVisitor
|
|
66
71
|
broadcast(:before_terminal, aTerminal)
|
67
72
|
broadcast(:after_terminal, aTerminal)
|
68
73
|
end
|
74
|
+
|
75
|
+
# Visit event. The visitor has completed its visit of the given rhs.
|
76
|
+
# @param rhs [SymbolSequence] the rhs of a production to visit.
|
77
|
+
def end_visit_rhs(rhs)
|
78
|
+
broadcast(:after_rhs, rhs)
|
79
|
+
end
|
69
80
|
|
70
81
|
# Visit event. The visitor has completed its visit of the given production.
|
71
82
|
# @param aProduction [Production] the production to visit.
|
72
83
|
def end_visit_production(aProduction)
|
73
|
-
broadcast(:after_rhs, aProduction.rhs)
|
74
84
|
broadcast(:after_production, aProduction)
|
75
|
-
|
76
85
|
end
|
77
86
|
|
78
87
|
# Visit event. The visitor has completed the visit of the grammar.
|
data/lib/sequitur/production.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'digram'
|
2
|
+
require_relative 'symbol_sequence'
|
2
3
|
require_relative 'production_ref'
|
3
4
|
|
4
5
|
module Sequitur # Module for classes implementing the Sequitur algorithm
|
@@ -25,10 +26,12 @@ class Production
|
|
25
26
|
# Constructor.
|
26
27
|
# Build a production with an empty RHS.
|
27
28
|
def initialize()
|
28
|
-
|
29
|
+
@rhs = SymbolSequence.new
|
29
30
|
@refcount = 0
|
30
31
|
@digrams = []
|
31
32
|
end
|
33
|
+
|
34
|
+
|
32
35
|
|
33
36
|
public
|
34
37
|
|
@@ -69,7 +72,7 @@ class Production
|
|
69
72
|
# Select the references to production appearing in the rhs.
|
70
73
|
# @return [Array of ProductionRef]
|
71
74
|
def references()
|
72
|
-
return rhs.
|
75
|
+
return rhs.references
|
73
76
|
end
|
74
77
|
|
75
78
|
# Look in the rhs all the references to a production passed a argument.
|
@@ -87,8 +90,7 @@ class Production
|
|
87
90
|
return [] if rhs.size < 2
|
88
91
|
|
89
92
|
result = []
|
90
|
-
rhs.each_cons(2) { |couple| result << Digram.new(*couple, self) }
|
91
|
-
|
93
|
+
rhs.symbols.each_cons(2) { |couple| result << Digram.new(*couple, self) }
|
92
94
|
@digrams = result
|
93
95
|
end
|
94
96
|
|
@@ -129,14 +131,7 @@ class Production
|
|
129
131
|
# object id of production : rhs as space-separated sequence of symbols.
|
130
132
|
# @return [String]
|
131
133
|
def to_string()
|
132
|
-
|
133
|
-
case elem
|
134
|
-
when String then "'#{elem}'"
|
135
|
-
else elem.to_s
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
return "#{object_id} : #{rhs_text.join(' ')}."
|
134
|
+
return "#{object_id} : #{rhs.to_string}."
|
140
135
|
end
|
141
136
|
|
142
137
|
# Add a (grammar) symbol at the end of the RHS.
|
@@ -163,11 +158,7 @@ class Production
|
|
163
158
|
# Clear the right-hand side.
|
164
159
|
# Any referenced production has its reference counter decremented.
|
165
160
|
def clear_rhs()
|
166
|
-
|
167
|
-
refs = references
|
168
|
-
refs.each(&:unbind)
|
169
|
-
end
|
170
|
-
@rhs = []
|
161
|
+
rhs.clear()
|
171
162
|
end
|
172
163
|
|
173
164
|
# Find all the positions where the digram occurs in the rhs
|
@@ -203,20 +194,11 @@ class Production
|
|
203
194
|
# @param another [Production or ProductionRef] a production that
|
204
195
|
# consists exactly of one digram (= 2 symbols).
|
205
196
|
def reduce_step(another)
|
206
|
-
(symb1, symb2) = another.rhs
|
197
|
+
(symb1, symb2) = another.rhs.symbols
|
207
198
|
pos = positions_of(symb1, symb2).reverse
|
208
199
|
|
209
200
|
# Replace the two symbol sequence by the production
|
210
|
-
pos.each
|
211
|
-
if rhs[index].is_a?(ProductionRef)
|
212
|
-
rhs[index].bind_to(another)
|
213
|
-
else
|
214
|
-
rhs[index] = ProductionRef.new(another)
|
215
|
-
end
|
216
|
-
index1 = index + 1
|
217
|
-
rhs[index1].unbind if rhs[index1].is_a?(ProductionRef)
|
218
|
-
rhs.delete_at(index1)
|
219
|
-
end
|
201
|
+
pos.each { |index| rhs.reduce_step(index, another) }
|
220
202
|
|
221
203
|
recalc_digrams
|
222
204
|
end
|
@@ -235,11 +217,7 @@ class Production
|
|
235
217
|
(0...rhs.size).to_a.reverse.each do |index|
|
236
218
|
next unless rhs[index] == another
|
237
219
|
|
238
|
-
|
239
|
-
other_rhs = another.rhs.map do |symb|
|
240
|
-
symb.is_a?(ProductionRef) ? symb.dup : symb
|
241
|
-
end
|
242
|
-
rhs.insert(index + 1, *other_rhs)
|
220
|
+
rhs.insert_at(index + 1, another.rhs)
|
243
221
|
another.decr_refcount
|
244
222
|
rhs.delete_at(index)
|
245
223
|
end
|
@@ -253,13 +231,7 @@ class Production
|
|
253
231
|
def accept(aVisitor)
|
254
232
|
aVisitor.start_visit_production(self)
|
255
233
|
|
256
|
-
rhs.
|
257
|
-
if a_symb.is_a?(ProductionRef)
|
258
|
-
a_symb.accept(aVisitor)
|
259
|
-
else
|
260
|
-
aVisitor.visit_terminal(a_symb)
|
261
|
-
end
|
262
|
-
end
|
234
|
+
rhs.accept(aVisitor)
|
263
235
|
|
264
236
|
aVisitor.end_visit_production(self)
|
265
237
|
end
|
@@ -97,7 +97,6 @@ CollisionDiagnosis = Struct.new(
|
|
97
97
|
# Then create a new production that will have
|
98
98
|
# the symbols of d as its rhs members.
|
99
99
|
def restore_unicity(aDiagnosis)
|
100
|
-
digr = aDiagnosis.digram
|
101
100
|
prods = aDiagnosis.productions
|
102
101
|
if prods.any?(&:single_digram?)
|
103
102
|
(simple, compound) = prods.partition(&:single_digram?)
|
@@ -105,14 +104,9 @@ CollisionDiagnosis = Struct.new(
|
|
105
104
|
else
|
106
105
|
# Create a new production with the digram's symbols as its
|
107
106
|
# sole rhs members.
|
108
|
-
new_prod =
|
109
|
-
|
110
|
-
|
111
|
-
if prods[0] == prods[1]
|
112
|
-
prods[0].reduce_step(new_prod)
|
113
|
-
else
|
114
|
-
prods.each { |a_prod| a_prod.reduce_step(new_prod) }
|
115
|
-
end
|
107
|
+
new_prod = build_production_for(aDiagnosis.digram)
|
108
|
+
prods[0].reduce_step(new_prod)
|
109
|
+
prods[1].reduce_step(new_prod) unless prods[1] == prods[0]
|
116
110
|
end
|
117
111
|
end
|
118
112
|
|
@@ -145,7 +139,15 @@ CollisionDiagnosis = Struct.new(
|
|
145
139
|
remove_production(index)
|
146
140
|
end
|
147
141
|
|
148
|
-
|
142
|
+
# Create a new production that will have the symbols from digram
|
143
|
+
# as its rhs members.
|
144
|
+
def build_production_for(aDigram)
|
145
|
+
new_prod = Production.new
|
146
|
+
aDigram.symbols.each { |sym| new_prod.append_symbol(sym) }
|
147
|
+
add_production(new_prod)
|
148
|
+
|
149
|
+
return new_prod
|
150
|
+
end
|
149
151
|
end # class
|
150
152
|
|
151
153
|
end # module
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Sequitur # Module for classes implementing the Sequitur algorithm
|
2
|
+
# Represents a sequence (concatenation) of grammar symbols
|
3
|
+
# as they appear in rhs of productions
|
4
|
+
class SymbolSequence
|
5
|
+
# The sequence of symbols itself
|
6
|
+
attr_reader(:symbols)
|
7
|
+
|
8
|
+
# Create an empty sequence
|
9
|
+
def initialize()
|
10
|
+
@symbols = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Copy constructor invoked by dup or clone methods.
|
14
|
+
# @param orig [SymbolSequence]
|
15
|
+
def initialize_copy(orig)
|
16
|
+
# Deep copy to avoid the aliasing of production reference
|
17
|
+
@symbols = orig.symbols.map do |sym|
|
18
|
+
sym.is_a?(Symbol) ? sym : sym.dup
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
public
|
23
|
+
|
24
|
+
# Clear the symbol sequence.
|
25
|
+
def clear()
|
26
|
+
refs = references
|
27
|
+
refs.each(&:unbind)
|
28
|
+
@symbols = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Tell whether the sequence is empty.
|
32
|
+
# @return [true / false] true only if the sequence has no symbol in it.
|
33
|
+
def empty?()
|
34
|
+
return symbols.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Count the number of elements in the sequence.
|
38
|
+
# @return [Fixnum] the number of elements
|
39
|
+
def size()
|
40
|
+
return symbols.size
|
41
|
+
end
|
42
|
+
|
43
|
+
# Append a grammar symbol at the end of the sequence.
|
44
|
+
# @param aSymbol [Object] The symbol to append.
|
45
|
+
def <<(aSymbol)
|
46
|
+
symbols << aSymbol
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieve the element from the sequence at given position.
|
50
|
+
# @param anIndex [Fixnum] A zero-based index of the element to access.
|
51
|
+
def [](anIndex)
|
52
|
+
return symbols[anIndex]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Equality testing.
|
56
|
+
# @param other [SymbolSequence or Array] the other other sequence
|
57
|
+
# to compare to.
|
58
|
+
# @return true when an item from self equals the corresponding
|
59
|
+
# item from 'other'
|
60
|
+
def ==(other)
|
61
|
+
return true if self.object_id == other.object_id
|
62
|
+
|
63
|
+
case other
|
64
|
+
when SymbolSequence
|
65
|
+
same = self.symbols == other.symbols
|
66
|
+
when Array
|
67
|
+
same = self.symbols == other
|
68
|
+
else
|
69
|
+
same = false
|
70
|
+
end
|
71
|
+
|
72
|
+
return same
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Select the references to production appearing in the rhs.
|
77
|
+
# @return [Array of ProductionRef]
|
78
|
+
def references()
|
79
|
+
return symbols.select { |symb| symb.is_a?(ProductionRef) }
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Emit a text representation of the symbol sequence.
|
84
|
+
# Text is of the form: space-separated sequence of symbols.
|
85
|
+
# @return [String]
|
86
|
+
def to_string()
|
87
|
+
rhs_text = symbols.map do |elem|
|
88
|
+
case elem
|
89
|
+
when String then "'#{elem}'"
|
90
|
+
else elem.to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
return rhs_text.join(' ')
|
95
|
+
end
|
96
|
+
|
97
|
+
# Insert at position the elements from another sequence.
|
98
|
+
# @param position [Fixnum] A zero-based index of the symbols to replace.
|
99
|
+
# @param another [Production] A production with a two-elements rhs
|
100
|
+
# (a single digram).
|
101
|
+
def insert_at(position, another)
|
102
|
+
klone = another.dup
|
103
|
+
symbols.insert(position, *klone.symbols)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Given that the production P passed as argument has exactly 2 symbols
|
107
|
+
# in its rhs s1 s2, substitute in the rhs of self all occurrences of
|
108
|
+
# s1 s2 by a reference to P.
|
109
|
+
# @param index [Fixnum] the position of a two symbol sequence to be replaced
|
110
|
+
# by the production
|
111
|
+
# @param aProduction [Production or ProductionRef] a production that
|
112
|
+
# consists exactly of one digram (= 2 symbols).
|
113
|
+
def reduce_step(index, aProduction)
|
114
|
+
if symbols[index].is_a?(ProductionRef)
|
115
|
+
symbols[index].bind_to(aProduction)
|
116
|
+
else
|
117
|
+
symbols[index] = ProductionRef.new(aProduction)
|
118
|
+
end
|
119
|
+
index1 = index + 1
|
120
|
+
symbols[index1].unbind if symbols[index1].is_a?(ProductionRef)
|
121
|
+
delete_at(index1)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Remove the element at given position
|
125
|
+
# @param position [Fixnum] a zero-based index.
|
126
|
+
def delete_at(position)
|
127
|
+
symbols.delete_at(position)
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
132
|
+
# @param aVisitor[GrammarVisitor]
|
133
|
+
def accept(aVisitor)
|
134
|
+
aVisitor.start_visit_rhs(self)
|
135
|
+
|
136
|
+
# Let's proceed with the visit of productions
|
137
|
+
symbols.each do |a_symb|
|
138
|
+
if a_symb.is_a?(ProductionRef)
|
139
|
+
a_symb.accept(aVisitor)
|
140
|
+
else
|
141
|
+
aVisitor.visit_terminal(a_symb)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
aVisitor.end_visit_rhs(self)
|
146
|
+
end
|
147
|
+
|
148
|
+
end # class
|
149
|
+
|
150
|
+
end # module
|
@@ -108,9 +108,12 @@ describe DynamicGrammar do
|
|
108
108
|
a_visitor.subscribe(fake_formatter)
|
109
109
|
|
110
110
|
expect(fake_formatter).to receive(:before_grammar).with(subject).ordered
|
111
|
-
expect(fake_formatter).to receive(:before_production)
|
112
|
-
|
113
|
-
expect(fake_formatter).to receive(:
|
111
|
+
expect(fake_formatter).to receive(:before_production)
|
112
|
+
.with(subject.start).ordered
|
113
|
+
expect(fake_formatter).to receive(:before_rhs)
|
114
|
+
.with(subject.start.rhs).ordered
|
115
|
+
expect(fake_formatter).to receive(:after_rhs)
|
116
|
+
.with(subject.start.rhs).ordered
|
114
117
|
expect(fake_formatter).to receive(:after_production).with(subject.start)
|
115
118
|
expect(fake_formatter).to receive(:before_production).with(p_a)
|
116
119
|
expect(fake_formatter).to receive(:before_rhs).with(p_a.rhs)
|
@@ -78,16 +78,6 @@ describe GrammarVisitor do
|
|
78
78
|
|
79
79
|
subject.start_visit_grammar(fake)
|
80
80
|
end
|
81
|
-
|
82
|
-
it 'should react to the start_visit_production message' do
|
83
|
-
expect(mock_production).to receive(:rhs).and_return([1, 2, 3])
|
84
|
-
|
85
|
-
# Notify subscribers when start the visit of the production
|
86
|
-
expect(listener1).to receive(:before_production).with(mock_production)
|
87
|
-
expect(listener1).to receive(:before_rhs).with([1, 2, 3])
|
88
|
-
|
89
|
-
subject.start_visit_production(mock_production)
|
90
|
-
end
|
91
81
|
|
92
82
|
end # context
|
93
83
|
|
@@ -330,6 +330,8 @@ describe Production do
|
|
330
330
|
|
331
331
|
# Empty production: visitor will receive a start and end visit messages
|
332
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(:end_visit_rhs).once.ordered
|
333
335
|
expect(fake).to receive(:end_visit_production).once.ordered
|
334
336
|
|
335
337
|
expect { subject.accept(fake) }.not_to raise_error
|
@@ -339,8 +341,10 @@ describe Production do
|
|
339
341
|
# Use a mock visitor
|
340
342
|
fake = double('fake_visitor')
|
341
343
|
expect(fake).to receive(:start_visit_production).once.ordered
|
344
|
+
expect(fake).to receive(:start_visit_rhs).once.ordered
|
342
345
|
expect(fake).to receive(:visit_terminal).with('b').ordered
|
343
346
|
expect(fake).to receive(:visit_terminal).with('c').ordered
|
347
|
+
expect(fake).to receive(:end_visit_rhs).once.ordered
|
344
348
|
expect(fake).to receive(:end_visit_production).once.ordered
|
345
349
|
|
346
350
|
expect { p_bc.accept(fake) }.not_to raise_error
|
@@ -353,8 +357,10 @@ describe Production do
|
|
353
357
|
|
354
358
|
fake = double('fake_visitor')
|
355
359
|
expect(fake).to receive(:start_visit_production).once.ordered
|
360
|
+
expect(fake).to receive(:start_visit_rhs).once.ordered
|
356
361
|
expect(fake).to receive(:visit_prod_ref).with(p_a).ordered
|
357
362
|
expect(fake).to receive(:visit_prod_ref).with(p_bc).ordered
|
363
|
+
expect(fake).to receive(:end_visit_rhs).once.ordered
|
358
364
|
expect(fake).to receive(:end_visit_production).once.ordered
|
359
365
|
|
360
366
|
expect { subject.accept(fake) }.not_to raise_error
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../lib/sequitur/symbol_sequence'
|
5
|
+
require_relative '../../lib/sequitur/production_ref'
|
6
|
+
require_relative '../../lib/sequitur/production'
|
7
|
+
|
8
|
+
module Sequitur # Re-open the module to get rid of qualified names
|
9
|
+
|
10
|
+
describe SymbolSequence do
|
11
|
+
|
12
|
+
let(:instance) { SymbolSequence.new }
|
13
|
+
|
14
|
+
context 'Creation and initialization:' do
|
15
|
+
|
16
|
+
it 'should be created without argument' do
|
17
|
+
expect { SymbolSequence.new }.not_to raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should be empty at creation' do
|
21
|
+
expect(subject).to be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
end # context
|
25
|
+
|
26
|
+
context 'Provided services:' do
|
27
|
+
subject do
|
28
|
+
an_instance = SymbolSequence.new
|
29
|
+
[:a, :b, :c].each { |a_sym| an_instance << a_sym }
|
30
|
+
an_instance
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should deep-copy clone itself' do
|
34
|
+
a_prod = Production.new
|
35
|
+
ref = ProductionRef.new(a_prod)
|
36
|
+
|
37
|
+
a, c = 'a', 'c'
|
38
|
+
[a, ref, c].each { |ch| instance << ch }
|
39
|
+
clone_a = instance.clone
|
40
|
+
|
41
|
+
# Check that cloning works
|
42
|
+
expect(clone_a).to eq(instance)
|
43
|
+
|
44
|
+
# Reference objects are distinct but points to same production
|
45
|
+
expect(clone_a.symbols[1].object_id).not_to eq(instance.symbols[1])
|
46
|
+
|
47
|
+
# Modifying the clone...
|
48
|
+
clone_a.symbols[1] = 'diff'
|
49
|
+
expect(clone_a).not_to eq(instance)
|
50
|
+
|
51
|
+
# ... should leave original unchanged
|
52
|
+
expect(instance.symbols[1]).to eq(ref)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
it 'should tell that it is equal to itself' do
|
57
|
+
# Case: Non-empty sequence
|
58
|
+
expect(subject).to eq(subject)
|
59
|
+
|
60
|
+
# Case: empty sequence
|
61
|
+
expect(instance).to eq(instance)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should know whether it is equal to another instance' do
|
65
|
+
expect(instance).to eq(instance)
|
66
|
+
|
67
|
+
expect(subject).not_to eq(instance)
|
68
|
+
[:a, :b, :c].each { |a_sym| instance << a_sym }
|
69
|
+
expect(subject).to eq(instance)
|
70
|
+
|
71
|
+
# Check that element order is relevant
|
72
|
+
instance.symbols.rotate!
|
73
|
+
expect(subject).not_to eq(instance)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should know whether it is equal to an array' do
|
77
|
+
expect(subject).to eq([:a, :b, :c])
|
78
|
+
|
79
|
+
# Check that element order is relevant
|
80
|
+
expect(subject).not_to eq([:c, :b, :a])
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should know that is not equal to something else' do
|
84
|
+
expect(subject).not_to eq(:abc)
|
85
|
+
end
|
86
|
+
|
87
|
+
end # context
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
end # describe
|
92
|
+
|
93
|
+
end # module
|
94
|
+
|
95
|
+
# End of file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequitur
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.09
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/sequitur/production.rb
|
115
115
|
- lib/sequitur/production_ref.rb
|
116
116
|
- lib/sequitur/sequitur_grammar.rb
|
117
|
+
- lib/sequitur/symbol_sequence.rb
|
117
118
|
- spec/sequitur/digram_spec.rb
|
118
119
|
- spec/sequitur/dynamic_grammar_spec.rb
|
119
120
|
- spec/sequitur/formatter/base_text_spec.rb
|
@@ -122,6 +123,7 @@ files:
|
|
122
123
|
- spec/sequitur/production_ref_spec.rb
|
123
124
|
- spec/sequitur/production_spec.rb
|
124
125
|
- spec/sequitur/sequitur_grammar_spec.rb
|
126
|
+
- spec/sequitur/symbol_sequence_spec.rb
|
125
127
|
- spec/spec_helper.rb
|
126
128
|
homepage: https://github.com/famished-tiger/Sequitur
|
127
129
|
licenses:
|
@@ -163,3 +165,4 @@ test_files:
|
|
163
165
|
- spec/sequitur/production_ref_spec.rb
|
164
166
|
- spec/sequitur/production_spec.rb
|
165
167
|
- spec/sequitur/sequitur_grammar_spec.rb
|
168
|
+
- spec/sequitur/symbol_sequence_spec.rb
|