content: -- Copyright (c) 2016-present, Facebook, Inc.
  -- All rights reserved.
  --
  -- This source code is licensed under the BSD-style license found in the
  -- LICENSE file in the root directory of this source tree.
  
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE NoRebindableSyntax #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Duckling.Time.HE.Rules
    ( rules
    ) where
  
  import Data.Maybe
  import Data.String
  import Data.Text (Text)
  import Prelude
  import qualified Data.Text as Text
  
  import Duckling.Dimensions.Types
  import Duckling.Duration.Helpers (isGrain)
  import Duckling.Numeral.Helpers (parseInt)
  import Duckling.Numeral.Types (NumeralData(..))
  import Duckling.Ordinal.Types (OrdinalData(..))
  import Duckling.Regex.Types
  import Duckling.Time.Helpers
  import Duckling.Time.Types (TimeData(..))
  import Duckling.Types
  import qualified Duckling.Numeral.Types as TNumeral
  import qualified Duckling.Ordinal.Types as TOrdinal
  import qualified Duckling.TimeGrain.Types as TG
  import qualified Duckling.Time.Types as TTime
  
  ruleNextDayofweek :: Rule
  ruleNextDayofweek = Rule
    { name = "next <day-of-week>"
    , pattern =
      [ Predicate isADayOfWeek
      , regex "(הבא(ה)?)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth 1 True td
        _ -> Nothing
    }
  
  ruleNamedday :: Rule
  ruleNamedday = Rule
    { name = "ב <named-day>"
    , pattern =
      [ regex "ב"
      , Predicate isADayOfWeek
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleAtHourTimeofday :: Rule
  ruleAtHourTimeofday = Rule
    { name = "at hour <time-of-day>"
    , pattern =
      [ regex "בשעה"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) ->
          tt $ notLatent td
        _ -> Nothing
    }
  
  ruleHourofdayAndInteger :: Rule
  ruleHourofdayAndInteger = Rule
    { name = "<hour-of-day> and <integer>"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "ו"
      , Predicate $ isIntegerBetween 1 59
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         _:
         token:
         _) -> do
          n <- getIntValue token
          tt $ hourMinute is12H hours n
        _ -> Nothing
    }
  
  ruleHourofdayAndQuarter :: Rule
  ruleHourofdayAndQuarter = Rule
    { name = "<hour-of-day> and quarter"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "ו"
      , regex "רבע(י)?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) ->
          tt $ hourMinute is12H hours 15
        _ -> Nothing
    }
  
  ruleHourofdayAndHalf :: Rule
  ruleHourofdayAndHalf = Rule
    { name = "<hour-of-day> and half"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "ו"
      , regex "חצי|מחצית"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) ->
          tt $ hourMinute is12H hours 30
        _ -> Nothing
    }
  
  ruleHourofdayInteger :: Rule
  ruleHourofdayInteger = Rule
    { name = "<hour-of-day> <integer>"
    , pattern =
      [ Predicate isAnHourOfDay
      , Predicate $ isIntegerBetween 1 59
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:
         token:
         _) -> do
          n <- getIntValue token
          tt $ hourMinute is12H hours n
        _ -> Nothing
    }
  
  ruleHourofdayQuarter :: Rule
  ruleHourofdayQuarter = Rule
    { name = "<hour-of-day> quarter"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "רבע(י)?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) ->
          tt $ hourMinute is12H hours 15
        _ -> Nothing
    }
  
  ruleHourofdayHalf :: Rule
  ruleHourofdayHalf = Rule
    { name = "<hour-of-day> half"
    , pattern =
      [ Predicate isAnHourOfDay
      , regex "חצי|מחצית"
      ]
    , prod = \tokens -> case tokens of
        (Token Time TimeData {TTime.form = Just (TTime.TimeOfDay (Just hours) is12H)}:_) ->
          tt $ hourMinute is12H hours 30
        _ -> Nothing
    }
  
  ruleIntegerTotillbeforeIntegerHourofday :: Rule
  ruleIntegerTotillbeforeIntegerHourofday = Rule
    { name = "<integer> to|till|before <integer> (hour-of-day)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 59
      , regex "לפני|ל"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> do
          n <- getIntValue token
          Token Time <$> minutesBefore n td
        _ -> Nothing
    }
  
  ruleQuarterTotillbeforeIntegerHourofday :: Rule
  ruleQuarterTotillbeforeIntegerHourofday = Rule
    { name = "quarter to|till|before <integer> (hour-of-day)"
    , pattern =
      [ regex "רבע(י)?"
      , regex "לפני|ל"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:_:Token Time td:_) -> Token Time <$> minutesBefore 15 td
        _ -> Nothing
    }
  
  ruleHalfTotillbeforeIntegerHourofday :: Rule
  ruleHalfTotillbeforeIntegerHourofday = Rule
    { name = "half to|till|before <integer> (hour-of-day)"
    , pattern =
      [ regex "חצי|מחצית"
      , regex "לפני|ל"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:_:Token Time td:_) -> Token Time <$> minutesBefore 30 td
        _ -> Nothing
    }
  
  ruleIntegerAfterpastIntegerHourofday :: Rule
  ruleIntegerAfterpastIntegerHourofday = Rule
    { name = "integer after|past <integer> (hour-of-day)"
    , pattern =
      [ Predicate $ isIntegerBetween 1 59
      , regex "אחרי"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> do
          n <- getIntValue token
          Token Time <$> minutesAfter n td
        _ -> Nothing
    }
  
  ruleQuarterAfterpastIntegerHourofday :: Rule
  ruleQuarterAfterpastIntegerHourofday = Rule
    { name = "quarter after|past <integer> (hour-of-day)"
    , pattern =
      [ regex "רבע(י)?"
      , regex "אחרי"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:_:Token Time td:_) -> Token Time <$> minutesAfter 15 td
        _ -> Nothing
    }
  
  ruleHalfAfterpastIntegerHourofday :: Rule
  ruleHalfAfterpastIntegerHourofday = Rule
    { name = "half after|past <integer> (hour-of-day)"
    , pattern =
      [ regex "חצי|מחצית"
      , regex "אחרי"
      , Predicate isAnHourOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> Token Time <$> minutesAfter 30 td
        _ -> Nothing
    }
  
  ruleNamedday2 :: Rule
  ruleNamedday2 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?שני"
      ]
    , prod = \_ -> tt $ dayOfWeek 1
    }
  
  ruleSinceTimeofday :: Rule
  ruleSinceTimeofday = Rule
    { name = "since <time-of-day>"
    , pattern =
      [ regex "מ"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleLastTime :: Rule
  ruleLastTime = Rule
    { name = "last <time>"
    , pattern =
      [ dimension Time
      , regex "שעבר|(ה)?קודם|(ה)?אחרון"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth (-1) False td
        _ -> Nothing
    }
  
  ruleNamedday6 :: Rule
  ruleNamedday6 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?שישי"
      ]
    , prod = \_ -> tt $ dayOfWeek 5
    }
  
  ruleDatetimeDatetimeInterval :: Rule
  ruleDatetimeDatetimeInterval = Rule
    { name = "<datetime> - <datetime> (interval)"
    , pattern =
      [ Predicate isNotLatent
      , regex "\\-|ע(ד)?"
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleTheDayofmonthNonOrdinal :: Rule
  ruleTheDayofmonthNonOrdinal = Rule
    { name = "the <day-of-month> (non ordinal)"
    , pattern =
      [ regex "ה/S"
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (_:token:_) -> do
          n <- getIntValue token
          tt . mkLatent $ dayOfMonth n
        _ -> Nothing
    }
  
  ruleCycleAfterTime :: Rule
  ruleCycleAfterTime = Rule
    { name = "<cycle> after <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "אחרי"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter False grain 1 td
        _ -> Nothing
    }
  
  ruleInDuration :: Rule
  ruleInDuration = Rule
    { name = "in <duration>"
    , pattern =
      [ regex "בעוד"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleInNamedmonth :: Rule
  ruleInNamedmonth = Rule
    { name = "in <named-month>"
    , pattern =
      [ regex "ב"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleNow :: Rule
  ruleNow = Rule
    { name = "now"
    , pattern =
      [ regex "עכשיו|מייד"
      ]
    , prod = \_ -> tt now
    }
  
  ruleCurrentDayofweek :: Rule
  ruleCurrentDayofweek = Rule
    { name = "current <day-of-week>"
    , pattern =
      [ Predicate isADayOfWeek
      , regex "(הזה|הזאת|הקרוב(ה)?)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth 0 True td
        _ -> Nothing
    }
  
  ruleFromDatetimeDatetimeInterval :: Rule
  ruleFromDatetimeDatetimeInterval = Rule
    { name = "from <datetime> - <datetime> (interval)"
    , pattern =
      [ regex "מ|משעה"
      , dimension Time
      , regex "\\-|עד"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleNamedday4 :: Rule
  ruleNamedday4 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?רביעי"
      ]
    , prod = \_ -> tt $ dayOfWeek 3
    }
  
  ruleTheCycleAfterTime :: Rule
  ruleTheCycleAfterTime = Rule
    { name = "the <cycle> after <time>"
    , pattern =
      [ regex "ה"
      , dimension TimeGrain
      , regex "אחרי"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter False grain 1 td
        _ -> Nothing
    }
  
  ruleTheCycleBeforeTime :: Rule
  ruleTheCycleBeforeTime = Rule
    { name = "the <cycle> before <time>"
    , pattern =
      [ regex "ה"
      , dimension TimeGrain
      , regex "לפני"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter False grain (-1) td
        _ -> Nothing
    }
  
  ruleLastDayofweek :: Rule
  ruleLastDayofweek = Rule
    { name = "last <day-of-week>"
    , pattern =
      [ Predicate isADayOfWeek
      , regex "(שעבר(ה)?|הקודמת|הקודם)"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth 0 True td
        _ -> Nothing
    }
  
  ruleTheIdesOfNamedmonth :: Rule
  ruleTheIdesOfNamedmonth = Rule
    { name = "the ides of <named-month>"
    , pattern =
      [ regex "באמצע"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td@TimeData {TTime.form = Just (TTime.Month m)}:_) ->
          Token Time <$>
            intersect td (dayOfMonth $ if elem m [3, 5, 7, 10] then 15 else 13)
        _ -> Nothing
    }
  
  ruleNoon :: Rule
  ruleNoon = Rule
    { name = "noon"
    , pattern =
      [ regex "(ב)?צהריים"
      ]
    , prod = \_ -> tt $ hour False 12
    }
  
  ruleToday :: Rule
  ruleToday = Rule
    { name = "today"
    , pattern =
      [ regex "היום"
      ]
    , prod = \_ -> tt today
    }
  
  ruleBetweenTimeofdayAndTimeofdayInterval :: Rule
  ruleBetweenTimeofdayAndTimeofdayInterval = Rule
    { name = "between <time-of-day> and <time-of-day> (interval)"
    , pattern =
      [ regex "בין"
      , Predicate isATimeOfDay
      , regex "ל"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleNextCycle :: Rule
  ruleNextCycle = Rule
    { name = "next <cycle>"
    , pattern =
      [ dimension TimeGrain
      , regex "הבא(ה)?"
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_) -> tt $ cycleNth grain 1
        _ -> Nothing
    }
  
  ruleForDuration :: Rule
  ruleForDuration = Rule
    { name = "for <duration>"
    , pattern =
      [ regex "תוך"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) ->
          tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleDurationFromNow :: Rule
  ruleDurationFromNow = Rule
    { name = "<duration> from now"
    , pattern =
      [ dimension Duration
      , regex "מעכשיו"
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_) ->
          tt $ inDuration dd
        _ -> Nothing
    }
  
  ruleLunch :: Rule
  ruleLunch = Rule
    { name = "lunch"
    , pattern =
      [ regex "(ב)?צהריים"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 12) (hour False 14)
    }
  
  ruleLastCycle :: Rule
  ruleLastCycle = Rule
    { name = "last <cycle>"
    , pattern =
      [ dimension TimeGrain
      , regex "האחרון|האחרונה|שעבר|שעברה"
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_) ->
          tt . cycleNth grain $ - 1
        _ -> Nothing
    }
  
  ruleAfternoon :: Rule
  ruleAfternoon = Rule
    { name = "afternoon"
    , pattern =
      [ regex "אחה(״)?צ|אחר הצהריים"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 12) (hour False 19)
    }
  
  ruleNamedmonthDayofmonthOrdinal :: Rule
  ruleNamedmonthDayofmonthOrdinal = Rule
    { name = "<named-month> <day-of-month> (ordinal)"
    , pattern =
      [ Predicate isAMonth
      , Predicate isDOMOrdinal
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleNamedday5 :: Rule
  ruleNamedday5 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?חמישי"
      ]
    , prod = \_ -> tt $ dayOfWeek 4
    }
  
  ruleDayofmonthordinalNamedmonth :: Rule
  ruleDayofmonthordinalNamedmonth = Rule
    { name = "<day-of-month>(ordinal) <named-month>"
    , pattern =
      [ Predicate isDOMOrdinal
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleIntersectBy :: Rule
  ruleIntersectBy = Rule
    { name = "intersect by \",\""
    , pattern =
      [ Predicate isNotLatent
      , regex ","
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) -> Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleNthTimeAfterTime :: Rule
  ruleNthTimeAfterTime = Rule
    { name = "nth <time> after <time>"
    , pattern =
      [ dimension Ordinal
      , dimension Time
      , regex "אחרי"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Ordinal od:Token Time td1:_:Token Time td2:_) ->
          tt $ predNthAfter (TOrdinal.value od - 1) td1 td2
        _ -> Nothing
    }
  
  ruleMmdd :: Rule
  ruleMmdd = Rule
    { name = "mm/dd"
    , pattern =
      [ regex "(0?[1-9]|1[0-2])/(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (mm:dd:_)):_) -> do
          m <- parseInt mm
          d <- parseInt dd
          tt $ monthDay m d
        _ -> Nothing
    }
  
  ruleAfterDuration :: Rule
  ruleAfterDuration = Rule
    { name = "after <duration>"
    , pattern =
      [ regex "אחרי"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) -> tt . withDirection TTime.After $ inDuration dd
        _ -> Nothing
    }
  
  ruleTimeofdayLatent :: Rule
  ruleTimeofdayLatent = Rule
    { name = "time-of-day (latent)"
    , pattern =
      [ Predicate $ isIntegerBetween 0 23
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt . mkLatent $ hour True n
        _ -> Nothing
    }
  
  ruleDayofmonthOrdinalOfNamedmonth :: Rule
  ruleDayofmonthOrdinalOfNamedmonth = Rule
    { name = "<day-of-month> (ordinal) of <named-month>"
    , pattern =
      [ Predicate isDOMOrdinal
      , regex "של|ב|ל"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleThisEvening :: Rule
  ruleThisEvening = Rule
    { name = "this evening"
    , pattern =
      [ regex "הערב"
      ]
    , prod = \_ -> do
        td <- interval TTime.Open (hour False 18) (hour False 0)
        Token Time . partOfDay <$> intersect today td
    }
  
  ruleBetweenDatetimeAndDatetimeInterval :: Rule
  ruleBetweenDatetimeAndDatetimeInterval = Rule
    { name = "between <datetime> and <datetime> (interval)"
    , pattern =
      [ regex "בין"
      , dimension Time
      , regex "ל"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) ->
          Token Time <$> interval TTime.Closed td1 td2
        _ -> Nothing
    }
  
  ruleEndOfYear :: Rule
  ruleEndOfYear = Rule
    { name = "End of year"
    , pattern =
      [ regex "סוף (ה)?שנה"
      ]
    , prod = \_ -> tt $ cycleNth TG.Year 1
    }
  
  ruleDurationAgo :: Rule
  ruleDurationAgo = Rule
    { name = "<duration> ago"
    , pattern =
      [ regex "לפני"
      , dimension Duration
      ]
    , prod = \tokens -> case tokens of
        (_:Token Duration dd:_) ->
          tt $ durationAgo dd
        _ -> Nothing
    }
  
  ruleLastNCycle :: Rule
  ruleLastNCycle = Rule
    { name = "last n <cycle>"
    , pattern =
      [ Predicate $ isIntegerBetween 1 9999
      , dimension TimeGrain
      , regex "אחרון|אחרונות|אחרונה|אחרונים"
      ]
    , prod = \tokens -> case tokens of
        (token:Token TimeGrain grain:_) -> do
          v <- getIntValue token
          tt . cycleN True grain $ - v
        _ -> Nothing
    }
  
  ruleMidnighteodendOfDay :: Rule
  ruleMidnighteodendOfDay = Rule
    { name = "midnight|EOD|end of day"
    , pattern =
      [ regex "(ב)?חצות"
      ]
    , prod = \_ -> tt $ hour False 0
    }
  
  ruleDayofmonthNonOrdinalNamedmonth :: Rule
  ruleDayofmonthNonOrdinalNamedmonth = Rule
    { name = "<day-of-month> (non ordinal) <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleIntersect :: Rule
  ruleIntersect = Rule
    { name = "intersect"
    , pattern =
      [ Predicate isNotLatent
      , Predicate isNotLatent
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:Token Time td2:_) -> Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleUntilTimeofday :: Rule
  ruleUntilTimeofday = Rule
    { name = "until <time-of-day>"
    , pattern =
      [ regex "עד"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) ->
          tt $ withDirection TTime.Before td
        _ -> Nothing
    }
  
  ruleAtTimeofday :: Rule
  ruleAtTimeofday = Rule
    { name = "at <time-of-day>"
    , pattern =
      [ regex "ב"
      , Predicate isATimeOfDay
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleNthTimeOfTime :: Rule
  ruleNthTimeOfTime = Rule
    { name = "nth <time> of <time>"
    , pattern =
      [ dimension Time
      , dimension Ordinal
      , regex "של|ב"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:Token Ordinal od:_:Token Time td2:_) -> Token Time .
          predNth (TOrdinal.value od - 1) False <$> intersect td2 td1
        _ -> Nothing
    }
  
  ruleTimePartofday :: Rule
  ruleTimePartofday = Rule
    { name = "<time> <part-of-day>"
    , pattern =
      [ dimension Time
      , Predicate isAPartOfDay
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:Token Time pod:_) -> Token Time <$> intersect pod td
        _ -> Nothing
    }
  
  ruleWeekend :: Rule
  ruleWeekend = Rule
    { name = "week-end"
    , pattern =
      [ regex "(סופ״ש|סוף השבוע)"
      ]
    , prod = \_ -> tt weekend
    }
  
  ruleNameddayDayofmonthOrdinal :: Rule
  ruleNameddayDayofmonthOrdinal = Rule
    { name = "<named-day> <day-of-month> (ordinal)"
    , pattern =
      [ Predicate isADayOfWeek
      , Predicate isDOMOrdinal
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleDate :: Rule
  ruleDate = Rule
    { name = "ב <date>"
    , pattern =
      [ regex "ב"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:x:_) -> Just x
        _ -> Nothing
    }
  
  ruleNextTime :: Rule
  ruleNextTime = Rule
    { name = "next <time>"
    , pattern =
      [ Predicate isNotLatent
      , regex "הבא(ה)?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ predNth 0 True td
        _ -> Nothing
    }
  
  ruleOrdinalQuarterYear :: Rule
  ruleOrdinalQuarterYear = Rule
    { name = "<ordinal> quarter <year>"
    , pattern =
      [ dimension Ordinal
      , Predicate $ isGrain TG.Quarter
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Ordinal od:_:Token Time td:_) ->
          tt $ cycleNthAfter False TG.Quarter (TOrdinal.value od - 1) td
        _ -> Nothing
    }
  
  ruleYyyymmdd :: Rule
  ruleYyyymmdd = Rule
    { name = "yyyy-mm-dd"
    , pattern =
      [ regex "(\\d{2,4})-(0?[1-9]|1[0-2])-(3[01]|[12]\\d|0?[1-9])"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (yy:mm:dd:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleTheOrdinalCycleAfterTime :: Rule
  ruleTheOrdinalCycleAfterTime = Rule
    { name = "the <ordinal> <cycle> after <time>"
    , pattern =
      [ regex "ה"
      , dimension Ordinal
      , dimension TimeGrain
      , regex "אחרי|לאחר"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td
        _ -> Nothing
    }
  
  ruleNextNCycle :: Rule
  ruleNextNCycle = Rule
    { name = "next n <cycle>"
    , pattern =
      [ Predicate $ isIntegerBetween 1 9999
      , dimension TimeGrain
      , regex "הבא|הבאה|הבאים|הבאות"
      ]
    , prod = \tokens -> case tokens of
        (token:Token TimeGrain grain:_) -> do
          v <- getIntValue token
          tt $ cycleN True grain v
        _ -> Nothing
    }
  
  ruleMorning :: Rule
  ruleMorning = Rule
    { name = "morning"
    , pattern =
      [ regex "(ב)?בוקר"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 4) (hour False 12)
    }
  
  ruleThisCycle :: Rule
  ruleThisCycle = Rule
    { name = "this <cycle>"
    , pattern =
      [ dimension TimeGrain
      , regex "הקרו(ב)?ה|הזה|הזאת"
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_) ->
          tt $ cycleNth grain 0
        _ -> Nothing
    }
  
  ruleThisTime :: Rule
  ruleThisTime = Rule
    { name = "this <time>"
    , pattern =
      [ dimension Time
      , regex "הקרוב|הזה"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) ->
          tt $ predNth 0 False td
        _ -> Nothing
    }
  
  ruleDayofmonthNonOrdinalOfNamedmonth :: Rule
  ruleDayofmonthNonOrdinalOfNamedmonth = Rule
    { name = "<day-of-month> (non ordinal) of <named-month>"
    , pattern =
      [ Predicate isDOMInteger
      , regex "של|ב|ל"
      , Predicate isAMonth
      ]
    , prod = \tokens -> case tokens of
        (token:_:Token Time td:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleEndOfMonth :: Rule
  ruleEndOfMonth = Rule
    { name = "End of month"
    , pattern =
      [ regex "סוף (ה)?חודש"
      ]
    , prod = \_ -> tt $ cycleNth TG.Month 1
    }
  
  ruleYesterday :: Rule
  ruleYesterday = Rule
    { name = "yesterday"
    , pattern =
      [ regex "(אתמול|אמש)"
      ]
    , prod = \_ -> tt . cycleNth TG.Day $ - 1
    }
  
  ruleAfterTimeofday :: Rule
  ruleAfterTimeofday = Rule
    { name = "after <time-of-day>"
    , pattern =
      [ regex "אחרי"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td:_) -> tt $ withDirection TTime.After td
        _ -> Nothing
    }
  
  ruleDayofmonthOrdinal :: Rule
  ruleDayofmonthOrdinal = Rule
    { name = "<day-of-month> (ordinal)"
    , pattern =
      [ Predicate isDOMOrdinal
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt . mkLatent $ dayOfMonth n
        _ -> Nothing
    }
  
  ruleTimeofdayAmpm :: Rule
  ruleTimeofdayAmpm = Rule
    { name = "<time-of-day> am|pm"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "(in the )?([ap])(\\s|\\.)?m?\\.?"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:Token RegexMatch (GroupMatch (_:ap:_)):_) ->
          tt $ timeOfDayAMPM (Text.toLower ap == "a") td
        _ -> Nothing
    }
  
  ruleOrdinalCycleAfterTime :: Rule
  ruleOrdinalCycleAfterTime = Rule
    { name = "<ordinal> <cycle> after <time>"
    , pattern =
      [ dimension Ordinal
      , dimension TimeGrain
      , regex "אחרי|לאחר"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Ordinal od:Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter True grain (TOrdinal.value od - 1) td
        _ -> Nothing
    }
  
  ruleNamedday7 :: Rule
  ruleNamedday7 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?שבת"
      ]
    , prod = \_ -> tt $ dayOfWeek 6
    }
  
  ruleHhmm :: Rule
  ruleHhmm = Rule
    { name = "hh:mm"
    , pattern =
      [ regex "((?:[01]?\\d)|(?:2[0-3]))[:.]([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          tt $ hourMinute True h m
        _ -> Nothing
    }
  
  ruleTimeOfPartofday :: Rule
  ruleTimeOfPartofday = Rule
    { name = "<time> of <part-of-day>"
    , pattern =
      [ dimension Time
      , regex "ב"
      , Predicate isAPartOfDay
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) -> Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleYear :: Rule
  ruleYear = Rule
    { name = "year"
    , pattern =
      [ Predicate $ isIntegerBetween 1000 2100
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt $ year n
        _ -> Nothing
    }
  
  ruleNamedmonthDayofmonthNonOrdinal :: Rule
  ruleNamedmonthDayofmonthNonOrdinal = Rule
    { name = "<named-month> <day-of-month> (non ordinal)"
    , pattern =
      [ Predicate isAMonth
      , Predicate isDOMInteger
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:token:_) -> Token Time <$> intersectDOM td token
        _ -> Nothing
    }
  
  ruleNamedday8 :: Rule
  ruleNamedday8 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?ראשון"
      ]
    , prod = \_ -> tt $ dayOfWeek 7
    }
  
  ruleAbsorptionOfAfterNamedDay :: Rule
  ruleAbsorptionOfAfterNamedDay = Rule
    { name = "absorption of , after named day"
    , pattern =
      [ Predicate isADayOfWeek
      , regex ","
      ]
    , prod = \tokens -> case tokens of
        (x:_) -> Just x
        _ -> Nothing
    }
  
  ruleLastDayofweekOfTime :: Rule
  ruleLastDayofweekOfTime = Rule
    { name = "last <day-of-week> of <time>"
    , pattern =
      [ Predicate isADayOfWeek
      , regex "האחרון של"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Time td1:_:Token Time td2:_) ->
          tt $ predLastOf td1 td2
        _ -> Nothing
    }
  
  ruleHhmmMilitaryAmpm :: Rule
  ruleHhmmMilitaryAmpm = Rule
    { name = "hhmm (military) am|pm"
    , pattern =
      [ regex "((?:1[012]|0?\\d))([0-5]\\d)"
      , regex "([ap])\\.?m?\\.?"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:_)):Token RegexMatch (GroupMatch (ap:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          tt . timeOfDayAMPM (Text.toLower ap == "a") $ hourMinute True h m
        _ -> Nothing
    }
  
  ruleCycleBeforeTime :: Rule
  ruleCycleBeforeTime = Rule
    { name = "<cycle> before <time>"
    , pattern =
      [ dimension TimeGrain
      , regex "לפני"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token TimeGrain grain:_:Token Time td:_) ->
          tt $ cycleNthAfter False grain (-1) td
        _ -> Nothing
    }
  
  ruleDurationAfterTime :: Rule
  ruleDurationAfterTime = Rule
    { name = "<duration> after <time>"
    , pattern =
      [ dimension Duration
      , regex "אחרי|לאחר"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_:Token Time td:_) ->
          tt $ durationAfter dd td
        _ -> Nothing
    }
  
  ruleEveningnight :: Rule
  ruleEveningnight = Rule
    { name = "evening|night"
    , pattern =
      [ regex "(ב)?ערב"
      ]
    , prod = \_ -> Token Time . partOfDay . mkLatent <$>
        interval TTime.Open (hour False 18) (hour False 0)
    }
  
  ruleOrdinalQuarter :: Rule
  ruleOrdinalQuarter = Rule
    { name = "<ordinal> quarter"
    , pattern =
      [ dimension Ordinal
      , Predicate $ isGrain TG.Quarter
      ]
    , prod = \tokens -> case tokens of
        (token:_) -> do
          n <- getIntValue token
          tt . cycleNthAfter True TG.Quarter (n - 1) $
            cycleNth TG.Year 0
        _ -> Nothing
    }
  
  ruleNamedday3 :: Rule
  ruleNamedday3 = Rule
    { name = "named-day"
    , pattern =
      [ regex "(יום )?שלישי"
      ]
    , prod = \_ -> tt $ dayOfWeek 2
    }
  
  ruleTheDayofmonthOrdinal :: Rule
  ruleTheDayofmonthOrdinal = Rule
    { name = "the <day-of-month> (ordinal)"
    , pattern =
      [ regex "ה"
      , Predicate isDOMOrdinal
      ]
    , prod = \tokens -> case tokens of
        (_:
         Token Ordinal OrdinalData{TOrdinal.value = v}:
         _) -> tt $ dayOfMonth v
        _ -> Nothing
    }
  
  ruleDurationBeforeTime :: Rule
  ruleDurationBeforeTime = Rule
    { name = "<duration> before <time>"
    , pattern =
      [ dimension Duration
      , regex "לפני"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (Token Duration dd:_:Token Time td:_) ->
          tt $ durationBefore dd td
        _ -> Nothing
    }
  
  rulePartofdayOfTime :: Rule
  rulePartofdayOfTime = Rule
    { name = "<part-of-day> of <time>"
    , pattern =
      [ regex "ב"
      , Predicate isAPartOfDay
      , regex "של"
      , dimension Time
      ]
    , prod = \tokens -> case tokens of
        (_:Token Time td1:_:Token Time td2:_) -> Token Time <$> intersect td1 td2
        _ -> Nothing
    }
  
  ruleMmddyyyy :: Rule
  ruleMmddyyyy = Rule
    { name = "mm/dd/yyyy"
    , pattern =
      [ regex "(0?[1-9]|1[0-2])[/-](3[01]|[12]\\d|0?[1-9])[-/](\\d{2,4})"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (mm:dd:yy:_)):_) -> do
          y <- parseInt yy
          m <- parseInt mm
          d <- parseInt dd
          tt $ yearMonthDay y m d
        _ -> Nothing
    }
  
  ruleTomorrow :: Rule
  ruleTomorrow = Rule
    { name = "tomorrow"
    , pattern =
      [ regex "(מחר|למחרת)"
      ]
    , prod = \_ -> tt $ cycleNth TG.Day 1
    }
  
  ruleTimeofdayOclock :: Rule
  ruleTimeofdayOclock = Rule
    { name = "<time-of-day> o'clock"
    , pattern =
      [ Predicate isATimeOfDay
      , regex "o.?clock"
      ]
    , prod = \tokens -> case tokens of
        (Token Time td:_) -> tt $ notLatent td
        _ -> Nothing
    }
  
  ruleDayofmonthordinalNamedmonthYear :: Rule
  ruleDayofmonthordinalNamedmonthYear = Rule
    { name = "<day-of-month>(ordinal) <named-month> year"
    , pattern =
      [ Predicate isDOMOrdinal
      , Predicate isAMonth
      , regex "(\\d{2,4})"
      ]
    , prod = \tokens -> case tokens of
        (token:Token Time td:Token RegexMatch (GroupMatch (match:_)):_) -> do
          intVal <- parseInt match
          dom <- intersectDOM td token
          Token Time <$> intersect dom (year intVal)
        _ -> Nothing
    }
  
  ruleHhmmss :: Rule
  ruleHhmmss = Rule
    { name = "hh:mm:ss"
    , pattern =
      [ regex "((?:[01]?\\d)|(?:2[0-3]))[:.]([0-5]\\d)[:.]([0-5]\\d)"
      ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (hh:mm:ss:_)):_) -> do
          h <- parseInt hh
          m <- parseInt mm
          s <- parseInt ss
          tt $ hourMinuteSecond True h m s
        _ -> Nothing
    }
  
  ruleMonths :: [Rule]
  ruleMonths = mkRuleMonths
    [ ( "ינואר", "ינואר" )
    , ( "פברואר", "פברואר" )
    , ( "מרץ", "מרץ" )
    , ( "אפריל", "אפריל" )
    , ( "מאי", "מאי" )
    , ( "יוני", "יוני" )
    , ( "יולי", "יולי" )
    , ( "אוגוסט", "אוגוסט" )
    , ( "ספטמבר", "ספטמבר" )
    , ( "אוקטובר", "אוקטובר" )
    , ( "נובמבר", "נובמבר" )
    , ( "דצמבר", "דצמבר" )
    ]
  
  rules :: [Rule]
  rules =
    [ ruleAbsorptionOfAfterNamedDay
    , ruleAfterDuration
    , ruleAfterTimeofday
    , ruleAfternoon
    , ruleAtHourTimeofday
    , ruleAtTimeofday
    , ruleBetweenDatetimeAndDatetimeInterval
    , ruleBetweenTimeofdayAndTimeofdayInterval
    , ruleCurrentDayofweek
    , ruleCycleAfterTime
    , ruleCycleBeforeTime
    , ruleDate
    , ruleDatetimeDatetimeInterval
    , ruleDayofmonthNonOrdinalNamedmonth
    , ruleDayofmonthNonOrdinalOfNamedmonth
    , ruleDayofmonthOrdinal
    , ruleDayofmonthOrdinalOfNamedmonth
    , ruleDayofmonthordinalNamedmonth
    , ruleDayofmonthordinalNamedmonthYear
    , ruleDurationAfterTime
    , ruleDurationAgo
    , ruleDurationBeforeTime
    , ruleDurationFromNow
    , ruleEndOfMonth
    , ruleEndOfYear
    , ruleEveningnight
    , ruleForDuration
    , ruleFromDatetimeDatetimeInterval
    , ruleHhmm
    , ruleHhmmMilitaryAmpm
    , ruleHhmmss
    , ruleHourofdayAndInteger
    , ruleHourofdayAndQuarter
    , ruleHourofdayAndHalf
    , ruleHourofdayInteger
    , ruleHourofdayQuarter
    , ruleHourofdayHalf
    , ruleInDuration
    , ruleInNamedmonth
    , ruleIntersect
    , ruleIntersectBy
    , ruleLastCycle
    , ruleLastDayofweek
    , ruleLastDayofweekOfTime
    , ruleLastNCycle
    , ruleLastTime
    , ruleLunch
    , ruleMidnighteodendOfDay
    , ruleMmdd
    , ruleMmddyyyy
    , ruleMorning
    , ruleNamedday
    , ruleNamedday2
    , ruleNamedday3
    , ruleNamedday4
    , ruleNamedday5
    , ruleNamedday6
    , ruleNamedday7
    , ruleNamedday8
    , ruleNameddayDayofmonthOrdinal
    , ruleNamedmonthDayofmonthNonOrdinal
    , ruleNamedmonthDayofmonthOrdinal
    , ruleNextCycle
    , ruleNextDayofweek
    , ruleNextNCycle
    , ruleNextTime
    , ruleNoon
    , ruleNow
    , ruleNthTimeAfterTime
    , ruleNthTimeOfTime
    , ruleOrdinalCycleAfterTime
    , ruleOrdinalQuarter
    , ruleOrdinalQuarterYear
    , rulePartofdayOfTime
    , ruleIntegerAfterpastIntegerHourofday
    , ruleQuarterAfterpastIntegerHourofday
    , ruleHalfAfterpastIntegerHourofday
    , ruleIntegerTotillbeforeIntegerHourofday
    , ruleQuarterTotillbeforeIntegerHourofday
    , ruleHalfTotillbeforeIntegerHourofday
    , ruleSinceTimeofday
    , ruleTheCycleAfterTime
    , ruleTheCycleBeforeTime
    , ruleTheDayofmonthNonOrdinal
    , ruleTheDayofmonthOrdinal
    , ruleTheIdesOfNamedmonth
    , ruleTheOrdinalCycleAfterTime
    , ruleThisCycle
    , ruleThisEvening
    , ruleThisTime
    , ruleTimeOfPartofday
    , ruleTimePartofday
    , ruleTimeofdayAmpm
    , ruleTimeofdayLatent
    , ruleTimeofdayOclock
    , ruleToday
    , ruleTomorrow
    , ruleUntilTimeofday
    , ruleWeekend
    , ruleYear
    , ruleYesterday
    , ruleYyyymmdd
    ]
    ++ ruleMonths
  