mirror of
https://github.com/anoma/juvix.git
synced 2024-12-02 10:47:32 +03:00
6fcc9f21d2
Currently formatting a project is equivalent to running `juvix format`
on each individual file. Hence, the performance is quadratic wrt the
number of modules in the project. This pr fixes that and we now we only
process each module once.
# Benchmark (1236% faster 🚀)
Checking the standard library
```
hyperfine --warmup 1 'juvix format --check' 'juvix-main format --check'
Benchmark 1: juvix format --check
Time (mean ± σ): 450.6 ms ± 33.7 ms [User: 707.2 ms, System: 178.7 ms]
Range (min … max): 396.0 ms … 497.0 ms 10 runs
Benchmark 2: juvix-main format --check
Time (mean ± σ): 6.019 s ± 0.267 s [User: 9.333 s, System: 1.512 s]
Range (min … max): 5.598 s … 6.524 s 10 runs
Summary
juvix format --check ran
13.36 ± 1.16 times faster than juvix-main format --check
```
# Other changes:
1. The `EntryPoint` field `entryPointModulePath` is now optional.
2. I've introduced a new type `TopModulePathKey` which is analogous to
`TopModulePath` but wihout location information. It is used in hashmap
keys where the location in the key is never used. This is useful as we
can now get a `TopModulePathKey` from a `Path Rel File`.
3. I've refactored the `_formatInput` field in `FormatOptions` so that
it doesn't need to be a special case anymore.
4. I've introduced a new effect `Forcing` that allows to individually
force fields of a record type with a convenient syntax.
5. I've refactored some of the constraints in scoping so that they only
require `Reader Package` instead of `Reader EntryPoint`.
6. I've introduced a new type family so that local modules are no longer
required to have `ModuleId` from their type. Before, they were assigned
one, but it was never used.
# Future work:
1. For project-wise formatting, the compilation is done in parallel, but
the formatting is still done sequentially. That should be improved.
106 lines
3.7 KiB
Haskell
106 lines
3.7 KiB
Haskell
module BackendMarkdown.Negative where
|
|
|
|
import Base
|
|
import Juvix.Compiler.Backend.Markdown.Error
|
|
import Juvix.Compiler.Backend.Markdown.Translation.FromTyped.Source
|
|
import Juvix.Compiler.Concrete qualified as Concrete
|
|
import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping qualified as Scoper
|
|
import Juvix.Parser.Error
|
|
|
|
type FailMsg = String
|
|
|
|
data NegTest = NegTest
|
|
{ _name :: String,
|
|
_relDir :: Path Rel Dir,
|
|
_file :: Path Rel File,
|
|
_checkErr :: JuvixError -> Maybe FailMsg
|
|
}
|
|
|
|
testDescr :: NegTest -> TestDescr
|
|
testDescr NegTest {..} =
|
|
let tRoot = root <//> _relDir
|
|
file' = tRoot <//> _file
|
|
in TestDescr
|
|
{ _testName = _name,
|
|
_testRoot = tRoot,
|
|
_testAssertion = Single $ do
|
|
entryPoint <- testDefaultEntryPointIO tRoot file'
|
|
result <- testTaggedLockedToIO (runIOEither entryPoint upToScopingEntry)
|
|
case result of
|
|
Left err -> whenJust (_checkErr err) assertFailure
|
|
Right (_, pipelineRes) -> checkResult pipelineRes
|
|
}
|
|
where
|
|
checkResult :: PipelineResult Scoper.ScoperResult -> IO ()
|
|
checkResult PipelineResult {..} = do
|
|
let m = _pipelineResult ^. Scoper.resultModule
|
|
opts =
|
|
ProcessJuvixBlocksArgs
|
|
{ _processJuvixBlocksArgsConcreteOpts = Concrete.defaultOptions,
|
|
_processJuvixBlocksArgsUrlPrefix = "",
|
|
_processJuvixBlocksArgsIdPrefix = "",
|
|
_processJuvixBlocksArgsNoPath = True,
|
|
_processJuvixBlocksArgsExt = ".html",
|
|
_processJuvixBlocksArgsStripPrefix = "",
|
|
_processJuvixBlocksArgsComments = Comments mempty,
|
|
_processJuvixBlocksArgsFolderStructure = False,
|
|
_processJuvixBlocksArgsModule = m,
|
|
_processJuvixBlocksArgsOutputDir =
|
|
root <//> $(mkRelDir "markdown")
|
|
}
|
|
res = fromJuvixMarkdown' opts
|
|
case res of
|
|
Left err -> whenJust (_checkErr (JuvixError err)) assertFailure
|
|
Right _ -> assertFailure "Unexpected success"
|
|
|
|
allTests :: TestTree
|
|
allTests =
|
|
testGroup
|
|
"Markdown negative tests"
|
|
(map (mkTest . testDescr) tests)
|
|
|
|
root :: Path Abs Dir
|
|
root = relToProject $(mkRelDir "tests/negative")
|
|
|
|
wrongError :: JuvixError -> Maybe FailMsg
|
|
wrongError e = Just ("unexpected error: " <> unpack (renderTextDefault e))
|
|
|
|
tests :: [NegTest]
|
|
tests =
|
|
[ NegTest
|
|
"Empty file"
|
|
$(mkRelDir "Markdown")
|
|
$(mkRelFile "Empty.juvix.md")
|
|
$ \e -> case fromJuvixError e of
|
|
Just (ErrMarkdownBackend (ErrNoJuvixCodeBlocks _)) -> Nothing
|
|
_ -> wrongError e,
|
|
NegTest
|
|
"No Juvix code blocks"
|
|
$(mkRelDir "Markdown")
|
|
$(mkRelFile "NoJuvixCodeBlocks.juvix.md")
|
|
$ \e -> case fromJuvixError e of
|
|
Just (ErrMarkdownBackend (ErrNoJuvixCodeBlocks _)) -> Nothing
|
|
_ -> wrongError e,
|
|
NegTest
|
|
"extract-module-statements with no local module"
|
|
$(mkRelDir "Markdown")
|
|
$(mkRelFile "InvalidExtractModuleBlockNoModule.juvix.md")
|
|
$ \e -> case fromJuvixError e of
|
|
Just (ErrInvalidExtractModuleBlock _) -> Nothing
|
|
_ -> wrongError e,
|
|
NegTest
|
|
"extract-module-statements with no local module"
|
|
$(mkRelDir "Markdown")
|
|
$(mkRelFile "InvalidExtractModuleBlockNotJustModule.juvix.md")
|
|
$ \e -> case fromJuvixError e of
|
|
Just (ErrInvalidExtractModuleBlock _) -> Nothing
|
|
_ -> wrongError e,
|
|
NegTest
|
|
"code block with both hide and extract-module-statements"
|
|
$(mkRelDir "Markdown")
|
|
$(mkRelFile "InvalidCodeBlockExtraAttribute.juvix.md")
|
|
$ \e -> case fromJuvixError e of
|
|
Just (ErrInvalidCodeBlockAttribtues _) -> Nothing
|
|
_ -> wrongError e
|
|
]
|