edtf 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -8,20 +8,15 @@ Specification](http://www.loc.gov/standards/datetime/spec.html).
8
8
  Compatibility
9
9
  -------------
10
10
 
11
- As of EDTF Specification DRAFT, August 4, 2001:
12
-
13
- * Level 0: fully implemented
14
- * Level 1: fully implemented
15
- * Level 2: implemented features 202, 203, 204, 2041, 207, 208, and 209
11
+ EDTF-Ruby parser fully implements all levels and features of EDTF
12
+ Specification DRAFT, August 4, 2011.
16
13
 
17
14
  The level 2 list extensions (203 and 204) currently return simple Ruby arrays;
18
15
  therefore, advanced behavior (such as 'earlier' or 'later') is parsed correctly
19
16
  but not yet exposed by the Ruby API.
20
17
 
21
- The level 2 extensions 201 and 205 are currently _not_ supported.
22
-
23
18
  EDTF-Ruby has been confirmed to work on the following Ruby implementations:
24
- 1.9.2, 1.8.7, rbx, jruby.
19
+ 1.9.2, 1.8.7, Rubinius, and JRuby.
25
20
 
26
21
 
27
22
  Quickstart
@@ -37,33 +32,35 @@ given a valid EDTF string the return value will either be an (extended) `Date`,
37
32
  > require 'edtf'
38
33
  > d = Date.edtf('1984?')
39
34
  > d.uncertain?
40
- => true
35
+ => true
41
36
  > d.certain!
42
37
  > d.uncertain?
43
- => false
38
+ => false
44
39
  > d = Date.edtf('1999-03-uu')
45
40
  > d.unspecified?
46
- => true
41
+ => true
47
42
  > d.unspecified? :year
48
- => false
43
+ => false
49
44
  > d.unspecified? :day
50
- => true
45
+ => true
51
46
  > Date.edtf('2003-24').winter?
52
- => true
47
+ => true
53
48
  > Date.edtf('196x')
54
- => #<Date: 1960-01-01>...#<Date: 1970-01-01>
49
+ => #<Date: 1960-01-01>...#<Date: 1970-01-01>
55
50
  > Date.edtf('y-17e7').year
56
- => -170000000
51
+ => -170000000
57
52
  > d = Date.edtf('1984-06?/2004-08?')
58
53
  > d.from.uncertain?
59
- => true
60
- > d.each.to_a.length
61
- => 7367 # days between 1984-06 and 2004-08
62
- > Date.edtf('2004-01-01/open').open?
63
- => true
54
+ => true
55
+ > d.to_a.length
56
+ => 7397 # days between 1984-06-01 and 2004-08-31
57
+ > Date.edtf('1582-10/1582-10').to_a.length
58
+ => 21 # number of days in October 1582 (Gregorian calendar)
59
+ > Date.edtf('2004/open').open?
60
+ => true
64
61
 
65
62
 
66
- For additional features take a look at the rdoc, source, and rspec examples.
63
+ For additional features take a look at the RDoc and RSpec examples.
67
64
 
68
65
 
69
66
  Development
@@ -72,16 +69,18 @@ Development
72
69
  $ git clone https://inukshuk@github.com/inukshuk/edtf-ruby.git
73
70
  $ cd edtf-ruby
74
71
  $ bundle install
75
- $ bundle exec rake racc
72
+ $ bundle exec rake racc_debug
76
73
  $ bundle exec rspec spec
77
74
  $ bundle exec cucumber
78
75
 
79
- For extra credit, fork the project on github: pull requests welcome!
76
+ For extra credit, fork the project on GitHub: pull requests welcome!
77
+
80
78
 
81
79
  Credits
82
80
  -------
83
81
 
84
- EDTF-Ruby was written by [Sylvester Keil](http://sylvester.keil.or.at).
82
+ EDTF-Ruby was written by [Sylvester Keil](http://sylvester.keil.or.at) and
83
+ [Namyra](https://github.com/namyra).
85
84
 
86
85
  Published under the terms and conditions of the FreeBSD License; see LICENSE
87
86
  for details.
@@ -31,11 +31,11 @@ Feature: EDTF parser parses date strings
31
31
 
32
32
  @101 @level1
33
33
  Scenarios: uncertain date examples from the specification
34
- | string | year | uncertain-year | month | uncertain-month | day | uncertain-day |
35
- | 1992? | 1992 | yes | 1 | yes | 1 | yes |
36
- | 1984? | 1984 | yes | 1 | yes | 1 | yes |
37
- | 2004-06? | 2004 | yes | 6 | yes | 1 | yes |
38
- | 2004-06-11? | 2004 | yes | 6 | yes | 11 | yes |
34
+ | string | year | uncertain-year | month | uncertain-month | day | uncertain-day |
35
+ | 1992? | 1992 | yes | 1 | no | 1 | no |
36
+ | 1984? | 1984 | yes | 1 | no | 1 | no |
37
+ | 2004-06? | 2004 | yes | 6 | yes | 1 | no |
38
+ | 2004-06-11? | 2004 | yes | 6 | yes | 11 | yes |
39
39
 
40
40
 
41
41
  Scenario Outline: EDTF parses uncertain or approximate date strings
@@ -51,3 +51,26 @@ Feature: EDTF parser parses date strings
51
51
  | string | year | month | day | uncertain | approximate |
52
52
  | 1984~ | 1984 | 1 | 1 | no | yes |
53
53
  | 1984?~ | 1984 | 1 | 1 | yes | yes |
54
+
55
+ Scenario Outline: EDTF parses uncertain or approximate dates
56
+ When I parse the string "<string>"
57
+ And the year should be uncertain? "<?-year>"
58
+ And the month should be uncertain? "<?-month>"
59
+ And the day should be uncertain? "<?-day>"
60
+ And the year should be approximate? "<~-year>"
61
+ And the month should be approximate? "<~-month>"
62
+ And the day should be approximate? "<~-day>"
63
+
64
+ @201 @level2
65
+ Scenarios: uncertain date examples from the specification
66
+ | string | ~-year | ?-year | ~-month | ?-month | ~-day | ?-day |
67
+ | 2004?-06-11 | no | yes | no | no | no | no |
68
+ | 2004-06~-11 | yes | no | yes | no | no | no |
69
+ | 2004-(06)?-11 | no | no | no | yes | no | no |
70
+ | 2004-06-(11)~ | no | no | no | no | yes | no |
71
+ | 2004-(06)?~ | no | no | yes | yes | no | no |
72
+ | 2004-(06-11)? | no | no | no | yes | no | yes |
73
+ | 2004?-06-(11)~ | no | yes | no | no | yes | no |
74
+ | (2004-(06)~)? | no | yes | yes | yes | no | no |
75
+ | 2004-06-(01)~ | no | no | no | no | yes | no |
76
+
@@ -0,0 +1,73 @@
1
+ Feature: Print Date/Time objects as Level 1 EDTF strings
2
+ As a Ruby programmer
3
+ I want to convert Date/Time objects to EDTF strings
4
+
5
+ @001 @level0
6
+ Scenario: Convert simple dates
7
+ Given the date "2004-08-12"
8
+ When I convert the date
9
+ Then the EDTF string should be "2004-08-12"
10
+
11
+ @001 @level0
12
+ Scenario: Convert simple dates with precision
13
+ Given the date "1980-08-24" with precision set to "day"
14
+ When I convert the date
15
+ Then the EDTF string should be "1980-08-24"
16
+ Given the date "1980-08-24" with precision set to "month"
17
+ When I convert the date
18
+ Then the EDTF string should be "1980-08"
19
+ Given the date "1980-08-24" with precision set to "year"
20
+ When I convert the date
21
+ Then the EDTF string should be "1980"
22
+
23
+ @001 @level0
24
+ Scenario: Date Roundtrips
25
+ When I parse the string "2001-02-03"
26
+ When I convert the date
27
+ Then the EDTF string should be "2001-02-03"
28
+ When I parse the string "2001-02"
29
+ When I convert the date
30
+ Then the EDTF string should be "2001-02"
31
+ When I parse the string "2001"
32
+ When I convert the date
33
+ Then the EDTF string should be "2001"
34
+ When I parse the string "-9909"
35
+ When I convert the date
36
+ Then the EDTF string should be "-9909"
37
+ When I parse the string "0000"
38
+ When I convert the date
39
+ Then the EDTF string should be "0000"
40
+
41
+ @002 @level0
42
+ Scenario: DateTime Roundtrips
43
+ When I parse the string "2001-02-03T09:30:01"
44
+ When I convert the date
45
+ Then the EDTF string should be "2001-02-03T09:30:01+00:00"
46
+ When I parse the string "2004-01-01T10:10:10Z"
47
+ When I convert the date
48
+ Then the EDTF string should be "2004-01-01T10:10:10+00:00"
49
+ When I parse the string "2004-01-01T10:10:10+05:00"
50
+ When I convert the date
51
+ Then the EDTF string should be "2004-01-01T10:10:10+05:00"
52
+
53
+ @004 @level0
54
+ Scenario: Interval Roundtrips
55
+ When I parse the string "1964/2008"
56
+ When I convert the date
57
+ Then the EDTF string should be "1964/2008"
58
+ When I parse the string "2004-06/2006-08"
59
+ When I convert the date
60
+ Then the EDTF string should be "2004-06/2006-08"
61
+ When I parse the string "2004-02-01/2005-02-08"
62
+ When I convert the date
63
+ Then the EDTF string should be "2004-02-01/2005-02-08"
64
+ When I parse the string "2004-02-01/2005-02"
65
+ When I convert the date
66
+ Then the EDTF string should be "2004-02-01/2005-02"
67
+ When I parse the string "2004-02-01/2005"
68
+ When I convert the date
69
+ Then the EDTF string should be "2004-02-01/2005"
70
+ When I parse the string "2005/2006-02"
71
+ When I convert the date
72
+ Then the EDTF string should be "2005/2006-02"
73
+
@@ -1,68 +1,15 @@
1
- Feature: Print Date/Time objects as Level 1 EDTF strings
1
+ Feature: Print Date/Time objects as Level 2 EDTF strings
2
2
  As a Ruby programmer
3
3
  I want to convert Date/Time objects to EDTF strings
4
4
 
5
- Scenario: Convert simple dates
6
- Given the date "2004-08-12"
7
- When I convert the date
8
- Then the EDTF string should be "2004-08-12"
9
-
10
- Scenario: Convert simple dates with precision
11
- Given the date "1980-08-24" with precision set to "day"
5
+ @101 @level1
6
+ Scenario: Uncertain or approximate dates
7
+ When I parse the string "2001?"
12
8
  When I convert the date
13
- Then the EDTF string should be "1980-08-24"
14
- Given the date "1980-08-24" with precision set to "month"
9
+ Then the EDTF string should be "2001?"
10
+ When I parse the string "2004-06?"
15
11
  When I convert the date
16
- Then the EDTF string should be "1980-08"
17
- Given the date "1980-08-24" with precision set to "year"
12
+ Then the EDTF string should be "2004-06?"
13
+ When I parse the string "2004-06-11?"
18
14
  When I convert the date
19
- Then the EDTF string should be "1980"
20
-
21
- Scenario: Date Roundtrips
22
- When I parse the string "2001-02-03"
23
- When I convert the date
24
- Then the EDTF string should be "2001-02-03"
25
- When I parse the string "2001-02"
26
- When I convert the date
27
- Then the EDTF string should be "2001-02"
28
- When I parse the string "2001"
29
- When I convert the date
30
- Then the EDTF string should be "2001"
31
- When I parse the string "-9909"
32
- When I convert the date
33
- Then the EDTF string should be "-9909"
34
- When I parse the string "0000"
35
- When I convert the date
36
- Then the EDTF string should be "0000"
37
-
38
- Scenario: DateTime Roundtrips
39
- When I parse the string "2001-02-03T09:30:01"
40
- When I convert the date
41
- Then the EDTF string should be "2001-02-03T09:30:01+00:00"
42
- When I parse the string "2004-01-01T10:10:10Z"
43
- When I convert the date
44
- Then the EDTF string should be "2004-01-01T10:10:10+00:00"
45
- When I parse the string "2004-01-01T10:10:10+05:00"
46
- When I convert the date
47
- Then the EDTF string should be "2004-01-01T10:10:10+05:00"
48
-
49
- Scenario: Interval Roundtrips
50
- When I parse the string "1964/2008"
51
- When I convert the date
52
- Then the EDTF string should be "1964/2008"
53
- When I parse the string "2004-06/2006-08"
54
- When I convert the date
55
- Then the EDTF string should be "2004-06/2006-08"
56
- When I parse the string "2004-02-01/2005-02-08"
57
- When I convert the date
58
- Then the EDTF string should be "2004-02-01/2005-02-08"
59
- When I parse the string "2004-02-01/2005-02"
60
- When I convert the date
61
- Then the EDTF string should be "2004-02-01/2005-02"
62
- When I parse the string "2004-02-01/2005"
63
- When I convert the date
64
- Then the EDTF string should be "2004-02-01/2005"
65
- When I parse the string "2005/2006-02"
66
- When I convert the date
67
- Then the EDTF string should be "2005/2006-02"
68
-
15
+ Then the EDTF string should be "2004-06-11?"
@@ -93,6 +93,19 @@ Then /^the date should be approximate\? "([^"]*)"$/ do |arg1|
93
93
  @date.approximate?.should == !!(arg1 =~ /y(es)?/i)
94
94
  end
95
95
 
96
+ Then /^the year should be approximate\? "([^"]*)"$/ do |arg1|
97
+ @date.approximate?(:year).should == !!(arg1 =~ /y(es)?/i)
98
+ end
99
+
100
+ Then /^the month should be approximate\? "([^"]*)"$/ do |arg1|
101
+ @date.approximate?(:month).should == !!(arg1 =~ /y(es)?/i)
102
+ end
103
+
104
+ Then /^the day should be approximate\? "([^"]*)"$/ do |arg1|
105
+ @date.approximate?(:day).should == !!(arg1 =~ /y(es)?/i)
106
+ end
107
+
108
+
96
109
  Then /^the unspecified string code be "([^"]*)"$/ do |arg1|
97
110
  @date.unspecified.to_s.should == arg1
98
111
  end
@@ -46,6 +46,7 @@ class Date
46
46
  @precision ||= :day
47
47
  end
48
48
 
49
+ # Sets this Date/Time's precision to the passed-in value.
49
50
  def precision=(precision)
50
51
  precision = precision.to_sym
51
52
  raise ArgumentError, "invalid precision #{precision.inspect}" unless PRECISIONS.include?(precision)
@@ -71,37 +72,37 @@ class Date
71
72
 
72
73
  def_delegators :uncertain, :uncertain?, :certain?
73
74
 
74
- def certain!(*arguments)
75
- uncertain.certain!(*arguments)
75
+ def certain!(arguments = precision_filter)
76
+ uncertain.certain!(arguments)
76
77
  self
77
78
  end
78
79
 
79
- def uncertain!(*arguments)
80
- uncertain.uncertain!(*arguments)
80
+ def uncertain!(arguments = precision_filter)
81
+ uncertain.uncertain!(arguments)
81
82
  self
82
83
  end
83
84
 
84
- def approximate?(*arguments)
85
- approximate.uncertain?(*arguments)
85
+ def approximate?(arguments = precision_filter)
86
+ approximate.uncertain?(arguments)
86
87
  end
87
88
 
88
89
  alias approximately? approximate?
89
90
 
90
- def approximate!(*arguments)
91
- approximate.uncertain!(*arguments)
91
+ def approximate!(arguments = precision_filter)
92
+ approximate.uncertain!(arguments)
92
93
  self
93
94
  end
94
95
 
95
96
  alias approximately! approximate!
96
97
 
97
- def precise?(*arguments)
98
- !approximate?(*arguments)
98
+ def precise?(arguments = precision_filter)
99
+ !approximate?(arguments)
99
100
  end
100
101
 
101
102
  alias precisely? precise?
102
103
 
103
- def precise!(*arguments)
104
- approximate.certain!(*arguments)
104
+ def precise!(arguments = precision_filter)
105
+ approximate.certain!(arguments)
105
106
  self
106
107
  end
107
108
 
@@ -109,15 +110,15 @@ class Date
109
110
 
110
111
  def_delegators :unspecified, :unspecified?, :specified?, :unsepcific?, :specific?
111
112
 
112
- def unspecified!(*arguments)
113
- unspecified.unspecified!(*arguments)
113
+ def unspecified!(arguments = precision_filter)
114
+ unspecified.unspecified!(arguments)
114
115
  self
115
116
  end
116
117
 
117
118
  alias unspecific! unspecified!
118
119
 
119
- def specified!(*arguments)
120
- unspecified.specified!(*arguments)
120
+ def specified!(arguments = precision_filter)
121
+ unspecified.specified!(arguments)
121
122
  self
122
123
  end
123
124
 
@@ -130,11 +131,13 @@ class Date
130
131
  end
131
132
 
132
133
  def edtf
133
- FORMATS.take(values.length).join('-') % values
134
+ FORMATS.take(values.length).join('-') % values << (uncertain? ? '?' : '')
134
135
  end
135
136
 
136
137
  alias to_edtf edtf
137
138
 
139
+ # Returns a the Date of the next day, month, or year depending on the
140
+ # current Date/Time's precision.
138
141
  def next
139
142
  send("next_#{precision}")
140
143
  end
@@ -151,6 +154,8 @@ class Date
151
154
  # def ===(other)
152
155
  # end
153
156
 
157
+ # Returns an array of the current year, month, and day values filtered by
158
+ # the Date/Time's precision.
154
159
  def values
155
160
  precision_filter.map { |p| send(p) }
156
161
  end
@@ -2,9 +2,7 @@
2
2
 
3
3
  class EDTF::Parser
4
4
 
5
- token T Z E X PLUS MINUS COLON SLASH D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 LP RP
6
- UNCERTAIN APPROXIMATE UNSPECIFIED UNKNOWN OPEN LONGYEAR CARET UNMATCHED
7
- DOTS COMMA LBRACE RBRACE LSQUARE RSQUARE
5
+ token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS IUA
8
6
 
9
7
  expect 0
10
8
 
@@ -13,253 +11,421 @@ rule
13
11
  edtf : level_0_expression
14
12
  | level_1_expression
15
13
  | level_2_expression
16
- # | { result = Date.today }
17
-
14
+ ;
18
15
 
19
16
  # ---- Level 0 / ISO 8601 Rules ----
20
-
17
+
18
+ # NB: level 0 intervals are covered by the level 1 interval rules
21
19
  level_0_expression : date
22
20
  | date_time
23
- # | level_0_interval # --> level_1_interval
21
+ ;
24
22
 
25
23
  date : positive_date
26
24
  | negative_date
25
+ ;
27
26
 
28
- positive_date : year { result = Date.new(val[0]); result.precision = :year }
29
- | year_month { result = Date.new(*val.flatten); result.precision = :month }
30
- | year_month_day { result = Date.new(*val.flatten); result.precision = :day }
31
-
32
- negative_date : MINUS positive_date { result = -val[1] }
27
+ positive_date :
28
+ year { result = Date.new(val[0]); result.precision = :year }
29
+ | year_month { result = Date.new(*val.flatten); result.precision = :month }
30
+ | year_month_day { result = Date.new(*val.flatten); result.precision = :day }
31
+ ;
33
32
 
33
+ negative_date : '-' positive_date { result = -val[1] }
34
34
 
35
- date_time : date T time { result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2]) }
35
+
36
+ date_time : date T time {
37
+ result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2])
38
+ }
36
39
 
37
40
  time : base_time
38
41
  | base_time zone_offset { result = val.flatten }
39
-
40
- base_time : hour COLON minute COLON second { result = [val[0], val[2], val[4]] }
42
+
43
+ base_time : hour ':' minute ':' second { result = val.values_at(0, 2, 4) }
41
44
  | midnight
42
-
43
- midnight : D2 D4 COLON D0 D0 COLON D0 D0 { result = [24, 0, 0] }
44
-
45
- zone_offset : Z { result = 0 }
46
- | MINUS zone_offset_hour { result = -1 * val[1] }
47
- | PLUS positive_zone_offset { result = val[1] }
45
+
46
+ midnight : '2' '4' ':' '0' '0' ':' '0' '0' { result = [24, 0, 0] }
47
+
48
+ zone_offset : Z { result = 0 }
49
+ | '-' zone_offset_hour { result = -1 * val[1] }
50
+ | '+' positive_zone_offset { result = val[1] }
51
+ ;
48
52
 
49
53
  positive_zone_offset : zone_offset_hour
50
- | D0 D0 COLON D0 D0 { result = 0 }
51
-
52
-
53
- zone_offset_hour : d01_13 COLON minute { result = Rational(val[0] * 60 + val[2], 1440) }
54
- | D1 D4 COLON D0 D0 { result = Rational(840, 1440) }
55
- | D0 D0 COLON d01_59 { result = Rational(val[3], 1440) }
56
-
57
- year : digit digit digit digit { result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b } }
58
-
54
+ | '0' '0' ':' '0' '0' { result = 0 }
55
+ ;
56
+
57
+
58
+ zone_offset_hour : d01_13 ':' minute { result = Rational(val[0] * 60 + val[2], 1440) }
59
+ | '1' '4' ':' '0' '0' { result = Rational(840, 1440) }
60
+ | '0' '0' ':' d01_59 { result = Rational(val[3], 1440) }
61
+ ;
62
+
63
+ year : digit digit digit digit {
64
+ result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
65
+ }
66
+
59
67
  month : d01_12
60
-
61
- year_month : year MINUS month { result = [val[0], val[2]] }
62
-
68
+
69
+ year_month : year '-' month { result = [val[0], val[2]] }
70
+
63
71
  # We raise an exception if there are two many days for the month, but
64
72
  # do not consider leap years, as the EDTF BNF did not either.
65
73
  # NB: an exception will be raised regardless, because the Ruby Date
66
74
  # implementation calculates leap years.
67
- year_month_day : year_month MINUS d01_31 { result = val[0] << val[2]; raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})" if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2) }
75
+ year_month_day : year_month '-' d01_31 {
76
+ result = val[0] << val[2]
77
+ if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2)
78
+ raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})"
79
+ end
80
+ }
68
81
 
69
- hour : d00_23
70
-
82
+ hour : d00_23
71
83
  minute : d00_59
72
-
73
84
  second : d00_59
74
-
75
- # covered by level_1_interval
76
- # level_0_interval : date SLASH date { result = Interval.new(val[0], val[1]) }
85
+
86
+ # Completely covered by level_1_interval
87
+ # level_0_interval : date '/' date { result = Interval.new(val[0], val[1]) }
88
+
77
89
 
78
90
  # ---- Level 1 Extension Rules ----
79
-
80
- level_1_expression : uncertain_or_approximate_date
81
- | unspecified
82
- | level_1_interval
83
- | long_year_simple
84
- | season
85
-
86
91
 
87
- uncertain_or_approximate_date : date uncertain_or_approximate { result = val[0]; val[1].each { |m| result.send(m) } }
88
-
89
- uncertain_or_approximate : UNCERTAIN { result = [:uncertain!] }
90
- | APPROXIMATE { result = [:approximate!] }
91
- | UNCERTAIN APPROXIMATE { result = [:uncertain!, :approximate!] }
92
-
92
+ # NB: Uncertain/approximate Dates are covered by the Level 2 rules
93
+ level_1_expression : unspecified | level_1_interval | long_year_simple | season
94
+
95
+ # uncertain_or_approximate_date : date ua { result = uoa(val[0], val[1]) }
93
96
 
94
- unspecified : unspecified_year { result = Date.new(val[0][0]); result.unspecified.year[2,2] = val[0][1]; result.precision = :year }
97
+ unspecified : unspecified_year
98
+ {
99
+ result = Date.new(val[0][0])
100
+ result.unspecified.year[2,2] = val[0][1]
101
+ result.precision = :year
102
+ }
95
103
  | unspecified_month
96
104
  | unspecified_day
97
105
  | unspecified_day_and_month
98
-
99
- unspecified_year : digit digit digit UNSPECIFIED { result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]] }
100
- | digit digit UNSPECIFIED UNSPECIFIED { result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]] }
101
-
102
- unspecified_month : year MINUS UNSPECIFIED UNSPECIFIED { result = Date.new(val[0]).unspecified!(:month); result.precision = :month }
103
-
104
- unspecified_day : year_month MINUS UNSPECIFIED UNSPECIFIED { result = Date.new(*val[0]).unspecified!(:day) }
105
-
106
- unspecified_day_and_month : year MINUS UNSPECIFIED UNSPECIFIED MINUS UNSPECIFIED UNSPECIFIED { result = Date.new(val[0]).unspecified!([:day,:month]) }
106
+ ;
107
+
108
+ unspecified_year :
109
+ digit digit digit U
110
+ {
111
+ result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]]
112
+ }
113
+ | digit digit U U
114
+ {
115
+ result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]]
116
+ }
117
+
118
+ unspecified_month : year '-' U U {
119
+ result = Date.new(val[0]).unspecified!(:month)
120
+ result.precision = :month
121
+ }
107
122
 
123
+ unspecified_day : year_month '-' U U {
124
+ result = Date.new(*val[0]).unspecified!(:day)
125
+ }
108
126
 
109
- level_1_interval : level_1_start SLASH level_1_end { result = Interval.new(val[0], val[2]) }
127
+ unspecified_day_and_month : year '-' U U '-' U U {
128
+ result = Date.new(val[0]).unspecified!([:day,:month])
129
+ }
110
130
 
111
- level_1_start : date
112
- | uncertain_or_approximate_date
113
- | UNKNOWN { result = :unknown }
114
-
115
- level_1_end : level_1_start
116
- | OPEN { result = :open }
117
131
 
132
+ level_1_interval : level_1_start '/' level_1_end {
133
+ result = Interval.new(val[0], val[2])
134
+ }
118
135
 
119
- long_year_simple : LONGYEAR long_year { result = Date.new(val[1]); result.precision = :year }
120
- | LONGYEAR MINUS long_year { result = Date.new(-1 * val[2]); result.precision = :year }
121
-
122
- long_year : positive_digit digit digit digit digit { result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b } }
123
- | long_year digit { result = 10 * val[0] + val[1] }
136
+ level_1_start : date | internal_uncertain_or_approximate_date | UNKNOWN
137
+
138
+ level_1_end : level_1_start | OPEN
139
+
140
+
141
+ long_year_simple :
142
+ LONGYEAR long_year
143
+ {
144
+ result = Date.new(val[1])
145
+ result.precision = :year
146
+ }
147
+ | LONGYEAR '-' long_year
148
+ {
149
+ result = Date.new(-1 * val[2])
150
+ result.precision = :year
151
+ }
152
+ ;
153
+
154
+ long_year :
155
+ positive_digit digit digit digit digit {
156
+ result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
157
+ }
158
+ | long_year digit { result = 10 * val[0] + val[1] }
159
+ ;
124
160
 
125
161
 
126
- season : year MINUS season_number { result = Season.new(val[0], val[2]) }
162
+ season : year '-' season_number { result = Season.new(val[0], val[2]) }
127
163
 
128
- season_number : D2 D1 { result = 21 }
129
- | D2 D2 { result = 22 }
130
- | D2 D3 { result = 23 }
131
- | D2 D4 { result = 24 }
164
+ season_number : '2' '1' { result = 21 }
165
+ | '2' '2' { result = 22 }
166
+ | '2' '3' { result = 23 }
167
+ | '2' '4' { result = 24 }
168
+ ;
132
169
 
133
170
 
134
171
  # ---- Level 2 Extension Rules ----
135
-
172
+
173
+ # NB: Level 2 Intervals are covered by the Level 1 Interval rules.
136
174
  level_2_expression : season_qualified
137
- # | internal_uncertain_or_approximate
175
+ | internal_uncertain_or_approximate_date
138
176
  | internal_unspecified
139
177
  | choice_list
140
178
  | inclusive_list
141
179
  | masked_precision
142
- # | level_2_interval
143
180
  | date_and_calendar
144
181
  | long_year_scientific
145
-
182
+ ;
146
183
 
147
- season_qualified : season CARET { result = val[0]; result.qualifier = val[1] }
148
184
 
185
+ season_qualified : season '^' { result = val[0]; result.qualifier = val[1] }
149
186
 
150
- long_year_scientific : long_year_simple E integer { result = Date.new(val[0].year * 10 ** val[2]); result.precision = :year }
151
- | LONGYEAR int1_4 E integer { result = Date.new(val[1] * 10 ** val[3]); result.precision = :year }
152
- | LONGYEAR MINUS int1_4 E integer { result = Date.new(-1 * val[2] * 10 ** val[4]); result.precision = :year }
153
-
154
187
 
155
- date_and_calendar : date CARET { result = val[0]; result.calendar = val[1] }
156
-
188
+ long_year_scientific :
189
+ long_year_simple E integer
190
+ {
191
+ result = Date.new(val[0].year * 10 ** val[2])
192
+ result.precision = :year
193
+ }
194
+ | LONGYEAR int1_4 E integer
195
+ {
196
+ result = Date.new(val[1] * 10 ** val[3])
197
+ result.precision = :year
198
+ }
199
+ | LONGYEAR '-' int1_4 E integer
200
+ {
201
+ result = Date.new(-1 * val[2] * 10 ** val[4])
202
+ result.precision = :year
203
+ }
204
+ ;
157
205
 
158
- masked_precision : digit digit digit X { d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }; result = Date.new(d) ... Date.new(d+10) }
159
- | digit digit X X { d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }; result = Date.new(d) ... Date.new(d+100) }
160
206
 
161
-
162
- choice_list : LSQUARE list RSQUARE { result = val[1] }
163
-
164
- inclusive_list : LBRACE list RBRACE { result = val[1] }
165
-
166
- list : earlier { result = [val[0]] }
167
- | earlier COMMA list_elements COMMA later { result = [val[0]] + val[2] + [val[4]] }
168
- | earlier COMMA list_elements { result = [val[0]] + val[2] }
169
- | earlier COMMA later { result = [val[0]] + [val[2]] }
170
- | list_elements COMMA later { result = val[0] + [val[2]] }
207
+ date_and_calendar : date '^' { result = val[0]; result.calendar = val[1] }
208
+
209
+
210
+ masked_precision :
211
+ digit digit digit X
212
+ {
213
+ d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }
214
+ result = Date.new(d) ... Date.new(d+10)
215
+ }
216
+ | digit digit X X
217
+ {
218
+ d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }
219
+ result = Date.new(d) ... Date.new(d+100)
220
+ }
221
+ ;
222
+
223
+
224
+ choice_list : '[' list ']' { result = val[1] }
225
+
226
+ inclusive_list : '{' list '}' { result = val[1] }
227
+
228
+ list : earlier { result = val }
229
+ | earlier ',' list_elements ',' later { result = [val[0]] + val[2] + [val[4]] }
230
+ | earlier ',' list_elements { result = [val[0]] + val[2] }
231
+ | earlier ',' later { result = [val[0]] + [val[2]] }
232
+ | list_elements ',' later { result = val[0] + [val[2]] }
171
233
  | list_elements
172
- | later { result = [val[0]] }
173
-
174
- list_elements : list_element { result = [val[0]].flatten }
175
- | list_elements COMMA list_element { result = val[0] + [val[2]].flatten }
176
-
234
+ | later { result = val }
235
+ ;
236
+
237
+ list_elements : list_element { result = [val[0]].flatten }
238
+ | list_elements ',' list_element { result = val[0] + [val[2]].flatten }
239
+ ;
240
+
177
241
  list_element : date
178
- # | date_with_internal_uncertainty
179
- | uncertain_or_approximate_date
242
+ | internal_uncertain_or_approximate_date
180
243
  | unspecified
181
- | consecutives { result = val[0].map { |d| Date.new(*d) } }
182
-
183
- earlier : DOTS date { result = val[1] }
184
-
185
- later : year_month_day DOTS { result = Date.new(*val[0]); result.precision = :day }
186
- | year_month DOTS { result = Date.new(*val[0]); result.precision = :month }
187
- | year DOTS { result = Date.new(val[0]); result.precision = :year }
188
-
244
+ | consecutives { result = val[0].map { |d| Date.new(*d) } }
245
+ ;
246
+
247
+ earlier : DOTS date { result = val[1] }
248
+
249
+ later : year_month_day DOTS { result = Date.new(*val[0]); result.precision = :day }
250
+ | year_month DOTS { result = Date.new(*val[0]); result.precision = :month }
251
+ | year DOTS { result = Date.new(val[0]); result.precision = :year }
252
+ ;
253
+
189
254
  consecutives : year_month_day DOTS year_month_day
190
255
  | year_month DOTS year_month
191
- | year DOTS year { result = (val[0]..val[2]).to_a.map }
192
-
193
-
194
- internal_unspecified : unspecified_year MINUS month MINUS d01_31 { result = Date.new(val[0][0], val[2], val[4]); result.unspecified.year[2,2] = val[0][1] }
195
- | unspecified_year MINUS UNSPECIFIED UNSPECIFIED MINUS d01_31 { result = Date.new(val[0][0], 1, val[5]); result.unspecified.year[2,2] = val[0][1]; result.unspecified!(:month) }
196
- | unspecified_year MINUS UNSPECIFIED UNSPECIFIED MINUS UNSPECIFIED UNSPECIFIED { result = Date.new(val[0][0], 1, 1); result.unspecified.year[2,2] = val[0][1]; result.unspecified!([:month, :day]) }
197
- | unspecified_year MINUS month MINUS UNSPECIFIED UNSPECIFIED { result = Date.new(val[0][0], val[2], 1); result.unspecified.year[2,2] = val[0][1]; result.unspecified!(:day) }
198
- | year MINUS UNSPECIFIED UNSPECIFIED MINUS d01_31 { result = Date.new(val[0], 1, val[5]); result.unspecified!(:month) }
199
-
256
+ | year DOTS year { result = (val[0]..val[2]).to_a.map }
257
+ ;
258
+
259
+ internal_unspecified :
260
+ unspecified_year '-' month '-' d01_31
261
+ {
262
+ result = Date.new(val[0][0], val[2], val[4])
263
+ result.unspecified.year[2,2] = val[0][1]
264
+ }
265
+ | unspecified_year '-' U U '-' d01_31
266
+ {
267
+ result = Date.new(val[0][0], 1, val[5])
268
+ result.unspecified.year[2,2] = val[0][1]
269
+ result.unspecified!(:month)
270
+ }
271
+ | unspecified_year '-' U U '-' U U
272
+ {
273
+ result = Date.new(val[0][0], 1, 1)
274
+ result.unspecified.year[2,2] = val[0][1]
275
+ result.unspecified!([:month, :day])
276
+ }
277
+ | unspecified_year '-' month '-' U U
278
+ {
279
+ result = Date.new(val[0][0], val[2], 1)
280
+ result.unspecified.year[2,2] = val[0][1]
281
+ result.unspecified!(:day)
282
+ }
283
+ | year '-' U U '-' d01_31
284
+ {
285
+ result = Date.new(val[0], 1, val[5])
286
+ result.unspecified!(:month)
287
+ }
288
+ ;
289
+
290
+ internal_uncertain_or_approximate_date : internal_uncertain_or_approximate
291
+ | '(' internal_uncertain_or_approximate ')' ua { result = uoa(val[1], val[3]) }
292
+
293
+ internal_uncertain_or_approximate :
294
+ iua_year { result = val[0]; result.precision = :year }
295
+ | iua_year_month { result = val[0]; result.precision = :month }
296
+ | iua_year_month_day
297
+
298
+ iua_year : year ua { result = uoa(Date.new(val[0]), val[1], :year) }
299
+
300
+ iua_year_month :
301
+ iua_year '-' month opt_ua
302
+ {
303
+ result = uoa(val[0].change(:month => val[2]), val[3], [:month, :year])
304
+ }
305
+ | '(' iua_year IUA month opt_ua
306
+ {
307
+ result = uoa(uoa(val[1], val[2], :year).change(:month => val[3]), val[4], :month)
308
+ }
309
+ | year '-' month ua
310
+ {
311
+ result = uoa(Date.new(val[0], val[2]), val[3], [:year, :month])
312
+ }
313
+ | year '-' '(' month ')' ua
314
+ {
315
+ result = uoa(Date.new(val[0], val[3]), val[5], [:month])
316
+ }
317
+ ;
318
+
319
+ iua_year_month_day :
320
+ iua_year_month '-' d01_31 opt_ua
321
+ {
322
+ result = uoa(val[0].change(:day => val[2]), val[3])
323
+ }
324
+ | iua_year_month '-' '(' d01_31 ')' ua
325
+ {
326
+ result = uoa(val[0].change(:day => val[3]), val[5], [:day])
327
+ }
328
+ | '(' iua_year_month IUA d01_31 opt_ua
329
+ {
330
+ result = uoa(uoa(val[1], val[2], [:year, :month]).change(:day => val[3]), val[4], :day)
331
+ }
332
+ | year '-' '(' month IUA d01_31 opt_ua
333
+ {
334
+ result = uoa(uoa(Date.new(val[0], val[3], val[5]), val[4], :month), val[6], :day)
335
+ }
336
+ | year_month '-' d01_31 ua
337
+ {
338
+ result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3])
339
+ }
340
+ | year_month '-' '(' d01_31 ')' ua
341
+ {
342
+ result = uoa(Date.new(val[0][0], val[0][1], val[3]), val[5], [:day])
343
+ }
344
+ | year '-' '(' month '-' d01_31 ')' ua
345
+ {
346
+ result = uoa(Date.new(val[0], val[3], val[5]), val[7], [:month, :day])
347
+ }
348
+ ;
349
+
350
+ opt_ua : { result = [] } | ua
351
+
352
+ ua : '?'
353
+ | '~'
354
+ | '?' '~' { result = val.flatten }
355
+ ;
200
356
 
201
357
  # ---- Auxiliary Rules ----
202
358
 
203
- digit : D0 { result = 0 }
359
+ digit : '0' { result = 0 }
204
360
  | positive_digit
205
-
206
- positive_digit : D1 { result = 1 }
207
- | D2 { result = 2 }
208
- | D3 { result = 3 }
209
- | D4 { result = 4 }
210
- | D5 { result = 5 }
211
- | D6 { result = 6 }
212
- | D7 { result = 7 }
213
- | D8 { result = 8 }
214
- | D9 { result = 9 }
215
-
216
- d01_12 : D0 positive_digit { result = val[1] }
217
- | D1 D0 { result = 10 }
218
- | D1 D1 { result = 11 }
219
- | D1 D2 { result = 12 }
361
+ ;
362
+
363
+ positive_digit : '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
364
+
365
+ d01_12 : '0' positive_digit { result = val[1] }
366
+ | '1' '0' { result = 10 }
367
+ | '1' '1' { result = 11 }
368
+ | '1' '2' { result = 12 }
369
+ ;
220
370
 
221
371
  d01_13 : d01_12
222
- | D1 D3 { result = 13 }
223
-
224
- d01_23 : D0 positive_digit { result = val[1] }
225
- | D1 digit { result = 10 + val[1] }
226
- | D2 D0 { result = 20 }
227
- | D2 D1 { result = 21 }
228
- | D2 D2 { result = 22 }
229
- | D2 D3 { result = 23 }
230
-
231
- d00_23 : D0 D0 { result = 0 }
372
+ | '1' '3' { result = 13 }
373
+ ;
374
+
375
+ d01_23 : '0' positive_digit { result = val[1] }
376
+ | '1' digit { result = 10 + val[1] }
377
+ | '2' '0' { result = 20 }
378
+ | '2' '1' { result = 21 }
379
+ | '2' '2' { result = 22 }
380
+ | '2' '3' { result = 23 }
381
+ ;
382
+
383
+ d00_23 : '0' '0'
232
384
  | d01_23
385
+ ;
233
386
 
234
387
  d01_29 : d01_23
235
- | D2 D4 { result = 24 }
236
- | D2 D5 { result = 25 }
237
- | D2 D6 { result = 26 }
238
- | D2 D7 { result = 27 }
239
- | D2 D8 { result = 28 }
240
- | D2 D9 { result = 29 }
388
+ | '2' '4' { result = 24 }
389
+ | '2' '5' { result = 25 }
390
+ | '2' '6' { result = 26 }
391
+ | '2' '7' { result = 27 }
392
+ | '2' '8' { result = 28 }
393
+ | '2' '9' { result = 29 }
394
+ ;
241
395
 
242
396
  d01_30 : d01_29
243
- | D3 D0 { result = 30 }
397
+ | '3' '0' { result = 30 }
398
+ ;
244
399
 
245
400
  d01_31 : d01_30
246
- | D3 D1 { result = 31 }
247
-
401
+ | '3' '1' { result = 31 }
402
+ ;
403
+
248
404
  d01_59 : d01_29
249
- | D3 digit { result = 30 + val[1] }
250
- | D4 digit { result = 40 + val[1] }
251
- | D5 digit { result = 50 + val[1] }
252
-
253
- d00_59 : D0 D0 { result = 0 }
405
+ | '3' digit { result = 30 + val[1] }
406
+ | '4' digit { result = 40 + val[1] }
407
+ | '5' digit { result = 50 + val[1] }
408
+ ;
409
+
410
+ d00_59 : '0' '0'
254
411
  | d01_59
255
-
256
- int1_4 : positive_digit { result = val[0] }
257
- | positive_digit digit { result = 10 * val[0] + val[1] }
258
- | positive_digit digit digit { result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b } }
259
- | positive_digit digit digit digit { result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b } }
412
+ ;
413
+
414
+ int1_4 : positive_digit { result = val[0] }
415
+ | positive_digit digit { result = 10 * val[0] + val[1] }
416
+ | positive_digit digit digit
417
+ {
418
+ result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
419
+ }
420
+ | positive_digit digit digit digit
421
+ {
422
+ result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
423
+ }
424
+ ;
260
425
 
261
426
  integer : positive_digit { result = val[0] }
262
- | integer digit { result = 10 * val[0] + val[1] }
427
+ | integer digit { result = 10 * val[0] + val[1] }
428
+ ;
263
429
 
264
430
 
265
431
 
@@ -291,63 +457,78 @@ require 'strscan'
291
457
  warn "failed to parse extended date time %s (%s) %s" % [val.inspect, token_to_str(tid) || '?', vstack.inspect]
292
458
  end
293
459
 
460
+ def apply_uncertainty(date, uncertainty, scope = nil)
461
+ uncertainty.each do |u|
462
+ scope.nil? ? date.send(u) : date.send(u, scope)
463
+ end
464
+ date
465
+ end
466
+
467
+ alias uoa apply_uncertainty
468
+
294
469
  def next_token
295
- case
296
- when @src.eos?
297
- nil
298
- # when @src.scan(/\s+/)
299
- # ignore whitespace
300
- when @src.scan(/\(/)
301
- [:LP, @src.matched]
302
- when @src.scan(/\)/)
303
- [:RP, @src.matched]
304
- when @src.scan(/\[/)
305
- [:LSQUARE, @src.matched]
306
- when @src.scan(/\]/)
307
- [:RSQUARE, @src.matched]
308
- when @src.scan(/\{/)
309
- [:LBRACE, @src.matched]
310
- when @src.scan(/\}/)
311
- [:RBRACE, @src.matched]
312
- when @src.scan(/T/)
313
- [:T, @src.matched]
314
- when @src.scan(/Z/)
315
- [:Z, @src.matched]
316
- when @src.scan(/\?/)
317
- [:UNCERTAIN, @src.matched]
318
- when @src.scan(/~/)
319
- [:APPROXIMATE, @src.matched]
320
- when @src.scan(/open/i)
321
- [:OPEN, @src.matched]
322
- when @src.scan(/unkn?own/i) # matches 'unkown' typo too
323
- [:UNKNOWN, @src.matched]
324
- when @src.scan(/u/)
325
- [:UNSPECIFIED, @src.matched]
326
- when @src.scan(/x/i)
327
- [:X, @src.matched]
328
- when @src.scan(/y/)
329
- [:LONGYEAR, @src.matched]
330
- when @src.scan(/e/)
331
- [:E, @src.matched]
332
- when @src.scan(/\+/)
333
- [:PLUS, @src.matched]
334
- when @src.scan(/-/)
335
- [:MINUS, @src.matched]
336
- when @src.scan(/:/)
337
- [:COLON, @src.matched]
338
- when @src.scan(/\//)
339
- [:SLASH, @src.matched]
340
- when @src.scan(/\s*\.\.\s*/)
341
- [:DOTS, '..']
342
- when @src.scan(/\s*,\s*/)
343
- [:COMMA, ',']
344
- when @src.scan(/\^\w+/)
345
- [:CARET, @src.matched[1..-1]]
346
- when @src.scan(/\d/)
347
- [['D', @src.matched].join.intern, @src.matched]
348
- else @src.scan(/./)
349
- [:UNMATCHED, @src.rest]
350
- end
470
+ case
471
+ when @src.eos?
472
+ nil
473
+ # when @src.scan(/\s+/)
474
+ # ignore whitespace
475
+ when @src.scan(/\(/)
476
+ ['(', @src.matched]
477
+ when @src.scan(/\)\?~-/)
478
+ [:IUA, [:uncertain!, :approximate!]]
479
+ when @src.scan(/\)\?-/)
480
+ [:IUA, [:uncertain!]]
481
+ when @src.scan(/\)~-/)
482
+ [:IUA, [:approximate!]]
483
+ when @src.scan(/\)/)
484
+ [')', @src.matched]
485
+ when @src.scan(/\[/)
486
+ ['[', @src.matched]
487
+ when @src.scan(/\]/)
488
+ [']', @src.matched]
489
+ when @src.scan(/\{/)
490
+ ['{', @src.matched]
491
+ when @src.scan(/\}/)
492
+ ['}', @src.matched]
493
+ when @src.scan(/T/)
494
+ [:T, @src.matched]
495
+ when @src.scan(/Z/)
496
+ [:Z, @src.matched]
497
+ when @src.scan(/\?/)
498
+ ['?', [:uncertain!]]
499
+ when @src.scan(/~/)
500
+ ['~', [:approximate!]]
501
+ when @src.scan(/open/i)
502
+ [:OPEN, :open]
503
+ when @src.scan(/unkn?own/i) # matches 'unkown' typo too
504
+ [:UNKNOWN, :unknown]
505
+ when @src.scan(/u/)
506
+ [:U, @src.matched]
507
+ when @src.scan(/x/i)
508
+ [:X, @src.matched]
509
+ when @src.scan(/y/)
510
+ [:LONGYEAR, @src.matched]
511
+ when @src.scan(/e/)
512
+ [:E, @src.matched]
513
+ when @src.scan(/\+/)
514
+ ['+', @src.matched]
515
+ when @src.scan(/-/)
516
+ ['-', @src.matched]
517
+ when @src.scan(/:/)
518
+ [':', @src.matched]
519
+ when @src.scan(/\//)
520
+ ['/', @src.matched]
521
+ when @src.scan(/\s*\.\.\s*/)
522
+ [:DOTS, '..']
523
+ when @src.scan(/\s*,\s*/)
524
+ [',', ',']
525
+ when @src.scan(/\^\w+/)
526
+ ['^', @src.matched[1..-1]]
527
+ when @src.scan(/\d/)
528
+ [@src.matched, @src.matched.to_i]
529
+ else @src.scan(/./)
530
+ [:UNMATCHED, @src.rest]
531
+ end
351
532
  end
352
533
 
353
534