1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-13 11:16:48 +03:00
juvix/test/Base.hs
Paul Cadman a9995b8e1c
Add nockma evaluator (#2564)
This PR adds an parser, pretty printer, evaluator, repl and quasi-quoter
for Nock terms.

## Parser / Pretty Printer

The parser and pretty printer handle both standard Nock terms and
'pretty' Nock terms (where op codes and paths can be named). Standard
and pretty Nock forms can be mixed in the same term.

For example instead of `[0 2]` you can write `[@ L]`.

See
a6028b0d92/src/Juvix/Compiler/Nockma/Language.hs (L79)
for the correspondence between pretty Nock and Nock operators.

In pretty Nock, paths are represented as strings of `L` (for head) and
`R` (for tail) instead of the number encoding in standard nock. The
character `S` is used to refer to the whole subject, i.e it is sugar for
`1` in standard Nock.

See
a6028b0d92/src/Juvix/Compiler/Nockma/Language.hs (L177)
for the correspondence between pretty Nock path and standard Nock
position.

## Quasi-quoter

A quasi-quoter is added so Nock terms can be included in the source, e.g
`[nock| [@ LL] |]`.

## REPL

Launch the repl with `juvix dev nockma repl`.

A Nock `[subject formula]` cell is input as `subject / formula` , e.g:

```
nockma>  [1 0] / [@ L]
1
```

The subject can be set using `:set-stack`.

```
nockma> :set-stack [1 0]
nockma> [@ L]
1
```

The subject can be viewed using `:get-stack`.

```
nockma> :set-stack [1 0]
nockma> :get-stack
[1 0]
```

You can assign a Nock term to a variable and use it in another
expression:

```
nockma> r := [@ L]
nockma> [1 0] / r
1
```

A list of assignments can be read from a file:

```
$ cat stack.nock
r := [@ L]
$ juvix dev nockma repl
nockma> :load stack.nock
nockma> [1 0] / r
1
```

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

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2024-01-11 12:04:38 +00:00

118 lines
3.4 KiB
Haskell

module Base
( module Test.Tasty,
module Test.Tasty.HUnit,
module Juvix.Prelude,
module Base,
module Juvix.Extra.Paths,
module Juvix.Prelude.Env,
module Juvix.Compiler.Pipeline.Run,
module Juvix.Compiler.Pipeline.EntryPoint.IO,
)
where
import Control.Monad.Extra as Monad
import Data.Algorithm.Diff
import Data.Algorithm.DiffOutput
import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.Termination
import Juvix.Compiler.Pipeline.EntryPoint.IO
import Juvix.Compiler.Pipeline.Loader.PathResolver
import Juvix.Compiler.Pipeline.Run
import Juvix.Data.Effect.TaggedLock
import Juvix.Extra.Paths hiding (rootBuildDir)
import Juvix.Prelude hiding (assert)
import Juvix.Prelude.Env
import Test.Tasty
import Test.Tasty.HUnit hiding (assertFailure)
import Test.Tasty.HUnit qualified as HUnit
data AssertionDescr
= Single Assertion
| Steps ((String -> IO ()) -> Assertion)
data TestDescr = TestDescr
{ _testName :: String,
_testRoot :: Path Abs Dir,
-- | relative to root
_testAssertion :: AssertionDescr
}
newtype WASMInfo = WASMInfo
{ _wasmInfoActual :: Path Abs File -> IO Text
}
makeLenses ''TestDescr
data StdlibMode
= StdlibInclude
| StdlibExclude
deriving stock (Show, Eq)
data CompileMode
= WASI StdlibMode
| WASM WASMInfo
mkTest :: TestDescr -> TestTree
mkTest TestDescr {..} = case _testAssertion of
Single assertion -> testCase _testName (withCurrentDir _testRoot assertion)
Steps steps -> testCaseSteps _testName (withCurrentDir _testRoot . steps)
assertEqDiffText :: String -> Text -> Text -> Assertion
assertEqDiffText = assertEqDiff unpack
assertEqDiff :: (Eq a) => (a -> String) -> String -> a -> a -> Assertion
assertEqDiff show_ msg a b
| a == b = return ()
| otherwise = do
putStrLn (pack $ ppDiff (getGroupedDiff pa pb))
putStrLn "End diff"
Monad.fail msg
where
pa = lines $ show_ a
pb = lines $ show_ b
assertEqDiffShow :: (Eq a, Show a) => String -> a -> a -> Assertion
assertEqDiffShow = assertEqDiff show
assertCmdExists :: Path Rel File -> Assertion
assertCmdExists cmd =
assertBool ("Command: " <> toFilePath cmd <> " is not present on $PATH")
. isJust
=<< findExecutable cmd
testTaggedLockedToIO :: Sem PipelineAppEffects a -> IO a
testTaggedLockedToIO =
runFinal
. resourceToIOFinal
. embedToFinal @IO
. runTaggedLock LockModeExclusive
testRunIO ::
forall a.
EntryPoint ->
Sem (PipelineEff PipelineAppEffects) a ->
IO (ResolverState, PipelineResult a)
testRunIO e = testTaggedLockedToIO . runIO defaultGenericOptions e
testDefaultEntryPointIO :: Path Abs Dir -> Path Abs File -> IO EntryPoint
testDefaultEntryPointIO cwd mainFile = testTaggedLockedToIO (defaultEntryPointIO cwd mainFile)
testDefaultEntryPointNoFileIO :: Path Abs Dir -> IO EntryPoint
testDefaultEntryPointNoFileIO cwd = testTaggedLockedToIO (defaultEntryPointNoFileIO cwd)
testRunIOEither ::
EntryPoint ->
Sem (PipelineEff PipelineAppEffects) a ->
IO (Either JuvixError (ResolverState, PipelineResult a))
testRunIOEither entry = testTaggedLockedToIO . runIOEither entry
testRunIOEitherTermination ::
EntryPoint ->
Sem (Termination ': PipelineEff PipelineAppEffects) a ->
IO (Either JuvixError (ResolverState, PipelineResult a))
testRunIOEitherTermination entry =
testRunIOEither entry
. evalTermination iniTerminationState
assertFailure :: (MonadIO m) => String -> m a
assertFailure = liftIO . HUnit.assertFailure