eol = _{ NEWLINE | EOI } WHITESPACE = _{ !eol ~ WHITE_SPACE } rest_some = { (!eol ~ ANY)+ } rest_any = { (!eol ~ ANY)* } include = { "INCLUDE" ~ WHITESPACE ~ rest_some ~ eol } timezone = { "TIMEZONE" ~ WHITESPACE ~ rest_some ~ eol } number = @{ ASCII_DIGIT{1,9} } // Fits into an i32 title = { WHITESPACE ~ rest_some ~ eol } year = @{ ASCII_DIGIT{4} } month = @{ ASCII_DIGIT{2} } day = @{ ASCII_DIGIT{2} } datum = ${ year ~ "-" ~ month ~ "-" ~ day } bdatum = ${ (year | "?") ~ "-" ~ month ~ "-" ~ day } hour = @{ ASCII_DIGIT{2} } minute = @{ ASCII_DIGIT{2} } time = ${ hour ~ ":" ~ minute } weekday = { "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun" } amount_sign = { "+" | "-" } amount = { amount_sign? ~ number? } delta_weekdays = { amount ~ weekday } delta_minutes = { amount ~ "min" } delta_years = { amount ~ "y" } delta_months = { amount ~ "m" } delta_months_reverse = { amount ~ "M" } delta_days = { amount ~ "d" } delta_weeks = { amount ~ "w" } delta_hours = { amount ~ "h" } delta = { ( delta_weekdays | delta_minutes | delta_years | delta_months | delta_months_reverse | delta_days | delta_weeks | delta_hours )+ } boolean = { "true" | "false" } variable = { "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun" | "isWeekday" | "isWeekend" | "isLeapYear" | "j" | "yl" | "yd" | "yD" | "yw" | "yW" | "y" | "ml" | "mw" | "mW" | "m" | "d" | "D" | "iyl" | "iy" | "wd" | "e" } unop_neg = { "-" } unop_not = { "!" } unop = _{ unop_neg | unop_not } op_add = { "+" } op_sub = { "-" } op_mul = { "*" } op_div = { "/" } op_mod = { "%" } op_eq = { "=" } op_neq = { "!=" } op_lt = { "<" } op_lte = { "<=" } op_gt = { ">" } op_gte = { ">=" } op_and = { "&" } op_or = { "|" } op_xor = { "^" } op = _{ op_add | op_sub | op_mul | op_div | op_mod | op_eq | op_neq | op_lt | op_lte | op_gt | op_gte | op_and | op_or | op_xor } paren_expr = { "(" ~ expr ~ ")" } unop_expr = { unop ~ expr } term = { number | boolean | variable | paren_expr | unop_expr } expr = { term ~ (op ~ term)* } date_fixed_start = { datum ~ delta? ~ time? } date_fixed_end = { datum ~ delta? ~ time? | delta ~ time? | time } repeat_done = { "done" } date_fixed_repeat = { repeat_done? ~ delta } date_fixed = { date_fixed_start ~ ("--" ~ date_fixed_end)? ~ (";" ~ date_fixed_repeat)? } date_expr_start = { ("*" | paren_expr) ~ delta? ~ time? } date_expr_end = { delta ~ time? | time } date_expr = { date_expr_start ~ ("--" ~ date_expr_end)? } date_weekday_start = { weekday ~ time? } date_weekday_end = { weekday ~ time? | delta ~ time? | time } date_weekday = { date_weekday_start ~ ("--" ~ date_weekday_end)? } stmt_date = !{ "DATE" ~ (date_fixed | date_expr | date_weekday) ~ eol } stmt_bdate = !{ "BDATE" ~ bdatum ~ eol } stmt_from = !{ "FROM" ~ (datum | "*") ~ eol } stmt_until = !{ "UNTIL" ~ (datum | "*") ~ eol } stmt_except = !{ "EXCEPT" ~ datum ~ eol } stmt_move = !{ "MOVE" ~ datum ~ "TO" ~ datum ~ eol } statements = { (stmt_date | stmt_bdate | stmt_from | stmt_until | stmt_except | stmt_move)* } donedate = { datum ~ time ~ "--" ~ datum ~ time | datum ~ time ~ "--" ~ time | datum ~ time | datum ~ "--" ~ datum | datum } done = !{ "DONE" ~ "[" ~ datum ~ "]" ~ donedate? ~ eol } dones = { done* } desc_line = { "#" ~ (" " ~ rest_any)? ~ eol } description = { desc_line* } task = { "TASK" ~ title ~ statements ~ dones ~ description } note = { "NOTE" ~ title ~ statements ~ description } empty_line = _{ WHITESPACE* ~ NEWLINE } command = { include | timezone | task | note } file = ${ SOI ~ (empty_line* ~ command)* ~ empty_line* ~ WHITESPACE* ~ EOI }