quox/exe/Main.idr

366 lines
9.6 KiB
Idris
Raw Normal View History

2022-04-27 14:04:03 -04:00
module Main
2021-07-07 07:11:39 -04:00
2023-10-20 11:42:01 -04:00
import Quox.Syntax as Q
import Quox.Definition as Q
import Quox.Untyped.Syntax as U
2023-11-27 01:39:17 -05:00
import Quox.Parser
2023-10-20 11:42:01 -04:00
import Quox.Untyped.Erase
2023-10-24 17:52:19 -04:00
import Quox.Untyped.Scheme
2023-11-27 01:39:17 -05:00
import Quox.Pretty
2023-10-20 11:42:01 -04:00
import Options
2021-07-20 16:05:19 -04:00
2023-10-20 11:42:01 -04:00
import System
import System.File
2023-11-27 01:39:17 -05:00
import Data.IORef
2023-03-31 13:31:29 -04:00
import Control.Eff
2021-07-20 16:05:19 -04:00
2023-11-27 01:39:17 -05:00
%default total
2023-10-20 11:42:01 -04:00
%hide Doc.(>>=)
%hide Core.(>>=)
2023-10-24 17:52:19 -04:00
private
die : HasIO io => (opts : LayoutOpts) -> Doc opts -> io a
die opts err = do
ignore $ fPutStr stderr $ render opts err
exitFailure
2023-10-20 11:42:01 -04:00
2023-11-05 09:47:52 -05:00
private
hlFor : HLType -> OutFile -> HL -> Highlight
hlFor Guess Console = highlightSGR
hlFor Guess _ = noHighlight
hlFor NoHL _ = noHighlight
hlFor Term _ = highlightSGR
hlFor Html _ = highlightHtml
2023-10-20 11:42:01 -04:00
private
2023-11-27 01:39:17 -05:00
runPretty : Options -> OutFile -> Eff Pretty a -> a
runPretty opts file act =
runPrettyWith Outer opts.flavor (hlFor opts.hlType file) 2 act
2023-10-20 11:42:01 -04:00
2023-05-14 13:58:46 -04:00
private
2023-10-20 11:42:01 -04:00
record State where
constructor MkState
seen : IORef SeenSet
defs : IORef Q.Definitions
ns : IORef Mods
suf : IORef NameSuf
%name Main.State state
2023-05-14 13:58:46 -04:00
private
2023-10-20 11:42:01 -04:00
newState : HasIO io => io State
newState = pure $ MkState {
seen = !(newIORef empty),
defs = !(newIORef empty),
ns = !(newIORef [<]),
suf = !(newIORef 0)
}
private
data Error =
ParseError String Parser.Error
| FromParserError FromParser.Error
| EraseError Erase.Error
| WriteError FilePath FileError
| NoMain
| MultipleMains (List Id)
2023-10-20 11:42:01 -04:00
%hide FromParser.Error
%hide Erase.Error
%hide Lexer.Error
%hide Parser.Error
2023-10-20 11:42:01 -04:00
private
loadError : Loc -> FilePath -> FileError -> Error
loadError loc file err = FromParserError $ LoadError loc file err
2023-10-24 17:52:19 -04:00
private
prettyError : {opts : LayoutOpts} -> Error -> Eff Pretty (Doc opts)
prettyError (ParseError file e) = prettyParseError file e
prettyError (FromParserError e) = FromParser.prettyError True e
prettyError (EraseError e) = Erase.prettyError True e
prettyError NoMain = pure "no #[main] function given"
prettyError (MultipleMains xs) =
pure $ sep ["multiple #[main] functions given:",
separateLoose "," !(traverse prettyId xs)]
2023-10-24 17:52:19 -04:00
prettyError (WriteError file e) = pure $
hangSingle 2 (text "couldn't write file \{file}:") (pshow e)
2023-11-27 01:39:17 -05:00
private
dieError : Options -> Error -> IO a
dieError opts e =
die (Opts opts.width) $
runPretty ({outFile := Console} opts) Console $
prettyError e
2023-10-20 11:42:01 -04:00
private
data CompileTag = OPTS | STATE
private
Compile : List (Type -> Type)
Compile =
[Except Error,
ReaderL STATE State, ReaderL OPTS Options,
LoadFile, IO]
2023-11-27 01:39:17 -05:00
private covering
2023-10-20 11:42:01 -04:00
runCompile : Options -> State -> Eff Compile a -> IO (Either Error a)
runCompile opts state act =
fromIOErr $ runEff act $ with Union.(::)
[handleExcept (\e => ioLeft e),
handleReaderConst state,
handleReaderConst opts,
handleLoadFileIOE loadError ParseError state.seen opts.include,
liftIO]
2023-05-14 13:58:46 -04:00
private
2023-10-20 11:42:01 -04:00
data StopTag = STOP
2023-05-14 13:58:46 -04:00
private
2023-10-20 11:42:01 -04:00
CompileStop : List (Type -> Type)
CompileStop = FailL STOP :: Compile
private
withEarlyStop : Has (FailL STOP) fs => Eff fs () -> Eff (fs - FailL STOP) ()
withEarlyStop = ignore . runFailAt STOP
private
stopHere : Has (FailL STOP) fs => Eff fs ()
stopHere = failAt STOP
private
2023-11-27 01:39:17 -05:00
data ConsoleChannel = COut | CErr
private
data OpenFile = OConsole ConsoleChannel | OFile String File | ONone
private
rethrowFile : String -> Either FileError a -> Eff Compile a
rethrowFile f = rethrow . mapFst (WriteError f)
private
toOutFile : OpenFile -> OutFile
toOutFile (OConsole _) = Console
toOutFile (OFile f _) = File f
toOutFile ONone = NoOut
2023-10-20 11:42:01 -04:00
2023-11-27 01:39:17 -05:00
private
withFileC : String -> (OpenFile -> Eff Compile a) -> Eff Compile a
withFileC f act =
withFile f WriteTruncate pure (Prelude.map Right . act . OFile f) >>=
rethrowFile f
2023-10-20 11:42:01 -04:00
private
2023-11-27 01:39:17 -05:00
withOutFile : ConsoleChannel -> OutFile ->
(OpenFile -> Eff Compile a) -> Eff Compile a
withOutFile _ (File f) act = withFileC f act
withOutFile ch Console act = act $ OConsole ch
withOutFile _ NoOut act = act ONone
2023-10-20 11:42:01 -04:00
private
2023-11-27 01:39:17 -05:00
outputStr : OpenFile -> Lazy String -> Eff Compile ()
outputStr ONone _ = pure ()
outputStr (OConsole COut) str = putStr str
outputStr (OConsole CErr) str = fPutStr stderr str >>= rethrowFile "<stderr>"
outputStr (OFile f h) str = fPutStr h str >>= rethrowFile f
2023-10-24 17:52:19 -04:00
private
2023-11-27 01:39:17 -05:00
outputDocs : OpenFile ->
({opts : LayoutOpts} -> Eff Pretty (List (Doc opts))) ->
Eff Compile ()
outputDocs file docs = do
2023-10-24 17:52:19 -04:00
opts <- askAt OPTS
2023-11-27 01:39:17 -05:00
for_ (runPretty opts (toOutFile file) docs) $ \x =>
outputStr file $ render (Opts opts.width) x
private
outputDoc : OpenFile ->
({opts : LayoutOpts} -> Eff Pretty (Doc opts)) -> Eff Compile ()
outputDoc file doc = outputDocs file $ singleton <$> doc
2023-10-20 11:42:01 -04:00
private
2023-11-27 01:39:17 -05:00
liftFromParser : Eff FromParserIO a -> Eff Compile a
2023-10-20 11:42:01 -04:00
liftFromParser act =
runEff act $ with Union.(::)
[handleExcept $ \err => throw $ FromParserError err,
2023-10-20 11:42:01 -04:00
handleStateIORef !(asksAt STATE defs),
handleStateIORef !(asksAt STATE ns),
handleStateIORef !(asksAt STATE suf),
\g => send g]
2023-10-20 11:42:01 -04:00
private
2023-11-27 01:39:17 -05:00
liftErase : Q.Definitions -> Eff Erase a -> Eff Compile a
2023-10-20 11:42:01 -04:00
liftErase defs act =
runEff act
[handleExcept $ \err => throw $ EraseError err,
2023-10-20 11:42:01 -04:00
handleStateIORef !(asksAt STATE suf)]
2023-10-24 17:52:19 -04:00
private
2023-11-27 01:39:17 -05:00
liftScheme : Eff Scheme a -> Eff Compile (a, List Id)
liftScheme act = do
runEff [|MkPair act (getAt MAIN)|]
[handleStateIORef !(newIORef empty),
handleStateIORef !(newIORef [])]
2023-10-24 17:52:19 -04:00
private
2023-11-27 01:39:17 -05:00
Step : Type -> Type -> Type
Step i o = OpenFile -> i -> Eff Compile o
-- private
-- processFile : String -> Eff Compile ()
-- processFile file = withEarlyStop $ do
-- Just ast <- loadFile noLoc file
-- | Nothing => pure ()
-- -- putErrLn "checking \{file}"
-- when (!(asksAt OPTS until) == Just Parse) $ do
-- lift $ outputStr $ show ast
-- stopHere
-- defList <- liftFromParser $ concat <$> traverse fromPTopLevel ast
-- outputDocStopIf Check $
-- traverse (uncurry Q.prettyDef) defList
-- let defs = SortedMap.fromList defList
-- erased <- liftErase defs $
-- traverse (\(x, d) => (x,) <$> eraseDef defs x d) defList
-- outputDocStopIf Erase $
-- traverse (uncurry U.prettyDef) erased
-- (scheme, mains) <- liftScheme $ map catMaybes $
-- traverse (uncurry defToScheme) erased
-- outputDocStopIf Scheme $
-- intersperse empty <$> traverse prettySexp scheme
2023-11-05 09:47:52 -05:00
2023-10-20 11:42:01 -04:00
private
2023-11-27 01:39:17 -05:00
step : {default CErr console : ConsoleChannel} ->
Phase -> OutFile -> Step i o -> i -> Eff CompileStop o
step phase file act x = do
opts <- askAt OPTS
2023-11-27 01:39:17 -05:00
res <- lift $ withOutFile console file $ \h => act h x
when (opts.until == Just phase) stopHere
pure res
private covering
parse : Step String PFile
parse h file = do
Just ast <- loadFile noLoc file
| Nothing => pure []
outputStr h $ show ast
pure ast
private covering
check : Step PFile (List Q.NDefinition)
check h decls =
map concat $ for decls $ \decl => do
defs <- liftFromParser $ fromPTopLevel decl
outputDocs h $ traverse (\(x, d) => prettyDef x d) defs
pure defs
private covering
erase : Step (List Q.NDefinition) (List U.NDefinition)
erase h defList =
for defList $ \(x, def) => do
def <- liftErase defs $ eraseDef defs x def
outputDoc h $ U.prettyDef x def
pure (x, def)
where defs = SortedMap.fromList defList
private covering
scheme : Step (List U.NDefinition) (List Sexp, Id)
scheme h defs = do
sexps' <- for defs $ \(x, d) => do
(msexp, mains) <- liftScheme $ defToScheme x d
outputDoc h $ maybe (sayErased x) prettySexp msexp
pure (msexp, mains)
bitraverse (pure . catMaybes) (oneMain . concat) $ unzip sexps'
where
sayErased : {opts : LayoutOpts} -> Name -> Eff Pretty (Doc opts)
sayErased x = pure $ hsep [";;", prettyName x, "erased"]
oneMain : List Id -> Eff Compile Id
oneMain [m] = pure m
oneMain [] = throw NoMain
oneMain ms = throw $ MultipleMains ms
private covering
output : Step (List Sexp, Id) ()
output h (sexps, main) =
lift $ outputDocs h $ do
res <- traverse prettySexp sexps
runner <- makeRunMain main
pure $ text Scheme.prelude :: res ++ [runner]
2023-10-20 11:42:01 -04:00
2023-11-27 01:39:17 -05:00
private covering
processFile : String -> Eff Compile ()
processFile file = withEarlyStop $ pipeline !(askAt OPTS) file where
pipeline : Options -> String -> Eff CompileStop ()
pipeline opts =
step Parse opts.dump.parse Main.parse >=>
step Check opts.dump.check Main.check >=>
step Erase opts.dump.erase Main.erase >=>
step Scheme opts.dump.scheme Main.scheme >=>
step End opts.outFile Main.output {console = COut}
export covering
2023-03-31 13:31:29 -04:00
main : IO ()
main = do
2023-10-20 11:42:01 -04:00
(_, opts, files) <- options
case !(runCompile opts !newState $ traverse_ processFile files) of
Right () => pure ()
2023-11-27 01:39:17 -05:00
Left e => dieError opts e
2023-10-20 11:42:01 -04:00
2023-03-31 13:31:29 -04:00
-----------------------------------
2023-05-14 13:58:46 -04:00
{-
2021-07-20 16:05:19 -04:00
2022-04-11 08:09:37 -04:00
private
2022-04-11 15:58:33 -04:00
text : PrettyOpts -> List String
2022-04-11 08:09:37 -04:00
text _ =
["",
#" ___ ___ _____ __ __"#,
#"/ _ `/ // / _ \\ \ /"#,
#"\_, /\_,_/\___/_\_\"#,
#" /_/"#,
""]
2022-03-06 19:19:26 -05:00
2023-11-27 01:39:17 -05:00
-- ["",
-- #" __ _ _ _ _____ __"#,
-- #"/ _` | || / _ \ \ /"#,
-- #"\__, |\_,_\___/_\_\"#,
-- #" |_|"#,
-- ""]
2022-04-11 08:09:37 -04:00
private
2022-04-11 15:58:33 -04:00
qtuwu : PrettyOpts -> List String
2022-04-11 08:09:37 -04:00
qtuwu opts =
if opts.unicode then
[#" ___,-´⎠ "#,
#"(·`──´ ◡ -´⎠"#,
2023-02-26 04:58:47 -05:00
#" \/\/──´⎞/`──´ "#,
#" ⎜⎟───,-₎ ⎞ "#,
#" ⎝⎠ (‾‾) ⎟ "#,
2022-04-11 08:09:37 -04:00
#" (‾‾‾) ⎟ "#]
else
[#" ___,-´/ "#,
#"(.`--´ u -´/"#,
#" \/\/--´|/`--´ "#,
2023-02-26 04:58:47 -05:00
#" ||---,-, \ "#,
#" `´ (--) | "#,
2022-04-11 08:09:37 -04:00
#" (---) | "#]
private
2022-04-11 15:58:33 -04:00
join1 : PrettyOpts -> String -> String -> String
2022-04-11 08:09:37 -04:00
join1 opts l r =
if opts.color then
" " <+> show (colored Green l) <+> " " <+> show (colored Magenta r)
else
" " <+> l <+> " " <+> r
export
2022-04-11 15:58:33 -04:00
banner : PrettyOpts -> String
2022-04-11 08:09:37 -04:00
banner opts = unlines $ zipWith (join1 opts) (qtuwu opts) (text opts)
2023-05-14 13:58:46 -04:00
-}