quox/exe/Options.idr

203 lines
5.6 KiB
Idris
Raw Normal View History

2023-10-20 11:42:01 -04:00
module Options
import Quox.Pretty
import System
import System.Console.GetOpt
import System.File
import System.Term
import Derive.Prelude
%default total
2023-10-20 11:42:01 -04:00
%language ElabReflection
public export
2023-11-05 09:47:52 -05:00
data OutFile = File String | Console | NoOut
2023-10-20 11:42:01 -04:00
%name OutFile f
2023-11-30 08:46:45 -05:00
%runElab derive "OutFile" [Eq, Show]
2023-10-20 11:42:01 -04:00
public export
2023-11-27 01:39:17 -05:00
data Phase = Parse | Check | Erase | Scheme | End
2023-10-20 11:42:01 -04:00
%name Phase p
2023-11-30 08:46:45 -05:00
%runElab derive "Phase" [Eq, Show]
2023-10-20 11:42:01 -04:00
2023-11-27 01:39:17 -05:00
||| a list of all intermediate `Phase`s (excluding `End`)
2023-10-20 11:42:01 -04:00
public export %inline
allPhases : List Phase
allPhases = %runElab do
cs <- getCons $ fst !(lookupName "Phase")
2023-11-27 01:39:17 -05:00
traverse (check . var) $ fromMaybe [] $ init' cs
2023-10-20 11:42:01 -04:00
2023-11-27 01:39:17 -05:00
||| `Guess` is `Term` for a terminal and `NoHL` for a file
2023-11-05 09:47:52 -05:00
public export
data HLType = Guess | NoHL | Term | Html
2023-11-30 08:46:45 -05:00
%runElab derive "HLType" [Eq, Show]
2023-11-05 09:47:52 -05:00
2023-11-27 01:39:17 -05:00
public export
record Dump where
constructor MkDump
parse, check, erase, scheme : OutFile
%name Dump dump
%runElab derive "Dump" [Show]
2023-10-20 11:42:01 -04:00
public export
record Options where
constructor MkOpts
2023-11-05 09:47:52 -05:00
hlType : HLType
2023-11-27 01:39:17 -05:00
dump : Dump
2023-10-20 11:42:01 -04:00
outFile : OutFile
until : Maybe Phase
flavor : Pretty.Flavor
width : Nat
include : List String
%name Options opts
%runElab derive "Options" [Show]
2023-11-05 09:40:19 -05:00
export
defaultWidth : IO Nat
defaultWidth = do
w <- cast {to = Nat} <$> getTermCols
pure $ if w == 0 then 80 else w
2023-10-20 11:42:01 -04:00
export
defaultOpts : IO Options
defaultOpts = pure $ MkOpts {
2023-11-05 09:47:52 -05:00
hlType = Guess,
2023-11-27 01:39:17 -05:00
dump = MkDump NoOut NoOut NoOut NoOut,
outFile = Console,
2023-10-20 11:42:01 -04:00
until = Nothing,
flavor = Unicode,
2023-11-05 09:40:19 -05:00
width = !defaultWidth,
2023-10-20 11:42:01 -04:00
include = ["."]
}
private
data HelpType = Common | All
private
data OptAction = ShowHelp HelpType | Err String | Ok (Options -> Options)
%name OptAction act
private
2023-11-27 01:39:17 -05:00
toOutFile : String -> OutFile
toOutFile "" = NoOut
toOutFile "-" = Console
toOutFile f = File f
2023-10-20 11:42:01 -04:00
private
toPhase : String -> OptAction
toPhase str =
let lstr = toLower str in
case find (\p => toLower (show p) == lstr) allPhases of
2023-11-27 01:39:17 -05:00
Just p => Ok $ setPhase p
Nothing => Err "unknown phase name \{show str}\nphases: \{phaseNames}"
2023-11-27 01:39:17 -05:00
where
phaseNames = joinBy ", " $ map (toLower . show) allPhases
defConsole : OutFile -> OutFile
defConsole NoOut = Console
defConsole f = f
setPhase : Phase -> Options -> Options
setPhase Parse = {until := Just Parse, dump.parse $= defConsole}
setPhase Check = {until := Just Check, dump.check $= defConsole}
setPhase Erase = {until := Just Erase, dump.erase $= defConsole}
setPhase Scheme = {until := Just Scheme, dump.scheme $= defConsole}
setPhase End = id
2023-10-20 11:42:01 -04:00
private
toWidth : String -> OptAction
toWidth s = case parsePositive s of
Just n => Ok {width := n}
Nothing => Err "invalid width: \{show s}"
2023-11-05 09:47:52 -05:00
private
toHLType : String -> OptAction
toHLType str = case toLower str of
"none" => Ok {hlType := NoHL}
"term" => Ok {hlType := Term}
"html" => Ok {hlType := Html}
2023-11-27 01:39:17 -05:00
_ => Err "unknown highlighting type \{show str}\ntypes: term, html, none"
||| like ghc, -i '' clears the search path; -i a:b:c adds a,b,c to the end
private
dirListFlag : String -> List String -> List String
dirListFlag arg val =
if null arg then [] else val ++ toList (split (== ':') arg)
2023-11-05 09:47:52 -05:00
2023-10-20 11:42:01 -04:00
private
commonOptDescrs' : List (OptDescr OptAction)
commonOptDescrs' = [
2023-11-27 01:39:17 -05:00
MkOpt ['i'] ["include"]
(ReqArg (\is => Ok {include $= dirListFlag is}) "<dir>:<dir>...")
"add directories to look for source files",
MkOpt ['o'] ["output"] (ReqArg (\s => Ok {outFile := toOutFile s}) "<file>")
2023-10-20 11:42:01 -04:00
"output file (\"-\" for stdout, \"\" for no output)",
MkOpt ['P'] ["phase"] (ReqArg toPhase "<phase>")
2023-11-27 01:39:17 -05:00
"stop after the given phase"
2023-10-20 11:42:01 -04:00
]
private
extraOptDescrs : List (OptDescr OptAction)
extraOptDescrs = [
MkOpt [] ["unicode"] (NoArg $ Ok {flavor := Unicode})
"use unicode syntax when printing (default)",
MkOpt [] ["ascii"] (NoArg $ Ok {flavor := Ascii})
"use ascii syntax when printing",
MkOpt [] ["width"] (ReqArg toWidth "<width>")
"max output width (defaults to terminal width)",
2023-11-05 09:47:52 -05:00
MkOpt [] ["color", "colour"] (ReqArg toHLType "<type>")
2023-11-27 01:39:17 -05:00
"select highlighting type",
MkOpt [] ["dparse"] (ReqArg (\s => Ok {dump.parse := toOutFile s}) "<file>")
"dump AST",
MkOpt [] ["dcheck"] (ReqArg (\s => Ok {dump.check := toOutFile s}) "<file>")
"dump typechecker output",
MkOpt [] ["derase"] (ReqArg (\s => Ok {dump.erase := toOutFile s}) "<file>")
"dump erasure output",
MkOpt [] ["dscheme"] (ReqArg (\s => Ok {dump.scheme := toOutFile s}) "<file>")
"dump scheme output (without prelude)"
2023-10-20 11:42:01 -04:00
]
private
helpOptDescrs : List (OptDescr OptAction)
helpOptDescrs = [
MkOpt ['h'] ["help"] (NoArg $ ShowHelp Common) "show common options",
MkOpt [] ["help-all"] (NoArg $ ShowHelp All) "show all options"
]
commonOptDescrs = commonOptDescrs' ++ helpOptDescrs
allOptDescrs = commonOptDescrs' ++ extraOptDescrs ++ helpOptDescrs
export
usageHeader : String
2023-11-05 09:47:52 -05:00
usageHeader = trim """
quox [options] [file.quox ...]
rawr
"""
2023-10-20 11:42:01 -04:00
export
usage : List (OptDescr _) -> IO a
usage ds = do
ignore $ fPutStr stderr $ usageInfo usageHeader ds
exitSuccess
private
applyAction : Options -> OptAction -> IO Options
applyAction opts (ShowHelp Common) = usage commonOptDescrs
applyAction opts (ShowHelp All) = usage allOptDescrs
applyAction opts (Err err) = die err
applyAction opts (Ok f) = pure $ f opts
export
options : IO (String, Options, List String)
options = do
app :: args <- getArgs
| [] => die "couldn't get command line arguments"
let res = getOpt Permute allOptDescrs args
unless (null res.errors) $
die $ trim $ concat res.errors
unless (null res.unrecognized) $
die "unrecognised options: \{joinBy ", " res.unrecognized}"
opts <- foldlM applyAction !defaultOpts res.options
2023-11-27 01:39:17 -05:00
pure (app, opts, res.nonOptions)