1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-04 17:07:28 +03:00
juvix/app/TopCommand/Options.hs
Jan Mas Rovira 2d36a65324
Make compile targets a subcommand instead of a flag (#2700)
# Changes
The main goal of this pr is to remove the `--target` flag for `juvix
compile` and use subcommands instead. The targets that are relevant to
normal users are found in `juvix compile --help`. Targets that are
relevant only to developers are found in `juvix dev compile --help`.

Below I list some of the changes in more detail.
## Compile targets for user-facing languages
- `juvix compile native`
- `juvix compile wasi`. I wasn't sure how to call this: `wasm`,
`wasm32-wasi`, etc. In the end I thought `wasi` was short and accurate,
but we can change it.
- `juvix compile vampir`
- `juvix compile anoma`
- `juvix compile cairo`
## *New* compile targets for internal languages
See `juvix dev compile --help`.

1. `dev compile core` has the same behaviour as `dev core
from-concrete`. The `dev core from-concrete` is redundant at the moment.
2. `dev compile tree` compiles to Tree and prints the InfoTable to the
output file wihout any additional checks.
3. `dev compile reg` compiles to Reg and prints the InfoTable to the
output file wihout any additional checks.
4. `dev compile asm` compiles to Asm and prints the InfoTable to the
output file wihout any additional checks.
5. 4. `dev compile casm` compiles to Asm and prints the Result to the
output file wihout any additional checks. TODO: should the Result be
printed or something else? At the moment the Result lacks a pretty
instance.
6. 
## Optional input file
1. The input file for commands that expect a .juvix file as input is now
optional. If the argument is ommited, he main file given in the
package.yaml will be used. This applies to the following commands:
   1. `juvix compile [native|wasi|geb|vampir|anoma|cairo]`
   8.  `juvix dev compile [core|reg|tree|casm|asm]`
   1. `juvix html`
   3. `juvix markdown`.
   4. `juvix dev internal [typecheck|pretty]`.
   5. `juvix dev [parse|scope]`
   7. `juvix compile [native|wasi|geb|vampir|anoma|cairo]`
   9. note that `juvix format` has not changed its behaviour.

## Refactor some C-like compiler flags
Both `juvix compile native` and `juvix compile wasi` support `--only-c`
(`-C`), `--only-preprocess` (`-E`), `--only-assemble` (`-S`). I propose
to deviate from the `gcc` style and instead use a flag with a single
argument:
- `--cstage [source|preprocess|assembly|exec(default)]`. I'm open to
suggestions. For now, I've kept the legacy flags but marked them as
deprecated in the help message.

## Remove code duplication
I've tried to reduce code duplication. This is sometimes in tension with
code readability so I've tried to find a good balance. I've tried to
make it so we don't have to jump to many different files to understand
what a single command is doing. I'm sure there is still room for
improvement.

## Other refactors
I've implemented other small refactors that I considered improved the
quality of the code.

## TODO/Future work
We should refactor commands (under `compile dev`) which still use
`module Commands.Extra.Compile` and remove it.
2024-04-09 13:29:07 +02:00

245 lines
6.6 KiB
Haskell

module TopCommand.Options where
import Commands.Clean.Options
import Commands.Compile.Options
import Commands.Dependencies.Options qualified as Dependencies
import Commands.Dev.Options qualified as Dev
import Commands.Doctor.Options
import Commands.Eval.Options
import Commands.Format.Options
import Commands.Html.Options
import Commands.Init.Options
import Commands.Markdown.Options
import Commands.Repl.Options
import Commands.Typecheck.Options
import CommonOptions hiding (Doc)
import Data.Generics.Uniplate.Data
import GlobalOptions
import Options.Applicative.Help.Pretty
data TopCommand
= DisplayVersion
| DisplayNumericVersion
| DisplayHelp
| Typecheck TypecheckOptions
| Compile CompileCommand
| Clean CleanOptions
| Eval EvalOptions
| Html HtmlOptions
| Markdown MarkdownOptions
| Dev Dev.DevCommand
| Doctor DoctorOptions
| Init InitOptions
| JuvixRepl ReplOptions
| JuvixFormat FormatOptions
| Dependencies Dependencies.DependenciesCommand
deriving stock (Data)
topCommandInputPath :: TopCommand -> IO (Maybe (SomePath Abs))
topCommandInputPath = \case
JuvixFormat fopts -> case fopts ^. formatInput of
Just f -> getInputPathFromPrepathFileOrDir f
Nothing -> return Nothing
t -> do
d <- firstJustM getInputFileOrDir (universeBi t)
f <- firstJustM getInputFile (universeBi t)
return (f <|> d)
where
getInputFile :: AppPath File -> IO (Maybe (SomePath Abs))
getInputFile p
| p ^. pathIsInput = do
cwd <- getCurrentDir
Just . File <$> prepathToAbsFile cwd (p ^. pathPath)
| otherwise = return Nothing
getInputFileOrDir :: AppPath FileOrDir -> IO (Maybe (SomePath Abs))
getInputFileOrDir p
| p ^. pathIsInput = getInputPathFromPrepathFileOrDir (p ^. pathPath)
| otherwise = return Nothing
getInputPathFromPrepathFileOrDir :: Prepath FileOrDir -> IO (Maybe (SomePath Abs))
getInputPathFromPrepathFileOrDir p = do
cwd <- getCurrentDir
lr <- fromPreFileOrDir cwd p
return . Just $ case lr of
Left file -> File file
Right dir -> Dir dir
parseDisplayVersion :: Parser TopCommand
parseDisplayVersion =
flag'
DisplayVersion
( long "version"
<> short 'v'
<> help "Show version information"
)
parseDisplayNumericVersion :: Parser TopCommand
parseDisplayNumericVersion =
flag'
DisplayNumericVersion
( long "numeric-version"
<> help "Show only the version number"
)
parseDisplayHelp :: Parser TopCommand
parseDisplayHelp =
flag'
DisplayHelp
( long "help"
<> short 'h'
<> help "Show the help text"
)
parseUtility :: Parser TopCommand
parseUtility =
hsubparser
( mconcat
[ commandGroup "Utility commands:",
metavar "UTILITY_CMD",
commandInit,
commandRepl,
commandFormat,
commandClean,
commandDependencies,
commandDoctor,
commandDev
]
)
where
commandInit :: Mod CommandFields TopCommand
commandInit =
command
"init"
( info
(Init <$> parseInitOptions)
(progDesc "Interactively initialize a Juvix project in the current directory")
)
commandDoctor :: Mod CommandFields TopCommand
commandDoctor =
command
"doctor"
( info
(Doctor <$> parseDoctorOptions)
(progDesc "Perform checks on your Juvix development environment")
)
commandRepl :: Mod CommandFields TopCommand
commandRepl =
command
"repl"
( info
(JuvixRepl <$> parseRepl)
(progDesc "Run the Juvix REPL")
)
commandFormat :: Mod CommandFields TopCommand
commandFormat =
command "format" $
info
(JuvixFormat <$> parseFormat)
( headerDoc
( Just
( vsep
[ "juvix format is used to format Juvix source files.",
"",
"Given a file, it prints the reformatted source to standard output.",
"Given a project directory it prints a list of unformatted files in the project.",
"Given no argument it prints a list of unformatted files in the project which contains the current directory."
]
)
)
<> progDesc "Format a Juvix file or Juvix project"
)
commandClean :: Mod CommandFields TopCommand
commandClean =
command
"clean"
(info (Clean <$> parseCleanOptions) (progDesc "Delete build artifacts"))
commandDependencies :: Mod CommandFields TopCommand
commandDependencies =
command
"dependencies"
(info (Dependencies <$> Dependencies.parseDependenciesCommand) (progDesc "Subcommands related to dependencies"))
commandCheck :: Mod CommandFields TopCommand
commandCheck =
command "typecheck" $
info
(Typecheck <$> parseTypecheck)
(progDesc "Typecheck a Juvix file")
commandCompile :: Mod CommandFields TopCommand
commandCompile =
command "compile" $
info
(Compile <$> parseCompileCommand)
(progDesc "Compile a Juvix file")
commandEval :: Mod CommandFields TopCommand
commandEval =
command "eval" $
info
(Eval <$> parseEvalOptions)
(progDesc "Evaluate a Juvix file")
commandHtml :: Mod CommandFields TopCommand
commandHtml =
command "html" $
info
(Html <$> parseHtml)
(progDesc "Generate HTML for a Juvix file")
commandMarkdown :: Mod CommandFields TopCommand
commandMarkdown =
command "markdown" $
info
(Markdown <$> parseJuvixMarkdown)
(progDesc "Translate Juvix code blocks in a Markdown file to Markdown")
commandDev :: Mod CommandFields TopCommand
commandDev =
command "dev" $
info
(Dev <$> Dev.parseDevCommand)
(progDesc "Commands for the developers")
parseCompilerCommand :: Parser TopCommand
parseCompilerCommand =
hsubparser
( mconcat
[ commandGroup "Compiler commands:",
metavar "COMPILER_CMD",
commandCheck,
commandCompile,
commandEval,
commandHtml,
commandMarkdown
]
)
parseTopCommand :: Parser TopCommand
parseTopCommand =
parseDisplayVersion
<|> parseDisplayNumericVersion
<|> parseDisplayHelp
<|> parseCompilerCommand
<|> parseUtility
descr :: ParserInfo (GlobalOptions, TopCommand)
descr =
info
( do
cli <- parseTopCommand
opts <- parseGlobalFlags
return (opts, cli)
)
( fullDesc
<> progDesc "The Juvix compiler."
<> footerDoc (Just foot)
)
where
foot :: Doc
foot = annotate bold "maintainers: " <> "The Juvix Team"