{-
GenI surface realiser
Copyright (C) 2005 Carlos Areces and Eric Kow
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-- This module is meant to act as a standalone program which serves
-- strictly as a converter between the formats recognised by GenI.
module Main (main) where
import Control.Monad
import Data.Binary (decodeFile, encodeFile)
import Data.List
import Data.Maybe
import System (ExitCode(ExitFailure), exitWith, getArgs, getProgName)
import System.Console.GetOpt(OptDescr(Option), ArgDescr(..), usageInfo, getOpt, ArgOrder(Permute))
import System.IO.Unsafe(unsafeInterleaveIO)
import Prelude hiding (appendFile, getContents, readFile, writeFile, putStrLn)
import System.IO.UTF8
import NLP.GenI.Btypes
import NLP.GenI.BtypesBinary ()
import NLP.GenI.General
import NLP.GenI.GeniParsers
import NLP.GenI.GeniShow
import NLP.GenI.HsShow (hsShow)
import NLP.GenI.Converter.ReadTagml (readTagmlMacros)
import Paths_geniconvert
import Data.Version
main :: IO ()
main =
do args <- getArgs
progname <- getProgName
case getOpt Permute options args of
(o,_, [] ) | VersionFlg `elem` o ->
do putStrLn $ "geniconvert version " ++ showVersion version
(o,fs,[] ) -> convert (toInputParams o) fs
_ -> showUsage progname
where
showUsage p = do
let header = "usage: " ++ p ++ " [--macros|--lexicon] -f [tagml|geni] -t [haskell|geni|genib] -o output < input"
ePutStrLn $ usageInfo header options
exitWith (ExitFailure 1)
convert :: InputParams -> [FilePath] -> IO ()
convert (InputParams iForm oForm f iType) fs =
do iFormat <- case getFormat iForm of
Nothing -> fail $ "Unknown input format: " ++ iForm
Just fo -> return fo
oFormat <- case getFormat oForm of
Nothing -> fail $ "Unknown output format: " ++ oForm
Just fo -> return fo
when (null f) $
fail $ "Sorry, you must specify an output file with -o"
when (oFormat `elem` [ haskellFormat, genibFormat ] && length fs > 1) $
fail $ "Sorry, I can't convert more than one file to " ++ showFormat oFormat ++ " at a time"
-- empty out the file first! (there might be an old one lying around)
writeFile f ""
--
let getParser p = maybe (oops iFormat "parse") textReader $ p iFormat
getReader r = fromMaybe (oops iFormat "read") $ r iFormat
getWriter w = fromMaybe (oops oFormat "write") $ w oFormat
oops format d =
fail $ unwords [ "Sorry, I don't know how to", d, show iType, "in the", showFormat format, "format" ]
let convertString x = case iType of
MacrosItype -> getParser parseMacros x >>= (getWriter writeMacros f)
LexiconItype -> getParser parseLexicon x >>= (getWriter writeLexicon f)
convertFile x = case iType of
MacrosItype -> getReader readMacros x >>= (getWriter writeMacros f)
LexiconItype -> getReader readLexicon x >>= (getWriter writeLexicon f)
if null fs
then getContents >>= convertString
else forM_ fs $ (\x -> unsafeInterleaveIO (readFile x) >>= convertFile)
-- -------------------------------------------------------------------
-- command line arguments
-- -------------------------------------------------------------------
data Flag = FromFlg String | ToFlg String | OutputFlg String
| MacrosFlg | LexiconFlg | VersionFlg
deriving (Eq)
options :: [OptDescr Flag]
options =
[ Option "f" ["from"] (ReqArg FromFlg "TYPE") "tagml|geni"
, Option "t" ["to"] (ReqArg ToFlg "TYPE") "haskell|geni|genib"
, Option "o" ["output"] (ReqArg OutputFlg "STRING") "output file, or -t haskell, prefix for output files"
, Option "v" ["version"] (NoArg VersionFlg) "display version"
--
, Option "" ["macros"] (NoArg MacrosFlg)"input file is a macros file (default)"
, Option "" ["lexicon"] (NoArg LexiconFlg) "input file is a lexicon file"
]
data InputType = MacrosItype | LexiconItype
instance Show InputType where
show MacrosItype = "macros"
show LexiconItype = "lexicons"
data InputParams = InputParams { fromArg :: String
, toArg :: String
, stemArg :: String
, itype :: InputType
}
defaultParams :: InputParams
defaultParams = InputParams "" "" "" MacrosItype
toInputParams :: [Flag] -> InputParams
toInputParams fs = foldr ($) defaultParams $ map processFlag fs
processFlag :: Flag -> InputParams -> InputParams
processFlag (FromFlg x) = \p -> p { fromArg = x }
processFlag (ToFlg x) = \p -> p { toArg = x }
processFlag (OutputFlg x) = \p -> p { stemArg = x }
processFlag MacrosFlg = \p -> p { itype = MacrosItype }
processFlag LexiconFlg = \p -> p { itype = LexiconItype }
processFlag VersionFlg = id
-- -------------------------------------------------------------------
-- formats
-- -------------------------------------------------------------------
type TextParser a = String -> Either String a
type FileReader a = FilePath -> IO a
type FileWriter a = FilePath -> a -> IO ()
data FileFormat = FileFormat
{ showFormat :: String
--
, parseMacros :: Maybe (TextParser Macros)
, readMacros :: Maybe (FileReader Macros)
, writeMacros :: Maybe (FileWriter Macros)
--
, parseLexicon :: Maybe (TextParser [ILexEntry])
, readLexicon :: Maybe (FileReader [ILexEntry])
, writeLexicon :: Maybe (FileWriter [ILexEntry])
}
instance Eq FileFormat where
a == b = showFormat a == showFormat b
wrapParsec :: CharParser () a -> (String -> Either String a)
wrapParsec p lf = either (Left . show) (Right) (parse p "" lf)
textReader :: (String -> Either String a) -> String -> IO a
textReader p lf = either fail return (p lf)
getFormat :: String -> Maybe FileFormat
getFormat x = listToMaybe [ f | f <- formats, x == showFormat f ]
formats :: [FileFormat]
formats = [ tagmlFormat, geniFormat, genibFormat, haskellFormat ]
-- -------------------------------------------------------------------
-- tagml format
-- -------------------------------------------------------------------
tagmlFormat :: FileFormat
tagmlFormat = FileFormat
{ showFormat = "tagml"
, parseMacros = pMacros
, readMacros = fmap textReader pMacros
, writeMacros = Nothing
--
, parseLexicon = pLex
, readLexicon = fmap textReader pLex
, writeLexicon = Nothing
}
where
pMacros = Just $ readTagmlMacros
pLex = Nothing
-- -------------------------------------------------------------------
-- geni format
-- -------------------------------------------------------------------
geniFormat :: FileFormat
geniFormat = FileFormat
{ showFormat = "geni"
--
, parseMacros = pMacros
, readMacros = fmap textReader pMacros
, writeMacros = Just geniWriter
--
, parseLexicon = pLex
, readLexicon = fmap textReader pLex
, writeLexicon = Nothing
}
where
pMacros = Just $ wrapParsec geniMacros
pLex = Just $ wrapParsec geniLexicon
geniWriter :: GeniShow a => FilePath -> [a] -> IO ()
geniWriter mf ms = appendFile mf $ unlines $ map geniShow ms
-- -------------------------------------------------------------------
-- genib format
-- -------------------------------------------------------------------
genibFormat :: FileFormat
genibFormat = FileFormat
{ showFormat = "genib"
--
, parseMacros = Nothing
, readMacros = Just decodeFile
, writeMacros = Just encodeFile
--
, parseLexicon = Nothing
, readLexicon = Just decodeFile
, writeLexicon = Just encodeFile
}
-- -------------------------------------------------------------------
-- haskell macros file
-- -------------------------------------------------------------------
haskellFormat :: FileFormat
haskellFormat = FileFormat
{ showFormat = "haskell"
--
, parseMacros = Nothing
, readMacros = Nothing
, writeMacros = Just writeHaskellMacros
--
, parseLexicon = Nothing
, readLexicon = Nothing
, writeLexicon = Nothing
}
writeHaskellMacros :: String -> Macros -> IO ()
writeHaskellMacros rawStem ms =
do let tpairs = zip [1::Integer ..] ms
stem = toUpperHead rawStem
-- write the sub files
let chunks = everyN 15 tpairs
chunkIds = take (length chunks) [1::Int ..]
sequence $ zipWith (writeChunk stem) chunkIds chunks
-- write the main file
writeFile (stem ++ ".hs") $ unlines $
[ "module " ++ stem ++ "(myGeniGrammar) where"
, "import NLP.GenI.Btypes"
, "" ] ++
map (\i -> "import " ++ stem ++ (show i)) chunkIds ++
[ ""
, "myGeniGrammar :: [MTtree]"
, "myGeniGrammar = "
, " [" ++ (uncommas $ map valName tpairs)
, " ]" ]
writeChunk :: String -> Int -> [(Integer,MTtree)] -> IO ()
writeChunk stem n tps =
let filename = stem ++ (show n) ++ ".hs"
modulename = stem ++ (show n)
treenames = uncommas $ map valName tps
in writeFile filename $
unlines $ [ "module " ++ modulename ++ "(" ++ treenames ++ ") where"
, "import Data.Tree"
, "import NLP.GenI.Btypes"
, ""
, treenames ++ " :: MTtree"
, "" ] ++
(intersperse "" $ map (\ (i,t) -> valName (i,t) ++ " = " ++ hsShow t) tps)
valName :: (Integer, MTtree) -> String
valName (i,t) = "t" ++ (show i) ++ "_" ++ (pfamily t)
-- | Break a list up in to n sized chunks; the last element of the list might
-- be somewhat smaller, eh?
everyN :: Int -> [a] -> [[a]]
everyN _ [] = []
everyN n xs = take n xs : (everyN n $ drop n xs)
uncommas :: [String] -> String
uncommas = concat . (intersperse ", ")