1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-02 10:47:32 +03:00
Commit Graph

1582 Commits

Author SHA1 Message Date
Paul Cadman
823b37c216
Release 0.6.2 (#2795)
This PR updates:

- [x] Package version
- [x] Smoke test
- [x] Changelog
2024-06-01 11:33:36 +02:00
Paul Cadman
88b4cb88f0
Use little endian encoding of ByteStrings in Anoma (#2793)
Anoma decodes integer atoms as bytes assuming a little endian layout.


ea25f88cea/lib/noun.ex (L182)

This commit adds functions `byteStringToIntegerLE` and
`integerToByteStringLE` that makes it clear that little endian encoding
is being used.
2024-05-31 18:20:48 +02:00
Jan Mas Rovira
e9afdad82a
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 12:41:30 +01:00
Łukasz Czajka
cfaa176af9
Update stdlib to the main branch (#2791)
* Update Juvix Standard Library to its main branch
* This include the Pedersen hash commit
2024-05-29 14:44:07 +02:00
Łukasz Czajka
55598e0f95
Rust backend (#2787)
* Implements code generation through Rust.
* CLI: adds two `dev` compilation targets: 
  1. `rust` for generating Rust code
  2. `native-rust` for generating a native executable via Rust
* Adds end-to-end tests for compilation from Juvix to native executable
via Rust.
* A target for RISC0 needs to be added in a separate PR building on this
one.
2024-05-29 13:34:04 +02:00
Paul Cadman
9faa88d4da
Add support for Strings in the Anoma backend (#2789)
This PR adds support for the `String` type, String literals and string
concatenation to the Nockma backend. Support for the builtins `show` and
`intToString` is not supported.

### Example

test079.juvix
```
module test079;

import Stdlib.Prelude open;

main (s : String) : String :=
  s ++str " " ++str " héllo" ++str " " ++str "world ";
```

args.nockma
```
[quote "Juvix!"]
```

```
$ juvix compile anoma test079.juvix
$ juvix dev nockma run test079.pretty.nockma --args args.nockma
"Juvix!  héllo world "
```

### String representation

A String is a sequence of UTF-8 encoded bytes. We interpret these bytes
as a sequence of bits to represent the string as an integer atom in
nockma.

For example:

The string `"a"` is UTF-8 encoded as `97` which is `0b1100001` in bits.

The string `"ab"` is UTF-8 encoded at the pair of bytes: `97 98` which
is `0b1100001 0b1100010`.

When we combine the bytes into a single sequence of bits we must take
care to pad each binary representation with zeros to each byte boundary.

So the binary representation of `"ab"` as an atom is `0b110000101100010`
or `24930` as an integer atom.

### String concatenation

We use the
[cat](ea25f88cea/hoon/anoma.hoon (L215))
function in the Anoma stdlib to concatenate the bytes representing two
strings.

We need to use the block parameter `3` in the Anoma call because we want
to treat the atoms representing the strings as sequences of bytes (= 2^3
bits).

To find the relevant Nock code to call `cat` with block parameter `3` we
use the urbit dojo as follows:

```
 =>  anoma  !=(~(cat block 3))
[8 [9 10 0 7] 9 4 10 [6 7 [0 3] 1 3] 0 2]
```

### Stdlib intercept in Evaluator

The evaluator has support for strings using `AtomHint`s, so strings can
be printed and traced. The stdlib `cat` call is also intercepted because
evaluating the unjetted hoon version is slow.

### String support in pretty nockma

In a pretty nockma file or `nock` quasi-quote you can write double
quoted string literals, e.g "abc". These are automatically translated to
UTF-8 integer atoms as in the previous section.
2024-05-28 17:20:19 +01:00
Paul Cadman
e30905ae95
Support Anoma stdlib APIs sign and verify (#2788)
This PR adds support for the Anoma stdlib `sign` and `verify` APIs.

```
builtin anoma-sign
axiom anomaSign : {A : Type}
  -- message to sign
  -> A
  -- secret key
  -> Nat
  -- signed message
  -> Nat;

builtin anoma-verify
axiom anomaVerify : {A : Type}
  -- signed message to verify 
  -> Nat 
  -- public key
  -> Nat 
  -- message with signature removed
  -> A;
```

These correspond to the
[`sign`](https://hexdocs.pm/enacl/enacl.html#sign-2) and
[`sign_open`](https://hexdocs.pm/enacl/enacl.html#sign_open-2) APIs from
libsodium respectively.

If signature verification fails in `anomaVerify`, the Anoma program
exits. We copy this behaviour in the evaluator by throwing an error in
this case.

## Notes

The Haskell Ed25519 library does not support `sign_open`. Its
verification function returns Bool, i.e it checks that the signature is
valid. The signed message is simply the concatenation of the signature
(64 bytes) and the original message so I added a function to remove the
signature from a signed message.
2024-05-28 09:02:03 +01:00
Paul Cadman
d233bbd704 Install cargo in static binary build
This is now required by the runtime build target
2024-05-23 13:53:28 +01:00
Paul Cadman
830bf04275
Support Anoma stdlib API verifyDetached (#2785)
This PR adds support for`anomaVerifyDetached` stdlib API via a Juvix
builtin.

It has signature:

```
builtin anoma-verify-detached
axiom anomaVerifyDetached : {A : Type}
   --- signature
  -> Nat 
   --- message
  -> A
   --- public key
  -> Nat
 -> Bool;
```

The [ed25519](https://hackage.haskell.org/package/ed25519) library is
used in the evaluator becuase Anoma uses ed25519 signatures
(https://hexdocs.pm/enacl/enacl.html).

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-05-23 13:40:05 +01:00
Paul Cadman
d697cd58a1
Update Anoma nock stdlib (#2786)
This stdlib contains the cryptographic stdlib APIs, sign, verify,
sign-detatched (sic) and verify-detatched (sic)

It is obtained from
c28de348c1/lib/nock.ex (L469)

As noted in the comment the stdlibPaths are obtained using the Urbit
dojo. Run:

```
=>  anoma  !=(s)
```
where s is a stdlib symbol

 eg:
```
=>  anoma  !=(add)
      [9 20 0 63]
```
2024-05-23 13:17:35 +02:00
Łukasz Czajka
161a34c36b
Optional braces in case syntax (#2778)
* Closes #2769 
* Removes old case syntax
* Pretty printing doesn't print braces in `case` if the `case` is a
"top" expression in a definition.
2024-05-22 18:14:03 +01:00
Łukasz Czajka
7e737d7037
Rust runtime (#2782)
* Closes #2781 
* This PR only implements the Rust runtime. The Rust backend / code
generation need to be implemented in a separate PR.
* The tests are unit tests for different modules and tests with
"manually" compiled Juvix programs.
* Adds building & testing of the Rust runtime to the CI.
2024-05-22 12:26:51 +02:00
Paul Cadman
60bffcfeb8
refactor: Add Anoma Node in Tree language (#2784)
Similarly to how the Cairo operations are handled we add a separate Tree
language Node for Anoma operations instead of handling them as an Unop
Node.

This is necessary because we need to add support for new Anoma
operations that are not unary.

This PR also adds support for `anoma-encode` and `anoma-decode`
functions in `jvt` tree source files which was missed in the previous
PRs.
2024-05-17 09:14:05 +01:00
Paul Cadman
52f8afdb2b
Add support for anoma-decode builtin (#2775)
This PR adds support for the `anoma-decode` builtin

```
builtin anoma-decode
axiom anomaDecode : {A : Type} -> Nat -> A
```

Adds:
* An implementation of the `cue` function in Haskell
* Unit tests for `cue`
* A benchmark for `cue` applied to the Anoma / nockma stdlib

Benchmark results:

```
      cue (jam stdlib): OK
        36.0 ms ± 2.0 ms
```

Closes:
*  https://github.com/anoma/juvix/issues/2764
2024-05-15 18:30:17 +01:00
Łukasz Czajka
325d43f172
Support type synonyms in instance types (#2772)
* Closes #2358
2024-05-15 14:29:44 +02:00
Paul Cadman
47b3b199cf
justfile: replace enableOptimized flag with disableOptimized (#2783)
`just install` now builds the optimized binary by default.

use `just disableOptimized=yes install` to build the non-optimized
binary (with faster build time).
2024-05-15 12:54:50 +02:00
Jan Mas Rovira
e9a7e94fdb
Effects for finite and infinite supplies (#2780)
This pr refactors the `Input` effect. It is now meant to be used with
finite input lists.

It also introduces the `StreamOf` effect, which is meant to be used for
infinite supplies.

Both have static implementations so they should add negligible overhead
when used.
2024-05-15 10:00:42 +02:00
Łukasz Czajka
d59d02ccb7
Bugfix: modules associated with inductive types should be declared after their inductive types (#2768)
* Closes #2763. 
* Fixes a bug in the scoper, likely introduced in
https://github.com/anoma/juvix/pull/2468 by making later declarations
depend on earlier ones. The problem was that the inductive modules were
always added at the beginning of a section, which resulted in an
incorrect definition dependency graph (an inductive type depended on its
associated projections).
* Now inductive modules are added just after a group of inductive
definitions, before the next function definition. This implies that
inductive type definitions which depend on each other cannot be
separated by function definitions. Existing Juvix code needs to be
adjusted.
* The behaviour is now equivalent to "manually" inserting module
declarations with projections after each group of inductive definitions.
2024-05-14 19:32:22 +02:00
Paul Cadman
1ab94f5537
Add support for anoma-encode builtin (#2766)
This PR adds support for the `anoma-encode` builtin:

```
builtin anoma-encode
axiom anomaEncode : {A : Type} -> A -> Nat
```

In the backend this is compiled to a call to the Anoma / nockma stdlib
`jam` function.

This PR also contains:
* An implementation of the `jam` function in Haskell. This is used in
the Nockma evaluator.
* Unit tests for `jam`
* A benchmark for `jam` applied to the Anoma / nockma stdlib.

Benchmark results:

```
$ juvixbench -p 'Jam'
All
  Nockma
    Jam
      jam stdlib: OK
        109  ms ± 6.2 ms
```
2024-05-14 17:45:49 +01:00
Paul Cadman
6d660f583c
Update Anoma nockma stdlib (#2765)
This PR updates the Anoma nockma stdlib to the version in Anoma 0.13.0
obtained from:

*
1bbaf664ad/lib/nock.ex (L433)

NB: As is documented in the code, the standard library paths are
obtained by running commands in the urbit dojo against the loaded
stdlib. For example to find the path to the `dec` function in the stdlib
run:

```
~zod:dojo> =>  anoma  !=(dec)
[9 342 0 31]
```

Part of:

* https://github.com/anoma/juvix/issues/2764
2024-05-14 14:46:48 +01:00
Jan Mas Rovira
7c59e2aa10
Import tree (#2751)
- Contributes to #2750 

# New commands:
1. `dev import-tree scan FILE`. Scans a single file and lists all the
imports in it.
2. `dev import-tree print`. Scans all files in the package and its
dependencies. Builds an import dependency tree and prints it to stdin.
If the `--stats` flag is given, it reports the number of scanned
modules, the number of unique imports, and the length of the longest
import chain.

Example: this is the truncated output of `juvix dev import-tree print
--stats` in the `juvix-stdlib` directory.
```
[...]
Stdlib/Trait/Partial.juvix imports Stdlib/Data/String/Base.juvix
Stdlib/Trait/Partial.juvix imports Stdlib/Debug/Fail.juvix
Stdlib/Trait/Show.juvix imports Stdlib/Data/String/Base.juvix
index.juvix imports Stdlib/Cairo/Poseidon.juvix
index.juvix imports Stdlib/Data/Int/Ord.juvix
index.juvix imports Stdlib/Data/Nat/Ord.juvix
index.juvix imports Stdlib/Data/String/Ord.juvix
index.juvix imports Stdlib/Prelude.juvix

Import Tree Statistics:
=======================
• Total number of modules: 56
• Total number of edges: 193
• Height (longest chain of imports): 15
```

Bot commands support the `--scan-strategy` flag, which determines which
parser we use to scan the imports. The possible values are:
1. `flatparse`. It uses the low-level
[FlatParse](https://hackage.haskell.org/package/flatparse-0.5.1.0/docs/FlatParse-Basic.html)
parsing library. This parser is made specifically to only parse imports
and ignores the rest. So we expect this to have a much better
performance. It does not have error messages.
2. `megaparsec`. It uses the normal juvix parser and we simply collect
the imports from it.
4. `flatparse-megaparsec` (default). It uses the flatparse backend and
fallbacks to megaparsec if it fails.

# Internal changes
## Megaparsec Parser (`Concrete.FromSource`)
In order to be able to run the parser during the scanning phase, I've
adjusted some of the effects used in the parser:
1. I've removed the `NameIdGen` and `Files` constraints, which were
unused.
2. I've removed `Reader EntryPoint`. It was used to get the `ModuleId`.
Now the `ModuleId` is generated during scoping.
3. I've replaced `PathResolver` by the `TopModuleNameChecker` effect.
This new effect, as the name suggests, only checks the name of the
module (same rules as we had in the `PathResolver` before). It is also
possible to ignore the effect, which is needed if we want to use this
parser without an entrypoint.

## `PathResolver` effet refactor
1. The `WithPath` command has been removed.
2. New command `ResolvePath :: ImportScan -> PathResolver m
(PackageInfo, FileExt)`. Useful for resolving imports during scanning
phase.
3. New command `WithResolverRoot :: Path Abs Dir -> m a -> PathResolver
m a`. Useful for switching package context.
4. New command `GetPackageInfos :: PathResolver m (HashMap (Path Abs
Dir) PackageInfo)` , which returns a table with all packages. Useful to
scan all dependencies.

The `Package.PathResolver` has been refactored to be more like to normal
`PathResolver`. We've discussed with @paulcadman the possibility to try
to unify both implementations in the near future.

## Misc
1. `Package.juvix` no longer ends up in
`PackageInfo.packageRelativeFiles`.
1. I've introduced string definitions for `--`, `{-` and `-}`.
2. I've fixed a bug were `.juvix.md` was detected as an invalid
extension.
3. I've added `LazyHashMap` to the prelude. I've also added `ordSet` to
create ordered Sets, `ordMap` for ordered maps, etc.

# Benchmarks
I've profiled `juvix dev import-tree --scan-strategy [megaparsec |
flatparse] --stats` with optimization enabled.
In the images below we see that in the megaparsec case, the scanning
takes 54.8% of the total time, whereas in the flatparse case it only
takes 9.6% of the total time.

- **Megaparsec**

![image](https://github.com/anoma/juvix/assets/5511599/05ec42cf-d79d-4bbf-b462-c0e48593fe51)

- **Flatparse**

![image](https://github.com/anoma/juvix/assets/5511599/1d7b363c-a915-463c-8dc4-613ab4b7d473)

## Hyperfine
```
hyperfine --warmup 1 'juvix dev import-tree print --scan-strategy flatparse --stats' 'juvix dev import-tree print --scan-strategy megaparsec --stats' --min-runs 20
Benchmark 1: juvix dev import-tree print --scan-strategy flatparse --stats
  Time (mean ± σ):      82.0 ms ±   4.5 ms    [User: 64.8 ms, System: 17.3 ms]
  Range (min … max):    77.0 ms … 102.4 ms    37 runs

Benchmark 2: juvix dev import-tree print --scan-strategy megaparsec --stats
  Time (mean ± σ):     174.1 ms ±   2.7 ms    [User: 157.5 ms, System: 16.8 ms]
  Range (min … max):   169.7 ms … 181.5 ms    20 runs

Summary
  juvix dev import-tree print --scan-strategy flatparse --stats ran
    2.12 ± 0.12 times faster than juvix dev import-tree print --scan-strategy megaparsec --stats
```

In order to compare (almost) only the parsing, I've forced the scanning
of each file to be performed 50 times (so that the cost of other parts
get swallowed). Here are the results:
```
hyperfine --warmup 1 'juvix dev import-tree print --scan-strategy flatparse --stats' 'juvix dev import-tree print --scan-strategy megaparsec --stats' --min-runs 10
Benchmark 1: juvix dev import-tree print --scan-strategy flatparse --stats
  Time (mean ± σ):     189.5 ms ±   3.6 ms    [User: 161.7 ms, System: 27.6 ms]
  Range (min … max):   185.1 ms … 197.1 ms    15 runs

Benchmark 2: juvix dev import-tree print --scan-strategy megaparsec --stats
  Time (mean ± σ):      5.113 s ±  0.023 s    [User: 5.084 s, System: 0.035 s]
  Range (min … max):    5.085 s …  5.148 s    10 runs

Summary
  juvix dev import-tree print --scan-strategy flatparse --stats ran
   26.99 ± 0.52 times faster than juvix dev import-tree print --scan-strategy megaparsec --stats
```
2024-05-14 10:53:33 +02:00
Łukasz Czajka
66d5b258a6
Remove checking for module fragility (#2777)
* Closes #2773
2024-05-13 23:01:43 +02:00
Łukasz Czajka
dc8ac00e0a
Use multiway if in the standard library (#2776)
Updates the stdlib code to use multiway `if` for better readability.

This PR also contains:

* https://github.com/anoma/juvix-stdlib/pull/96
* https://github.com/anoma/juvix-stdlib/pull/97
2024-05-13 19:48:05 +02:00
Łukasz Czajka
851afbaa99
Multiway if syntax (#2770)
* Closes #2736
2024-05-08 16:18:34 +02:00
Paul Cadman
640d96e0db
Deduplicate project remote dependencies (#2761)
## Goal

The goal of this PR is to deduplicate all dependencies in a Juvix
project.

Two dependencies are __identical__ when:

* For path dependencies, their paths are equal
* For git dependencies, their URL and their resolved revision (i.e the
git revision hash after resolving a tag) are equal

For example in the following dependency tree, where each of the named
dependencies represent identical git dependencies.

```
MyPkg
|
|-- Dep1-hash1
|   |
|   |-- Dep2-hash2
|   |   |
|   |   `-- Stdlib-hash3
|   |
|   `-- Stdlib-hash3
|
|-- Dep2-hash2
|   |
|   `-- Stdlib-hash3
|
`-- Stdlib-hash3
```

The project `MyPkg` should just contain the following dependencies:
`Dep1-hash1, Dep2-hash2, Stdlib-hash3`.

## Design

### Storage of transitive dependencies

Currently the transitive dependencies of a project are fetched/stored in
`.juvix-build` directories of the corresponding dependencies. After this
PR all dependencies, including transitive ones are stored in the
`.juvix-build` directory of the root project.

Again, assuming that all the transitive dependencies have the same git
hash, in the file system we label the dependencies with their git hash.

```
MyPkg
|
`- .juvix-build
          |- Dep1-hash1
          |- Dep2-hash2
          `- Stdlib-hash3
```

Say we have two versions of `Dep2` in the transitive dependency graph:

```
MyPkg
|
|-- Dep1-hash1
|   |
|   |-- Dep2-hash2
|   |   |
|   |   `-- Stdlib-hash3
|   |
|   `-- Stdlib-hash3
|
|-- Dep2-hash4
|   |
|   `-- Stdlib-hash3
|
`-- Stdlib-hash3
```

we would have two copies of `Dep2` in the `.juvix-build` directory with
different hashes:

```
MyPkg
|
`- .juvix-build
          |- Dep1-hash1
          |- Dep2-hash2
          |- Dep2-hash4
          `- Stdlib-hash3
```

### Storage of git clones

As a consequence of this design we cannot store the git clones for each
dependency in the `.juvix-build` directory as we do now.

We now store the git clones in a global directory
`~/.config/juvix/0.6.1/git-cache`.

When a dependency at a particular revision is required, the global git
clone is fetched/checked out at the required revision and copied into
the `.juvix-build` directory of the relevant project.

### Naming of git clones

The requirement for the naming of the global git clones is that they can
be identified by URL.

In this PR the name of a clone is formed by taking the SHA256 hash of
the dependency git URL. This is to avoid issues with file-system safe
escaping of characters.

### Naming of dependency directories

The requirement for the naming of the dependency directories is that
they can be identified by URL.
 / revision in accordance with our definition of identical dependencies.

In this PR the name of a clone is formed by taking the SHA256 hash of
the concatenation of the dependency git URL and git revision. This is to
avoid issues with file-system safe escaping of characters.

The downside of this approach is that it's hard to see which directories
correspond to which dependencies when navigating the filesystem.
However, navigating using the Juvix tooling by using go-to-definition
etc. will continue to work as before.

## Benchmarks

I tested using [`juvix-containers` test
`Main.juvix`](ebe8d2a873/test/Main.juvix).

The following benchmarks show timings excluding the initial clone of
dependencies (which happens in the warmup run).

Before:
```
$ juvix clean && juvix clean -g
$ hyperfine -w 1 'juvix compile native Main.juvix'
Benchmark 1: juvix compile native Main.juvix
  Time (mean ± σ):      5.598 s ±  0.410 s    [User: 5.020 s, System: 0.586 s]
  Range (min … max):    5.106 s …  6.382 s    10 runs
```

After:
```
$ juvix clean && juvix clean -g
$ hyperfine -w 1 'juvix compile native Main.juvix'
Benchmark 1: juvix compile native Main.juvix
  Time (mean ± σ):      4.418 s ±  0.241 s    [User: 4.083 s, System: 0.343 s]
  Range (min … max):    4.237 s …  4.927 s    10 runs
```

The time saved is due to the fact that before the project depends on 2
copies of the stdlib and after the project depends on 1 copy of the
stdlib.

Time is also saved in the initial run because the stdlib is only cloned
once instead of twice. The cached stdlib clone is also shared between
all project which will improve the performance of all projects that use
the stdlib.

Closes
* https://github.com/anoma/juvix/issues/2760
2024-05-03 19:28:25 +01:00
Paul Cadman
3c795fe011
justfile: use numParallelJobs in test flag (#2762)
The numParallelJobs option on the `justfile` is used to control the
total amount of concurrency when running build commands. This PR adds
this number to the test runner command: `+RTS -N$numParallelJobs -RTS`.
This means that `$numParallelJobs` threads will be used by the test
runner.

NB: ``+RTS -N -RTS` means that tests will use the number of threads
equal to the number of CPUs on the machine.
2024-05-03 10:05:06 +01:00
Łukasz Czajka
4d229eb9f9
Fully respect _evalOptionsNoFailure in the Core evaluator (#2756)
* Closes #2742
2024-04-30 14:09:01 +01:00
Łukasz Czajka
55dbcca097
Check for unsupported builtins (#2757)
* Closes #2743
2024-04-29 11:15:08 +01:00
Paul Cadman
844f302093
Support sharing ModuleInfoCache between pipeline runs (#2753)
This PR delays running of the pipeline `MCache` (renamed to
`ModuleInfoCache`) to allow this cache to be shared between pipeline
runs in `processRecursiveUpToTyped`. `processRecursiveUpToTyped` is used
by the `juvix html` command to recursively build HTML for modules in a
project.

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

## Performance

The docs build is now much faster and takes much less memory:

Before:

```
$ /usr/bin/time -lh juvix html docs/index.juvix.md	
1m17.41s real		35.39s user		11.42s sys
          5918703616  maximum resident set size
                   0  average shared memory size
                   0  average unshared data size
                   0  average unshared stack size
             3665213  page reclaims
                 697  page faults
                   0  swaps
                   0  block input operations
                   0  block output operations
                   0  messages sent
                   0  messages received
                   0  signals received
              114533  voluntary context switches
               81450  involuntary context switches
        595152097097  instructions retired
        143688878963  cycles elapsed
         19323983744  peak memory footprint
```

After:

```
$ /usr/bin/time -lh juvix html docs/index.juvix.md
	8.35s real		5.76s user		0.62s sys
          2992160768  maximum resident set size
                   0  average shared memory size
                   0  average unshared data size
                   0  average unshared stack size
              221870  page reclaims
                 719  page faults
                   0  swaps
                   0  block input operations
                   0  block output operations
                   0  messages sent
                   0  messages received
                   0  signals received
                1965  voluntary context switches
                1962  involuntary context switches
         93909891240  instructions retired
         19317129226  cycles elapsed
          2963053632  peak memory footprint
```

## Notes

* `MCache` is renamed to `ModuleInfoCache`

* `ModuleInfoCache` must be defined in a separate module instead of
being defined in `Compiler.Pipeline.Driver` to avoid a cyclic
dependency. `ModuleInfoCache` is now used used in
`Juvix.Compiler.Pipeline` (in `PipelineEff`) and this module is imported
by `Juvix.Compiler.Pipeline.Driver`).
2024-04-25 17:34:57 +01:00
Paul Cadman
778b6267a4
Use aarch64 macOS agent for CI build (#2754)
This PR updates the macOS CI build to use the `macos-14` build agent.
This uses the M series aarch64 chips.

* The M1 macOS agent does not have stack preinstalled so we must install
it using brew before building anything.
* The cache keys must have the `runner.arch` prefix to avoid building
with the x86_64 cache.

The macOS build / test run in about the same time as the linux build /
test, i.e significantly faster than on the x86 macOS agents.

## Notes

* We use the x86_64 binary of vamp-ir because the vamp-ir aarch64
releases do not have names that are compatible with the GitHub action we
are using to download the binary releases. The x86_64 binary works fine
for the purposes of testing.
2024-04-25 17:40:02 +02:00
Jan Mas Rovira
f5c25bfd2e
Remove unused resourcet-effectful dependency (#2748) 2024-04-24 14:52:43 +01:00
Paul Cadman
cde3ed98d5
Fix dev compile options documentation (#2741) 2024-04-23 12:06:54 +01:00
Jan Mas Rovira
8497c29982
Add integer literals with bases (#2746)
- Closes #2735 

Allow integer literals to be expressed in different bases:
1. binary, with prefix `0b`.
2. octal, with prefix `0o`.
3. decimal, with no prefix.
4. hexadecimal, with prefix `0x`. Both capital and lower case letters
are parsed. They are always printed as lower case letters.

This applies to all languages that use integer literals, but only in the
concrete language the base will be preserved when pretty printing.
2024-04-22 17:07:32 +02:00
Łukasz Czajka
8b8f323bf1
Cairo Elliptic Curve builtin (#2731)
* Closes #2720 
* Depends on https://github.com/anoma/juvix-cairo-vm/pull/3. For proper
functioning, the latest version of `juvix-cairo-vm` needs to be
installed.
2024-04-22 14:24:09 +02:00
Paul Cadman
ca14c3cfcb
Fix benchmark runtime variant (#2740)
This PR fixes the broken benchmarks build:


https://github.com/anoma/juvix-nightly-builds/actions/runs/8731798856/job/23958282008

Target `wasm32-wasi` has been renamed to `wasi`
2024-04-22 11:09:27 +01:00
Paul Cadman
0e8ccb7db2
Remove support for examples from judoc (#2747)
The judoc examples feature is currently unused. This feature was added
in https://github.com/anoma/juvix/pull/1442

Keeping support for this feature adds a cost to HTML generation. We are
removing this to improve the performance of `juvix html`.

To just render the HTML documentation we only require the scoper result
from the pipeline. To support the examples we need the type checking
result. The cost is significant in larger projects as the pipeline is
run for each import.

Part of https://github.com/anoma/juvix/issues/2744
2024-04-22 10:03:21 +01:00
Jan Mas Rovira
6231946d08
Style refactor (#2739)
This pr does some style refactoring so that the code is more consistent
with the rest of the codebase and easier to read
2024-04-19 10:05:46 +02:00
Jan Mas Rovira
d200b34991
remove redundant GitClone constraint (#2738)
Removes redundant `GitClone` constraint in the pipeline
2024-04-18 20:00:18 +02:00
Paul Cadman
52a3eed162
Support extract-module-statements attribute in juvix code blocks (#2734)
This PR adds support for the `extract-module-statements` attribute for
Juvix code blocks:

So if you write something like the following block in a Juvix markdown
file:

````
```juvix extract-module-statements
module Foo;
   type T := t;
end;
```
````

The statement `type T := t;` from the body of the module is rendered in
the output. The `module Foo;` , and `end;` lines are not rendered in the
output.

A block with the `extract-module-statements` must contain a single local
module statement and nothing else. An error is reported if this is not
the case.

The `extract-module-statements` attribute also takes an optional
argument. It sets the number of statements from the module body to drop
from the output.

In the following example, the output will contain the single line `a : T
:= t;`.

````
```juvix extract-module-statements 1
module Foo;
   type T := t;
   a : T  := t;
end;
```
````

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-04-18 17:01:59 +01:00
Łukasz Czajka
75b5228258
Cairo Poseidon hash builtin support (#2723)
* Closes #2719
2024-04-18 14:11:57 +02:00
Paul Cadman
b0fb240219
Use the justfile for CI builds (#2730)
This PR changes the CI build to use the justfile instead of the Makefile
to run builds and tests. CI builds now take advantage of parallel module
builds from https://github.com/anoma/juvix/pull/2729.

In order support this the runtime build target in the justfile now
supports `runtimeCcArg` and `runtimeLibtoolArg` so that the `CC` and
`LIBTOOL` Makefile argument can be set. This is required for the macOS
build.

In addition this PR upgrades the stack setup step action. Previously the
stack build flags included `--fast` which meant the whole project was
rebuilt in the `test` step, this has also been fixed.

Overall this speeds up the CI:
* Linux now takes 30mins (from 40mins)
* macOS now takes 60mins (from 80mins)
2024-04-18 12:52:37 +02:00
Jan Mas Rovira
2ec8a4343a
Apply common options in dev compile subcommands (#2732) 2024-04-17 16:49:22 +02:00
Łukasz Czajka
ad76c7a583
Support for Cairo builtins (#2718)
This PR implements generic support for Cairo VM builtins. The calling
convention in the generated CASM code is changed to allow for passing
around the builtin pointers. Appropriate builtin initialization and
finalization code is added. Support for specific builtins (e.g. Poseidon
hash, range check, Elliptic Curve operation) still needs to be
implemented in separate PRs.

* Closes #2683
2024-04-16 19:01:30 +02:00
Jan Mas Rovira
65176a333d
refactor --target into subcommands for dev tree compile and other improvements (#2713)
- refactor `--target` into subcommands for `dev tree compile`.
- prepend `App` to all `CompileTarget` constructors to avoid name
clashes with `Target`.
- parameterize compile options type with the input kind. The input kind
indicates the expected file extension of the input file. If the input
file is a .juvix file, then it is optional, otherwise it is mandatory.
- Add `AppError MegaparsecError` instance and simplify some related
code.
2024-04-16 17:32:44 +02:00
Paul Cadman
622bedf222
Support parallel module compilation in justfile (#2729)
Thanks to @janmasrovira for figuring out that the stack
`--ghc-options=-j` flag enables [parallel module compilation in
GHC](https://downloads.haskell.org/ghc/latest/docs/users_guide/using.html#using-ghc-make).

This PR adds support for this in the project justfile.

You can configure the argument to `-j` using the `numParallelJobs`
option, for example:

```
just numParallelJobs=24 build
+ stack build --fast -j24 --ghc-options=-j24
```

If `numParallelJobs` is not set then `-j` is passed with no arguments in
`--ghc-options` (this is equivalent to passing the number of cpus of the
machine.) and is passed with the number of cpus of the machine for the
stack `-j` option (the stack `-j` option requires an argument).

The `numParallelJobs` option also sets the argument to the [stack `-j`
option](https://docs.haskellstack.org/en/stable/global_flags/#-jobs-or-j-option).

To disable build parallelism set the `disableParallel` flag:

```
just disableParallel=yes build
+ stack build --fast
```
2024-04-16 14:16:32 +01:00
Jan Mas Rovira
698560076f
--debug implies -O0 as the default (#2728)
- Fixes #2726
2024-04-16 12:52:23 +01:00
Jan Mas Rovira
138e8f4aca
Fix output file extension for compile cairo (#2727)
- Fixes #2724
2024-04-16 12:09:44 +02:00
Jan Mas Rovira
e0ae356cd7
Use prettyString instead of show . pretty (#2711)
Use `prettyString` instead of relying on `Show` instance for `Doc a` so
that it is more consistent with `prettyText`.
2024-04-12 10:26:54 +02:00
Łukasz Czajka
b472e8cdcd
Cairo disassembler (#2710)
Implements a disassembler which converts Cairo bytecode into textual
CASM representation. Useful for debugging the Cairo backend.

* Adds the `juvix dev casm from-cairo` command
2024-04-11 15:03:27 +02:00
Paul Cadman
1fd3b3437a
Fix benchmarks suite juvix compile variants (#2717)
The `--target` flag was replaced by subcommands in
https://github.com/anoma/juvix/pull/2700

This PR fixes the benchmark suite to use `juvix compile native` and
`juvix compile wasi`.

In addition this PR adds as `compile-only` target to `juvix-bench`

The compile-only target only compiles the executable for each variant of
each suite. It doesn't actually run the benchmarks. This is useful when
checking that the variant build steps are correct before committing.

Example to run with stack:

```
stack bench --ba 'compile-only'
```
2024-04-10 18:54:03 +02:00