1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-14 08:27:03 +03:00
juvix/test/Scope/Positive.hs
Paul Cadman 0a8799a344
Use a Juvix source file to define a package (#2434)
Depends on:
* ~~https://github.com/anoma/juvix/pull/2459~~
* https://github.com/anoma/juvix/pull/2462

This PR is part of a series implementing:
* https://github.com/anoma/juvix/issues/2336

This PR adds the package file loading function, including a file
evaluation effect. It integrates this with the existing `readPackage`
function and adds tests / smoke tests.

## Package.juvix format

Instead of `juvix.yaml` (which is still supported currently) users can
now place a `Package.juvix` file in the root of their project. The
simplest `Package.juvix` file you can write is:

```
module Package;

import PackageDescription open;

package : Package := defaultPackage;
```

The
[PackageDescription](35b2f618f0/include/package/PackageDescription.juvix)
module defines the `Package` type. Users can use "go-to definition" in
their IDE from the Package file to see the documentation and
definitions.

Users may also import `Stdlib.Prelude` in their Package file. This is
loaded from the global project. No other module imports are supported.

Notes:
* If a directory contains both `Package.juvix` and `juvix.yaml` then
`Package.juvix` is used in preference.

## Default stdlib dependency

The `Dependency` type has a constructor called `defaultStdlib`. This
means that any project can use the compiler builtin standard library
dependency. With `juvix.yaml` this dependency is only available when the
`dependencies` field is unspecified.

```
module Package;

import PackageDescription open;

package : Package := defaultPackage { dependencies := [defaultStdlib] };
```

## Validation

As well as the standard type checking validation that the Juvix compiler
provides additional validation is made on the file.

* The Package module must contain the identifier `package` and it must
have type `Package` that's obtained from the global `PackageDescription`
module.
* Every dependency specified in the Package.juvix must be unique.

* Closes https://github.com/anoma/juvix/issues/2336

## Examples

### Package with name and version

```
module Package;

import PackageDescription open;

package : Package :=
  defaultPackage {name := "a-package";
                  version := mkVersion 0 1 0};
```

### Package with GitHub dependency

```
module Package;

import PackageDescription open;

package : Package :=
  defaultPackage {name := "a-package";
                  version := mkVersion 0 1 0;
                  dependencies := [defaultStdlib;
                                   github (org := "anoma";
                                           repo := "juvix-containers";
                                           ref := "v0.7.1")]};
```

## Package with main and buildDir fields

```
module Package;

import Stdlib.Prelude open;
import PackageDescription open;

package : Package :=
  defaultPackage {name := "a-package";
                  version := mkVersion 0 1 0;
                  dependencies := [defaultStdlib;
                                   github (org := "anoma";
                                           repo := "juvix-containers";
                                           ref := "v0.7.1")];
                  buildDir := just "/tmp/build";
                  main := just "HelloWorld.juvix"
                };

```
2023-10-27 12:35:20 +01:00

289 lines
9.5 KiB
Haskell

module Scope.Positive where
import Base
import Data.HashMap.Strict qualified as HashMap
import Juvix.Compiler.Builtins (evalTopBuiltins)
import Juvix.Compiler.Concrete qualified as Concrete
import Juvix.Compiler.Concrete.Data.Highlight (ignoreHighlightBuilder)
import Juvix.Compiler.Concrete.Extra
import Juvix.Compiler.Concrete.Print qualified as P
import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.PathResolver
import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping qualified as Scoper
import Juvix.Compiler.Concrete.Translation.FromSource qualified as Parser
import Juvix.Compiler.Pipeline.Package.Loader.Error
import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO
import Juvix.Compiler.Pipeline.Package.Loader.PathResolver
import Juvix.Compiler.Pipeline.Setup
import Juvix.Data.Effect.Git
import Juvix.Data.Effect.Process
import Juvix.Prelude.Aeson
import Juvix.Prelude.Pretty
data PathResolverMode
= FullPathResolver
| PackagePathResolver
deriving stock (Eq)
data PosTest = PosTest
{ _name :: String,
_relDir :: Path Rel Dir,
_file :: Path Rel File,
_pathResolverMode :: PathResolverMode
}
posTest :: String -> Path Rel Dir -> Path Rel File -> PosTest
posTest _name _relDir _file = PosTest {_pathResolverMode = FullPathResolver, ..}
makeLenses ''PosTest
root :: Path Abs Dir
root = relToProject $(mkRelDir "tests/positive")
renderCodeNew :: (P.PrettyPrint c) => c -> Text
renderCodeNew = toPlainText . P.ppOutNoComments P.defaultOptions
testDescr :: PosTest -> TestDescr
testDescr PosTest {..} = helper renderCodeNew
where
helper :: (forall c. (HasLoc c, P.PrettyPrint c) => c -> Text) -> TestDescr
helper renderer =
let tRoot = root <//> _relDir
file' = tRoot <//> _file
in TestDescr
{ _testName = _name,
_testRoot = tRoot,
_testAssertion = Steps $ \step -> do
entryPoint <- defaultEntryPointCwdIO file'
let runHelper :: HashMap (Path Abs File) Text -> Sem PipelineEff a -> IO (ResolverState, a)
runHelper files = do
let runPathResolver' = case _pathResolverMode of
FullPathResolver -> runPathResolverPipe
PackagePathResolver -> runPackagePathResolver' (entryPoint ^. entryPointResolverRoot)
runM
. evalInternetOffline
. ignoreHighlightBuilder
. runErrorIO' @JuvixError
. evalTopBuiltins
. evalTopNameIdGen
. runFilesPure files tRoot
. runReader entryPoint
. ignoreLog
. runProcessIO
. mapError (JuvixError @GitProcessError)
. runGitProcess
. mapError (JuvixError @DependencyError)
. mapError (JuvixError @PackageLoaderError)
. runEvalFileEffIO
. runPathResolver'
evalHelper :: HashMap (Path Abs File) Text -> Sem PipelineEff a -> IO a
evalHelper files = fmap snd . runHelper files
step "Parsing"
p :: Parser.ParserResult <- snd <$> runIO' entryPoint upToParsing
step "Scoping"
(resolverState :: ResolverState, s :: Scoper.ScoperResult) <-
runIO'
entryPoint
( do
void (entrySetup defaultDependenciesConfig)
Concrete.fromParsed p
)
let yamlFiles :: [(Path Abs File, Text)]
yamlFiles =
[ (pkgi ^. packageRoot <//> juvixYamlFile, encodeToText (rawPackage (pkgi ^. packagePackage)))
| pkgi <- (^. resolverCacheItemPackage) <$> toList (resolverState ^. resolverCache)
]
fsScoped :: HashMap (Path Abs File) Text
fsScoped =
HashMap.fromList $
[ (getModuleFilePath m, renderer m)
| m <- toList (s ^. Scoper.resultScoperTable . Scoper.infoModules)
]
<> yamlFiles
fsParsed :: HashMap (Path Abs File) Text
fsParsed =
HashMap.fromList $
[ (getModuleFilePath m, renderCodeNew m)
| m <- toList (p ^. Parser.resultTable . Parser.infoParsedModules)
]
<> yamlFiles
step "Parsing pretty scoped"
p' :: Parser.ParserResult <- evalHelper fsScoped upToParsing
step "Parsing pretty parsed"
parsedPretty' :: Parser.ParserResult <- evalHelper fsParsed upToParsing
step "Scoping the scoped"
s' :: Scoper.ScoperResult <- evalHelper fsScoped upToScoping
step "Checks"
let smodules = s ^. Scoper.resultModules
smodules' = s' ^. Scoper.resultModules
let pmodules = p ^. Parser.resultModules
pmodules' = p' ^. Parser.resultModules
parsedPrettyModules = parsedPretty' ^. Parser.resultModules
assertEqDiffShow "check: scope . parse . pretty . scope . parse = scope . parse" smodules smodules'
assertEqDiffShow "check: parse . pretty . scope . parse = parse" pmodules pmodules'
assertEqDiffShow "check: parse . pretty . parse = parse" pmodules parsedPrettyModules
}
allTests :: TestTree
allTests =
testGroup
"Scope positive tests"
(map (mkTest . testDescr) tests)
tests :: [PosTest]
tests =
[ posTest
"Inductive"
$(mkRelDir ".")
$(mkRelFile "Inductive.juvix"),
posTest
"Pipes symbol as possible prefix for each data constructor"
$(mkRelDir ".")
$(mkRelFile "InductivePipes.juvix"),
posTest
"Imports and qualified names"
$(mkRelDir "Imports")
$(mkRelFile "A.juvix"),
posTest
"Data.Bool from the stdlib"
$(mkRelDir "StdlibList")
$(mkRelFile "Data/Bool.juvix"),
posTest
"Data.Nat from the stdlib"
$(mkRelDir "StdlibList")
$(mkRelFile "Data/Nat.juvix"),
posTest
"Data.Ord from the stdlib"
$(mkRelDir "StdlibList")
$(mkRelFile "Data/Ord.juvix"),
posTest
"Data.Product from the stdlib"
$(mkRelDir "StdlibList")
$(mkRelFile "Data/Product.juvix"),
posTest
"Data.List and friends from the stdlib"
$(mkRelDir "StdlibList")
$(mkRelFile "Data/List.juvix"),
posTest
"Operators (+)"
$(mkRelDir ".")
$(mkRelFile "Operators.juvix"),
posTest
"Literals"
$(mkRelDir ".")
$(mkRelFile "Literals.juvix"),
posTest
"Axiom with backends"
$(mkRelDir ".")
$(mkRelFile "Axiom.juvix"),
posTest
"Multiple modules non-ambiguous symbol - same file"
$(mkRelDir "QualifiedSymbol")
$(mkRelFile "M.juvix"),
posTest
"Multiple modules non-ambiguous symbol"
$(mkRelDir "QualifiedSymbol2")
$(mkRelFile "N.juvix"),
posTest
"Multiple modules constructor non-ambiguous symbol"
$(mkRelDir "QualifiedConstructor")
$(mkRelFile "M.juvix"),
posTest
"Parsing"
$(mkRelDir ".")
$(mkRelFile "Parsing.juvix"),
posTest
"open overrides open public"
$(mkRelDir ".")
$(mkRelFile "ShadowPublicOpen.juvix"),
posTest
"Infix chains"
$(mkRelDir ".")
$(mkRelFile "Ape.juvix"),
posTest
"Import embedded standard library"
$(mkRelDir "StdlibImport")
$(mkRelFile "StdlibImport.juvix"),
posTest
"Basic dependencies"
$(mkRelDir "Dependencies")
$(mkRelFile "Input.juvix"),
posTest
"Check Valid Symbols"
$(mkRelDir ".")
$(mkRelFile "Symbols.juvix"),
posTest
"Builtin bool"
$(mkRelDir ".")
$(mkRelFile "BuiltinsBool.juvix"),
posTest
"Type signature with body"
$(mkRelDir ".")
$(mkRelFile "SignatureWithBody.juvix"),
posTest
"Case expressions"
$(mkRelDir "Internal")
$(mkRelFile "Case.juvix"),
posTest
"Qualified imports"
$(mkRelDir "QualifiedImports")
$(mkRelFile "Main.juvix"),
posTest
"Short syntax for multiple parameters"
$(mkRelDir ".")
$(mkRelFile "MultiParams.juvix"),
posTest
"Shadow imported symbol"
$(mkRelDir "ImportShadow")
$(mkRelFile "Main.juvix"),
posTest
"Judoc"
$(mkRelDir ".")
$(mkRelFile "Judoc.juvix"),
posTest
"Pragmas"
$(mkRelDir ".")
$(mkRelFile "Pragmas.juvix"),
posTest
"Import as open"
$(mkRelDir "ImportAsOpen")
$(mkRelFile "Main.juvix"),
posTest
"Iterators"
$(mkRelDir ".")
$(mkRelFile "Iterators.juvix"),
posTest
"New function syntax"
$(mkRelDir ".")
$(mkRelFile "Syntax.juvix"),
posTest
"Format pragma"
$(mkRelDir ".")
$(mkRelFile "FormatPragma.juvix"),
posTest
"Namespaces"
$(mkRelDir ".")
$(mkRelFile "Namespaces.juvix"),
posTest
"Adt"
$(mkRelDir ".")
$(mkRelFile "Adt.juvix"),
posTest
"Let open"
$(mkRelDir ".")
$(mkRelFile "LetOpen.juvix"),
PosTest
"Package file"
$(mkRelDir "package")
$(mkRelFile "Package.juvix")
PackagePathResolver
]