Permalink
Cannot retrieve contributors at this time
parser grammar DateParser; | |
options { | |
tokenVocab=DateLexer; | |
output=AST; | |
} | |
import NumericRules; | |
tokens { | |
INT; | |
MONTH_OF_YEAR; | |
DAY_OF_MONTH; | |
DAY_OF_WEEK; | |
DAY_OF_YEAR; | |
YEAR_OF; | |
DATE_TIME; | |
DATE_TIME_ALTERNATIVE; | |
EXPLICIT_DATE; | |
RELATIVE_DATE; | |
SEEK; | |
DIRECTION; | |
SEEK_BY; | |
EXPLICIT_SEEK; | |
SPAN; | |
EXPLICIT_TIME; | |
RELATIVE_TIME; | |
HOURS_OF_DAY; | |
MINUTES_OF_HOUR; | |
SECONDS_OF_MINUTE; | |
AM_PM; | |
ZONE; | |
ZONE_OFFSET; | |
RECURRENCE; | |
HOLIDAY; | |
SEASON; | |
} | |
@header { | |
package com.joestelmach.natty.generated; | |
} | |
@members { | |
private static org.slf4j.Logger _logger = | |
org.slf4j.LoggerFactory.getLogger(com.joestelmach.natty.generated.DateParser.class); | |
@Override | |
public void displayRecognitionError(String[] tokenNames, RecognitionException re) { | |
String message = getErrorHeader(re); | |
try { message += getErrorMessage(re, tokenNames); } catch(Exception e) {} | |
_logger.debug(message); | |
} | |
} | |
parse | |
: empty ((recurrence)=>recurrence | date_time_alternative) | |
; | |
recurrence | |
: EVERY WHITE_SPACE date_time_alternative (WHITE_SPACE UNTIL WHITE_SPACE date_time)? | |
-> date_time_alternative ^(RECURRENCE date_time?) | |
; | |
empty | |
: | |
; | |
date_time | |
: ( | |
(date)=>date (date_time_separator explicit_time)? | |
| explicit_time (time_date_separator date)? | |
) -> ^(DATE_TIME date? explicit_time?) | |
| relative_time -> ^(DATE_TIME relative_time?) | |
; | |
date_time_separator | |
: WHITE_SPACE (AT WHITE_SPACE)? | |
| WHITE_SPACE? COMMA WHITE_SPACE? (AT WHITE_SPACE)? | |
| T | |
; | |
time_date_separator | |
: WHITE_SPACE ((ON | OF) WHITE_SPACE)? | |
| COMMA WHITE_SPACE? ((ON | OF) WHITE_SPACE)? | |
; | |
date | |
: formal_date | |
| relaxed_date | |
| relative_date | |
| explicit_relative_date | |
| global_date_prefix WHITE_SPACE date | |
-> ^(RELATIVE_DATE ^(SEEK global_date_prefix date)) | |
; | |
date_time_alternative | |
// for 3 days, for 7 months, for twenty seconds | |
: (date_time_alternative_range)=> date_time_alternative_range | |
-> ^(DATE_TIME_ALTERNATIVE date_time_alternative_range) | |
// today or the day after that, feb 16th or 2 days after that, january fourth or the friday after | |
| (date conjunction global_date_prefix)=> | |
date conjunction global_date_prefix (WHITE_SPACE THAT)? (date_time_separator explicit_time)? | |
-> ^(DATE_TIME_ALTERNATIVE | |
^(DATE_TIME date explicit_time?) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK global_date_prefix date) explicit_time?))) | |
// first or last day of 2009 | |
| (alternative_day_of_year_list)=> alternative_day_of_year_list | |
-> ^(DATE_TIME_ALTERNATIVE alternative_day_of_year_list) | |
// feb 16, 17, or 18 | |
| (alternative_day_of_month_list)=> alternative_day_of_month_list | |
-> ^(DATE_TIME_ALTERNATIVE alternative_day_of_month_list) | |
// "next wed or thurs" , "next wed, thurs, or fri", "this wed or next" | |
| (alternative_day_of_week_list)=> alternative_day_of_week_list | |
-> ^(DATE_TIME_ALTERNATIVE alternative_day_of_week_list) | |
// 1/2 or 1/4 or 1/6 at 6pm, Aug 16 at 10am or Sept 28th at 11am, Feb 28th | |
| date_time (conjunction date_time)* | |
-> ^(DATE_TIME_ALTERNATIVE date_time+) | |
; | |
date_time_alternative_range | |
: ( | |
// in two days, in 3 or 4 days | |
range_direction one=spelled_or_int_optional_prefix | |
(conjunction two=spelled_or_int_optional_prefix)? WHITE_SPACE range_span | |
// two to 7 days, 4 to 5 days ago | |
| one=spelled_or_int_optional_prefix conjunction two=spelled_or_int_optional_prefix WHITE_SPACE | |
range_span (WHITE_SPACE relative_date_suffix)? | |
) | |
-> {$range_direction.text != null && $range_direction.inclusive && $two.text != null}? | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK range_direction INT["0"] range_span))) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK range_direction $one range_span))) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK range_direction $two range_span))) | |
-> {$range_direction.text != null && $range_direction.inclusive}? | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK range_direction INT["0"] range_span))) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK range_direction $one range_span))) | |
-> {$relative_date_suffix.text != null && $two.text != null}? | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK relative_date_suffix $one range_span))) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK relative_date_suffix $two range_span))) | |
-> {$relative_date_suffix.text != null}? | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK relative_date_suffix $one range_span))) | |
-> {$two.text != null}? | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] $one range_span))) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] $two range_span))) | |
-> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] $one range_span))) | |
; | |
range_direction returns [Boolean inclusive] | |
: (FOR | NEXT) WHITE_SPACE {$inclusive=true;} -> DIRECTION[">"] SEEK_BY["by_day"] | |
| (LAST | PAST) WHITE_SPACE {$inclusive=true;} -> DIRECTION["<"] SEEK_BY["by_day"] | |
| IN WHITE_SPACE {$inclusive=false;} -> DIRECTION[">"] SEEK_BY["by_day"] | |
; | |
conjunction | |
: COMMA? WHITE_SPACE (AND | OR | TO | THROUGH | DASH) WHITE_SPACE | |
; | |
range_span | |
: relative_date_span | |
| relative_time_span | |
; | |
alternative_day_of_year_list | |
: first=explicit_day_of_year_part conjunction second=explicit_day_of_year_part WHITE_SPACE relaxed_year | |
-> ^(DATE_TIME ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_year) $first)) | |
^(DATE_TIME ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_year) $second)) | |
; | |
alternative_day_of_month_list | |
// mon may 15 or tues may 16 | |
: ((relaxed_day_of_week? relaxed_month WHITE_SPACE relaxed_day_of_month (conjunction relaxed_day_of_month)+) (date_time_separator explicit_time)?) | |
-> ^(DATE_TIME ^(EXPLICIT_DATE relaxed_month relaxed_day_of_month) explicit_time?)+ | |
| first=explicit_day_of_month_part conjunction second=explicit_day_of_month_part WHITE_SPACE alternative_day_seek (date_time_separator explicit_time)? | |
-> ^(DATE_TIME ^(RELATIVE_DATE alternative_day_seek $first) explicit_time?) | |
^(DATE_TIME ^(RELATIVE_DATE alternative_day_seek $second) explicit_time?) | |
; | |
alternative_day_seek | |
// next september | |
: prefix WHITE_SPACE explicit_relative_month | |
-> ^(SEEK prefix explicit_relative_month) | |
// 2 septembers from now | |
| spelled_or_int_optional_prefix WHITE_SPACE explicit_relative_month WHITE_SPACE relative_date_suffix | |
-> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix explicit_relative_month) | |
// september | |
| relaxed_month | |
-> ^(EXPLICIT_SEEK relaxed_month) | |
; | |
alternative_day_of_week_list | |
: first_direction=alternative_direction WHITE_SPACE day_of_week | |
// this wed or thursday or friday | |
((day_of_week_list_separator day_of_week)+ (date_time_separator explicit_time)? | |
-> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK $first_direction day_of_week)) explicit_time?)+ | |
// this wed. or next | |
| conjunction second_direction=alternative_direction (date_time_separator explicit_time)? | |
-> ^(DATE_TIME ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] day_of_week)) explicit_time?) | |
^(DATE_TIME ^(RELATIVE_DATE ^(SEEK $second_direction day_of_week)) explicit_time?) | |
) | |
; | |
day_of_week_list_separator | |
: COMMA (WHITE_SPACE | conjunction) | conjunction | |
; | |
alternative_direction | |
: NEXT -> DIRECTION[">"] SEEK_BY["by_week"] INT["1"] | |
| LAST -> DIRECTION["<"] SEEK_BY["by_week"] INT["1"] | |
| THIS -> DIRECTION[">"] SEEK_BY["by_day"] INT["0"] | |
| -> DIRECTION[">"] SEEK_BY["by_day"] INT["0"] | |
; | |
global_date_prefix | |
// the day after | |
: (THE WHITE_SPACE)? DAY WHITE_SPACE prefix_direction | |
-> prefix_direction SEEK_BY["by_day"] INT["1"] | |
// 2 weeks from now | |
| (global_date_prefix_amount WHITE_SPACE)? global_date_prefix_seek prefix_direction | |
-> {$global_date_prefix_amount.text != null}? | |
prefix_direction global_date_prefix_seek global_date_prefix_amount | |
-> prefix_direction global_date_prefix_seek INT["1"] | |
// 3 fridays before, the friday after, 2 fridays from now | |
| (THE WHITE_SPACE)? (global_date_prefix_amount WHITE_SPACE)? day_of_week WHITE_SPACE prefix_direction | |
-> {$global_date_prefix_amount.text != null}? | |
prefix_direction SEEK_BY["by_day"] global_date_prefix_amount day_of_week | |
-> prefix_direction SEEK_BY["by_day"] INT["1"] day_of_week | |
; | |
global_date_prefix_amount | |
: spelled_first_to_thirty_first | |
| spelled_or_int_optional_prefix | |
; | |
global_date_prefix_seek | |
: DAY WHITE_SPACE -> SEEK_BY["by_day"] | |
| WEEK WHITE_SPACE -> SEEK_BY["by_week"] | |
| MONTH WHITE_SPACE -> SEEK_BY["by_month"] | |
| YEAR WHITE_SPACE -> SEEK_BY["by_year"] | |
; | |
prefix_direction | |
: (AFTER | FROM | ON) -> DIRECTION[">"] | |
| BEFORE -> DIRECTION["<"] | |
; | |
// ********** relaxed date rules ********** | |
// relaxed date with a spelled-out or abbreviated month | |
relaxed_date | |
: relaxed_date_month_first | |
| relaxed_date_month_last | |
; | |
relaxed_date_month_first | |
: relaxed_day_of_week? relaxed_month COMMA? WHITE_SPACE relaxed_day_of_month (relaxed_year_prefix relaxed_year)? | |
-> ^(EXPLICIT_DATE relaxed_month relaxed_day_of_month relaxed_day_of_week? relaxed_year?) | |
; | |
relaxed_date_month_last | |
: relaxed_day_of_week? relaxed_day_of_month_prefix? relaxed_day_of_month | |
WHITE_SPACE (OF WHITE_SPACE)? relaxed_month (relaxed_year_prefix relaxed_year)? | |
-> ^(EXPLICIT_DATE relaxed_month relaxed_day_of_month relaxed_day_of_week? relaxed_year?) | |
; | |
relaxed_day_of_week | |
: (prefix WHITE_SPACE)? day_of_week COMMA? WHITE_SPACE? -> day_of_week | |
; | |
relaxed_day_of_month_prefix | |
: (THE WHITE_SPACE) | (COMMA WHITE_SPACE?) | |
; | |
relaxed_month | |
: JANUARY -> ^(MONTH_OF_YEAR INT["1"]) | |
| FEBRUARY -> ^(MONTH_OF_YEAR INT["2"]) | |
| MARCH -> ^(MONTH_OF_YEAR INT["3"]) | |
| APRIL -> ^(MONTH_OF_YEAR INT["4"]) | |
| MAY -> ^(MONTH_OF_YEAR INT["5"]) | |
| JUNE -> ^(MONTH_OF_YEAR INT["6"]) | |
| JULY -> ^(MONTH_OF_YEAR INT["7"]) | |
| AUGUST -> ^(MONTH_OF_YEAR INT["8"]) | |
| SEPTEMBER -> ^(MONTH_OF_YEAR INT["9"]) | |
| OCTOBER -> ^(MONTH_OF_YEAR INT["10"]) | |
| NOVEMBER -> ^(MONTH_OF_YEAR INT["11"]) | |
| DECEMBER -> ^(MONTH_OF_YEAR INT["12"]) | |
; | |
relaxed_day_of_month | |
: spelled_or_int_01_to_31_optional_prefix | |
-> ^(DAY_OF_MONTH spelled_or_int_01_to_31_optional_prefix) | |
| spelled_first_to_thirty_first | |
-> ^(DAY_OF_MONTH spelled_first_to_thirty_first) | |
; | |
// TODO expand these to 366 | |
relaxed_day_of_year | |
: spelled_or_int_01_to_31_optional_prefix | |
-> ^(DAY_OF_YEAR spelled_or_int_01_to_31_optional_prefix) | |
| spelled_first_to_thirty_first | |
-> ^(DAY_OF_YEAR spelled_first_to_thirty_first) | |
; | |
relaxed_year | |
: SINGLE_QUOTE int_00_to_99_mandatory_prefix | |
-> ^(YEAR_OF int_00_to_99_mandatory_prefix) | |
| int_four_digits | |
-> ^(YEAR_OF int_four_digits) | |
; | |
relaxed_year_prefix | |
: (COMMA WHITE_SPACE? | WHITE_SPACE) (IN WHITE_SPACE THE WHITE_SPACE YEAR WHITE_SPACE)? | |
; | |
// ********** formal date rules ********** | |
formal_date | |
// march 2009 | |
: (relaxed_month WHITE_SPACE relaxed_year)=> | |
relaxed_month WHITE_SPACE relaxed_year | |
-> ^(EXPLICIT_DATE relaxed_month ^(DAY_OF_MONTH INT["1"]) relaxed_year?) | |
// year first: 1979-02-28, 1980/01/02, etc. full 4 digit year required to match | |
| relaxed_day_of_week? formal_year_four_digits formal_date_separator (formal_month_of_year | relaxed_month) formal_date_separator formal_day_of_month | |
-> ^(EXPLICIT_DATE formal_month_of_year? relaxed_month? formal_day_of_month relaxed_day_of_week? formal_year_four_digits) | |
// year last: 1/02/1980, 2/28/79. 2 or 4 digit year is acceptable | |
| relaxed_day_of_week? formal_month_of_year formal_date_separator formal_day_of_month (formal_date_separator formal_year)? | |
-> ^(EXPLICIT_DATE formal_month_of_year formal_day_of_month relaxed_day_of_week? formal_year?) | |
// 15-Apr-2014 | |
| formal_day_of_month formal_date_separator relaxed_month (formal_date_separator formal_year_four_digits)? | |
-> ^(EXPLICIT_DATE relaxed_month formal_day_of_month formal_year_four_digits?) | |
; | |
formal_month_of_year | |
: int_01_to_12_optional_prefix -> ^(MONTH_OF_YEAR int_01_to_12_optional_prefix) | |
; | |
formal_day_of_month | |
: int_01_to_31_optional_prefix -> ^(DAY_OF_MONTH int_01_to_31_optional_prefix) | |
; | |
formal_year | |
: formal_year_four_digits | |
| int_00_to_99_mandatory_prefix -> ^(YEAR_OF int_00_to_99_mandatory_prefix) | |
; | |
formal_year_four_digits | |
: int_four_digits -> ^(YEAR_OF int_four_digits) | |
; | |
formal_date_separator | |
: DASH | |
| SLASH | |
; | |
// ********** relative date rules ********** | |
relative_date | |
// next wed, last month | |
: relative_date_prefix WHITE_SPACE relative_target (WHITE_SPACE spelled_or_int_optional_prefix WHITE_SPACE relative_date_span)* | |
-> ^(RELATIVE_DATE ^(SEEK relative_date_prefix relative_target) ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] spelled_or_int_optional_prefix relative_date_span)*) | |
// this month, this week | |
| implicit_prefix WHITE_SPACE relative_target | |
-> ^(RELATIVE_DATE ^(SEEK implicit_prefix relative_target)) | |
// monday, tuesday | |
| day_of_week | |
// relative target with no prefix has an implicit seek of 0 | |
-> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] day_of_week)) | |
// january, february | |
| relaxed_month | |
-> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] relaxed_month)) | |
// one month from now | |
| spelled_or_int_optional_prefix WHITE_SPACE relative_target WHITE_SPACE relative_date_suffix | |
-> ^(RELATIVE_DATE ^(SEEK relative_date_suffix spelled_or_int_optional_prefix relative_target)) | |
| one=spelled_or_int_optional_prefix WHITE_SPACE relative_target (WHITE_SPACE two+=spelled_or_int_optional_prefix WHITE_SPACE relative_date_span)+ WHITE_SPACE relative_date_suffix | |
-> ^(RELATIVE_DATE ^(SEEK relative_date_suffix $one relative_target) ^(SEEK relative_date_suffix $two relative_date_span)*) | |
// a month from now | |
| relative_target WHITE_SPACE relative_date_suffix | |
-> ^(RELATIVE_DATE ^(SEEK relative_date_suffix INT["1"] relative_target)) | |
// the week after next | |
| (THE WHITE_SPACE)? relative_date_span WHITE_SPACE AFTER WHITE_SPACE NEXT | |
-> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] relative_date_span)) | |
// today, tomorrow | |
| named_relative_date | |
// next christmas, 2 thanksgivings ago | |
| holiday | |
-> ^(RELATIVE_DATE holiday) | |
// next fall, 2 summers from now | |
| season | |
-> ^(RELATIVE_DATE season) | |
; | |
// ********** explicit relative date rules ********** | |
// these represent explicit points within a relative range | |
explicit_relative_date | |
// the first day of 2009 | |
: explicit_day_of_year_part WHITE_SPACE relaxed_year | |
-> ^(RELATIVE_DATE ^(EXPLICIT_SEEK relaxed_year) explicit_day_of_year_part) | |
| explicit_day_of_month_part WHITE_SPACE explicit_relative_month_seek (relaxed_year_prefix relaxed_year)? | |
-> {$relaxed_year.text != null}? | |
^(RELATIVE_DATE explicit_relative_month_seek explicit_day_of_month_part ^(EXPLICIT_SEEK relaxed_year)) | |
-> ^(RELATIVE_DATE explicit_relative_month_seek explicit_day_of_month_part) | |
| explicit_day_of_week_part WHITE_SPACE explicit_relative_week_seek | |
-> ^(RELATIVE_DATE explicit_relative_week_seek explicit_day_of_week_part) | |
; | |
explicit_relative_month_seek | |
// 1st of three months ago, 10th of 3 octobers from now, the last monday in 2 novembers ago | |
: spelled_or_int_optional_prefix WHITE_SPACE explicit_relative_month WHITE_SPACE relative_date_suffix | |
-> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix explicit_relative_month) | |
// 10th of next month, 31st of last month, 10th of next october, 30th of this month, the last thursday of last november | |
| prefix WHITE_SPACE explicit_relative_month | |
-> ^(SEEK prefix explicit_relative_month) | |
// 10th of the month after next | |
| THE WHITE_SPACE MONTH WHITE_SPACE AFTER WHITE_SPACE NEXT | |
-> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] SPAN["month"]) | |
// september | |
| relaxed_month | |
-> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] relaxed_month) | |
; | |
explicit_relative_week_seek | |
// after next | |
: AFTER WHITE_SPACE NEXT | |
-> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] SPAN["week"]) | |
// before last | |
| BEFORE WHITE_SPACE LAST | |
-> ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] INT["2"] SPAN["week"]) | |
// last week, tuesday of next week | |
| prefix WHITE_SPACE WEEK | |
-> ^(SEEK prefix SPAN["week"]) | |
// 2 weeks ago, tuesday of 3 weeks from now | |
| spelled_or_int_optional_prefix WHITE_SPACE WEEK WHITE_SPACE relative_date_suffix | |
-> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix SPAN["week"]) | |
// the week after next | |
| THE WHITE_SPACE WEEK WHITE_SPACE AFTER WHITE_SPACE NEXT | |
-> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["2"] SPAN["week"]) | |
; | |
explicit_day_of_month_part | |
// first of, 10th of, 31st of, | |
: (THE WHITE_SPACE)? relaxed_day_of_month day_of_month_suffix? | |
-> ^(EXPLICIT_SEEK relaxed_day_of_month) | |
// the last thursday | |
| (THE WHITE_SPACE)? relative_occurrence_index WHITE_SPACE day_of_week day_of_month_suffix | |
-> ^(EXPLICIT_SEEK relative_occurrence_index day_of_week) | |
// in the start of, at the beginning of, the end of, last day of, first day of | |
| (((IN | AT) WHITE_SPACE)? THE WHITE_SPACE)? explicit_day_of_month_bound day_of_month_suffix? | |
-> explicit_day_of_month_bound | |
; | |
day_of_month_suffix | |
: WHITE_SPACE (IN | OF) (WHITE_SPACE MONTH)? | |
; | |
explicit_day_of_week_part | |
// monday of, tuesday of | |
: (THE WHITE_SPACE)? relaxed_day_of_week (IN | OF)? | |
-> ^(EXPLICIT_SEEK relaxed_day_of_week) | |
// in the end of, at the beginning of | |
| (((IN | AT) WHITE_SPACE)? THE WHITE_SPACE)? explicit_day_of_week_bound WHITE_SPACE (OF | IN) | |
-> explicit_day_of_week_bound | |
; | |
explicit_day_of_year_part | |
// last of, first of, 15th day of | |
: (THE WHITE_SPACE)? relaxed_day_of_year (WHITE_SPACE (IN | OF))? | |
-> ^(EXPLICIT_SEEK relaxed_day_of_year) | |
// in the start of, at the beginning of, the end of, last day of, first day of | |
| (((IN | AT) WHITE_SPACE)? THE WHITE_SPACE)? explicit_day_of_year_bound (WHITE_SPACE (OF | IN))? | |
-> explicit_day_of_year_bound | |
; | |
// the lower or upper bound when talking about days in a year | |
explicit_day_of_year_bound | |
// beginning, start | |
: (BEGINNING | START) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_YEAR INT["1"])) | |
// first day, 2nd day, etc | |
| (spelled_first_to_thirty_first WHITE_SPACE DAY) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_YEAR spelled_first_to_thirty_first)) | |
// end, last day | |
| (END | (LAST WHITE_SPACE DAY)) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_YEAR INT["366"])) | |
; | |
// the lower or upper bound when talking about days in a month | |
explicit_day_of_month_bound | |
// beginning, start | |
: (BEGINNING | START) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_MONTH INT["1"])) | |
// first day, 2nd day, etc | |
| (spelled_first_to_thirty_first WHITE_SPACE DAY) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_MONTH spelled_first_to_thirty_first)) | |
// end, last day | |
| (END | (LAST WHITE_SPACE DAY)) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_MONTH INT["31"])) | |
; | |
// the lower or upper bound when talking about the days in a week: | |
explicit_day_of_week_bound | |
// beginning, start, first day | |
: (BEGINNING | START | (FIRST WHITE_SPACE DAY)) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_WEEK INT["2"])) | |
// end, last day | |
| (END | (LAST WHITE_SPACE DAY)) | |
-> ^(EXPLICIT_SEEK ^(DAY_OF_WEEK INT["6"])) | |
; | |
explicit_relative_month | |
: relaxed_month | |
| MONTH -> SPAN["month"] | |
; | |
relative_occurrence_index | |
: (FIRST | INT_1 ST?) -> INT["1"] | |
| (SECOND | INT_2 ND?) -> INT["2"] | |
| (THIRD | INT_3 RD?) -> INT["3"] | |
| (FOURTH | INT_4 TH?) -> INT["4"] | |
| (FIFTH | INT_5 TH?) -> INT["5"] | |
| LAST -> INT["5"] | |
; | |
relative_target | |
: day_of_week | |
| relaxed_month | |
| relative_date_span | |
; | |
relative_time_target | |
: relative_time_span | |
; | |
relative_time_span | |
: HOUR -> SPAN["hour"] | |
| MINUTE -> SPAN["minute"] | |
| SECOND -> SPAN["second"] | |
; | |
implicit_prefix | |
: (THIS | CURRENT) -> DIRECTION[">"] SEEK_BY["by_day"] INT["0"] | |
; | |
relative_date_prefix | |
: (THIS WHITE_SPACE)? LAST -> DIRECTION["<"] SEEK_BY["by_week"] INT["1"] | |
| (THIS WHITE_SPACE)? NEXT -> DIRECTION[">"] SEEK_BY["by_week"] INT["1"] | |
| (THIS WHITE_SPACE)? PAST -> DIRECTION["<"] SEEK_BY["by_day"] INT["1"] | |
| (THIS WHITE_SPACE)? COMING -> DIRECTION[">"] SEEK_BY["by_day"] INT["1"] | |
| (THIS WHITE_SPACE)? UPCOMING -> DIRECTION[">"] SEEK_BY["by_day"] INT["1"] | |
| IN WHITE_SPACE (AM | AN) -> DIRECTION[">"] SEEK_BY["by_day"] INT["1"] | |
| (IN WHITE_SPACE)? spelled_or_int_optional_prefix | |
-> DIRECTION[">"] SEEK_BY["by_day"] spelled_or_int_optional_prefix | |
; | |
prefix | |
: relative_date_prefix | |
| implicit_prefix | |
; | |
relative_date_suffix | |
// from now, after today | |
: (FROM | AFTER) WHITE_SPACE (NOW | TODAY) -> DIRECTION[">"] SEEK_BY["by_day"] | |
| AGO -> DIRECTION["<"] SEEK_BY["by_day"] | |
; | |
relative_time_suffix | |
// from now, after today, before noon, after 4pm | |
: (FROM | AFTER) (WHITE_SPACE relative_time_suffix_anchor)? | |
-> DIRECTION[">"] SEEK_BY["by_day"] relative_time_suffix_anchor? | |
// before noon, before 3pm | |
| BEFORE (WHITE_SPACE relative_time_suffix_anchor)? | |
-> DIRECTION["<"] SEEK_BY["by_day"] relative_time_suffix_anchor? | |
| AGO | |
-> DIRECTION["<"] SEEK_BY["by_day"] | |
; | |
relative_time_suffix_anchor | |
: named_relative_time | |
| explicit_time | |
-> ^(EXPLICIT_SEEK explicit_time) | |
; | |
relative_date_span | |
: DAY -> SPAN["day"] | |
| WEEK -> SPAN["week"] | |
| MONTH -> SPAN["month"] | |
| YEAR -> SPAN["year"] | |
; | |
day_of_week | |
: SUNDAY -> ^(DAY_OF_WEEK INT["1"]) | |
| MONDAY -> ^(DAY_OF_WEEK INT["2"]) | |
| TUESDAY -> ^(DAY_OF_WEEK INT["3"]) | |
| WEDNESDAY -> ^(DAY_OF_WEEK INT["4"]) | |
| THURSDAY -> ^(DAY_OF_WEEK INT["5"]) | |
| FRIDAY -> ^(DAY_OF_WEEK INT["6"]) | |
| SATURDAY -> ^(DAY_OF_WEEK INT["7"]) | |
; | |
named_relative_date | |
: (TODAY | NOW) -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] SPAN["day"])) | |
| TOMORROW -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["1"] SPAN["day"])) | |
| YESTERDAY -> ^(RELATIVE_DATE ^(SEEK DIRECTION["<"] SEEK_BY["by_day"] INT["1"] SPAN["day"])) | |
; | |
named_relative_time | |
: NOW -> ^(RELATIVE_DATE ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["0"] SPAN["day"])) | |
; | |
// ********** holidays ********** | |
holiday | |
: spelled_or_int_optional_prefix WHITE_SPACE holiday_name WHITE_SPACE relative_date_suffix | |
-> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix holiday_name) | |
| relative_date_prefix WHITE_SPACE holiday_name | |
-> ^(SEEK relative_date_prefix holiday_name) | |
| (holiday_name relaxed_year_prefix relaxed_year)=> | |
holiday_name relaxed_year_prefix relaxed_year | |
-> ^(EXPLICIT_SEEK holiday_name relaxed_year) | |
| holiday_name | |
-> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["1"] holiday_name) | |
; | |
holiday_name | |
: APRIL WHITE_SPACE FOOL (WHITE_SPACE DAY)? | |
-> HOLIDAY["APRIL_FOOLS_DAY"] | |
| BLACK WHITE_SPACE FRIDAY | |
-> HOLIDAY["BLACK_FRIDAY"] | |
| (CHRISTMAS WHITE_SPACE EVENING)=> | |
CHRISTMAS WHITE_SPACE EVENING | |
-> HOLIDAY["CHRISTMAS_EVE"] | |
| CHRISTMAS (WHITE_SPACE DAY)? | |
-> HOLIDAY["CHRISTMAS"] | |
| COLUMBUS WHITE_SPACE DAY | |
-> HOLIDAY["COLUMBUS_DAY"] | |
| EARTH WHITE_SPACE DAY | |
-> HOLIDAY["EARTH_DAY"] | |
| EASTER (WHITE_SPACE (SUNDAY | DAY))? | |
-> HOLIDAY["EASTER"] | |
| FATHER WHITE_SPACE DAY | |
-> HOLIDAY["FATHERS_DAY"] | |
| FLAG WHITE_SPACE DAY | |
-> HOLIDAY["FLAG_DAY"] | |
| GOOD WHITE_SPACE FRIDAY | |
-> HOLIDAY["GOOD_FRIDAY"] | |
| GROUNDHOG WHITE_SPACE? DAY | |
-> HOLIDAY["GROUNDHOG_DAY"] | |
| HALLOWEEN (WHITE_SPACE DAY)? | |
-> HOLIDAY["HALLOWEEN"] | |
| INAUGURATION WHITE_SPACE DAY | |
-> HOLIDAY["INAUGURATION_DAY"] | |
| INDEPENDENCE WHITE_SPACE DAY | |
-> HOLIDAY["INDEPENDENCE_DAY"] | |
| KWANZAA (WHITE_SPACE DAY)? | |
-> HOLIDAY["KWANZAA"] | |
| LABOR WHITE_SPACE DAY | |
-> HOLIDAY["LABOR_DAY"] | |
| MLK (WHITE_SPACE DAY)? | |
-> HOLIDAY["MLK_DAY"] | |
| MEMORIAL WHITE_SPACE DAY | |
-> HOLIDAY["MEMORIAL_DAY"] | |
| MOTHER WHITE_SPACE DAY | |
-> HOLIDAY["MOTHERS_DAY"] | |
| (NEW WHITE_SPACE YEAR WHITE_SPACE EVENING)=> | |
NEW WHITE_SPACE YEAR WHITE_SPACE EVENING | |
-> HOLIDAY["NEW_YEARS_EVE"] | |
| NEW WHITE_SPACE YEAR (WHITE_SPACE DAY)? | |
-> HOLIDAY["NEW_YEARS_DAY"] | |
| PATRIOT WHITE_SPACE DAY | |
-> HOLIDAY["PATRIOT_DAY"] | |
| PRESIDENT WHITE_SPACE DAY | |
-> HOLIDAY["PRESIDENTS_DAY"] | |
| (SAINT | ST DOT?) WHITE_SPACE PATRICK WHITE_SPACE DAY | |
-> HOLIDAY["ST_PATRICKS_DAY"] | |
| TAX WHITE_SPACE DAY | |
-> HOLIDAY["TAX_DAY"] | |
| THANKSGIVING (WHITE_SPACE DAY)? | |
-> HOLIDAY["THANKSGIVING"] | |
| ELECTION WHITE_SPACE DAY | |
-> HOLIDAY["ELECTION_DAY"] | |
| VALENTINE WHITE_SPACE DAY | |
-> HOLIDAY["VALENTINES_DAY"] | |
| VETERAN WHITE_SPACE DAY | |
-> HOLIDAY["VETERANS_DAY"] | |
; | |
season | |
: spelled_or_int_optional_prefix WHITE_SPACE season_name WHITE_SPACE relative_date_suffix | |
-> ^(SEEK relative_date_suffix spelled_or_int_optional_prefix season_name) | |
| relative_date_prefix WHITE_SPACE season_name | |
-> ^(SEEK relative_date_prefix season_name) | |
| (season_name relaxed_year_prefix)=> | |
season_name relaxed_year_prefix relaxed_year | |
-> ^(EXPLICIT_SEEK season_name relaxed_year) | |
| season_name | |
-> ^(SEEK DIRECTION[">"] SEEK_BY["by_day"] INT["1"] season_name) | |
; | |
season_name | |
:WINTER | |
-> SEASON["WINTER"] | |
| SPRING | |
-> SEASON["SPRING"] | |
| SUMMER | |
-> SEASON["SUMMER"] | |
| (FALL | AUTUMN) | |
-> SEASON["FALL"] | |
; | |
// ********** time rules ********** | |
relative_time | |
// 10 hours ago, 20 minutes before noon | |
: spelled_or_int_optional_prefix WHITE_SPACE relative_time_target WHITE_SPACE relative_time_suffix | |
-> ^(RELATIVE_TIME ^(SEEK relative_time_suffix spelled_or_int_optional_prefix relative_time_target)) | |
// next hour, last minute | |
| prefix WHITE_SPACE relative_time_target | |
-> ^(RELATIVE_TIME ^(SEEK prefix relative_time_target)) | |
; | |
// a time with an hour, optional minutes, and optional meridian indicator | |
explicit_time | |
: (AT WHITE_SPACE)? explicit_time_hours_minutes (WHITE_SPACE? time_zone)? | |
-> ^(EXPLICIT_TIME explicit_time_hours_minutes time_zone?) | |
| named_time (WHITE_SPACE time_zone)? | |
-> ^(EXPLICIT_TIME named_time time_zone?) | |
; | |
explicit_time_hours_minutes returns [String hours, String minutes, String ampm] | |
: hours (COLON | DOT)? minutes ((COLON | DOT)? seconds)? (WHITE_SPACE? (meridian_indicator | (MILITARY_HOUR_SUFFIX | HOUR)))? | |
{$hours=$hours.text; $minutes=$minutes.text; $ampm=$meridian_indicator.text;} | |
-> hours minutes seconds? meridian_indicator? | |
| hours (WHITE_SPACE? meridian_indicator)? | |
{$hours=$hours.text; $ampm=$meridian_indicator.text;} | |
-> hours ^(MINUTES_OF_HOUR INT["0"]) meridian_indicator? | |
; | |
// hour of the day | |
hours | |
: int_00_to_23_optional_prefix -> ^(HOURS_OF_DAY int_00_to_23_optional_prefix) | |
; | |
// minutes of the hour | |
minutes | |
: int_00_to_59_mandatory_prefix -> ^(MINUTES_OF_HOUR int_00_to_59_mandatory_prefix) | |
; | |
// seconds of the minute | |
seconds | |
: int_00_to_59_mandatory_prefix -> ^(SECONDS_OF_MINUTE int_00_to_59_mandatory_prefix) | |
; | |
// meridian am/pm indicator | |
meridian_indicator | |
: simple_meridian_indicator | |
| friendly_meridian_indicator | |
; | |
simple_meridian_indicator | |
: AM -> AM_PM["am"] | |
| PM -> AM_PM["pm"] | |
; | |
friendly_meridian_indicator | |
: (((IN WHITE_SPACE THE) | AT) WHITE_SPACE)? | |
( | |
MORNING -> AM_PM["am"] | |
| (NOON | EVENING | NIGHT) -> AM_PM["pm"] | |
) | |
; | |
named_time | |
: (named_time_prefix? named_hour (WHITE_SPACE AT)? WHITE_SPACE hm=explicit_time_hours_minutes)=> | |
named_time_prefix? named_hour (WHITE_SPACE AT)? WHITE_SPACE hm=explicit_time_hours_minutes | |
// If the named time is at night, but the hour given is before 5, we'll assume tomorrow morning | |
-> {$named_hour.ampm != null && $named_hour.ampm.equals("pm") && Integer.parseInt($hm.hours) < 5}? | |
^(HOURS_OF_DAY INT[Integer.toString(Integer.parseInt($hm.hours) + 24)]) | |
^(MINUTES_OF_HOUR INT[$hm.minutes]) | |
^(SECONDS_OF_MINUTE INT["0"]) AM_PM[$named_hour.ampm] | |
-> ^(HOURS_OF_DAY INT[$hm.hours]) | |
^(MINUTES_OF_HOUR INT[$hm.minutes]) | |
^(SECONDS_OF_MINUTE INT["0"]) AM_PM[$named_hour.ampm] | |
| named_time_prefix? named_hour | |
-> named_hour | |
; | |
named_time_prefix | |
: ((IN WHITE_SPACE THE) | AT | THIS) WHITE_SPACE | |
; | |
named_hour returns [String ampm] | |
: MORNING {$ampm="am";} -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["am"] | |
| MIDNIGHT {$ampm="am";} -> ^(HOURS_OF_DAY INT["12"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["am"] | |
| NOON {$ampm="pm";} -> ^(HOURS_OF_DAY INT["12"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] | |
| NIGHT {$ampm="pm";} -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] | |
| TONIGHT {$ampm="pm";} -> ^(HOURS_OF_DAY INT["8"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] | |
| EVENING {$ampm="pm";} -> ^(HOURS_OF_DAY INT["7"]) ^(MINUTES_OF_HOUR INT["0"]) ^(SECONDS_OF_MINUTE INT["0"]) AM_PM["pm"] | |
; | |
time_zone | |
: time_zone_plus_offset | |
| time_zone_abbreviation | |
; | |
time_zone_plus_offset | |
: UTC? time_zone_offset -> ZONE_OFFSET[$time_zone_offset.text] | |
; | |
time_zone_offset | |
: (PLUS | DASH) hours (COLON? minutes)? | |
; | |
time_zone_abbreviation | |
: UTC -> ZONE["UTC"] | |
| EST -> ZONE["America/New_York"] | |
| CST -> ZONE["America/Chicago"] | |
| PST -> ZONE["America/Los_Angeles"] | |
| MST -> ZONE["America/Denver"] | |
| AKST -> ZONE["America/Anchorage"] | |
| HAST -> ZONE["Pacific/Honolulu"] | |
; |