packages feed

cap-1.0: src/Language/Cap/Main.hs

module Main where

import           Language.Cap.Interpret.Program
import           Language.Cap.Interpret.Parse
import qualified Language.Cap.Interpret.Pretty  as Pretty
import           Language.Cap.Debug.TraceMode
import           Language.Cap.Debug.Trace
import qualified Language.Cap.Debug.Algorithmic as Algorithmic
import           Language.Cap.Debug.EDT
import           Language.Cap.Debug.FDT
import qualified Language.Cap.Debug.Dotty       as Dotty

import System
import System.IO
import qualified Data.Map as M
import Data.List

-- | Interpreter and debugger for the TART language and trace.
main = do args <- System.getArgs
          let opts = options args
          hSetBuffering stdout NoBuffering
          hSetBuffering stdin LineBuffering
          if fileType opts == Trace
            then do catFile <- readFile $ fileName opts
                    trace (mode opts) (read catFile)
            else do capFile <- readFile $ fileName opts
                    interpretLoop (moduleName (fileName opts))
                                  (parseProgram capFile)
                                  []

-- | Event loop for the interactive interpretter
interpretLoop :: String -> Program -> [Graph] -> IO ()
interpretLoop name p xs =
  do putStr (name ++ " > ")
     x <- getLine
     runCommand name p xs x

-- | Understands a command from the user and does the appropriate thing.
runCommand :: String -> Program -> [Graph] -> String -> IO ()
runCommand name p xs ":q" = return ()
runCommand name p xs ":w" = do writeFile (name ++ ".pmt") (show xs)
                               interpretLoop name p xs
runCommand name p xs ":p" = do writeDots 1 name xs
                               interpretLoop name p xs
runCommand name p xs ":prog" = do putStrLn $ showProg p
                                  interpretLoop name p xs
runCommand name p xs ":s"  = do trace Stat xs
                                interpretLoop name p xs
runCommand name p xs (':':c) =
  case c of
    "ad"            -> do trace (AlgorithmicDebug (FDT Index)) xs
                          interpretLoop name p xs
    ('a':'d':' ':x) ->
      case x of
        "edt"           -> do trace (AlgorithmicDebug EDT) xs
                              interpretLoop name p xs
        ('f':'d':'t':y) -> 
          case y of
            " -o"  -> do trace (AlgorithmicDebug (FDT OnTheFly)) xs
                         interpretLoop name p xs
            " -i"  -> do trace (AlgorithmicDebug (FDT Index)) xs
                         interpretLoop name p xs
            " -if" -> do trace (AlgorithmicDebug (FDT IndexFunctions)) xs
                         interpretLoop name p xs
        _               -> do trace (AlgorithmicDebug (FDT Index)) xs
                              interpretLoop name p xs
    _               -> do putStrLn "Unknown command"
                          interpretLoop name p xs
runCommand name p xs l = do let g = interpret p (parseTerm l)
                            putStrLn $ Pretty.pretty $ nodeResult g ""
                            interpretLoop name p (g:xs)

writeDots :: Int -> String -> [Graph] -> IO ()
writeDots _ _ [] = return ()
writeDots n name (x:xs) =
  do writeFile (name ++ (show n) ++ ".dot") (Dotty.pretty x)
     writeDots (n + 1) name xs

{- | Trace a given graph in a specific node.  This generates a String -> String
     function that can be used by interact to create an interactive session.
-}
trace :: TraceMode -> [Graph] -> IO ()
trace Stat g = putStrLn $ concatMap stats g
trace (AlgorithmicDebug m) g =
  do putStrLn (concat (zipWith (\x y -> x ++ ".\t" ++ y ++ "\n")
                               (map show [1..])
                               (map ((flip dispReduction) "") g)))
     x <- getLine
     let reductionNum = (read x) :: Int
     let numReductions = length g
     if reductionNum > 0 && reductionNum <= numReductions
       then case m of 
              EDT ->
                Algorithmic.debug (g !! (reductionNum - 1)) buildEDT edtQuestion
              (FDT m) ->
                Algorithmic.debug (g !! (reductionNum - 1))
                                  buildFDT
                                  (fdtQuestion m)
       else putStrLn "Error: Reduction does not exist"

-- | Formats the program given to it.  Rules are grouped together according to
--   function symbol, and output with blank lines seperating functions
showProg :: Program -> String
showProg = concat . intersperse "\n" . map (concatMap showRule) . groupRules

-- | Takes a program and groups the rules by function symbol.
groupRules :: Program -> [Program]
groupRules [] = []
groupRules (r:rs) =
  groupRules' (r:rs) (functionName r) []
  where
    groupRules' :: Program -> String -> Program -> [Program]
    groupRules' [] _ _ = []
    groupRules' (r:rs) n g =
      if functionName r == n then groupRules' rs n (g ++ [r])
                             else (g:(groupRules' (r:rs) (functionName r) []))


-- | Generate statistics for a specific program run
stats :: Graph -> String
stats g =
     "Computation: " ++ dispReduction g ""
  ++ " |  Number of nodes: " ++ show (numNodes g) ++ "\n"
  ++ " |  Number of applications: " ++ show (numApps g) ++ "\n"
  ++ " |  Number of reductions: " ++ show (numReductions g) ++ "\n"
  ++ "------------\n"

{- | Strips the extension from the file name.
-}
moduleName :: String -> String
moduleName n
  | ".cap" `isSuffixOf` n = take (length n - 4) n
  | ".cat" `isSuffixOf` n = take (length n - 4) n
  | otherwise             = n