-- -- Copyright (c) 2005 Lennart Augustsson -- See LICENSE for licensing details. -- module Main(main) where import Data.Char(isAlpha, isSpace, isAlphaNum) import Data.List(sortBy, nub, intersperse) import Data.Ratio import Text.ParserCombinators.ReadP import Control.Monad(when) import Control.Monad.Error() import System.IO import System.Exit import System.Environment import REPL import LJT --import MJ import HTypes import HCheck(htCheckEnv, htCheckType) import Help main :: IO () main = do args <- getArgs let decodeOptions (('-':cs) : as) st = decodeOption cs >>= \f -> decodeOptions as (f False st) decodeOptions (('+':cs) : as) st = decodeOption cs >>= \f -> decodeOptions as (f True st) decodeOptions as st = return (as, st) decodeOption cs = case [ set | (cmd, _, _, set) <- options, isPrefix cs cmd ] of [] -> do usage; exitWith (ExitFailure 1) set : _ -> return set (args', state) <- decodeOptions args startState case args' of [] -> repl (hsGenRepl state) _ -> loop state args' where loop _ [] = return () loop s (a:as) = do putStrLn $ "-- loading file " ++ a (q, s') <- loadFile s a if q then return () else loop s' as usage :: IO () usage = putStrLn "Usage: djinn [option ...] [file ...]" hsGenRepl :: State -> REPL State hsGenRepl state = REPL { repl_init = inIt state, repl_eval = eval, repl_exit = exit } data State = State { synonyms :: [(HSymbol, ([HSymbol], HType, HKind))], axioms :: [(HSymbol, HType)], classes :: [ClassDef], multi :: Bool, sorted :: Bool, debug :: Bool, cutOff :: Int } deriving (Show) startState :: State startState = State { synonyms = syns, classes = clss, axioms = [], multi = False, sorted = True, debug = False, cutOff = 100 } where syns = either (const $ error "Bad initial environment") id $ htCheckEnv $ reverse [ ("()", ([], HTUnion [("()",[])], undefined)), ("Either", (["a","b"], HTUnion [("Left", [HTVar "a"]), ("Right", [HTVar "b"])], undefined)), ("Maybe", (["a"], HTUnion [("Nothing", []), ("Just", [HTVar "a"])], undefined)), ("Bool", ([], HTUnion [("False", []), ("True", [])], undefined)), ("Void", ([], HTUnion [], undefined)), ("Not", (["x"], htNot "x", undefined)) ] clss = [("Eq", (["a"], [("==", a `HTArrow` (a `HTArrow` HTCon "Bool"))]))] a = HTVar "a" version :: String version = "version 2008-01-18" inIt :: State -> IO (String, State) inIt state = do putStrLn $ "Welcome to Djinn " ++ version ++ "." putStrLn $ "Type :h to get help." return ("Djinn> ", state) eval :: State -> String -> IO (Bool, State) eval s line = case filter (null . snd) (readP_to_S pCmd line) of [] -> do putStrLn $ "Cannot parse command" return (False, s) (cmd, "") : _ -> runCmd s cmd _ -> error "eval" exit :: State -> IO () exit _s = do putStrLn "Bye." return () type Context = (HSymbol, [HType]) type ClassDef = (HSymbol, ([HSymbol], [Method])) data Cmd = Help Bool | Quit | Add HSymbol HType | Query HSymbol [Context] HType | Del HSymbol | Load HSymbol | Noop | Env | Type (HSymbol, ([HSymbol], HType, HKind)) | Set (State -> State) | Clear | Class ClassDef pCmd :: ReadP Cmd pCmd = do skipSpaces let adds (':':s) p = do schar ':'; pPrefix (takeWhile (/= ' ') s); c <- p; skipSpaces; return c adds _ p = do c <- p; skipSpaces; return c cmd <- foldr1 (+++) [ adds s p | (s, _, p) <- commands ] skipSpaces return cmd pPrefix :: String -> ReadP String pPrefix s = do skipSpaces cs <- look let w = takeWhile isAlpha cs if isPrefix w s then string w else pfail isPrefix :: String -> String -> Bool isPrefix p s = not (null p) && length p <= length s && take (length p) s == p runCmd :: State -> Cmd -> IO (Bool, State) runCmd s Noop = return (False, s) runCmd s (Help verbose) = do putStr $ helpText ++ unlines (map getHelp commands) ++ getSettings s when verbose $ putStr verboseHelp return (False, s) runCmd s Quit = return (True, s) runCmd s (Load f) = loadFile s f runCmd s (Add i t) = case htCheckType (synonyms s) t of Left msg -> do putStrLn $ "Error: " ++ msg; return (False, s) Right _ -> return (False, s { axioms = (i, t) : axioms s }) runCmd _ Clear = return (False, startState) runCmd s (Del i) = return (False, s { axioms = filter ((i /=) . fst) (axioms s) , synonyms = filter ((i /=) . fst) (synonyms s) , classes = filter ((i /=) . fst) (classes s) }) runCmd s Env = do -- print s let tname t = if isHTUnion t then "data" else "type" showd (HTUnion []) = "" showd t = " = " ++ show t mapM_ (\ (i, (vs, t, _)) -> putStrLn $ tname t ++ " " ++ unwords (i:vs) ++ showd t) (reverse $ synonyms s) mapM_ (\ (i, t) -> putStrLn $ i ++ " :: " ++ show t) (reverse $ axioms s) mapM_ (putStrLn . showClass) (reverse $ classes s) return (False, s) runCmd s (Type syn) = do case htCheckEnv (syn : synonyms s) of Left msg -> do putStrLn $ "Error: " ++ msg; return (False, s) Right syns -> return (False, s { synonyms = syns }) runCmd s (Set f) = return (False, f s) runCmd s (Query i ctx g) = case htCheckType (synonyms s) g >> mapM (ctxLookup (classes s)) ctx of Left msg -> do putStrLn $ "Error: " ++ msg; return (False, s) Right mss -> do let form = hTypeToFormula (synonyms s) g env = [ (Symbol v, hTypeToFormula (synonyms s) t) | (v, t) <- axioms s ] ++ ctxEnv ctxEnv = [ (Symbol v, hTypeToFormula (synonyms s) t) | ms <- mss, (v, t) <- ms ] mpr = prove (multi s || sorted s) env form when (debug s) $ putStrLn ("*** " ++ show form) case mpr of [] -> do putStrLn $ "-- " ++ i ++ " cannot be realized." return (False, s) ps -> do let f p = let c = termToHClause i p bvs = getBinderVars c r = if null bvs then (0, 0) else (length (filter (== "_") bvs) % length bvs, length bvs) in (r, c) e:es = nub $ if sorted s then map snd $ sortBy (\ (x,_) (y,_) -> compare x y) $ map f $ take (cutOff s) ps else map (termToHClause i) $ take (cutOff s) ps pr = putStrLn . hPrClause sctx = if null ctx then "" else showContexts ctx ++ " => " when (debug s) $ putStrLn ("+++ " ++ show (head ps)) putStrLn $ i ++ " :: " ++ sctx ++ show g pr e when (multi s) $ mapM_ (\ x -> putStrLn "-- or" >> pr x) es return (False, s) runCmd s (Class c) = do return (False, s { classes = c : classes s }) loadFile :: State -> String -> IO (Bool, State) loadFile s name = do file <- readFile name evalCmds s $ lines $ stripComments file stripComments :: String -> String stripComments "" = "" stripComments ('-':'-':cs) = skip cs where skip "" = "" skip s@('\n':_) = stripComments s skip (_:s) = skip s stripComments (c:cs) = c : stripComments cs showClass :: ClassDef -> String showClass (c, (as, ms)) = "class " ++ showContext (c, map HTVar as) ++ " where " ++ concat (intersperse "; " $ map sm ms) where sm (i, t) = pp i ++ " :: " ++ show t pp i@(ch:_) | not (isAlphaNum ch) = "(" ++ i ++ ")" pp i = i showContext :: Context -> String showContext (c, as) = show $ foldl HTApp (HTCon c) as showContexts :: [Context] -> String showContexts [] = "" showContexts cs = "(" ++ concat (intersperse ", " $ map showContext cs) ++ ")" ctxLookup :: [ClassDef] -> Context -> Either String [Method] ctxLookup clss (c, as) = case lookup c clss of Nothing -> Left $ "Class not found: " ++ c Just (ps, ms) -> Right [(m, substHT (zip ps as) t) | (m, t) <- ms ] evalCmds :: State -> [String] -> IO (Bool, State) evalCmds state [] = return (False, state) evalCmds state (l:ls) = do qs@(q, state') <- eval state l if q then return qs else evalCmds state' ls commands :: [(String, String, ReadP Cmd)] commands = [ (":clear", "Clear the envirnment", return Clear), (":delete ", "Delete from environment.", pDel), (":environment", "Show environment", return Env), (":help", "Print this message.", return (Help False)), (":load ", "Load a file", pLoad), (":quit", "Quit program.", return Quit), (":set