remindee-parser 0.3.0

Natural language reminder parser for remindee-bot
Documentation
//! Reminder grammar

ws = _{ " " | "\t" | "\r" | "\n" }

// --- literals ---
year_unit   = _{ ^"y"~(^"e"~(^"a"~(^"r"~(^"s"?)?)?)?)? }
month_unit  = _{ ^"mo"~(^"n"~(^"t"~(^"h"~(^"s"?)?)?)?)? }
week_unit   = _{ ^"w"~(^"e"~(^"e"~(^"k"~(^"s"?)?)?)?)? }
day_unit    = _{ ^"d"~(^"a"~(^"y"~(^"s"?)?)?)? }
hour_unit   = _{ ^"h"~(^"o"~(^"u"~(^"r"~(^"s"?)?)?)?)? }
minute_unit = _{
    ^"m"~(^"i"~(^"n"~(
        ^"s"
      | ^"u"~(^"t"~(^"e"~(^"s"?)?)?)?
    )?)?)?
}
second_unit = _{ ^"s"~(^"e"~(^"c"~(^"o"~(^"n"~(^"d"~(^"s"?)?)?)?)?)?)? }
date_month_unit  = _{ ^"m"~(^"o"~(^"n"~(^"t"~(^"h"~(^"s"?)?)?)?)?)? }

monday    = @{ ^"m"~(^"o"~(^"n"~(^"d"~(^"a"~^"y"?)?)?)?)? }
tuesday   = @{ ^"tu"~(^"e"~(^"s"~(^"d"~(^"a"~^"y"?)?)?)?)? }
wednesday = @{ ^"w"~(^"e"~(^"d"~(^"n"~(^"e"~(^"s"~(^"d"~(^"a"~^"y"?)?)?)?)?)?)?)? }
thursday  = @{ ^"th"~(^"u"~(^"r"~(^"s"~(^"d"~(^"a"~^"y"?)?)?)?)?)? }
friday    = @{ ^"f"~(^"r"~(^"i"~(^"d"~(^"a"~^"y"?)?)?)?)? }
saturday  = @{ ^"sa"~(^"t"~(^"u"~(^"r"~(^"d"~(^"a"~^"y"?)?)?)?)?)? }
sunday    = @{ ^"su"~(^"n"~(^"d"~(^"a"~^"y"?)?)?)? }
// ----------------

// --- time point units ---
day = @{
    '1'..'2' ~ ASCII_DIGIT
  | "3" ~ '0'..'1'
  | "0"? ~ ASCII_NONZERO_DIGIT
}
month = @{
    "1" ~ '0'..'2'
  | "0"? ~ ASCII_NONZERO_DIGIT
}
year = @{ ASCII_DIGIT{4} }

hour = @{ 
    "2" ~ '0'..'3'
  | '0'..'1' ~ ASCII_DIGIT
  | ASCII_DIGIT
}
minute_or_second = _{ '0'..'5' ~ ASCII_DIGIT | ASCII_DIGIT }
minute = @{ minute_or_second }
second = @{ minute_or_second }

weekday = _{
    monday
  | tuesday
  | wednesday
  | thursday
  | friday
  | saturday
  | sunday
}
// ------------------------

// --- human-readable sugar ---
interval_divisor_hrprefix = _{ ("/" | ^"every") ~ ws* }
time_divisor_hrprefix     = _{ ("/" | ^"every") ~ ws* }
time_hrprefix             = _{ ^"at"? ~ ws* }
countdown_hrprefix        = _{ (^"after" | ^"in" | "+")? ~ ws* }
weekdays_divisor_hrprefix = _{ ("/" | ^"every" | ^"on") ~ ws* }
splitter = _{ "—" | "--" | "-" }
// ----------------------------

// --- cron ---
cron_prefix = _{ ^"cron" ~ (":" | ws) ~ ws* }
cron_field_char = _{
    ASCII_ALPHANUMERIC
  | "*"
  | "/"
  | "-"
  | ","
}
cron_field = @{ cron_field_char+ }
cron_expr = @{ cron_field ~ (ws+ ~ cron_field){4} }
cron = ${ cron_prefix ~ cron_expr ~ &(ws | EOI) }
// ------------

// --- interval ---
interval = ${
    interval_component+
}
interval_component = _{
    interval_component_years
  | interval_component_months
  | interval_component_weeks
  | interval_component_days
  | interval_component_hours
  | interval_component_minutes
  | interval_component_seconds
}
time_interval = ${
    time_interval_component+
}
time_interval_component = _{
    interval_component_hours
  | interval_component_minutes
  | interval_component_seconds
}
date_interval = ${
    date_interval_component+
}
date_interval_component = _{
    interval_component_years
  | date_interval_component_months
  | interval_component_weeks
  | interval_component_days
}

interval_component_years   = _{ interval_years   ~ year_unit }
interval_component_months  = _{ interval_months  ~ month_unit }
interval_component_weeks   = _{ interval_weeks   ~ week_unit }
interval_component_days    = _{ interval_days    ~ day_unit }
interval_component_hours   = _{ interval_hours   ~ hour_unit }
interval_component_minutes = _{ interval_minutes ~ minute_unit }
interval_component_seconds = _{ interval_seconds ~ second_unit }
date_interval_component_months  = _{ interval_months  ~ date_month_unit }

interval_value   = @{ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
interval_years   = @{ interval_value }
interval_months  = @{ interval_value }
interval_weeks   = @{ interval_value }
interval_days    = @{ interval_value }
interval_hours   = @{ interval_value }
interval_minutes = @{ interval_value }
interval_seconds = @{ interval_value }
// ----------------

// --- date and time formats ---
// accept both year/month/day and day.month.year formats
date = _{
    ( ( year ~ "/" )? ~ month ~ "/" )? ~ day ~ &(splitter | "/" | ws)
  | day ~ ( "." ~ month ~ ( "." ~ year )? )?
}
time = _{
    hour ~ ( ":" ~ minute ~ ( ":" ~ second )? )?
}
// -----------------------------

// --- date and time divisors ---
date_divisor = _{
    interval_divisor_hrprefix ~ date_interval
  | weekdays_divisor_hrprefix ~ weekdays_ranges
}
time_divisor = _{
    time_divisor_hrprefix
    ~ time_interval
}
// ------------------------------

// --- date and time ranges ---
date_from  = ${ date }
date_until = ${ date }
dates_point = ${ date }
dates_range = ${
    date_divisor
  | date_from? ~ splitter ~ date_until? ~ (ws* ~ date_divisor)?
  | date_from ~ ws* ~ date_divisor
}
dates_pattern = _{
    dates_range | dates_point
}
dates_patterns = _{
    dates_pattern ~ ("," ~ dates_pattern)*
}

time_from  = ${ time }
time_until = ${ time }
time_point = ${ time }
time_range = ${
    time_divisor
  | time_from? ~ splitter ~ time_until? ~ ws* ~ time_divisor
  | time_from ~ ws* ~ time_divisor
}
time_pattern = _{
    time_range | time_point
}
time_patterns = _{
    time_hrprefix
  ~ time_pattern ~ ("," ~ time_pattern)*
}

weekday_from = ${ weekday }
weekday_to   = ${ weekday }
weekdays_range = ${
    weekday_from ~ ("-" ~ weekday_to)?
}
weekdays_ranges = _{
    weekdays_range ~ ("," ~ weekdays_range)*
}
// ----------------------------

// --- reminder patterns ---
recurrence = ${
    dates_patterns ~ ws+ ~ time_patterns
  | time_patterns
}
countdown_one = _{
    countdown_hrprefix ~ interval
}
countdown = ${
    countdown_one ~ ("," ~ countdown_one)*
}
nag_suffix = ${
    "!" ~ interval
}
reminder_pattern = _{
    recurrence ~ nag_suffix? ~ &(ws | EOI)
  | countdown ~ nag_suffix? ~ &(ws | EOI)
  | cron
}
// -------------------------

// --- description ---
// match non-empty sequence of words
// until trailing whitespace sequence (exclusive)
description_word = _{ (!ws ~ ANY)+ }
description = @{ description_word ~ (ws* ~ description_word)* }
// -------------------

reminder = ${
    SOI
    ~ ws* ~ reminder_pattern 
    ~ ws* ~ description?
    ~ ws* ~ EOI
}