cofgratx 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ class RuleError < Exception
2
+ end
@@ -0,0 +1,25 @@
1
+ class Terminal
2
+ attr_accessor :terminal
3
+
4
+ def initialize param
5
+ unless %w{Regexp String}.include? param.class.name
6
+ raise ArgumentError.new("expected Regular Expression or String; got #{param.class.name}")
7
+ end
8
+ param = param.class == String ?
9
+ Regexp.escape(param) :
10
+ param.source
11
+
12
+ @terminal = Regexp.compile "^(" + param +")"
13
+ end
14
+
15
+ def match? string
16
+ (@terminal =~ string) == 0
17
+ end
18
+
19
+ def extract string
20
+ return [nil, string] unless @terminal =~ string
21
+ terminal_match = $1
22
+ [ $1, string[terminal_match.length..-1] ]
23
+ end
24
+
25
+ end
@@ -0,0 +1,33 @@
1
+ class TranslationRepetitionSet
2
+
3
+ attr_accessor :offset, :translations
4
+
5
+ def initialize offset = 1, *translations
6
+ self.offset = offset
7
+ self.translations = translations
8
+ end
9
+
10
+ def offset= offset
11
+ if offset.class.name != "Fixnum"
12
+ raise ArgumentError.new("expected Fixnum; got '#{offset.class.name}'")
13
+ elsif offset <= 0
14
+ raise ArgumentError.new("expected positive Fixnum; got '#{offset}'")
15
+ end
16
+
17
+ @offset = offset.to_i
18
+ end
19
+
20
+ def translations= *translations
21
+ good_parts = []
22
+ [ translations ].flatten.each do |translation|
23
+ if ! [Fixnum, String].include? translation.class
24
+ raise ArgumentError.new("expected Fixnum or String; got #{translation.class.name}")
25
+ elsif translation.class == Fixnum and translation <= 0
26
+ raise TranslationRepetitionSetError.new("subrule number cannot be less than 1")
27
+ end
28
+ good_parts << translation
29
+ end
30
+ @translations = good_parts
31
+ end
32
+
33
+ end
@@ -0,0 +1,2 @@
1
+ class TranslationRepetitionSetError < Exception
2
+ end
@@ -0,0 +1,3 @@
1
+ module Cofgratx
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,423 @@
1
+ require 'spec_helper'
2
+ require 'cofgratx/cfg/terminal'
3
+ require 'cofgratx/cfg/repetition'
4
+ require 'cofgratx/cfg/translation_repetition_set_error'
5
+ require 'cofgratx/cfg/translation_repetition_set'
6
+ require 'cofgratx/cfg/rule_error'
7
+ require 'cofgratx/cfg/grammar_error'
8
+ require 'cofgratx/cfg/rule'
9
+ require 'cofgratx/cfg/non_terminal'
10
+ require 'cofgratx/cfg/grammar'
11
+
12
+ describe Grammar do
13
+ context ".initialize" do
14
+ it { expect{ described_class.new }.to_not raise_error }
15
+ end
16
+
17
+ context ".add_rules" do
18
+ before do
19
+ @grammar = described_class.new
20
+ end
21
+
22
+ it "allows rule symbols to be given without rules" do
23
+ expect_any_instance_of(NonTerminal).to receive(:add_rules).with(no_args).and_call_original
24
+
25
+ expect( @grammar.rules.keys.size ).to equal 0
26
+ @grammar.add_rules :one
27
+ expect( @grammar.rules.keys.size ).to equal 1
28
+ expect( @grammar.rules.values.first.class ).to equal NonTerminal
29
+ end
30
+
31
+ it "allows non terminal names to be given as strings or symbols" do
32
+ expect( @grammar.rules.keys.size ).to equal 0
33
+ @grammar.add_rules :one, [ [Terminal.new(/a/)] ,[] ]
34
+ expect( @grammar.rules.keys.size ).to equal 1
35
+
36
+ @grammar.add_rules "one", [ [Terminal.new(/b/)] ,[] ]
37
+ expect( @grammar.rules.keys.size ).to equal 1
38
+ end
39
+
40
+ it "converts symbols in the subrule arrays to nonterminals" do
41
+ expect( @grammar.rules.keys.size ).to equal 0
42
+ @grammar.add_rules :one, [ [:S] ,[] ]
43
+ expect( @grammar.rules.keys ).to match_array [ :one, :S ]
44
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ [@grammar.rules[:S]] ]
45
+ end
46
+
47
+ it "adds a rule with translation" do
48
+ expect( @grammar.rules.keys.size ).to equal 0
49
+ expect{
50
+ @grammar.add_rules :one,
51
+ [ [Terminal.new(/a/), Terminal.new("b"), :two, Repetition.new(",")],
52
+ [1,2,TranslationRepetitionSet.new(1,3,2,1,"STOP")]
53
+ ]
54
+ }.to_not raise_error
55
+ expect( @grammar.rules.keys.size ).to equal 2
56
+ end
57
+
58
+ it "adds a rule with invalid translation for nonexistant repetition" do
59
+ expect( @grammar.rules.keys.size ).to equal 0
60
+ expect{
61
+ @grammar.add_rules :one,
62
+ [ [Terminal.new(/a/), Terminal.new("b"), :two],
63
+ [1,2,TranslationRepetitionSet.new(1,3,2,1,"STOP")]
64
+ ]
65
+ }.to raise_error GrammarError, "rule does not contain repetition"
66
+ expect{
67
+ @grammar.add_rules :one,
68
+ [ [Terminal.new(/a/), Terminal.new("b"), :two, Repetition.new(",")],
69
+ [1,2,TranslationRepetitionSet.new(1,3,9,1,"STOP")]
70
+ ]
71
+ }.to raise_error GrammarError, "rule contains fewer parts than the TranslationRepetitionSet has for a translation: [3, 9, 1, \"STOP\"]"
72
+ expect{
73
+ @grammar.add_rules :one,
74
+ [ [Terminal.new(/a/), Terminal.new("b"), :two],
75
+ [1,4]
76
+ ]
77
+ }.to raise_error GrammarError, "rule contains fewer parts than translation number: 4"
78
+
79
+ expect( @grammar.rules.keys.size ).to equal 2
80
+ end
81
+
82
+ it "add several rules some with translations" do
83
+ expect( @grammar.rules.keys.size ).to equal 0
84
+ expect{
85
+ @grammar.add_rules :one,
86
+ [ [Terminal.new(/a/), Terminal.new("b"), :two, Repetition.new(",")],
87
+ [1,2,TranslationRepetitionSet.new(1,3,2,1,"STOP")]
88
+ ],
89
+ [ [:one, :three, :one],
90
+ [1,":",2]
91
+ ]
92
+ }.to_not raise_error
93
+ expect( @grammar.rules.keys.size ).to equal 3
94
+ end
95
+
96
+ context "bad rules and translations" do
97
+ it "does not add bad rules" do
98
+ expect( @grammar.rules.keys.size ).to equal 0
99
+ expect{
100
+ @grammar.add_rules :one,
101
+ [ ["BAD", :two, Repetition.new(",")],
102
+ [1,2,TranslationRepetitionSet.new(1,3,2,1,"STOP")]
103
+ ]
104
+ }.to raise_error
105
+ expect( @grammar.rules.keys.size ).to equal 2
106
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ ]
107
+ expect( @grammar.rules[:two].rules.map(&:rule) ).to match_array [ ]
108
+ end
109
+
110
+ it "does not rules when a bad rule is also given" do
111
+ expect( @grammar.rules.keys.size ).to equal 0
112
+ expect{
113
+ @grammar.add_rules :one,
114
+ [ [Terminal.new("GOOD")], [] ],
115
+ [ ["BAD", :two, Repetition.new(",")],
116
+ [1,2,TranslationRepetitionSet.new(1,3,2,1,"STOP")]
117
+ ]
118
+ }.to raise_error ArgumentError, "expected Terminal, NonTerminal or Repetition; got String"
119
+ expect( @grammar.rules.keys.size ).to equal 2
120
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ ]
121
+ expect( @grammar.rules[:two].rules.map(&:rule) ).to match_array [ ]
122
+ end
123
+
124
+ it "does not remove good rules already added" do
125
+ @terminal_a = Terminal.new("a")
126
+ expect{ @grammar.add_rules :one, [ [@terminal_a, :two], [] ] }.to_not raise_error
127
+ expect( @grammar.rules.keys.size ).to equal 2
128
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ [@terminal_a, @grammar.rules[:two]] ]
129
+ expect{
130
+ @grammar.add_rules :one,
131
+ [ ["BAD", Repetition.new(",")],
132
+ [1,2,TranslationRepetitionSet.new(1,2,1,"STOP")]
133
+ ]
134
+ }.to raise_error
135
+ expect( @grammar.rules.keys.size ).to equal 2
136
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ [@terminal_a, @grammar.rules[:two]] ]
137
+ end
138
+
139
+ it "does not add rules with bad translations" do
140
+ expect( @grammar.rules.keys.size ).to equal 0
141
+ expect{
142
+ @grammar.add_rules :one,
143
+ [ [Terminal.new("BAD")],
144
+ [1,2,3.14159]
145
+ ]
146
+ }.to raise_error
147
+ expect( @grammar.rules.keys.size ).to equal 1
148
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ ]
149
+ expect( @grammar.rules[:one].rules.map(&:translation) ).to match_array [ ]
150
+
151
+ expect{
152
+ @grammar.add_rules :one,
153
+ [ [Terminal.new("GOOD"), Terminal.new("GOOD"), Terminal.new("GOOD"), :two],
154
+ [1,2,TranslationRepetitionSet.new(1,3,2,1,"STOP")]
155
+ ]
156
+ }.to raise_error GrammarError
157
+ expect{
158
+ @grammar.add_rules :one,
159
+ [ [:two, Repetition.new(",")],
160
+ [1,2,TranslationRepetitionSet.new(1,9)]
161
+ ]
162
+ }.to raise_error GrammarError
163
+ expect{
164
+ @grammar.add_rules :one,
165
+ [ [:two, Repetition.new(",")],
166
+ [12]
167
+ ]
168
+ }.to raise_error GrammarError
169
+ end
170
+
171
+ it "does not add any rules when bad translations are also given" do
172
+ expect( @grammar.rules.keys.size ).to equal 0
173
+ expect{
174
+ @grammar.add_rules :one,
175
+ [ [Terminal.new("GOOD")], ["DONT LET ME IN"] ],
176
+ [ [Terminal.new("BAD")],
177
+ [1,2,6.011]
178
+ ]
179
+ }.to raise_error
180
+ expect( @grammar.rules.keys.size ).to equal 1
181
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ ]
182
+ expect( @grammar.rules[:one].rules.map(&:translation) ).to match_array [ ]
183
+ end
184
+
185
+ it "does not remove good rules and translations already added" do
186
+ @terminal_a = Terminal.new("a")
187
+ expect{ @grammar.add_rules :one, [ [@terminal_a, :two], [1,2] ] }.to_not raise_error
188
+ expect( @grammar.rules.keys.size ).to equal 2
189
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ [@terminal_a, @grammar.rules[:two]] ]
190
+ expect( @grammar.rules[:one].rules.map(&:translation) ).to match_array [ [1,2] ]
191
+ expect{
192
+ @grammar.add_rules :one,
193
+ [ [Terminal.new("BAD"), Repetition.new(",")],
194
+ [1,2,2.718]
195
+ ]
196
+ }.to raise_error
197
+ expect( @grammar.rules.keys.size ).to equal 2
198
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ [@terminal_a, @grammar.rules[:two]] ]
199
+ expect( @grammar.rules[:one].rules.map(&:translation) ).to match_array [ [1,2] ]
200
+ end
201
+ end
202
+ end
203
+
204
+ context ".clear_rule" do
205
+ it "wipes out the productions for a given nonterminal" do
206
+ @grammar = described_class.new
207
+ @terminal_a = Terminal.new("a")
208
+ expect{ @grammar.add_rules :one, [ [@terminal_a], [1] ] }.to_not raise_error
209
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array [ [@terminal_a] ]
210
+ expect( @grammar.rules[:one].rules.map(&:translation) ).to match_array [ [1] ]
211
+ expect{ @grammar.clear_rule :one }.to_not raise_error
212
+ expect( @grammar.rules[:one].rules.map(&:rule) ).to match_array []
213
+ expect( @grammar.rules[:one].rules.map(&:translation) ).to match_array []
214
+ end
215
+ end
216
+
217
+ context ".match?" do
218
+ before(:all) do
219
+ @terminal_a = Terminal.new("a")
220
+ @terminal_b = Terminal.new("b")
221
+ @repetition = Repetition.new(" and ")
222
+ end
223
+
224
+ context "simple grammar" do
225
+ before(:all) do
226
+ @simple_grammar = described_class.new
227
+ @simple_grammar.add_rules :one, [ [@terminal_a, :one], [] ]
228
+ @simple_grammar.add_rules :one, [ [@terminal_a], [] ]
229
+
230
+ @empty_grammar = described_class.new
231
+ @empty_grammar.add_rules :Z, [ [], [] ]
232
+ end
233
+
234
+ it{ expect( @simple_grammar.match?("a", "one") ).to be_truthy }
235
+ it{ expect( @simple_grammar.match?("a", :one) ).to be_truthy }
236
+ it{ expect( @simple_grammar.match?("aa", :one) ).to be_truthy }
237
+ it{ expect( @simple_grammar.match?("aaaa", :one) ).to be_truthy }
238
+ it{ expect( @simple_grammar.match?("aaaaaa", :one) ).to be_truthy }
239
+
240
+ it{ expect( @simple_grammar.match?("", "one") ).to be_falsey }
241
+ it{ expect( @simple_grammar.match?("ab", :one) ).to be_falsey }
242
+ it{ expect( @simple_grammar.match?(" a ", :one) ).to be_falsey }
243
+ it{ expect( @simple_grammar.match?("NO", :one) ).to be_falsey }
244
+
245
+ it{ expect( @empty_grammar.match?("", :Z) ).to be_truthy }
246
+ it{ expect( @empty_grammar.match?("NO", :Z) ).to be_falsey }
247
+ it{ expect( @empty_grammar.match?(" ", :Z) ).to be_falsey }
248
+ end
249
+
250
+ context "comlpex grammar" do
251
+ before(:all) do
252
+ @terminal_a = Terminal.new("a")
253
+ @terminal_b = Terminal.new("b")
254
+ @repetition = Repetition.new(" and ")
255
+ @complex_grammar = described_class.new
256
+ @grammar_with_self_referential_rule = described_class.new
257
+
258
+ @complex_grammar.add_rules :S, [ [@terminal_a, :A, @terminal_a, @repetition], [] ], [ [@terminal_a], [] ]
259
+ @complex_grammar.add_rules :A, [ [@terminal_b, :S, @terminal_b], [] ], [ [], [] ]
260
+
261
+ #the problem with the rule below, the nonterminal keeps going down forever, maybe add something that stops it, or start
262
+ #unwinding the stack once it goes too deep. Either way, not all legal grammars will be able to be processed.
263
+ #Or maybe make it illegal to have a nonterminal as the first (which should still be a legal production, but makes
264
+ #creating a solution easier)
265
+ @grammar_with_self_referential_rule.add_rules :S, [ [@terminal_a, :S, @terminal_a], [] ], [ [@terminal_a], [] ] , [ [:S, @repetition], [] ]
266
+ #@complex_grammar2.add_rules :A, [ [@terminal_b, :S, @terminal_b], [] ], [ [], [] ]
267
+ end
268
+
269
+ it{ expect( @complex_grammar.match?("a", :S) ).to be_truthy }
270
+ it{ expect( @complex_grammar.match?("aa", :S) ).to be_truthy }
271
+ it{ expect( @complex_grammar.match?("ababa", :S) ).to be_truthy }
272
+ it{ expect( @complex_grammar.match?("ababaababa", :S) ).to be_truthy }
273
+ it{ expect( @complex_grammar.match?("aa and ababababa", :S) ).to be_truthy }
274
+ it{ expect( @complex_grammar.match?("aa and aa", :S) ).to be_truthy }
275
+ it{ expect( @complex_grammar.match?("ababa", :S) ).to be_truthy }
276
+
277
+ #Bug
278
+ it{ expect{ @grammar_with_self_referential_rule.match?("a", :S) }.to raise_error(SystemStackError) }
279
+ #it{ expect( @grammar_with_self_referential_rule.match?("aa", :S) ).to be_truthy }
280
+ #it{ expect( @grammar_with_self_referential_rule.match?("ababa", :S) ).to be_truthy }
281
+ #it{ expect( @grammar_with_self_referential_rule.match?("ababaababa", :S) ).to be_truthy }
282
+ #it{ expect( @grammar_with_self_referential_rule.match?("aa and ababababa", :S) ).to be_truthy }
283
+ #it{ expect( @grammar_with_self_referential_rule.match?("aa and a", :S) ).to be_truthy }
284
+ #it{ expect( @grammar_with_self_referential_rule.match?("ababa", :S) ).to be_truthy }
285
+
286
+ it{ expect( @complex_grammar.match?("", :S) ).to be_falsey }
287
+ it{ expect( @complex_grammar.match?("a but also stuff that isn't in the grammar", :S) ).to be_falsey }
288
+ it{ expect( @complex_grammar.match?(" ababa", :S) ).to be_falsey }
289
+ end
290
+ end
291
+
292
+ context ".translate" do
293
+ before(:all) do
294
+ @terminal_a = Terminal.new("a")
295
+ @terminal_b = Terminal.new("b")
296
+ @repetition = Repetition.new(" and ")
297
+ end
298
+
299
+ context "simple grammar - no actual translations" do
300
+ before(:all) do
301
+ @simple_grammar = described_class.new
302
+ @simple_grammar.add_rules :one, [ [@terminal_a, :one], [] ]
303
+ @simple_grammar.add_rules :one, [ [@terminal_a], [] ]
304
+
305
+ @empty_grammar = described_class.new
306
+ @empty_grammar.add_rules :Z, [ [], [] ]
307
+ end
308
+
309
+ it{ expect( @simple_grammar.translate("a", "one") ).to match_array [ ["a", ""] ] }
310
+ it{ expect( @simple_grammar.translate("a", :one) ).to match_array [ ["a", ""] ] }
311
+ it{ expect( @simple_grammar.translate("aa", :one) ).to match_array [ ["aa", ""] ] }
312
+ it{ expect( @simple_grammar.translate("aaaa", :one) ).to match_array [ ["aaaa", ""] ] }
313
+ it{ expect( @simple_grammar.translate("aaaaaa", :one) ).to match_array [ ["aaaaaa", ""] ] }
314
+
315
+ it{ expect( @simple_grammar.translate("", "one") ).to match_array [ ] }
316
+ it{ expect( @simple_grammar.translate("ab", :one) ).to match_array [ ] }
317
+ it{ expect( @simple_grammar.translate(" a ", :one) ).to match_array [ ] }
318
+ it{ expect( @simple_grammar.translate("NO", :one) ).to match_array [ ] }
319
+
320
+ it{ expect( @empty_grammar.translate("", :Z) ).to match_array [ ["",""] ] }
321
+ it{ expect( @empty_grammar.translate("NO", :Z) ).to match_array [ ] }
322
+ it{ expect( @empty_grammar.translate(" ", :Z) ).to match_array [ ] }
323
+ end
324
+
325
+ context "comlpex grammar - no actual translations" do
326
+ before(:all) do
327
+ @terminal_a = Terminal.new("a")
328
+ @terminal_b = Terminal.new("b")
329
+ @repetition = Repetition.new(" and ")
330
+ @complex_grammar = described_class.new
331
+ @grammar_with_self_referential_rule = described_class.new
332
+
333
+ @complex_grammar.add_rules :S, [ [@terminal_a, :A, @terminal_a, @repetition], [] ], [ [@terminal_a], [] ]
334
+ @complex_grammar.add_rules :A, [ [@terminal_b, :S, @terminal_b], [] ], [ [], [] ]
335
+
336
+ #the problem with the rule below, the nonterminal keeps going down forever, maybe add something that stops it, or start
337
+ #unwinding the stack once it goes too deep. Either way, not all legal grammars will be able to be processed.
338
+ #Or maybe make it illegal to have a nonterminal as the first (which should still be a legal production, but makes
339
+ #creating a solution easier)
340
+ @grammar_with_self_referential_rule.add_rules :S, [ [@terminal_a, :S, @terminal_a], [] ], [ [@terminal_a], [] ] , [ [:S, @repetition], [] ]
341
+ #@complex_grammar2.add_rules :A, [ [@terminal_b, :S, @terminal_b], [] ], [ [], [] ]
342
+ end
343
+
344
+ it{ expect( @complex_grammar.translate("a", :S) ).to match_array [ ["a",""] ] }
345
+ it{ expect( @complex_grammar.translate("aa", :S) ).to match_array [ ["aa",""] ] }
346
+ it{ expect( @complex_grammar.translate("ababa", :S) ).to match_array [ ["ababa",""] ] }
347
+ it{ expect( @complex_grammar.translate("ababaababa", :S) ).to match_array [ ["ababaababa",""] ] }
348
+ it{ expect( @complex_grammar.translate("aa and ababababa", :S) ).to match_array [ ["aa and ababababa",""] ] }
349
+ it{ expect( @complex_grammar.translate("aa and aa", :S) ).to match_array [ ["aa and aa",""] ] }
350
+
351
+ #Bug
352
+ it{ expect{ @grammar_with_self_referential_rule.translate("a", :S) }.to raise_error(SystemStackError) }
353
+
354
+ it{ expect( @complex_grammar.translate("", :S) ).to match_array [] }
355
+ it{ expect( @complex_grammar.translate("a but also stuff that isn't in the grammar", :S) ).to match_array [] }
356
+ it{ expect( @complex_grammar.translate(" ababa", :S) ).to match_array [] }
357
+ end
358
+
359
+ context "simple grammar - translations" do
360
+ before(:all) do
361
+ #@tx_rep_set = TranslationRepetitionSet.new(1, ":", 2, 1, ";")
362
+
363
+ @simple_grammar = described_class.new
364
+ @simple_grammar.add_rules :one, [ [@terminal_a, :one], [2,1] ]
365
+ @simple_grammar.add_rules :one, [ [@terminal_a], ["IWasTheLastA"] ]
366
+
367
+ @empty_grammar = described_class.new
368
+ @empty_grammar.add_rules :Z, [ [], ["Translate nothing"] ]
369
+ end
370
+
371
+ it{ expect( @simple_grammar.translate("a", "one") ).to match_array [ ["IWasTheLastA", ""] ] }
372
+ it{ expect( @simple_grammar.translate("a", :one) ).to match_array [ ["IWasTheLastA", ""] ] }
373
+ it{ expect( @simple_grammar.translate("aa", :one) ).to match_array [ ["IWasTheLastAa", ""] ] }
374
+ it{ expect( @simple_grammar.translate("aaaa", :one) ).to match_array [ ["IWasTheLastAaaa", ""] ] }
375
+ it{ expect( @simple_grammar.translate("aaaaaa", :one) ).to match_array [ ["IWasTheLastAaaaaa", ""] ] }
376
+
377
+ it{ expect( @simple_grammar.translate("", "one") ).to match_array [ ] }
378
+ it{ expect( @simple_grammar.translate("ab", :one) ).to match_array [ ] }
379
+ it{ expect( @simple_grammar.translate(" a ", :one) ).to match_array [ ] }
380
+ it{ expect( @simple_grammar.translate("NO", :one) ).to match_array [ ] }
381
+
382
+ it{ expect( @empty_grammar.translate("", :Z) ).to match_array [ ["Translate nothing",""] ] }
383
+ it{ expect( @empty_grammar.translate("NO", :Z) ).to match_array [ ] }
384
+ it{ expect( @empty_grammar.translate(" ", :Z) ).to match_array [ ] }
385
+ end
386
+
387
+ context "comlpex grammar - translations" do
388
+ before(:all) do
389
+ @tx_rep_set = TranslationRepetitionSet.new(3, "[", 4,"&",3,1,"&",2, "]")
390
+
391
+ @terminal_a = Terminal.new("a")
392
+ @terminal_b = Terminal.new("b")
393
+ @repetition = Repetition.new(" and ")
394
+ @complex_grammar = described_class.new
395
+ @grammar_with_self_referential_rule = described_class.new
396
+
397
+ @complex_grammar.add_rules :S, [ [@terminal_a, :A, @terminal_a, @repetition], [1,3,4," ",2,":",@tx_rep_set] ], [ [@terminal_a], ["IWasALoneA"] ]
398
+ @complex_grammar.add_rules :A, [ [@terminal_b, :S, @terminal_b], ["<",2,">"] ], [ [], ["-IWasNothing-"] ]
399
+
400
+ #Add self referential grammar and grammar with rules having nonterminals as the first thing in a production
401
+ #When that bug is fixed
402
+ @grammar_with_self_referential_rule.add_rules :S, [ [@terminal_a, :S, @terminal_a], [2] ], [ [@terminal_a], ["JustAnA"] ] , [ [:S, @repetition], [1,2] ]
403
+ end
404
+
405
+ it{ expect( @complex_grammar.translate("a", :S) ).to match_array [ ["IWasALoneA",""] ] }
406
+ it{ expect( @complex_grammar.translate("aa", :S) ).to match_array [ ["aa -IWasNothing-:",""] ] }
407
+ it{ expect( @complex_grammar.translate("ababa", :S) ).to match_array [ ["aa <IWasALoneA>:",""] ] }
408
+ it{ expect( @complex_grammar.translate("ababaababa", :S) ).to match_array [ ["aa <aa <aa -IWasNothing-:>:>:", ""] ] }
409
+ it{ expect( @complex_grammar.translate("aa and ababababa", :S) ).to match_array [ ["aa and -IWasNothing-:", ""] ] }
410
+ it{ expect( @complex_grammar.translate("aa and aa and ababa and aa and ababa and ababaababa", :S) ).to match_array [
411
+ ["aa and -IWasNothing-:[ and &aa&<IWasALoneA>][ and &aa&-IWasNothing-][ and &aa&<IWasALoneA>][&aa&<aa <aa -IWasNothing-:>:>]", ""]
412
+ ] }
413
+
414
+ #Bug
415
+ it{ expect{ @grammar_with_self_referential_rule.translate("a", :S) }.to raise_error(SystemStackError) }
416
+
417
+ it{ expect( @complex_grammar.translate("", :S) ).to match_array [] }
418
+ it{ expect( @complex_grammar.translate("a but also stuff that isn't in the grammar", :S) ).to match_array [] }
419
+ it{ expect( @complex_grammar.translate(" ababa", :S) ).to match_array [] }
420
+ end
421
+ end
422
+
423
+ end