ruby-marc-spec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +18 -0
  3. data/.gitignore +388 -0
  4. data/.gitmodules +3 -0
  5. data/.idea/codeStyles/codeStyleConfig.xml +5 -0
  6. data/.idea/go.imports.xml +6 -0
  7. data/.idea/inspectionProfiles/Project_Default.xml +23 -0
  8. data/.idea/marc_spec.iml +102 -0
  9. data/.idea/misc.xml +6 -0
  10. data/.idea/modules.xml +8 -0
  11. data/.idea/templateLanguages.xml +6 -0
  12. data/.idea/vcs.xml +7 -0
  13. data/.rubocop.yml +269 -0
  14. data/.ruby-version +1 -0
  15. data/.simplecov +8 -0
  16. data/CHANGES.md +3 -0
  17. data/Gemfile +6 -0
  18. data/LICENSE.md +21 -0
  19. data/README.md +172 -0
  20. data/Rakefile +20 -0
  21. data/lib/.rubocop.yml +5 -0
  22. data/lib/marc/spec/module_info.rb +14 -0
  23. data/lib/marc/spec/parsing/closed_int_range.rb +28 -0
  24. data/lib/marc/spec/parsing/closed_lc_alpha_range.rb +28 -0
  25. data/lib/marc/spec/parsing/parser.rb +213 -0
  26. data/lib/marc/spec/parsing.rb +1 -0
  27. data/lib/marc/spec/queries/al_num_range.rb +105 -0
  28. data/lib/marc/spec/queries/applicable.rb +18 -0
  29. data/lib/marc/spec/queries/character_spec.rb +81 -0
  30. data/lib/marc/spec/queries/comparison_string.rb +45 -0
  31. data/lib/marc/spec/queries/condition.rb +133 -0
  32. data/lib/marc/spec/queries/condition_context.rb +49 -0
  33. data/lib/marc/spec/queries/dsl.rb +80 -0
  34. data/lib/marc/spec/queries/indicator_value.rb +77 -0
  35. data/lib/marc/spec/queries/operator.rb +129 -0
  36. data/lib/marc/spec/queries/part.rb +63 -0
  37. data/lib/marc/spec/queries/position.rb +59 -0
  38. data/lib/marc/spec/queries/position_or_range.rb +27 -0
  39. data/lib/marc/spec/queries/query.rb +94 -0
  40. data/lib/marc/spec/queries/query_executor.rb +52 -0
  41. data/lib/marc/spec/queries/selector.rb +12 -0
  42. data/lib/marc/spec/queries/subfield.rb +88 -0
  43. data/lib/marc/spec/queries/subfield_value.rb +63 -0
  44. data/lib/marc/spec/queries/tag.rb +107 -0
  45. data/lib/marc/spec/queries/transform.rb +154 -0
  46. data/lib/marc/spec/queries.rb +1 -0
  47. data/lib/marc/spec.rb +32 -0
  48. data/rakelib/.rubocop.yml +19 -0
  49. data/rakelib/bundle.rake +8 -0
  50. data/rakelib/coverage.rake +11 -0
  51. data/rakelib/gem.rake +54 -0
  52. data/rakelib/parser_specs/formatter.rb +31 -0
  53. data/rakelib/parser_specs/parser_specs.rb.txt.erb +35 -0
  54. data/rakelib/parser_specs/rule.rb +95 -0
  55. data/rakelib/parser_specs/suite.rb +91 -0
  56. data/rakelib/parser_specs/test.rb +97 -0
  57. data/rakelib/parser_specs.rb +1 -0
  58. data/rakelib/rubocop.rake +18 -0
  59. data/rakelib/spec.rake +27 -0
  60. data/ruby-marc-spec.gemspec +42 -0
  61. data/spec/.rubocop.yml +46 -0
  62. data/spec/README.md +16 -0
  63. data/spec/data/b23161018-sru.xml +182 -0
  64. data/spec/data/sandburg.xml +82 -0
  65. data/spec/generated/char_indicator_spec.rb +174 -0
  66. data/spec/generated/char_spec.rb +113 -0
  67. data/spec/generated/comparison_string_spec.rb +74 -0
  68. data/spec/generated/field_tag_spec.rb +156 -0
  69. data/spec/generated/index_char_spec.rb +669 -0
  70. data/spec/generated/index_indicator_spec.rb +174 -0
  71. data/spec/generated/index_spec.rb +113 -0
  72. data/spec/generated/index_sub_spec_spec.rb +1087 -0
  73. data/spec/generated/indicators_spec.rb +75 -0
  74. data/spec/generated/position_or_range_spec.rb +110 -0
  75. data/spec/generated/sub_spec_spec.rb +208 -0
  76. data/spec/generated/sub_spec_sub_spec_spec.rb +1829 -0
  77. data/spec/generated/subfield_char_spec.rb +405 -0
  78. data/spec/generated/subfield_range_range_spec.rb +48 -0
  79. data/spec/generated/subfield_range_spec.rb +87 -0
  80. data/spec/generated/subfield_range_sub_spec_spec.rb +214 -0
  81. data/spec/generated/subfield_tag_range_spec.rb +477 -0
  82. data/spec/generated/subfield_tag_sub_spec_spec.rb +3216 -0
  83. data/spec/generated/subfield_tag_tag_spec.rb +5592 -0
  84. data/spec/marc/spec/parsing/closed_int_range_spec.rb +49 -0
  85. data/spec/marc/spec/parsing/closed_lc_alpha_range_spec.rb +49 -0
  86. data/spec/marc/spec/parsing/parser_spec.rb +545 -0
  87. data/spec/marc/spec/queries/al_num_range_spec.rb +114 -0
  88. data/spec/marc/spec/queries/character_spec_spec.rb +28 -0
  89. data/spec/marc/spec/queries/comparison_string_spec.rb +28 -0
  90. data/spec/marc/spec/queries/indicator_value_spec.rb +28 -0
  91. data/spec/marc/spec/queries/query_spec.rb +200 -0
  92. data/spec/marc/spec/queries/subfield_spec.rb +92 -0
  93. data/spec/marc/spec/queries/subfield_value_spec.rb +31 -0
  94. data/spec/marc/spec/queries/tag_spec.rb +144 -0
  95. data/spec/marc/spec/queries/transform_spec.rb +459 -0
  96. data/spec/marc_spec_spec.rb +247 -0
  97. data/spec/scratch_spec.rb +112 -0
  98. data/spec/spec_helper.rb +23 -0
  99. metadata +341 -0
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ module MARC::Spec
5
+ module Parsing
6
+ describe ClosedIntRange do
7
+ attr_reader :range
8
+
9
+ before(:each) do
10
+ @range = ClosedIntRange.new
11
+ end
12
+
13
+ it 'parses a valid range' do
14
+ valid_ranges = %w[
15
+ 0-1
16
+ 1-2
17
+ 12-345
18
+ 67890-123456
19
+ ]
20
+ aggregate_failures 'valid ranges' do
21
+ valid_ranges.each do |r|
22
+ expect(range).to parse(r)
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'does not parse an invalid range' do
28
+ invalid_ranges = %w[
29
+ 1-0
30
+ 2-1
31
+ 345-12
32
+ 123456-67890
33
+ ]
34
+ aggregate_failures 'invalid ranges' do
35
+ invalid_ranges.each do |r|
36
+ expect(range).not_to parse(r)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe :to_s do
42
+ it 'accepts a precedece argument' do
43
+ prec = Parslet::Atoms::Precedence::OUTER
44
+ expect(range.to_s(prec)).to eq('ClosedIntRange')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ module MARC::Spec
5
+ module Parsing
6
+ describe ClosedLcAlphaRange do
7
+ attr_reader :range
8
+
9
+ before(:each) do
10
+ @range = ClosedLcAlphaRange.new
11
+ end
12
+
13
+ it 'parses a valid range' do
14
+ valid_ranges = %w[
15
+ a-b
16
+ c-d
17
+ e-f
18
+ g-z
19
+ ]
20
+ aggregate_failures 'valid ranges' do
21
+ valid_ranges.each do |r|
22
+ expect(range).to parse(r)
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'does not parse an invalid range' do
28
+ invalid_ranges = %w[
29
+ z-a
30
+ b-a
31
+ d-c
32
+ x-f
33
+ ]
34
+ aggregate_failures 'invalid ranges' do
35
+ invalid_ranges.each do |r|
36
+ expect(range).not_to parse(r)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe :to_s do
42
+ it 'accepts a precedece argument' do
43
+ prec = Parslet::Atoms::Precedence::OUTER
44
+ expect(range.to_s(prec)).to eq('ClosedLcAlphaRange')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,545 @@
1
+ require 'spec_helper'
2
+ require 'parslet/rig/rspec'
3
+
4
+ # noinspection RubyResolve
5
+ module MARC::Spec
6
+ module Parsing
7
+ include MARC::Spec::Queries::Part
8
+
9
+ describe Parser do
10
+ let(:parser) { Parser.new }
11
+ let(:tags) { %w[245 2.. 24. .45 ..5 ... LDR] }
12
+ let(:positions) { %w[0 3 5] }
13
+ let(:pos_ranges) { [%w[1 3], %w[1 #], %w[# 1], %w[2 4], %w[# #]] }
14
+ let(:codes) { %w[b 2 $ \\ { }] }
15
+ let(:code_ranges) { [%w[a c], %w[1 3]] }
16
+ let(:inds) { %w[1 2] }
17
+
18
+ let(:r) { Parslet::ErrorReporter::Deepest.new }
19
+
20
+ # TODO: clean this up
21
+ # before(:all) do
22
+ # @all_parses = {}
23
+ # end
24
+ #
25
+ # after(:all) do
26
+ # parse_list = @all_parses.map do |val, parse_tree|
27
+ # [val.inspect, parse_tree.inspect].join(' => ')
28
+ # end
29
+ # parse_list.sort!
30
+ # parse_list.uniq!
31
+ # puts "{\n#{parse_list.join(",\n")}\n}"
32
+ # end
33
+
34
+ def parse_with(rule, value)
35
+ rule.parse(value).tap do |result|
36
+ # @all_parses[value] = result
37
+ end
38
+ end
39
+
40
+ describe :field_tag do
41
+ let(:rule) { parser.field_tag }
42
+
43
+ it 'parses tags' do
44
+ aggregate_failures do
45
+ tags.each do |tag|
46
+ expect(rule).to parse(tag)
47
+ result = parse_with(rule, tag)
48
+ expect(result).to eq(tag)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ describe :position do
55
+ let(:rule) { parser.position }
56
+
57
+ it 'parses positions' do
58
+ aggregate_failures do
59
+ %w[0 7 17 317].each do |pos|
60
+ expect(rule).to parse(pos, reporter: r)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ describe :position_or_range do
67
+ let(:rule) { parser.position_or_range }
68
+
69
+ it 'parses positions' do
70
+ aggregate_failures do
71
+ positions.each do |pos|
72
+ expect(rule).to parse(pos, reporter: r)
73
+ result = parse_with(rule, pos)
74
+ expect(result[:pos]).to eq(pos)
75
+ end
76
+ end
77
+ end
78
+
79
+ it 'parses ranges' do
80
+ aggregate_failures do
81
+ pos_ranges.each do |from, to|
82
+ val = "#{from}-#{to}"
83
+ expect(rule).to parse(val, reporter: r)
84
+ result = parse_with(rule, val)
85
+ expect(result[:from]).to eq(from == '#' ? nil : from)
86
+ expect(result[:to]).to eq(to == '#' ? nil : to)
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ describe :character_spec do
93
+ let(:rule) { parser.character_spec }
94
+
95
+ it 'parses positions' do
96
+ aggregate_failures do
97
+ positions.each do |pos|
98
+ val = "/#{pos}"
99
+ expect(rule).to parse(val, reporter: r)
100
+ result = parse_with(rule, val)
101
+ character_spec = result[:character_spec]
102
+ expect(character_spec[:pos]).to eq(pos)
103
+ end
104
+ end
105
+ end
106
+
107
+ it 'parses ranges' do
108
+ aggregate_failures do
109
+ pos_ranges.each do |from, to|
110
+ val = "/#{from}-#{to}"
111
+ expect(rule).to parse(val, reporter: r)
112
+ result = parse_with(rule, val)
113
+ character_spec = result[:character_spec]
114
+ expect(character_spec[:from]).to eq(from == '#' ? nil : from)
115
+ expect(character_spec[:to]).to eq(to == '#' ? nil : to)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe :field_spec do
122
+ let(:rule) { parser.field_spec }
123
+
124
+ it 'parses tags' do
125
+ aggregate_failures do
126
+ tags.each do |tag|
127
+ expect(rule).to parse(tag)
128
+ result = parse_with(rule, tag)
129
+ expect(result[:tag]).to eq(tag)
130
+ end
131
+ end
132
+ end
133
+
134
+ describe 'with substrings' do
135
+ it 'parses positions' do
136
+ aggregate_failures do
137
+ tags.each do |tag|
138
+ positions.each do |pos|
139
+ val = "#{tag}/#{pos}"
140
+ expect(rule).to parse(val, reporter: r)
141
+ result = parse_with(rule, val)
142
+ expect(result[:tag]).to eq(tag)
143
+ selector = result[:selector]
144
+ character_spec = selector[:character_spec]
145
+ expect(character_spec[:pos]).to eq(pos)
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ it 'parses ranges' do
152
+ aggregate_failures do
153
+ tags.each do |tag|
154
+ pos_ranges.each do |from, to|
155
+ val = "#{tag}/#{from}-#{to}"
156
+ expect(rule).to parse(val, reporter: r)
157
+ result = parse_with(rule, val)
158
+ expect(result[:tag]).to eq(tag)
159
+ selector = result[:selector]
160
+ character_spec = selector[:character_spec]
161
+ expect(character_spec[:from]).to eq(from == '#' ? nil : from)
162
+ expect(character_spec[:to]).to eq(to == '#' ? nil : to)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ describe 'with indices' do
170
+ it 'parses positions' do
171
+ aggregate_failures do
172
+ tags.each do |tag|
173
+ positions.each do |pos_index|
174
+ val = "#{tag}[#{pos_index}]"
175
+ expect(rule).to parse(val, reporter: r)
176
+ result = parse_with(rule, val)
177
+ expect(result[:tag]).to eq(tag)
178
+ index = result[:index]
179
+ expect(index[:pos]).to eq(pos_index)
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ it 'parses ranges' do
186
+ aggregate_failures do
187
+ tags.each do |tag|
188
+ pos_ranges.each do |from_index, to_index|
189
+ val = "#{tag}[#{from_index}-#{to_index}]"
190
+ expect(rule).to parse(val, reporter: r)
191
+ result = parse_with(rule, val)
192
+ expect(result[:tag]).to eq(tag)
193
+ index = result[:index]
194
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
195
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ describe :subfield_code do
204
+ let(:rule) { parser.subfield_code }
205
+
206
+ it 'parses codes' do
207
+ aggregate_failures do
208
+ codes.each do |code|
209
+ val = "$#{code}"
210
+ expect(rule).to parse(val, reporter: r)
211
+ expect(parse_with(rule, val)).to eq(code)
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ describe :subfield_spec do
218
+ let(:rule) { parser.subfield_spec }
219
+
220
+ it 'parses codes' do
221
+ aggregate_failures do
222
+ tags.each do |tag|
223
+ codes.each do |code|
224
+ val = "#{tag}$#{code}"
225
+ expect(rule).to parse(val, reporter: r)
226
+ result = parse_with(rule, val)
227
+ expect(result[:tag]).to eq(tag)
228
+ selector = result[:selector]
229
+ expect(selector).to eq({ code: code })
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ it 'parses code ranges' do
236
+ aggregate_failures do
237
+ tags.each do |tag|
238
+ code_ranges.each do |from, to|
239
+ val = "#{tag}$#{from}-#{to}"
240
+ expect(rule).to parse(val, reporter: r)
241
+ result = parse_with(rule, val)
242
+ expect(result[:tag]).to eq(tag)
243
+
244
+ selector = result[:selector]
245
+ expect(selector).not_to be_nil
246
+
247
+ code_range = selector[:code]
248
+ expect(code_range[:from]).to eq(from)
249
+ expect(code_range[:to]).to eq(to)
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ it 'parses codes with indices' do
256
+ aggregate_failures do
257
+ tags.each do |tag|
258
+ positions.each do |pos_index|
259
+ codes.each do |code|
260
+ val = "#{tag}[#{pos_index}]$#{code}"
261
+ expect(rule).to parse(val, reporter: r)
262
+ result = parse_with(rule, val)
263
+ expect(result[:tag]).to eq(tag)
264
+
265
+ index = result[:index]
266
+ expect(index[:pos]).to eq(pos_index)
267
+
268
+ selector = result[:selector]
269
+ expect(selector).not_to be_nil
270
+ expect(selector[:code]).to eq(code)
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ it 'parses code ranges with indices' do
278
+ pos_ranges.each do |from_index, to_index|
279
+ aggregate_failures do
280
+ tags.each do |tag|
281
+ code_ranges.each do |from, to|
282
+ val = "#{tag}[#{from_index}-#{to_index}]$#{from}-#{to}"
283
+ expect(rule).to parse(val, reporter: r)
284
+ result = parse_with(rule, val)
285
+ expect(result[:tag]).to eq(tag)
286
+
287
+ index = result[:index]
288
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
289
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
290
+
291
+ selector = result[:selector]
292
+ expect(selector).not_to be_nil
293
+
294
+ code_range = selector[:code]
295
+ expect(code_range[:from]).to eq(from)
296
+ expect(code_range[:to]).to eq(to)
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ describe :indicator_spec do
305
+ let(:rule) { parser.indicator_spec }
306
+
307
+ it 'parses indicators with plain tags' do
308
+ aggregate_failures do
309
+ tags.each do |tag|
310
+ inds.each do |ind|
311
+ val = "#{tag}^#{ind}"
312
+ expect(rule).to parse(val)
313
+ result = parse_with(rule, val)
314
+ expect(result[:tag]).to eq(tag)
315
+ selector = result[:selector]
316
+ expect(selector[:ind]).to eq(ind)
317
+ end
318
+ end
319
+ end
320
+ end
321
+
322
+ it 'parses indicators with position-indexed tags' do
323
+ aggregate_failures do
324
+ tags.each do |tag|
325
+ positions.each do |pos_index|
326
+ inds.each do |ind|
327
+ val = "#{tag}[#{pos_index}]^#{ind}"
328
+ result = parse_with(rule, val)
329
+ expect(result[:tag]).to eq(tag)
330
+ selector = result[:selector]
331
+ expect(selector[:ind]).to eq(ind)
332
+ index = result[:index]
333
+ expect(index[:pos]).to eq(pos_index)
334
+ end
335
+ end
336
+ end
337
+ end
338
+ end
339
+
340
+ it 'parses indicators with range-indexed tags' do
341
+ aggregate_failures do
342
+ tags.each do |tag|
343
+ pos_ranges.each do |from_index, to_index|
344
+ inds.each do |ind|
345
+ val = "#{tag}[#{from_index}-#{to_index}]^#{ind}"
346
+ result = parse_with(rule, val)
347
+ expect(result[:tag]).to eq(tag)
348
+ selector = result[:selector]
349
+ expect(selector[:ind]).to eq(ind)
350
+ index = result[:index]
351
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
352
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
353
+ end
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ end
360
+
361
+ describe :comparison_string do
362
+ let(:rule) { parser._comparison_string }
363
+
364
+ it 'parses a comparison string' do
365
+ expecteds = {
366
+ '\\svalue' => '\\svalue',
367
+ '\\\\svalue' => '\\svalue',
368
+ '\\value' => 'value',
369
+ '\\value\\!' => 'value\\!',
370
+ '\\!value' => '!value',
371
+ '\\\\!value' => '\\!value',
372
+ '\\help\\sI\\sam\\strapped\\sin\\sa\\sunit\\stest\\!\\sso\\sam\\sI' =>
373
+ 'help\\sI\\sam\\strapped\\sin\\sa\\sunit\\stest\\!\\sso\\sam\\sI',
374
+ '\\a\\{b\\}\\$1\\\\23\\=\\~\\|\\?' => 'a\\{b\\}\\$1\\\\23\\=\\~\\|\\?'
375
+ }
376
+ aggregate_failures do
377
+ expecteds.each do |val, expected|
378
+ expect(rule).to parse(val, reporter: r)
379
+ result = parse_with(rule, val)
380
+ expect(result).to be_a(Hash)
381
+ next unless result.is_a?(Hash)
382
+
383
+ cstr = result[:comparison_string]
384
+ expect(cstr).to eq(expected)
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ describe :sub_spec do
391
+ let(:rule) { parser.sub_spec }
392
+
393
+ it 'parses a condition' do
394
+ expecteds = {
395
+ '{/#=\\/}' => {
396
+ left: {
397
+ selector: {
398
+ character_spec: {
399
+ pos: '#'
400
+ }
401
+ }
402
+ },
403
+ operator: '=',
404
+ right: {
405
+ comparison_string: '/'
406
+ }
407
+ }
408
+ }
409
+ aggregate_failures do
410
+ expecteds.each do |val, expected|
411
+ expect(rule).to parse(val, reporter: r)
412
+ result = parse_with(rule, val)
413
+ expect(result).to eq(expected)
414
+ end
415
+ end
416
+ end
417
+ end
418
+
419
+ describe 'with multiple subfields' do
420
+ it 'parses subfield code lists' do
421
+ aggregate_failures do
422
+ tags.each do |tag|
423
+ code_list = codes.map { |c| "$#{c}" }.join
424
+ val = "#{tag}#{code_list}"
425
+ expect(parser).to parse(val, reporter: r)
426
+ result = parse_with(parser, val)
427
+
428
+ expect(result[:tag]).to eq(tag)
429
+
430
+ subqueries = result[:subqueries]
431
+ expect(subqueries.size).to eq(codes.size)
432
+ subqueries.each_with_index do |subquery, i|
433
+ selector = subquery[:selector]
434
+ subfield = selector
435
+ expect(subfield[:code]).to eq(codes[i])
436
+ end
437
+ end
438
+ end
439
+ end
440
+
441
+ it 'handles indices' do
442
+ aggregate_failures do
443
+ tags.each do |tag|
444
+ pos_ranges.each do |from_index, to_index|
445
+ code_list = codes.map { |c| "$#{c}" }.join
446
+ val = "#{tag}[#{from_index}-#{to_index}]#{code_list}"
447
+ expect(parser).to parse(val, reporter: r)
448
+ result = parse_with(parser, val)
449
+
450
+ expect(result[:tag]).to eq(tag)
451
+ index = result[:index]
452
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
453
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
454
+
455
+ subqueries = result[:subqueries]
456
+ expect(subqueries.size).to eq(codes.size)
457
+ subqueries.each_with_index do |subquery, i|
458
+ selector = subquery[:selector]
459
+ subfield = selector
460
+ expect(subfield[:code]).to eq(codes[i])
461
+ end
462
+ end
463
+ end
464
+ end
465
+ end
466
+
467
+ it 'parses subfield code lists with conditions' do
468
+ aggregate_failures do
469
+ tags.each do |tag|
470
+ pos_ranges.each do |from_index, to_index|
471
+ code_list = codes.map.with_index { |c, i| "$#{c}{~\\ok#{i}}" }.join
472
+ val = "#{tag}[#{from_index}-#{to_index}]#{code_list}"
473
+ expect(parser).to parse(val, reporter: r)
474
+ result = parse_with(parser, val)
475
+
476
+ expect(result[:tag]).to eq(tag)
477
+ index = result[:index]
478
+ expect(index[:from]).to eq(from_index == '#' ? nil : from_index)
479
+ expect(index[:to]).to eq(to_index == '#' ? nil : to_index)
480
+
481
+ subqueries = result[:subqueries]
482
+ expect(subqueries.size).to eq(codes.size)
483
+ subqueries.each_with_index do |subquery, i|
484
+ selector = subquery[:selector]
485
+ subfield = selector
486
+ expect(subfield[:code]).to eq(codes[i])
487
+ condition = subquery[:condition]
488
+ expect(condition[:operator]).to eq('~')
489
+ right_operand = condition[:right]
490
+ expect(right_operand[:comparison_string]).to eq("ok#{i}")
491
+ end
492
+ end
493
+ end
494
+ end
495
+ end
496
+
497
+ it 'handles complex combinations of subfields and subspecs' do
498
+ val = '880$a{?$f}$b$c$e{$f=\\q}'
499
+
500
+ expect(parser).to parse(val, reporter: r)
501
+ result = parse_with(parser, val)
502
+ expect(result[:tag]).to eq('880')
503
+
504
+ subqueries = result[:subqueries]
505
+ expect(subqueries.size).to eq(4)
506
+
507
+ expect(subqueries[0][:selector]).to eq({ code: 'a' })
508
+ condition0 = subqueries[0][:condition]
509
+ expect(condition0[:operator]).to eq('?')
510
+ expect(condition0[:right][:selector]).to eq({ code: 'f' })
511
+
512
+ expect(subqueries[1][:selector]).to eq({ code: 'b' })
513
+
514
+ expect(subqueries[2][:selector]).to eq({ code: 'c' })
515
+
516
+ expect(subqueries[3][:selector]).to eq({ code: 'e' })
517
+ condition3 = subqueries[3][:condition]
518
+ expect(condition3[:left][:selector]).to eq({ code: 'f' })
519
+ expect(condition3[:operator]).to eq('=')
520
+ expect(condition3[:right]).to eq({ comparison_string: 'q' })
521
+ end
522
+ end
523
+
524
+ describe 'indicators with conditions' do
525
+ it 'handles plain tags' do
526
+ aggregate_failures do
527
+ tags.each do |tag|
528
+ inds.each do |ind|
529
+ val = "#{tag}^#{ind}{=\\a}"
530
+ expect(parser).to parse(val)
531
+ result = parse_with(parser, val)
532
+ expect(result[:tag]).to eq(tag)
533
+ selector = result[:selector]
534
+ expect(selector[:ind]).to eq(ind)
535
+ condition = result[:condition]
536
+ expect(condition[:operator]).to eq('=')
537
+ expect(condition[:right][:comparison_string]).to eq('a')
538
+ end
539
+ end
540
+ end
541
+ end
542
+ end
543
+ end
544
+ end
545
+ end