packages feed

haskell-postal (empty) → 0.1.0.0

raw patch · 6 files changed

+288/−0 lines, 6 filesdep +basedep +bytestringdep +containerssetup-changed

Dependencies added: base, bytestring, containers, inline-c, text

Files

+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2018 Fábián Tamás László++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,8 @@+# haskell-postal++Haskell binding for the libpostal library++You have to install libpostal. Get it from here: https://github.com/openvenues/libpostal++This binding is in alpha state, expect more functionality to come soon. Currently+it can only be used to do basic parsing and normalization.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ haskell-postal.cabal view
@@ -0,0 +1,46 @@+name:                haskell-postal+version:             0.1.0.0+synopsis:            Haskell binding for the libpostal library+description:         Provides an interface for the libpostal NLP library+homepage:            https://github.com/netom/haskell-postal#readme+license:             MIT+license-file:        LICENSE+author:              Fábián Tamás László+maintainer:          giganetom@gmail.com+copyright:           2018 Fábián Tamás László+category:            Natural Language Processing+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     NLP.Postal+  default-language:    Haskell2010+  pkgconfig-depends:   libpostal+  ghc-options:         -Wall -fPIC+  build-depends:+      base >= 4.7 && < 5+    , containers >= 0.5.11 && < 0.6+    , bytestring >= 0.10.8 && < 0.11+    , text >= 1.2.3 && < 1.3+    , inline-c >= 0.6.1 && < 0.7++executable haskell-postal+  hs-source-dirs:      src+  main-is:             main.hs+  default-language:    Haskell2010+  pkgconfig-depends:   libpostal+  ghc-options:         -Wall+  build-depends:+      base >= 4.7 && < 5+    , containers+    , bytestring+    , text+    , inline-c+  other-modules:+      NLP.Postal++source-repository head+  type:     git+  location: https://github.com/netom/haskell-postal
+ src/NLP/Postal.hs view
@@ -0,0 +1,174 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module NLP.Postal+    ( AddressParserOptions+    , NormalizeOptions+    , setup+    , setupParser+    , setupLanguageClassifier+    , getAddressParserDefaultOptions+    , getDefaultNormalizeOptions+    , parseAddress+    , expandAddress+    , tearDownParser+    , tearDownLanguageClassifier+    , tearDown+    ) where++import Data.Monoid ((<>))+import Foreign.C.Types+import qualified Language.C.Inline as C+import Language.C.Inline.Context as CX++import Foreign.Ptr++import Data.ByteString.Internal (fromForeignPtr)++import Foreign.ForeignPtr+import Control.Monad (forM, when)++import qualified Data.Text as T+import qualified Data.Text.Encoding as TE++C.context (CX.baseCtx <> CX.bsCtx)++C.include "<libpostal/libpostal.h>"+C.include "<string.h>"++-- |Phantom type for a Ptr containing address parser options+data AddressParserOptions++-- |Phantom type for a Ptr containing options for the normalizer+data NormalizeOptions++foreign import ccall "stdlib.h &free" p_free :: FunPtr (Ptr a -> IO ())++-- |Calls libpostal_setup() to set up the library+-- Make sure you call this before anything else+setup :: IO Int+setup = fromIntegral <$> [C.exp| int { libpostal_setup() } |]++-- |Calls libpostal_setup_parser() to set up the address parser+-- Call this before trying to parse addresses+setupParser :: IO Int+setupParser = fromIntegral <$> [C.exp| int { libpostal_setup_parser() } |]++-- |Calls libpostal_setup_language_classifier() to set up the classifier+-- Call this before doing address normaliation+setupLanguageClassifier :: IO Int+setupLanguageClassifier =  fromIntegral <$> [C.exp| int { libpostal_setup_language_classifier() } |]++-- |Returns default parser options+-- returned by libpostal_get_address_parser_default_options()+getAddressParserDefaultOptions :: IO (Ptr AddressParserOptions)+getAddressParserDefaultOptions = castPtr <$> [C.block|+    void * {+        libpostal_address_parser_options_t options = libpostal_get_address_parser_default_options();+        void * hoptions = malloc(sizeof(options));+        memcpy(hoptions, &options, sizeof(options));+        return hoptions;+    } |]++-- |Returns default normalizer options+-- returned by libpostal_get_default_options()+getDefaultNormalizeOptions :: IO (Ptr NormalizeOptions)+getDefaultNormalizeOptions = castPtr <$> [C.block|+    void * {+        libpostal_normalize_options_t options = libpostal_get_default_options();+        void * hoptions = malloc(sizeof(options));+        memcpy(hoptions, &options, sizeof(options));+        return hoptions;+    } |]++-- |Parse an address+-- Calls libpostal_parse_address() to parse an address and returns the parsed+-- parts as a list of key-value tuples (an association list).+parseAddress :: Ptr AddressParserOptions -> T.Text -> IO [(T.Text, T.Text)]+parseAddress options address = do+        response <- [C.exp| void * { libpostal_parse_address($bs-ptr:bsAddress, * (libpostal_address_parser_options_t *) $(void * castedOptions)) } |]++        when (response == nullPtr) $ fail "libpostal_parse_address returned NULL a pointer"++        numComponents <- [C.exp| int { ((libpostal_address_parser_response_t *) $(void * response))->num_components } |]++        -- TODO: zero length label+        -- TODO: NULL == malloc()+        -- TODO: put C functions in actual C files, call those with C.exp-s+        result <- forM [0..numComponents-1] $ \i -> do+            (labelLen, labelPtr) <- C.withPtr $ \len -> [C.block| char * {+                char * srcbuf = ((libpostal_address_parser_response_t *) $(void * response))->labels[$(int i)];+                *$(size_t * len) = strlen(srcbuf);+                char * dstbuf = malloc(*$(size_t * len));+                memcpy(dstbuf, srcbuf, *$(size_t * len));+                return dstbuf;+            } |]++            labelFPtr <- newForeignPtr p_free labelPtr++            (compLen, compPtr) <- C.withPtr $ \len -> [C.block| char * {+                char * srcbuf = ((libpostal_address_parser_response_t *) $(void * response))->components[$(int i)];+                *$(size_t * len) = strlen(srcbuf);+                char * dstbuf = malloc(*$(size_t * len));+                memcpy(dstbuf, srcbuf, *$(size_t * len));+                return dstbuf;+            } |]++            compFPtr <- newForeignPtr p_free compPtr++            return+                ( TE.decodeUtf8 $ fromForeignPtr (castForeignPtr labelFPtr) 0 (fromIntegral labelLen)+                , TE.decodeUtf8 $ fromForeignPtr (castForeignPtr compFPtr) 0 (fromIntegral compLen)+                )++        [C.exp| void { libpostal_address_parser_response_destroy((libpostal_address_parser_response_t *) $(void * response)) } |]++        return result++        where+            castedOptions = castPtr options+            bsAddress = TE.encodeUtf8 address++-- |Returns the expansion of an address+-- Calls libpostal_expand_address() to normalize an address and return the list+-- of normalized addresses.+expandAddress :: Ptr NormalizeOptions -> T.Text -> IO [T.Text]+expandAddress options address = do+    (numExpansions, expansions) <- C.withPtr $ \numExpansions ->+        [C.exp| char * * { libpostal_expand_address($bs-ptr:bsAddress, * (libpostal_normalize_options_t *) $(void * cOptions), $(size_t * numExpansions) ) } |]++    -- TODO: this also turns an array of (char *) to a list of Texts.+    result <- forM [0..numExpansions-1] $ \i -> do+        (xpLen, xpPtr) <- C.withPtr $ \len -> [C.block| char * {+            char * srcbuf = $(char * * expansions)[$(size_t i)];+            *$(size_t * len) = strlen(srcbuf);+            char * dstbuf = malloc(*$(size_t * len));+            memcpy(dstbuf, srcbuf, *$(size_t * len));+            return dstbuf;+        } |]++        xpFPtr <- newForeignPtr p_free xpPtr++        return $ TE.decodeUtf8 $ fromForeignPtr (castForeignPtr xpFPtr) 0 (fromIntegral xpLen)++    [C.exp| void { libpostal_expansion_array_destroy($(char * * expansions), $(size_t numExpansions)) } |]++    return result++    where+        bsAddress = TE.encodeUtf8 address+        cOptions = castPtr options++-- |Calls libpostal_teardown_parser()+tearDownParser :: IO ()+tearDownParser = [C.exp| void { libpostal_teardown_parser() } |]++-- |Calls libpostal_teardown_language_classifier()+tearDownLanguageClassifier :: IO ()+tearDownLanguageClassifier = [C.exp| void { libpostal_teardown_language_classifier() } |]++-- |Calls libpostal_teardown()+tearDown :: IO ()+tearDown = [C.exp| void { libpostal_teardown() } |]
+ src/main.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Control.Monad+import System.Environment+import System.Exit+import NLP.Postal++import qualified Data.Text as T++main :: IO ()+main = do+    args <- getArgs++    when (length args /= 1) $ die "Usage: haskell-postal <address>"++    let address = T.pack $ head args++    putStrLn "Setting up libpostal..."+    setup >>= \x -> when (x == 0) $ die "FAIL"++    putStrLn "Setting up the parser..."+    setupParser >>= \x -> when (x == 0) $ die "FAIL"++    putStrLn "Setting up the language classifier..."+    setupLanguageClassifier >>= \x -> when (x == 0) $ die "FAIL"++    defParserOpts <- getAddressParserDefaultOptions+    defNormalizeOptions <- getDefaultNormalizeOptions++    parseAddress defParserOpts address >>= print+    expandAddress defNormalizeOptions address >>= print++    tearDownLanguageClassifier+    tearDownParser+    tearDown