FM-SBLEX-3.0: src/lib/Frontend.hs
{-# OPTIONS_GHC -fglasgow-exts #-}
module Frontend where
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Dict.Abs as Abs
import Dictionary
import Dict.ErrM
import Util
import Char
import System
import Print
import General
import Monad(when)
import Maybe(isJust)
import System.IO.Unsafe (unsafePerformIO)
import IO
import Tokenize
import qualified CTrie
import List(intersperse)
import EditDistance
import List
import Compound
-- Note that all Functions have default definitions, but
-- in the common case, you give, at least, definitions for "paradigms"
-- "internDict" and "composition"
type Label = String
type TestInput = (String, Dictionary_Word, Category, Paradigm, [String], [Inherent],String)
tword :: TestInput -> Maybe String
tword ("",_,_,_,_,_,_) = Nothing
tword (s,_,_,_,_,_,_) = Just s
w :: TestInput -> String
w t = case (tword t) of
Just s -> s
Nothing -> []
thead :: TestInput -> String
thead (_,s,_,_,_,_,_) = s
tcat :: TestInput -> String
tcat (_,_,s,_,_,_,_) = s
tpara :: TestInput -> String
tpara (_,_,_,s,_,_,_) = s
tparam :: TestInput -> [String]
tparam (_,_,_,_,xs,_,_) = xs
tinhs :: TestInput -> [String]
tinhs (_,_,_,_,_,xs,_) = xs
tid :: TestInput -> String
tid (_,_,_,_,_,_,s) = s
type PositiveTests = [(TestInput -> Maybe String)]
type NegativeTests = [(TestInput -> Maybe String)]
type Result = Maybe String
message :: TestInput -> String -> Result
message t s = Just $ concat ["[ ", s, " ]\n ", prw (w t), " {h:\"", pr (thead t),"\" pos:",pr (tcat t)," param:", prl (tparam t),
" is:",pri (tinhs t), " id:", pr (tid t), " p:", pr (tpara t),"}"]
where prw s = case s of
[] -> " * "
x -> quote s
pr s = case s of
[] -> "unknown"
_ -> s
pri xs = case xs of
[] -> "none"
_ -> unwords xs
prl xs = case xs of
[] -> "unknown"
_ -> unwords xs
pass :: Maybe String
pass = Nothing
type Encoding = String
type TrPos = String -> String
type TrInhs = [String] -> [String]
type TrParam = (String,String,[String]) -> String
--class Show a => Language a b | a -> b where
-- | A class defined to be able to construct a language independent frontend
class Show a => Language a where
name :: a -> String
morphology_header :: a -> String
dbaseName :: a -> String
composition :: a -> Maybe CompDesc --([General.Attr] -> Bool)
word_attr :: a -> [Attr]
affixes :: a -> Set.Set String
env :: a -> String
paradigms :: a -> Commands
internDict :: a -> Dictionary
tokenizer :: a -> String -> [General.Tok]
wordGuesser :: a -> String -> [String]
termParser :: a -> [Abs.Term] -> Entry -> Entry
testBench :: a -> (PositiveTests,NegativeTests)
dup_id_exceptions :: a -> [(String,String)]
sandhi_rules :: a -> (String,String) -> [(String,String)]
encoding :: a -> Map.Map Encoding (Maybe TrPos, Maybe TrInhs, Maybe TrParam)
lprinter :: a -> [(String,Dictionary -> String)]
dictionary_postprocessing :: a -> Dictionary -> Dictionary
paradigm_dup_exception :: a -> Set.Set String
name l = map toLower (show l)
morphology_header l = name l
dbaseName l = name l ++ ".dict"
composition l = Nothing -- noComp
word_attr l = [0]
affixes l = Set.empty
env l = "FM_" ++ map toUpper (show l)
encoding _ = Map.empty
paradigms _ = emptyC
internDict _ = emptyDict
tokenizer _ = tokens
sandhi_rules _ = (:[])
wordGuesser _ = const []
testBench _ = ([],[])
dup_id_exceptions _ = []
termParser _ _ e = e
lprinter _ = []
dictionary_postprocessing _ d = d
paradigm_dup_exception _ = Set.empty
-- | type for Command Map
type Commands = Map.Map String ([String], [String] -> Entry)
-- | empty Command Map
emptyC :: Commands
emptyC = Map.empty
isComp :: Language l => l -> Bool
isComp l = case composition l of
Nothing -> False
_ -> True
-- | add a command
insertCommand :: (String,[String],[String] -> Entry) -> Commands -> Commands
insertCommand (n,args,f) cs
| Map.member n cs = unsafePerformIO $
do prErr $ "internal error:\nduplicated paradigm identifier in command table: " ++ n
return cs
| otherwise = Map.insert n (args,f) cs
-- | Construct a Command Map
mkCommands :: [(String,[String],[String] -> Entry)] -> Commands
mkCommands = foldr insertCommand Map.empty
-- | Create a dictionary from the list of paradigms.
-- prParadigms :: Language a => a -> Dictionary -> String
-- prParadigms l d = prDictionary d
--unlines $ filter (not . null) $ map pr [(unwords (p:(map quote xs)), dictionary [f xs]) | (p,(xs,f)) <- Map.toList (paradigms l)]
-- where pr (s,d)
-- | is_empty_dictionary d = ""
-- | otherwise = "{\n" ++ s ++ "\n\n" ++ prDictionary d ++ "}\n"
prTagset :: Language a => a -> Dictionary -> String
prTagset l d = collect_and_print (Set.empty,Set.empty,Set.empty) (unDict d)
where collect_and_print :: (Set.Set String, Set.Set String, Set.Set String) -> [Entry] -> String
collect_and_print (s,i,p) [] = concat $ ["{\n\"pos\":[",
(concat (intersperse "," (map quote (Set.toList s)))),
"],\n",
"\"inherent\":[",
(concat (intersperse "," (map quote (Set.toList i)))),
"],\n",
"\"param\":[",
(concat (intersperse "," (map quote (Set.toList p)))),
"]\n}\n"]
collect_and_print (s,i,p) ((_,_, _ , pos, inhs, infl,_):xs) = collect_and_print (update [pos] s, update inhs i, update [t | (t,_) <- infl] p) xs
update [] s = s
update (x:xs) s = update xs (Set.insert x s)
prCompound :: Language a => a -> String
prCompound l = case (composition l) of
Nothing -> "NONE"
Just x -> prCompDesc x
prExtract :: Language a => a -> String
prExtract l = concat [pr p (entrywords (f xs)) | (p,(xs,f)) <- Map.toList (paradigms l)]
where
pr p ([],[]) = []
pr p (s,xs)
-- do not include multi-word paradigms or numbers.
| contains_space (s:xs) || contains_digits (s:xs) = []
| otherwise = print_paradigm p (s:xs) (commonSubsequences (s:xs))
contains_space xs = or [ elem ' ' x | x <- xs]
contains_digits xs = or [ any isDigit x | x <- xs]
print_paradigm :: String -> [String] -> Set.Set String -> String
print_paradigm name xs@(x:_) set =
let vars = concat (intersperse "," (zipWith (++) (Set.toList set) (repeat ":char*"))) in
unlines
["paradigm " ++ name ++
if null vars then "" else " [" ++ vars ++ "]",
" = " ++ (print (transform [] x)),
" {", (splitLines (intersperse "|" (map (print . (transform [])) (nub xs)))), " };\n"
]
where print [] = []
print ((s,b):xs)
| b = concat [s,if_conc xs,print xs]
| otherwise = concat ["\"",s,"\"",if_conc xs,print xs]
if_conc [] = []
if_conc _ = "+"
splitLines [] = []
splitLines xs = case splitAt 6 xs of
(ys,[]) -> (" " ++ unwords ys)
(ys,zs) -> (" " ++ unwords ys ++ "\n") ++ splitLines zs
transform [] [] = []
transform (x:xs) [] = [(reverse (x:xs),False)]
transform ws (x:xs) = case [z | z <- reverse (inits (x:xs)),
Set.member z set] of
(y:_) | null ws -> (y,True):
transform [] (drop (length y) (x:xs))
(y:_) -> (reverse ws,False):(y,True):
transform [] (drop (length y) (x:xs))
_ -> transform (x:ws) xs
lookup_paradigm :: Language l => l -> IO()
lookup_paradigm l = do s <- getContents
let ls = head $ filter (not.null) (lines s)
putStrLn $ lparadigm $ (commas ls)
where
trim s = unwords $ words s
commas :: String -> [String]
commas [] = []
commas s = case span (/=',') s of
(x,[]) -> [trim x]
(x,(_:rest)) -> trim x:commas rest
lparadigm :: [String] -> String
lparadigm (x:xs) =
case span (/=':') x of
(x,[]) -> "[]"
(x,(_:pos)) ->
let ps = [p | (p,e) <- [(p,f [x]) | (p,(_,f)) <- Map.toList (paradigms l)], get_pos e == pos,all (\w -> elem w (snd (entrywords e))) xs] in
concat $ "[":(intersperse ",\n" ["\"" ++ p ++ "\"" | p <- ps])++["]"]
para = paradigms l
printErrors :: ParadigmErrors -> (Bool, Bool) -> IO()
printErrors _ (False,False) = return ()
printErrors (unknowns, wrong_arguments) (ub,wb) =
prStd (concat
[if ub then printUnknowns (Set.toList unknowns) else "",
if wb then printWrongArguments (Set.toList wrong_arguments) else ""])
where printUnknowns [] = "No undefined paradigms detected!\n\n"
printUnknowns xs =
"Undefined paradigms detected\n\n" ++ (prErrorTable xs) ++ "\n"
printWrongArguments [] = "No argument count mismatches detected!\n"
printWrongArguments xs =
"Argument count mismatches detected\n\n" ++
(prErrorTable (map fst xs))
check_lemma_duplication :: Dictionary -> IO()
check_lemma_duplication d = prStd $
case duplicated_lemma_id d of
[] -> "No lemma id duplications detected!\n"
xs -> "Lemma id duplications detected\n\n" ++
(prErrorTable xs)
-- | List paradigm names
paradigmNames :: Language a => a -> [String]
paradigmNames l = [ c ++ " \"" ++ unwords args ++ "\" ;" | (c,(args,_)) <- Map.toList (paradigms l)]
paradigmID :: Language a => a -> [String]
paradigmID l = [ c | (c,_) <- Map.toList (paradigms l)]
-- | Number of paradigms.
paradigmCount :: Language a => a -> Int
paradigmCount l = length $ Map.toList (paradigms l)
-- | Is input string a paradigm identifier?
isParadigm :: Language a => a -> String -> Bool
isParadigm l s = isJust $ Map.lookup s (paradigms l)
class App a where
app :: a -> [String] -> Entry
arity :: a -> Int
instance App Entry where
app e [] = e
app e xs = error $ "Too many arguments, got " ++ show (length xs) ++ " wanted 0"
arity _ = 0
instance App a => App (String -> a) where
app f ys@(x:xs) | length ys == arity f = app (f x) (xs)
| otherwise = error $ "Wrong number of arguments, got " ++ show (length ys) ++ " wanted " ++ show (arity f) ++ " in arguments: '" ++ (unwords ys) ++ "'"
arity f = 1 + arity (f undefined)
-- paradigm :: String -> (String, [String], [String] -> Entry)
paradigm :: (App a) => String -> [String] -> a -> (String, [String], [String] -> Dictionary.Entry)
paradigm id exs f = (id, exs, set_paradigm_id id . app f)
paradigm_h :: (App a) => String -> [String] -> a -> (String, [String], [String] -> Dictionary.Entry)
paradigm_h id exs f = (id, exs, \xs -> case xs of
[] -> set_paradigm_id id $ app f xs
(x:xs) -> set_head x $ set_paradigm_id id $ app f (x:xs))
-- paradigm_id :: (App a) => String -> [String] -> String -> a -> (String, [String], [String] -> Dictionary.Entry)
-- paradigm_id id exs p f = (id, exs, set_paradigm_id p . (app f))