module Data.Encoding.Helper.Template
(makeISOInstance
,makeJISInstance)
where
import Data.Encoding.Base
import Data.Bits
import Data.Char
import Data.Maybe (mapMaybe)
import Data.Map as Map (Map,fromList,lookup)
import Data.Array.Unboxed
import Data.Typeable
import Data.Word (Word8)
import Language.Haskell.TH
makeISOInstance :: String -> FilePath -> Q [Dec]
makeISOInstance name file = do
trans <- runIO (readTranslation 0 id file)
let mp = encodingMap (validTranslations trans)
let arr = decodingArray (fillTranslations 0 255 trans)
return $ encodingInstance (ConT ''Word8) 'encodeWithMap 'decodeWithArray 'encodeableWithMap name mp arr
makeJISInstance :: Int -> String -> FilePath -> Q [Dec]
makeJISInstance offset name file = do
trans <- runIO (readTranslation offset (\src -> (src `shiftR` 8,src .&. 0xFF)) file)
let mp = encodingMap2 (validTranslations trans)
let arr = decodingArray2 (fillTranslations (0x21,0x21) (0x7E,0x7E) trans)
return $ encodingInstance ((TupleT 2) `AppT` (ConT ''Word8) `AppT` (ConT ''Word8)) 'encodeWithMap2 'decodeWithArray2 'encodeableWithMap name mp arr
encodingInstance :: Type -> Name -> Name -> Name -> String -> Exp -> Exp -> [Dec]
encodingInstance tp enc dec able name mp arr
= [ DataD [] rname [] [NormalC rname []] [''Show,''Eq,''Typeable]
, SigD rmp (((ConT ''Map.Map) `AppT` (ConT ''Char)) `AppT` tp)
, ValD (VarP rmp) (NormalB mp) []
, SigD rarr ((ConT ''UArray) `AppT` tp `AppT` (ConT ''Int))
, ValD (VarP rarr) (NormalB arr) []
, InstanceD [] (AppT (ConT ''Encoding) (ConT rname))
[FunD 'encodeChar
[Clause [WildP] (NormalB $ AppE (VarE enc) (VarE rmp))
[]
]
,FunD 'decodeChar
[Clause [WildP] (NormalB $ AppE (VarE dec) (VarE rarr))
[]
]
,FunD 'encodeable
[Clause [WildP] (NormalB $ AppE (VarE able) (VarE rmp))
[]
]
]
]
where
rname = mkName name
rarr = mkName ("encoding_arr_"++name)
rmp = mkName ("decoding_map_"++name)
createCharArray :: [(Integer,Maybe Char)] -> Integer -> Integer -> Exp
createCharArray lst f t = createArray (map (\(x,y) ->
(LitE $ IntegerL x,mbCharToExp y)
) lst) (LitE $ IntegerL f) (LitE $ IntegerL t)
createCharArray2 :: [((Integer,Integer),Maybe Char)] -> (Integer,Integer) -> (Integer,Integer) -> Exp
createCharArray2 lst (f1,f2) (t1,t2)
= createArray (map (\((x1,x2),y) ->
(TupE [integerExp x1,integerExp x2],mbCharToExp y)
) lst)
(TupE [integerExp f1,integerExp f2])
(TupE [integerExp t1,integerExp t2])
mbCharToExp :: Maybe Char -> Exp
mbCharToExp Nothing = LitE (IntegerL (1))
mbCharToExp (Just c) = LitE (IntegerL $ fromIntegral $ ord c)
integerExp :: Integer -> Exp
integerExp i = LitE $ IntegerL i
createArray :: [(Exp,Exp)] -> Exp -> Exp -> Exp
createArray lst from to
= AppE
(AppE
(VarE 'array)
(TupE [from,to]))
(ListE [TupE [x,y] | (x,y) <- lst])
decodingArray :: [(Integer,Maybe Char)] -> Exp
decodingArray trans = createCharArray trans 0 255
decodingArray2 :: [((Integer,Integer),Maybe Char)] -> Exp
decodingArray2 trans = createCharArray2 trans (0x21,0x21) (0x7E,0x7E)
encodingMap :: [(Integer,Char)] -> Exp
encodingMap trans = AppE
(VarE 'fromList)
(ListE [ TupE [LitE $ CharL to,LitE $ IntegerL from]
| (from,to) <- trans])
encodingMap2 :: [((Integer,Integer),Char)] -> Exp
encodingMap2 trans = AppE
(VarE 'fromList)
(ListE [ TupE [LitE $ CharL to,TupE [integerExp f1,integerExp f2]]
| ((f1,f2),to) <- trans])
readTranslation :: Int -> (Integer -> a) -> FilePath -> IO [(a,Maybe Char)]
readTranslation offset f file = do
cont <- readFile file
return $ mapMaybe (\ln -> case drop offset ln of
[src] -> Just (f src,Nothing)
[src,trg] -> Just (f src,Just $ chr $ fromIntegral trg)
_ -> Nothing) (parseTranslationTable cont)
parseTranslationTable :: String -> [[Integer]]
parseTranslationTable cont = filter (not.null) (map (\ln -> map read (takeWhile ((/='#').head) (words ln))) (lines cont))
fillTranslations :: (Ix a,Show a) => a -> a -> [(a,Maybe Char)] -> [(a,Maybe Char)]
fillTranslations f t = merge (range (f,t))
where
merge xs [] = map (\x -> (x,Nothing)) xs
merge [] cs = error $ "Data.Encoding.Helper.Template.fillTranslations: Character translations out of range: " ++ show cs
merge (x:xs) (y:ys) = if x < fst y
then (x,Nothing):(merge xs (y:ys))
else y:(merge xs ys)
validTranslations :: [(a,Maybe Char)] -> [(a,Char)]
validTranslations = mapMaybe (\(n,mc) -> case mc of
Nothing -> Nothing
Just c -> Just (n,c))