biodiversity 0.5.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1195 @@
1
+ # encoding: UTF-8
2
+ grammar ScientificNameClean
3
+
4
+ rule root
5
+ space a:scientific_name_5 space {
6
+ def value
7
+ a.value.gsub(/\s{2,}/, ' ').strip
8
+ end
9
+
10
+ def canonical
11
+ a.canonical.gsub(/\s{2,}/, ' ').strip
12
+ end
13
+
14
+ def pos
15
+ a.pos
16
+ end
17
+
18
+ def hybrid
19
+ a.hybrid
20
+ end
21
+
22
+ def details
23
+ a.details.class == Array ? a.details : [a.details]
24
+ end
25
+ }
26
+ end
27
+
28
+ rule scientific_name_5
29
+ a:scientific_name_1 space b:taxon_concept_rank space c:authorship {
30
+ def value
31
+ a.value + " " + b.apply(c)
32
+ end
33
+
34
+ def canonical
35
+ a.canonical
36
+ end
37
+
38
+ def pos
39
+ a.pos.merge(c.pos)
40
+ end
41
+
42
+ def hybrid
43
+ a.hybrid
44
+ end
45
+
46
+ def details
47
+ a.details.merge(b.details(c))
48
+ end
49
+ }
50
+ /
51
+ scientific_name_4
52
+ end
53
+
54
+ rule scientific_name_4
55
+ a:scientific_name_1 space hybrid_character space b:scientific_name_1 {
56
+ def value
57
+ a.value + " × " + b.value
58
+ end
59
+
60
+ def canonical
61
+ a.canonical + " " + b.canonical
62
+ end
63
+
64
+ def pos
65
+ a.pos.merge(b.pos)
66
+ end
67
+
68
+ def hybrid
69
+ true
70
+ end
71
+
72
+ def details
73
+ [a.details, b.details]
74
+ end
75
+ }
76
+ /
77
+ a:scientific_name_1 space hybrid_character space [\?]? {
78
+ def value
79
+ a.value + " × ?"
80
+ end
81
+
82
+ def canonical
83
+ a.canonical
84
+ end
85
+
86
+ def pos
87
+ a.pos
88
+ end
89
+
90
+ def hybrid
91
+ true
92
+ end
93
+
94
+ def details
95
+ [a.details, "?"]
96
+ end
97
+ }
98
+ /
99
+ scientific_name_3
100
+ end
101
+
102
+ rule scientific_name_3
103
+ a:hybrid_character space b:scientific_name_2 {
104
+ def value
105
+ a.value + " " + b.value
106
+ end
107
+
108
+ def canonical
109
+ b.canonical
110
+ end
111
+
112
+ def pos
113
+ b.pos
114
+ end
115
+
116
+ def hybrid
117
+ true
118
+ end
119
+
120
+ def details
121
+ b.details
122
+ end
123
+ }
124
+ /
125
+ scientific_name_2
126
+ end
127
+
128
+ rule scientific_name_2
129
+ a:scientific_name_1 space b:status_part {
130
+ def value
131
+ a.value + " " + b.value
132
+ end
133
+
134
+ def canonical
135
+ a.canonical
136
+ end
137
+
138
+ def pos
139
+ a.pos
140
+ end
141
+
142
+ def hybrid
143
+ a.hybrid rescue false
144
+ end
145
+
146
+ def details
147
+ a.details.merge(b.details)
148
+ end
149
+ }
150
+ /
151
+ scientific_name_1
152
+ end
153
+
154
+ rule scientific_name_1
155
+ multinomial_name
156
+ /
157
+ uninomial_name
158
+ end
159
+
160
+
161
+ rule status_part
162
+ a:status_word space b:status_part {
163
+ def value
164
+ a.value + " " + b.value
165
+ end
166
+ def details
167
+ {:status => value}
168
+ end
169
+ }
170
+ /
171
+ status_word
172
+ end
173
+
174
+ rule status_word
175
+ latin_word [\.] {
176
+ def value
177
+ text_value.strip
178
+ end
179
+ def details
180
+ {:status => value}
181
+ end
182
+ }
183
+ #/
184
+ #latin_word
185
+ end
186
+
187
+
188
+ rule multinomial_name
189
+ a:genus space b:subgenus space species_prefix? space c:species space_hard d:infraspecies_mult {
190
+ def value
191
+ a.value + " " + b.value + " " + c.value + " " + d.value
192
+ end
193
+
194
+ def canonical
195
+ a.canonical + " " + b.canonical + " " + c.canonical + " " + d.canonical
196
+ end
197
+
198
+ def pos
199
+ a.pos.merge(b.pos).merge(c.pos).merge(d.pos)
200
+ end
201
+
202
+ def hybrid
203
+ c.hybrid rescue false
204
+ end
205
+
206
+ def details
207
+ a.details.merge(b.details).merge(c.details).merge(d.details)
208
+ end
209
+ }
210
+ /
211
+ a:genus space b:subgenus space species_prefix? space c:species {
212
+ def value
213
+ a.value + " " + b.value + " " + c.value
214
+ end
215
+
216
+ def canonical
217
+ a.canonical + " " + c.canonical
218
+ end
219
+
220
+ def pos
221
+ a.pos.merge(b.pos).merge(c.pos)
222
+ end
223
+
224
+ def hybrid
225
+ c.hybrid rescue false
226
+ end
227
+
228
+ def details
229
+ a.details.merge(b.details).merge(c.details)
230
+ end
231
+ }
232
+ /
233
+ a:genus space species_prefix? space b:species space_hard c:infraspecies_mult {
234
+ def value
235
+ a.value + " " + b.value + " " + c.value
236
+ end
237
+
238
+ def canonical
239
+ a.canonical + " " + b.canonical + " " + c.canonical
240
+ end
241
+
242
+ def pos
243
+ a.pos.merge(b.pos).merge(c.pos)
244
+ end
245
+
246
+ def hybrid
247
+ b.hybrid rescue false
248
+ end
249
+
250
+ def details
251
+ a.details.merge(b.details).merge(c.details)
252
+ end
253
+ }
254
+ /
255
+ a:genus space species_prefix? space b:species {
256
+ def value
257
+ a.value + " " + b.value
258
+ end
259
+
260
+ def canonical
261
+ a.canonical + " " + b.canonical
262
+ end
263
+
264
+ def pos
265
+ a.pos.merge(b.pos)
266
+ end
267
+
268
+ def hybrid
269
+ b.hybrid rescue false
270
+ end
271
+
272
+ def details
273
+ a.details.merge(b.details)
274
+ end
275
+ }
276
+ end
277
+
278
+ rule infraspecies_mult
279
+ a:infraspecies space b:infraspecies_mult {
280
+ def value
281
+ a.value + " " + b.value
282
+ end
283
+
284
+ def canonical
285
+ a.canonical + " " + b.canonical
286
+ end
287
+
288
+ def pos
289
+ a.pos.merge(b.pos)
290
+ end
291
+
292
+ def details
293
+ a_array = a.details[:infraspecies].class == Array ? a.details[:infraspecies] : [a.details[:infraspecies]]
294
+ b_array = b.details[:infraspecies].class == Array ? b.details[:infraspecies] : [b.details[:infraspecies]]
295
+ a.details.merge({:infraspecies => a_array + b_array})
296
+ end
297
+ }
298
+ /
299
+ infraspecies {
300
+ def details
301
+ {:infraspecies => [super[:infraspecies]]}
302
+ end
303
+ }
304
+ end
305
+
306
+ rule infraspecies
307
+ a:infraspecies_epitheton space b:authorship {
308
+ def value
309
+ a.value + " " + b.value
310
+ end
311
+
312
+ def canonical
313
+ a.canonical
314
+ end
315
+
316
+ def pos
317
+ a.pos.merge(b.pos)
318
+ end
319
+
320
+ def details
321
+ {:infraspecies => a.details[:infraspecies].merge(b.details)}
322
+ end
323
+ }
324
+ /
325
+ infraspecies_epitheton
326
+ end
327
+
328
+ rule infraspecies_epitheton
329
+ sel:rank space_hard a:species_word {
330
+ def value
331
+ sel.apply(a)
332
+ end
333
+ def canonical
334
+ sel.canonical(a)
335
+ end
336
+
337
+ def pos
338
+ {a.interval.begin => ['infraspecies', a.interval.end]}
339
+ end
340
+
341
+ def details
342
+ sel.details(a)
343
+ end
344
+ }
345
+ /
346
+ species_word ![\.] {
347
+ def value
348
+ text_value
349
+ end
350
+
351
+ def canonical
352
+ value
353
+ end
354
+
355
+ def pos
356
+ {interval.begin => ['infraspecies', interval.end]}
357
+ end
358
+
359
+ def details
360
+ {:infraspecies => {:epitheton => value, :rank => 'n/a'}}
361
+ end
362
+ }
363
+ end
364
+
365
+ rule taxon_concept_rank
366
+ ("sec."/"sensu.") {
367
+ def value
368
+ "sec."
369
+ end
370
+ def apply(a)
371
+ " " + value + " " + a.value
372
+ end
373
+ def details(a = nil)
374
+ {:taxon_concept => a.details}
375
+ end
376
+ }
377
+ end
378
+
379
+ rule rank
380
+ ("morph."/"f.sp."/"B"/"ssp."/"mut."/"nat"/"nothosubsp."/"pseudovar."/"sect."/"ser."/"var."/"subvar."/ "[var.]" /"subsp."/"subf."/"race"/"α"
381
+ /"ββ"/"β"/"γ"/"δ"/"ε"/"φ"/"θ"/"μ"/"a."/"b."/"c."/"d."/"e."/"g."/"k."/"****"/"**"/"*")
382
+ {
383
+ def value
384
+ text_value.strip
385
+ end
386
+
387
+ def apply(a)
388
+ " " + text_value + " " + a.value
389
+ end
390
+
391
+ def canonical(a)
392
+ " " + a.value
393
+ end
394
+
395
+ def details(a = nil)
396
+ {:infraspecies => {:epitheton => (a.value rescue nil), :rank => text_value}}
397
+ end
398
+ }
399
+ /
400
+ rank_forma
401
+ end
402
+
403
+ rule rank_forma
404
+ ("forma"/"form."/"fo."/"f.")
405
+ {
406
+ def value
407
+ "f."
408
+ end
409
+ def apply(a)
410
+ " " + value + " " + a.value
411
+ end
412
+ def canonical(a)
413
+ " " + a.value
414
+ end
415
+ def details(a = nil)
416
+ {:infraspecies => {:epitheton => (a.value rescue nil), :rank => value}}
417
+ end
418
+ }
419
+ end
420
+
421
+ rule species
422
+ a:species_epitheton space b:authorship {
423
+ def value
424
+ a.value + " " + b.value
425
+ end
426
+
427
+ def canonical
428
+ a.canonical
429
+ end
430
+
431
+ def hybrid
432
+ a.hybrid rescue false
433
+ end
434
+
435
+ def pos
436
+ a.pos.merge(b.pos)
437
+ end
438
+
439
+ def details
440
+ {:species => a.details[:species].merge(b.details)}
441
+ end
442
+ }
443
+ /
444
+ species_epitheton
445
+ end
446
+
447
+ rule species_epitheton
448
+ a:species_word &(space_hard author_prefix_word space_hard) {
449
+ def value
450
+ a.value
451
+ end
452
+
453
+ def canonical
454
+ a.value
455
+ end
456
+
457
+ def hybrid
458
+ a.hybrid rescue false
459
+ end
460
+
461
+ def pos
462
+ {a.interval.begin => ['species', a.interval.end]}
463
+ end
464
+
465
+ def details
466
+ {:species => {:epitheton => a.value}}
467
+ end
468
+ }
469
+ /
470
+ species_word {
471
+ def canonical
472
+ value
473
+ end
474
+
475
+ def pos
476
+ {interval.begin => ['species', interval.end]}
477
+ end
478
+
479
+ def hybrid
480
+ false
481
+ end
482
+
483
+ def details
484
+ {:species => {:epitheton => value}}
485
+ end
486
+ }
487
+ /
488
+ species_word_hybrid
489
+ end
490
+
491
+ rule subgenus
492
+ left_paren space a:cap_latin_word space right_paren {
493
+ def value
494
+ "(" + a.value + ")"
495
+ end
496
+
497
+ def canonical
498
+ a.value
499
+ end
500
+
501
+ def pos
502
+ {a.interval.begin => ['subgenus', a.interval.end]}
503
+ end
504
+
505
+ def details
506
+ {:subgenus => {:epitheton => a.value}}
507
+ end
508
+ }
509
+ end
510
+
511
+ rule genus
512
+ a:cap_latin_word !(space_hard author_prefix_word space_hard author_word) {
513
+ def value
514
+ a.value
515
+ end
516
+
517
+ def pos
518
+ {a.interval.begin => ['genus', a.interval.end]}
519
+ end
520
+
521
+ def canonical
522
+ a.value
523
+ end
524
+
525
+ def details
526
+ {:genus => {:epitheton => a.value}}
527
+ end
528
+ }
529
+ end
530
+
531
+ rule uninomial_name
532
+ a:uninomial_epitheton space_hard b:authorship {
533
+ def value
534
+ a.value + " " + b.value
535
+ end
536
+
537
+ def canonical
538
+ a.canonical
539
+ end
540
+
541
+ def pos
542
+ a.pos.merge(b.pos)
543
+ end
544
+
545
+ def hybrid
546
+ false
547
+ end
548
+
549
+ def details
550
+ {:uninomial => a.details[:uninomial].merge(b.details)}
551
+ end
552
+ }
553
+ /
554
+ uninomial_epitheton
555
+ end
556
+
557
+ rule uninomial_epitheton
558
+ cap_latin_word {
559
+ def canonical
560
+ value
561
+ end
562
+
563
+ def pos
564
+ {interval.begin => ['uninomial', interval.end]}
565
+ end
566
+
567
+ def hybrid
568
+ false
569
+ end
570
+
571
+ def details
572
+ {:uninomial => {:epitheton => value}}
573
+ end
574
+ }
575
+ end
576
+
577
+ rule authorship
578
+ a:basionym_authorship_with_parenthesis space b:simple_authorship ","? space c:ex_authorship {
579
+ def value
580
+ a.value + " " + b.value + " " + c.value
581
+ end
582
+
583
+ def pos
584
+ a.pos.merge(b.pos).merge(c.pos)
585
+ end
586
+
587
+ def details
588
+ val = {:authorship => text_value.strip, :combinationAuthorTeam => b.details[:basionymAuthorTeam], :basionymAuthorTeam => a.details[:basionymAuthorTeam]}
589
+ val[:combinationAuthorTeam].merge!(c.details)
590
+ val
591
+ end
592
+ }
593
+ /
594
+ a:basionym_authorship_with_parenthesis space b:simple_authorship {
595
+ def value
596
+ a.value + " " + b.value
597
+ end
598
+
599
+ def pos
600
+ a.pos.merge(b.pos)
601
+ end
602
+
603
+ def details
604
+ {:authorship => text_value.strip, :combinationAuthorTeam => b.details[:basionymAuthorTeam], :basionymAuthorTeam => a.details[:basionymAuthorTeam]}
605
+ end
606
+ }
607
+ /
608
+ basionym_authorship_with_parenthesis
609
+ /
610
+ a:simple_authorship ","? space b:ex_authorship {
611
+ def value
612
+ a.value + " " + b.value
613
+ end
614
+
615
+ def pos
616
+ a.pos.merge(b.pos)
617
+ end
618
+
619
+ def details
620
+ val = a.details
621
+ val[:authorship] = text_value.strip
622
+ val[:basionymAuthorTeam].merge!(b.details)
623
+ val
624
+ end
625
+ }
626
+ /
627
+ simple_authorship
628
+ end
629
+
630
+
631
+ rule basionym_authorship_with_parenthesis
632
+ left_paren space a:authors_names space right_paren space [,]? space b:year {
633
+ def value
634
+ "(" + a.value + " " + b.value + ")"
635
+ end
636
+
637
+ def pos
638
+ a.pos.merge(b.pos)
639
+ end
640
+
641
+ def details
642
+ { :authorship => text_value,
643
+ :basionymAuthorTeam => {:author_team => text_value}.merge(a.details).merge(b.details)
644
+ }
645
+ end
646
+ }
647
+ /
648
+ left_paren space a:simple_authorship ","? space b:ex_authorship space right_paren {
649
+ def value
650
+ "(" + a.value + " " + b.value + ")"
651
+ end
652
+
653
+ def pos
654
+ a.pos.merge(b.pos)
655
+ end
656
+
657
+ def details
658
+ val = a.details
659
+ val[:basionymAuthorTeam].merge!(b.details)
660
+ val[:authorship] = text_value.strip
661
+ val
662
+ end
663
+ }
664
+ /
665
+ left_paren space a:simple_authorship space right_paren {
666
+ def value
667
+ "(" + a.value + ")"
668
+ end
669
+
670
+ def pos
671
+ a.pos
672
+ end
673
+
674
+ def details
675
+ val = a.details
676
+ val[:authorship] = text_value
677
+ val
678
+ end
679
+ }
680
+ /
681
+ left_paren space a:"?" space right_paren {
682
+ def value
683
+ "(?)"
684
+ end
685
+
686
+ def pos
687
+ {a.interval.begin => ['unknown_author', a.interval.end]}
688
+ end
689
+
690
+ def details
691
+ {:authorship => text_value, :basionymAuthorTeam => {:authorTeam => text_value, :author => ['?']}}
692
+ end
693
+ }
694
+ end
695
+
696
+ rule ex_authorship
697
+ ex_sep space b:simple_authorship {
698
+ def value
699
+ " ex " + b.value
700
+ end
701
+
702
+ def pos
703
+ b.pos
704
+ end
705
+
706
+ def details
707
+ val = {:exAuthorTeam => {:authorTeam => b.text_value.strip}.merge(b.details[:basionymAuthorTeam])}
708
+ val
709
+ end
710
+ }
711
+ end
712
+
713
+
714
+ rule simple_authorship
715
+ a:authors_names space [,]? space b:year? [,]? space "non" space authors_names space [,]? space year {
716
+ def value
717
+ a.value + " " + b.value
718
+ end
719
+
720
+ def pos
721
+ a.pos.merge(b.pos)
722
+ end
723
+
724
+ def details
725
+ details_with_arg(:basionymAuthorTeam)
726
+ end
727
+
728
+ def details_with_arg(authorTeamType = 'basionymAuthorTeam')
729
+ { :authorship => text_value,
730
+ authorTeamType.to_sym => {
731
+ :authorTeam => a.text_value.strip
732
+ }.merge(a.details).merge(b.details)
733
+ }
734
+ end
735
+ }
736
+ /
737
+ a:authors_names space [,]? space b:year {
738
+ def value
739
+ a.value + " " + b.value
740
+ end
741
+
742
+ def pos
743
+ a.pos.merge(b.pos)
744
+ end
745
+
746
+ def details
747
+ details_with_arg(:basionymAuthorTeam)
748
+ end
749
+
750
+ def details_with_arg(authorTeamType = 'basionymAuthorTeam')
751
+ { :authorship => text_value,
752
+ authorTeamType.to_sym => {
753
+ :authorTeam => a.text_value.strip
754
+ }.merge(a.details).merge(b.details)
755
+ }
756
+ end
757
+ }
758
+ /
759
+ authors_names {
760
+ def details
761
+ details = details_with_arg(:basionymAuthorTeam)
762
+ details[:basionymAuthorTeam].merge!(super)
763
+ details
764
+ end
765
+
766
+ def details_with_arg(authorTeamType = 'basionymAuthorTeam')
767
+ { :authorship => text_value,
768
+ authorTeamType.to_sym => {
769
+ :authorTeam => text_value,
770
+ }
771
+ }
772
+ end
773
+ }
774
+ end
775
+
776
+ rule authors_names
777
+ a:author_name space sep:author_separator space b:authors_names {
778
+ def value
779
+ sep.apply(a,b)
780
+ end
781
+
782
+ def pos
783
+ sep.pos(a,b)
784
+ end
785
+
786
+ def details
787
+ sep.details(a,b)
788
+ end
789
+ }
790
+ /
791
+ author_name
792
+ /
793
+ unknown_auth
794
+ end
795
+
796
+
797
+ rule unknown_auth
798
+ ("auct."/"hort."/"anon."/"ht.") {
799
+ def value
800
+ text_value
801
+ end
802
+
803
+ def pos
804
+ {interval.begin => ['unknown_author', interval.end]}
805
+ end
806
+
807
+ def details
808
+ {:author => ["unknown"]}
809
+ end
810
+ }
811
+ end
812
+
813
+ rule ex_sep
814
+ ("ex"/"in") &[\s]
815
+ end
816
+
817
+ rule author_separator
818
+ ("&"/","/"and"/"et") {
819
+ def apply(a,b)
820
+ sep = text_value.strip
821
+ sep = " et" if ["&","and","et"].include? sep
822
+ a.value + sep + " " + b.value
823
+ end
824
+
825
+ def pos(a,b)
826
+ a.pos.merge(b.pos)
827
+ end
828
+
829
+ def details(a,b)
830
+ {:author => a.details[:author] + b.details[:author]}
831
+ end
832
+ }
833
+ end
834
+
835
+ rule author_name
836
+ space a:author_prefix_word space b:author_name space {
837
+ def value
838
+ a.value + " " + b.value
839
+ end
840
+
841
+ def pos
842
+ a.pos.merge(b.pos)
843
+ end
844
+
845
+ def details
846
+ {:author => [value]}
847
+ end
848
+ }
849
+ /
850
+ space a:author_word space b:author_name space {
851
+ def value
852
+ a.value + " " + b.value
853
+ end
854
+
855
+ def pos
856
+ a.pos.merge(b.pos)
857
+ end
858
+
859
+ def details
860
+ {:author => [value]}
861
+ end
862
+ }
863
+ /
864
+ author_word
865
+ end
866
+
867
+ rule author_word
868
+ "A S. Xu" {
869
+ def value
870
+ text_value.strip
871
+ end
872
+
873
+ def pos
874
+ {interval.begin => ['author_word', 1], (interval.begin + 2) => ['author_word', 2], (interval.begin + 5) => ['author_word', 2]}
875
+ end
876
+
877
+ def details
878
+ {:author => [value]}
879
+ end
880
+ }
881
+ /
882
+ ("arg."/"et al.\{\?\}"/"et al.") {
883
+ def value
884
+ text_value.strip
885
+ end
886
+
887
+ def pos
888
+ #cheating because there are several words in some of them
889
+ {interval.begin => ['author_word', interval.end]}
890
+ end
891
+
892
+ def details
893
+ {:author => [value]}
894
+ end
895
+ }
896
+ /
897
+ ("Å"/"Ö"/"Á"/"Ø"/"Ô"/"Š"/"Ś"/"Č"/"Ķ"/"Ł"/"É"/"Ž"/[A-W]/[Y-Z]) [^0-9\[\]\(\)\s&,]* {
898
+ def value
899
+ text_value
900
+ end
901
+
902
+ def pos
903
+ {interval.begin => ['author_word', interval.end]}
904
+ end
905
+
906
+ def details
907
+ {:author => [value]}
908
+ end
909
+ }
910
+ /
911
+ "X" [^0-9\[\]\(\)\s&,]+ {
912
+ def value
913
+ text_value
914
+ end
915
+
916
+ def pos
917
+ {interval.begin => ['author_word', interval.end]}
918
+ end
919
+
920
+ def details
921
+ {:author => [value]}
922
+ end
923
+ }
924
+ /
925
+ author_prefix_word
926
+ end
927
+
928
+ rule author_prefix_word
929
+ space ("ab"/"bis"/"da"/"der"/"den"/"della"/"dela"/"de"/"di"/"du"/"la"/"ter"/"van"/"von") &space_hard {
930
+ def value
931
+ text_value
932
+ end
933
+
934
+ def pos
935
+ #cheating because there are several words in some of them
936
+ {interval.begin => ['author_word', interval.end]}
937
+ end
938
+ }
939
+ end
940
+
941
+ rule cap_latin_word
942
+ a:([A-Z]/cap_digraph) b:latin_word "?" {
943
+ def value
944
+ (a.value rescue a.text_value) + b.value
945
+ end
946
+ }
947
+ /
948
+ a:([A-Z]/cap_digraph) b:latin_word {
949
+ def value
950
+ (a.value rescue a.text_value) + b.value
951
+ end
952
+ }
953
+ /
954
+ ("Ca"/"Ea"/"Ge"/"Ia"/"Io"/"Io"/"Ix"/"Lo"/"Oa"/"Ra"/"Ty"/"Ua"/"Aa"/"Ja"/"Zu"/"La"/"Qu"/"As"/"Ba") {
955
+ def value
956
+ text_value
957
+ end
958
+ }
959
+ end
960
+
961
+ rule species_word_hybrid
962
+ a:multiplication_sign space b:species_word {
963
+ def value
964
+ a.value + " " + b.value
965
+ end
966
+
967
+ def canonical
968
+ b.value
969
+ end
970
+
971
+ def hybrid
972
+ true
973
+ end
974
+
975
+ def pos
976
+ {b.interval.begin => ['species', b.interval.end]}
977
+ end
978
+
979
+ def details
980
+ {:species => {:epitheton => b.value}}
981
+ end
982
+ }
983
+ /
984
+ a:"X" space b:species_word {
985
+ def value
986
+ "× " + b.value
987
+ end
988
+
989
+ def canonical
990
+ b.value
991
+ end
992
+
993
+ def hybrid
994
+ true
995
+ end
996
+
997
+ def pos
998
+ {b.interval.begin => ['species', b.interval.end]}
999
+ end
1000
+
1001
+ def details
1002
+ {:species => {:epitheton => b.value}}
1003
+ end
1004
+ }
1005
+ /
1006
+ a:"x" space_hard b:species_word {
1007
+ def value
1008
+ "× " + b.value
1009
+ end
1010
+
1011
+ def canonical
1012
+ b.value
1013
+ end
1014
+
1015
+ def hybrid
1016
+ true
1017
+ end
1018
+
1019
+ def pos
1020
+ {b.interval.begin => ['species', b.interval.end]}
1021
+ end
1022
+
1023
+ def details
1024
+ {:species => {:epitheton => b.value}}
1025
+ end
1026
+ }
1027
+ end
1028
+
1029
+ rule species_prefix
1030
+ ("aff."/"corrig."/"?") &space_hard
1031
+ end
1032
+
1033
+ rule species_word
1034
+ a:[0-9]+ "-"? b:latin_word {
1035
+ def value
1036
+ a.text_value + "-" + b.value
1037
+ end
1038
+ }
1039
+ /
1040
+ latin_word
1041
+ end
1042
+
1043
+ rule latin_word
1044
+ a:[a-zëüäöïéåóç] b:full_name_letters {
1045
+ def value
1046
+ a.text_value + b.value
1047
+ end
1048
+ }
1049
+ /
1050
+ a:digraph b:full_name_letters {
1051
+ def value
1052
+ a.value + b.value
1053
+ end
1054
+ }
1055
+ end
1056
+
1057
+ rule full_name_letters
1058
+ a:digraph b:full_name_letters {
1059
+ def value
1060
+ a.value + b.value
1061
+ end
1062
+ }
1063
+ /
1064
+ a:valid_name_letters b:digraph c:full_name_letters {
1065
+ def value
1066
+ a.value + b.value + c.value
1067
+ end
1068
+ }
1069
+ /
1070
+ valid_name_letters
1071
+ end
1072
+
1073
+ rule valid_name_letters
1074
+ [a-z\-ëüäöïéåóç]+ {
1075
+ def value
1076
+ text_value
1077
+ end
1078
+ }
1079
+ end
1080
+
1081
+ rule cap_digraph
1082
+ "Æ" {
1083
+ def value
1084
+ 'Ae'
1085
+ end
1086
+ }
1087
+ /
1088
+ "Œ" {
1089
+ def value
1090
+ 'Oe'
1091
+ end
1092
+ }
1093
+ end
1094
+
1095
+ rule digraph
1096
+ "æ" {
1097
+ def value
1098
+ 'ae'
1099
+ end
1100
+ }
1101
+ /
1102
+ "œ" {
1103
+ def value
1104
+ 'oe'
1105
+ end
1106
+ }
1107
+ end
1108
+
1109
+ rule year
1110
+ b:left_paren space a:(year_number_with_character/year_number) space c:right_paren {
1111
+ def value
1112
+ a.value
1113
+ end
1114
+
1115
+ def pos
1116
+ a.pos
1117
+ end
1118
+
1119
+ def details
1120
+ a.details
1121
+ end
1122
+ }
1123
+ /
1124
+ year_number_with_character
1125
+ /
1126
+ year_number
1127
+ end
1128
+
1129
+ rule year_number_with_character
1130
+ a:year_number [a-zA-Z] {
1131
+ def value
1132
+ a.text_value
1133
+ end
1134
+
1135
+ def pos
1136
+ {interval.begin => ['year', interval.end]}
1137
+ end
1138
+
1139
+ def details
1140
+ {:year => value}
1141
+ end
1142
+ }
1143
+ end
1144
+
1145
+ rule year_number
1146
+ [12] [7890] [0-9] [0-9]? [\?]? {
1147
+ def value
1148
+ text_value
1149
+ end
1150
+
1151
+ def pos
1152
+ {interval.begin => ['year', interval.end]}
1153
+ end
1154
+
1155
+ def details
1156
+ {:year => value}
1157
+ end
1158
+ }
1159
+ end
1160
+
1161
+ rule left_paren
1162
+ "("
1163
+ end
1164
+
1165
+ rule right_paren
1166
+ ")"
1167
+ end
1168
+
1169
+ rule hybrid_character
1170
+ ("x"/"X") {
1171
+ def value
1172
+ "×"
1173
+ end
1174
+ }
1175
+ /
1176
+ multiplication_sign
1177
+ end
1178
+
1179
+ rule multiplication_sign
1180
+ "×" {
1181
+ def value
1182
+ text_value
1183
+ end
1184
+ }
1185
+ end
1186
+
1187
+ rule space
1188
+ [\s]*
1189
+ end
1190
+
1191
+ rule space_hard
1192
+ [\s]+
1193
+ end
1194
+
1195
+ end