2022-11-07 16:47:56 +03:00
|
|
|
module Commands.Repl where
|
|
|
|
|
2023-04-27 18:33:08 +03:00
|
|
|
import Commands.Base hiding
|
|
|
|
( command,
|
|
|
|
)
|
2023-05-30 11:19:09 +03:00
|
|
|
import Commands.Repl.Base
|
2022-11-07 16:47:56 +03:00
|
|
|
import Commands.Repl.Options
|
|
|
|
import Control.Exception (throwIO)
|
2023-05-30 11:19:09 +03:00
|
|
|
import Control.Monad.Except qualified as Except
|
|
|
|
import Control.Monad.Reader qualified as Reader
|
2022-11-07 16:47:56 +03:00
|
|
|
import Control.Monad.State.Strict qualified as State
|
2023-05-30 11:19:09 +03:00
|
|
|
import Control.Monad.Trans.Class (lift)
|
2024-02-07 12:47:48 +03:00
|
|
|
import Control.Monad.Trans.Reader (mapReaderT)
|
2024-11-13 18:41:06 +03:00
|
|
|
import Data.String.Interpolate (i)
|
2024-02-07 12:47:48 +03:00
|
|
|
import HaskelineJB
|
2023-03-30 14:39:27 +03:00
|
|
|
import Juvix.Compiler.Concrete.Data.Scope (scopePath)
|
2023-12-30 22:15:35 +03:00
|
|
|
import Juvix.Compiler.Concrete.Data.Scope qualified as Scoped
|
2023-03-30 14:39:27 +03:00
|
|
|
import Juvix.Compiler.Concrete.Data.ScopedName (absTopModulePath)
|
2023-05-30 11:19:09 +03:00
|
|
|
import Juvix.Compiler.Concrete.Data.ScopedName qualified as Scoped
|
|
|
|
import Juvix.Compiler.Concrete.Language qualified as Concrete
|
|
|
|
import Juvix.Compiler.Concrete.Pretty qualified as Concrete
|
2023-03-30 14:39:27 +03:00
|
|
|
import Juvix.Compiler.Core qualified as Core
|
2023-04-12 13:52:40 +03:00
|
|
|
import Juvix.Compiler.Core.Extra.Value
|
2022-11-07 16:47:56 +03:00
|
|
|
import Juvix.Compiler.Core.Info qualified as Info
|
|
|
|
import Juvix.Compiler.Core.Info.NoDisplayInfo qualified as Info
|
|
|
|
import Juvix.Compiler.Core.Pretty qualified as Core
|
|
|
|
import Juvix.Compiler.Internal.Language qualified as Internal
|
|
|
|
import Juvix.Compiler.Internal.Pretty qualified as Internal
|
2023-05-08 13:23:15 +03:00
|
|
|
import Juvix.Compiler.Pipeline.Repl
|
2023-12-30 22:15:35 +03:00
|
|
|
import Juvix.Compiler.Store.Extra
|
2023-11-03 14:51:45 +03:00
|
|
|
import Juvix.Data.CodeAnn (Ann)
|
2022-11-07 16:47:56 +03:00
|
|
|
import Juvix.Data.Error.GenericError qualified as Error
|
2023-05-30 11:19:09 +03:00
|
|
|
import Juvix.Data.NameKind
|
2023-10-30 16:05:52 +03:00
|
|
|
import Juvix.Extra.Paths qualified as P
|
2023-04-13 12:27:39 +03:00
|
|
|
import Juvix.Extra.Stdlib
|
2022-11-07 16:47:56 +03:00
|
|
|
import Juvix.Extra.Version
|
|
|
|
import Juvix.Prelude.Pretty qualified as P
|
|
|
|
import System.Console.ANSI qualified as Ansi
|
|
|
|
import System.Console.Haskeline
|
|
|
|
import System.Console.Repline
|
|
|
|
import System.Console.Repline qualified as Repline
|
|
|
|
|
2023-08-01 11:46:22 +03:00
|
|
|
printHelpTxt :: ReplOptions -> Repl ()
|
|
|
|
printHelpTxt opts = do
|
|
|
|
liftIO $ do
|
|
|
|
putStrLn normalCmds
|
|
|
|
let isDev = opts ^. replIsDev
|
|
|
|
when isDev (putStrLn devCmds)
|
|
|
|
where
|
|
|
|
normalCmds :: Text
|
|
|
|
normalCmds =
|
|
|
|
[__i|
|
2022-11-07 20:43:30 +03:00
|
|
|
EXPRESSION Evaluate an expression in the context of the currently loaded module
|
|
|
|
:help Print help text and describe options
|
|
|
|
:load FILE Load a file into the REPL
|
|
|
|
:reload Reload the currently loaded file
|
|
|
|
:type EXPRESSION Infer the type of an expression
|
2023-05-30 11:19:09 +03:00
|
|
|
:def IDENTIFIER Print the definition of the identifier
|
2023-05-30 19:19:39 +03:00
|
|
|
:doc IDENTIFIER Print the documentation of the identifier
|
2022-11-07 20:43:30 +03:00
|
|
|
:core EXPRESSION Translate the expression to JuvixCore
|
|
|
|
:multiline Start a multi-line input. Submit with <Ctrl-D>
|
|
|
|
:root Print the current project root
|
|
|
|
:version Display the Juvix version
|
|
|
|
:quit Exit the REPL
|
2022-11-07 16:47:56 +03:00
|
|
|
|]
|
2023-08-01 11:46:22 +03:00
|
|
|
|
|
|
|
devCmds :: Text
|
|
|
|
devCmds =
|
|
|
|
[__i|
|
|
|
|
:dev DEV CMD Command reserved for debugging
|
|
|
|
|]
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
replDefaultLoc :: Interval
|
2023-10-30 16:05:52 +03:00
|
|
|
replDefaultLoc = singletonInterval (mkInitialLoc P.replPath)
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
replFromJust :: Repl a -> Maybe a -> Repl a
|
|
|
|
replFromJust err = maybe err return
|
|
|
|
|
|
|
|
replFromEither :: Either JuvixError a -> Repl a
|
|
|
|
replFromEither = either (lift . Except.throwError) return
|
|
|
|
|
|
|
|
replGetContext :: Repl ReplContext
|
|
|
|
replGetContext = State.gets (^. replStateContext) >>= replFromJust noFileLoadedErr
|
|
|
|
|
|
|
|
replError :: AnsiText -> Repl a
|
|
|
|
replError msg =
|
|
|
|
lift
|
|
|
|
. Except.throwError
|
|
|
|
. JuvixError
|
|
|
|
$ GenericError
|
|
|
|
{ _genericErrorLoc = replDefaultLoc,
|
|
|
|
_genericErrorMessage = msg,
|
|
|
|
_genericErrorIntervals = [replDefaultLoc]
|
|
|
|
}
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
noFileLoadedErr :: Repl a
|
2023-05-31 10:53:08 +03:00
|
|
|
noFileLoadedErr = replError (mkAnsiText @Text "No file loaded. Load a file using the `:load FILE` command.")
|
2023-05-30 11:19:09 +03:00
|
|
|
|
2023-08-25 19:37:23 +03:00
|
|
|
welcomeMsg :: (MonadIO m) => m ()
|
2022-11-07 16:47:56 +03:00
|
|
|
welcomeMsg = liftIO (putStrLn [i|Juvix REPL version #{versionTag}: https://juvix.org. Run :help for help|])
|
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
multilineCmd :: String
|
|
|
|
multilineCmd = "multiline"
|
|
|
|
|
|
|
|
quit :: String -> Repl ()
|
|
|
|
quit _ = liftIO (throwIO Interrupt)
|
|
|
|
|
|
|
|
loadEntryPoint :: EntryPoint -> Repl ()
|
|
|
|
loadEntryPoint ep = do
|
2024-07-31 11:02:38 +03:00
|
|
|
artif <- runReplPipelineIO ep
|
2023-05-30 11:19:09 +03:00
|
|
|
let newCtx =
|
|
|
|
ReplContext
|
|
|
|
{ _replContextArtifacts = artif,
|
|
|
|
_replContextEntryPoint = ep
|
|
|
|
}
|
|
|
|
State.modify (set replStateContext (Just newCtx))
|
2023-12-30 22:15:35 +03:00
|
|
|
let epPath :: Maybe (Path Abs File) = ep ^. entryPointModulePath
|
Parallel pipeline (#2779)
This pr introduces parallelism in the pipeline to gain performance. I've
included benchmarks at the end.
- Closes #2750.
# Flags:
There are two new global flags:
1. `-N / --threads`. It is used to set the number of capabilities.
According to [GHC
documentation](https://hackage.haskell.org/package/base-4.20.0.0/docs/GHC-Conc.html#v:setNumCapabilities):
_Set the number of Haskell threads that can run truly simultaneously (on
separate physical processors) at any given time_. When compiling in
parallel, we create this many worker threads. The default value is `-N
auto`, which sets `-N` to half the number of logical cores, capped at 8.
2. `--dev-show-thread-ids`. When given, the thread id is printed in the
compilation progress log. E.g.
![image](https://github.com/anoma/juvix/assets/5511599/9359fae2-0be1-43e5-8d74-faa82cba4034)
# Parallel compilation
1. I've added `src/Parallel/ParallelTemplate.hs` which contains all the
concurrency related code. I think it is good to keep this code separated
from the actual compiler code.
2. I've added a progress log (only for the parallel driver) that outputs
a log of the compilation progress, similar to what stack/cabal do.
# Code changes:
1. I've removed the `setup` stage where we were registering
dependencies. Instead, the dependencies are registered when the
`pathResolver` is run for the first time. This way it is safer.
1. Now the `ImportTree` is needed to run the pipeline. Cycles are
detected during the construction of this tree, so I've removed `Reader
ImportParents` from the pipeline.
3. For the package pathresolver, we do not support parallelism yet (we
could add support for it in the future, but the gains will be small).
4. When `-N1`, the pipeline remains unchanged, so performance should be
the same as in the main branch (except there is a small performance
degradation due to adding the `-threaded` flag).
5. I've introduced `PipelineOptions`, which are options that are used to
pass options to the effects in the pipeline.
6. `PathResolver` constraint has been removed from the `upTo*` functions
in the pipeline due to being redundant.
7. I've added a lot of `NFData` instances. They are needed to force the
full evaluation of `Stored.ModuleInfo` in each of the threads.
2. The `Cache` effect uses
[`SharedState`](https://hackage.haskell.org/package/effectful-core-2.3.0.1/docs/Effectful-State-Static-Shared.html)
as opposed to
[`LocalState`](https://hackage.haskell.org/package/effectful-core-2.3.0.1/docs/Effectful-Writer-Static-Local.html).
Perhaps we should provide different versions.
3. I've added a `Cache` handler that accepts a setup function. The setup
is triggered when a miss is detected. It is used to lazily compile the
modules in parallel.
# Tests
1. I've adapted the smoke test suite to ignore the progress log in the
stderr.
5. I've had to adapt `tests/positive/Internal/Lambda.juvix`. Due to
laziness, a crash happening in this file was not being caught. The
problem is that in this file we have a lambda function with different
number of patterns in their clauses, which we currently do not support
(https://github.com/anoma/juvix/issues/1706).
6. I've had to comment out the definition
```
x : Box ((A : Type) → A → A) := box λ {A a := a};
```
From the test as it was causing a crash
(https://github.com/anoma/juvix/issues/2247).
# Future Work
1. It should be investigated how much performance we lose by fully
evaluating the `Stored.ModuleInfo`, since some information in it will be
discarded. It may be possible to be more fine-grained when forcing
evaluation.
8. The scanning of imports to build the import tree is sequential. Now,
we build the import tree from the entry point module and only the
modules that are imported from it are in the tree. However, we have
discussed that at some point we should make a distinction between
`juvix` _the compiler_ and `juvix` _the build tool_. When using `juvix`
as a build tool it makes sense to typecheck/compile (to stored core) all
modules in the project. When/if we do this, scanning imports in all
modules in parallel becomes trivial.
9. The implementation of the `ParallelTemplate` uses low level
primitives such as
[forkIO](https://hackage.haskell.org/package/base-4.20.0.0/docs/Control-Concurrent.html#v:forkIO).
At some point it should be refactored to use safer functions from the
[`Effectful.Concurrent.Async`](https://hackage.haskell.org/package/effectful-2.3.0.0/docs/Effectful-Concurrent-Async.html)
module.
10. The number of cores and worker threads that we spawn is determined
by the command line. Ideally, we could use to import tree to compute an
upper bound to the ideal number of cores to use.
11. We could add an animation that displays which modules are being
compiled in parallel and which have finished being compiled.
# Benchmarks
On some benchmarks, I include the GHC runtime option
[`-A`](https://downloads.haskell.org/ghc/latest/docs/users_guide/runtime_control.html#rts-flag--A%20%E2%9F%A8size%E2%9F%A9),
which sometimes makes a good impact on performance. Thanks to
@paulcadman for pointing this out. I've figured a good combination of
`-N` and `-A` through trial and error (but this oviously depends on the
cpu and juvix projects).
## Typecheck the standard library
### Clean run (88% faster than main):
```
hyperfine --warmup 1 --prepare 'juvix clean' 'juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432' 'juvix -N 4 typecheck Stdlib/Prelude.juvix' 'juvix-main typecheck Stdlib/Prelude.juvix'
Benchmark 1: juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432
Time (mean ± σ): 444.1 ms ± 6.5 ms [User: 1018.0 ms, System: 77.7 ms]
Range (min … max): 432.6 ms … 455.9 ms 10 runs
Benchmark 2: juvix -N 4 typecheck Stdlib/Prelude.juvix
Time (mean ± σ): 628.3 ms ± 23.9 ms [User: 1227.6 ms, System: 69.5 ms]
Range (min … max): 584.7 ms … 670.6 ms 10 runs
Benchmark 3: juvix-main typecheck Stdlib/Prelude.juvix
Time (mean ± σ): 835.9 ms ± 12.3 ms [User: 788.5 ms, System: 31.9 ms]
Range (min … max): 816.0 ms … 853.6 ms 10 runs
Summary
juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432 ran
1.41 ± 0.06 times faster than juvix -N 4 typecheck Stdlib/Prelude.juvix
1.88 ± 0.04 times faster than juvix-main typecheck Stdlib/Prelude.juvix
```
### Cached run (43% faster than main):
```
hyperfine --warmup 1 'juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432' 'juvix -N 4 typecheck Stdlib/Prelude.juvix' 'juvix-main typecheck Stdlib/Prelude.juvix'
Benchmark 1: juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432
Time (mean ± σ): 241.3 ms ± 7.3 ms [User: 538.6 ms, System: 101.3 ms]
Range (min … max): 231.5 ms … 251.3 ms 11 runs
Benchmark 2: juvix -N 4 typecheck Stdlib/Prelude.juvix
Time (mean ± σ): 235.1 ms ± 12.0 ms [User: 405.3 ms, System: 87.7 ms]
Range (min … max): 216.1 ms … 253.1 ms 12 runs
Benchmark 3: juvix-main typecheck Stdlib/Prelude.juvix
Time (mean ± σ): 336.7 ms ± 13.3 ms [User: 269.5 ms, System: 67.1 ms]
Range (min … max): 316.9 ms … 351.8 ms 10 runs
Summary
juvix -N 4 typecheck Stdlib/Prelude.juvix ran
1.03 ± 0.06 times faster than juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432
1.43 ± 0.09 times faster than juvix-main typecheck Stdlib/Prelude.juvix
```
## Typecheck the test suite of the containers library
At the moment this is the biggest juvix project that we have.
### Clean run (105% faster than main)
```
hyperfine --warmup 1 --prepare 'juvix clean' 'juvix -N 6 typecheck Main.juvix +RTS -A67108864' 'juvix -N 4 typecheck Main.juvix' 'juvix-main typecheck Main.juvix'
Benchmark 1: juvix -N 6 typecheck Main.juvix +RTS -A67108864
Time (mean ± σ): 1.006 s ± 0.011 s [User: 2.171 s, System: 0.162 s]
Range (min … max): 0.991 s … 1.023 s 10 runs
Benchmark 2: juvix -N 4 typecheck Main.juvix
Time (mean ± σ): 1.584 s ± 0.046 s [User: 2.934 s, System: 0.149 s]
Range (min … max): 1.535 s … 1.660 s 10 runs
Benchmark 3: juvix-main typecheck Main.juvix
Time (mean ± σ): 2.066 s ± 0.010 s [User: 1.939 s, System: 0.089 s]
Range (min … max): 2.048 s … 2.077 s 10 runs
Summary
juvix -N 6 typecheck Main.juvix +RTS -A67108864 ran
1.57 ± 0.05 times faster than juvix -N 4 typecheck Main.juvix
2.05 ± 0.03 times faster than juvix-main typecheck Main.juvix
```
### Cached run (54% faster than main)
```
hyperfine --warmup 1 'juvix -N 6 typecheck Main.juvix +RTS -A33554432' 'juvix -N 4 typecheck Main.juvix' 'juvix-main typecheck Main.juvix'
Benchmark 1: juvix -N 6 typecheck Main.juvix +RTS -A33554432
Time (mean ± σ): 551.8 ms ± 13.2 ms [User: 1419.8 ms, System: 199.4 ms]
Range (min … max): 535.2 ms … 570.6 ms 10 runs
Benchmark 2: juvix -N 4 typecheck Main.juvix
Time (mean ± σ): 636.7 ms ± 17.3 ms [User: 1006.3 ms, System: 196.3 ms]
Range (min … max): 601.6 ms … 655.3 ms 10 runs
Benchmark 3: juvix-main typecheck Main.juvix
Time (mean ± σ): 847.2 ms ± 58.9 ms [User: 710.1 ms, System: 126.5 ms]
Range (min … max): 731.1 ms … 890.0 ms 10 runs
Summary
juvix -N 6 typecheck Main.juvix +RTS -A33554432 ran
1.15 ± 0.04 times faster than juvix -N 4 typecheck Main.juvix
1.54 ± 0.11 times faster than juvix-main typecheck Main.juvix
```
2024-05-31 14:41:30 +03:00
|
|
|
whenJust epPath $ \path -> liftIO (putStrLn [i|OK loaded: #{toFilePath @String path}|])
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
reloadFile :: String -> Repl ()
|
|
|
|
reloadFile _ = replGetContext >>= loadEntryPoint . (^. replContextEntryPoint)
|
|
|
|
|
|
|
|
pSomeFile :: String -> Prepath File
|
|
|
|
pSomeFile = mkPrepath
|
|
|
|
|
|
|
|
loadFile :: Prepath File -> Repl ()
|
|
|
|
loadFile f = do
|
2023-06-05 18:52:52 +03:00
|
|
|
entryPoint <- getReplEntryPointFromPrepath f
|
2023-05-30 11:19:09 +03:00
|
|
|
loadEntryPoint entryPoint
|
|
|
|
|
|
|
|
loadDefaultPrelude :: Repl ()
|
2023-12-30 22:15:35 +03:00
|
|
|
loadDefaultPrelude =
|
|
|
|
whenJustM
|
|
|
|
defaultPreludeEntryPoint
|
|
|
|
loadEntryPoint
|
2023-05-30 11:19:09 +03:00
|
|
|
|
2023-10-30 16:05:52 +03:00
|
|
|
getReplEntryPoint :: (Root -> a -> GlobalOptions -> IO EntryPoint) -> a -> Repl EntryPoint
|
2023-06-05 18:52:52 +03:00
|
|
|
getReplEntryPoint f inputFile = do
|
2023-10-30 16:05:52 +03:00
|
|
|
root <- Reader.asks (^. replRoot)
|
2023-05-30 11:19:09 +03:00
|
|
|
gopts <- State.gets (^. replStateGlobalOptions)
|
2023-10-30 16:05:52 +03:00
|
|
|
liftIO (set entryPointSymbolPruningMode KeepAll <$> f root inputFile gopts)
|
2023-06-05 18:52:52 +03:00
|
|
|
|
|
|
|
getReplEntryPointFromPrepath :: Prepath File -> Repl EntryPoint
|
2024-07-01 19:05:24 +03:00
|
|
|
getReplEntryPointFromPrepath = getReplEntryPoint (\r x -> runM . runTaggedLockPermissive . entryPointFromGlobalOptionsPre r (Just x))
|
2023-06-05 18:52:52 +03:00
|
|
|
|
|
|
|
getReplEntryPointFromPath :: Path Abs File -> Repl EntryPoint
|
2024-07-01 19:05:24 +03:00
|
|
|
getReplEntryPointFromPath = getReplEntryPoint (\r a -> runM . runTaggedLockPermissive . entryPointFromGlobalOptions r (Just a))
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
displayVersion :: String -> Repl ()
|
|
|
|
displayVersion _ = liftIO (putStrLn versionTag)
|
|
|
|
|
|
|
|
replCommand :: ReplOptions -> String -> Repl ()
|
2024-01-16 19:22:10 +03:00
|
|
|
replCommand opts input_ = catchAll $ do
|
2023-05-30 11:19:09 +03:00
|
|
|
ctx <- replGetContext
|
2023-12-30 22:15:35 +03:00
|
|
|
let tab = Core.computeCombinedInfoTable $ ctx ^. replContextArtifacts . artifactCoreModule
|
2024-01-16 19:22:10 +03:00
|
|
|
evalRes <- compileThenEval ctx input_
|
2023-05-30 11:19:09 +03:00
|
|
|
whenJust evalRes $ \n ->
|
|
|
|
if
|
|
|
|
| Info.member Info.kNoDisplayInfo (Core.getInfo n) -> return ()
|
|
|
|
| opts ^. replPrintValues ->
|
|
|
|
renderOutLn (Core.ppOut opts (toValue tab n))
|
|
|
|
| otherwise -> renderOutLn (Core.ppOut opts n)
|
|
|
|
where
|
|
|
|
compileThenEval :: ReplContext -> String -> Repl (Maybe Core.Node)
|
|
|
|
compileThenEval ctx s = compileString >>= mapM eval
|
|
|
|
where
|
|
|
|
artif :: Artifacts
|
|
|
|
artif = ctx ^. replContextArtifacts
|
|
|
|
|
|
|
|
eval :: Core.Node -> Repl Core.Node
|
|
|
|
eval n = do
|
2024-02-27 16:54:43 +03:00
|
|
|
gopts <- State.gets (^. replStateGlobalOptions)
|
2023-10-30 16:05:52 +03:00
|
|
|
ep <- getReplEntryPointFromPrepath (mkPrepath (toFilePath P.replPath))
|
2023-05-30 11:19:09 +03:00
|
|
|
let shouldDisambiguate :: Bool
|
2023-04-03 11:58:08 +03:00
|
|
|
shouldDisambiguate = not (opts ^. replNoDisambiguate)
|
2023-05-30 11:19:09 +03:00
|
|
|
(artif', n') <-
|
|
|
|
replFromEither
|
|
|
|
. run
|
|
|
|
. runReader ep
|
|
|
|
. runError @JuvixError
|
|
|
|
. runState artif
|
|
|
|
. runTransformations shouldDisambiguate (opts ^. replTransformations)
|
|
|
|
$ n
|
2024-02-27 16:54:43 +03:00
|
|
|
liftIO (doEvalIO' (project gopts ^. Core.optFieldSize) artif' n') >>= replFromEither
|
2023-05-30 11:19:09 +03:00
|
|
|
|
2024-02-27 16:54:43 +03:00
|
|
|
doEvalIO' :: Natural -> Artifacts -> Core.Node -> IO (Either JuvixError Core.Node)
|
|
|
|
doEvalIO' fsize artif' n =
|
2023-05-30 11:19:09 +03:00
|
|
|
mapLeft (JuvixError @Core.CoreError)
|
2024-02-27 16:54:43 +03:00
|
|
|
<$> Core.doEvalIO (Just fsize) False replDefaultLoc (Core.computeCombinedInfoTable $ artif' ^. artifactCoreModule) n
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
compileString :: Repl (Maybe Core.Node)
|
|
|
|
compileString = do
|
2024-11-01 17:42:18 +03:00
|
|
|
(artifacts, res) <- compileReplInputIO' ctx (strip (pack s))
|
2023-05-30 11:19:09 +03:00
|
|
|
res' <- replFromEither res
|
|
|
|
State.modify (over (replStateContext . _Just) (set replContextArtifacts artifacts))
|
|
|
|
return res'
|
|
|
|
|
|
|
|
core :: String -> Repl ()
|
2024-01-16 19:22:10 +03:00
|
|
|
core input_ = do
|
2023-05-30 11:19:09 +03:00
|
|
|
ctx <- replGetContext
|
|
|
|
opts <- Reader.asks (^. replOptions)
|
2024-11-01 17:42:18 +03:00
|
|
|
compileRes <- compileReplInputIO' ctx (strip (pack input_)) >>= replFromEither . snd
|
2023-05-30 11:19:09 +03:00
|
|
|
whenJust compileRes (renderOutLn . Core.ppOut opts)
|
|
|
|
|
2023-08-01 11:46:22 +03:00
|
|
|
dev :: String -> Repl ()
|
2024-01-16 19:22:10 +03:00
|
|
|
dev input_ = do
|
2023-08-01 11:46:22 +03:00
|
|
|
ctx <- replGetContext
|
|
|
|
if
|
2024-01-16 19:22:10 +03:00
|
|
|
| input_ == scoperStateCmd -> do
|
2023-08-01 11:46:22 +03:00
|
|
|
renderOutLn (Concrete.ppTrace (ctx ^. replContextArtifacts . artifactScoperState))
|
|
|
|
| otherwise ->
|
|
|
|
renderOutLn
|
|
|
|
( "Unrecognized command "
|
2024-01-16 19:22:10 +03:00
|
|
|
<> input_
|
2023-08-01 11:46:22 +03:00
|
|
|
<> "\nAvailable commands: "
|
|
|
|
<> unwords cmds
|
|
|
|
)
|
|
|
|
where
|
|
|
|
cmds :: [String]
|
|
|
|
cmds = [scoperStateCmd]
|
|
|
|
scoperStateCmd :: String
|
|
|
|
scoperStateCmd = "scoperState"
|
|
|
|
|
2023-08-25 19:37:23 +03:00
|
|
|
ppConcrete :: (Concrete.PrettyPrint a) => a -> Repl AnsiText
|
2023-05-30 11:19:09 +03:00
|
|
|
ppConcrete a = do
|
|
|
|
gopts <- State.gets (^. replStateGlobalOptions)
|
|
|
|
let popts :: GenericOptions = project' gopts
|
|
|
|
return (Concrete.ppOut popts a)
|
|
|
|
|
2023-08-25 19:37:23 +03:00
|
|
|
printConcrete :: (Concrete.PrettyPrint a) => a -> Repl ()
|
2023-05-30 19:19:39 +03:00
|
|
|
printConcrete = ppConcrete >=> renderOut
|
2023-05-30 11:19:09 +03:00
|
|
|
|
2023-08-25 19:37:23 +03:00
|
|
|
printConcreteLn :: (Concrete.PrettyPrint a) => a -> Repl ()
|
2023-05-30 19:19:39 +03:00
|
|
|
printConcreteLn = ppConcrete >=> renderOutLn
|
|
|
|
|
|
|
|
replParseIdentifiers :: String -> Repl (NonEmpty Concrete.ScopedIden)
|
2024-01-16 19:22:10 +03:00
|
|
|
replParseIdentifiers input_ =
|
|
|
|
replExpressionUpToScopedAtoms (strip (pack input_))
|
2023-05-30 11:19:09 +03:00
|
|
|
>>= getIdentifiers
|
|
|
|
where
|
|
|
|
getIdentifiers :: Concrete.ExpressionAtoms 'Concrete.Scoped -> Repl (NonEmpty Concrete.ScopedIden)
|
|
|
|
getIdentifiers as = mapM getIdentifier (as ^. Concrete.expressionAtoms)
|
|
|
|
where
|
|
|
|
getIdentifier :: Concrete.ExpressionAtom 'Concrete.Scoped -> Repl (Concrete.ScopedIden)
|
|
|
|
getIdentifier = \case
|
|
|
|
Concrete.AtomIdentifier a -> return a
|
|
|
|
Concrete.AtomParens p
|
|
|
|
| Concrete.ExpressionIdentifier a <- p -> return a
|
|
|
|
| Concrete.ExpressionParensIdentifier a <- p -> return a
|
|
|
|
_ -> err
|
|
|
|
where
|
|
|
|
err :: Repl a
|
2023-05-31 10:53:08 +03:00
|
|
|
err = replError (mkAnsiText @Text ":def expects one or more identifiers")
|
2023-05-30 11:19:09 +03:00
|
|
|
|
2023-12-30 22:15:35 +03:00
|
|
|
getScopedInfoTable :: Repl Scoped.InfoTable
|
|
|
|
getScopedInfoTable = do
|
|
|
|
artifs <- (^. replContextArtifacts) <$> replGetContext
|
|
|
|
let tab0 = artifs ^. artifactScopeTable
|
|
|
|
return $ tab0 <> computeCombinedScopedInfoTable (artifs ^. artifactModuleTable)
|
|
|
|
|
2023-05-30 19:19:39 +03:00
|
|
|
printDocumentation :: String -> Repl ()
|
|
|
|
printDocumentation = replParseIdentifiers >=> printIdentifiers
|
|
|
|
where
|
|
|
|
printIdentifiers :: NonEmpty Concrete.ScopedIden -> Repl ()
|
|
|
|
printIdentifiers (d :| ds) = do
|
|
|
|
printIdentifier d
|
|
|
|
whenJust (nonEmpty ds) $ \ds' -> replNewline >> printIdentifiers ds'
|
|
|
|
where
|
|
|
|
printIdentifier :: Concrete.ScopedIden -> Repl ()
|
|
|
|
printIdentifier s = do
|
2023-08-25 16:28:58 +03:00
|
|
|
let n = s ^. Concrete.scopedIdenFinal . Scoped.nameId
|
2023-07-26 10:59:50 +03:00
|
|
|
mdoc <- case getNameKind s of
|
|
|
|
KNameAxiom -> getDocAxiom n
|
|
|
|
KNameInductive -> getDocInductive n
|
|
|
|
KNameLocal -> return Nothing
|
|
|
|
KNameFunction -> getDocFunction n
|
|
|
|
KNameConstructor -> getDocConstructor n
|
|
|
|
KNameLocalModule -> impossible
|
|
|
|
KNameTopModule -> impossible
|
2023-08-25 16:28:58 +03:00
|
|
|
KNameAlias -> impossible
|
User-friendly operator declaration syntax (#2270)
* Closes #1964
Adds the possibility to define operator fixities. They live in a
separate namespace. Standard library defines a few in
`Stdlib.Data.Fixity`:
```
syntax fixity rapp {arity: binary, assoc: right};
syntax fixity lapp {arity: binary, assoc: left, same: rapp};
syntax fixity seq {arity: binary, assoc: left, above: [lapp]};
syntax fixity functor {arity: binary, assoc: right};
syntax fixity logical {arity: binary, assoc: right, above: [seq]};
syntax fixity comparison {arity: binary, assoc: none, above: [logical]};
syntax fixity pair {arity: binary, assoc: right};
syntax fixity cons {arity: binary, assoc: right, above: [pair]};
syntax fixity step {arity: binary, assoc: right};
syntax fixity range {arity: binary, assoc: right, above: [step]};
syntax fixity additive {arity: binary, assoc: left, above: [comparison, range, cons]};
syntax fixity multiplicative {arity: binary, assoc: left, above: [additive]};
syntax fixity composition {arity: binary, assoc: right, above: [multiplicative]};
```
The fixities are identifiers in a separate namespace (different from
symbol and module namespaces). They can be exported/imported and then
used in operator declarations:
```
import Stdlib.Data.Fixity open;
syntax operator && logical;
syntax operator || logical;
syntax operator + additive;
syntax operator * multiplicative;
```
2023-08-09 19:15:51 +03:00
|
|
|
KNameFixity -> impossible
|
2023-05-30 19:19:39 +03:00
|
|
|
printDoc mdoc
|
|
|
|
where
|
|
|
|
printDoc :: Maybe (Concrete.Judoc 'Concrete.Scoped) -> Repl ()
|
|
|
|
printDoc = \case
|
|
|
|
Nothing -> do
|
2023-11-03 14:51:45 +03:00
|
|
|
let s' :: Doc Ann = pretty s
|
|
|
|
msg = "No documentation available for" <+> s'
|
|
|
|
renderOutLn (toAnsiText True msg)
|
2023-05-30 19:19:39 +03:00
|
|
|
Just ju -> printConcrete ju
|
|
|
|
|
|
|
|
getDocFunction :: Scoped.NameId -> Repl (Maybe (Concrete.Judoc 'Concrete.Scoped))
|
|
|
|
getDocFunction fun = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let def = tbl ^?! Scoped.infoFunctions . at fun . _Just
|
|
|
|
return (def ^. Concrete.signDoc)
|
2023-05-30 19:19:39 +03:00
|
|
|
|
|
|
|
getDocInductive :: Scoped.NameId -> Repl (Maybe (Concrete.Judoc 'Concrete.Scoped))
|
|
|
|
getDocInductive ind = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let def :: Concrete.InductiveDef 'Concrete.Scoped = tbl ^?! Scoped.infoInductives . at ind . _Just
|
2023-05-30 19:19:39 +03:00
|
|
|
return (def ^. Concrete.inductiveDoc)
|
|
|
|
|
|
|
|
getDocAxiom :: Scoped.NameId -> Repl (Maybe (Concrete.Judoc 'Concrete.Scoped))
|
|
|
|
getDocAxiom ax = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let def :: Concrete.AxiomDef 'Concrete.Scoped = tbl ^?! Scoped.infoAxioms . at ax . _Just
|
2023-05-30 19:19:39 +03:00
|
|
|
return (def ^. Concrete.axiomDoc)
|
|
|
|
|
|
|
|
getDocConstructor :: Scoped.NameId -> Repl (Maybe (Concrete.Judoc 'Concrete.Scoped))
|
|
|
|
getDocConstructor c = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let def = tbl ^?! Scoped.infoConstructors . at c . _Just
|
|
|
|
return (def ^. Concrete.constructorDoc)
|
2023-05-30 19:19:39 +03:00
|
|
|
|
|
|
|
printDefinition :: String -> Repl ()
|
|
|
|
printDefinition = replParseIdentifiers >=> printIdentifiers
|
|
|
|
where
|
2023-05-30 11:19:09 +03:00
|
|
|
printIdentifiers :: NonEmpty Concrete.ScopedIden -> Repl ()
|
|
|
|
printIdentifiers (d :| ds) = do
|
|
|
|
printIdentifier d
|
|
|
|
whenJust (nonEmpty ds) $ \ds' -> replNewline >> printIdentifiers ds'
|
|
|
|
where
|
|
|
|
printIdentifier :: Concrete.ScopedIden -> Repl ()
|
2023-07-26 10:59:50 +03:00
|
|
|
printIdentifier s =
|
2023-08-25 16:28:58 +03:00
|
|
|
let n = s ^. Concrete.scopedIdenFinal . Scoped.nameId
|
2023-07-26 10:59:50 +03:00
|
|
|
in case getNameKind s of
|
|
|
|
KNameAxiom -> printAxiom n
|
|
|
|
KNameInductive -> printInductive n
|
|
|
|
KNameLocal -> return ()
|
|
|
|
KNameFunction -> printFunction n
|
|
|
|
KNameConstructor -> printConstructor n
|
|
|
|
KNameLocalModule -> impossible
|
|
|
|
KNameTopModule -> impossible
|
User-friendly operator declaration syntax (#2270)
* Closes #1964
Adds the possibility to define operator fixities. They live in a
separate namespace. Standard library defines a few in
`Stdlib.Data.Fixity`:
```
syntax fixity rapp {arity: binary, assoc: right};
syntax fixity lapp {arity: binary, assoc: left, same: rapp};
syntax fixity seq {arity: binary, assoc: left, above: [lapp]};
syntax fixity functor {arity: binary, assoc: right};
syntax fixity logical {arity: binary, assoc: right, above: [seq]};
syntax fixity comparison {arity: binary, assoc: none, above: [logical]};
syntax fixity pair {arity: binary, assoc: right};
syntax fixity cons {arity: binary, assoc: right, above: [pair]};
syntax fixity step {arity: binary, assoc: right};
syntax fixity range {arity: binary, assoc: right, above: [step]};
syntax fixity additive {arity: binary, assoc: left, above: [comparison, range, cons]};
syntax fixity multiplicative {arity: binary, assoc: left, above: [additive]};
syntax fixity composition {arity: binary, assoc: right, above: [multiplicative]};
```
The fixities are identifiers in a separate namespace (different from
symbol and module namespaces). They can be exported/imported and then
used in operator declarations:
```
import Stdlib.Data.Fixity open;
syntax operator && logical;
syntax operator || logical;
syntax operator + additive;
syntax operator * multiplicative;
```
2023-08-09 19:15:51 +03:00
|
|
|
KNameFixity -> impossible
|
2023-08-25 16:28:58 +03:00
|
|
|
KNameAlias -> impossible
|
2023-05-30 11:19:09 +03:00
|
|
|
where
|
2024-02-07 12:47:48 +03:00
|
|
|
printLocation :: (HasLoc c) => c -> Repl ()
|
2023-05-30 11:19:09 +03:00
|
|
|
printLocation def = do
|
|
|
|
s' <- ppConcrete s
|
|
|
|
let txt :: Text = " is " <> prettyText (nameKindWithArticle (getNameKind s)) <> " defined at " <> prettyText (getLoc def)
|
2023-05-31 10:53:08 +03:00
|
|
|
renderOutLn (s' <> mkAnsiText txt)
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
printFunction :: Scoped.NameId -> Repl ()
|
|
|
|
printFunction fun = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
2023-10-12 19:59:47 +03:00
|
|
|
case tbl ^. Scoped.infoFunctions . at fun of
|
|
|
|
Just def -> do
|
|
|
|
printLocation def
|
|
|
|
printConcreteLn def
|
|
|
|
Nothing -> return ()
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
printInductive :: Scoped.NameId -> Repl ()
|
|
|
|
printInductive ind = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let def :: Concrete.InductiveDef 'Concrete.Scoped = tbl ^?! Scoped.infoInductives . at ind . _Just
|
2023-05-30 11:19:09 +03:00
|
|
|
printLocation def
|
2023-05-30 19:19:39 +03:00
|
|
|
printConcreteLn def
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
printAxiom :: Scoped.NameId -> Repl ()
|
|
|
|
printAxiom ax = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let def :: Concrete.AxiomDef 'Concrete.Scoped = tbl ^?! Scoped.infoAxioms . at ax . _Just
|
2023-05-30 11:19:09 +03:00
|
|
|
printLocation def
|
2023-05-30 19:19:39 +03:00
|
|
|
printConcreteLn def
|
2023-05-30 11:19:09 +03:00
|
|
|
|
|
|
|
printConstructor :: Scoped.NameId -> Repl ()
|
|
|
|
printConstructor c = do
|
2023-12-30 22:15:35 +03:00
|
|
|
tbl :: Scoped.InfoTable <- getScopedInfoTable
|
|
|
|
let ind = tbl ^?! Scoped.infoConstructors . at c . _Just . Concrete.constructorInductiveName
|
2023-05-30 11:19:09 +03:00
|
|
|
printInductive (ind ^. Scoped.nameId)
|
|
|
|
|
|
|
|
inferType :: String -> Repl ()
|
2024-01-16 19:22:10 +03:00
|
|
|
inferType input_ = do
|
2023-05-30 11:19:09 +03:00
|
|
|
gopts <- State.gets (^. replStateGlobalOptions)
|
2024-01-16 19:22:10 +03:00
|
|
|
n <- replExpressionUpToTyped (strip (pack input_))
|
2023-05-30 11:19:09 +03:00
|
|
|
renderOutLn (Internal.ppOut (project' @GenericOptions gopts) (n ^. Internal.typedType))
|
|
|
|
|
2023-08-01 11:46:22 +03:00
|
|
|
replCommands :: ReplOptions -> [(String, String -> Repl ())]
|
|
|
|
replCommands opts = catchable ++ nonCatchable
|
2023-05-30 11:19:09 +03:00
|
|
|
where
|
|
|
|
nonCatchable :: [(String, String -> Repl ())]
|
|
|
|
nonCatchable =
|
|
|
|
[ ("quit", quit)
|
|
|
|
]
|
|
|
|
catchable :: [(String, String -> Repl ())]
|
|
|
|
catchable =
|
|
|
|
map
|
|
|
|
(second (catchAll .))
|
2023-08-01 11:46:22 +03:00
|
|
|
[ ("help", const (printHelpTxt opts)),
|
2022-11-07 16:47:56 +03:00
|
|
|
-- `multiline` is included here for auto-completion purposes only.
|
|
|
|
-- `repline`'s `multilineCommand` logic overrides this no-op.
|
2023-05-30 11:19:09 +03:00
|
|
|
(multilineCmd, const (return ())),
|
|
|
|
("load", loadFile . pSomeFile),
|
|
|
|
("reload", reloadFile),
|
2022-11-07 16:47:56 +03:00
|
|
|
("root", printRoot),
|
2023-05-30 11:19:09 +03:00
|
|
|
("def", printDefinition),
|
2023-05-30 19:19:39 +03:00
|
|
|
("doc", printDocumentation),
|
2022-11-07 16:47:56 +03:00
|
|
|
("type", inferType),
|
2022-11-07 20:43:30 +03:00
|
|
|
("version", displayVersion),
|
2023-08-01 11:46:22 +03:00
|
|
|
("core", core),
|
|
|
|
("dev", dev)
|
2022-11-07 16:47:56 +03:00
|
|
|
]
|
|
|
|
|
2024-02-07 12:47:48 +03:00
|
|
|
mapInputT_ :: (m () -> m ()) -> InputT m () -> InputT m ()
|
|
|
|
mapInputT_ f =
|
|
|
|
mkInputT
|
|
|
|
. mapReaderT
|
|
|
|
( mapReaderT
|
|
|
|
( mapReaderT
|
|
|
|
(mapReaderT (mapReaderT f))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
. unInputT
|
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
catchAll :: Repl () -> Repl ()
|
|
|
|
catchAll = Repline.dontCrash . catchJuvixError
|
|
|
|
where
|
|
|
|
catchJuvixError :: Repl () -> Repl ()
|
2024-02-07 12:47:48 +03:00
|
|
|
catchJuvixError = mkHaskelineT . mapInputT_ catchErrorS . unHaskelineT
|
2023-05-30 11:19:09 +03:00
|
|
|
where
|
2024-02-07 12:47:48 +03:00
|
|
|
printErrorS :: JuvixError -> ReplS ()
|
|
|
|
printErrorS e = do
|
|
|
|
opts <- State.gets (^. replStateGlobalOptions)
|
|
|
|
hasAnsi <- liftIO (Ansi.hSupportsANSIColor stderr)
|
|
|
|
liftIO
|
|
|
|
. hPutStrLn stderr
|
|
|
|
. run
|
|
|
|
. runReader (project' @GenericOptions opts)
|
2024-11-11 12:06:57 +03:00
|
|
|
$ Error.render (renderType opts hasAnsi) Nothing e
|
|
|
|
where
|
|
|
|
renderType :: GlobalOptions -> Bool -> Error.RenderType
|
|
|
|
renderType opts hasAnsi
|
|
|
|
| opts ^. globalVSCode = Error.RenderVSCode
|
|
|
|
| opts ^. globalNoColors || not hasAnsi = Error.RenderText
|
|
|
|
| otherwise = Error.RenderAnsi
|
2024-02-07 12:47:48 +03:00
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
catchErrorS :: ReplS () -> ReplS ()
|
|
|
|
catchErrorS = (`Except.catchError` printErrorS)
|
|
|
|
|
|
|
|
defaultMatcher :: [(String, CompletionFunc ReplS)]
|
|
|
|
defaultMatcher = [(":load", fileCompleter)]
|
|
|
|
|
|
|
|
optsCompleter :: WordCompleter ReplS
|
|
|
|
optsCompleter n = do
|
2023-08-01 11:46:22 +03:00
|
|
|
opts <- Reader.asks (^. replOptions)
|
|
|
|
let names = (":" <>) . fst <$> replCommands opts
|
2023-05-30 11:19:09 +03:00
|
|
|
return (filter (isPrefixOf n) names)
|
|
|
|
|
|
|
|
replBanner :: MultiLine -> Repl String
|
|
|
|
replBanner = \case
|
|
|
|
MultiLine -> return "... "
|
|
|
|
SingleLine -> do
|
|
|
|
mmodulePath <-
|
|
|
|
State.gets
|
|
|
|
( ^?
|
|
|
|
replStateContext
|
|
|
|
. _Just
|
|
|
|
. replContextArtifacts
|
|
|
|
. artifactMainModuleScope
|
|
|
|
. _Just
|
|
|
|
. scopePath
|
|
|
|
. absTopModulePath
|
|
|
|
)
|
|
|
|
return $ case mmodulePath of
|
|
|
|
Just path -> [i|#{unpack (P.prettyText path)}> |]
|
|
|
|
Nothing -> "juvix> "
|
|
|
|
|
|
|
|
replPrefix :: Maybe Char
|
|
|
|
replPrefix = Just ':'
|
|
|
|
|
|
|
|
replMultilineCommand :: Maybe String
|
|
|
|
replMultilineCommand = Just multilineCmd
|
|
|
|
|
|
|
|
replInitialiser :: Repl ()
|
|
|
|
replInitialiser = do
|
|
|
|
gopts <- State.gets (^. replStateGlobalOptions)
|
|
|
|
opts <- Reader.asks (^. replOptions)
|
|
|
|
welcomeMsg
|
|
|
|
unless
|
|
|
|
(opts ^. replNoPrelude || gopts ^. globalNoStdlib)
|
|
|
|
(maybe loadDefaultPrelude (loadFile . (^. pathPath)) (opts ^. replInputFile))
|
|
|
|
|
|
|
|
replFinaliser :: Repl ExitDecision
|
|
|
|
replFinaliser = return Exit
|
|
|
|
|
|
|
|
replTabComplete :: CompleterStyle ReplS
|
|
|
|
replTabComplete = Prefix (wordCompleter optsCompleter) defaultMatcher
|
|
|
|
|
|
|
|
printRoot :: String -> Repl ()
|
|
|
|
printRoot _ = do
|
2023-10-30 16:05:52 +03:00
|
|
|
r <- State.gets (^. replStateRoot . rootRootDir)
|
2024-11-01 17:42:18 +03:00
|
|
|
putStrLn (pack (toFilePath r))
|
2023-05-30 11:19:09 +03:00
|
|
|
|
2024-02-13 21:00:01 +03:00
|
|
|
runCommand :: (Members '[EmbedIO, App, TaggedLock] r) => ReplOptions -> Sem r ()
|
2023-05-30 11:19:09 +03:00
|
|
|
runCommand opts = do
|
2023-10-30 16:05:52 +03:00
|
|
|
root <- askRoot
|
2023-12-06 20:24:59 +03:00
|
|
|
pkg <- askPackage
|
2023-05-30 11:19:09 +03:00
|
|
|
let replAction :: ReplS ()
|
2022-12-20 15:05:40 +03:00
|
|
|
replAction = do
|
|
|
|
evalReplOpts
|
|
|
|
ReplOpts
|
2023-05-30 11:19:09 +03:00
|
|
|
{ prefix = replPrefix,
|
|
|
|
multilineCommand = replMultilineCommand,
|
|
|
|
initialiser = replInitialiser,
|
|
|
|
finaliser = replFinaliser,
|
|
|
|
tabComplete = replTabComplete,
|
|
|
|
command = replCommand opts,
|
2023-08-01 11:46:22 +03:00
|
|
|
options = replCommands opts,
|
2023-05-30 11:19:09 +03:00
|
|
|
banner = replBanner
|
2022-12-20 15:05:40 +03:00
|
|
|
}
|
2022-11-07 16:47:56 +03:00
|
|
|
globalOptions <- askGlobalOptions
|
2023-05-30 11:19:09 +03:00
|
|
|
let env =
|
|
|
|
ReplEnv
|
2023-10-30 16:05:52 +03:00
|
|
|
{ _replRoot = root,
|
2023-12-06 20:24:59 +03:00
|
|
|
_replOptions = opts,
|
|
|
|
_replPackage = pkg
|
2023-05-30 11:19:09 +03:00
|
|
|
}
|
|
|
|
iniState =
|
|
|
|
ReplState
|
2023-10-30 16:05:52 +03:00
|
|
|
{ _replStateRoot = root,
|
2023-05-30 11:19:09 +03:00
|
|
|
_replStateContext = Nothing,
|
|
|
|
_replStateGlobalOptions = globalOptions
|
|
|
|
}
|
|
|
|
e <-
|
2024-03-21 15:09:34 +03:00
|
|
|
liftIO
|
2023-05-30 11:19:09 +03:00
|
|
|
. Except.runExceptT
|
|
|
|
. (`State.evalStateT` iniState)
|
|
|
|
. (`Reader.runReaderT` env)
|
|
|
|
$ replAction
|
|
|
|
case e of
|
|
|
|
Left {} -> error "impossible: uncaught exception"
|
|
|
|
Right () -> return ()
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2023-04-13 12:27:39 +03:00
|
|
|
-- | If the package contains the stdlib as a dependency, loads the Prelude
|
|
|
|
defaultPreludeEntryPoint :: Repl (Maybe EntryPoint)
|
2022-11-10 14:26:38 +03:00
|
|
|
defaultPreludeEntryPoint = do
|
2023-10-30 16:05:52 +03:00
|
|
|
root <- State.gets (^. replStateRoot)
|
2023-11-07 21:11:02 +03:00
|
|
|
let buildRoot = root ^. rootRootDir
|
|
|
|
buildDir = resolveAbsBuildDir buildRoot (root ^. rootBuildDir)
|
2023-12-06 20:24:59 +03:00
|
|
|
pkg <- Reader.asks (^. replPackage)
|
2024-03-21 15:09:34 +03:00
|
|
|
mstdlibPath <- runM (runFilesIO (packageStdlib buildRoot buildDir (pkg ^. packageDependencies)))
|
2023-04-19 17:56:48 +03:00
|
|
|
case mstdlibPath of
|
2023-04-13 12:27:39 +03:00
|
|
|
Just stdlibPath ->
|
2023-09-05 18:11:17 +03:00
|
|
|
Just
|
|
|
|
. set entryPointResolverRoot stdlibPath
|
2023-10-30 16:05:52 +03:00
|
|
|
<$> getReplEntryPointFromPath (stdlibPath <//> P.preludePath)
|
2023-04-19 17:56:48 +03:00
|
|
|
Nothing -> return Nothing
|
2022-11-10 14:26:38 +03:00
|
|
|
|
2022-12-20 15:05:40 +03:00
|
|
|
replMakeAbsolute :: SomeBase b -> Repl (Path Abs b)
|
|
|
|
replMakeAbsolute = \case
|
|
|
|
Abs p -> return p
|
|
|
|
Rel r -> do
|
2023-10-30 16:05:52 +03:00
|
|
|
invokeDir <- State.gets (^. replStateRoot . rootInvokeDir)
|
2022-12-20 15:05:40 +03:00
|
|
|
return (invokeDir <//> r)
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
replExpressionUpToScopedAtoms :: Text -> Repl (Concrete.ExpressionAtoms 'Concrete.Scoped)
|
|
|
|
replExpressionUpToScopedAtoms txt = do
|
|
|
|
ctx <- replGetContext
|
|
|
|
x <-
|
2024-03-21 15:09:34 +03:00
|
|
|
runM
|
2023-05-30 11:19:09 +03:00
|
|
|
. runError
|
|
|
|
. evalState (ctx ^. replContextArtifacts)
|
|
|
|
. runReader (ctx ^. replContextEntryPoint)
|
2023-10-30 16:05:52 +03:00
|
|
|
$ expressionUpToAtomsScoped P.replPath txt
|
2023-05-30 11:19:09 +03:00
|
|
|
replFromEither x
|
|
|
|
|
|
|
|
replExpressionUpToTyped :: Text -> Repl Internal.TypedExpression
|
|
|
|
replExpressionUpToTyped txt = do
|
|
|
|
ctx <- replGetContext
|
|
|
|
x <-
|
2024-03-21 15:09:34 +03:00
|
|
|
runM
|
2023-05-30 11:19:09 +03:00
|
|
|
. runError
|
|
|
|
. evalState (ctx ^. replContextArtifacts)
|
|
|
|
. runReader (ctx ^. replContextEntryPoint)
|
2023-10-30 16:05:52 +03:00
|
|
|
$ expressionUpToTyped P.replPath txt
|
2023-05-30 11:19:09 +03:00
|
|
|
replFromEither x
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2024-11-01 17:42:18 +03:00
|
|
|
compileReplInputIO' :: (MonadIO m) => ReplContext -> Text -> m (Artifacts, (Either JuvixError (Maybe Core.Node)))
|
2023-05-08 13:23:15 +03:00
|
|
|
compileReplInputIO' ctx txt =
|
2023-03-30 14:39:27 +03:00
|
|
|
runM
|
2023-05-08 13:23:15 +03:00
|
|
|
. runState (ctx ^. replContextArtifacts)
|
|
|
|
. runReader (ctx ^. replContextEntryPoint)
|
|
|
|
$ do
|
2023-10-30 16:05:52 +03:00
|
|
|
r <- compileReplInputIO P.replPath txt
|
2023-05-08 13:23:15 +03:00
|
|
|
return (extractNode <$> r)
|
|
|
|
where
|
|
|
|
extractNode :: ReplPipelineResult -> Maybe Core.Node
|
|
|
|
extractNode = \case
|
|
|
|
ReplPipelineResultNode n -> Just n
|
|
|
|
ReplPipelineResultImport {} -> Nothing
|
2023-10-03 00:13:45 +03:00
|
|
|
ReplPipelineResultOpen {} -> Nothing
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2022-11-10 14:26:38 +03:00
|
|
|
render' :: (P.HasAnsiBackend a, P.HasTextBackend a) => a -> Repl ()
|
|
|
|
render' t = do
|
|
|
|
opts <- State.gets (^. replStateGlobalOptions)
|
|
|
|
hasAnsi <- liftIO (Ansi.hSupportsANSIColor stdout)
|
|
|
|
liftIO (P.renderIO (not (opts ^. globalNoColors) && hasAnsi) t)
|
2022-11-07 16:47:56 +03:00
|
|
|
|
2023-05-30 11:19:09 +03:00
|
|
|
replNewline :: Repl ()
|
|
|
|
replNewline = liftIO (putStrLn "")
|
|
|
|
|
2022-11-10 14:26:38 +03:00
|
|
|
renderOut :: (P.HasAnsiBackend a, P.HasTextBackend a) => a -> Repl ()
|
2023-05-30 11:19:09 +03:00
|
|
|
renderOut = render'
|
|
|
|
|
|
|
|
renderOutLn :: (P.HasAnsiBackend a, P.HasTextBackend a) => a -> Repl ()
|
|
|
|
renderOutLn t = renderOut t >> replNewline
|