packages feed

text-icu-translit (empty) → 0.1.0.7

raw patch · 9 files changed

+439/−0 lines, 9 filesdep +QuickCheckdep +basedep +test-frameworksetup-changed

Dependencies added: QuickCheck, base, test-framework, test-framework-quickcheck2, text, text-icu, text-icu-translit

Files

+ Data/Text/ICU/Translit.hs view
@@ -0,0 +1,40 @@+-- |+--  Module:     Data.Text.ICU.Translit+--  License:    BSD-style+--  Maintainer: me@lelf.lu+-- +-- This module provides the bindings to the transliteration features+-- by the ICU (International Components for Unicode) library.+-- +-- >>> IO.putStrLn $ transliterate (trans "name-any; ru") "\\N{RABBIT FACE} Nu pogodi!"+-- 🐰 Ну погоди!+-- +-- >>> IO.putStrLn $ transliterate (trans "nl-title") "gelderse ijssel"+-- Gelderse IJssel+-- +-- >>> IO.putStrLn $ transliterate (trans "ja") "Amsterdam"+-- アムステルダム+-- +-- More information about the rules is+-- <http://userguide.icu-project.org/transforms/general here>.++++module Data.Text.ICU.Translit+    (IO.Transliterator, trans, transliterate) where++import qualified Data.Text.ICU.Translit.Internal as IO+import System.IO.Unsafe+import Data.Text+++-- | Construct new transliterator by name. Will throw an error if+-- there is no such transliterator+trans :: Text -> IO.Transliterator+trans t = unsafePerformIO $ IO.transliterator t+++-- | Transliterate the text using the transliterator+transliterate :: IO.Transliterator -> Text -> Text+transliterate tr txt = unsafePerformIO $ IO.transliterate tr txt+
+ Data/Text/ICU/Translit/ICUHelper.hsc view
@@ -0,0 +1,122 @@+{-# LANGUAGE DeriveDataTypeable, MultiWayIf #-}+module Data.Text.ICU.Translit.ICUHelper+    (+      ICUError(..)+    , UChar+    , UErrorCode+    , isFailure+    , errorName+    , handleError+    , handleFilledOverflowError+    , throwOnError+    ) where+++-- Many functions in this module are straight from the+-- Data.Text.ICU.Error.Internal (text-icu).+-- +-- XXX TODO:+--   ⋆ export this and similar functionality somewhere;+-- or+--   ⋆ merge text-icu-* into text-icu?+++import Control.Exception (Exception, throwIO)+import Data.Typeable (Typeable)+import Foreign.C.Types (CInt(..))+import Foreign.C.String (CString, peekCString)+import qualified System.IO.Unsafe as IO (unsafePerformIO)+import Foreign ++type UErrorCode = CInt+type UChar = Word16++newtype ICUError = ICUError {+      fromErrorCode :: UErrorCode+    } deriving (Eq, Typeable)++instance Show ICUError where+    show code = "ICUError " ++ errorName code++instance Exception ICUError+++#include <unicode/utypes.h>+++-- | Indicate whether the given error code is a failure.+isFailure :: ICUError -> Bool+{-# INLINE isFailure #-}+isFailure = (> 0) . fromErrorCode+++-- | Throw an exception if the given code is actually an error.+throwOnError :: UErrorCode -> IO ()+{-# INLINE throwOnError #-}+throwOnError code = do+  let err = (ICUError code)+  if isFailure err+    then throwIO err+    else return ()++++handleError :: (Ptr UErrorCode -> IO a) -> IO a+{-# INLINE handleError #-}+handleError action = with 0 $ \errPtr -> do+                       ret <- action errPtr+                       throwOnError =<< peek errPtr+                       return ret++++-- | Deal with ICU functions that report a buffer overflow error if we+-- give them an insufficiently large buffer.  The difference between+-- this function and+-- 'Data.Text.ICU.Error.Internal.handleOverflowError' is that this one+-- doesn't change the contents of the provided buffer, while the+-- latter assumes buffers to be write-only.+handleFilledOverflowError :: (Storable a) =>+                             Ptr a+                          -- ^ Initial buffer.+                          -> Int+                          -- ^ Initial buffer size.+                          -> (Ptr a -> Int32 -> Ptr UErrorCode -> IO Int32)+                          -- ^ Function that retrieves data.+                          -> (Ptr a -> Int -> IO b)+                          -- ^ Function that fills destination buffer if no+                          -- overflow occurred.+                          -> IO b+handleFilledOverflowError text len0 fill retrieve =+    do buf0 <- mallocArray len0+       copyArray buf0 text len0+       go buf0 len0+    where+      go buf len = alloca $ \errPtr -> do+                     poke errPtr 0+                     len' <- fill buf (fromIntegral len) errPtr+                     err <- peek errPtr+                     if | err == (#const U_BUFFER_OVERFLOW_ERROR)+                            -> do buf' <- reallocArray buf (fromIntegral len')+                                  copyArray buf' text len0+                                  go buf' (fromIntegral len')+                        | err > 0+                            -> throwIO (ICUError err)+                        | otherwise+                            -> retrieve buf (fromIntegral len')+++++++++-- | Return a string representing the name of the given error code.+errorName :: ICUError -> String+errorName code = IO.unsafePerformIO $+                 peekCString (u_errorName (fromErrorCode code))++foreign import ccall unsafe "trans.h __hs_translit_u_errorName" u_errorName+    :: UErrorCode -> CString+
+ Data/Text/ICU/Translit/Internal.hs view
@@ -0,0 +1,54 @@+module Data.Text.ICU.Translit.Internal+    (Transliterator,+     transliterator,+     transliterate) where++import Foreign+import Data.Text+import Data.Text.Foreign+import Data.Text.ICU.Translit.ICUHelper++data UTransliterator++foreign import ccall "trans.h __hs_translit_open_trans" openTrans+    :: Ptr UChar -> Int -> Ptr UErrorCode -> IO (Ptr UTransliterator)+foreign import ccall "trans.h &__hs_translit_close_trans" closeTrans+    :: FunPtr (Ptr UTransliterator -> IO ())+foreign import ccall "trans.h __hs_translit_do_trans" doTrans+    :: Ptr UTransliterator -> Ptr UChar -> Int32 -> Int32+    -> Ptr UErrorCode -> IO Int32++++data Transliterator = Transliterator {+      transPtr :: ForeignPtr UTransliterator,+      transSpec :: Text+    }+++instance Show Transliterator where+    show tr = "Transliterator " ++ show (transSpec tr)++++transliterator :: Text -> IO Transliterator+transliterator spec =+    useAsPtr spec $ \ptr len -> do+           q <- handleError $ openTrans ptr (fromIntegral len)+           ref <- newForeignPtr closeTrans q+           touchForeignPtr ref+           return $ Transliterator ref spec+++transliterate :: Transliterator -> Text -> IO Text+transliterate tr txt = do+  (fptr, len) <- asForeignPtr txt+  withForeignPtr fptr $ \ptr ->+      withForeignPtr (transPtr tr) $ \tr_ptr -> do+             handleFilledOverflowError ptr (fromIntegral len)+                 (\dptr dlen ->+                        doTrans tr_ptr dptr (fromIntegral len) (fromIntegral dlen))+                 (\dptr dlen ->+                        fromPtr (castPtr dptr) (fromIntegral dlen))++
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Antonio Nikishaev++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Antonio Nikishaev nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cbits/trans.c view
@@ -0,0 +1,55 @@+#include <unicode/utrans.h>+#include <stdio.h>+#include "trans.h"++void *+__hs_translit_open_trans (const UChar *name, int len, UErrorCode *err)+{+  UTransliterator *tr = utrans_openU (name, len, UTRANS_FORWARD, 0, -1, 0, err);++  /* printf ("trans %p, err=%s\n", tr, u_errorName (*err)); */++  return tr;+}++void+__hs_translit_close_trans (UTransliterator *trans)+{+  /* printf ("close\n"); */+  utrans_close (trans);+}++int32_t+__hs_translit_do_trans (UTransliterator *trans, UChar *text, int32_t len,+			 int32_t capacity, UErrorCode *err)+{+  /* int len0 = len; */+  int lim = len;++  utrans_transUChars (trans, text, &len, capacity, 0, &lim, err);++  /* printf ("trans len=lim=%d cap=%d -> len=%d lim=%d err=%d\n", */+  /* 	  len0, capacity, len, lim, *err); */+  /* printf ("T: %s\n", u_errorName (*err)); */++  return lim;+}+++/* char *listTrans (UErrorCode *err) */+/* { */+/*   UEnumeration *iter = utrans_openIDs (err); */+/*   for (const char *x; x = uenum_next (iter, 0, err);) */+/*     u_printf ("-%s\n", x); */++  +/* } */++++const char *+__hs_translit_u_errorName (UErrorCode code)+{+    return u_errorName(code);+}+
+ include/trans.h view
@@ -0,0 +1,11 @@++void *__hs_translit_open_trans (const UChar *name, int len, UErrorCode *err);+void __hs_translit_close_trans (UTransliterator *trans);++int32_t __hs_translit_do_trans (UTransliterator *trans,+				UChar *text, int32_t len,+				int32_t capacity, UErrorCode *err);++const char *__hs_translit_u_errorName (UErrorCode code);++
+ tests/Test.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}++import Test.QuickCheck+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.ICU as U+import Data.Text.ICU.Translit+import Text.Printf+import Data.Char++import Test.Framework+import Test.Framework.Providers.QuickCheck2++-- newtype Random a = Random { unRandom :: a } deriving Show+++instance Arbitrary Text where+    arbitrary = fmap T.pack arbitrary+    shrink = fmap T.pack . shrink . T.unpack++-- instance Arbitrary a => Arbitrary (Random a) where+--     arbitrary = fmap Random arbitrary+++samples :: [Text]+samples = ["hello","текст","维基百科:海納百川,有容乃大"]+++newtype IdempTr = IdempTr Text deriving Show++++instance Arbitrary IdempTr where+    arbitrary = elements transes0+        where transes0 = map IdempTr ["ru-en", "en-ru"]++hexUnicode :: Text -> Text+hexUnicode txt = T.pack $ concat [ fmt c | c <- T.unpack txt ]+    where fmt c = printf (if ord c < 0x10000 then "U+%04X" else "U+%X") (ord c)+++prop_idemp (IdempTr t,s) = transliterate tr (transliterate tr s) == transliterate tr s+    where tr = trans t+prop_toLower' t = U.toLower U.Root t == transliterate (trans "Lower") t+prop_toLower t = T.toLower t == transliterate (trans "Lower") t+prop_NFC t = U.normalize U.NFC t == transliterate (trans "NFC") t+prop_hexUnicode t = hexUnicode t == transliterate (trans "hex/unicode") t++++main = defaultMain [tests]++tests :: Test+tests = testGroup "props" [+         testProperty "idemp" prop_idemp,+         testProperty "toLower'" prop_toLower',+         testProperty "toLower" prop_toLower,+         testProperty "NFC" prop_NFC,+         testProperty "hexUnicode" prop_hexUnicode+        ]++++
+ text-icu-translit.cabal view
@@ -0,0 +1,61 @@++name:                text-icu-translit+version:             0.1.0.7+synopsis:            ICU transliteration+description:+  Bindings to the transliteration features by the+  International Components for Unicode (ICU) library++license:             BSD3+license-file:        LICENSE+author:              Antonio Nikishaev+maintainer:          Antonio Nikishaev <me@lelf.lu>+copyright:           2014 Antonio Nikishaev+bug-reports:         https://github.com/llelf/text-icu-translit/issues+category:            Text+build-type:          Simple+cabal-version:       >=1.10+extra-source-files:  include/trans.h++library+  exposed-modules:     Data.Text.ICU.Translit+  other-modules:       Data.Text.ICU.Translit.Internal,+                       Data.Text.ICU.Translit.ICUHelper++  ghc-options: -Wall++  c-sources: cbits/trans.c+  include-dirs: include++  extra-libraries: icuuc+  if os(mingw32)+    extra-libraries: icuin icudt+  else+    extra-libraries: icui18n icudata++  -- other-extensions:    +  build-depends:       base >=4 && <5, text >= 1.0+  -- hs-source-dirs:      +  default-language:    Haskell2010++test-suite tests+  type:                exitcode-stdio-1.0+  default-language:    Haskell2010+  hs-source-dirs:      tests+  main-is:             Test.hs+  build-depends:       base, text,+                       text-icu >= 0.6.3,+                       text-icu-translit,+                       QuickCheck >= 2.4,+                       test-framework >= 0.8,+                       test-framework-quickcheck2 >= 0.3+++source-repository head+  type:     darcs+  location: http://hub.darcs.net/lelf/text-icu-translit++source-repository head+  type:     git+  location: https://github.com/llelf/text-icu-translit+