grammar Deeds::Grammar
rule root (date interval? durations? activity?) { def date capture(:date).value end def minutes result = [] result.push(capture(:interval).try!(:minutes) || 0) result.push(capture(:durations).try!(:minutes) || 0) result.sum end def activity capture(:activity).value end } end # What you did during that time is entirely freetext # rule activity (.*) { to_str.presence } end # A Date in the format `yyyy-mm-dd`. # The `#value` is a `Date` instance if it's a valid calendar date. # rule date (number "-" number "-" number) { begin Date.parse to_str rescue ArgumentError exception = Citrus::ParseError.new input exception.message << ' This is not a valid date' raise exception end } end # An Interval is the difference between one time and another # during the same day, separated by a dash. Whitespace is ignored. # The `#minutes` is the difference in minutes or nil if invalid. # # Examples # # `8:00-17:00` # `3:5 - 23:07` # rule interval (from:time "-" till:time) { def minutes return if capture(:till).minutes.nil? return if capture(:from).minutes.nil? capture(:till).minutes - capture(:from).minutes end } end # A Timestamp is two digits separated by a colon. # It can only do military time and it ignores whitespace. # `#minutes` contains the number of seconds since midnight or nil. # # Examples # # `7:30` # ` 23 : 50 ` # rule time (number ":" number) { def minutes Time.parse(to_str).seconds_since_midnight.to_i / 60 rescue ArgumentError exception = Citrus::ParseError.new input exception.message << ' This is not a valid time' raise exception end } end # One or more durations. # This is just a wrapper for `multiple_durations`. # I'm not entirely sure how to refactor this away. # # Examples # # `1m` # `7h` # `4m 6m 3h` # rule durations multiple_durations | duration end # Two or more durations. # The total minutes are summed up in `#minutes`. # # Examples # # `3m -4m` # `5h 2m +6m 20m` # rule multiple_durations (duration more:(multiple_durations | duration)) { def minutes [capture(:duration).minutes, capture(:more).minutes].sum end } end # Duration is either a minute or an hour. # The number of minutes are exposed via `#minutes`. # # Examples # # `3m` # `5h` # rule duration minute | hour end # A minute is a digit immediately follwed by the letter `m`. # Whitespace is only allowed before the digit and after the `m`. # # Examples # # `3m` # ` 3m ` # rule minute (number "m" whitespace) { def minutes capture(:number).value end } end # An hour is a digit immediately follwed by the letter `h`. # Whitespace is only allowed before the digit and after the `h`. # Hours are broken down into minutes by `#minutes` # # Examples # # `3h` # ` 3h ` # rule hour (number "h" whitespace) { def minutes capture(:number).value * 60 end } end # A number can optionally have a minus or a plus as prefix. # Whitespace is allowed, even between that prefix and the digit. # rule number (whitespace [\-\+]? whitespace [0-9]+ whitespace) { to_str.gsub(/[^\-\+\d]*/, '').to_i } end rule whitespace [\s]* end
end