chronic 0.2.2 → 0.10.2
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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +8 -0
- data/HISTORY.md +243 -0
- data/LICENSE +21 -0
- data/README.md +182 -0
- data/Rakefile +62 -15
- data/chronic.gemspec +23 -0
- data/lib/chronic/date.rb +82 -0
- data/lib/chronic/grabber.rb +24 -17
- data/lib/chronic/handler.rb +97 -0
- data/lib/chronic/handlers.rb +490 -312
- data/lib/chronic/mini_date.rb +38 -0
- data/lib/chronic/numerizer.rb +130 -0
- data/lib/chronic/ordinal.rb +33 -24
- data/lib/chronic/parser.rb +268 -0
- data/lib/chronic/pointer.rb +22 -17
- data/lib/chronic/repeater.rb +137 -107
- data/lib/chronic/repeaters/repeater_day.rb +51 -44
- data/lib/chronic/repeaters/repeater_day_name.rb +50 -43
- data/lib/chronic/repeaters/repeater_day_portion.rb +97 -81
- data/lib/chronic/repeaters/repeater_fortnight.rb +61 -54
- data/lib/chronic/repeaters/repeater_hour.rb +51 -44
- data/lib/chronic/repeaters/repeater_minute.rb +51 -44
- data/lib/chronic/repeaters/repeater_month.rb +79 -60
- data/lib/chronic/repeaters/repeater_month_name.rb +85 -83
- data/lib/chronic/repeaters/repeater_season.rb +110 -22
- data/lib/chronic/repeaters/repeater_season_name.rb +41 -22
- data/lib/chronic/repeaters/repeater_second.rb +41 -34
- data/lib/chronic/repeaters/repeater_time.rb +128 -104
- data/lib/chronic/repeaters/repeater_week.rb +65 -58
- data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
- data/lib/chronic/repeaters/repeater_weekend.rb +59 -52
- data/lib/chronic/repeaters/repeater_year.rb +72 -52
- data/lib/chronic/scalar.rb +53 -46
- data/lib/chronic/season.rb +26 -0
- data/lib/chronic/separator.rb +174 -43
- data/lib/chronic/sign.rb +49 -0
- data/lib/chronic/span.rb +31 -0
- data/lib/chronic/tag.rb +37 -0
- data/lib/chronic/time.rb +40 -0
- data/lib/chronic/time_zone.rb +21 -11
- data/lib/chronic/token.rb +51 -0
- data/lib/chronic.rb +94 -69
- data/test/helper.rb +12 -0
- data/test/test_chronic.rb +183 -0
- data/test/test_daylight_savings.rb +118 -0
- data/test/{test_Handler.rb → test_handler.rb} +73 -55
- data/test/test_mini_date.rb +32 -0
- data/test/test_numerizer.rb +86 -0
- data/test/test_parsing.rb +891 -248
- data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +16 -17
- data/test/test_repeater_day_portion.rb +254 -0
- data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +17 -18
- data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +23 -20
- data/test/test_repeater_minute.rb +34 -0
- data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +21 -18
- data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +18 -19
- data/test/test_repeater_season.rb +40 -0
- data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +39 -23
- data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +17 -18
- data/test/test_repeater_weekday.rb +55 -0
- data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +22 -23
- data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +25 -19
- data/test/{test_Span.rb → test_span.rb} +7 -8
- data/test/{test_Token.rb → test_token.rb} +6 -7
- metadata +163 -85
- data/History.txt +0 -49
- data/Manifest.txt +0 -47
- data/README.txt +0 -149
- data/lib/chronic/chronic.rb +0 -239
- data/lib/numerizer/numerizer.rb +0 -103
- data/test/suite.rb +0 -9
- data/test/test_Chronic.rb +0 -50
- data/test/test_Numerizer.rb +0 -48
- data/test/test_Time.rb +0 -50
data/lib/chronic/handlers.rb
CHANGED
|
@@ -1,122 +1,119 @@
|
|
|
1
1
|
module Chronic
|
|
2
|
+
module Handlers
|
|
3
|
+
module_function
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],
|
|
30
|
-
|
|
31
|
-
# 3rd week in march
|
|
32
|
-
:narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
|
|
33
|
-
Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]
|
|
34
|
-
}
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def tokens_to_span(tokens, options) #:nodoc:
|
|
38
|
-
# maybe it's a specific date
|
|
39
|
-
|
|
40
|
-
self.definitions[:date].each do |handler|
|
|
41
|
-
if handler.match(tokens, self.definitions)
|
|
42
|
-
puts "-date" if Chronic.debug
|
|
43
|
-
good_tokens = tokens.select { |o| !o.get_tag Separator }
|
|
44
|
-
return self.send(handler.handler_method, good_tokens, options)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# I guess it's not a specific date, maybe it's just an anchor
|
|
49
|
-
|
|
50
|
-
self.definitions[:anchor].each do |handler|
|
|
51
|
-
if handler.match(tokens, self.definitions)
|
|
52
|
-
puts "-anchor" if Chronic.debug
|
|
53
|
-
good_tokens = tokens.select { |o| !o.get_tag Separator }
|
|
54
|
-
return self.send(handler.handler_method, good_tokens, options)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# not an anchor, perhaps it's an arrow
|
|
59
|
-
|
|
60
|
-
self.definitions[:arrow].each do |handler|
|
|
61
|
-
if handler.match(tokens, self.definitions)
|
|
62
|
-
puts "-arrow" if Chronic.debug
|
|
63
|
-
good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
|
|
64
|
-
return self.send(handler.handler_method, good_tokens, options)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# not an arrow, let's hope it's a narrow
|
|
69
|
-
|
|
70
|
-
self.definitions[:narrow].each do |handler|
|
|
71
|
-
if handler.match(tokens, self.definitions)
|
|
72
|
-
puts "-narrow" if Chronic.debug
|
|
73
|
-
#good_tokens = tokens.select { |o| !o.get_tag Separator }
|
|
74
|
-
return self.send(handler.handler_method, tokens, options)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# I guess you're out of luck!
|
|
79
|
-
puts "-none" if Chronic.debug
|
|
80
|
-
return nil
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
#--------------
|
|
84
|
-
|
|
85
|
-
def day_or_time(day_start, time_tokens, options)
|
|
86
|
-
outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
|
|
87
|
-
|
|
88
|
-
if !time_tokens.empty?
|
|
89
|
-
@now = outer_span.begin
|
|
90
|
-
time = get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
|
|
91
|
-
return time
|
|
5
|
+
# Handle month/day
|
|
6
|
+
def handle_m_d(month, day, time_tokens, options)
|
|
7
|
+
month.start = self.now
|
|
8
|
+
span = month.this(options[:context])
|
|
9
|
+
year, month = span.begin.year, span.begin.month
|
|
10
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
11
|
+
|
|
12
|
+
day_or_time(day_start, time_tokens, options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Handle repeater-month-name/scalar-day
|
|
16
|
+
def handle_rmn_sd(tokens, options)
|
|
17
|
+
month = tokens[0].get_tag(RepeaterMonthName)
|
|
18
|
+
day = tokens[1].get_tag(ScalarDay).type
|
|
19
|
+
|
|
20
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
21
|
+
|
|
22
|
+
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Handle repeater-month-name/scalar-day with separator-on
|
|
26
|
+
def handle_rmn_sd_on(tokens, options)
|
|
27
|
+
if tokens.size > 3
|
|
28
|
+
month = tokens[2].get_tag(RepeaterMonthName)
|
|
29
|
+
day = tokens[3].get_tag(ScalarDay).type
|
|
30
|
+
token_range = 0..1
|
|
92
31
|
else
|
|
93
|
-
|
|
32
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
33
|
+
day = tokens[2].get_tag(ScalarDay).type
|
|
34
|
+
token_range = 0..0
|
|
94
35
|
end
|
|
36
|
+
|
|
37
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
38
|
+
|
|
39
|
+
handle_m_d(month, day, tokens[token_range], options)
|
|
95
40
|
end
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
day_or_time(day_start, time_tokens, options)
|
|
41
|
+
|
|
42
|
+
# Handle repeater-month-name/ordinal-day
|
|
43
|
+
def handle_rmn_od(tokens, options)
|
|
44
|
+
month = tokens[0].get_tag(RepeaterMonthName)
|
|
45
|
+
day = tokens[1].get_tag(OrdinalDay).type
|
|
46
|
+
|
|
47
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
48
|
+
|
|
49
|
+
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
106
50
|
end
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
51
|
+
|
|
52
|
+
# Handle ordinal this month
|
|
53
|
+
def handle_od_rm(tokens, options)
|
|
54
|
+
day = tokens[0].get_tag(OrdinalDay).type
|
|
55
|
+
month = tokens[2].get_tag(RepeaterMonth)
|
|
56
|
+
handle_m_d(month, day, tokens[3..tokens.size], options)
|
|
110
57
|
end
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
58
|
+
|
|
59
|
+
# Handle ordinal-day/repeater-month-name
|
|
60
|
+
def handle_od_rmn(tokens, options)
|
|
61
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
62
|
+
day = tokens[0].get_tag(OrdinalDay).type
|
|
63
|
+
|
|
64
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
65
|
+
|
|
66
|
+
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
114
67
|
end
|
|
115
|
-
|
|
116
|
-
def
|
|
68
|
+
|
|
69
|
+
def handle_sy_rmn_od(tokens, options)
|
|
70
|
+
year = tokens[0].get_tag(ScalarYear).type
|
|
71
|
+
month = tokens[1].get_tag(RepeaterMonthName).index
|
|
72
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
73
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
74
|
+
|
|
75
|
+
return if month_overflow?(year, month, day)
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
79
|
+
day_or_time(day_start, time_tokens, options)
|
|
80
|
+
rescue ArgumentError
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Handle scalar-day/repeater-month-name
|
|
86
|
+
def handle_sd_rmn(tokens, options)
|
|
87
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
88
|
+
day = tokens[0].get_tag(ScalarDay).type
|
|
89
|
+
|
|
90
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
91
|
+
|
|
92
|
+
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Handle repeater-month-name/ordinal-day with separator-on
|
|
96
|
+
def handle_rmn_od_on(tokens, options)
|
|
97
|
+
if tokens.size > 3
|
|
98
|
+
month = tokens[2].get_tag(RepeaterMonthName)
|
|
99
|
+
day = tokens[3].get_tag(OrdinalDay).type
|
|
100
|
+
token_range = 0..1
|
|
101
|
+
else
|
|
102
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
103
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
104
|
+
token_range = 0..0
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
108
|
+
|
|
109
|
+
handle_m_d(month, day, tokens[token_range], options)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Handle repeater-month-name/scalar-year
|
|
113
|
+
def handle_rmn_sy(tokens, options)
|
|
117
114
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
|
118
115
|
year = tokens[1].get_tag(ScalarYear).type
|
|
119
|
-
|
|
116
|
+
|
|
120
117
|
if month == 12
|
|
121
118
|
next_month_year = year + 1
|
|
122
119
|
next_month_month = 1
|
|
@@ -124,79 +121,138 @@ module Chronic
|
|
|
124
121
|
next_month_year = year
|
|
125
122
|
next_month_month = month + 1
|
|
126
123
|
end
|
|
127
|
-
|
|
124
|
+
|
|
128
125
|
begin
|
|
129
|
-
|
|
126
|
+
end_time = Chronic.time_class.local(next_month_year, next_month_month)
|
|
127
|
+
Span.new(Chronic.time_class.local(year, month), end_time)
|
|
130
128
|
rescue ArgumentError
|
|
131
129
|
nil
|
|
132
130
|
end
|
|
133
131
|
end
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
|
|
133
|
+
# Handle generic timestamp (ruby 1.8)
|
|
134
|
+
def handle_generic(tokens, options)
|
|
135
|
+
t = Chronic.time_class.parse(options[:text])
|
|
136
|
+
Span.new(t, t + 1)
|
|
137
|
+
rescue ArgumentError => e
|
|
138
|
+
raise e unless e.message =~ /out of range/
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Handle repeater-month-name/scalar-day/scalar-year
|
|
142
|
+
def handle_rmn_sd_sy(tokens, options)
|
|
143
|
+
month = tokens[0].get_tag(RepeaterMonthName).index
|
|
144
|
+
day = tokens[1].get_tag(ScalarDay).type
|
|
145
|
+
year = tokens[2].get_tag(ScalarYear).type
|
|
146
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
147
|
+
|
|
148
|
+
return if month_overflow?(year, month, day)
|
|
149
|
+
|
|
140
150
|
begin
|
|
141
|
-
day_start =
|
|
142
|
-
day_or_time(day_start,
|
|
151
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
152
|
+
day_or_time(day_start, time_tokens, options)
|
|
143
153
|
rescue ArgumentError
|
|
144
154
|
nil
|
|
145
155
|
end
|
|
146
156
|
end
|
|
147
|
-
|
|
148
|
-
|
|
157
|
+
|
|
158
|
+
# Handle repeater-month-name/ordinal-day/scalar-year
|
|
159
|
+
def handle_rmn_od_sy(tokens, options)
|
|
149
160
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
|
150
|
-
day = tokens[1].get_tag(
|
|
161
|
+
day = tokens[1].get_tag(OrdinalDay).type
|
|
151
162
|
year = tokens[2].get_tag(ScalarYear).type
|
|
152
|
-
|
|
153
163
|
time_tokens = tokens.last(tokens.size - 3)
|
|
154
|
-
|
|
164
|
+
|
|
165
|
+
return if month_overflow?(year, month, day)
|
|
166
|
+
|
|
155
167
|
begin
|
|
156
|
-
day_start =
|
|
168
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
157
169
|
day_or_time(day_start, time_tokens, options)
|
|
158
170
|
rescue ArgumentError
|
|
159
171
|
nil
|
|
160
172
|
end
|
|
161
173
|
end
|
|
162
|
-
|
|
163
|
-
|
|
174
|
+
|
|
175
|
+
# Handle oridinal-day/repeater-month-name/scalar-year
|
|
176
|
+
def handle_od_rmn_sy(tokens, options)
|
|
177
|
+
day = tokens[0].get_tag(OrdinalDay).type
|
|
178
|
+
month = tokens[1].get_tag(RepeaterMonthName).index
|
|
179
|
+
year = tokens[2].get_tag(ScalarYear).type
|
|
180
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
181
|
+
|
|
182
|
+
return if month_overflow?(year, month, day)
|
|
183
|
+
|
|
184
|
+
begin
|
|
185
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
186
|
+
day_or_time(day_start, time_tokens, options)
|
|
187
|
+
rescue ArgumentError
|
|
188
|
+
nil
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Handle scalar-day/repeater-month-name/scalar-year
|
|
193
|
+
def handle_sd_rmn_sy(tokens, options)
|
|
164
194
|
new_tokens = [tokens[1], tokens[0], tokens[2]]
|
|
165
195
|
time_tokens = tokens.last(tokens.size - 3)
|
|
166
|
-
|
|
196
|
+
handle_rmn_sd_sy(new_tokens + time_tokens, options)
|
|
167
197
|
end
|
|
168
|
-
|
|
169
|
-
|
|
198
|
+
|
|
199
|
+
# Handle scalar-month/scalar-day/scalar-year (endian middle)
|
|
200
|
+
def handle_sm_sd_sy(tokens, options)
|
|
170
201
|
month = tokens[0].get_tag(ScalarMonth).type
|
|
171
202
|
day = tokens[1].get_tag(ScalarDay).type
|
|
172
203
|
year = tokens[2].get_tag(ScalarYear).type
|
|
173
|
-
|
|
174
204
|
time_tokens = tokens.last(tokens.size - 3)
|
|
175
|
-
|
|
205
|
+
|
|
206
|
+
return if month_overflow?(year, month, day)
|
|
207
|
+
|
|
176
208
|
begin
|
|
177
|
-
day_start =
|
|
209
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
178
210
|
day_or_time(day_start, time_tokens, options)
|
|
179
211
|
rescue ArgumentError
|
|
180
212
|
nil
|
|
181
213
|
end
|
|
182
214
|
end
|
|
183
|
-
|
|
184
|
-
|
|
215
|
+
|
|
216
|
+
# Handle scalar-day/scalar-month/scalar-year (endian little)
|
|
217
|
+
def handle_sd_sm_sy(tokens, options)
|
|
185
218
|
new_tokens = [tokens[1], tokens[0], tokens[2]]
|
|
186
219
|
time_tokens = tokens.last(tokens.size - 3)
|
|
187
|
-
|
|
220
|
+
handle_sm_sd_sy(new_tokens + time_tokens, options)
|
|
188
221
|
end
|
|
189
|
-
|
|
190
|
-
|
|
222
|
+
|
|
223
|
+
# Handle scalar-year/scalar-month/scalar-day
|
|
224
|
+
def handle_sy_sm_sd(tokens, options)
|
|
191
225
|
new_tokens = [tokens[1], tokens[2], tokens[0]]
|
|
192
226
|
time_tokens = tokens.last(tokens.size - 3)
|
|
193
|
-
|
|
227
|
+
handle_sm_sd_sy(new_tokens + time_tokens, options)
|
|
194
228
|
end
|
|
195
|
-
|
|
196
|
-
|
|
229
|
+
|
|
230
|
+
# Handle scalar-month/scalar-day
|
|
231
|
+
def handle_sm_sd(tokens, options)
|
|
197
232
|
month = tokens[0].get_tag(ScalarMonth).type
|
|
198
|
-
|
|
199
|
-
|
|
233
|
+
day = tokens[1].get_tag(ScalarDay).type
|
|
234
|
+
year = self.now.year
|
|
235
|
+
time_tokens = tokens.last(tokens.size - 2)
|
|
236
|
+
|
|
237
|
+
return if month_overflow?(year, month, day)
|
|
238
|
+
|
|
239
|
+
begin
|
|
240
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
241
|
+
day_start = Chronic.time_class.local(year + 1, month, day) if options[:context] == :future && day_start < now
|
|
242
|
+
day_or_time(day_start, time_tokens, options)
|
|
243
|
+
rescue ArgumentError
|
|
244
|
+
nil
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Handle scalar-day/scalar-month
|
|
249
|
+
def handle_sd_sm(tokens, options)
|
|
250
|
+
new_tokens = [tokens[1], tokens[0]]
|
|
251
|
+
time_tokens = tokens.last(tokens.size - 2)
|
|
252
|
+
handle_sm_sd(new_tokens + time_tokens, options)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def handle_year_and_month(year, month)
|
|
200
256
|
if month == 12
|
|
201
257
|
next_month_year = year + 1
|
|
202
258
|
next_month_month = 1
|
|
@@ -204,161 +260,334 @@ module Chronic
|
|
|
204
260
|
next_month_year = year
|
|
205
261
|
next_month_month = month + 1
|
|
206
262
|
end
|
|
207
|
-
|
|
263
|
+
|
|
264
|
+
begin
|
|
265
|
+
end_time = Chronic.time_class.local(next_month_year, next_month_month)
|
|
266
|
+
Span.new(Chronic.time_class.local(year, month), end_time)
|
|
267
|
+
rescue ArgumentError
|
|
268
|
+
nil
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Handle scalar-month/scalar-year
|
|
273
|
+
def handle_sm_sy(tokens, options)
|
|
274
|
+
month = tokens[0].get_tag(ScalarMonth).type
|
|
275
|
+
year = tokens[1].get_tag(ScalarYear).type
|
|
276
|
+
handle_year_and_month(year, month)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Handle scalar-year/scalar-month
|
|
280
|
+
def handle_sy_sm(tokens, options)
|
|
281
|
+
year = tokens[0].get_tag(ScalarYear).type
|
|
282
|
+
month = tokens[1].get_tag(ScalarMonth).type
|
|
283
|
+
handle_year_and_month(year, month)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Handle RepeaterDayName RepeaterMonthName OrdinalDay
|
|
287
|
+
def handle_rdn_rmn_od(tokens, options)
|
|
288
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
289
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
290
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
291
|
+
year = self.now.year
|
|
292
|
+
|
|
293
|
+
return if month_overflow?(year, month.index, day)
|
|
294
|
+
|
|
295
|
+
begin
|
|
296
|
+
if time_tokens.empty?
|
|
297
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
298
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
299
|
+
Span.new(start_time, end_time)
|
|
300
|
+
else
|
|
301
|
+
day_start = Chronic.time_class.local(year, month.index, day)
|
|
302
|
+
day_or_time(day_start, time_tokens, options)
|
|
303
|
+
end
|
|
304
|
+
rescue ArgumentError
|
|
305
|
+
nil
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Handle RepeaterDayName RepeaterMonthName OrdinalDay ScalarYear
|
|
310
|
+
def handle_rdn_rmn_od_sy(tokens, options)
|
|
311
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
312
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
313
|
+
year = tokens[3].get_tag(ScalarYear).type
|
|
314
|
+
|
|
315
|
+
return if month_overflow?(year, month.index, day)
|
|
316
|
+
|
|
208
317
|
begin
|
|
209
|
-
|
|
318
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
319
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
320
|
+
Span.new(start_time, end_time)
|
|
210
321
|
rescue ArgumentError
|
|
211
322
|
nil
|
|
212
323
|
end
|
|
213
324
|
end
|
|
214
|
-
|
|
325
|
+
|
|
326
|
+
# Handle RepeaterDayName OrdinalDay
|
|
327
|
+
def handle_rdn_od(tokens, options)
|
|
328
|
+
day = tokens[1].get_tag(OrdinalDay).type
|
|
329
|
+
time_tokens = tokens.last(tokens.size - 2)
|
|
330
|
+
year = self.now.year
|
|
331
|
+
month = self.now.month
|
|
332
|
+
if options[:context] == :future
|
|
333
|
+
self.now.day > day ? month += 1 : month
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
return if month_overflow?(year, month, day)
|
|
337
|
+
|
|
338
|
+
begin
|
|
339
|
+
if time_tokens.empty?
|
|
340
|
+
start_time = Chronic.time_class.local(year, month, day)
|
|
341
|
+
end_time = time_with_rollover(year, month, day + 1)
|
|
342
|
+
Span.new(start_time, end_time)
|
|
343
|
+
else
|
|
344
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
345
|
+
day_or_time(day_start, time_tokens, options)
|
|
346
|
+
end
|
|
347
|
+
rescue ArgumentError
|
|
348
|
+
nil
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Handle RepeaterDayName RepeaterMonthName ScalarDay
|
|
353
|
+
def handle_rdn_rmn_sd(tokens, options)
|
|
354
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
355
|
+
day = tokens[2].get_tag(ScalarDay).type
|
|
356
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
357
|
+
year = self.now.year
|
|
358
|
+
|
|
359
|
+
return if month_overflow?(year, month.index, day)
|
|
360
|
+
|
|
361
|
+
begin
|
|
362
|
+
if time_tokens.empty?
|
|
363
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
364
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
365
|
+
Span.new(start_time, end_time)
|
|
366
|
+
else
|
|
367
|
+
day_start = Chronic.time_class.local(year, month.index, day)
|
|
368
|
+
day_or_time(day_start, time_tokens, options)
|
|
369
|
+
end
|
|
370
|
+
rescue ArgumentError
|
|
371
|
+
nil
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Handle RepeaterDayName RepeaterMonthName ScalarDay ScalarYear
|
|
376
|
+
def handle_rdn_rmn_sd_sy(tokens, options)
|
|
377
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
378
|
+
day = tokens[2].get_tag(ScalarDay).type
|
|
379
|
+
year = tokens[3].get_tag(ScalarYear).type
|
|
380
|
+
|
|
381
|
+
return if month_overflow?(year, month.index, day)
|
|
382
|
+
|
|
383
|
+
begin
|
|
384
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
385
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
386
|
+
Span.new(start_time, end_time)
|
|
387
|
+
rescue ArgumentError
|
|
388
|
+
nil
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def handle_sm_rmn_sy(tokens, options)
|
|
393
|
+
day = tokens[0].get_tag(ScalarDay).type
|
|
394
|
+
month = tokens[1].get_tag(RepeaterMonthName).index
|
|
395
|
+
year = tokens[2].get_tag(ScalarYear).type
|
|
396
|
+
if tokens.size > 3
|
|
397
|
+
time = get_anchor([tokens.last], options).begin
|
|
398
|
+
h, m, s = time.hour, time.min, time.sec
|
|
399
|
+
time = Chronic.time_class.local(year, month, day, h, m, s)
|
|
400
|
+
end_time = Chronic.time_class.local(year, month, day + 1, h, m, s)
|
|
401
|
+
else
|
|
402
|
+
time = Chronic.time_class.local(year, month, day)
|
|
403
|
+
day += 1 unless day >= 31
|
|
404
|
+
end_time = Chronic.time_class.local(year, month, day)
|
|
405
|
+
end
|
|
406
|
+
Span.new(time, end_time)
|
|
407
|
+
end
|
|
408
|
+
|
|
215
409
|
# anchors
|
|
216
|
-
|
|
217
|
-
|
|
410
|
+
|
|
411
|
+
# Handle repeaters
|
|
412
|
+
def handle_r(tokens, options)
|
|
218
413
|
dd_tokens = dealias_and_disambiguate_times(tokens, options)
|
|
219
|
-
|
|
414
|
+
get_anchor(dd_tokens, options)
|
|
220
415
|
end
|
|
221
|
-
|
|
222
|
-
|
|
416
|
+
|
|
417
|
+
# Handle repeater/grabber/repeater
|
|
418
|
+
def handle_r_g_r(tokens, options)
|
|
223
419
|
new_tokens = [tokens[1], tokens[0], tokens[2]]
|
|
224
|
-
|
|
420
|
+
handle_r(new_tokens, options)
|
|
225
421
|
end
|
|
226
|
-
|
|
422
|
+
|
|
227
423
|
# arrows
|
|
228
|
-
|
|
229
|
-
|
|
424
|
+
|
|
425
|
+
# Handle scalar/repeater/pointer helper
|
|
426
|
+
def handle_srp(tokens, span, options)
|
|
230
427
|
distance = tokens[0].get_tag(Scalar).type
|
|
231
428
|
repeater = tokens[1].get_tag(Repeater)
|
|
232
429
|
pointer = tokens[2].get_tag(Pointer).type
|
|
233
|
-
|
|
234
|
-
repeater.offset(span, distance, pointer)
|
|
430
|
+
|
|
431
|
+
repeater.offset(span, distance, pointer) if repeater.respond_to?(:offset)
|
|
235
432
|
end
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
# when [RepeaterMinute, RepeaterSecond].include?(repeater.class)
|
|
247
|
-
# self.parse("this second", :guess => false, :now => @now)
|
|
248
|
-
# else
|
|
249
|
-
# raise(ChronicPain, "Invalid repeater: #{repeater.class}")
|
|
250
|
-
# end
|
|
251
|
-
|
|
252
|
-
span = self.parse("this second", :guess => false, :now => @now)
|
|
253
|
-
|
|
254
|
-
self.handle_srp(tokens, span, options)
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def handle_p_s_r(tokens, options) #:nodoc:
|
|
433
|
+
|
|
434
|
+
# Handle scalar/repeater/pointer
|
|
435
|
+
def handle_s_r_p(tokens, options)
|
|
436
|
+
span = Span.new(self.now, self.now + 1)
|
|
437
|
+
|
|
438
|
+
handle_srp(tokens, span, options)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Handle pointer/scalar/repeater
|
|
442
|
+
def handle_p_s_r(tokens, options)
|
|
258
443
|
new_tokens = [tokens[1], tokens[2], tokens[0]]
|
|
259
|
-
|
|
444
|
+
handle_s_r_p(new_tokens, options)
|
|
260
445
|
end
|
|
261
|
-
|
|
262
|
-
|
|
446
|
+
|
|
447
|
+
# Handle scalar/repeater/pointer/anchor
|
|
448
|
+
def handle_s_r_p_a(tokens, options)
|
|
263
449
|
anchor_span = get_anchor(tokens[3..tokens.size - 1], options)
|
|
264
|
-
|
|
450
|
+
handle_srp(tokens, anchor_span, options)
|
|
265
451
|
end
|
|
266
|
-
|
|
452
|
+
|
|
453
|
+
def handle_s_r_a_s_r_p_a(tokens, options)
|
|
454
|
+
anchor_span = get_anchor(tokens[4..tokens.size - 1], options)
|
|
455
|
+
|
|
456
|
+
span = handle_srp(tokens[0..1]+tokens[4..6], anchor_span, options)
|
|
457
|
+
handle_srp(tokens[2..3]+tokens[4..6], span, options)
|
|
458
|
+
end
|
|
459
|
+
|
|
267
460
|
# narrows
|
|
268
|
-
|
|
269
|
-
|
|
461
|
+
|
|
462
|
+
# Handle oridinal repeaters
|
|
463
|
+
def handle_orr(tokens, outer_span, options)
|
|
270
464
|
repeater = tokens[1].get_tag(Repeater)
|
|
271
465
|
repeater.start = outer_span.begin - 1
|
|
272
466
|
ordinal = tokens[0].get_tag(Ordinal).type
|
|
273
467
|
span = nil
|
|
468
|
+
|
|
274
469
|
ordinal.times do
|
|
275
470
|
span = repeater.next(:future)
|
|
276
|
-
|
|
471
|
+
|
|
472
|
+
if span.begin >= outer_span.end
|
|
277
473
|
span = nil
|
|
278
474
|
break
|
|
279
475
|
end
|
|
280
476
|
end
|
|
477
|
+
|
|
281
478
|
span
|
|
282
479
|
end
|
|
283
|
-
|
|
284
|
-
|
|
480
|
+
|
|
481
|
+
# Handle ordinal/repeater/separator/repeater
|
|
482
|
+
def handle_o_r_s_r(tokens, options)
|
|
285
483
|
outer_span = get_anchor([tokens[3]], options)
|
|
286
484
|
handle_orr(tokens[0..1], outer_span, options)
|
|
287
485
|
end
|
|
288
|
-
|
|
289
|
-
|
|
486
|
+
|
|
487
|
+
# Handle ordinal/repeater/grabber/repeater
|
|
488
|
+
def handle_o_r_g_r(tokens, options)
|
|
290
489
|
outer_span = get_anchor(tokens[2..3], options)
|
|
291
490
|
handle_orr(tokens[0..1], outer_span, options)
|
|
292
491
|
end
|
|
293
|
-
|
|
492
|
+
|
|
294
493
|
# support methods
|
|
295
|
-
|
|
296
|
-
def
|
|
494
|
+
|
|
495
|
+
def day_or_time(day_start, time_tokens, options)
|
|
496
|
+
outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
|
|
497
|
+
|
|
498
|
+
unless time_tokens.empty?
|
|
499
|
+
self.now = outer_span.begin
|
|
500
|
+
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options.merge(:context => :future))
|
|
501
|
+
else
|
|
502
|
+
outer_span
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def get_anchor(tokens, options)
|
|
297
507
|
grabber = Grabber.new(:this)
|
|
298
508
|
pointer = :future
|
|
299
|
-
|
|
300
|
-
repeaters = self.get_repeaters(tokens)
|
|
509
|
+
repeaters = get_repeaters(tokens)
|
|
301
510
|
repeaters.size.times { tokens.pop }
|
|
302
|
-
|
|
511
|
+
|
|
303
512
|
if tokens.first && tokens.first.get_tag(Grabber)
|
|
304
|
-
grabber = tokens.
|
|
305
|
-
tokens.pop
|
|
513
|
+
grabber = tokens.shift.get_tag(Grabber)
|
|
306
514
|
end
|
|
307
|
-
|
|
515
|
+
|
|
308
516
|
head = repeaters.shift
|
|
309
|
-
head.start =
|
|
310
|
-
|
|
517
|
+
head.start = self.now
|
|
518
|
+
|
|
311
519
|
case grabber.type
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
end
|
|
320
|
-
when :next
|
|
321
|
-
outer_span = head.next(:future)
|
|
322
|
-
else raise(ChronicPain, "Invalid grabber")
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
puts "--#{outer_span}" if Chronic.debug
|
|
326
|
-
anchor = find_within(repeaters, outer_span, pointer)
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
def get_repeaters(tokens) #:nodoc:
|
|
330
|
-
repeaters = []
|
|
331
|
-
tokens.each do |token|
|
|
332
|
-
if t = token.get_tag(Repeater)
|
|
333
|
-
repeaters << t
|
|
520
|
+
when :last
|
|
521
|
+
outer_span = head.next(:past)
|
|
522
|
+
when :this
|
|
523
|
+
if options[:context] != :past and repeaters.size > 0
|
|
524
|
+
outer_span = head.this(:none)
|
|
525
|
+
else
|
|
526
|
+
outer_span = head.this(options[:context])
|
|
334
527
|
end
|
|
528
|
+
when :next
|
|
529
|
+
outer_span = head.next(:future)
|
|
530
|
+
else
|
|
531
|
+
raise "Invalid grabber"
|
|
335
532
|
end
|
|
336
|
-
|
|
533
|
+
|
|
534
|
+
if Chronic.debug
|
|
535
|
+
puts "Handler-class: #{head.class}"
|
|
536
|
+
puts "--#{outer_span}"
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
find_within(repeaters, outer_span, pointer)
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def get_repeaters(tokens)
|
|
543
|
+
tokens.map { |token| token.get_tag(Repeater) }.compact.sort.reverse
|
|
337
544
|
end
|
|
338
|
-
|
|
545
|
+
|
|
546
|
+
def month_overflow?(year, month, day)
|
|
547
|
+
if ::Date.leap?(year)
|
|
548
|
+
day > RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
|
|
549
|
+
else
|
|
550
|
+
day > RepeaterMonth::MONTH_DAYS[month - 1]
|
|
551
|
+
end
|
|
552
|
+
rescue ArgumentError
|
|
553
|
+
false
|
|
554
|
+
end
|
|
555
|
+
|
|
339
556
|
# Recursively finds repeaters within other repeaters.
|
|
340
557
|
# Returns a Span representing the innermost time span
|
|
341
558
|
# or nil if no repeater union could be found
|
|
342
|
-
def find_within(tags, span, pointer)
|
|
559
|
+
def find_within(tags, span, pointer)
|
|
343
560
|
puts "--#{span}" if Chronic.debug
|
|
344
561
|
return span if tags.empty?
|
|
345
|
-
|
|
346
|
-
head
|
|
347
|
-
head.start = pointer == :future ? span.begin : span.end
|
|
562
|
+
|
|
563
|
+
head = tags.shift
|
|
564
|
+
head.start = (pointer == :future ? span.begin : span.end)
|
|
348
565
|
h = head.this(:none)
|
|
349
|
-
|
|
350
|
-
if span.
|
|
351
|
-
|
|
352
|
-
else
|
|
353
|
-
return nil
|
|
566
|
+
|
|
567
|
+
if span.cover?(h.begin) || span.cover?(h.end)
|
|
568
|
+
find_within(tags, h, pointer)
|
|
354
569
|
end
|
|
355
570
|
end
|
|
356
|
-
|
|
357
|
-
def
|
|
571
|
+
|
|
572
|
+
def time_with_rollover(year, month, day)
|
|
573
|
+
date_parts =
|
|
574
|
+
if month_overflow?(year, month, day)
|
|
575
|
+
if month == 12
|
|
576
|
+
[year + 1, 1, 1]
|
|
577
|
+
else
|
|
578
|
+
[year, month + 1, 1]
|
|
579
|
+
end
|
|
580
|
+
else
|
|
581
|
+
[year, month, day]
|
|
582
|
+
end
|
|
583
|
+
Chronic.time_class.local(*date_parts)
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def dealias_and_disambiguate_times(tokens, options)
|
|
358
587
|
# handle aliases of am/pm
|
|
359
588
|
# 5:00 in the morning -> 5:00 am
|
|
360
589
|
# 7:00 in the evening -> 7:00 pm
|
|
361
|
-
|
|
590
|
+
|
|
362
591
|
day_portion_index = nil
|
|
363
592
|
tokens.each_with_index do |t, i|
|
|
364
593
|
if t.get_tag(RepeaterDayPortion)
|
|
@@ -366,7 +595,7 @@ module Chronic
|
|
|
366
595
|
break
|
|
367
596
|
end
|
|
368
597
|
end
|
|
369
|
-
|
|
598
|
+
|
|
370
599
|
time_index = nil
|
|
371
600
|
tokens.each_with_index do |t, i|
|
|
372
601
|
if t.get_tag(RepeaterTime)
|
|
@@ -374,96 +603,45 @@ module Chronic
|
|
|
374
603
|
break
|
|
375
604
|
end
|
|
376
605
|
end
|
|
377
|
-
|
|
378
|
-
if
|
|
606
|
+
|
|
607
|
+
if day_portion_index && time_index
|
|
379
608
|
t1 = tokens[day_portion_index]
|
|
380
609
|
t1tag = t1.get_tag(RepeaterDayPortion)
|
|
381
|
-
|
|
382
|
-
|
|
610
|
+
|
|
611
|
+
case t1tag.type
|
|
612
|
+
when :morning
|
|
383
613
|
puts '--morning->am' if Chronic.debug
|
|
384
614
|
t1.untag(RepeaterDayPortion)
|
|
385
615
|
t1.tag(RepeaterDayPortion.new(:am))
|
|
386
|
-
|
|
616
|
+
when :afternoon, :evening, :night
|
|
387
617
|
puts "--#{t1tag.type}->pm" if Chronic.debug
|
|
388
618
|
t1.untag(RepeaterDayPortion)
|
|
389
619
|
t1.tag(RepeaterDayPortion.new(:pm))
|
|
390
620
|
end
|
|
391
621
|
end
|
|
392
|
-
|
|
393
|
-
# tokens.each_with_index do |t0, i|
|
|
394
|
-
# t1 = tokens[i + 1]
|
|
395
|
-
# if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
|
|
396
|
-
# if [:morning].include?(t1tag.type)
|
|
397
|
-
# puts '--morning->am' if Chronic.debug
|
|
398
|
-
# t1.untag(RepeaterDayPortion)
|
|
399
|
-
# t1.tag(RepeaterDayPortion.new(:am))
|
|
400
|
-
# elsif [:afternoon, :evening, :night].include?(t1tag.type)
|
|
401
|
-
# puts "--#{t1tag.type}->pm" if Chronic.debug
|
|
402
|
-
# t1.untag(RepeaterDayPortion)
|
|
403
|
-
# t1.tag(RepeaterDayPortion.new(:pm))
|
|
404
|
-
# end
|
|
405
|
-
# end
|
|
406
|
-
# end
|
|
407
|
-
|
|
622
|
+
|
|
408
623
|
# handle ambiguous times if :ambiguous_time_range is specified
|
|
409
624
|
if options[:ambiguous_time_range] != :none
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
625
|
+
ambiguous_tokens = []
|
|
626
|
+
|
|
627
|
+
tokens.each_with_index do |token, i|
|
|
628
|
+
ambiguous_tokens << token
|
|
629
|
+
next_token = tokens[i + 1]
|
|
630
|
+
|
|
631
|
+
if token.get_tag(RepeaterTime) && token.get_tag(RepeaterTime).type.ambiguous? && (!next_token || !next_token.get_tag(RepeaterDayPortion))
|
|
415
632
|
distoken = Token.new('disambiguator')
|
|
633
|
+
|
|
416
634
|
distoken.tag(RepeaterDayPortion.new(options[:ambiguous_time_range]))
|
|
417
|
-
|
|
635
|
+
ambiguous_tokens << distoken
|
|
418
636
|
end
|
|
419
637
|
end
|
|
420
|
-
|
|
638
|
+
|
|
639
|
+
tokens = ambiguous_tokens
|
|
421
640
|
end
|
|
422
|
-
|
|
641
|
+
|
|
423
642
|
tokens
|
|
424
643
|
end
|
|
425
|
-
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
class Handler #:nodoc:
|
|
429
|
-
attr_accessor :pattern, :handler_method
|
|
430
|
-
|
|
431
|
-
def initialize(pattern, handler_method)
|
|
432
|
-
@pattern = pattern
|
|
433
|
-
@handler_method = handler_method
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
def constantize(name)
|
|
437
|
-
camel = name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
|
|
438
|
-
::Chronic.module_eval(camel, __FILE__, __LINE__)
|
|
439
|
-
end
|
|
440
|
-
|
|
441
|
-
def match(tokens, definitions)
|
|
442
|
-
token_index = 0
|
|
443
|
-
@pattern.each do |element|
|
|
444
|
-
name = element.to_s
|
|
445
|
-
optional = name.reverse[0..0] == '?'
|
|
446
|
-
name = name.chop if optional
|
|
447
|
-
if element.instance_of? Symbol
|
|
448
|
-
klass = constantize(name)
|
|
449
|
-
match = tokens[token_index] && !tokens[token_index].tags.select { |o| o.kind_of?(klass) }.empty?
|
|
450
|
-
return false if !match && !optional
|
|
451
|
-
(token_index += 1; next) if match
|
|
452
|
-
next if !match && optional
|
|
453
|
-
elsif element.instance_of? String
|
|
454
|
-
return true if optional && token_index == tokens.size
|
|
455
|
-
sub_handlers = definitions[name.intern] || raise(ChronicPain, "Invalid subset #{name} specified")
|
|
456
|
-
sub_handlers.each do |sub_handler|
|
|
457
|
-
return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
|
|
458
|
-
end
|
|
459
|
-
return false
|
|
460
|
-
else
|
|
461
|
-
raise(ChronicPain, "Invalid match type: #{element.class}")
|
|
462
|
-
end
|
|
463
|
-
end
|
|
464
|
-
return false if token_index != tokens.size
|
|
465
|
-
return true
|
|
466
|
-
end
|
|
644
|
+
|
|
467
645
|
end
|
|
468
|
-
|
|
469
|
-
end
|
|
646
|
+
|
|
647
|
+
end
|