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

34 Commits

Author SHA1 Message Date
Łukasz Czajka
b4347bdd23
Numeric range types (#2232)
* Closes #2145
2023-06-28 17:24:08 +02:00
Łukasz Czajka
2fff65030b
Case folding (#2229)
Fold constant cases, i.e., convert
```
case C a b
| A := ..
| B x := ..
| C x y := M
```
to
```
let x := a;
     y := b;
in
M
```
2023-06-27 12:29:26 +02:00
Łukasz Czajka
7c039d687b
Specialization optimisation (#2164)
* Closes #2147 

Adds a `specialize` pragma which allows to specify (explicit) arguments
considered for specialization. Whenever a function is applied to a
constant known value for the specialized argument, a new version of the
function will be created with the argument pasted in. For example, the
code

```juvix
{-# specialize: [1] #-}
mymap : {A B : Type} -> (A -> B) -> List A -> List B;
mymap f nil := nil;
mymap f (x :: xs) := f x :: mymap f xs;

main : Nat;
main := length (mymap λ{x := x + 3} (1 :: 2 :: 3 :: 4 :: nil));
```

will be transformed into code equivalent to

```juvix
mymap' : (Nat -> Nat) -> List Nat -> List Nat;
mymap' f nil := nil;
mymap' f (x :: xs) := λ{x := x + 3} x :: mymap' xs;

main : Nat;
main := length (mymap' (1 :: 2 :: 3 :: 4 :: nil));
```
2023-06-26 16:49:19 +02:00
Łukasz Czajka
4c0e0b13c8
Respect fixity in runtime printer (#2182)
* Closes #2180
2023-06-07 11:44:41 +02:00
Łukasz Czajka
e9284b3ef6
Iterator syntax (#2126)
* Closes #1992 

A function identifier `fun` can be declared as an iterator with
```
syntax iterator fun;
```
For example:
```haskell
syntax iterator for;
for : {A B : Type} -> (A -> B -> A) -> A -> List B -> List A;
for f acc nil := acc;
for f acc (x :: xs) := for (f acc x) xs;
```
Iterator application syntax allows for a finite number of initializers
`acc := a` followed by a finite number of ranges `x in xs`. For example:
```
for (acc := 0) (x in lst) acc + x
```
The number of initializers plus the number of ranges must be non-zero.

An iterator application
```
fun (acc1 := a1; ..; accn := an) (x1 in b1; ..; xk in bk) body
```
gets desugared to
```
fun \{acc1 .. accn x1 .. xk := body} a1 .. an b1 .. bk
```
The `acc1`, ..., `accn`, `x1`, ..., `xk` can be patterns.

The desugaring works on a purely syntactic level. Without further
restrictions, it is not checked if the number of initializers/ranges
matches the type of the identifier. The restrictions on the number of
initializers/ranges can be specified in iterator declaration:
```
syntax iterator fun {init: n, range: k};
syntax iterator for {init: 1, range: 1};
syntax iterator map {init: 0, range: 1};
```
The attributes (`init`, `range`) in between braces are parsed as YAML to
avoid inventing and parsing a new attribute language. Both attributes
are optional.
2023-05-30 15:30:11 +02:00
Jan Mas Rovira
39b797ecfa
Add Bottom node (#2112)
- Closes #2056 
- Depends on #2103 

I am not sure about the implementation of `isType` for `NBot`. (solved).

The `Eq` instance returns `True` for every two `Bottom` terms,
regardless of their type.

---------

Co-authored-by: Jonathan Cubides <jonathan.cubides@uib.no>
Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-05-23 18:31:28 +02:00
Łukasz Czajka
336a934d18
Normalization by Evaluation (#2038)
* Closes #2032.
* Adds the `juvix dev core normalize` command.
* Adds the `:n` command in JuvixCore REPL.
* Adds the `--normalize` flag to `juvix dev core read` and `juvix dev
core from-concrete`.
* Adds `pipeline-normalize` which denotes pipeline steps necessary
before normalization.
* Adds normalization tests in `tests/VampIR/positive/Core`.
2023-05-15 18:01:40 +02:00
Łukasz Czajka
8aa54ecc28
Inlining (#2036)
* Closes #1989
* Adds optimization phases to the pipline (specified by
`opt-phase-eval`, `opt-phase-exec` and `opt-phase-geb` transformations).
* Adds the `-O` option to the `compile` command to specify the
optimization level.
* Functions can be declared for inlining with the `inline` pragma:
   ```
   {-# inline: true #-}
   const : {A B : Type} -> A -> B -> A;
   const x _ := x;
   ```
By default, the function is inlined only if it's fully applied. One can
specify that a function (partially) applied to at least `n` explicit
arguments should be inlined.
   ```
   {-# inline: 2 #-}
   compose : {A B C : Type} -> (B -> C) -> (A -> B) -> A -> C;
   compose f g x := f (g x);
   ```
Then `compose f g` will be inlined, even though it's not fully applied.
But `compose f` won't be inlined.
* Non-recursive fully applied functions are automatically inlined if the
height of the body term does not exceed the inlining depth limit, which
can be specified with the `--inline` option to the `compile` command.
* The pragma `inline: false` disables automatic inlining on a
per-function basis.
2023-05-15 17:27:05 +02:00
Jan Mas Rovira
3cbf6f5cb6
Fix ordering of statements in Abstract -> Internal (#2040)
- Closes #2039 
- Closes #2055 
- Depends on #2053 

Changes in this pr:
- Local modules are removed (flattened) in the translation abstract ->
internal.
- In the translation abstract -> internal we group definitions in
mutually recursive blocks. These blocks can contain function definitions
and type definitions. Previously we only handled functions.
- The translation of Internal has been enhanced to handle these mutually
recursive blocks.
- Some improvements the pretty printer for internal (e.g. we now print
builtin tags properly).
- A "hack" that puts the builtin bool definition at the beginning of a
module if present. This was the easiest way to handle the implicit
dependency of the builtin stringToNat with bool in the internal-to-core
translation.
- A moderately sized test defining a simple lambda calculus involving
and an evaluator for it. This example showcases mutually recursive types
in juvix.

---------

Co-authored-by: Jonathan Cubides <jonathan.cubides@uib.no>
2023-05-15 13:02:09 +02:00
janmasrovira
528eaa72d0
Substitute calls after lambda lifting (#2031)
- Closes #2006

During lambda lifting, we now substitute the calls to the lifted
functions before recursively applying lambda lifting. This will slightly
increase the amount of captured variables. However, I think this is the
only way since we need all identifiers to have a type when recursing.
2023-04-26 12:56:44 +02:00
janmasrovira
5de0026d83
Add juvix global project under xdg directory and other improvements (#1963)
Co-authored-by: Paul Cadman <git@paulcadman.dev>
2023-04-13 11:27:39 +02:00
Paul Cadman
ea09ec3068
Add builtin integer type to the surface language (#1948)
This PR adds a builtin integer type to the surface language that is
compiled to the backend integer type.

## Inductive definition

The `Int` type is defined in the standard library as:

```
builtin int
type Int :=
  | --- ofNat n represents the integer n
    ofNat : Nat -> Int
  | --- negSuc n represents the integer -(n + 1)
    negSuc : Nat -> Int;
```

## New builtin functions defined in the standard library

```
intToString : Int -> String;
+ : Int -> Int -> Int;
neg : Int -> Int;
* : Int -> Int -> Int;
- : Int -> Int -> Int;
div : Int -> Int -> Int;
mod : Int -> Int -> Int;

== : Int -> Int -> Bool;
<= : Int -> Int -> Bool;
< : Int -> Int -> Bool;
```

Additional builtins required in the definition of the other builtins:

```
negNat : Nat -> Int;
intSubNat : Nat -> Nat -> Int;
nonNeg : Int -> Bool;
```

## REPL types of literals

In the REPL, non-negative integer literals have the inferred type `Nat`,
negative integer literals have the inferred type `Int`.

```
Stdlib.Prelude> :t 1
Nat
Stdlib.Prelude> :t -1
Int
:t let x : Int := 1 in x
Int
```

## The standard library Prelude

The definitions of `*`, `+`, `div` and `mod` are not exported from the
standard library prelude as these would conflict with the definitions
from `Stdlib.Data.Nat`.

Stdlib.Prelude
```
open import Stdlib.Data.Int hiding {+;*;div;mod} public;
```

* Closes https://github.com/anoma/juvix/issues/1679
* Closes https://github.com/anoma/juvix/issues/1984

---------

Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-04-13 08:16:49 +01:00
Łukasz Czajka
dbd24f2f93
Check for the executable (WASM/native) pipeline prerequisites (#1970)
* Closes #1959
2023-04-04 11:58:36 +02:00
Łukasz Czajka
63a2c5144d
Print quoted strings in the runtime (#1969)
* Closes #1968
2023-04-04 10:29:02 +01:00
janmasrovira
ff495ef326
Support local modules (#1872)
Support local modules.

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
2023-03-31 14:57:37 +01:00
Łukasz Czajka
58dbf62520
Fix removal of polymorphic type arguments (#1954)
* Closes #1930
2023-03-30 19:56:07 +02:00
janmasrovira
90a7a5e7e0
Fix REPL state to include enough information to rerun the pipeline (#1911)
Previously we were:
* discarding the types table 
* discarding the name ids state
after processing an expression in the REPL.

For example evaluating:
```
let even : _; odd : _; odd zero := false; odd (suc n) := not (even n); even zero := true; even (suc n) := not (odd n) in even 10
```
would loop in the REPL.

We noticed that the `n` in `suc n` was being given type `Type` instead
of `Nat`. This was because the name id given to n was incorrect, the
REPL started using name ids from 0 again.

We fixed this issue by storing information, including the types table
and name ids state in the Artifacts data structure that is returned when
we run the pipeline for the first time. This information is then used
when we call functions to compile / type check REPL expressions.

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
2023-03-30 13:39:27 +02:00
Łukasz Czajka
2b5524ded1
Preserve name and location information in Internal-to-Core (#1933)
* Closes #1846 
* Preserves location information for all created `Match` nodes so that
match-to-case always has a location available.
2023-03-28 10:29:24 +02:00
Łukasz Czajka
c9b8cdd5e9
Pattern matching compilation (#1874)
This implements a basic version of the algorithm from: Luc Maranget,
[Compiling pattern matching to good decision
trees](http://moscova.inria.fr/~maranget/papers/ml05e-maranget.pdf). No
heuristics are used - the first column is always chosen.

* Closes #1798 
* Closes #1225 
* Closes #1926 
* Adds a global `--no-coverage` option which turns off coverage checking
in favour of generating runtime failures
* Changes the representation of Match patterns in JuvixCore to achieve a
more streamlined implementation
* Adds options to the Core pipeline
2023-03-27 10:42:27 +02:00
Łukasz Czajka
8d7e669f74
Fix bug with unregistered builtin bool (#1917)
* Closes #1884
2023-03-24 10:29:57 +01:00
Łukasz Czajka
2a8585ede0
Fix bug in IO runtime (#1906)
Co-authored-by: Paul Cadman <git@paulcadman.dev>
2023-03-21 14:34:46 +00:00
Łukasz Czajka
2803f3feee
Fix JuvixAsm validation (#1903)
Co-authored-by: Paul Cadman <git@paulcadman.dev>
2023-03-20 12:01:35 +00:00
Paul Cadman
51978f947c
Fix registration of builtin inductive axioms (#1901)
builtin inductive axioms must be registered in the same pass as
inductive types becuase inductive types may use builtin inductives in
the types of their constructors.

```
builtin string axiom String : Type;

type BoxedString :=
  | boxed : String -> BoxedString;
```

The separate passes for processing functions and inductives was
unnecessary. This commit combines `registerInductiveDefs` and
`registerFunctionDefs` into a single pass over a modules statements
2023-03-20 10:00:22 +00:00
Łukasz Czajka
ac6d08e259
Add errors to the Core pipeline and check GEB prerequisites (#1871)
* Depends on #1832 
* Closes #1844
* Adds errors to the Core pipeline
* Checks for no recursion in the GEB pipeline
* Checks for no polymorphism in the GEB pipeline
* Checks for no dynamic type in the GEB pipeline
* Checks for no IO in the GEB pipeline
* Checks for no unsupported builtins in the GEB pipeline
2023-03-20 10:13:07 +01:00
Paul Cadman
786ff9d075
internal-to-core: Fix index shifting of pattern arguments (#1900) 2023-03-18 00:30:51 +01:00
janmasrovira
934a273e2d
Automatically detect and split mutually recursive blocks in let expressions (#1894)
- Closes #1677
2023-03-17 11:05:55 +00:00
Łukasz Czajka
2d798ec31c
New compilation pipeline (#1832)
* Depends on PR #1824 
* Closes #1556 
* Closes #1825 
* Closes #1843
* Closes #1729 
* Closes #1596 
* Closes #1343 
* Closes #1382 
* Closes #1867 
* Closes #1876 
* Changes the `juvix compile` command to use the new pipeline.
* Removes the `juvix dev minic` command and the `BackendC` tests.
* Adds the `juvix eval` command.
* Fixes bugs in the Nat-to-integer conversion.
* Fixes bugs in the Internal-to-Core and Core-to-Core.Stripped
translations.
* Fixes bugs in the RemoveTypeArgs transformation.
* Fixes bugs in lambda-lifting (incorrect de Bruijn indices in the types
of added binders).
* Fixes several other bugs in the compilation pipeline.
* Adds a separate EtaExpandApps transformation to avoid quadratic
runtime in the Internal-to-Core translation due to repeated calls to
etaExpandApps.
* Changes Internal-to-Core to avoid generating matches on values which
don't have an inductive type.

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
Co-authored-by: janmasrovira <janmasrovira@gmail.com>
2023-03-14 16:24:07 +01:00
Paul Cadman
c93013229a
Add compilation of complex pattern matching to case (#1824)
This PR adds the `match-to-case` Core transformation. This transforms
pattern matching nodes to a sequence of case and let nodes.

## High level description

Each branch of the match is compiled to a lambda. In the combined match 

Each branch of the match is compiled to a lambda. These lambdas are
combined in nested lets and each lambda is called in turn as each branch
gets checked. The lambda corresponding to the first branch gets called
first, if the pattern match in the branch fails, the lambda
corresponding to the next branch is called and so on. If no branches
match then a lambda is called which returns a fail node.

Conceptually:

<table>
<tr>
<td>
Core
</td>
<td>
Transformed
</td>
</tr>
<tr>
<td>

```

match v1 .. vn {
  b1
  b2
  ...
  bk
}

```

</td>
<td>

```
λ
  let c0 := λ FAIL in
    let ck := λ {...} in
      ...
      let c1 := λ {...} in
  c1 v1 ... vn

```

</td>
</tr>
</table>

The patterns on each branch are compiled to either let bindings (pattern
binders) or case expressions (constructor patterns).

Auxillary bindings are added in the case of nested constructor patterns.

The default branch in each case expression has a call to the lambda
corresponding to the next branch of the match. This is because the
default
branch is reached if the pattern match fails.

<table>

<tr>
<td>
Pattern match
</td>
<td>
Transformed
</td>
</tr>
<tr>
<td>

```
suc (suc n) ↦ n
```

</td>
<td>

```
  case ?$0 of {
    suc arg_8 := case ?$0 of {
      suc n := let n := ?$0 in n$0;
      _ := ?$2 ?$1
    };
    _ := ?$1 ?$0
  }

```

</td>
</tr>
</table>

The body of each branch is wrapped in let bindings so that the indicies
of bound
variables in the body point to the correct variables in the compiled
expression.
This is necessary because the auxiliary bindings added for nested
constructor
patterns will cause the original indicies to be offset.

Finally, the free variables in the match branch body need to be shifted
by all the bindings we've added as part of the compilation.

## Examples

### Single wildcard

<table>
<tr>
<td> Juvix </td> <td> Core </td> <td> Transformed Core </td>
</tr>
<tr>
<td>

```
f : Nat -> Nat;
f _ := 1;
```

</td>
<td>

```
λ? match ?$0 with {
  _ω309 ↦ ? 1
}
```

</td>
<td>

```
λ? let ? := λ? fail "Non-exhaustive patterns" in
   let ? := λ? let _ω309 := ?$0 in
               let _ω309 := ?$0 in 1 in
   ?$0 ?$2
```

</td>
</tr>
</table>

### Single binder

<table>
<tr>
<td> Juvix </td> <td> Core </td> <td> Transformed Core </td>
</tr>
<tr>
<td>

```
f : Nat -> Nat;
f n := n;
```

</td>
<td>

```
λ? match ?$0 with {
  n ↦ n$0
}
```

</td>
<td>

```
λ? let ? := λ? fail "Non-exhaustive patterns" in
   let ? := λ? let n := ?$0 in
               let n := ?$0 in n$0 in
   ?$0 ?$2
```

</td>
</tr>
</table>

### Single Constructor

<table>
<tr>
<td> Juvix </td> <td> Core </td> <td> Transformed Core </td>
</tr>
<tr>
<td>

```
f : Nat -> Nat;
f (suc n) := n;
```

</td>
<td>

```
λ? match ?$0 with {
  suc n ↦ n$0
}
```

</td>
<td>

```
λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of {
  suc n := let n := ?$0 in let n := ?$0 in n$0;
  _ := ?$1 ?$0
} in ?$0 ?$2
```

</td>
</tr>
</table>

### Nested Constructor

<table>
<tr>
<td> Juvix </td> <td> Core </td> <td> Transformed Core </td>
</tr>
<tr>
<td>

```
f : Nat -> Nat;
f (suc (suc n)) := n;
```

</td>
<td>

```
λ? match ?$0 with {
  suc (suc n) ↦ n$0
}
```

</td>
<td>

```
λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of {
  suc arg_8 := case ?$0 of {
    suc n := let n := ?$0 in let n := ?$0 in n$0;
    _ := ?$2 ?$1
  };
  _ := ?$1 ?$0
} in ?$0 ?$2
```

</td>
</tr>
</table>

### Multiple Branches

<table>
<tr>
<td> Juvix </td> <td> Core </td> <td> Transformed Core </td>
</tr>
<tr>
<td>

```
f : Nat -> Nat;
f (suc n) := n;
f zero := 0;
```

</td>
<td>

```
λ? match ?$0 with {
  suc n ↦ n$0;
  zero ↦ ? 0
}
```

</td>
<td>

```
λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of {
  zero := ? 0;
  _ := ?$1 ?$0
} in let ? := λ? case ?$0 of {
  suc n := let n := ?$0 in let n := ?$0 in n$0;
  _ := ?$1 ?$0
} in ?$0 ?$3
```

</td>
</tr>
</table>

### Nested case with captured variable

<table>
<tr>
<td> Juvix </td> <td> Core </td> <td> Transformed Core </td>
</tr>
<tr>
<td>

```
f : Nat -> Nat -> Nat;
f n m := case m
  | suc k := n + k;
```

</td>
<td>

```
f = λ? λ? match ?$1, ?$0 with {
  n, m ↦ match m$0 with {
    suc k ↦ + n$2 k$0
  }
}
```

</td>
<td>

```
λ? λ?
  let ? := λ? λ? fail "Non-exhaustive patterns" in
  let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in
      let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of {
            suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0;
            _ := ?$1 ?$0
} in ?$0 m$2 in ?$0 ?$3 ?$2
```

</td>
</tr>
</table>

## Testing

The `tests/Compilation/positive` tests are run up to the Core evaluator
with `match-to-case` and `nat-to-int` transformations on Core turned on.

---------

Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 11:30:12 +01:00
Łukasz Czajka
acea6615a4
Lazy boolean operators (#1743)
Closes #1701
2023-01-25 18:57:47 +01:00
Łukasz Czajka
ecac5e07c7 Translate 'let' to Core (#1740)
Closes #1351
2023-01-19 12:56:37 +01:00
Łukasz Czajka
6499100d67 Add test for div and mod (#1741) 2023-01-19 12:52:51 +01:00
janmasrovira
0193a33d4c
Fix inference loop (#1726) 2023-01-17 13:28:38 +01:00
janmasrovira
f7205915a5
Typecheck let expressions (#1712) 2023-01-17 09:41:07 +01:00
Łukasz Czajka
186f4f66ef
Tests for the new compilation pipeline (#1703)
Adds Juvix tests for the compilation pipeline - these are converted from
the JuvixCore tests (those that make sense). Currently, only the
translation from Juvix to JuvixCore is checked for the tests that can be
type-checked. Ultimately, the entire compilation pipeline down to native
code / WebAssembly should be checked on these tests.

Closes #1689
2023-01-12 11:22:32 +01:00