Skip to content

Instantly share code, notes, and snippets.

@remi-dupre
Created April 30, 2020 07:57
Show Gist options
  • Save remi-dupre/7cbbc13d52e3f90006196fa8104d7250 to your computer and use it in GitHub Desktop.
Save remi-dupre/7cbbc13d52e3f90006196fa8104d7250 to your computer and use it in GitHub Desktop.
grammar for OSM opening hours
// Grammar for opening hours description as defined in OSM wiki:
// https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
// Time domain
input_time_domain = _{ SOI ~ time_domain ~ &EOI }
time_domain = { rule_sequence ~ ( any_rule_separator ~ rule_sequence )* }
rule_sequence = { selector_sequence ~ (space ~ rules_modifier)? }
// Rule separators
any_rule_separator = {
normal_rule_separator
| additional_rule_separator
| fallback_rule_separator
}
normal_rule_separator = { ";" ~ space }
additional_rule_separator = { "," ~ space }
fallback_rule_separator = { space ~ "||" ~ space }
// Rule modifier
rules_modifier = {
rules_modifier_enum ~ ( space ~ comment )?
| comment
}
rules_modifier_enum = {
rules_modifier_enum_closed
| rules_modifier_enum_open
| rules_modifier_enum_unknown
}
rules_modifier_enum_closed = @{ "closed" | "off" }
rules_modifier_enum_open = @{ "open" }
rules_modifier_enum_unknown = @{ "unknown" }
// Selectors
selector_sequence = {
"24/7"
| wide_range_selectors ~ small_range_selectors
}
wide_range_selectors = {
year_selector? ~ monthday_selector? ~ week_selector? ~
separator_for_readability?
}
small_range_selectors = {
weekday_selector ~ space ~ time_selector
| weekday_selector
| time_selector
}
separator_for_readability = { ":" }
// Time selector
time_selector = { timespan ~ ( "," ~ timespan )* }
timespan = {
time ~ "-" ~ extended_time ~ "/" ~ hour_minutes
| time ~ "-" ~ extended_time ~ "/" ~ minute
| time ~ "-" ~ extended_time ~ "+"
| time ~ "-" ~ extended_time
| time ~ "+"
| time
}
time = {
hour_minutes
| variable_time
}
extended_time = {
extended_hour_minutes
| variable_time
}
variable_time = {
"(" ~ event ~ plus_or_minus ~ hour_minutes ~ ")"
| event
}
event = { "dawn" | "sunrise" | "sunset" | "dusk" }
// Weekday selector
weekday_selector = {
holiday_sequence ~ ( "," ~ weekday_sequence )?
| weekday_sequence ~ ( "," ~ holiday_sequence )?
| holiday_sequence ~ space ~ weekday_sequence
}
weekday_sequence = { weekday_range ~ ( "," ~ weekday_range )* }
weekday_range = {
wday ~ ( nth_entry ~ ( "," ~ nth_entry )* ) ~ date_offset?
| wday ~ "-" ~ wday
| wday
}
holiday_sequence = { holiday ~ ( "," ~ holiday )* }
holiday = {
public_holiday ~ day_offset?
| school_holiday
}
public_holiday = { "PH" }
school_holiday = { "SH" }
nth_entry = {
nth ~ "-" ~ nth
| "-" ~ nth
| nth
}
nth = { '1'..'5' }
day_offset = { space ~ plus_or_minus ~ positive_number ~ space ~ "day" ~ "s"? }
// Week selector
week_selector = { week ~ ( "," ~ week )* }
week = { weeknum ~ ( "-" ~ weeknum ~ ( "/" ~ positive_number )? )? }
// Mounth selector
monthday_selector = { monthday_range ~ ( "," ~ monthday_range )* }
monthday_range = {
year? ~ month ~ ( "-" ~ month )?
| date_from ~ date_offset? ~ "-" ~ date_to ~ date_offset?
| date_from ~ date_offset*
}
date_offset = {
(plus_or_minus ~ wday) ~ day_offset?
| day_offset
}
date_from = {
year? ~ month ~ daynum
| year? ~ variable_date
}
date_to = {
date_from
| daynum
}
variable_date = { "easter" }
// Year selector
year_selector = { year_range ~ ( "," ~ year_range )* }
year_range = {
year ~ ( "-" ~ year ~ ( "/" ~ positive_number ) )?
| year ~ "+"
}
// Basic elements
plus_or_minus = @{ "+" | "-" }
hour = @{
"00" | "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10"
| "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" |
"21" | "22" | "23" | "24"
}
extended_hour = @{
hour | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32" | "33" | "34"
| "35" | "36" | "37" | "38" | "39" | "40" | "41" | "42" | "43" | "44" |
"45" | "46" | "47" | "48"
}
minute = @{
"00" | "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10"
| "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" |
"21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31"
| "32" | "33" | "34" | "35" | "36" | "37" | "38" | "39" | "40" | "41" |
"42" | "43" | "44" | "45" | "46" | "47" | "48" | "49" | "50" | "51" | "52"
| "53" | "54" | "55" | "56" | "57" | "58" | "59"
}
hour_minutes = { hour ~ ":" ~ minute }
extended_hour_minutes = { extended_hour ~ ":" ~ minute }
wday = @{ "Su" | "Mo" | "Tu" | "We" | "Th" | "Fr" | "Sa" }
daynum = @{
"01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10" | "11"
| "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" |
"22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31"
}
weeknum = @{
"01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10" | "11"
| "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" |
"22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32"
| "33" | "34" | "35" | "36" | "37" | "38" | "39" | "40" | "41" | "42" |
"43" | "44" | "45" | "46" | "47" | "48" | "49" | "50" | "51" | "52" | "53"
}
month = @{
"Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" |
"Oct" | "Nov" | "Dec"
}
year = @{
( "19" ~ ASCII_DIGIT{2} )
| ( '2'..'9' ~ ASCII_DIGIT{3} )
}
positive_number = @{ "0"* ~ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
comment = @{ comment_delimiter ~ comment_character+ ~ comment_delimiter }
comment_delimiter = @{ "\"" }
comment_character = @{ !comment_delimiter ~ ANY }
space = _{ " " }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment