diff --git a/Duckling/AmountOfMoney/AR/Corpus.hs b/Duckling/AmountOfMoney/AR/Corpus.hs new file mode 100644 index 000000000..ecbf793b4 --- /dev/null +++ b/Duckling/AmountOfMoney/AR/Corpus.hs @@ -0,0 +1,125 @@ +-- 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. An additional grant +-- of patent rights can be found in the PATENTS file in the same directory. + + +{-# LANGUAGE OverloadedStrings #-} + +module Duckling.AmountOfMoney.AR.Corpus + ( corpus + ) where + +import Data.String +import Prelude + +import Duckling.AmountOfMoney.Types +import Duckling.Locale +import Duckling.Resolve +import Duckling.Testing.Types + +corpus :: Corpus +corpus = (testContext {locale = makeLocale AR Nothing}, allExamples) + +allExamples :: [Example] +allExamples = concat + [ examples (simple Dollar 1) + [ "$1" + , "1 دولار" + ] + , examples (simple Dollar 10) + [ "$10" + , "$ 10" + , "10 $" + , "10$" + , "10$" + , "10 دولار" + , "عشرة دولارات" + ] + , examples (simple Cent 10) + [ "10 سنتات" + , "10 سنت" + , "10 سينت" + , "10 سينتات" + , "10 c" + , "10¢" + ] + , examples (simple Dollar 1e4) + [ "10000 دولار" + , "$10,000" + ] + , examples (simple EUR 20) + [ "20 يورو" + , "20 اورو" + , "20 أورو" + , "20€" + , "20 €" + ] + , examples (simple Pound 10) + [ "10 جنيه" + , "10 جنيهات" + ] + , examples (simple Dollar 20.43) + [ "20$ و 43c" + , "$20 43" + , "20$ 43c" + , "20 دولار و43 سنت" + , "20.43 $" + ] + , examples (simple GBP 3) + [ "3 جنيهات استرلينية" + , "3 جنيه استرليني" + ] + , examples (simple KWD 42) + [ "42 KWD" + , "42 دينار كويتي" + ] + , examples (simple LBP 42) + [ "42 LBP" + , "42 ليرة لبنانية" + , "42 ليرات لبنانية" + ] + , examples (simple EGP 42) + [ "42 EGP" + , "42 جنيه مصري" + , "42 جنيهات مصريه" + ] + , examples (simple QAR 42) + [ "42 QAR" + , "42 ريال قطري" + , "42 ريالات قطرية" + ] + , examples (simple SAR 42) + [ "42 SAR" + , "42 ريال سعودي" + ] + , examples (between Dollar (10, 20)) + [ "من 10 الى 20 دولار" + , "بحدود 10-20 دولار" + , "ما بين عشرة وعشرون دولار" + , "من عشرة لعشرين دولار" + , "حوالي 10$-20$" + , "10-20 $" + , "10-20 دولار" + ] + , examples (under EUR 7) + [ "تحت سبعة اورو" + , "اقل من سبعة يورو" + , "اقل من 7 يورو" + ] + , examples (above Dollar 3.42) + [ "اكثر من ثلاثة دولار و42 سينت" + , "3.42$ على الاقل" + ] + , examples (simple JOD 5) + [ "5 دينار اردني" + , "5 دنانير أردنية" + ] + , examples (simple ILS 5) + [ "5 شيقل" + , "5 شواقل" + , "خمسة شيكل" + ] + ] diff --git a/Duckling/AmountOfMoney/AR/Rules.hs b/Duckling/AmountOfMoney/AR/Rules.hs new file mode 100644 index 000000000..3ff0eb5c6 --- /dev/null +++ b/Duckling/AmountOfMoney/AR/Rules.hs @@ -0,0 +1,398 @@ +-- 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. An additional grant +-- of patent rights can be found in the PATENTS file in the same directory. + + +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} + +module Duckling.AmountOfMoney.AR.Rules + ( rules ) where + +import Data.Maybe +import qualified Data.Text as Text +import Prelude +import Data.String + +import Duckling.AmountOfMoney.Helpers +import Duckling.AmountOfMoney.Types (Currency(..), AmountOfMoneyData (..)) +import qualified Duckling.AmountOfMoney.Types as TAmountOfMoney +import Duckling.Dimensions.Types +import Duckling.Numeral.Types (NumeralData (..)) +import qualified Duckling.Numeral.Types as TNumeral +import Duckling.Regex.Types +import Duckling.Types + +rulePounds :: Rule +rulePounds = Rule + { name = "£" + , pattern = + [ regex "جنيه(ات)?" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly Pound + } + +ruleDinars :: Rule +ruleDinars = Rule + { name = "dinars" + , pattern = + [ regex "دينار|دنانير" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly Dinar + } + +ruleDirhams :: Rule +ruleDirhams = Rule + { name = "Dirhams" + , pattern = + [ regex "درا?هم" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly Dirham + } + +ruleRiyals :: Rule +ruleRiyals = Rule + { name = "Riyals" + , pattern = + [ regex "ريال(ات)?" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly Riyal + } + +ruleDollars :: Rule +ruleDollars = Rule + { name = "Dollars" + , pattern = + [ regex "دولار(ات)?" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly Dollar + } + +ruleShekel :: Rule +ruleShekel = Rule + { name = "Shekel" + , pattern = + [ regex "شي[كق]ل|شوا[كق]ل" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly ILS + } + +ruleEuro :: Rule +ruleEuro = Rule + { name = "Euro" + , pattern = + [ regex "[أاي]ورو" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly EUR + } + +ruleOtherPounds :: Rule +ruleOtherPounds = Rule + { name = "other pounds" + , pattern = + [ regex "جنيه(ات)? (مصر|استرلين)ي?[ةه]?" + ] + , prod = \tokens -> case tokens of + (Token RegexMatch (GroupMatch (_:match:_)):_) -> case Text.toLower match of + "مصر" -> Just . Token AmountOfMoney $ currencyOnly EGP + "استرلين" -> Just . Token AmountOfMoney $ currencyOnly GBP + _ -> Nothing + _ -> Nothing + } + +ruleOtherDinars :: Rule +ruleOtherDinars = Rule + { name = "dinars" + , pattern = + [ regex "(دينار|دنانير) ([أا]ردن|كويت|عراق)ي?[ةه]?" + ] + , prod = \tokens -> case tokens of + (Token RegexMatch (GroupMatch (_:match:_)):_) -> case Text.toLower match of + "كويت" -> Just . Token AmountOfMoney $ currencyOnly KWD + "اردن" -> Just . Token AmountOfMoney $ currencyOnly JOD + "أردن" -> Just . Token AmountOfMoney $ currencyOnly JOD + "عراق" -> Just . Token AmountOfMoney $ currencyOnly IQD + _ -> Nothing + _ -> Nothing + } + +ruleOtherDirhams :: Rule +ruleOtherDirhams = Rule + { name = "dirham" + , pattern = + [ regex "(درهم|دراهم) (اردن|أردن|مغرب)ي?[ةه]?" + ] + , prod = \tokens -> case tokens of + (Token RegexMatch (GroupMatch (_:match:_)):_) -> case Text.toLower match of + "إمارات" -> Just . Token AmountOfMoney $ currencyOnly AED + "امارات" -> Just . Token AmountOfMoney $ currencyOnly AED + "مغرب" -> Just . Token AmountOfMoney $ currencyOnly MAD + _ -> Nothing + _ -> Nothing + } + +ruleOtherRiyals :: Rule +ruleOtherRiyals = Rule + { name = "riyals" + , pattern = + [ regex "(ريال|ريالات) (سعود|قطر)ي?[ةه]?" + ] + , prod = \tokens -> case tokens of + (Token RegexMatch (GroupMatch (_:match:_)):_) -> case Text.toLower match of + "قطر" -> Just . Token AmountOfMoney $ currencyOnly QAR + "سعود" -> Just . Token AmountOfMoney $ currencyOnly SAR + _ -> Nothing + _ -> Nothing + } + +ruleCent :: Rule +ruleCent = Rule + { name = "cent" + , pattern = + [ regex "سي?نت(ات)?" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly Cent + } + +ruleLBP :: Rule +ruleLBP = Rule + { name = "LBP" + , pattern = + [ regex "لير([ةه]|ات) لبناني[ةه]?" + ] + , prod = \_ -> Just . Token AmountOfMoney $ currencyOnly LBP + } + +ruleIntersectAndXCents :: Rule +ruleIntersectAndXCents = Rule + { name = "intersect (and X cents)" + , pattern = + [ financeWith TAmountOfMoney.value isJust + , regex "و" + , financeWith TAmountOfMoney.currency (== Cent) + ] + , prod = \tokens -> case tokens of + (Token AmountOfMoney fd: + _: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just c}): + _) -> Just . Token AmountOfMoney $ withCents c fd + _ -> Nothing + } + +ruleIntersect :: Rule +ruleIntersect = Rule + { name = "intersect" + , pattern = + [ financeWith TAmountOfMoney.value isJust + , dimension Numeral + ] + , prod = \tokens -> case tokens of + (Token AmountOfMoney fd: + Token Numeral (NumeralData {TNumeral.value = c}): + _) -> Just . Token AmountOfMoney $ withCents c fd + _ -> Nothing + } + +ruleIntersectAndNumeral :: Rule +ruleIntersectAndNumeral = Rule + { name = "intersect (and number)" + , pattern = + [ financeWith TAmountOfMoney.value isJust + , regex "و" + , dimension Numeral + ] + , prod = \tokens -> case tokens of + (Token AmountOfMoney fd: + _: + Token Numeral (NumeralData {TNumeral.value = c}): + _) -> Just . Token AmountOfMoney $ withCents c fd + _ -> Nothing + } + +ruleIntersectXCents :: Rule +ruleIntersectXCents = Rule + { name = "intersect (X cents)" + , pattern = + [ financeWith TAmountOfMoney.value isJust + , financeWith id $ \x -> case TAmountOfMoney.value x of + Just v | v > 0 -> TAmountOfMoney.currency x == Cent + _ -> False + ] + , prod = \tokens -> case tokens of + (Token AmountOfMoney fd: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just c}): + _) -> Just . Token AmountOfMoney $ withCents c fd + _ -> Nothing + } + +rulePrecision :: Rule +rulePrecision = Rule + { name = "about|exactly " + , pattern = + [ regex "حوال[ي|ى]|تقريبا|بحدود" + , dimension AmountOfMoney + ] + , prod = \tokens -> case tokens of + (_:token:_) -> Just token + _ -> Nothing + } + +ruleIntervalBetweenNumeral :: Rule +ruleIntervalBetweenNumeral = Rule + { name = "between|from to|and " + , pattern = + [ regex "(من|(ما )?بين)( ال)?" + , dimension Numeral + , regex "(الى|حتى|و|ل)( ا?ل)?" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (_: + Token Numeral (NumeralData {TNumeral.value = from}): + _: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c}): + _) -> + Just . Token AmountOfMoney . withInterval (from, to) $ currencyOnly c + _ -> Nothing + } + +ruleIntervalBetween :: Rule +ruleIntervalBetween = Rule + { name = "between|from to|and " + , pattern = + [ regex "(من|(ما )?بين)( ال)?" + , financeWith TAmountOfMoney.value isJust + , regex "(الى|حتى|و|ل)( ا?ل)?" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (_: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just from, TAmountOfMoney.currency = c1}): + _: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c2}): + _) | c1 == c2 -> + Just . Token AmountOfMoney . withInterval (from, to) $ currencyOnly c1 + _ -> Nothing + } + +ruleIntervalNumeralDash :: Rule +ruleIntervalNumeralDash = Rule + { name = " - " + , pattern = + [ dimension Numeral + , regex "-" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (Token Numeral (NumeralData {TNumeral.value = from}): + _: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c}): + _) -> + Just . Token AmountOfMoney . withInterval (from, to) $ currencyOnly c + _ -> Nothing + } + +ruleIntervalDash :: Rule +ruleIntervalDash = Rule + { name = " - " + , pattern = + [ financeWith TAmountOfMoney.value isJust + , regex "-" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just from, TAmountOfMoney.currency = c1}): + _: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c2}): + _) | c1 == c2 -> + Just . Token AmountOfMoney . withInterval (from, to) $ currencyOnly c1 + _ -> Nothing + } + +ruleIntervalMax :: Rule +ruleIntervalMax = Rule + { name = "under/less/lower/no more than " + , pattern = + [ regex "(حتى|[أا]قل من|تحت|(ما )?دون)|على ال[أا]كثر" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (_: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c}): + _) -> Just . Token AmountOfMoney . withMax to $ currencyOnly c + _ -> Nothing + } + +ruleIntervalAtMost :: Rule +ruleIntervalAtMost = Rule + { name = "under/less/lower/no more than " + , pattern = + [ regex "على ال[أا]كثر" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (_: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c}): + _) -> Just . Token AmountOfMoney . withMax to $ currencyOnly c + _ -> Nothing + } + +ruleIntervalMin :: Rule +ruleIntervalMin = Rule + { name = "over/above/more than " + , pattern = + [ regex "فوق||[أا]كثر من|على ال[اأ]قل" + , financeWith TAmountOfMoney.value isJust + ] + , prod = \tokens -> case tokens of + (_: + Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c}): + _) -> Just . Token AmountOfMoney . withMin to $ currencyOnly c + _ -> Nothing + } + +ruleIntervalAtLeast :: Rule +ruleIntervalAtLeast = Rule + { name = " at least" + , pattern = + [ financeWith TAmountOfMoney.value isJust + , regex "على ال[اأ]قل" + ] + , prod = \tokens -> case tokens of + (Token AmountOfMoney (AmountOfMoneyData {TAmountOfMoney.value = Just to, TAmountOfMoney.currency = c}): + _) -> Just . Token AmountOfMoney . withMin to $ currencyOnly c + _ -> Nothing + } + +rules :: [Rule] +rules = + [ ruleCent + , rulePounds + , ruleDinars + , ruleDirhams + , ruleRiyals + , ruleDollars + , ruleShekel + , ruleEuro + , ruleOtherPounds + , ruleOtherDinars + , ruleOtherDirhams + , ruleOtherRiyals + , ruleLBP + , ruleIntersect + , ruleIntersectAndNumeral + , ruleIntersectAndXCents + , ruleIntersectXCents + , ruleIntervalBetweenNumeral + , ruleIntervalBetween + , ruleIntervalMax + , ruleIntervalAtMost + , ruleIntervalMin + , ruleIntervalAtLeast + , ruleIntervalNumeralDash + , ruleIntervalDash + , rulePrecision + ] diff --git a/Duckling/AmountOfMoney/Rules.hs b/Duckling/AmountOfMoney/Rules.hs index 0df8a79a0..f419b8438 100644 --- a/Duckling/AmountOfMoney/Rules.hs +++ b/Duckling/AmountOfMoney/Rules.hs @@ -55,17 +55,21 @@ currencies = HashMap.fromList , ("gbp", GBP) , ("hrk", HRK) , ("idr", IDR) + , ("ils", ILS) , ("inr", INR) + , ("iqd", IQD) , ("rs", INR) , ("rs.", INR) , ("rupee", INR) , ("rupees", INR) + , ("jod", JOD) , ("¥", JPY) , ("jpy", JPY) , ("yen", JPY) , ("krw", KRW) , ("kwd", KWD) , ("lbp", LBP) + , ("mad", MAD) , ("myr", MYR) , ("rm", MYR) , ("nok", NOK) @@ -90,7 +94,7 @@ ruleCurrencies :: Rule ruleCurrencies = Rule { name = "currencies" , pattern = - [ regex "(aed|aud|bgn|brl|byn|¢|c|\\$|dollars?|egp|(e|€)uro?s?|€|gbp|hrk|idr|inr|¥|jpy|krw|kwd|lbp|myr|rm|nok|£|pta?s?|qar|₽|rs\\.?|ron|rub|rupees?|sar|sek|sgb|us(d|\\$)|vnd|yen)" + [ regex "(aed|aud|bgn|brl|byn|¢|c|\\$|dollars?|egp|(e|€)uro?s?|€|gbp|hrk|idr|ils|inr|iqd|jod|¥|jpy|krw|kwd|lbp|mad|myr|rm|nok|£|pta?s?|qar|₽|rs\\.?|ron|rub|rupees?|sar|sek|sgb|us(d|\\$)|vnd|yen)" ] , prod = \tokens -> case tokens of (Token RegexMatch (GroupMatch (match:_)):_) -> do diff --git a/Duckling/AmountOfMoney/Types.hs b/Duckling/AmountOfMoney/Types.hs index 668ae453a..118023cc4 100644 --- a/Duckling/AmountOfMoney/Types.hs +++ b/Duckling/AmountOfMoney/Types.hs @@ -30,6 +30,9 @@ data Currency = Cent | Dollar | Pound + | Dinar + | Dirham + | Riyal | Unnamed -- e.g. bucks -- unambiguous | AED @@ -42,11 +45,15 @@ data Currency | GBP | HRK | IDR + | ILS | INR + | IQD + | JOD | JPY | KRW | KWD | LBP + | MAD | MYR | NOK | PTS @@ -64,6 +71,9 @@ instance ToJSON Currency where toJSON Cent = "cent" toJSON Dollar = "$" toJSON Pound = "\x00a3" + toJSON Dinar = "dinar" + toJSON Dirham = "dirham" + toJSON Riyal = "Riyal" toJSON Unnamed = "unknown" toJSON AED = "AED" toJSON AUD = "AUD" @@ -75,11 +85,15 @@ instance ToJSON Currency where toJSON GBP = "GBP" toJSON HRK = "HRK" toJSON IDR = "IDR" + toJSON ILS = "ILS" + toJSON IQD = "IQD" toJSON INR = "INR" + toJSON JOD = "JOD" toJSON JPY = "JPY" toJSON KRW = "KRW" toJSON KWD = "KWD" toJSON LBP = "LBP" + toJSON MAD = "MAD" toJSON MYR = "MYR" toJSON NOK = "NOK" toJSON PTS = "PTS" diff --git a/Duckling/Rules/AR.hs b/Duckling/Rules/AR.hs index cc0718b9b..d3c8810ba 100644 --- a/Duckling/Rules/AR.hs +++ b/Duckling/Rules/AR.hs @@ -18,6 +18,7 @@ module Duckling.Rules.AR import Duckling.Dimensions.Types import Duckling.Locale import Duckling.Types +import qualified Duckling.AmountOfMoney.AR.Rules as AmountOfMoney import qualified Duckling.Duration.AR.Rules as Duration import qualified Duckling.Numeral.AR.Rules as Numeral import qualified Duckling.Ordinal.AR.Rules as Ordinal @@ -31,7 +32,7 @@ localeRules :: Region -> Some Dimension -> [Rule] localeRules _ _ = [] langRules :: Some Dimension -> [Rule] -langRules (This AmountOfMoney) = [] +langRules (This AmountOfMoney) = AmountOfMoney.rules langRules (This Distance) = [] langRules (This Duration) = Duration.rules langRules (This Email) = [] diff --git a/duckling.cabal b/duckling.cabal index 5bbc80a82..23e534f8b 100644 --- a/duckling.cabal +++ b/duckling.cabal @@ -158,6 +158,8 @@ library , Duckling.Dimensions.ZH -- AmountOfMoney + , Duckling.AmountOfMoney.AR.Corpus + , Duckling.AmountOfMoney.AR.Rules , Duckling.AmountOfMoney.EN.Corpus , Duckling.AmountOfMoney.EN.Rules , Duckling.AmountOfMoney.BG.Corpus @@ -640,6 +642,7 @@ test-suite duckling-test , Duckling.Dimensions.Tests -- AmountOfMoney + , Duckling.AmountOfMoney.AR.Tests , Duckling.AmountOfMoney.EN.Tests , Duckling.AmountOfMoney.BG.Tests , Duckling.AmountOfMoney.ES.Tests diff --git a/tests/Duckling/AmountOfMoney/AR/Tests.hs b/tests/Duckling/AmountOfMoney/AR/Tests.hs new file mode 100644 index 000000000..857fd97f1 --- /dev/null +++ b/tests/Duckling/AmountOfMoney/AR/Tests.hs @@ -0,0 +1,23 @@ +-- 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. An additional grant +-- of patent rights can be found in the PATENTS file in the same directory. + + +module Duckling.AmountOfMoney.AR.Tests + ( tests ) where + +import Data.String +import Prelude +import Test.Tasty + +import Duckling.AmountOfMoney.AR.Corpus +import Duckling.Dimensions.Types +import Duckling.Testing.Asserts + +tests :: TestTree +tests = testGroup "AR Tests" + [ makeCorpusTest [This AmountOfMoney] corpus + ] diff --git a/tests/Duckling/AmountOfMoney/Tests.hs b/tests/Duckling/AmountOfMoney/Tests.hs index 505abe66a..383ae0511 100644 --- a/tests/Duckling/AmountOfMoney/Tests.hs +++ b/tests/Duckling/AmountOfMoney/Tests.hs @@ -12,6 +12,7 @@ import Data.String import Prelude import Test.Tasty +import qualified Duckling.AmountOfMoney.AR.Tests as AR import qualified Duckling.AmountOfMoney.EN.Tests as EN import qualified Duckling.AmountOfMoney.BG.Tests as BG import qualified Duckling.AmountOfMoney.ES.Tests as ES @@ -29,7 +30,8 @@ import qualified Duckling.AmountOfMoney.VI.Tests as VI tests :: TestTree tests = testGroup "AmountOfMoney Tests" - [ EN.tests + [ AR.tests + , EN.tests , BG.tests , ES.tests , FR.tests