--import CharArray #ifdef _DEBUG import Language.Eq.Renderer.Sexpr #endif import Control.Monad import System.Environment import System.Exit import System.IO import qualified System.IO as Io import System.Console.GetOpt import Data.List( find, intersperse, foldl' ) import Data.Maybe( fromMaybe ) import qualified Data.Map as Map import Language.Eq import Language.Eq.CharArray import Language.Eq.Repl -- Debugging {-import EqManips.Renderer.CharRender-} data Flag = Output | Input | Unicode | SupportedFunction | SupportedOperators | SupportedPreprocLanguages -- for plotting | ContourPlotting | PlotWidth | PlotHeight | XBeg | XEnd | YBeg | YEnd | XLogScale | YLogScale | DrawXaxis | DrawYaxis | Draw0axis | NoDrawXLabel | NoDrawYLabel | XLabelPrecision | YLabelPrecision | XLabelSpacing | YLabelSpacing | PlotTitle deriving (Eq, Show) version :: String version = "1.1" commonOption :: [OptDescr (Flag, String)] commonOption = [ Option "o" ["output"] (ReqArg ((,) Output) "FILE") "output FILE" , Option "f" ["file"] (ReqArg ((,) Input) "FILE") "input FILE, use - for stdin" , Option "u" ["unicode"] (NoArg (Unicode, "")) "Output with unicode character set" ] askingOption :: [OptDescr (Flag, String)] askingOption = [ Option "" ["functions"] (NoArg (SupportedFunction,"")) "Ask for defined function list" , Option "" ["operators"] (NoArg (SupportedOperators,"")) "Ask for defined operator list" , Option "" ["languages"] (NoArg (SupportedPreprocLanguages,"")) "Ask for supported languages for the preprocessor" ] plotOption :: [OptDescr (Flag, String)] plotOption = [ Option "c" ["contour"] (NoArg (ContourPlotting,"")) "Do a contour plot instead of a regular plot" , Option "x" ["xBegin"] (ReqArg ((,) XBeg) "XBEG") "Beginning of plot (x), float" , Option "" ["xe", "xEnd"] (ReqArg ((,) XEnd) "XEND") "End of plot (x), float" , Option "y" ["yBegin"] (ReqArg ((,) YBeg) "YBEG") "Beginning of plot (y), float" , Option "" ["ye", "yEnd"] (ReqArg ((,) YEnd) "YEnd") "End of plot (y), float" , Option "w" ["width"] (ReqArg ((,) PlotWidth) "Width") "Plotting width, int" , Option "h" ["height"] (ReqArg ((,) PlotHeight) "height") "Plotting height, int" , Option "" ["lx", "logwidth"] (NoArg (XLogScale,"")) "Plot with a logrithmic scale in x" , Option "" ["ly", "logheight"] (NoArg (YLogScale,"")) "Plot with a logrithmic scale in y" , Option "" ["ax", "xaxis"] (NoArg (DrawXaxis,"")) "Draw the X axis on the graph" , Option "" ["ay", "yaxis"] (NoArg (DrawYaxis,"")) "Draw the Y axis on the graph" , Option "" ["a0", "zeroaxis"] (NoArg (Draw0axis,"")) "Draw the 0 axis on the graph" , Option "" ["nlx", "nolabelx"] (NoArg (NoDrawXLabel,"")) "Don't draw label on x Axis" , Option "" ["nly", "nolabely"] (NoArg (NoDrawYLabel,"")) "Don't draw label on Y Axis" , Option "" ["lpx", "xlabelprecision"] (ReqArg ((,) XLabelPrecision) "p") "Display label on x axis with 'p' decimals" , Option "" ["lpy", "ylabelprecision"] (ReqArg ((,) YLabelPrecision) "p") "Display label on y axis with 'p' decimals" , Option "" ["spx", "labelspacingx"] (ReqArg ((,) XLabelSpacing) "s") "Put a label evry 's' chars on x axis" , Option "" ["spy", "labelspacingy"] (ReqArg ((,) YLabelSpacing) "s") "Put a label evry 's' chars on y axis" , Option "t" ["title"] (ReqArg ((,) PlotTitle) "t") "Add a title t under the graph" ] preparePlotConf :: PlotConf -> (Flag, String) -> PlotConf preparePlotConf conf (ContourPlotting, _) = conf { mode = CountourPlot } preparePlotConf conf (PlotWidth, val) = conf { xDim = (xDim conf){ projectionSize = read val } } preparePlotConf conf (PlotHeight, val) = conf { yDim = (yDim conf){ projectionSize = read val }} preparePlotConf conf (XBeg, val) = conf { xDim = (xDim conf){ minVal = read val }} preparePlotConf conf (XEnd, val) = conf { xDim = (xDim conf){ maxVal = read val }} preparePlotConf conf (YBeg, val) = conf { yDim = (yDim conf){ minVal = read val }} preparePlotConf conf (YEnd, val) = conf { yDim = (yDim conf){ maxVal = read val }} preparePlotConf conf (XLogScale, _) = conf { xDim = (xDim conf){ scaling = Logarithmic } } preparePlotConf conf (YLogScale, _) = conf { yDim = (yDim conf){ scaling = Logarithmic } } preparePlotConf conf (DrawXaxis, _) = conf { xDim = (xDim conf){ drawAxis = True } } preparePlotConf conf (DrawYaxis, _) = conf { yDim = (yDim conf){ drawAxis = True } } preparePlotConf conf (Draw0axis, _) = conf { draw0Axis = True } preparePlotConf conf (NoDrawXLabel, _) = conf { xDim = (xDim conf){ labelEvery = Nothing } } preparePlotConf conf (NoDrawYLabel, _) = conf { yDim = (yDim conf){ labelEvery = Nothing } } preparePlotConf conf (XLabelSpacing, val) = conf { xDim = (xDim conf){ labelEvery = Just $ read val} } preparePlotConf conf (YLabelSpacing, val) = conf { yDim = (yDim conf){ labelEvery = Just $ read val} } preparePlotConf conf (XLabelPrecision, val) = conf { xDim = (xDim conf){ labelPrecision = read val} } preparePlotConf conf (YLabelPrecision, val) = conf { yDim = (yDim conf){ labelPrecision = read val} } preparePlotConf conf (PlotTitle, val) = conf { graphTitle = Just val } preparePlotConf conf _ = conf preprocOptions :: [OptDescr (Flag, String)] preprocOptions = commonOption formatOption :: [OptDescr (Flag, String)] formatOption = commonOption -- | Helper function to get file names for input/output getInputOutput :: [(Flag, String)] -> [String] -> (IO String, IO Handle) getInputOutput opts args = ( inputFile , do o <- outputFile hSetEncoding o utf8 return o) where outputFile = maybe (return stdout) (flip openFile WriteMode) (lookup Output opts) inputFile = maybe (return $ head args) infiler (lookup Input opts) infiler "-" = Io.hGetContents stdin infiler f = Io.readFile f filterCommand :: (String -> String) -> [String] -> IO Bool filterCommand transformator args = do text <- input output <- outputFile Io.putStr text Io.putStr "==========================================\n" Io.hPutStrLn output $ transformator text Io.putStr "==========================================\n\n" hClose output return True where (opt, rest, _) = getOpt Permute formatOption args (input, outputFile) = getInputOutput opt rest -- | Command which just format an equation -- without affecting it's form. formatCommand :: (Conf -> Formula TreeForm -> String) -> [String] -> IO Bool formatCommand formulaFormater args = do formulaText <- input let formula = perfectParse formulaText output <- outputFile either (parseErrorPrint output) (\formula' -> do Io.hPutStrLn output . formulaFormater conf $ treeIfyFormula formula' hClose output return True) formula where (opt, rest, _) = getOpt Permute formatOption args (input, outputFile) = getInputOutput opt rest conf = defaultRenderConf{ useUnicode = Unicode `lookup` opt /= Nothing } printErrors :: [(Formula TreeForm, String)] -> IO () printErrors = mapM_ (\(f,s) -> do Io.putStrLn s Io.putStrLn $ formatFormula defaultRenderConf f) parseErrorPrint :: (Show a) => Handle -> a -> IO Bool parseErrorPrint finalFile err = do Io.hPutStr finalFile "Error : " Io.hPutStr finalFile $ show err hClose finalFile return False -- | Give the user some information about the defined -- elements. This help cannot lie =) introspect :: [String] -> IO Bool introspect args = do when ((SupportedFunction, "") `elem` opts) (do Io.putStrLn "Supported functions :" Io.putStrLn "=====================" Io.putStrLn "Built-in functions :" Io.putStrLn "--------------------" mapM_ (Io.putStrLn . ('\t':) . fst) $ unaryFunctions ++ metaFunctionList mapM_ Io.putStrLn [ '\t': name ++ '(' : (concat . intersperse ", " $ map fst params) ++ ")" | (name, (_,_,params,_)) <- multiParamsFunctions] Io.putStrLn "\nBase library functions :" Io.putStrLn "------------------------" mapM_ (Io.putStrLn . ('\t':)) $ Map.keys defaultSymbolTable ) when ((SupportedOperators, "") `elem` opts) (do Io.putStrLn "Supported operators : " Io.putStrLn "=====================" Io.putStrLn "\nBinary operators (Priority - name - description)" Io.putStrLn "------------------------------------------------" let names = [n | (_,(_,n,_)) <- binopDefs] maxName = maximum $ map length names binFormat (prio, name, descr) = '\t': show prio ++ " - " ++ name ++ replicate (maxName - length name) ' ' ++ " - " ++ descr mapM_ (Io.putStrLn . binFormat . snd) binopDefs Io.putStrLn "\nUnary operators (name - description)" Io.putStrLn "------------------------------------" mapM_ (Io.putStrLn . (\(_, n, d) -> '\t' : n ++ " - " ++ d)) realUnopOperators) when ((SupportedPreprocLanguages, "") `elem` opts) (do Io.putStrLn "Supported languages for preprocessing :" Io.putStrLn "=======================================" let maxi = maximum [ length n | (n, _) <- kindAssociation ] preprocFormat (ext, lang) = '\t' : ext ++ replicate (maxi - length ext) ' ' ++ " - " ++ languageName lang mapM_ (Io.putStrLn . preprocFormat) kindAssociation ) return True where (opts, _, _) = getOpt Permute askingOption args preprocessCommand :: [String] -> IO Bool preprocessCommand args = if inName == "" then do print "Error, no input name given" return False else do outFile <- processFile inName Io.writeFile outName outFile return True where (opts, _, _) = getOpt Permute preprocOptions args inName = fromMaybe "" (lookup Input opts) outName = fromMaybe inName (lookup Output opts) transformParseFormula :: (Formula ListForm -> EqContext (Formula ListForm)) -> [String] -> IO Bool transformParseFormula operation args = do formulaText <- input finalFile <- outputFile let formulaList = parseProgramm formulaText either (parseErrorPrint finalFile) (\formulal -> do #ifdef _DEBUG mapM_ (\a-> do Io.hPutStr finalFile $ sexprRender a Io.hPutStr finalFile "\n") formulal hFlush finalFile #endif let rez = performLastTransformationWithContext defaultSymbolTable $ mapM operation formulal #ifdef _DEBUG Io.hPutStrLn finalFile "\n####### #########" printTrace finalFile rez Io.hPutStrLn finalFile "####### #########\n" Io.hPutStrLn finalFile . show $ result rez Io.hPutStrLn finalFile . sexprRender $ result rez #endif printErrors $ errorList rez Io.hPutStr finalFile . formatFormula conf . treeIfyFormula $ result rez hClose finalFile return . null $ errorList rez) formulaList where (opt, rest, _) = getOpt Permute formatOption args (input, outputFile) = getInputOutput opt rest conf = defaultRenderConf{ useUnicode = Unicode `lookup` opt /= Nothing } plotCommand :: [String] -> IO Bool plotCommand args = do formulaText <- input finalFile <- outputFile let formulaList = parseProgramm formulaText either (parseErrorPrint finalFile) (\formulal -> do case plotFunction plotConf . unTagFormula $ head formulal of Left err -> do Io.hPutStr finalFile err hClose finalFile return False Right v -> do Io.hPutStr finalFile $ charArrayToString v return True) formulaList where (opt, rest, _) = getOpt Permute (commonOption ++ plotOption) args plotConf = foldl' preparePlotConf defaultPlotConf opt (input, outputFile) = getInputOutput opt rest printVer :: IO () printVer = Io.putStrLn $ "EqManips " ++ version ++ " command list" helpCommand :: [String] -> IO Bool helpCommand [] = do printVer Io.putStrLn "" mapM_ printCommand commandList Io.putStrLn "" return True where maxCommandLen = 4 + maximum [ length c | (c,_,_,_) <- commandList ] spaces = repeat ' ' printCommand (com, hlp, _, _) = Io.putStrLn $ ' ' : com ++ take (maxCommandLen - length com) spaces ++ hlp helpCommand (x:_) = case find (\(x',_,_,_) -> x' == x) commandList of Just (_, hlp, _, options) -> do printVer Io.putStrLn $ usageInfo hlp options return True Nothing -> do Io.putStrLn $ "Unknown command " ++ x return False #ifdef _GHCI_DEBUG transformParseDebug :: (Formula ListForm -> EqContext (Formula ListForm)) -> String -> IO Bool transformParseDebug operation formulaText = do let formulaList = parseProgramm formulaText either (parseErrorPrint stdout) (\formulal -> do let rez = performLastTransformationWithContext defaultSymbolTable $ mapM operation formulal #ifdef _DEBUG mapM (\a-> do hPutStr stdout $ sexprRender a hPutStr stdout "\n") formulal Io.hPutStrLn stdout "\n####### #########" printTrace stdout rez Io.hPutStrLn stdout "####### #########\n" Io.hPutStrLn stdout . sexprRender $ result rez #endif printErrors $ errorList rez Io.hPutStr stdout . formatFormula . treeIfyFormula $ result rez return True ) formulaList evalDebug :: String -> IO Bool evalDebug = transformParseDebug evalGlobalLossyStatement #endif commandList :: [(String, String, [String] -> IO Bool, [OptDescr (Flag, String)])] commandList = [ ("cleanup", "Perform trivial simplification on formula" , transformParseFormula (return . cleanup), commonOption) , ("eval", "Try to evaluate/reduce the formula" , transformParseFormula evalGlobalLossyStatement, commonOption) , ("exacteval", "Try to evaluate/reduce the formula, without performing lossy operation" , transformParseFormula evalGlobalLosslessStatement, commonOption) , ("format", "Load and display the formula in ASCII Art" , formatCommand formatFormula, commonOption) , ("interactive", "Invoke Eq as an interactive prompt", (\_ -> do repl evalGlobalLossyStatement return True), []) , ("latexify", "Translate the formula into latex" , formatCommand latexRender, commonOption) , ("mathmlify", "Translate the formula into MathML" , formatCommand mathmlRender, commonOption) , ("toraw", "Show internal representation of formula" , formatCommand $ const show, commonOption) , ("help", "Ask specific help for a command, or this" , helpCommand, []) , ("preprocess", "Parse a source file and apply inline action in it" , preprocessCommand, commonOption) , ("demathmlify", "Try to transform a MathML Input to EQ language" , filterCommand mathMlToEqLang', commonOption) , ("show" , "Try to retrieve some information about supported options" , introspect, askingOption) , ("plot", "Print an ASCII-art plot of the given function" , plotCommand, commonOption ++ plotOption) ] reducedCommand :: [(String, [String] -> IO Bool)] reducedCommand = map (\(n,_,a,_) -> (n,a)) commandList main :: IO () main = do #ifdef _DEBUG putStrLn "Debug build" #endif args <- getArgs if null args then error "No command given, try the help command" else case lookup (head args) reducedCommand of Just c -> c (tail args) >>= systemReturn Nothing -> error $ "Unknown command " ++ head args where systemReturn True = exitWith ExitSuccess systemReturn False = exitWith $ ExitFailure 1