Skip to content

Commit

Permalink
Support custom dimensions
Browse files Browse the repository at this point in the history
Summary:
Support custom dimensions

Had to move the definition of `Dimension` from `Duckling.Dimensions.Types` to `Duckling.Types` to avoid cyclic imports between these two modules.

A sample custom dimension is in `exe/CustomDimensionExample.hs`.

Limitations of custom dimensions:

- All rules for a custom dimension must be in the same module with the definition of the custom dimension. Otherwise there will be cyclic imports, because the definition of the dimension and the rules refer to each other.
- The custom dimension must be specified when using `parse`, since there's no way to get all the existing custom dimensions.

Reviewed By: patapizza

Differential Revision: D7630360

fbshipit-source-id: 30e12dcb33611f5692c4f5949de377bf61b75e1e
  • Loading branch information
Ziyang Liu authored and facebook-github-bot committed Apr 19, 2018
1 parent eccf5c8 commit 5460d8d
Show file tree
Hide file tree
Showing 50 changed files with 312 additions and 141 deletions.
2 changes: 0 additions & 2 deletions Duckling/AmountOfMoney/BG/Rules.hs
Expand Up @@ -17,14 +17,12 @@ module Duckling.AmountOfMoney.BG.Rules
import Data.Maybe
import Data.String
import Prelude
import qualified Data.Text as Text

import Duckling.AmountOfMoney.Helpers
import Duckling.AmountOfMoney.Types (Currency(..), AmountOfMoneyData (..))
import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers (isNatural, isPositive)
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.AmountOfMoney.Types as TAmountOfMoney
import qualified Duckling.Numeral.Types as TNumeral
Expand Down
1 change: 0 additions & 1 deletion Duckling/Api.hs
Expand Up @@ -21,7 +21,6 @@ import Data.HashMap.Strict (HashMap)
import Data.HashSet (HashSet)
import Data.Text (Text)
import Prelude
import TextShow
import qualified Data.HashMap.Strict as HashMap
import qualified Data.HashSet as HashSet
import qualified Data.Text as Text
Expand Down
1 change: 0 additions & 1 deletion Duckling/Core.hs
Expand Up @@ -37,7 +37,6 @@ module Duckling.Core
) where

import Data.HashMap.Strict (HashMap)
import Data.Maybe
import Data.Text (Text)
import Data.Time
import Data.Time.LocalTime.TimeZone.Series
Expand Down
4 changes: 3 additions & 1 deletion Duckling/Dimensions.hs
Expand Up @@ -19,6 +19,8 @@ import Prelude
import qualified Data.HashSet as HashSet

import Duckling.Dimensions.Types
import Duckling.Locale
import Duckling.Types
import qualified Duckling.Dimensions.Common as CommonDimensions
import qualified Duckling.Dimensions.AR as ARDimensions
import qualified Duckling.Dimensions.BG as BGDimensions
Expand Down Expand Up @@ -54,7 +56,6 @@ import qualified Duckling.Dimensions.TR as TRDimensions
import qualified Duckling.Dimensions.UK as UKDimensions
import qualified Duckling.Dimensions.VI as VIDimensions
import qualified Duckling.Dimensions.ZH as ZHDimensions
import Duckling.Locale

allDimensions :: Lang -> [Some Dimension]
allDimensions lang = CommonDimensions.allDimensions ++ langDimensions lang
Expand Down Expand Up @@ -82,6 +83,7 @@ dependents (This Time) =
dependents (This TimeGrain) = HashSet.empty
dependents (This Url) = HashSet.empty
dependents (This Volume) = HashSet.singleton (This Numeral)
dependents (This (CustomDimension dim)) = dimDependents dim

langDimensions :: Lang -> [Some Dimension]
langDimensions AR = ARDimensions.allDimensions
Expand Down
126 changes: 4 additions & 122 deletions Duckling/Dimensions/Types.hs
Expand Up @@ -6,12 +6,8 @@
-- of patent rights can be found in the PATENTS file in the same directory.



{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE TypeOperators #-}


module Duckling.Dimensions.Types
Expand All @@ -22,99 +18,14 @@ module Duckling.Dimensions.Types
, toName
) where

import Data.GADT.Compare
import Data.GADT.Show
import Data.Hashable
import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
import Data.Some
import Data.Text (Text)
-- Intentionally limit use of Typeable to avoid casting or typeOf usage
import Data.Typeable ((:~:)(..))
import TextShow (TextShow(..))
import qualified TextShow as TS
import Prelude
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

import Duckling.AmountOfMoney.Types (AmountOfMoneyData)
import Duckling.Distance.Types (DistanceData)
import Duckling.Duration.Types (DurationData)
import Duckling.Email.Types (EmailData)
import Duckling.Numeral.Types (NumeralData)
import Duckling.Ordinal.Types (OrdinalData)
import Duckling.PhoneNumber.Types (PhoneNumberData)
import Duckling.Quantity.Types (QuantityData)
import Duckling.Regex.Types (GroupMatch)
import Duckling.Temperature.Types (TemperatureData)
import Duckling.Time.Types (TimeData)
import Duckling.TimeGrain.Types (Grain)
import Duckling.Url.Types (UrlData)
import Duckling.Volume.Types (VolumeData)

-- -----------------------------------------------------------------
-- Dimension

-- | GADT for differentiating between dimensions
-- Each dimension should have its own constructor and provide the data structure
-- for its parsed data
data Dimension a where
RegexMatch :: Dimension GroupMatch
AmountOfMoney :: Dimension AmountOfMoneyData
Distance :: Dimension DistanceData
Duration :: Dimension DurationData
Email :: Dimension EmailData
Numeral :: Dimension NumeralData
Ordinal :: Dimension OrdinalData
PhoneNumber :: Dimension PhoneNumberData
Quantity :: Dimension QuantityData
Temperature :: Dimension TemperatureData
Time :: Dimension TimeData
TimeGrain :: Dimension Grain
Url :: Dimension UrlData
Volume :: Dimension VolumeData

-- Show
instance Show (Dimension a) where
show RegexMatch = "RegexMatch"
show Distance = "Distance"
show Duration = "Duration"
show Email = "Email"
show AmountOfMoney = "AmountOfMoney"
show Numeral = "Numeral"
show Ordinal = "Ordinal"
show PhoneNumber = "PhoneNumber"
show Quantity = "Quantity"
show Temperature = "Temperature"
show Time = "Time"
show TimeGrain = "TimeGrain"
show Url = "Url"
show Volume = "Volume"
instance GShow Dimension where gshowsPrec = showsPrec

-- TextShow
instance TextShow (Dimension a) where
showb d = TS.fromString $ show d
instance TextShow (Some Dimension) where
showb (This d) = showb d

-- Hashable
instance Hashable (Some Dimension) where
hashWithSalt s (This a) = hashWithSalt s a
instance Hashable (Dimension a) where
hashWithSalt s RegexMatch = hashWithSalt s (0::Int)
hashWithSalt s Distance = hashWithSalt s (1::Int)
hashWithSalt s Duration = hashWithSalt s (2::Int)
hashWithSalt s Email = hashWithSalt s (3::Int)
hashWithSalt s AmountOfMoney = hashWithSalt s (4::Int)
hashWithSalt s Numeral = hashWithSalt s (5::Int)
hashWithSalt s Ordinal = hashWithSalt s (6::Int)
hashWithSalt s PhoneNumber = hashWithSalt s (7::Int)
hashWithSalt s Quantity = hashWithSalt s (8::Int)
hashWithSalt s Temperature = hashWithSalt s (9::Int)
hashWithSalt s Time = hashWithSalt s (10::Int)
hashWithSalt s TimeGrain = hashWithSalt s (11::Int)
hashWithSalt s Url = hashWithSalt s (12::Int)
hashWithSalt s Volume = hashWithSalt s (13::Int)

import Duckling.Types

toName :: Dimension a -> Text
toName RegexMatch = "regex"
Expand All @@ -131,6 +42,7 @@ toName Time = "time"
toName TimeGrain = "time-grain"
toName Url = "url"
toName Volume = "volume"
toName (CustomDimension dim) = Text.pack (show dim)

fromName :: Text -> Maybe (Some Dimension)
fromName name = HashMap.lookup name m
Expand All @@ -149,33 +61,3 @@ fromName name = HashMap.lookup name m
, ("url", This Url)
, ("volume", This Volume)
]

instance GEq Dimension where
geq RegexMatch RegexMatch = Just Refl
geq RegexMatch _ = Nothing
geq Distance Distance = Just Refl
geq Distance _ = Nothing
geq Duration Duration = Just Refl
geq Duration _ = Nothing
geq Email Email = Just Refl
geq Email _ = Nothing
geq AmountOfMoney AmountOfMoney = Just Refl
geq AmountOfMoney _ = Nothing
geq Numeral Numeral = Just Refl
geq Numeral _ = Nothing
geq Ordinal Ordinal = Just Refl
geq Ordinal _ = Nothing
geq PhoneNumber PhoneNumber = Just Refl
geq PhoneNumber _ = Nothing
geq Quantity Quantity = Just Refl
geq Quantity _ = Nothing
geq Temperature Temperature = Just Refl
geq Temperature _ = Nothing
geq Time Time = Just Refl
geq Time _ = Nothing
geq TimeGrain TimeGrain = Just Refl
geq TimeGrain _ = Nothing
geq Url Url = Just Refl
geq Url _ = Nothing
geq Volume Volume = Just Refl
geq Volume _ = Nothing
1 change: 0 additions & 1 deletion Duckling/Numeral/BG/Rules.hs
Expand Up @@ -16,7 +16,6 @@ module Duckling.Numeral.BG.Rules

import Data.HashMap.Strict (HashMap)
import Data.Maybe
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.HashMap.Strict as HashMap
Expand Down
1 change: 0 additions & 1 deletion Duckling/Ordinal/AR/Rules.hs
Expand Up @@ -15,7 +15,6 @@ module Duckling.Ordinal.AR.Rules
) where

import Data.HashMap.Strict (HashMap)
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.Text as Text
Expand Down
1 change: 0 additions & 1 deletion Duckling/Ordinal/BG/Rules.hs
Expand Up @@ -15,7 +15,6 @@ module Duckling.Ordinal.BG.Rules
) where

import Data.HashMap.Strict (HashMap)
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.HashMap.Strict as HashMap
Expand Down
2 changes: 2 additions & 0 deletions Duckling/Rules/AR.hs
Expand Up @@ -32,6 +32,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -49,3 +50,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = Volume.rules
langRules (This (CustomDimension dim)) = dimLangRules AR dim
2 changes: 2 additions & 0 deletions Duckling/Rules/BG.hs
Expand Up @@ -29,6 +29,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -46,3 +47,4 @@ langRules (This Time) = []
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = []
langRules (This (CustomDimension dim)) = dimLangRules BG dim
2 changes: 2 additions & 0 deletions Duckling/Rules/CS.hs
Expand Up @@ -25,6 +25,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -42,3 +43,4 @@ langRules (This Time) = []
langRules (This TimeGrain) = []
langRules (This Url) = []
langRules (This Volume) = []
langRules (This (CustomDimension dim)) = dimLangRules CS dim
1 change: 1 addition & 0 deletions Duckling/Rules/Common.hs
Expand Up @@ -40,3 +40,4 @@ rules (This Time) = []
rules (This TimeGrain) = []
rules (This Url) = Url.rules
rules (This Volume) = Volume.rules
rules (This (CustomDimension dim)) = dimRules dim
2 changes: 2 additions & 0 deletions Duckling/Rules/DA.hs
Expand Up @@ -28,6 +28,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -45,3 +46,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = []
langRules (This (CustomDimension dim)) = dimLangRules DA dim
2 changes: 2 additions & 0 deletions Duckling/Rules/DE.hs
Expand Up @@ -28,6 +28,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -45,3 +46,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = []
langRules (This (CustomDimension dim)) = dimLangRules DE dim
2 changes: 2 additions & 0 deletions Duckling/Rules/EL.hs
Expand Up @@ -28,6 +28,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -45,3 +46,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = []
langRules (This (CustomDimension dim)) = dimLangRules EL dim
4 changes: 3 additions & 1 deletion Duckling/Rules/EN.hs
Expand Up @@ -61,7 +61,8 @@ localeRules PH (This Time) = TimePH.rules
localeRules TT (This Time) = TimeTT.rules
localeRules US (This Time) = TimeUS.rules
localeRules ZA (This Time) = TimeZA.rules
localeRules _ _ = []
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
langRules (This AmountOfMoney) = AmountOfMoney.rules
Expand All @@ -78,3 +79,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = Volume.rules
langRules (This (CustomDimension dim)) = dimLangRules EN dim
2 changes: 2 additions & 0 deletions Duckling/Rules/ES.hs
Expand Up @@ -31,6 +31,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -48,3 +49,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = Volume.rules
langRules (This (CustomDimension dim)) = dimLangRules ES dim
2 changes: 2 additions & 0 deletions Duckling/Rules/ET.hs
Expand Up @@ -25,6 +25,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -42,3 +43,4 @@ langRules (This Time) = []
langRules (This TimeGrain) = []
langRules (This Url) = []
langRules (This Volume) = []
langRules (This (CustomDimension dim)) = dimLangRules ET dim
2 changes: 2 additions & 0 deletions Duckling/Rules/FR.hs
Expand Up @@ -34,6 +34,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -51,3 +52,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = Volume.rules
langRules (This (CustomDimension dim)) = dimLangRules FR dim
2 changes: 2 additions & 0 deletions Duckling/Rules/GA.hs
Expand Up @@ -32,6 +32,7 @@ defaultRules :: Some Dimension -> [Rule]
defaultRules = langRules

localeRules :: Region -> Some Dimension -> [Rule]
localeRules region (This (CustomDimension dim)) = dimLocaleRules region dim
localeRules _ _ = []

langRules :: Some Dimension -> [Rule]
Expand All @@ -49,3 +50,4 @@ langRules (This Time) = Time.rules
langRules (This TimeGrain) = TimeGrain.rules
langRules (This Url) = []
langRules (This Volume) = Volume.rules
langRules (This (CustomDimension dim)) = dimLangRules GA dim

0 comments on commit 5460d8d

Please sign in to comment.