sequitur 0.1.07 → 0.1.09
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/sequitur.svg)](http://badge.fury.io/rb/sequitur)
|
9
9
|
[![Dependency Status](https://gemnasium.com/famished-tiger/Sequitur.png)](https://gemnasium.com/famished-tiger/Sequitur)
|
10
10
|
[![Inline docs](http://inch-ci.org/github/famished-tiger/Sequitur.svg?branch=master)](http://inch-ci.org/github/famished-tiger/Sequitur)
|
11
|
-
|
11
|
+
[![Code Climate](https://codeclimate.com/github/famished-tiger/Sequitur.png)](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
|