1
1
mirror of https://github.com/anoma/juvix.git synced 2024-11-10 15:06:54 +03:00
juvix/app/Main.hs
2022-03-07 16:43:30 +01:00

321 lines
9.4 KiB
Haskell

{-# LANGUAGE ApplicativeDo #-}
module Main (main) where
import Control.Monad.Extra
import qualified MiniJuvix.Syntax.Concrete.Language as M
import qualified MiniJuvix.Syntax.Concrete.Parser as M
import qualified MiniJuvix.Syntax.Concrete.Scoped.Pretty.Ansi as M
import qualified MiniJuvix.Termination as T
import qualified MiniJuvix.Translation.ScopedToAbstract as A
import qualified MiniJuvix.Syntax.Concrete.Scoped.Pretty.Base as M
import qualified MiniJuvix.Termination.CallGraph as A
import qualified MiniJuvix.Syntax.Abstract.Pretty.Base as A
import qualified MiniJuvix.Syntax.Concrete.Scoped.Scoper as M
import MiniJuvix.Prelude hiding (Doc)
import Options.Applicative
import Options.Applicative.Help.Pretty
import Text.Show.Pretty hiding (Html)
import MiniJuvix.Syntax.Concrete.Scoped.Pretty.Html
import MiniJuvix.Syntax.Concrete.Scoped.Pretty.Base (defaultOptions)
import qualified MiniJuvix.Syntax.Abstract.Pretty.Ansi as A
import qualified MiniJuvix.Termination.CallGraph as T
data Command
= Scope ScopeOptions
| Parse ParseOptions
| Html HtmlOptions
| Calls CallsOptions
| CallGraph CallGraphOptions
data ScopeOptions = ScopeOptions
{ _scopeRootDir :: FilePath,
_scopeInputFiles :: [FilePath],
_scopeShowIds :: Bool,
_scopeInlineImports :: Bool
}
data ParseOptions = ParseOptions
{ _parseInputFile :: FilePath,
_parseNoPrettyShow :: Bool
}
data HtmlOptions = HtmlOptions
{ _htmlInputFile :: FilePath,
_htmlRecursive :: Bool,
_htmlTheme :: Theme
}
data CallsOptions = CallsOptions
{ _callsInputFile :: FilePath,
_callsShowIds :: Bool,
_callsFunctionNameFilter :: Maybe Text,
_callsShowDecreasingArgs :: A.ShowDecrArgs
}
data CallGraphOptions = CallGraphOptions
{ _graphInputFile :: FilePath,
_graphFunctionNameFilter :: Maybe Text
}
parseHtml :: Parser HtmlOptions
parseHtml = do
_htmlInputFile <- parseInputFile
_htmlRecursive <-
switch
( long "recursive"
<> help "export imported modules recursively"
)
_htmlTheme <- option (eitherReader parseTheme)
( long "theme"
<> metavar "THEME"
<> value Nord
<> showDefault
<> help "selects a theme: ayu (light); nord (dark)"
)
pure HtmlOptions {..}
where
parseTheme :: String -> Either String Theme
parseTheme s = case s of
"nord" -> Right Nord
"ayu" -> Right Ayu
_ -> Left $ "unrecognised theme: " <> s
parseCalls :: Parser CallsOptions
parseCalls = do
_callsInputFile <- parseInputFile
_callsShowIds <-
switch
( long "show-name-ids"
<> help "Show the unique number of each identifier"
)
_callsFunctionNameFilter <-
optional $ option str
( long "function"
<> short 'f'
<> help "Only shows the specified function"
)
_callsShowDecreasingArgs <-
option decrArgsParser
( long "show-decreasing-args"
<> short 'd'
<> value A.ArgRel
<> help "possible values: argument, relation, both"
)
pure CallsOptions {..}
where
decrArgsParser :: ReadM A.ShowDecrArgs
decrArgsParser = eitherReader $ \s ->
case map toLower s of
"argument" -> return A.OnlyArg
"relation" -> return A.OnlyRel
"both" -> return A.ArgRel
_ -> Left "bad argument"
parseCallGraph :: Parser CallGraphOptions
parseCallGraph = do
_graphInputFile <- parseInputFile
_graphFunctionNameFilter <-
optional $ option str
( long "function"
<> short 'f'
<> help "Only shows the specified function"
)
pure CallGraphOptions {..}
parseInputFile :: Parser FilePath
parseInputFile =
argument
str
( metavar "MINIJUVIX_FILE"
<> help "Path to a .mjuvix file"
)
parseParse :: Parser ParseOptions
parseParse = do
_parseInputFile <- parseInputFile
_parseNoPrettyShow <-
switch
( long "no-pretty-show"
<> help "Disable formatting of the Haskell AST"
)
pure ParseOptions {..}
parseScope :: Parser ScopeOptions
parseScope = do
_scopeRootDir <-
strOption
( long "rootDir"
<> short 'd'
<> metavar "DIR"
<> value "."
<> showDefault
<> help "Root directory"
)
_scopeInputFiles <-
some $ argument
str
( metavar "MINIJUVIX_FILE(s)"
<> help "Path to one ore more .mjuvix files"
)
_scopeShowIds <-
switch
( long "show-name-ids"
<> help "Show the unique number of each identifier"
)
_scopeInlineImports <-
switch
( long "inline-imports"
<> help "Show the code of imported modules next to the import statement"
)
pure ScopeOptions {..}
descr :: ParserInfo Command
descr =
info
(parseCommand <**> helper)
( fullDesc
<> progDesc "The MiniJuvix compiler."
<> headerDoc (Just headDoc)
<> footerDoc (Just foot)
)
where
headDoc :: Doc
headDoc = dullblue $ bold $ underline "MiniJuvix help"
foot :: Doc
foot = bold "maintainers: " <> "jan@heliax.dev; jonathan@heliax.dev"
parseCommand :: Parser Command
parseCommand =
hsubparser $
mconcat
[ commandParse,
commandScope,
commandHtml,
commandCalls,
commandGraph
]
where
commandParse :: Mod CommandFields Command
commandParse = command "parse" minfo
where
minfo :: ParserInfo Command
minfo =
info
(Parse <$> parseParse)
(progDesc "Parse a .mjuvix file")
commandHtml :: Mod CommandFields Command
commandHtml = command "html" minfo
where
minfo :: ParserInfo Command
minfo =
info
(Html <$> parseHtml)
(progDesc "Generate html for a .mjuvix file")
commandScope :: Mod CommandFields Command
commandScope = command "scope" minfo
where
minfo :: ParserInfo Command
minfo =
info
(Scope <$> parseScope)
(progDesc "Parse and scope a .mjuvix file")
commandCalls :: Mod CommandFields Command
commandCalls = command "calls" minfo
where
minfo :: ParserInfo Command
minfo =
info
(Calls <$> parseCalls)
(progDesc "Compute the calls table of a .mjuvix file")
commandGraph :: Mod CommandFields Command
commandGraph = command "graph" minfo
where
minfo :: ParserInfo Command
minfo =
info
(CallGraph <$> parseCallGraph)
(progDesc "Compute the complete call graph of a .mjuvix file")
mkScopePrettyOptions :: ScopeOptions -> M.Options
mkScopePrettyOptions ScopeOptions {..} =
M.defaultOptions
{ M._optShowNameId = _scopeShowIds,
M._optInlineImports = _scopeInlineImports
}
mkAbstractPrettyOptions :: CallsOptions -> A.Options
mkAbstractPrettyOptions CallsOptions {..} =
A.defaultOptions
{ A._optShowNameId = _callsShowIds,
A._optShowDecreasingArgs = _callsShowDecreasingArgs
}
parseModuleIO :: FilePath -> IO (M.Module 'M.Parsed 'M.ModuleTop)
parseModuleIO = fromRightIO id . M.runModuleParserIO
fromRightIO' :: (e -> IO ()) -> IO (Either e r) -> IO r
fromRightIO' pp = do
eitherM ifLeft return
where
ifLeft e = pp e >> exitFailure
fromRightIO :: (e -> Text) -> IO (Either e r) -> IO r
fromRightIO pp = fromRightIO' (putStrLn . pp)
go :: Command -> IO ()
go c = do
root <- getCurrentDirectory
case c of
Scope opts@ScopeOptions {..} -> do
forM_ _scopeInputFiles $ \scopeInputFile -> do
m <- parseModuleIO scopeInputFile
s <- fromRightIO' printErrorAnsi $ M.scopeCheck1IO root m
M.printPrettyCode (mkScopePrettyOptions opts) s
Parse ParseOptions {..} -> do
m <- parseModuleIO _parseInputFile
if _parseNoPrettyShow then print m else pPrint m
Html HtmlOptions {..} -> do
m <- parseModuleIO _htmlInputFile
s <- fromRightIO' printErrorAnsi $ M.scopeCheck1IO root m
genHtml defaultOptions _htmlRecursive _htmlTheme s
Calls opts@CallsOptions {..} -> do
m <- parseModuleIO _callsInputFile
s <- fromRightIO' printErrorAnsi $ M.scopeCheck1IO root m
a <- fromRightIO' putStrLn (return $ A.translateModule s)
let callMap0 = T.buildCallMap a
callMap = case _callsFunctionNameFilter of
Nothing -> callMap0
Just f -> T.filterCallMap f callMap0
opts' = mkAbstractPrettyOptions opts
A.printPrettyCode opts' callMap
putStrLn ""
CallGraph CallGraphOptions {..} -> do
m <- parseModuleIO _graphInputFile
s <- fromRightIO' printErrorAnsi $ M.scopeCheck1IO root m
a <- fromRightIO' putStrLn (return $ A.translateModule s)
let callMap0 = T.buildCallMap a
callMap = case _graphFunctionNameFilter of
Nothing -> callMap0
Just f -> T.filterCallMap f callMap0
opts' = A.defaultOptions
completeGraph = T.completeCallGraph callMap
recBehav = map T.recursiveBehaviour (T.reflexiveEdges completeGraph)
A.printPrettyCode opts' completeGraph
putStrLn ""
forM_ recBehav $ \r -> do
let n = M.renderPrettyCode M.defaultOptions $ A._recBehaviourFunction r
A.printPrettyCode A.defaultOptions r
putStrLn ""
case T.findOrder r of
Nothing -> putStrLn (n <> " Fails the termination checking")
Just (T.LexOrder k) -> putStrLn (n<> " Terminates with order " <> show (toList k))
putStrLn ""
main :: IO ()
main = execParser descr >>= go