1
1
mirror of https://github.com/anoma/juvix.git synced 2025-01-07 16:22:14 +03:00
juvix/app/Main.hs

248 lines
11 KiB
Haskell
Raw Normal View History

2022-01-18 14:25:42 +03:00
module Main (main) where
import App
import CLI
import Commands.Termination as Termination
2022-04-05 20:57:21 +03:00
import Control.Exception qualified as IO
2022-01-21 11:50:37 +03:00
import Control.Monad.Extra
import Data.HashMap.Strict qualified as HashMap
2022-04-07 13:53:05 +03:00
import MiniJuvix.Pipeline
import MiniJuvix.Prelude hiding (Doc)
2022-04-11 14:08:37 +03:00
import MiniJuvix.Prelude.Pretty hiding (Doc)
import MiniJuvix.Syntax.Abstract.InfoTable qualified as Abstract
import MiniJuvix.Syntax.Abstract.Language qualified as Abstract
2022-04-11 14:23:55 +03:00
import MiniJuvix.Syntax.Abstract.Pretty qualified as Abstract
2022-04-07 13:49:08 +03:00
import MiniJuvix.Syntax.Concrete.Parser qualified as Parser
import MiniJuvix.Syntax.Concrete.Scoped.Highlight qualified as Highlight
2022-04-07 13:49:08 +03:00
import MiniJuvix.Syntax.Concrete.Scoped.InfoTable qualified as Scoper
2022-04-11 14:08:37 +03:00
import MiniJuvix.Syntax.Concrete.Scoped.Pretty qualified as Scoper
import MiniJuvix.Syntax.Concrete.Scoped.Pretty.Html
2022-04-07 13:49:08 +03:00
import MiniJuvix.Syntax.Concrete.Scoped.Scoper qualified as Scoper
import MiniJuvix.Syntax.MicroJuvix.MicroJuvixArityResult qualified as MicroArity
2022-04-11 14:08:37 +03:00
import MiniJuvix.Syntax.MicroJuvix.Pretty qualified as Micro
import MiniJuvix.Syntax.MicroJuvix.TypeChecker qualified as MicroTyped
import MiniJuvix.Syntax.MiniHaskell.Pretty qualified as MiniHaskell
import MiniJuvix.Syntax.MonoJuvix.Pretty qualified as Mono
import MiniJuvix.Termination qualified as Termination
2022-04-05 20:57:21 +03:00
import MiniJuvix.Translation.AbstractToMicroJuvix qualified as Micro
import MiniJuvix.Translation.MicroJuvixToMonoJuvix qualified as Mono
Add C code generation backend (#68) * [cbackend] Adds an AST for C This should cover enough C to implement the microjuvix backend. * [cbackend] Add C serializer using language-c library We may decide to write our own serializer for the C AST but this demonstrates that the C AST is sufficient at least. * [cbackend] Declarations will always be typed * [cbackend] Add CPP support to AST * [cbackend] Rename some names for clarity * [cbackend] Add translation of InductiveDef to C * [cbackend] Add CLI for C backend * [cbackend] Add stdbool.h to file header * [cbackend] Allow Cpp and Verbatim code inline * [cbackend] Add a newline after printing C * [cbackend] Support foreign blocks * [cbackend] Add support for axioms * [cbackend] Remove code examples * [cbackend] wip FunctionDef including Expressions * [parser] Support esacping '}' inside a foreign block * [cbackend] Add support for patterns in functions * [cbackend] Add foreign C support to HelloWorld.mjuvix * hlint fixes * More hlint fixes not picked up by pre-commit * [cbackend] Remove CompileStatement from MonoJuvix * [cbackend] Add support for compile blocks * [cbackend] Move compileInfo extraction to MonoJuvixResult * [minihaskell] Fix compile block support * [chore] Remove ununsed isBackendSupported function * [chore] Remove unused imports * [cbackend] Use a Reader for pattern bindings * [cbackend] Fix compiler warnings * [cbackend] Add support for nested patterns * [cbackend] Use functions to instantiate argument names * [cbackend] Add non-exhaustive pattern error message * [cbackend] Adds test for c to WASM compile and execution * [cbackend] Add links to test dependencies in quickstart * [cbackend] Add test with inductive types and patterns * [cbackend] Fix indentation * [cbackend] Remove ExpressionTyped case https://github.com/heliaxdev/minijuvix/issues/79 * [lexer] Fix lexing of \ inside a foreign block * [cbackend] PR review fixes * [chore] Remove unused import * [cbackend] Rename CJuvix to MiniC * [cbackend] Rename MonoJuvixToC to MonoJuvixToMiniC * [cbackend] Add test for polymorphic function * [cbackend] Add module for string literals
2022-05-05 16:12:17 +03:00
import MiniJuvix.Translation.MonoJuvixToMiniC qualified as MiniC
import MiniJuvix.Translation.MonoJuvixToMiniHaskell qualified as MiniHaskell
2022-04-07 13:49:08 +03:00
import MiniJuvix.Translation.ScopedToAbstract qualified as Abstract
import MiniJuvix.Utils.Version (runDisplayVersion)
2022-01-18 14:25:42 +03:00
import Options.Applicative
import System.Environment (getProgName)
import Text.Show.Pretty hiding (Html)
minijuvixYamlFile :: FilePath
minijuvixYamlFile = "minijuvix.yaml"
findRoot :: CommandGlobalOptions -> IO FilePath
findRoot copts = do
let dir :: Maybe FilePath
dir = takeDirectory <$> commandFirstFile copts
whenJust dir setCurrentDirectory
r <- IO.try go :: IO (Either IO.SomeException FilePath)
case r of
Left err -> do
putStrLn "Something went wrong when figuring out the root of the project."
putStrLn (pack (IO.displayException err))
2022-04-04 16:53:16 +03:00
cur <- getCurrentDirectory
putStrLn ("I will try to use the current directory: " <> pack cur)
return cur
Right root -> return root
where
2022-04-05 20:57:21 +03:00
possiblePaths :: FilePath -> [FilePath]
possiblePaths start = takeWhile (/= "/") (aux start)
where
aux f = f : aux (takeDirectory f)
2022-04-05 20:57:21 +03:00
go :: IO FilePath
go = do
c <- getCurrentDirectory
l <- findFile (possiblePaths c) minijuvixYamlFile
case l of
Nothing -> return c
Just yaml -> return (takeDirectory yaml)
getEntryPoint :: FilePath -> GlobalOptions -> Maybe EntryPoint
getEntryPoint r opts = nonEmpty (opts ^. globalInputFiles) >>= Just <$> entryPoint
where
entryPoint :: NonEmpty FilePath -> EntryPoint
entryPoint l =
EntryPoint
{ _entryPointRoot = r,
_entryPointNoTermination = opts ^. globalNoTermination,
_entryPointModulePaths = l
}
runCommand :: Members '[Embed IO, App] r => CommandGlobalOptions -> Sem r ()
runCommand cmdWithOpts = do
let cmd = cmdWithOpts ^. cliCommand
globalOpts = cmdWithOpts ^. cliGlobalOptions
2022-04-11 14:08:37 +03:00
toAnsiText' :: forall a. (HasAnsiBackend a, HasTextBackend a) => a -> Text
toAnsiText' = toAnsiText (not (globalOpts ^. globalNoColors))
root <- embed (findRoot cmdWithOpts)
case cmd of
DisplayRoot -> say (pack root)
_ -> do
-- Other commands require an entry point:
case getEntryPoint root globalOpts of
Nothing -> printFailureExit "Provide a MiniJuvix file to run this command\nUse --help to see all the options"
Just entryPoint -> do
case cmd of
Highlight -> do
res <- runPipelineEither (upToScoping entryPoint)
case res of
Left err -> say (Highlight.goError (errorIntervals err))
Right r -> do
let tbl = r ^. Scoper.resultParserTable
items = tbl ^. Parser.infoParsedItems
names = r ^. (Scoper.resultScoperTable . Scoper.infoNames)
inputFile = entryPoint ^. mainModulePath
hinput =
Highlight.filterInput
inputFile
Highlight.HighlightInput
{ _highlightNames = names,
_highlightParsed = items
}
say (Highlight.go hinput)
Parse localOpts -> do
m <-
head . (^. Parser.resultModules)
<$> runPipeline (upToParsing entryPoint)
if localOpts ^. parseNoPrettyShow then say (show m) else say (pack (ppShow m))
Scope localOpts -> do
l <-
(^. Scoper.resultModules)
<$> runPipeline
(upToScoping entryPoint)
forM_ l $ \s -> do
renderStdOut (Scoper.ppOut (mkScopePrettyOptions globalOpts localOpts) s)
Html HtmlOptions {..} -> do
res <- runPipeline (upToScoping entryPoint)
let m = head (res ^. Scoper.resultModules)
embed (genHtml Scoper.defaultOptions _htmlRecursive _htmlTheme _htmlOutputDir _htmlPrintMetadata m)
MicroJuvix Pretty -> do
micro <-
head . (^. Micro.resultModules)
<$> runPipeline (upToMicroJuvix entryPoint)
let ppOpts =
Micro.defaultOptions
{ Micro._optShowNameIds = globalOpts ^. globalShowNameIds
}
App.renderStdOut (Micro.ppOut ppOpts micro)
MicroJuvix Arity -> do
micro <- head . (^. MicroArity.resultModules) <$> runPipeline (upToMicroJuvixArity entryPoint)
App.renderStdOut (Micro.ppOut Micro.defaultOptions micro)
MicroJuvix (TypeCheck localOpts) -> do
res <- runPipeline (upToMicroJuvixTyped entryPoint)
say "Well done! It type checks"
when (localOpts ^. microJuvixTypePrint) $ do
let ppOpts =
Micro.defaultOptions
{ Micro._optShowNameIds = globalOpts ^. globalShowNameIds
}
checkedModule = head (res ^. MicroTyped.resultModules)
renderStdOut (Micro.ppOut ppOpts checkedModule)
newline
let typeCalls = Mono.buildTypeCallMap res
renderStdOut (Micro.ppOut ppOpts typeCalls)
newline
let concreteTypeCalls = Mono.collectTypeCalls res
renderStdOut (Micro.ppOut ppOpts concreteTypeCalls)
MonoJuvix -> do
let ppOpts =
Mono.defaultOptions
{ Mono._optShowNameIds = globalOpts ^. globalShowNameIds
}
monojuvix <- head . (^. Mono.resultModules) <$> runPipeline (upToMonoJuvix entryPoint)
renderStdOut (Mono.ppOut ppOpts monojuvix)
MiniHaskell -> do
minihaskell <-
head . (^. MiniHaskell.resultModules)
<$> runPipeline (upToMiniHaskell entryPoint)
renderStdOut (MiniHaskell.ppOutDefault minihaskell)
MiniC -> do
miniC <- (^. MiniC.resultCCode) <$> runPipeline (upToMiniC entryPoint)
say miniC
Compile localOpts -> do
miniC <- (^. MiniC.resultCCode) <$> runPipeline (upToMiniC entryPoint)
let inputFile = entryPoint ^. mainModulePath
result <- embed (runCompile root inputFile localOpts miniC)
case result of
Left err -> printFailureExit err
_ -> return ()
Termination (Calls localOpts@CallsOptions {..}) -> do
results <- runPipeline (upToAbstract entryPoint)
let topModule = head (results ^. Abstract.resultModules)
infotable = results ^. Abstract.resultTable
callMap0 = Termination.buildCallMap infotable topModule
callMap = case _callsFunctionNameFilter of
Nothing -> callMap0
Just f -> Termination.filterCallMap f callMap0
localOpts' = Termination.callsPrettyOptions globalOpts localOpts
renderStdOut (Abstract.ppOut localOpts' callMap)
newline
Termination (CallGraph CallGraphOptions {..}) -> do
results <- runPipeline (upToAbstract entryPoint)
let topModule = head (results ^. Abstract.resultModules)
infotable = results ^. Abstract.resultTable
callMap = Termination.buildCallMap infotable topModule
localOpts' =
Abstract.defaultOptions
{ Abstract._optShowNameIds = globalOpts ^. globalShowNameIds
}
completeGraph = Termination.completeCallGraph callMap
filteredGraph =
maybe
completeGraph
(`Termination.unsafeFilterGraph` completeGraph)
_graphFunctionNameFilter
rEdges = Termination.reflexiveEdges filteredGraph
recBehav = map Termination.recursiveBehaviour rEdges
App.renderStdOut (Abstract.ppOut localOpts' filteredGraph)
newline
forM_ recBehav $ \r -> do
let funName = r ^. Termination.recursiveBehaviourFun
funRef = Abstract.FunctionRef funName
funInfo =
HashMap.lookupDefault
impossible
funRef
(infotable ^. Abstract.infoFunctions)
markedTerminating = funInfo ^. (Abstract.functionInfoDef . Abstract.funDefTerminating)
ppOpts =
Abstract.defaultOptions
{ Abstract._optShowNameIds = globalOpts ^. globalShowNameIds
}
n = toAnsiText' (Abstract.ppOut ppOpts funName)
App.renderStdOut (Abstract.ppOut localOpts' r)
newline
if
| markedTerminating ->
printSuccessExit (n <> " Terminates by assumption")
| otherwise ->
case Termination.findOrder r of
Nothing ->
printFailureExit (n <> " Fails the termination checking")
Just (Termination.LexOrder k) ->
printSuccessExit (n <> " Terminates with order " <> show (toList k))
showHelpText :: ParserPrefs -> IO ()
showHelpText p = do
progn <- getProgName
let helpText = parserFailure p descr (ShowHelpText Nothing) []
let (msg, _) = renderFailure helpText progn
putStrLn (pack msg)
2022-01-18 14:25:42 +03:00
main :: IO ()
main = do
let p = prefs showHelpOnEmpty
cli <- customExecParser p descr >>= makeAbsPaths
case cli of
DisplayVersion -> runDisplayVersion
DisplayHelp -> showHelpText p
Command cmd -> runM (runAppIO (cmd ^. cliGlobalOptions) (runCommand cmd))