DAY : "Mo" | "Tu" | "We" | "Th" | "Fr" | "Sa" | "Su"
MONTH : "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
PUBLIC_HOLIDAYS : "PH"
SCHOOL_HOLIDAYS : "SH"

SOLAR : "sunrise" | "sunset" | "dawn" | "dusk"
DIGITAL_MOMENT : /[0-2][0-9]:[0-5][0-9]+/

SOLAR_COMPLEX_MOMENT : "(" SOLAR ("+"|"-") DIGITAL_MOMENT ")"

easter : /((easter (\+|-)[1-9][0-9]? days?)|(easter))+/

moment : DIGITAL_MOMENT -> digital_moment
       | SOLAR -> solar_moment
       | SOLAR_COMPLEX_MOMENT -> solar_complex_moment

period : moment "-" moment

raw_consecutive_day_range : DAY "-" DAY

raw_consecutive_month_range : MONTH "-" MONTH

concerned_period : DAY ("," DAY)* ("," (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS))* " " -> unconsecutive_days
                 | MONTH ("," MONTH)* ("," (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS))* " " -> unconsecutive_months
                 | (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS) ["," (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS)] " " -> holiday
                 | raw_consecutive_day_range ("," DAY)* ("," (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS))* " " -> consecutive_day_range
                 | raw_consecutive_month_range ("," MONTH)* ("," (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS))* " " -> consecutive_month_range
                 | MONTH ("," MONTH)* " " DAY ("," DAY)* [":"] " " -> days_of_month
                 | MONTH ("," MONTH)* [":"] " " raw_consecutive_day_range ("," DAY)* " " -> consecutive_days_of_month
                 | raw_consecutive_month_range ("," MONTH)* [":"] " " DAY ("," DAY)* " " -> days_of_consecutive_months
                 | raw_consecutive_month_range [":"] " " raw_consecutive_day_range " " -> consecutive_days_of_consecutive_months  //TODO : Support months or days after commas.
                 | (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS) " " DAY " " -> holidays_unconsecutive_days
                 | (PUBLIC_HOLIDAYS | SCHOOL_HOLIDAYS) " " raw_consecutive_day_range ("," DAY)* " " -> holidays_consecutive_days

day_periods : period ("," period)*
            | "off" -> period_closed
            | "closed" -> period_closed

// This regex catches one or two digit numbers not followed by a colon ":", to avoid confusion with "DIGITAL_MOMENT".
date_exceptional_day : MONTH " " /([0-3][0-9])(?!:[0-9])|((?<![0-9])[0-9])(?!:[0-9])+/ -> exceptional_day

field_part : concerned_period day_periods
           | easter " " day_periods
           | day_periods -> everyday_periods
           | "24/7" -> always_open
           | date_exceptional_day ("," date_exceptional_day)* ":"? " " day_periods -> exceptional_dates

field : field_part ("; " field_part)*
