{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Language.Hunspell
(
createSpellChecker, SpellChecker
, spell, suggest, stem, add, remove
) where
import Control.Concurrent.STM
import Foreign
import Foreign.C.String
import Foreign.C.Types
createSpellChecker :: String
-> String
-> IO SpellChecker
createSpellChecker affpath dicpath = do
withCString affpath $ \aff ->
withCString dicpath $ \dic -> do
ptr <- newForeignPtr hunspellDestroy (hunspellCreate aff dic)
SpellChecker ptr <$> atomically (newTMVar ptr)
spell :: SpellChecker -> String -> IO Bool
spell SpellChecker{hunPtrVar=tmvar} word = do
withCString word $ \word' -> do
handlePtr <- atomically $ takeTMVar tmvar
result <- withForeignPtr handlePtr (flip hunspellSpell word')
atomically $ putTMVar tmvar handlePtr
return (if result == 0 then False else True)
suggest :: SpellChecker -> String -> IO [String]
suggest checker word = do
withCString word $ \word' ->
alloca $ \resultsPtr -> do
len <-
withHandle checker $ \handle -> hunspellSuggest handle resultsPtr word'
results <- peekWords len resultsPtr
freeList checker len resultsPtr
return results
stem :: SpellChecker -> String -> IO [String]
stem checker word = do
withCString word $ \word' -> do
alloca $ \resultsPtr -> do
len <-
withHandle checker $ \handle -> hunspellStem handle resultsPtr word'
results <- peekWords len resultsPtr
freeList checker len resultsPtr
return results
add :: SpellChecker -> String -> IO ()
add checker word =
withCString word $ \word' -> withHandle checker (flip hunspellAdd word')
remove :: SpellChecker -> String -> IO ()
remove checker word =
withCString word $ \word' -> withHandle checker (flip hunspellRemove word')
freeList :: SpellChecker -> CInt -> Ptr (Ptr CString) -> IO ()
freeList SpellChecker {hunPtr = handlePtr} len ptr =
withForeignPtr handlePtr $ \handle -> hunspellFreeList handle ptr len
withHandle :: SpellChecker -> (Hunhandle -> IO a) -> IO a
withHandle SpellChecker {hunPtrVar = tmvar} action = do
handlePtr <- atomically $ takeTMVar tmvar
result <- withForeignPtr handlePtr action
atomically $ putTMVar tmvar handlePtr
return result
peekWords :: CInt -> Ptr (Ptr CString) -> IO [String]
peekWords 0 _ = return []
peekWords len ptr = do
arrayPtr <- peek ptr
cstrings <- peekArray (fromIntegral len) arrayPtr
mapM peekCString cstrings
data Hunspell
type Hunhandle = Ptr Hunspell
data SpellChecker = SpellChecker
{ hunPtr :: ForeignPtr Hunspell
, hunPtrVar :: TMVar (ForeignPtr Hunspell)
}
foreign import ccall "Hunspell_create" hunspellCreate
:: CString -> CString -> Hunhandle
foreign import ccall "&Hunspell_destroy" hunspellDestroy
:: FunPtr (Hunhandle -> IO ())
foreign import ccall "Hunspell_free_list" hunspellFreeList
:: Hunhandle -> Ptr (Ptr CString) -> CInt -> IO ()
foreign import ccall "Hunspell_spell" hunspellSpell
:: Hunhandle -> CString -> IO CInt
foreign import ccall "Hunspell_suggest" hunspellSuggest
:: Hunhandle -> Ptr (Ptr CString) -> CString -> IO CInt
foreign import ccall "Hunspell_stem" hunspellStem
:: Hunhandle -> Ptr (Ptr CString) -> CString -> IO CInt
foreign import ccall "Hunspell_add" hunspellAdd :: Hunhandle -> CString -> IO ()
foreign import ccall "Hunspell_remove" hunspellRemove
:: Hunhandle -> CString -> IO ()