module Duckling.Numeral.ID.Rules
( rules ) where
import Data.Maybe
import qualified Data.Text as Text
import Prelude
import Data.String
import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import Duckling.Types
ruleTeen :: Rule
ruleTeen = Rule
{ name = "teen"
, pattern =
[ numberBetween 2 10
, regex "belas"
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = v}):_) -> double $ v + 10
_ -> Nothing
}
ruleNumeralCommaNumeral :: Rule
ruleNumeralCommaNumeral = Rule
{ name = "number comma number"
, pattern =
[ dimension Numeral
, regex "koma"
, numberWith TNumeral.grain isNothing
]
, prod = \tokens -> case tokens of
(Token Numeral nd1:_:Token Numeral nd2:_) ->
double $ TNumeral.value nd1 + decimalsToDouble (TNumeral.value nd2)
_ -> Nothing
}
ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
{ name = "numbers prefix with -, negative or minus"
, pattern =
[ regex "-|minus\\s?|negatif\\s?"
, dimension Numeral
]
, prod = \tokens -> case tokens of
(_:Token Numeral nd:_) -> double (TNumeral.value nd * (1))
_ -> Nothing
}
ruleIntegerNumeric :: Rule
ruleIntegerNumeric = Rule
{ name = "integer (numeric)"
, pattern =
[ regex "(\\d{1,18})"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> do
v <- toInteger <$> parseInt match
integer v
_ -> Nothing
}
ruleTen :: Rule
ruleTen = Rule
{ name = "ten"
, pattern =
[ regex "(se)?puluh"
]
, prod = \_ -> integer 10 >>= withGrain 1
}
ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
{ name = "decimal with thousands separator"
, pattern =
[ regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):
_) -> let dot = Text.singleton '.'
comma = Text.singleton ','
fmt = Text.replace comma dot $ Text.replace dot Text.empty match
in parseDouble fmt >>= double
_ -> Nothing
}
ruleMultiply :: Rule
ruleMultiply = Rule
{ name = "compose by multiplication"
, pattern =
[ dimension Numeral
, numberWith TNumeral.multipliable id
]
, prod = \tokens -> case tokens of
(token1:token2:_) -> multiply token1 token2
_ -> Nothing
}
ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
{ name = "decimal number"
, pattern =
[ regex "(\\d*,\\d+)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> parseDecimal False match
_ -> Nothing
}
ruleInteger3 :: Rule
ruleInteger3 = Rule
{ name = "integer 21..99"
, pattern =
[ oneOf [70, 20, 60, 50, 40, 90, 30, 80]
, numberBetween 1 10
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = v1}):
Token Numeral (NumeralData {TNumeral.value = v2}):
_) -> double $ v1 + v2
_ -> Nothing
}
ruleIntersect :: Rule
ruleIntersect = Rule
{ name = "intersect"
, pattern =
[ numberWith (fromMaybe 0 . TNumeral.grain) (>1)
, numberWith TNumeral.multipliable not
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = val1, TNumeral.grain = Just g}):
Token Numeral (NumeralData {TNumeral.value = val2}):
_) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
_ -> Nothing
}
ruleSomefewcouple :: Rule
ruleSomefewcouple = Rule
{ name = "some/few/couple"
, pattern =
[ regex "beberapa"
]
, prod = \_ -> integer 3
}
ruleNumeralsSuffixesKMG :: Rule
ruleNumeralsSuffixesKMG = Rule
{ name = "numbers suffixes (K, M, G)"
, pattern =
[ dimension Numeral
, regex "([kmg])(?=[\\W\\$\x20ac]|$)"
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = v}):
Token RegexMatch (GroupMatch (match:_)):
_) -> case Text.toLower match of
"k" -> double $ v * 1e3
"m" -> double $ v * 1e6
"g" -> double $ v * 1e9
_ -> Nothing
_ -> Nothing
}
rulePowersOfTen :: Rule
rulePowersOfTen = Rule
{ name = "powers of tens"
, pattern =
[ regex "(se)?(ratus|ribu|juta)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (_:match:_)):_) -> case Text.toLower match of
"ratus"-> double 1e2 >>= withGrain 2 >>= withMultipliable
"ribu" -> double 1e3 >>= withGrain 3 >>= withMultipliable
"juta" -> double 1e6 >>= withGrain 6 >>= withMultipliable
_ -> Nothing
_ -> Nothing
}
ruleDozen :: Rule
ruleDozen = Rule
{ name = "dozen"
, pattern =
[ regex "(se)?lusin"
]
, prod = \_ -> integer 12 >>= withGrain 1 >>= withMultipliable
}
ruleInteger :: Rule
ruleInteger = Rule
{ name = "integer (0..9 11)"
, pattern =
[ regex "(kosong|nol|satu|dua|tiga|empat|lima|enam|tujuh|delapan|sembilan|sebelas)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
"kosong" -> integer 0
"nol" -> integer 0
"satu" -> integer 1
"dua" -> integer 2
"tiga" -> integer 3
"empat" -> integer 4
"lima" -> integer 5
"enam" -> integer 6
"tujuh" -> integer 7
"delapan" -> integer 8
"sembilan" -> integer 9
"sebelas" -> integer 11
_ -> Nothing
_ -> Nothing
}
ruleInteger2 :: Rule
ruleInteger2 = Rule
{ name = "integer 20..90"
, pattern =
[ numberBetween 2 10
, numberWith TNumeral.value (== 10)
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = v1}):
Token Numeral (NumeralData {TNumeral.value = v2, TNumeral.grain = Just g}):
_) -> double (v1 * v2) >>= withGrain g
_ -> Nothing
}
ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
{ name = "integer with thousands separator ."
, pattern =
[ regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) ->
parseDouble (Text.replace (Text.singleton '.') Text.empty match) >>= double
_ -> Nothing
}
rules :: [Rule]
rules =
[ ruleDecimalNumeral
, ruleDecimalWithThousandsSeparator
, ruleDozen
, ruleInteger
, ruleInteger2
, ruleInteger3
, ruleIntegerNumeric
, ruleIntegerWithThousandsSeparator
, ruleIntersect
, ruleMultiply
, ruleNumeralCommaNumeral
, ruleNumeralsPrefixWithNegativeOrMinus
, ruleNumeralsSuffixesKMG
, rulePowersOfTen
, ruleSomefewcouple
, ruleTeen
, ruleTen
]