{-# LANGUAGE DeriveDataTypeable #-} module Main (main) where import Data.Logic.Propositional import Control.Monad (when, unless) import Data.Char (isSpace, toLower) import System.Console.CmdArgs import System.IO data Command = Exit | Help | Pretty | Eval Expr | Error String data ProgramMode = ProgramMode { evaluate :: String , interactive :: Bool , pretty :: Bool } deriving (Show, Data, Typeable) programMode :: ProgramMode programMode = ProgramMode { evaluate = "" &= typ "EXPRESSION" &= help "Print the truth table for the given expression" , interactive = False &= help "Enter interactive mode" , pretty = False &= help "Use Unicode logic symbols" } &= summary "Hatt 1.0, (c) Benedict Eastaugh 2011" &= program "hatt" main :: IO () main = do opts <- cmdArgs programMode let expStr = evaluate opts interMode = interactive opts evalMode = (not . null) expStr printer = selectPrinter opts -- If the --evaluate flag is passed with an expression, print the -- truth table for that expression. when evalMode $ putStr (eval printer expStr) -- Unless the --evaluate flag is passed with an expression and -- interactive mode is NOT explicitly requested, terminate the -- program; otherwise, enter interactive mode. unless (evalMode && not interMode) $ putStrLn replIntroText >> repl opts repl :: ProgramMode -> IO () repl mode = do putStr "> " hFlush stdout cmd <- getLine case parseCommand cmd of Exit -> return () Help -> putStr replHelpText >> repl mode Pretty -> putStrLn ppMessage >> repl (mode {pretty = not isPretty}) (Eval expr) -> putStr (truthTableP printer expr) >> repl mode (Error err) -> putStrLn ("Error: " ++ err) >> repl mode where printer = selectPrinter mode isPretty = pretty mode ppMessage = (if isPretty then "Dis" else "En") ++ "abling pretty-printing." eval :: (Expr -> String) -> String -> String eval p str = case parseExpr "" str of Left err -> "Parse error at " ++ show err ++ "\n" Right expr -> truthTableP p expr parseCommand :: String -> Command parseCommand input = case cmd . words . dropWhile isSpace $ input of "" -> Error "you must enter an expression or a command." "exit" -> Exit "help" -> Help "pretty" -> Pretty _ -> eval_ input where cmd [] = "" cmd ws = map toLower . head $ ws eval_ str = case parseExpr "" str of Left err -> Error $ "parse error at " ++ show err Right expr -> Eval expr replIntroText :: String replIntroText = unwords [ "Entering interactive mode." , "Type `help` if you don't know what to do!" ] replHelpText :: String replHelpText = unlines [ "Hatt's interactive mode has several commands." , "" , "help" , " Print this help text." , "" , "pretty" , " Pretty-print expressions using Unicode logic symbols. Only employ this" , " option if your console is Unicode-aware. If pretty-printing is already" , " enabled, using this command will disable it." , "" , "exit" , " Quit the program." , "" , "If you don't type in a command, the program will assume you're writing a" , "logical expression to be evaluated and attempt to parse it." , "" , "For example, if you enter \"(A -> B)\" at the prompt, Hatt will print the" , "truth table for that expression. Here's an example console session." , "" , " > (A | B)" , " A B | (A ∨ B)" , " -------------" , " T T | T" , " T F | T" , " F T | T" , " F F | F" , " > foobar" , " Error: parse error at (line 1, column 1):" , " unexpected \"f\"" , " expecting white space, \"(\" or \"~\"" , " > exit" , "" , "If none of this makes any sense, try reading the README file." ] selectPrinter :: ProgramMode -> Expr -> String selectPrinter m = if pretty m then show else showAscii