packages feed

country 0.1.2 → 0.1.3

raw patch · 5 files changed

+168/−20 lines, 5 filesdep +attoparsecPVP ok

version bump matches the API change (PVP)

Dependencies added: attoparsec

API changes (from Hackage documentation)

+ Country: parser :: Parser Country
+ Country: parserUtf8 :: Parser Country

Files

country.cabal view
@@ -1,5 +1,5 @@ name: country-version: 0.1.2+version: 0.1.3 synopsis: Country data type and functions description:   The `country` library provides a data type for dealing with@@ -38,6 +38,8 @@     Country.Unexposed.Enumerate     Country.Unexposed.ExtraNames     Country.Unexposed.Names+    Country.Unexposed.Trie+    Country.Unexposed.TrieByte   build-depends:       base >= 4.7 && < 4.10     , text >= 1.2 && < 1.3@@ -48,6 +50,7 @@     , hashable >= 1.2 && < 1.3     , aeson >= 0.11 && < 1.3     , scientific >= 0.3 && < 0.4+    , attoparsec >= 0.13 && < 0.14   default-language: Haskell2010  test-suite test
src/Country.hs view
@@ -11,6 +11,8 @@     -- * Name   , encodeEnglish   , decode+  , parser+  , parserUtf8     -- * Alpha-2 and Alpha-3   , alphaTwoUpper   , alphaThreeUpper@@ -22,14 +24,12 @@  import Country.Unsafe (Country(..)) import Country.Unexposed.Encode.English (countryNameQuads)-import Country.Unexposed.Names (englishCountryNamesText,numberOfPossibleCodes,alphaTwoHashMap,alphaThreeHashMap,decodeMap,decodeNumeric,encodeEnglish)-import Country.Unexposed.Enumerate (enumeratedCountries)+import Country.Unexposed.Names (numberOfPossibleCodes,alphaTwoHashMap,alphaThreeHashMap,decodeMap,decodeNumeric,encodeEnglish)+import Country.Unexposed.Trie (Trie,trieFromList,trieParser)+import Country.Unexposed.TrieByte (TrieByte,trieByteFromList,trieByteParser) import Data.Text (Text)-import Data.ByteString (ByteString)-import Data.Word (Word16,Word8)-import Data.Primitive (indexArray,newArray,unsafeFreezeArray,writeArray,-  writeByteArray,indexByteArray,unsafeFreezeByteArray,newByteArray)-import Data.HashMap.Strict (HashMap)+import Data.Word (Word16)+import Data.Primitive (indexArray,writeByteArray,indexByteArray,unsafeFreezeByteArray,newByteArray) import Data.Primitive.Array (Array(..)) import Data.Primitive.ByteArray (ByteArray(..)) import GHC.Prim (sizeofByteArray#,sizeofArray#)@@ -38,10 +38,13 @@ import Control.Monad import Data.Char (ord,chr,toLower) import Data.Bits (unsafeShiftL,unsafeShiftR)-import qualified Data.List as L+import Data.Coerce (coerce) import qualified Data.HashMap.Strict as HM import qualified Data.Text.Array as TA+import qualified Data.Text.Encoding as TE import qualified Data.Text.Internal as TI+import qualified Data.Attoparsec.Text as AT+import qualified Data.Attoparsec.ByteString as AB  -- | Convert a country to its numeric code. This is a --   three-digit number and will consequently be less than 1000.@@ -86,27 +89,35 @@ decode :: Text -> Maybe Country decode = flip HM.lookup decodeMap +-- | Parse a country from its name using an attoparsec text parser. This+--   function is language-agnostic and can handle any source language.+--   In the case that one possible country name is a prefix of another+--   possible name (for example, United States vs United States of America),+--   the longest possible will be parsed.+parser :: AT.Parser Country+parser = coerce (trieParser decodeTrie)++parserUtf8 :: AB.Parser Country+parserUtf8 = coerce (trieByteParser decodeTrieUtf8)+ word16ToInt :: Word16 -> Int word16ToInt = fromIntegral -intToWord16 :: Int -> Word16-intToWord16 = fromIntegral- charToWord16 :: Char -> Word16 charToWord16 = fromIntegral . ord  word16ToChar :: Word16 -> Char word16ToChar = chr . fromIntegral -arrayFoldl' :: (a -> b -> a) -> a -> Array b -> a-arrayFoldl' f z a = go 0 z-  where-  go i !acc | i < sizeofArray a = go (i+1) (f acc $ indexArray a i)-            | otherwise         = acc+-- arrayFoldl' :: (a -> b -> a) -> a -> Array b -> a+-- arrayFoldl' f z a = go 0 z+--   where+--   go i !acc | i < sizeofArray a = go (i+1) (f acc $ indexArray a i)+--             | otherwise         = acc -sizeofArray :: Array a -> Int-sizeofArray (Array a) = I# (sizeofArray# a)-{-# INLINE sizeofArray #-}+-- sizeofArray :: Array a -> Int+-- sizeofArray (Array a) = I# (sizeofArray# a)+-- {-# INLINE sizeofArray #-}  numberOfCountries :: Int numberOfCountries = length countryNameQuads@@ -168,5 +179,13 @@         else return ()   go 0   return m++decodeTrie :: Trie+decodeTrie = trieFromList (map (\(a,Country x) -> (a,x)) (HM.toList decodeMap))+{-# NOINLINE decodeTrie #-}++decodeTrieUtf8 :: TrieByte+decodeTrieUtf8 = trieByteFromList (map (\(a,Country x) -> (TE.encodeUtf8 a,x)) (HM.toList decodeMap))+{-# NOINLINE decodeTrieUtf8 #-}  
src/Country/Unexposed/Names.hs view
@@ -71,6 +71,7 @@   , (mexico,"México")   , (mexico,"Méjico")   , (ålandIslands,"Aland Islands")+  , (ålandIslands,"Aaland Islands")   , (venezuelaBolivarianRepublicOf,"Venezuela")   , (venezuelaBolivarianRepublicOf,"Bolivarian Republic of Venezuela")   , (boliviaPlurinationalStateOf,"Bolivia")
+ src/Country/Unexposed/Trie.hs view
@@ -0,0 +1,62 @@+module Country.Unexposed.Trie+  ( Trie+  , trieFromList+  , trieParser+  ) where++import Data.HashMap.Strict (HashMap)+import Data.Word (Word16)+import Data.Text (Text)+import Control.Applicative ((<|>))+import qualified Data.Text as T+import qualified Data.HashMap.Strict as HM+import qualified Data.Attoparsec.Text as AT++-- | If the value is not the max Word16 (65535), there +--   is a match. This means that 65535 cannot be used, which +--   is fine for this since 65535 is not used as a country code.+data Trie = Trie+  { trieValue :: {-# UNPACK #-} !Word16+  , trieChildren :: !(HashMap Char Trie)+  }++empty :: Trie+empty = Trie placeholder HM.empty++append :: Trie -> Trie -> Trie+append (Trie v1 c1) (Trie v2 c2) = Trie (min v1 v2) (HM.unionWith append c1 c2)++placeholder :: Word16+placeholder = 0xFFFF++singleton :: Text -> Word16 -> Trie+singleton fullName code = go fullName where+  go :: Text -> Trie+  go name = case T.uncons name of+    Just (char,nameNext) -> Trie placeholder (HM.singleton char (go nameNext))+    Nothing -> Trie code HM.empty++instance Monoid Trie where+  mempty = empty+  mappend = append++trieFromList :: [(Text,Word16)] -> Trie+trieFromList = foldMap (uncurry singleton)++-- it seems like attoparsec should have some kind of convenience+-- for being able to commit to consuming a certain amount of+-- input once your certain that it will be consumed, but I cannot+-- find a way to use the api to do this.+trieParser :: Trie -> AT.Parser Word16+trieParser = go where+  go :: Trie -> AT.Parser Word16+  go (Trie value children) = do+    let keepGoing = do+          c <- AT.anyChar+          case HM.lookup c children of+            Nothing -> fail "did not recognize country name"+            Just trieNext -> go trieNext+    if value == placeholder+      then keepGoing+      else keepGoing <|> return value+
+ src/Country/Unexposed/TrieByte.hs view
@@ -0,0 +1,63 @@+module Country.Unexposed.TrieByte+  ( TrieByte+  , trieByteFromList+  , trieByteParser+  ) where++import Data.HashMap.Strict (HashMap)+import Data.Word (Word16,Word8)+import Data.ByteString (ByteString)+import Control.Applicative ((<|>))+import qualified Data.ByteString as B+import qualified Data.HashMap.Strict as HM+import qualified Data.Attoparsec.ByteString as AB++-- | If the value is not the max Word16 (65535), there +--   is a match. This means that 65535 cannot be used, which +--   is fine for this since 65535 is not used as a country code.+data TrieByte = TrieByte+  { trieValue :: {-# UNPACK #-} !Word16+  , trieChildren :: !(HashMap Word8 TrieByte)+  }++empty :: TrieByte+empty = TrieByte placeholder HM.empty++append :: TrieByte -> TrieByte -> TrieByte+append (TrieByte v1 c1) (TrieByte v2 c2) = TrieByte (min v1 v2) (HM.unionWith append c1 c2)++placeholder :: Word16+placeholder = 0xFFFF++singleton :: ByteString -> Word16 -> TrieByte+singleton fullName code = go fullName where+  go :: ByteString -> TrieByte+  go name = case B.uncons name of+    Just (char,nameNext) -> TrieByte placeholder (HM.singleton char (go nameNext))+    Nothing -> TrieByte code HM.empty++instance Monoid TrieByte where+  mempty = empty+  mappend = append++trieByteFromList :: [(ByteString,Word16)] -> TrieByte+trieByteFromList = foldMap (uncurry singleton)++-- it seems like attoparsec should have some kind of convenience+-- for being able to commit to consuming a certain amount of+-- input once your certain that it will be consumed, but I cannot+-- find a way to use the api to do this.+trieByteParser :: TrieByte -> AB.Parser Word16+trieByteParser = go where+  go :: TrieByte -> AB.Parser Word16+  go (TrieByte value children) = do+    let keepGoing = do+          c <- AB.anyWord8+          case HM.lookup c children of+            Nothing -> fail "did not recognize country name"+            Just trieNext -> go trieNext+    if value == placeholder+      then keepGoing+      else keepGoing <|> return value++