diff --git a/CHANGELOG.md b/CHANGELOG.md index f4dbfc433..c865d7abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project does not currently adhere to a particular versioning scheme. ### Added +- Add type system for Bend. ([#615][gh-615], [#679][gh-679], see [Type Checking](docs/type-checking.md)) - Add import system. ([#544][gh-544]) - Add multi line comment `#{ ... #}` syntax. ([#595][gh-595]) - Add error message when input file is not found. ([#513][gh-513]) diff --git a/cspell.json b/cspell.json index 4aa80d973..0ade9d2e0 100644 --- a/cspell.json +++ b/cspell.json @@ -34,6 +34,7 @@ "foldl", "hasher", "hexdigit", + "Hindley", "hvm's", "indexmap", "inet", @@ -57,6 +58,7 @@ "lnil", "lpthread", "mant", + "Milner", "miscompilation", "mult", "namegen", @@ -109,6 +111,7 @@ "succ", "supercombinator", "supercombinators", + "Tarjan's", "tlsv", "TSPL", "tunr", diff --git a/docs/syntax.md b/docs/syntax.md index 389a422c0..25d5d03c8 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -10,6 +10,10 @@ Click [here](#import-syntax) to see the import syntax. Click [here](#comments) to see the syntax for commenting code. +Click [here](#imp-type-syntax) to see the imperative type syntax. + +Click [here](#fun-type-syntax) to see the functional type syntax. + Both syntaxes can be mixed in the same file like the example below: ```python @@ -44,12 +48,15 @@ main = Defines a top level function. ```python -def add(x, y): +def add(x: u24, y: u24) -> u24: result = x + y return result +def unchecked two() -> u24: + return 2 + def main: - return add(40, 2) + return add(40, two) ``` A function definition is composed by a name, a sequence of parameters and a body. @@ -59,6 +66,10 @@ A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except i The last statement of each function must either be a `return` or a selection statement (`if`, `switch`, `match`, `fold`) where all branches `return`. +Each parameter of the function can receive a type annotation with `param_name: type` and the return value of the function can also be annotated with `def fn_name(args) -> return_type:`. + +We can force the type-checker to run or not on a specific function by adding `checked` or `unchecked` between `def` and the function name. + ### Type Defines an algebraic data type. @@ -68,14 +79,16 @@ type Option: Some { value } None -type Tree: - Node { value, ~left, ~right } +type Tree(T): + Node { value: T, ~left: Tree(T), ~right: Tree(T) } Leaf ``` Type names must be unique, and should have at least one constructor. -Each constructor is defined by a name followed by its fields. +For a generic or polymorphic type, all type variables used in the constructors must be declared first in the type definition with `type Name(type_var1, ...):` + +Each constructor is defined by a name followed by its fields. The fields can be annotated with types that will be checked when creating values of that type. The `~` notation indicates a recursive field. To use `fold` statements with a type its recursive fields must be correctly marked with `~`. @@ -89,9 +102,9 @@ Read [defining data types](./defining-data-types.md) to know more. Defines a type with a single constructor (like a struct, a record or a class). ```python -object Pair { fst, snd } +object Pair(A, B) { fst: A, snd: B } -object Function { name, args, body } +object Function(T) { name: String, args, body: T } object Vec { len, data } ``` @@ -1256,6 +1269,11 @@ hvm link_ports: (a (b *)) & (c a) ~ (d e) & (e b) ~ (d c) + +# Casts a `u24` to itself. +# We can give type annotations to HVM definitions. +hvm u24_to_u24 -> (u24 -> u24): + ($([u24] ret) ret) ``` It's also possible to define functions using HVM syntax. This can be @@ -1338,3 +1356,248 @@ Multi-line commenting should also be used to document code. def second(x, y): return y ``` + +
+ +# Imp Type Syntax + +## Variable + +Any name represents a type variable. + +Used in generic or polymorphic type definitions. + +```python +# T is a type variable +type Option(T): + Some { value: T } + None + +# A is a type variable +def id(x: A) -> A: + return x +``` + +## Constructor + +`Ctr(...)` represents a constructor type. + +Used for defining custom data types or algebraic data types. +Can contain other types as parameters. + +```python +def head(list: List(T)) -> Option(T) + match list: + case List/Nil: + return Option/None + case List/Cons: + return Option/Some(list.head) +``` + +## Any + +`Any` represents the untyped type. + +It accepts values of alls type and will forcefully cast any type to `Any`. + +Can be used for values that can't be statically typed, either because +they are unknown (like in raw IO calls), because they contain untypable +expressions (like unscoped variables), or because the expression cannot +be typed with the current type system (like the self application `lambda x: x(x)`). + +```python +def main -> Any: + return 24 +``` + +## None + +`None` represents the eraser `*` or absence of a value. + +Often used to indicate that a function doesn't return anything. + +```python +def none -> None: + return * +``` + +## Hole + +`_` represents a hole type. + +This will let the type checker infer the most general type for an argument or return value. + +```python +def increment(x: _) -> _: + return x + 1 +``` + +## u24 + +`u24` represents an unsigned 24-bit integer. + +```python +def zero -> u24: + return 0 +``` + +## i24 + +`i24` represents a signed 24-bit integer. + +```python +def random_integer -> i24: + return -42 +``` + +## f24 + +`f24` represents a 24-bit floating-point number. + +```python +def PI -> f24: + return 3.14 +``` + +## Tuple + +`(_, _, ...)` represents a tuple type. + +Can contain two or more types separated by commas. + +```python +def make_tuple(fst: A, snd: B) -> (A, B): + return (fst, snd) +``` + +## Function + +`a -> b` represents a function type. + +`a` is the input type, and `b` is the output type. + +```python +def apply(f: A -> B, arg: A) -> B: + return f(arg) +``` + +
+ +# Fun Type Syntax + +## Variable + +Any name represents a type variable. + +Used in generic or polymorphic type definitions. + +```python +# T is a type variable +type (Option T) + = (Some T) + | None + +# A is a type variable +id : A -> A +id x = x +``` + +## Constructor + +`(Ctr ...)` represents a constructor type. + +Used for defining custom data types or algebraic data types. +Can contain other types as parameters. + +```python +head : (List T) -> (Option T) +head [] = Option/None +head (List/Cons head _) = (Option/Some head) +``` + +## Any + +`Any` represents the untyped type. + +It accepts values of alls type and will forcefully cast any type to `Any`. + +Can be used for values that can't be statically typed, either because +they are unknown (like in raw IO calls), because they contain untypable +expressions (like unscoped variables), or because the expression cannot +be typed with the current type system (like the self application `λx (x x)`). + +```python +main : Any +main = @x x +``` + +## None + +`None` represents the eraser `*` or absence of a value. + +Often used to indicate that a function doesn't return anything. + +```python +none : None +none = * +``` + +## Hole + +`_` represents a hole type. + +This will let the type checker infer the most general type for an argument or return value. + +```python +increment : _ -> _ +increment x = (+ x 1) +``` + +## u24 + +`u24` represents an unsigned 24-bit integer. + +```python +zero : u24 +zero = 0 +``` + +## i24 + +`i24` represents a signed 24-bit integer. + +```python +random_integer : i24 +random_integer = -24 +``` + +## f24 + +`f24` represents a 24-bit floating-point number. + +```python +PI : f24 +PI = 3.14 +``` + +## Tuple + +`(_, _, ...)` represents a tuple type. + +Can contain two or more types separated by commas. + +```python +make_tuple : A -> B -> (A, B) +make_tuple fst snd = (fst, snd) +``` + +## Function + +`a -> b` represents a function type. + +`a` is the input type, and `b` is the output type. + +```python +apply : (A -> B) -> A -> B +apply f arg = (f arg) +``` diff --git a/docs/type-checking.md b/docs/type-checking.md new file mode 100644 index 000000000..37ed41e3a --- /dev/null +++ b/docs/type-checking.md @@ -0,0 +1,138 @@ +# Type Checking + +Bend has a type checker with optional typing support based on a Hindley Milner type system. + +Programs can be optionally typed using the respective imp or fun type syntax. Type checking is +enabled by default, but can be toggled with the `-Otype-check` and `-Ono-type-check` options. + +Every function can be annotated with a type for its arguments and return value. +The type checker will infer the type of the function and then compare if it's compatible with the annotated type. + +```python +def add(x: u24, y: u24) -> u24: + return x + y + +# Arguments or return value without annotation are considered `Any`. +# They will be accepted by any function, regardless of being correct or not. +def push(list: List(T), value) -> List(T): + match list: + case List/Nil: + return List/Cons(value, List/Nil) + case List/Cons: + return List/Cons(list.head, push(list.tail, value)) + +# Error, List(T) must only receive values of type `T`. +def append_num(list: List(T), num: u24) -> List(T): + return List/Cons(num, list) + +# Error, Tree(T) can only store one type of value. +def my_tree() -> _: + return ![!1, !"a"] + +# Error, can't add a `u24` and a `f24`. +# Bend doesn't have implicit type conversions. +def add_float(x: u24, y: f24) -> f24: + return x + y +``` + +Bend comes with the following builtin types: + +* `u24`: Unsigned 24-bit integer. +* `i24`: Signed 24-bit integer. +* `f24`: Floating point number. +* `(T1, ..., Tn)`: Tuple with `n` elements of types `T1` to `Tn`. +* `Any`: Untyped value. +* `None`: Eraser `*`. +* `_`: A type that will be inferred by the type checker. + +The prelude library also defines some basic types that are used in Bend programs: + +* `String`: Text represented as a sequence of Unicode characters. +* `List(T)`: A list of values of type `T`. +* `Tree(T)`: A binary tree with values of type `T` at the leaves. +* `Map(T)`: A map from keys of type `u24` to values of type `T`. +* `IO(T)`: A monadic IO type that can be used to perform IO operations. +* `Result(O, E)`: Represents the result of an operation that can either succeed with an `O` or fail with an `E`. + + +Additionally, you can define your own algebraic data types. +In this case, all the type variables that occur in the constructors must be previously defined. + +```python +type Option(T): + Some { value: T } + None +``` + +All the constructors will be declared with the same type `TypeName(var2, var2, ...)`. + +### Enabling and disabling type checking + +In some cases we know that dynamically our program will not do something wrong despite not being able to give it the proper type. + +We can disable type checking for a specific function by either removing the type annotations or by giving it the `unchecked` keyword: + +```python +# Error, type-checked functions can't contain an unscoped variable. +def channel(x: u24) -> (u24 -> u24, u24): + return (lambda $a: x, $a) + +# We can remove the annotations. It won't be type-checked, +# but its type will be `Any -> Any`. +def channel(x): + return (lambda $a: x, $a) + +# Instead, we can use the `unchecked` keyword. +# The annotated type will be considered the truth, regardless of being correct or not. +def unchecked channel(x: u24) -> (u24 -> u24, u24): + return (lambda $a: x, $a) +``` + +The opposite is also possible, we can enable type checking for an unannotated function by using the `checked` keyword before the name of the function in its declaration: + +```python +# Despite the inferred type being `List(T) -> List(T)`, the type checker will consider it as `Any -> Any` because it's not annotated. +def checked tail(list): + match list: + case List/Nil: + return List/Nil + case List/Cons: + return list.tail + +# Error, can't infer the type of this function, despite having type `Any`. +# Not typeable by a Hindley-Milner type system. +checked (scott_concat a b) = (a + λh λt λcons λnil (cons h (scott_concat t b)) + b +) +``` + +We can also disable type checking for the entire program by using the `-Ono-type-check` option. + +Native HVM definitions are always unchecked. + +```python +# This function will be given the type `a -> a`. +hvm native_id -> (a -> a): + (x x) +``` + +### Limitations + +Currently, the following are not supported by the type checker: + +- Superpositions (`{a, b}`, the tuple type with duplication semantics, see [Dups and sups](https://github.com/HigherOrderCO/Bend/blob/main/docs/dups-and-sups.md)). +- Unscoped variables and variable binds (`$a`, `let $a = ...`, see [Scopeless lambdas](https://github.com/HigherOrderCO/Bend/blob/main/docs/using-scopeless-lambdas.md)). +- Expressions not typeable by a Hindley-Milner type system (e.g. self application `λx: x(x)`). + +Additionally, the builtin types `Number` and `Integer` can't be used directly in type annotations. They are used internally by the type checker to handle numeric expressions. + +```python +# The inferred type will be `Number(a) -> Number(a) -> Number(a)`. +def add(x: _, y: _) -> _: + return x + y + +# The inferred type will be `Integer(a) -> Integer(a) -> Integer(a)`. +def shift(x: _, n: _) -> _: + return x << n +``` diff --git a/examples/bubble_sort.bend b/examples/bubble_sort.bend index 4a0cf6f36..d3c05ade2 100644 --- a/examples/bubble_sort.bend +++ b/examples/bubble_sort.bend @@ -1,28 +1,26 @@ # Sorts a list -def sort(xs): +def sort(xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Nil case List/Cons: return insert(xs.head, sort(xs.tail)) -# Insert : U60 -> List -> List -def insert(v, xs): +def insert(v: u24, xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Cons(v, List/Nil) case List/Cons: return swap_gt(v, xs.head, xs.tail) -# SwapGT : U60 -> U60 -> U60 -> List -> List -def swap_gt(v, x, xs): +def swap_gt(v: u24, x: u24, xs: List(u24)) -> List(u24): if x > v: return List/Cons(v, List/Cons(x, xs)) else: return List/Cons(x, insert(v, xs)) # Generates a list of 'n' random u24 numbers using xorshift -def rnd(n): +def rnd(n: u24) -> List(u24): bend n, state=1: when n != 0: state = state ^ (state << 13) @@ -33,13 +31,13 @@ def rnd(n): return List/Nil # Sums a list of u24 numbers -def sum(xs): +def sum(xs: List(u24)) -> u24: fold xs: case List/Nil: return 0 case List/Cons: return xs.head + xs.tail -def main: +def main() -> u24: n = 100 return sum(sort(rnd(n))) diff --git a/examples/callcc.bend b/examples/callcc.bend index 57c484316..c21026062 100644 --- a/examples/callcc.bend +++ b/examples/callcc.bend @@ -1,16 +1,18 @@ # This program is an example that shows how scopeless lambdas can be used. -Seq a b = a +Seq (a: A) (b: B) : A = a # Create a program capable of using `callcc` -CC.lang = λprogram +unchecked CC.lang : ((((a -> b) -> b) -> c) -> c) -> d = +λprogram let callcc = λcallback (λ$garbage($hole) (callback λ$hole(0))); let result = (program callcc); let garbage = $garbage; # Discard `$garbage`, which is the value returned by `callback` (Seq result garbage) -main = (CC.lang λcallcc +main: u24 = (CC.lang λcallcc # This code calls `callcc`, then calls `k` to fill the hole with `42`. # This means that the call to callcc returns `42`, and the program returns `52` (+ 10 (callcc λk(+ (k 42) 1729))) ) + \ No newline at end of file diff --git a/examples/example_fun.bend b/examples/example_fun.bend index 3357091a0..8d701be0c 100644 --- a/examples/example_fun.bend +++ b/examples/example_fun.bend @@ -1,23 +1,38 @@ # Example of some things you can do with the 'fun' syntax -# Write definitions like this +# Define functions like this: +# By default they accept and return any type (Def1) = ((λa a) (λb b)) -# You can call a definition by just referencing its name -# It will be substituted in place of the reference +# You can call a definition by just referencing its name. +# It will be substituted in place of the reference. (Def2) = ((λa a) Def1) -# Definitions and variables can have names in upper and lower case and contain numbers -# Names defined in a more inner position shadow names in an outer position +# Definitions and variables can have names in upper and lower case and +# contain numbers, '.', '-', '_' and '/'. +# Names defined in a more inner position shadow names in an outer position. (def3) = ((λDef1 Def1) (λx λx x)) -# The language is affine, but if you use a variable more than once the compiler inserts duplications for you -# Of course you can always do them manually -(def4) = λz let {z1 z2} = z; (z1 ((λx (x x x x x)) z2)) +# You can annotate a function with the expected types and Bend will check them. +# The parentheses on the left side of the definition are optional. +const (a: A) (b: B) : A = a -# You can use machine numbers and some native numeric operations -# Numeric operations can only reduce numbers, doing (+ (λx x) 1) will not do anything -(nums) = λx1 λx2 (* (+ x1 1) (/ (- x2 2) 1)) +# There are three types of native numbers available: u24, i24 and f24. +# u24: Unsigned 24-bit integers +# i24: Signed 24-bit integers +# f24: Floating point numbers with 24-bit precision +(unsigneds (x1: u24) (x2: u24)) : u24 = (* (+ x1 1) (/ (- x2 2) 1)) + +# '+' or '-' are mandatory for i24. +(signeds (x1: i24) (x2: i24)) : i24 = (* (+ x1 +1) (/ (- x2 -2) +1)) + +# The '.' is mandatory for f24. +(floats (x1: f24) (x2: f24)) : f24 = (* (+ x1 1.0) (/ (- x2 -2.0) +1.0)) + +# Numeric operations are only meaningful on native numbers. +# You can force the compiler to do it anyway by using untyped values. +id = λx x # 'id' wasn't given a type so it's inferred as 'Any'. +bad_nums : Any = (+ id 1) # You can use numbers on the native match expression # The `+` arm binds the `scrutinee`-1 variable to the value of the number -1 @@ -34,7 +49,7 @@ type Bool = True | False # You can have pattern matching on definitions # Use `*` to ignore a pattern (Option.unwrap_or (Option/Some val) *) = val -(Option.unwrap_or Option/None or) = or +(Option.unwrap_or Option/None or) = or (Bool.or Bool/True *) = Bool/True (Bool.or * Bool/True) = Bool/True @@ -45,17 +60,16 @@ type Bool = True | False match bool { Bool/True: Bool/False Bool/False: Bool/True - } + } # Data types can store values -type Boxed = (Box val) +type Boxed T = (Box (val: T)) -# Types with only one constructor can be destructured using `let` or a single matching definition +# Types with only one constructor can be destructured using `open`, +# a single matching definition or a 'match'. (Box.map (Boxed/Box val) f) = (Boxed/Box (f val)) -(Box.unbox) = λbox match box { - Boxed/Box: box.val -} +(Box.unbox (box: (Boxed T))): T = open Boxed box; box.val # Use tuples to store two values together without needing to create a new data type (Tuple.new fst snd) = @@ -69,6 +83,68 @@ type Boxed = (Box val) let (fst, snd) = pair snd +# You can give type annotations to type definitions as well. +# The recursive fields should be annotated with a '~'. +type ListList T = + (Cons (head: (List T)) ~(tail: (ListList T))) | + Nil + +# Bend functions usually center around recursion +sum (List/Nil) = 0 +sum (List/Cons x xs) = (+ x (sum xs)) + +sum_list (ListList/Nil) = List/Nil +sum_list (ListList/Cons x xs) = (List/Cons (sum x) (sum_list xs)) + +# To create local recursive functions that consume a recursive type, you can use 'fold'. +sum_list2 ll = fold ll { + # The fold is implicitly called for fields marked with '~' in their definition. + # In this case, 'll.tail' is replaced with a recursive call to the fold. + ListList/Cons: (List/Cons (sum ll.head) ll.tail) + ListList/Nil: List/Nil +} + +# To do the opposite and create a recursive structure, you can use 'bend'. +# 'bend' can be seen as a tree-like generalization of a while loop. +new_list = bend x = 0 { + when (< x 10): + # 'fork' calls the bend recursively with the provided values. + (List/Cons x (fork (+ x 1))) + else: + List/Nil +} + +# To make programs that are more parallelizable, you generally want to +# avoid lists and use tree-like structures with multiple recursion instead. +sum_nums from to = + if (< from to) { + (+ + (sum_nums from (/ 2 (+ from to))) + (sum_nums (+ 1 (/ 2 (+ from to))) to)) + } else { + from + } + +# Internally, each variable is only used once. If you use a variable +# is used more than once, the compiler will insert duplications for you. +# +# You can also write them manually with 'let {a b} = ...', but then +# your function needs to be unchecked, either by not annotating it +# with types or by marking it as unchecked. +unchecked (def4) = λz let {z1 z2} = z; (z1 ((λx (x x x x x)) z2)) + +# Duplicating a term that duplicates a variable is not allowed and will break the program. +map f (List/Cons x xs) = (List/Cons (f x) (map f xs)) # 'f' is duplicated here +map f (List/Nil) = List/Nil + +# 'map' duplicated the lambda which duplicates 'a'. +# Although this is well defined, it does not produce a sound lambda-calculus result. +VeryBad (a: u24) (xs: (List u24)) : (List u24) = +(map + λ x (+ (* a x) a) # 'a' is duplicated here + [1, 2, 3, 4, 5]) + + # All files must have a main definition to run. # You can execute a program in Bend with "bend run " # Other options are "check" to just see if the file is well formed @@ -83,4 +159,4 @@ type Boxed = (Box val) let map = (Box.map box Option.unwrap_or) let unboxed = ((Box.unbox map) snd) - (nums 3 unboxed) + (unsigneds 3 unboxed) diff --git a/examples/fib.bend b/examples/fib.bend index 070c42090..6e37c19be 100644 --- a/examples/fib.bend +++ b/examples/fib.bend @@ -3,7 +3,7 @@ # Calculates fibonacci numbers recursively. # Although branching recursion is usually a good idea to parallelize, # it makes this code run in exponential time. -def fib_recursive(n): +def fib_recursive(n: u24) -> u24: switch n: case 0: return 0 @@ -14,14 +14,14 @@ def fib_recursive(n): # Calculates fibonacci numbers iteratively (tail-recursively). # This function is inherently sequential, but runs in linear time. -def fib_iterative(n): +def fib_iterative(n: u24) -> u24: bend a=0, b=1, n: when n != 0: return fork(b, a + b, n - 1) else: return a -def main(): +def main() -> u24: # With the iterative version, we can calculate large fibonacci numbers # While with the recursive version, we will quickly run out of memory. # Note that for numbers larger than 36 the result will overflow the space of the 24-bit integer. diff --git a/examples/fusing_not.bend b/examples/fusing_not.bend index b6fd4909b..50b535021 100644 --- a/examples/fusing_not.bend +++ b/examples/fusing_not.bend @@ -1,16 +1,19 @@ # Example of a boolean 'not' function that fuses inifinitely through composition.. -true = λt λf t -false = λt λf f +true : a -> b -> a = λt λf t +false : a -> b -> b = λt λf f -not = λboolean (boolean false true) -fusing_not = λboolean λt λf (boolean f t) +not : ((a -> b -> b) -> (c -> d -> c) -> e) -> e = + λboolean (boolean false true) + +fusing_not : (b -> a -> c) -> a -> b -> c = + λboolean λt λf (boolean f t) # Creates a Church numeral out of a native number -to_church n = switch n { +to_church (n: u24) : (a -> a) -> a -> a = switch n { 0: λf λx x _: λf λx (f (to_church n-1 f x)) } -main = +main: _ = # Self-composes `not` 2^23-1 times and prints the result. - ((to_church 0x7FFFFF) fusing_not) # try replacing 'fusing_not' by 'not'. Will it still work? + ((to_church 0x7FFFF) fusing_not) # try replacing 'fusing_not' by 'not'. Will it still work? diff --git a/examples/gen_tree.bend b/examples/gen_tree.bend index f433a5b97..ea2e72ad5 100644 --- a/examples/gen_tree.bend +++ b/examples/gen_tree.bend @@ -1,16 +1,16 @@ # Generates a tree with numbers in the nodes using 'bend' -type MyTree: - Node { val, ~left, ~right } +type MyTree(t): + Node { val: t, ~left: MyTree(t), ~right: MyTree(t) } Leaf -def tree_gen(n, x): +def tree_gen(n: u24, x: u24) -> MyTree(u24): bend n, x: when n != 0: return MyTree/Node(x, fork(n - 1, x*2+1), fork(n - 1, x*2+2)) else: return MyTree/Leaf -def main: +def main -> MyTree(u24): depth = 4 first_val = 1 return tree_gen(depth, first_val) diff --git a/examples/hello_world.bend b/examples/hello_world.bend index 81ad13e50..60f8d13a5 100644 --- a/examples/hello_world.bend +++ b/examples/hello_world.bend @@ -1,4 +1,4 @@ -def main(): +def main() -> IO(u24): with IO: * <- IO/print("Hello, world!\n") return wrap(0) \ No newline at end of file diff --git a/examples/insertion_sort.bend b/examples/insertion_sort.bend index 1119eeca8..667f52178 100644 --- a/examples/insertion_sort.bend +++ b/examples/insertion_sort.bend @@ -1,4 +1,4 @@ -def insertion_sort(xs): +def insertion_sort(xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Nil @@ -6,32 +6,32 @@ def insertion_sort(xs): return insertion_sort.insert(xs.head, insertion_sort(xs.tail)) # Inserts a value into a partially sorted list -def insertion_sort.insert(v, xs): +def insertion_sort.insert(v: u24, xs: List(u24)) -> List(u24): match xs: case List/Nil: return List/Cons(v, List/Nil) case List/Cons: return insert_aux(v > xs.head, v, xs.head, xs.tail) -def insert_aux(n, v, x, xs): +def insert_aux(n: u24, v: u24, x: u24, xs: List(u24)) -> List(u24): if n == 0: return List/Cons(v, List/Cons(x, xs)) else: return List/Cons(x, insertion_sort.insert(v, xs)) # Generates a list of random numbers -def rnd(n): +def rnd(n: u24) -> List(u24): if n == 0: return List/Nil else: return List/Cons(random(10000 - n), rnd(n - 1)) # Generates a pseudo-random number (not very good) -def random(n): +def random(n: u24) -> u24: if n == 0: return 0 else: return (random(n - 1) * 16 + 101387) % 429453 -def main(): +def main() -> List(u24): return insertion_sort(rnd(10)) diff --git a/examples/list.bend b/examples/list.bend index c3c0c487f..7ce66da29 100644 --- a/examples/list.bend +++ b/examples/list.bend @@ -12,173 +12,159 @@ ########################################### # List clear: -# List l -> list l # clears all elements from list l. This is equivalent to initializing an empty list. -clear = @l [] +clear : (List t) -> (List t) = @l [] # List concat: -# List l -> List l # combines two lists (l1, l2) from left to right. -concat = @l1 @l2 +concat : (List t) -> (List t) -> (List t) = + @l1 @l2 match l1 { List/Cons: (List/Cons l1.head (concat l1.tail l2)) List/Nil: l2 } # List add_front: -# List l -> List l -# adds a non-List element e to the front of list l. -add_front = @l @e +# adds an element e to the front of list l. +add_front : (List t) -> t -> (List t) = + @l @e match l { List/Cons: (List/Cons e l) List/Nil: (List/Cons e List/Nil) } # List append (add_back): -# List l -> List l -# adds a non-list element e to the back of list l. -append = @l @e - (concat l (List/Cons e List/Nil)) +# adds an element e to the back of list l. +append : (List t) -> t -> (List t) = + @l @e (concat l (List/Cons e List/Nil)) # list sum: -# List l -> uint # returns the sum of all items in the list. -sum = @l +sum : (List u24) -> u24 = @l match l { List/Cons: (+ l.head (sum l.tail)) List/Nil: 0 } # List reverse: -# List l -> List l # reverses the order of elements in list l. -reverse.aux = @acc @l +reverse.aux : (List t) -> (List t) -> (List t) = + @acc @l match l { List/Nil: acc List/Cons: (reverse.aux (List/Cons l.head acc) l.tail) } -reverse = @l - (reverse.aux [] l) +reverse : (List t) -> (List t) = + @l (reverse.aux [] l) # List length: -# List l -> uint # returns the number of elements in list l. -len = @l +len : (List t) -> u24 = + @l match l { List/Nil: 0 List/Cons: (+ 1 (len l.tail)) } # List count: -# List l -> uint -> uint -# returns the number of instances of Some s in list l. -count.aux = @acc @l @s +# returns the number of instances of some number n in list l. +count.aux : u24 -> (List u24) -> u24 -> u24 = + @acc @l @n match l { List/Nil: acc - List/Cons: use acc = switch (== l.head s) { - 0: acc; - _: (+ acc 1); - } - (count.aux acc l.tail s) + List/Cons: + let acc = if (== l.head n) { (+ acc 1) } else { acc } + (count.aux acc l.tail n) } -count = @l @s - (count.aux 0 l s) +count = @l @n + (count.aux 0 l n) # List index: -# List l -> Some s # returns the value of a specific list index i, or * if the index doesn't exist. -index = @l @i +index : (List t) -> u24 -> (Result t None) = + @l @i match l { List/Cons: switch i { - 0: l.head + 0: (Result/Ok l.head) _: (index l.tail (i-1)) } - List/Nil: * + List/Nil: (Result/Err *) } # List head: -# List l -> Some s # returns the first item in the list, or [] if the list is empty. -head = @l +head : (List t) -> (Result t None) = + @l match l { - List/Cons: l.head - List/Nil: [] + List/Cons: (Result/Ok l.head) + List/Nil : (Result/Err *) } # List tail: -# List l -> List -# returns the list except for the first item in it, or [] if the list is empty. -def tail(l): - match l: - case List/Cons: - return l.tail - case List/Nil: - return [] +# returns the list whithout the first item, or [] if the list is empty. +tail : (List t) -> (List t) = + @l + match l { + List/Cons: l.tail + List/Nil: [] + } # List equals: -# List xs, List ys, function cmp -> 1|0 # Compares the elements in two lists and returns 1 if they're equal, and 0 otherwise. # The function cmp compares two values and returns 1 if they're equal, and 0 otherwise. -equals xs ys cmp = match xs { - List/Cons: match ys { - List/Cons: if (cmp xs.head ys.head) { - (equals xs.tail ys.tail cmp) - } else { - 0 - } - List/Nil: 0 - } - List/Nil: match ys { - List/Cons: 0 - List/Nil: 1 - } -} +equals (xs: (List a)) (ys: (List b)) (cmp: a -> b -> u24) : u24 +equals (List/Cons x xs) (List/Cons y ys) cmp = if (cmp x y) { (equals xs ys cmp) } else { 0 } +equals (List/Cons x xs) List/Nil cmp = 0 +equals List/Nil (List/Cons y ys) cmp = 0 +equals List/Nil List/Nil cmp = 1 # List pop_front: -# List l -> List l # removes and discards the first item of list l. # The new list is returned, or [] if the list is empty. -pop_front = @l +pop_front : (List t) -> (List t) = + @l match l { List/Cons: l.tail List/Nil: [] } # List pop_back: -# List l -> List l # removes and discards the the last item of list l. +pop_back : (List t) -> (List t) pop_back (List/Nil) = List/Nil pop_back (List/Cons x List/Nil) = List/Nil pop_back (List/Cons head tail) = (List/Cons head (pop_back tail)) # List remove: -# List l -> Some s -> List l # removes the first occurrence of element e from list l. -remove = @l @s +remove : (List u24) -> u24 -> (List u24) = + @l @s match l { List/Cons: - switch (== l.head s) { - 0: (List/Cons l.head (remove l.tail s)) - _: l.tail + if (== l.head s) { + l.tail + } else { + (List/Cons l.head (remove l.tail s)) } List/Nil: List/Nil } # List split: -# list l -> uint i -> (List l, list l) # splits list l into two lists (l1, l2) at index i. # the second list takes the element at index i during the split. -split = @l @i (split.aux [] l i) +split : (List t) -> u24 -> ((List t), (List t)) = + @l @i (split.aux [] l i) -split.aux = @acc @l @i +split.aux : (List t) -> (List t) -> u24 -> ((List t), (List t)) = + @acc @l @i match l { List/Cons: switch i { 0: (acc, l) _: (split.aux (append acc l.head) l.tail i-1) } - List/Nil: * + List/Nil: (acc, []) } diff --git a/examples/parallel_and.bend b/examples/parallel_and.bend index 07fd6830d..bd41552da 100644 --- a/examples/parallel_and.bend +++ b/examples/parallel_and.bend @@ -3,26 +3,27 @@ type Bool: True False -def and(a, b): +def and(a: Bool, b: Bool) -> Bool: match a: case Bool/True: return b case Bool/False: return Bool/False -def all(tree): +# Trees of tuples are not typeable +def all(tree: Tree(Bool)) -> Bool: fold tree: case Tree/Node: return and(tree.left, tree.right) case Tree/Leaf: return tree.value -def gen(n): +def gen(n: u24) -> Tree(Bool): switch n: case 0: return Tree/Leaf(Bool/True) case _: return Tree/Node { left: gen(n-1), right: gen(n-1) } -def main(): +def main() -> Bool: return all(gen(8)) diff --git a/examples/parallel_sum.bend b/examples/parallel_sum.bend index 321d24fc9..b6fb578f1 100644 --- a/examples/parallel_sum.bend +++ b/examples/parallel_sum.bend @@ -2,12 +2,12 @@ # Creates a tree with numbers and then sums all values in parallel # a binary tree -type MyTree: - Node { val, ~left, ~right } +type MyTree(t): + Node { val: t, ~left: MyTree(t), ~right: MyTree(t) } Leaf # sums all values in a tree -def sum(tree): +def sum(tree: MyTree(u24)) -> u24: fold tree: case MyTree/Node: return tree.val + tree.left + tree.right @@ -15,7 +15,7 @@ def sum(tree): return 0 # generates a binary tree of given depth -def gen(depth): +def gen(depth: u24) -> MyTree(u24): bend height=0, val = 1: when height < depth: tree = MyTree/Node { val: val, left: fork(height+1, 2*val), right: fork(height+1, 2*val+1) } @@ -24,5 +24,5 @@ def gen(depth): return tree # returns the sum of [1 .. 2^16), truncated to 24 bits -def main: +def main() -> u24: return sum(gen(16)) \ No newline at end of file diff --git a/examples/queue.bend b/examples/queue.bend index 346651174..b43281b5b 100644 --- a/examples/queue.bend +++ b/examples/queue.bend @@ -2,16 +2,16 @@ # Implements constant-time queues with just lambdas # Qnew : Queue a -Qnew = λx x +Qnew: _ = λx x # Qadd : a -> Queue a -> Queue a -Qadd = λx λq λk (q λc (c x k)) +Qadd: _ = λx λq λk (q λc (c x k)) # Qrem : Queue a -> Pair a (Queue a) Qrem = λq (q $k λx λxs λp(p x λ$k xs)) # Output: [1, 2, 3] -main = +main: (List u24) = let q = Qnew let q = (Qadd 1 q) let q = (Qadd 2 q) diff --git a/examples/quick_sort.bend b/examples/quick_sort.bend index 84f0296f2..9fc39d92e 100644 --- a/examples/quick_sort.bend +++ b/examples/quick_sort.bend @@ -1,22 +1,26 @@ -type MyTree = Leaf | (Node lft val rgt) +type MyTree t = Leaf | (Node ~(lft: (MyTree t)) (val: t) ~(rgt: (MyTree t))) # Parallel QuickSort -(Sort List/Nil) = MyTree/Leaf +(Sort) : (List u24) -> (MyTree u24) +(Sort List/Nil) = MyTree/Leaf (Sort (List/Cons head tail)) = - ((Part head tail) λmin λmax - let lft = (Sort min) - let rgt = (Sort max) - (MyTree/Node lft head rgt)) + let (min, max) = (Part head tail) + let lft = (Sort min) + let rgt = (Sort max) + (MyTree/Node lft head rgt) - # Partitions a list in two halves, less-than-p and greater-than-p - (Part p List/Nil) = λt (t List/Nil List/Nil) - (Part p (List/Cons head tail)) = (Push (> head p) head (Part p tail)) +# Partitions a list in two halves, less-than-p and greater-than-p +(Part) : u24 -> (List u24) -> ((List u24), (List u24)) +(Part p List/Nil) = (List/Nil, List/Nil) +(Part p (List/Cons head tail)) = (Push (> head p) head (Part p tail)) - # Pushes a value to the first or second list of a pair - (Push 0 x pair) = (pair λmin λmax λp (p (List/Cons x min) max)) - (Push _ x pair) = (pair λmin λmax λp (p min (List/Cons x max))) +# Pushes a value to the first or second list of a pair +(Push) : u24 -> u24 -> ((List u24), (List u24)) -> ((List u24), (List u24)) +(Push 0 x (min, max)) = ((List/Cons x min), max) +(Push _ x (min, max)) = (min, (List/Cons x max)) # Generates a random list with xorshift +(Rnd) : u24 -> (u24) -> (List u24) (Rnd 0 state) = List/Nil (Rnd n state) = let state = (^ state (<< state 13)) @@ -25,11 +29,12 @@ type MyTree = Leaf | (Node lft val rgt) (List/Cons state (Rnd (- n 1) state)) # Sums all elements in a concatenation tree +(Sum) : (MyTree u24) -> u24 (Sum MyTree/Leaf) = 0 (Sum (MyTree/Node lft val rgt)) = (+ val (+ (Sum lft) (Sum rgt))) # Sorts and sums n random numbers -(Main) = +(Main) : u24 = (Sum (Sort (Rnd 0x100 1))) # Use an argument from cli diff --git a/examples/radix_sort.bend b/examples/radix_sort.bend index f92525c2f..8ea735c5f 100644 --- a/examples/radix_sort.bend +++ b/examples/radix_sort.bend @@ -1,100 +1,95 @@ -type Map_: +type MyMap: Free Used - Both {~a, ~b} + Both { ~a: MyMap, ~b: MyMap } -type Arr: +type Arr(t): Null - Leaf {~a} - Node {~a, ~b} + Leaf { a: t } + Node { ~a: Arr(t), ~b: Arr(t) } -def swap(s, a, b): +def swap(s: u24, a: MyMap, b: MyMap) -> MyMap: switch s: case 0: - return Map_/Both{ a: a, b: b } + return MyMap/Both{ a: a, b: b } case _: - return Map_/Both{ a: b, b: a } + return MyMap/Both{ a: b, b: a } -# Sort : Arr -> Arr -def sort(t): +def sort(t: Arr(u24)) -> Arr(u24): return to_arr(0, to_map(t)) -# ToMap : Arr -> Map -def to_map(t): +def to_map(t: Arr(u24)) -> MyMap: match t: case Arr/Null: - return Map_/Free + return MyMap/Free case Arr/Leaf: return radix(t.a) case Arr/Node: return merge(to_map(t.a), to_map(t.b)) -# ToArr : U60 -> Map -> Arr -def to_arr(x, m): +def to_arr(x: u24, m: MyMap) -> Arr(u24): match m: - case Map_/Free: + case MyMap/Free: return Arr/Null - case Map_/Used: + case MyMap/Used: return Arr/Leaf{ a: x } - case Map_/Both: + case MyMap/Both: return Arr/Node{ a: to_arr(x * 2, m.a), b: to_arr(x * 2 + 1, m.b) } -# Merge : Map -> Map -> Map -def merge(a, b): +def merge(a: MyMap, b: MyMap) -> MyMap: match a: - case Map_/Free: + case MyMap/Free: return b - case Map_/Used: - return Map_/Used - case Map_/Both: + case MyMap/Used: + return MyMap/Used + case MyMap/Both: match b: - case Map_/Free: + case MyMap/Free: return a - case Map_/Used: - return Map_/Used - case Map_/Both: - return Map_/Both{ a: merge(a.a, b.a), b: merge(a.b, b.b) } + case MyMap/Used: + return MyMap/Used + case MyMap/Both: + return MyMap/Both{ a: merge(a.a, b.a), b: merge(a.b, b.b) } -# Radix : U60 -> Map -def radix(n): - r = Map_/Used - r = swap(n & 1, r, Map_/Free) - r = swap(n & 2, r, Map_/Free) - r = swap(n & 4, r, Map_/Free) - r = swap(n & 8, r, Map_/Free) - r = swap(n & 16, r, Map_/Free) - r = swap(n & 32, r, Map_/Free) - r = swap(n & 64, r, Map_/Free) - r = swap(n & 128, r, Map_/Free) - r = swap(n & 256, r, Map_/Free) - r = swap(n & 512, r, Map_/Free) +def radix(n: u24) -> MyMap: + r = MyMap/Used + r = swap(n & 1, r, MyMap/Free) + r = swap(n & 2, r, MyMap/Free) + r = swap(n & 4, r, MyMap/Free) + r = swap(n & 8, r, MyMap/Free) + r = swap(n & 16, r, MyMap/Free) + r = swap(n & 32, r, MyMap/Free) + r = swap(n & 64, r, MyMap/Free) + r = swap(n & 128, r, MyMap/Free) + r = swap(n & 256, r, MyMap/Free) + r = swap(n & 512, r, MyMap/Free) return radix2(n, r) # At the moment, we need to manually break very large functions into smaller ones # if we want to run this program on the GPU. # In a future version of Bend, we will be able to do this automatically. -def radix2(n, r): - r = swap(n & 1024, r, Map_/Free) - r = swap(n & 2048, r, Map_/Free) - r = swap(n & 4096, r, Map_/Free) - r = swap(n & 8192, r, Map_/Free) - r = swap(n & 16384, r, Map_/Free) - r = swap(n & 32768, r, Map_/Free) - r = swap(n & 65536, r, Map_/Free) - r = swap(n & 131072, r, Map_/Free) - r = swap(n & 262144, r, Map_/Free) - r = swap(n & 524288, r, Map_/Free) +def radix2(n: u24, r: MyMap) -> MyMap: + r = swap(n & 1024, r, MyMap/Free) + r = swap(n & 2048, r, MyMap/Free) + r = swap(n & 4096, r, MyMap/Free) + r = swap(n & 8192, r, MyMap/Free) + r = swap(n & 16384, r, MyMap/Free) + r = swap(n & 32768, r, MyMap/Free) + r = swap(n & 65536, r, MyMap/Free) + r = swap(n & 131072, r, MyMap/Free) + r = swap(n & 262144, r, MyMap/Free) + r = swap(n & 524288, r, MyMap/Free) return radix3(n, r) -def radix3(n, r): - r = swap(n & 1048576, r, Map_/Free) - r = swap(n & 2097152, r, Map_/Free) - r = swap(n & 4194304, r, Map_/Free) - r = swap(n & 8388608, r, Map_/Free) +def radix3(n: u24, r: MyMap) -> MyMap: + r = swap(n & 1048576, r, MyMap/Free) + r = swap(n & 2097152, r, MyMap/Free) + r = swap(n & 4194304, r, MyMap/Free) + r = swap(n & 8388608, r, MyMap/Free) return r -def reverse(t): +def reverse(t: Arr(u24)) -> Arr(u24): match t: case Arr/Null: return Arr/Null @@ -103,8 +98,7 @@ def reverse(t): case Arr/Node: return Arr/Node{ a: reverse(t.b), b: reverse(t.a) } -# Sum : Arr -> U60 -def sum(t): +def sum(t: Arr(u24)) -> u24: match t: case Arr/Null: return 0 @@ -113,11 +107,10 @@ def sum(t): case Arr/Node: return sum(t.a) + sum(t.b) -# Gen : U60 -> Arr -def gen(n): +def gen(n: u24) -> Arr(u24): return gen_go(n, 0) -def gen_go(n, x): +def gen_go(n: u24, x: u24) -> Arr(u24): switch n: case 0: return Arr/Leaf{ a: x } @@ -126,4 +119,4 @@ def gen_go(n, x): b = x * 2 + 1 return Arr/Node{ a: gen_go(n-1, a), b: gen_go(n-1, b) } -Main = (sum (sort(reverse(gen 4)))) +Main: u24 = (sum (sort(reverse(gen 4)))) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index c8264ae37..088a81d8d 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -11,7 +11,6 @@ pub const ERR_INDENT_SIZE: usize = 2; #[derive(Debug, Clone, Default)] pub struct Diagnostics { - err_counter: usize, pub diagnostics: BTreeMap>, pub config: DiagnosticsConfig, } @@ -71,21 +70,18 @@ pub enum WarningType { impl Diagnostics { pub fn new(config: DiagnosticsConfig) -> Self { - Self { err_counter: 0, diagnostics: Default::default(), config } + Self { diagnostics: Default::default(), config } } pub fn add_parsing_error(&mut self, err: impl std::fmt::Display, source: Source) { - self.err_counter += 1; self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Parsing, source); } pub fn add_book_error(&mut self, err: impl std::fmt::Display) { - self.err_counter += 1; self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Book, Default::default()); } pub fn add_function_error(&mut self, err: impl std::fmt::Display, name: Name, source: Source) { - self.err_counter += 1; self.add_diagnostic( err, Severity::Error, @@ -95,7 +91,6 @@ impl Diagnostics { } pub fn add_inet_error(&mut self, err: impl std::fmt::Display, def_name: String) { - self.err_counter += 1; self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Inet(def_name), Default::default()); } @@ -107,9 +102,6 @@ impl Diagnostics { source: Source, ) { let severity = self.config.warning_severity(warn_type); - if severity == Severity::Error { - self.err_counter += 1; - } self.add_diagnostic( warn, severity, @@ -120,9 +112,6 @@ impl Diagnostics { pub fn add_book_warning(&mut self, warn: impl std::fmt::Display, warn_type: WarningType) { let severity = self.config.warning_severity(warn_type); - if severity == Severity::Error { - self.err_counter += 1; - } self.add_diagnostic(warn, severity, DiagnosticOrigin::Book, Default::default()); } @@ -173,16 +162,11 @@ impl Diagnostics { self.has_severity(Severity::Error) } - /// Resets the internal counter - pub fn start_pass(&mut self) { - self.err_counter = 0; - } - /// Checks if any error was emitted since the start of the pass, /// Returning all the current information as a `Err(Info)`, replacing `&mut self` with an empty one. /// Otherwise, returns the given arg as an `Ok(T)`. pub fn fatal(&mut self, t: T) -> Result { - if self.err_counter == 0 { + if !self.has_errors() { Ok(t) } else { Err(std::mem::take(self)) diff --git a/src/fun/builtins.bend b/src/fun/builtins.bend index cca33de52..542047d26 100644 --- a/src/fun/builtins.bend +++ b/src/fun/builtins.bend @@ -1,44 +1,61 @@ -type String = (Nil) | (Cons head ~tail) +type String: + Nil + Cons { head: u24, ~tail: String } -type List = (Nil) | (Cons head ~tail) +type List(T): + Nil + Cons { head: T, ~tail: List(T) } -# List/length(xs: List(T)) -> (u24, List(T)) # Returns the length of a list and the list itself. -List/length xs = fold xs with len=0, acc=DiffList/new { - List/Nil: (len, (DiffList/to_list acc)) - List/Cons: (xs.tail (+ len 1) (DiffList/append acc xs.head)) -} +def List/length(xs: List(T)) -> (u24, List(T)): + fold xs with len=0, acc=DiffList/new: + case List/Nil: + return (len, DiffList/to_list(acc)) + case List/Cons: + return xs.tail(len + 1, DiffList/append(acc, xs.head)) -# List/reverse(xs: List(T)) -> List(T) # Reverses a list. -List/reverse xs = fold xs with acc=[] { - List/Nil: acc - List/Cons: (xs.tail (List/Cons xs.head acc)) -} +def List/reverse(xs: List(T)) -> List(T): + fold xs with acc=[]: + case List/Nil: + return acc + case List/Cons: + return xs.tail(List/Cons(xs.head, acc)) -# List/flatten(xs: List(List(T))) -> List(T) # Flattens a list of lists. +List/flatten (xs: (List (List T))) : (List T) List/flatten (List/Cons x xs) = (List/concat x (List/flatten xs)) List/flatten (List/Nil) = (List/Nil) -# List/concat(xs: List(T), ys: List(T)) -> List(T) # Concatenates two lists. +List/concat(xs: (List T)) (ys: (List T)) : (List T) List/concat (List/Cons x xs) ys = (List/Cons x (List/concat xs ys)) List/concat (List/Nil) ys = ys -# List/split_once(xs: List(T), val: T) -> (Result(List(T), List(T))) -# Splits a list into two lists at the first occurrence of a value. -List/split_once xs val = (List/split_once.go xs val @x x) - List/split_once.go List/Nil val acc = (Result/Err (acc List/Nil)) - List/split_once.go (List/Cons x xs) val acc = - if (== val x) { - (Result/Ok ((acc List/Nil), xs)) - } else { - (List/split_once.go xs val @y (acc (List/Cons x y))) - } +# Splits a list into two lists at the first value that passes a condition. +# Returns the original list if the value is not found +def List/split_once( + xs: List(T), + cond: T -> u24 +) -> (Result((List(T), List(T)), List(T))): + return List/split_once.go(xs, cond, DiffList/new) + +def List/split_once.go( + xs: List(T), + cond: T -> u24, + acc: List(T) -> List(T) +) -> Result((List(T), List(T)), List(T)): + match xs: + case List/Nil: + return Result/Err(DiffList/to_list(acc)) + case List/Cons: + if cond(xs.head): + return Result/Ok((DiffList/to_list(acc), xs.tail)) + else: + return List/split_once.go(xs.tail, cond, DiffList/append(acc, xs.head)) -# List/filter(xs: List(T), pred: T -> Bool) -> List(T) # Filters a list based on a predicate function. +List/filter (xs: (List T)) (pred: T -> u24) : (List T) List/filter (List/Nil) _ = List/Nil List/filter (List/Cons x xs) pred = if (pred x) { @@ -47,8 +64,8 @@ List/filter (List/Cons x xs) pred = (List/filter xs pred) } -# String/equals(s1: String, s2: String) -> u24 # Checks if two strings are equal. +String/equals (s1: String) (s2: String) : u24 String/equals (String/Nil) (String/Nil) = 1 String/equals (String/Cons x xs) (String/Cons y ys) = if (== x y) { @@ -58,61 +75,86 @@ String/equals (String/Cons x xs) (String/Cons y ys) = } String/equals * * = 0 -# String/split(s: String, delimiter: u24) -> List(String) # Splits a list into two lists at the first occurrence of a value. -String/split s delim = (String/split.go s delim (List/Cons String/Nil List/Nil)) - String/split.go (String/Nil) _ acc = (List/reverse acc) - String/split.go (String/Cons c cs) delim acc = - if (== c delim) { - (String/split.go cs delim (List/Cons String/Nil acc)) - } else { - match acc { - List/Cons: (String/split.go cs delim (List/Cons (String/Cons c acc.head) acc.tail)) - List/Nil: [] - } +String/split (s: String) (delimiter: u24) : (List String) +String/split s delim = (String/split.go s delim [""]) + +String/split.go (cs: String) (delim: u24) (acc: (List String)) : (List String) +String/split.go (String/Nil) _ acc = (List/reverse acc) +String/split.go (String/Cons c cs) delim acc = + if (== c delim) { + # Start a new split string + (String/split.go cs delim (List/Cons String/Nil acc)) + } else { + match acc { + # Add the current character to the current split string + List/Cons: (String/split.go cs delim (List/Cons (String/Cons c acc.head) acc.tail)) + # Should be unreachable + List/Nil: [] } + } -# DiffList/new() -> (List(T) -> List(T)) # Create a new difference list -DiffList/new = λx x +def DiffList/new() -> (List(T) -> List(T)): + return lambda x: x -# DiffList/wrap(head: T) -> (List(T) -> List(T) # Creates a new difference list with just the given value. -def DiffList/wrap(head): +def DiffList/wrap(head: T) -> (List(T) -> List(T)): return lambda tail: List/Cons(head, tail) -# DiffList/append(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)) # Append a value to the end of the difference list -DiffList/append diff val = λx (diff (List/Cons val x)) +def DiffList/append(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)): + return lambda x: diff(List/Cons(val, x)) -# DiffList/append(left: List(T) -> List(T), right: List(T) -> List(T)) -> (List(T) -> List(T)) # Concatenates two difference lists. -def DiffList/concat(left, right): +def DiffList/concat( + left: List(T) -> List(T), + right: List(T) -> List(T) +) -> (List(T) -> List(T)): return lambda x: left(right(x)) -# DiffList/cons(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)) # Append a value to the beginning of the difference list -DiffList/cons diff val = λx (List/Cons val (diff x)) +def DiffList/cons(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T)): + return lambda x: List/Cons(val, diff(x)) -# DiffList/to_list(diff: List(T) -> List(T)) -> (List(T)) # Convert a difference list to a list -DiffList/to_list diff = (diff List/Nil) - -type Nat = (Succ ~pred) | (Zero) - -type Result = (Ok val) | (Err val) - -Result/unwrap res = match res { - Result/Ok: res.val; - Result/Err: res.val; -} - -type Tree: - Node { ~left, ~right } - Leaf { value } +def DiffList/to_list(diff: List(T) -> List(T)) -> (List(T)): + return diff(List/Nil) + +type Nat = (Succ ~(pred: Nat)) | (Zero) + +type (Result o e) = (Ok (val: o)) | (Err (val: e)) + +def Result/unwrap(res: Result(T, E)) -> Any: + match res: + case Result/Ok: + return res.val + case Result/Err: + return res.val + +# Returns the second result if the first one is `Ok`. +# Otherwise, returns the `Err` of the first result. +def Result/and(fst: Result(A, E), snd: Result(B, E)) -> Result(B, E): + match fst: + case Result/Ok: + return snd + case Result/Err: + return fst + +# Maps the error value of a result. +def Result/map_err(res: Result(T, E), f: E -> F) -> Result(T, F): + match res: + case Result/Ok: + return Result/Ok(res.val) + case Result/Err: + return Result/Err(f(res.val)) + +type Tree(T): + Node { ~left: Tree(T), ~right: Tree(T) } + Leaf { value: T } # Returns a List converted from a Tree. -def Tree/to_list(tree): +def Tree/to_list(tree: Tree(T)) -> List(T): fold tree: case Tree/Leaf: list = DiffList/wrap(tree.value) @@ -121,7 +163,7 @@ def Tree/to_list(tree): return DiffList/to_list(list) # Reverses a tree swapping right and left leaves. -def Tree/reverse(tree): +def Tree/reverse(tree: Tree(T)) -> Tree(T): fold tree: case Tree/Leaf: return !tree.value @@ -130,13 +172,13 @@ def Tree/reverse(tree): # MAP Impl -type Map = (Node value ~left ~right) | (Leaf) +type Map T = (Node (value: T) ~(left: (Map T)) ~(right: (Map T))) | (Leaf) -Map/empty = Map/Leaf +Map/empty : (Map T) = Map/Leaf -Map/get map key = +Map/get (map: (Map T)) (key: u24) : (T, (Map T)) = match map { - Map/Leaf: (*, map) + Map/Leaf: (unreachable, map) Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { @@ -151,7 +193,7 @@ Map/get map key = } } -Map/set map key value = +Map/set (map: (Map T)) (key: u24) (value: T) : (Map T) = match map { Map/Node: switch _ = (== 0 key) { @@ -164,13 +206,14 @@ Map/set map key value = Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { - 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf) - _: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)) + 0: (Map/Node unreachable (Map/set Map/Leaf (/ key 2) value) Map/Leaf) + _: (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ key 2) value)) } _: (Map/Node value Map/Leaf Map/Leaf) } } +Map/map (map: (Map T)) (key: u24) (f: T -> T) : (Map T) Map/map (Map/Leaf) key f = Map/Leaf Map/map (Map/Node value left right) key f = switch _ = (== 0 key) { @@ -186,266 +229,348 @@ Map/map (Map/Node value left right) key f = # IO Impl -type IO: - Done { magic, expr } - Call { magic, func, argm, cont } +type IO(T): + Done { magic: (u24, u24), expr: T } + Call { magic: (u24, u24), func: String, argm: Any, cont: Result(Any, IOError(Any)) -> IO(T) } -type IOError: +type IOError(T): Type Name - Inner { value } - -def IO/MAGIC: + Inner { value: T } + +def IOError/unwrap_inner(err: IOError(T)) -> T: + match err: + case IOError/Type: + return unreachable() + case IOError/Name: + return unreachable() + case IOError/Inner: + return err.value + +def IO/MAGIC() -> (u24, u24): return (0xD0CA11, 0xFF1FF1) -def IO/wrap(x): +def IO/wrap(x: T) -> IO(T): return IO/Done(IO/MAGIC, x) -def IO/bind(a, b): +def IO/bind(a: IO(A), b: ((Id -> Id) -> A -> IO(B))) -> IO(B): match a: case IO/Done: - b = undefer(b) - return b(a.expr) + return undefer(b, a.expr) case IO/Call: - return IO/Call(IO/MAGIC, a.func, a.argm, lambda x: IO/bind(a.cont(x), b)) - -def call(func, argm): + return IO/Call(a.magic, a.func, a.argm, lambda x: IO/bind(a.cont(x), b)) + +# Calls an IO by its name with the given arguments. +# +# The arguments are untyped and not checked for type correctness. +# If type safety is desired, this function should be wrapped with +# another that checks the types of the arguments and of the return. +# +# Always returns a `Result` where the error is an `IOError`, a type +# that either contains an internal error of the IO function, like an +# `errno` in the case of FS functions, or a general Bend IO error, +# like a type error if the arguments are invalid or a name error if +# the called IO is not found. +def IO/call(func: String, argm: Any) -> IO(Result(Any, IOError(Any))): return IO/Call(IO/MAGIC, func, argm, lambda x: IO/Done(IO/MAGIC, x)) -IO/done_on_err (IO/Call magic func argm cont) = (IO/Call magic func argm @res match res { - Result/Ok: (cont res.val) - Result/Err: (IO/Done IO/MAGIC (Result/Err res.val)) -}) -IO/done_on_err done = done +# Maps the result of an IO. +def IO/map(io: IO(A), f: A -> B) -> IO(B): + with IO: + a <- io + return wrap(f(a)) + +# Unwraps the `IOError` of the result of an IO, returning the `Inner` variant. +# +# Should only be called if the other `IOError` variants are unreachable. +def IO/unwrap_inner(io: IO(Result(A, IOError(B)))) -> IO(Result(A, B)): + with IO: + res <- io + match res: + case Result/Ok: + return wrap(Result/Ok(res.val)) + case Result/Err: + return wrap(Result/Err(IOError/unwrap_inner(res.val))) ## Time and sleep -# Returns a monotonically increasing nanosecond timestamp as an u48 encoded as a pair of u24s. -IO/get_time = with IO { - ask res = (IO/Call IO/MAGIC "GET_TIME" * @x (IO/Done IO/MAGIC x)) - (wrap (Result/unwrap res)) - } + +# Returns a monotonically increasing nanosecond timestamp as an u48 +# encoded as a pair of u24s. +def IO/get_time() -> IO((u24, u24)): + with IO: + res <- IO/call("GET_TIME", *) + return wrap(Result/unwrap(res)) # Sleeps for the given number of nanoseconds, given by an u48 encoded as a pair of u24s. -IO/nanosleep hi_lo = (IO/Call IO/MAGIC "SLEEP" hi_lo @x (IO/Done IO/MAGIC x)) +def IO/nanosleep(hi_lo: (u24, u24)) -> IO(None): + with IO: + res <- IO/call("SLEEP", hi_lo) + return wrap(Result/unwrap(res)) # Sleeps for a given amount of seconds as a float. -def IO/sleep(seconds): +def IO/sleep(seconds: f24) -> IO(None): nanos = seconds * 1_000_000_000.0 - lo = to_u24(nanos % 0x1_000_000.0) - hi = to_u24(nanos / 0x1_000_000.0) - with IO: - res <- IO/nanosleep((hi, lo)) - return wrap(Result/unwrap(res)) + lo = f24/to_u24(nanos % 0x1_000_000.0) + hi = f24/to_u24(nanos / 0x1_000_000.0) + return IO/nanosleep((hi, lo)) ## File IO ### File IO primitives -# IO/FS/open(path: String, mode: u24) -> (IO u24) -IO/FS/open path mode = (IO/Call IO/MAGIC "OPEN" (path, mode) IO/wrap) +def IO/FS/open(path: String, mode: String) -> IO(Result(u24, u24)): + return IO/unwrap_inner(IO/call("OPEN", (path, mode))) -# IO/FS/close(file: u24) -> (IO None) -IO/FS/close file = (IO/Call IO/MAGIC "CLOSE" file IO/wrap) +def IO/FS/close(file: u24) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("CLOSE", file)) -# IO/FS/read(file: u24, num_bytes: u24) -> (IO (List u24)) -IO/FS/read file num_bytes = (IO/Call IO/MAGIC "READ" (file, num_bytes) IO/wrap) +def IO/FS/read(file: u24, num_bytes: u24) -> IO(Result(List(u24), u24)): + return IO/unwrap_inner(IO/call("READ", (file, num_bytes))) -# IO/FS/write(file: u24, bytes: (List u24)) -> (IO None) -IO/FS/write file bytes = (IO/Call IO/MAGIC "WRITE" (file, bytes) IO/wrap) +def IO/FS/write(file: u24, bytes: List(u24)) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("WRITE", (file, bytes))) -# IO/FS/seek(file: u24, offset: i24, mode: u24) -> (IO None) -IO/FS/seek file offset mode = (IO/Call IO/MAGIC "SEEK" (file, (offset, mode)) IO/wrap) +def IO/FS/seek(file: u24, offset: i24, mode: i24) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("SEEK", (file, (offset, mode)))) -# IO/FS/flush(file: u24) -> (IO None) -IO/FS/flush file = (IO/Call IO/MAGIC "FLUSH" file IO/wrap) +def IO/FS/flush(file: u24) -> IO(Result(None, u24)): + return IO/unwrap_inner(IO/call("FLUSH", file)) ### Always available files -IO/FS/STDIN = 0 -IO/FS/STDOUT = 1 -IO/FS/STDERR = 2 +IO/FS/STDIN : u24 = 0 +IO/FS/STDOUT : u24 = 1 +IO/FS/STDERR : u24 = 2 ### Seek modes # Seek from start of file. -IO/FS/SEEK_SET = +0 +IO/FS/SEEK_SET : i24 = +0 # Seek from current position. -IO/FS/SEEK_CUR = +1 +IO/FS/SEEK_CUR : i24 = +1 # Seek from end of file. -IO/FS/SEEK_END = +2 +IO/FS/SEEK_END : i24 = +2 ### File utilities -# IO/FS/read_file(path: String) -> (IO (List u24)) # Reads an entire file, returning a list of bytes. -def IO/FS/read_file(path): +def IO/FS/read_file(path: String) -> IO(Result(List(u24), u24)): with IO: - fd <- IO/done_on_err(IO/FS/open(path, "r")) - bytes <- IO/FS/read_to_end(fd) - * <- IO/done_on_err(IO/FS/close(fd)) - return wrap(bytes) + res_fd <- IO/FS/open(path, "r") + match res_fd: + case Result/Ok: + fd = res_fd.val + res1 <- IO/FS/read_to_end(fd) + res2 <- IO/FS/close(fd) + return wrap(Result/and(res2, res1)) + case Result/Err: + return wrap(Result/Err(res_fd.val)) -# IO/FS/read_to_end(fd: u24) -> (IO (List u24)) # Reads the remaining contents of a file, returning a list of read bytes. -def IO/FS/read_to_end(fd): +def IO/FS/read_to_end(fd: u24) -> IO(Result(List(u24), u24)): return IO/FS/read_to_end.read_chunks(fd, []) -def IO/FS/read_to_end.read_chunks(fd, chunks): +def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(List(u24), u24)): with IO: # Read file in 1MB chunks - chunk <- IO/done_on_err(IO/FS/read(fd, 1048576)) - match chunk: - case List/Nil: - return wrap(List/flatten(chunks)) - case List/Cons: - return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks)) - -# IO/FS/read_line(fd: u24) -> (IO (List u24)) + res_chunk <- IO/FS/read(fd, 1048576) + match res_chunk: + case Result/Ok: + chunk = res_chunk.val + match chunk: + case List/Nil: + return wrap(Result/Ok(List/flatten(chunks))) + case List/Cons: + return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks)) + case Result/Err: + return wrap(Result/Err(res_chunk.val)) + # Reads a single line from a file, returning a list of bytes. -def IO/FS/read_line(fd): +def IO/FS/read_line(fd: u24) -> IO(Result(List(u24), u24)): return IO/FS/read_line.read_chunks(fd, []) -def IO/FS/read_line.read_chunks(fd, chunks): +def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(Result(List(u24), u24)): with IO: # Read line in 1kB chunks - chunk <- IO/done_on_err(IO/FS/read(fd, 1024)) - match res = List/split_once(chunk, '\n'): - # Found a newline, backtrack and join chunks + res_chunk <- IO/FS/read(fd, 1024) + match res_chunk: case Result/Ok: - (line, rest) = res.val - (length, *) = List/length(rest) - * <- IO/FS/seek(fd, to_i24(length) * -1, IO/FS/SEEK_CUR) - chunks = List/Cons(line, chunks) - bytes = List/flatten(chunks) - return wrap(bytes) - # Newline not found + chunk = res_chunk.val + match res = List/split_once(chunk, lambda x: x == '\n'): + # Found a newline, backtrack and join chunks + case Result/Ok: + (line, rest) = res.val + (length, *) = List/length(rest) + res_seek <- IO/FS/seek(fd, u24/to_i24(length) * -1, IO/FS/SEEK_CUR) + match res_seek: + case Result/Ok: + chunks = List/Cons(line, chunks) + bytes = List/flatten(chunks) + return wrap(Result/Ok(bytes)) + case Result/Err: + return wrap(Result/Err(res_seek.val)) + # Newline not found + case Result/Err: + line = res.val + (length, line) = List/length(line) + # If length is 0, the end of the file was reached, return as if it was a newline + if length == 0: + bytes = List/flatten(chunks) + return wrap(Result/Ok(bytes)) + # Otherwise, the line is still ongoing, read more chunks + else: + chunks = List/Cons(line, chunks) + return IO/FS/read_line.read_chunks(fd, chunks) case Result/Err: - line = res.val - (length, line) = List/length(line) - # If length is 0, the end of the file was reached, return as if it was a newline - if length == 0: - bytes = List/flatten(chunks) - return wrap(bytes) - # Otherwise, the line is still ongoing, read more chunks - else: - chunks = List/Cons(line, chunks) - return IO/FS/read_line.read_chunks(fd, chunks) - -# IO/FS/write_file(path: String, bytes: (List u24)) -> (IO None) + return wrap(Result/Err(res_chunk.val)) + # Writes a list of bytes to a file given by a path. -def IO/FS/write_file(path, bytes): +def IO/FS/write_file(path: String, bytes: List(u24)) -> IO(Result(None, u24)): with IO: - f <- IO/FS/open(path, "w") - match f: - case Result/Err: - return wrap(Result/Err(f.val)) + res_f <- IO/FS/open(path, "w") + match res_f: case Result/Ok: - * <- IO/done_on_err(IO/FS/write(f.val, bytes)) - * <- IO/done_on_err(IO/FS/close(f.val)) - return wrap(Result/Ok(bytes)) + f = res_f.val + res1 <- IO/FS/write(f, bytes) + res2 <- IO/FS/close(f) + return wrap(Result/and(res2, res1)) + case Result/Err: + return wrap(Result/Err(res_f.val)) ### Standard input and output utilities -# IO/print(text: String) -> (IO *) # Prints a string to stdout, encoding it with utf-8. -IO/print text = with IO { - ask res = (IO/FS/write IO/FS/STDOUT (String/encode_utf8 text)) - (wrap (Result/unwrap res)) - } +def IO/print(text: String) -> IO(None): + with IO: + res <- IO/FS/write(IO/FS/STDOUT, String/encode_utf8(text)) + return wrap(Result/unwrap(res)) # IO/input() -> IO String # Read characters from stdin until a newline is found. # Returns the read input decoded as utf-8. -IO/input = (IO/input.go DiffList/new) -def IO/input.go(acc): +def IO/input() -> IO(Result(String, u24)): + return IO/input.go(DiffList/new) + +def IO/input.go(acc: List(u24) -> List(u24)) -> IO(Result(String, u24)): # TODO: This is slow and inefficient, should be done in hvm using fgets. with IO: - byte <- IO/done_on_err(IO/FS/read(IO/FS/STDIN, 1)) - match byte: - case List/Nil: - # Nothing read, try again (to simulate blocking a read) - return IO/input.go(acc) - case List/Cons: - if byte.head == '\n': - bytes = DiffList/to_list(acc) - text = String/decode_utf8(bytes) - return wrap(text) - else: - acc = DiffList/append(acc, byte.head) - return IO/input.go(acc) + res_byte <- IO/FS/read(IO/FS/STDIN, 1) + match res_byte: + case Result/Ok: + byte = res_byte.val + match byte: + case List/Nil: + # Nothing read, try again (to simulate blocking a read) + return IO/input.go(acc) + case List/Cons: + if byte.head == '\n': + bytes = DiffList/to_list(acc) + text = String/decode_utf8(bytes) + return wrap(Result/Ok(text)) + else: + acc = DiffList/append(acc, byte.head) + return IO/input.go(acc) + case Result/Err: + return wrap(Result/Err(res_byte.val)) ### Dynamically linked libraries -# IO/DyLib/open(path: String, lazy: u24) -> u24 +# Returns an unique id to the library object encoded as a u24 # 'path' is the path to the library file. # 'lazy' is a boolean encoded as a u24 that determines if all functions are loaded lazily or upfront. -# Returns an unique id to the library object encoded as a u24 -def IO/DyLib/open(path, lazy): - return IO/Call(IO/MAGIC, "DL_OPEN", (path, lazy), IO/wrap) +def IO/DyLib/open(path: String, lazy: u24) -> IO(Result(u24, String)): + return IO/unwrap_inner(IO/call("DL_OPEN", (path, lazy))) -# IO/DyLib/call(dl: u24, fn: String, args: Any) -> Any # Calls a function of a previously opened library. +# The returned value is determined by the called function. # 'dl' is the id of the library object. # 'fn' is the name of the function in the library. # 'args' are the arguments to the function. The expected values depend on the called function. -# The returned value is determined by the called function. -def IO/DyLib/call(dl, fn, args): - return IO/Call(IO/MAGIC, "DL_CALL", (dl, fn, args), IO/wrap) +def IO/DyLib/call(dl: u24, fn: String, args: Any) -> IO(Result(Any, String)): + return IO/unwrap_inner(IO/call("DL_CALL", (dl, (fn, args)))) -# IO/DyLib/close(dl: u24) -> None # Closes a previously open library. -# 'dl' is the id of the library object. # Returns nothing. -def IO/DyLib/close(dl): - return IO/Call(IO/MAGIC, "DL_CLOSE", (dl), IO/wrap) +# 'dl' is the id of the library object. +def IO/DyLib/close(dl: u24) -> IO(Result(None, String)): + return IO/unwrap_inner(IO/call("DL_CLOSE", dl)) # Lazy thunks -# defer(val: T) -> ((T -> T) -> T) -# We can defer the evaluation of a function by wrapping it in a thunk + +# We can defer the evaluation of a function by wrapping it in a thunk. # Ex: @x (x @arg1 @arg2 @arg3 (f arg1 arg2 arg3) arg1 arg2 arg3) -# This is only evaluated when we call it with 'undefer' (undefer my_thunk) -# We can build a defered call directly or by by using defer and defer_arg +# +# This is only evaluated when we call it with `(undefer my_thunk)`. +# We can build a defered call directly or by by using `defer` and `defer_arg`. +# # The example above can be written as: +# # (defer_arg (defer_arg (defer_arg (defer @arg1 @arg2 @arg3 (f arg1 arg2 arg3)) arg1) arg2) arg3) -defer val = @x (x val) +def defer(val: T) -> (T -> T) -> T: + return lambda x: x(val) -# defer_arg(defered: A -> B -> C, arg: B) -> (A -> C) -defer_arg defered arg = @x (defered x arg) +def defer_arg(defered: (Id -> Id) -> A -> B, arg: A) -> ((Id -> Id) -> B): + return lambda x: defered(x, arg) -# undefer(defered: (A -> A) -> B) -> B -undefer defered = (defered @x x) +def undefer(defered: (Id -> Id) -> T) -> T: + return defered(lambda x: x) +# A function that can be used in unreachable code. +# +# Is not type safe and if used in code that is actually reachable, will corrupt the program. +def unreachable() -> Any: + return * # Native number casts -# to_f24(x: native number) -> f24 -# Casts any native number to an f24. -hvm to_f24: - ($([f24] ret) ret) +# Casts a f24 number to a u24. +hvm f24/to_u24 -> (f24 -> u24): + ($([u24] ret) ret) -# to_u24(x: native number) -> u24 -# Casts any native number to a u24. -hvm to_u24: +# Casts an i24 number to a u24. +hvm i24/to_u24 -> (i24 -> u24): ($([u24] ret) ret) -# to_i24(x: native number) -> i24 -# Casts any native number to an i24. -hvm to_i24: +# Casts a u24 number to an i24. +hvm u24/to_i24 -> (u24 -> i24): ($([i24] ret) ret) +# Casts a f24 number to an i24. +hvm f24/to_i24 -> (f24 -> i24): + ($([i24] ret) ret) + +# Casts a u24 number to a f24. +hvm u24/to_f24 -> (u24 -> f24): + ($([f24] ret) ret) + +# Casts an i24 number to a f24. +hvm i24/to_f24 -> (i24 -> f24): + ($([f24] ret) ret) + +def u24/to_string(n: u24) -> String: + def go(n: u24) -> String -> String: + r = n % 10 + d = n / 10 + c = '0' + r + if d == 0: + return lambda t: String/Cons(c, t) + else: + return lambda t: go(d, String/Cons(c, t)) + return go(n, String/Nil) + # String Encoding and Decoding -Utf8/REPLACEMENT_CHARACTER = '\u{FFFD}' +Utf8/REPLACEMENT_CHARACTER : u24 = '\u{FFFD}' -# String/decode_utf8(List u24) -> String # Decodes a sequence of bytes to a String using utf-8 encoding. -String/decode_utf8 bytes = +# Invalid utf-8 sequences are replaced with Utf8/REPLACEMENT_CHARACTER. +String/decode_utf8 (bytes: (List u24)) : String = let (got, rest) = (Utf8/decode_character bytes) match rest { List/Nil: (String/Cons got String/Nil) List/Cons: (String/Cons got (String/decode_utf8 rest)) } -# Utf8/decode_character(List u24) -> (u24, List u24) # Decodes one utf-8 character from the start of a sequence of bytes. # Returns the decoded character and the remaining bytes. +Utf8/decode_character (bytes: (List u24)) : (u24, (List u24)) Utf8/decode_character [] = (0, []) Utf8/decode_character [a] = if (<= a 0x7F) { (a, []) } else { (Utf8/REPLACEMENT_CHARACTER, []) } Utf8/decode_character [a, b] = @@ -506,8 +631,8 @@ Utf8/decode_character (List/Cons a (List/Cons b (List/Cons c (List/Cons d rest)) } } -# String/encode_utf8(String) -> (List u24) # Encodes a string to a sequence of bytes using utf-8 encoding. +String/encode_utf8 (str: String) : (List u24) String/encode_utf8 (String/Nil) = (List/Nil) String/encode_utf8 (String/Cons x xs) = use Utf8/rune1max = 0b01111111 @@ -541,13 +666,13 @@ String/encode_utf8 (String/Cons x xs) = } } -# String/decode_ascii(List u24) -> String # Decodes a sequence of bytes to a String using ascii encoding. +String/decode_ascii (bytes: (List u24)) : String String/decode_ascii (List/Cons x xs) = (String/Cons x (String/decode_ascii xs)) String/decode_ascii (List/Nil) = (String/Nil) -# String/encode_ascii(String) -> (List u24) # Encodes a string to a sequence of bytes using ascii encoding. +String/encode_ascii (str: String) : (List u24) String/encode_ascii (String/Cons x xs) = (List/Cons x (String/encode_ascii xs)) String/encode_ascii (String/Nil) = (List/Nil) @@ -555,95 +680,92 @@ String/encode_ascii (String/Nil) = (List/Nil) # Math/PI() -> f24 # The Pi (π) constant. -def Math/PI(): +def Math/PI() -> f24: return 3.1415926535 -def Math/E(): +def Math/E() -> f24: return 2.718281828 # Math/log(x: f24, base: f24) -> f24 # Computes the logarithm of `x` with the specified `base`. -hvm Math/log: +hvm Math/log -> (f24 -> f24 -> f24): (x ($([|] $(x ret)) ret)) # Math/atan2(x: f24, y: f24) -> f24 # Has the same behaviour as `atan2f` in the C math lib. # Computes the arctangent of the quotient of its two arguments. -hvm Math/atan2: +hvm Math/atan2 -> (f24 -> f24 -> f24): ($([&] $(y ret)) (y ret)) # Math/sin(a: f24) -> f24 # Computes the sine of the given angle in radians. -hvm Math/sin: +hvm Math/sin -> (f24 -> f24): ($([<<0x0] a) a) # Math/cos(a: f24) -> f24 # Computes the cosine of the given angle in radians. -hvm Math/cos: +hvm Math/cos -> (f24 -> f24): (a b) & @Math/PI ~ $([:/2.0] $([-] $(a $([<<0x0] b)))) # Math/tan(a: f24) -> f24 # Computes the tangent of the given angle in radians. -hvm Math/tan: +hvm Math/tan -> (f24 -> f24): ($([>>0x0] a) a) -# Math/cot(a: f24) -> f24 # Computes the cotangent of the given angle in radians. -Math/cot a = (/ 1.0 (Math/tan a)) +Math/cot (a: f24) : f24 = + (/ 1.0 (Math/tan a)) -# Math/sec(a: f24) -> f24 # Computes the secant of the given angle in radians. -Math/sec a = (/ 1.0 (Math/cos a)) +Math/sec (a: f24) : f24 = + (/ 1.0 (Math/cos a)) -# Math/csc(a: f24) -> f24 # Computes the cosecant of the given angle in radians. -Math/csc a = (/ 1.0 (Math/sin a)) +Math/csc (a: f24) : f24 = + (/ 1.0 (Math/sin a)) -# Math/atan(a: f24) -> f24 # Computes the arctangent of the given angle. -Math/atan a = (Math/atan2 a 1.0) +Math/atan (a: f24) : f24 = + (Math/atan2 a 1.0) -# Math/asin(a: f24) -> f24 # Computes the arcsine of the given angle. -Math/asin a = (Math/atan2 a (Math/sqrt (- 1.0 (* a a)))) +Math/asin (a: f24) : f24 = + (Math/atan2 a (Math/sqrt (- 1.0 (* a a)))) -# Math/acos(a: f24) -> f24 # Computes the arccosine of the given angle. -Math/acos a = (Math/atan2 (Math/sqrt (- 1.0 (* a a))) a) +Math/acos (a: f24) : f24 = + (Math/atan2 (Math/sqrt (- 1.0 (* a a))) a) -# Math/radians(a: f24) -> f24 # Converts degrees to radians. -Math/radians a = (* a (/ Math/PI 180.0)) +Math/radians (a: f24) : f24 = + (* a (/ Math/PI 180.0)) -# Math/sqrt(x: f24) -> f24 # Computes the square root of the given number. -Math/sqrt n = (** n 0.5) +Math/sqrt (n: f24) : f24 = + (** n 0.5) -# Math/ceil(n: f24) -> f24 # Round float up to the nearest integer. -def Math/ceil(n): - i_n = to_f24(to_i24(n)) +def Math/ceil(n: f24) -> f24: + i_n = i24/to_f24(f24/to_i24(n)) if n <= i_n: return i_n else: return i_n + 1.0 -# Math/floor(n: f24) -> f24 # Round float down to the nearest integer. -def Math/floor(n): - i_n = to_f24(to_i24(n)) +def Math/floor(n: f24) -> f24: + i_n = i24/to_f24(f24/to_i24(n)) if n < i_n: return i_n - 1.0 else: return i_n -# Math/round(n: f24) -> f24 # Round float to the nearest integer. -def Math/round(n): - i_n = to_f24(to_i24(n)) - if n - i_n < 0.5: +def Math/round(n: f24) -> f24: + i_n = i24/to_f24(f24/to_i24(n)) + if (n - i_n) < 0.5: return Math/floor(n) else: return Math/ceil(n) diff --git a/src/fun/builtins.rs b/src/fun/builtins.rs index d9489ffef..10d650874 100644 --- a/src/fun/builtins.rs +++ b/src/fun/builtins.rs @@ -1,5 +1,5 @@ use super::{ - parser::{ParseBook, TermParser}, + parser::{FunParser, ParseBook}, Book, Name, Num, Pattern, Term, }; use crate::maybe_grow; @@ -47,7 +47,8 @@ pub const BUILTIN_TYPES: &[&str] = &[LIST, STRING, NAT, TREE, MAP, IO]; impl ParseBook { pub fn builtins() -> Self { - let book = TermParser::new(BUILTINS).parse_book(Self::default(), true); + let book = + FunParser::new(Name::new("/src/fun/builtins.bend"), BUILTINS, true).parse_book(Self::default()); book.unwrap_or_else(|e| panic!("Error parsing builtin file, this should not happen:\n{e}")) } } diff --git a/src/fun/check/check_untyped.rs b/src/fun/check/check_untyped.rs new file mode 100644 index 000000000..9f74e80a2 --- /dev/null +++ b/src/fun/check/check_untyped.rs @@ -0,0 +1,81 @@ +use crate::{ + diagnostics::Diagnostics, + fun::{Ctx, FanKind, Pattern, Tag, Term}, + maybe_grow, +}; + +impl Ctx<'_> { + /// Checks that terms that cannot be typed are only used inside untyped functions. + pub fn check_untyped_terms(&mut self) -> Result<(), Diagnostics> { + for def in self.book.defs.values() { + if def.check { + for rule in def.rules.iter() { + if let Err(e) = rule.body.check_untyped_terms() { + self.info.add_function_error(e, def.name.clone(), def.source.clone()); + } + } + } + } + self.info.fatal(()) + } +} + +impl Term { + fn check_untyped_terms(&self) -> Result<(), String> { + maybe_grow(|| { + match self { + Term::Lam { tag: Tag::Static, pat, .. } => pat.check_untyped_patterns()?, + Term::Lam { tag: _, .. } => { + return Err("Tagged lambda in type-checked function".to_string()); + } + Term::Link { nam } => { + return Err(format!("Unscoped variable '${nam}' in type-checked function")); + } + Term::App { tag: Tag::Static, .. } => {} + Term::App { tag: _, .. } => { + return Err("Tagged application in type-checked function".to_string()); + } + Term::Fan { fan: FanKind::Dup, .. } => { + return Err("Superposition term in type-checked function".to_string()); + } + Term::Fan { fan: FanKind::Tup, tag: Tag::Static, .. } => {} + Term::Fan { fan: FanKind::Tup, tag: _, .. } => { + return Err("Tagged tuple in type-checked function".to_string()); + } + Term::Let { pat, .. } => { + pat.check_untyped_patterns()?; + } + _ => {} + } + for child in self.children() { + child.check_untyped_terms()?; + } + Ok(()) + }) + } +} + +impl Pattern { + fn check_untyped_patterns(&self) -> Result<(), String> { + maybe_grow(|| { + match self { + Pattern::Chn(x) => { + return Err(format!("Unscoped variable bind '${x}' in type-checked function")); + } + Pattern::Fan(FanKind::Dup, Tag::Auto, _) => {} + Pattern::Fan(FanKind::Dup, _, _) => { + return Err("Tagged duplication in type-checked function".to_string()); + } + Pattern::Fan(FanKind::Tup, Tag::Static, _) => {} + Pattern::Fan(FanKind::Tup, _, _) => { + return Err("Tagged tuple elimination in type-checked function".to_string()); + } + _ => {} + } + for pat in self.children() { + pat.check_untyped_patterns()?; + } + Ok(()) + }) + } +} diff --git a/src/fun/check/mod.rs b/src/fun/check/mod.rs index e54bdbd3f..21a8e2c8f 100644 --- a/src/fun/check/mod.rs +++ b/src/fun/check/mod.rs @@ -1,4 +1,6 @@ +pub mod check_untyped; pub mod set_entrypoint; pub mod shared_names; +pub mod type_check; pub mod unbound_refs; pub mod unbound_vars; diff --git a/src/fun/check/type_check.rs b/src/fun/check/type_check.rs new file mode 100644 index 000000000..a06935c11 --- /dev/null +++ b/src/fun/check/type_check.rs @@ -0,0 +1,785 @@ +//! Optional Hindley-Milner-like type system. +//! +//! Based on https://github.com/developedby/algorithm-w-rs +//! and https://github.com/mgrabmueller/AlgorithmW. +use crate::{ + diagnostics::Diagnostics, + fun::{num_to_name, Adt, Book, Ctx, FanKind, MatchRule, Name, Num, Op, Pattern, Tag, Term, Type}, + maybe_grow, +}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +impl Ctx<'_> { + pub fn type_check(&mut self) -> Result<(), Diagnostics> { + let types = infer_book(self.book, &mut self.info)?; + + for def in self.book.defs.values_mut() { + def.typ = types[&def.name].instantiate(&mut VarGen::default()); + } + + Ok(()) + } +} + +type ProgramTypes = HashMap; + +/// A type scheme, aka a polymorphic type. +#[derive(Clone)] +struct Scheme(Vec, Type); + +/// A finite mapping from type variables to types. +#[derive(Clone, Default)] +struct Subst(BTreeMap); + +/// A mapping from term variables to type schemes. +#[derive(Clone, Default)] +struct TypeEnv(BTreeMap); + +/// Variable generator for type variables. +#[derive(Default)] +struct VarGen(usize); + +/// Topologically ordered set of mutually recursive groups of functions. +struct RecGroups(Vec>); + +/* Implementations */ + +impl Type { + fn free_type_vars(&self) -> BTreeSet { + maybe_grow(|| match self { + Type::Var(x) => BTreeSet::from([x.clone()]), + Type::Ctr(_, ts) | Type::Tup(ts) => ts.iter().flat_map(|t| t.free_type_vars()).collect(), + Type::Arr(t1, t2) => t1.free_type_vars().union(&t2.free_type_vars()).cloned().collect(), + Type::Number(t) | Type::Integer(t) => t.free_type_vars(), + Type::U24 | Type::F24 | Type::I24 | Type::None | Type::Any | Type::Hole => BTreeSet::new(), + }) + } + + fn subst(&self, subst: &Subst) -> Type { + maybe_grow(|| match self { + Type::Var(nam) => match subst.0.get(nam) { + Some(new) => new.clone(), + None => self.clone(), + }, + Type::Ctr(name, ts) => Type::Ctr(name.clone(), ts.iter().map(|t| t.subst(subst)).collect()), + Type::Arr(t1, t2) => Type::Arr(Box::new(t1.subst(subst)), Box::new(t2.subst(subst))), + Type::Tup(els) => Type::Tup(els.iter().map(|t| t.subst(subst)).collect()), + Type::Number(t) => Type::Number(Box::new(t.subst(subst))), + Type::Integer(t) => Type::Integer(Box::new(t.subst(subst))), + t @ (Type::U24 | Type::F24 | Type::I24 | Type::None | Type::Any | Type::Hole) => t.clone(), + }) + } + + /// Converts a monomorphic type into a type scheme by abstracting + /// over the type variables free in `t`, but not free in the type + /// environment. + fn generalize(&self, env: &TypeEnv) -> Scheme { + let vars_env = env.free_type_vars(); + let vars_t = self.free_type_vars(); + let vars = vars_t.difference(&vars_env).cloned().collect(); + Scheme(vars, self.clone()) + } +} + +impl Scheme { + fn free_type_vars(&self) -> BTreeSet { + let vars = self.1.free_type_vars(); + let bound_vars = self.0.iter().cloned().collect(); + vars.difference(&bound_vars).cloned().collect() + } + + fn subst(&self, subst: &Subst) -> Scheme { + let mut subst = subst.clone(); + for x in self.0.iter() { + subst.0.remove(x); + } + let t = self.1.subst(&subst); + Scheme(self.0.clone(), t) + } + + /// Converts a type scheme into a monomorphic type by assigning + /// fresh type variables to each variable bound by the scheme. + fn instantiate(&self, var_gen: &mut VarGen) -> Type { + let new_vars = self.0.iter().map(|_| var_gen.fresh()); + let subst = Subst(self.0.iter().cloned().zip(new_vars).collect()); + self.1.subst(&subst) + } +} + +impl Subst { + /// Compose two substitutions. + /// + /// Applies the first substitution to the second, and then inserts the result into the first. + fn compose(mut self, other: Subst) -> Subst { + let other = other.0.into_iter().map(|(x, t)| (x, t.subst(&self))).collect::>(); + self.0.extend(other); + self + } +} + +impl TypeEnv { + fn free_type_vars(&self) -> BTreeSet { + let mut vars = BTreeSet::new(); + for scheme in self.0.values() { + let scheme_vars = scheme.free_type_vars(); + vars = vars.union(&scheme_vars).cloned().collect(); + } + vars + } + + fn subst(&self, subst: &Subst) -> TypeEnv { + let env = self.0.iter().map(|(x, scheme)| (x.clone(), scheme.subst(subst))).collect(); + TypeEnv(env) + } + + fn insert(&mut self, name: Name, scheme: Scheme) { + self.0.insert(name, scheme); + } + + fn add_binds<'a>( + &mut self, + bnd: impl IntoIterator, Scheme)>, + ) -> Vec<(Name, Option)> { + let mut old_bnd = vec![]; + for (name, scheme) in bnd { + if let Some(name) = name { + let old = self.0.insert(name.clone(), scheme); + old_bnd.push((name.clone(), old)); + } + } + old_bnd + } + + fn pop_binds(&mut self, old_bnd: Vec<(Name, Option)>) { + for (name, scheme) in old_bnd { + if let Some(scheme) = scheme { + self.0.insert(name, scheme); + } + } + } +} + +impl VarGen { + fn fresh(&mut self) -> Type { + let x = self.fresh_name(); + Type::Var(x) + } + + fn fresh_name(&mut self) -> Name { + let x = num_to_name(self.0 as u64); + self.0 += 1; + Name::new(x) + } +} + +impl RecGroups { + fn from_book(book: &Book) -> RecGroups { + type DependencyGraph<'a> = BTreeMap<&'a Name, BTreeSet<&'a Name>>; + + fn collect_dependencies<'a>( + term: &'a Term, + book: &'a Book, + scope: &mut Vec, + deps: &mut BTreeSet<&'a Name>, + ) { + if let Term::Ref { nam } = term { + if book.ctrs.contains_key(nam) || book.hvm_defs.contains_key(nam) || !book.defs[nam].check { + // Don't infer types for constructors or unchecked functions + } else { + deps.insert(nam); + } + } + for (child, binds) in term.children_with_binds() { + scope.extend(binds.clone().flatten().cloned()); + collect_dependencies(child, book, scope, deps); + scope.truncate(scope.len() - binds.flatten().count()); + } + } + + /// Tarjan's algorithm for finding strongly connected components. + fn strong_connect<'a>( + v: &'a Name, + deps: &DependencyGraph<'a>, + index: &mut usize, + index_map: &mut BTreeMap<&'a Name, usize>, + low_link: &mut BTreeMap<&'a Name, usize>, + stack: &mut Vec<&'a Name>, + components: &mut Vec>, + ) { + maybe_grow(|| { + index_map.insert(v, *index); + low_link.insert(v, *index); + *index += 1; + stack.push(v); + + if let Some(neighbors) = deps.get(v) { + for w in neighbors { + if !index_map.contains_key(w) { + // Successor w has not yet been visited, recurse on it. + strong_connect(w, deps, index, index_map, low_link, stack, components); + low_link.insert(v, low_link[v].min(low_link[w])); + } else if stack.contains(w) { + // Successor w is in stack S and hence in the current SCC. + low_link.insert(v, low_link[v].min(index_map[w])); + } else { + // If w is not on stack, then (v, w) is an edge pointing + // to an SCC already found and must be ignored. + } + } + } + + // If v is a root node, pop the stack and generate an SCC. + if low_link[v] == index_map[v] { + let mut component = BTreeSet::new(); + while let Some(w) = stack.pop() { + component.insert(w.clone()); + if w == v { + break; + } + } + components.push(component); + } + }) + } + + // Build the dependency graph + let mut deps = DependencyGraph::default(); + for (name, def) in &book.defs { + if book.ctrs.contains_key(name) || !def.check { + // Don't infer types for constructors or unchecked functions + continue; + } + let mut fn_deps = Default::default(); + collect_dependencies(&def.rule().body, book, &mut vec![], &mut fn_deps); + deps.insert(name, fn_deps); + } + + let mut index = 0; + let mut stack = Vec::new(); + let mut index_map = BTreeMap::new(); + let mut low_link = BTreeMap::new(); + let mut components = Vec::new(); + for name in deps.keys() { + if !index_map.contains_key(name) { + strong_connect(name, &deps, &mut index, &mut index_map, &mut low_link, &mut stack, &mut components); + } + } + let components = components.into_iter().map(|x| x.into_iter().collect()).collect(); + RecGroups(components) + } +} + +/* Inference, unification and type checking */ +fn infer_book(book: &Book, diags: &mut Diagnostics) -> Result { + let groups = RecGroups::from_book(book); + let mut env = TypeEnv::default(); + // Note: We store the inferred and generalized types in a separate + // environment, to avoid unnecessary cloning (since no immutable data). + let mut types = ProgramTypes::default(); + + // Add the constructors to the environment. + for adt in book.adts.values() { + for ctr in adt.ctrs.values() { + types.insert(ctr.name.clone(), ctr.typ.generalize(&TypeEnv::default())); + } + } + // Add the types of unchecked functions to the environment. + for def in book.defs.values() { + if !def.check { + types.insert(def.name.clone(), def.typ.generalize(&TypeEnv::default())); + } + } + // Add the types of hvm functions to the environment. + for def in book.hvm_defs.values() { + types.insert(def.name.clone(), def.typ.generalize(&TypeEnv::default())); + } + + // Infer the types of regular functions. + for group in &groups.0 { + infer_group(&mut env, book, group, &mut types, diags)?; + } + Ok(types) +} + +fn infer_group( + env: &mut TypeEnv, + book: &Book, + group: &[Name], + types: &mut ProgramTypes, + diags: &mut Diagnostics, +) -> Result<(), Diagnostics> { + let var_gen = &mut VarGen::default(); + // Generate fresh type variables for each function in the group. + let tvs = group.iter().map(|_| var_gen.fresh()).collect::>(); + for (name, tv) in group.iter().zip(tvs.iter()) { + env.insert(name.clone(), Scheme(vec![], tv.clone())); + } + + // Infer the types of the functions in the group. + let mut ss = vec![]; + let mut inf_ts = vec![]; + let mut exp_ts = vec![]; + for name in group { + let def = &book.defs[name]; + let (s, t) = infer(env, book, types, &def.rule().body, var_gen).map_err(|e| { + diags.add_function_error(e, name.clone(), def.source.clone()); + std::mem::take(diags) + })?; + let t = t.subst(&s); + ss.push(s); + inf_ts.push(t); + exp_ts.push(&def.typ); + } + + // Remove the type variables of the group from the environment. + // This avoids cloning of already generalized types. + for name in group.iter() { + env.0.remove(name); + } + + // Unify the inferred body with the corresponding type variable. + let mut s = ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(s)); + let mut ts = vec![]; + for ((bod_t, tv), nam) in inf_ts.into_iter().zip(tvs.iter()).zip(group.iter()) { + let (t, s2) = unify_term(&tv.subst(&s), &bod_t, &book.defs[nam].rule().body)?; + ts.push(t); + s = s.compose(s2); + } + let ts = ts.into_iter().map(|t| t.subst(&s)).collect::>(); + + // Specialize against the expected type, then generalize and store. + for ((name, exp_t), inf_t) in group.iter().zip(exp_ts.iter()).zip(ts.iter()) { + let t = specialize(inf_t, exp_t).map_err(|e| { + diags.add_function_error(e, name.clone(), book.defs[name].source.clone()); + std::mem::take(diags) + })?; + types.insert(name.clone(), t.generalize(&TypeEnv::default())); + } + + diags.fatal(()) +} + +/// Infer the type of a term in the given environment. +/// +/// The type environment must contain bindings for all the free variables of the term. +/// +/// The returned substitution records the type constraints imposed on type variables by the term. +/// The returned type is the type of the term. +fn infer( + env: &mut TypeEnv, + book: &Book, + types: &ProgramTypes, + term: &Term, + var_gen: &mut VarGen, +) -> Result<(Subst, Type), String> { + let res = maybe_grow(|| match term { + Term::Var { nam } | Term::Ref { nam } => { + if let Some(scheme) = env.0.get(nam) { + Ok::<_, String>((Subst::default(), scheme.instantiate(var_gen))) + } else if let Some(scheme) = types.get(nam) { + Ok((Subst::default(), scheme.instantiate(var_gen))) + } else { + unreachable!("unbound name '{}'", nam) + } + } + Term::Lam { tag: Tag::Static, pat, bod } => match pat.as_ref() { + Pattern::Var(nam) => { + let tv = var_gen.fresh(); + let old_bnd = env.add_binds([(nam, Scheme(vec![], tv.clone()))]); + let (s, bod_t) = infer(env, book, types, bod, var_gen)?; + env.pop_binds(old_bnd); + let var_t = tv.subst(&s); + Ok((s, Type::Arr(Box::new(var_t), Box::new(bod_t)))) + } + _ => unreachable!("{}", term), + }, + Term::App { tag: Tag::Static, fun, arg } => { + let (s1, fun_t) = infer(env, book, types, fun, var_gen)?; + let (s2, arg_t) = infer(&mut env.subst(&s1), book, types, arg, var_gen)?; + let app_t = var_gen.fresh(); + let (_, s3) = unify_term(&fun_t.subst(&s2), &Type::Arr(Box::new(arg_t), Box::new(app_t.clone())), fun)?; + let t = app_t.subst(&s3); + Ok((s3.compose(s2).compose(s1), t)) + } + Term::Let { pat, val, nxt } => match pat.as_ref() { + Pattern::Var(nam) => { + let (s1, val_t) = infer(env, book, types, val, var_gen)?; + let old_bnd = env.add_binds([(nam, val_t.generalize(&env.subst(&s1)))]); + let (s2, nxt_t) = infer(&mut env.subst(&s1), book, types, nxt, var_gen)?; + env.pop_binds(old_bnd); + Ok((s2.compose(s1), nxt_t)) + } + Pattern::Fan(FanKind::Tup, Tag::Static, _) => { + // Tuple elimination behaves like pattern matching. + // Variables from tuple patterns don't get generalized. + debug_assert!(!(pat.has_unscoped() || pat.has_nested())); + let (s1, val_t) = infer(env, book, types, val, var_gen)?; + + let tvs = pat.binds().map(|_| var_gen.fresh()).collect::>(); + let old_bnd = env.add_binds(pat.binds().zip(tvs.iter().map(|tv| Scheme(vec![], tv.clone())))); + let (s2, nxt_t) = infer(&mut env.subst(&s1), book, types, nxt, var_gen)?; + env.pop_binds(old_bnd); + let tvs = tvs.into_iter().map(|tv| tv.subst(&s2)).collect::>(); + let (_, s3) = unify_term(&val_t, &Type::Tup(tvs), val)?; + Ok((s3.compose(s2).compose(s1), nxt_t)) + } + Pattern::Fan(FanKind::Dup, Tag::Auto, _) => { + // We pretend that sups don't exist and dups don't collide. + // All variables must have the same type as the body of the dup. + debug_assert!(!(pat.has_unscoped() || pat.has_nested())); + let (s1, mut val_t) = infer(env, book, types, val, var_gen)?; + let tvs = pat.binds().map(|_| var_gen.fresh()).collect::>(); + let old_bnd = env.add_binds(pat.binds().zip(tvs.iter().map(|tv| Scheme(vec![], tv.clone())))); + let (mut s2, nxt_t) = infer(&mut env.subst(&s1), book, types, nxt, var_gen)?; + env.pop_binds(old_bnd); + for tv in tvs { + let (val_t_, s) = unify_term(&val_t, &tv.subst(&s2), val)?; + val_t = val_t_; + s2 = s2.compose(s); + } + Ok((s2.compose(s1), nxt_t)) + } + _ => unreachable!(), + }, + + Term::Mat { bnd: _, arg, with_bnd: _, with_arg: _, arms } => { + // Infer type of the scrutinee + let (s1, t1) = infer(env, book, types, arg, var_gen)?; + + // Instantiate the expected type of the scrutinee + let adt_name = book.ctrs.get(arms[0].0.as_ref().unwrap()).unwrap(); + let adt = &book.adts[adt_name]; + let (adt_s, adt_t) = instantiate_adt(adt, var_gen)?; + + // For each case, infer the types and unify them all. + // Unify the inferred type of the destructured fields with the + // expected from what we inferred from the scrutinee. + let (s2, nxt_t) = infer_match_cases(env.subst(&s1), book, types, adt, arms, &adt_s, var_gen)?; + + // Unify the inferred type with the expected type + let (_, s3) = unify_term(&t1, &adt_t.subst(&s2), arg)?; + Ok((s3.compose(s2).compose(s1), nxt_t)) + } + + Term::Num { val } => { + let t = match val { + Num::U24(_) => Type::U24, + Num::I24(_) => Type::I24, + Num::F24(_) => Type::F24, + }; + Ok((Subst::default(), t)) + } + Term::Oper { opr, fst, snd } => { + let (s1, t1) = infer(env, book, types, fst, var_gen)?; + let (s2, t2) = infer(&mut env.subst(&s1), book, types, snd, var_gen)?; + let (t2, s3) = unify_term(&t2.subst(&s1), &t1.subst(&s2), term)?; + let s_args = s3.compose(s2).compose(s1); + let t_args = t2.subst(&s_args); + // Check numeric type matches the operation + let tv = var_gen.fresh(); + let (t_opr, s_opr) = match opr { + // Any numeric type + Op::ADD | Op::SUB | Op::MUL | Op::DIV => { + unify_term(&t_args, &Type::Number(Box::new(tv.clone())), term)? + } + Op::EQ | Op::NEQ | Op::LT | Op::GT | Op::GE | Op::LE => { + let (_, s) = unify_term(&t_args, &Type::Number(Box::new(tv.clone())), term)?; + (Type::U24, s) + } + // Integers + Op::REM | Op::AND | Op::OR | Op::XOR | Op::SHL | Op::SHR => { + unify_term(&t_args, &Type::Integer(Box::new(tv.clone())), term)? + } + // Floating + Op::POW => unify_term(&t_args, &Type::F24, term)?, + }; + let t = t_opr.subst(&s_opr); + Ok((s_opr.compose(s_args), t)) + } + Term::Swt { bnd: _, arg, with_bnd: _, with_arg: _, pred, arms } => { + let (s1, t1) = infer(env, book, types, arg, var_gen)?; + let (_, s2) = unify_term(&t1, &Type::U24, arg)?; + let s_arg = s2.compose(s1); + let mut env = env.subst(&s_arg); + + let mut ss_nums = vec![]; + let mut ts_nums = vec![]; + for arm in arms.iter().rev().skip(1) { + let (s, t) = infer(&mut env, book, types, arm, var_gen)?; + env = env.subst(&s); + ss_nums.push(s); + ts_nums.push(t); + } + let old_bnd = env.add_binds([(pred, Scheme(vec![], Type::U24))]); + let (s_succ, t_succ) = infer(&mut env, book, types, &arms[1], var_gen)?; + env.pop_binds(old_bnd); + + let s_arms = ss_nums.into_iter().fold(s_succ, |acc, s| acc.compose(s)); + let mut t_swt = t_succ; + let mut s_swt = Subst::default(); + for t_num in ts_nums { + let (t, s) = unify_term(&t_swt, &t_num, term)?; + t_swt = t; + s_swt = s.compose(s_swt); + } + + let s = s_swt.compose(s_arms).compose(s_arg); + let t = t_swt.subst(&s); + Ok((s, t)) + } + + Term::Fan { fan: FanKind::Tup, tag: Tag::Static, els } => { + let res = els.iter().map(|el| infer(env, book, types, el, var_gen)).collect::, _>>()?; + let (ss, ts): (Vec, Vec) = res.into_iter().unzip(); + let t = Type::Tup(ts); + let s = ss.into_iter().fold(Subst::default(), |acc, s| acc.compose(s)); + Ok((s, t)) + } + Term::Era => Ok((Subst::default(), Type::None)), + Term::Fan { .. } | Term::Lam { tag: _, .. } | Term::App { tag: _, .. } | Term::Link { .. } => { + unreachable!("'{term}' while type checking. Should never occur in checked functions") + } + Term::Use { .. } + | Term::With { .. } + | Term::Ask { .. } + | Term::Nat { .. } + | Term::Str { .. } + | Term::List { .. } + | Term::Fold { .. } + | Term::Bend { .. } + | Term::Open { .. } + | Term::Def { .. } + | Term::Err => unreachable!("'{term}' while type checking. Should have been removed in earlier pass"), + })?; + Ok(res) +} + +/// Instantiates the type constructor of an ADT, also returning the +/// ADT var to instantiated var substitution, to be used when +/// instantiating the types of the fields of the eliminated constructors. +fn instantiate_adt(adt: &Adt, var_gen: &mut VarGen) -> Result<(Subst, Type), String> { + let tvs = adt.vars.iter().map(|_| var_gen.fresh()); + let s = Subst(adt.vars.iter().zip(tvs).map(|(x, t)| (x.clone(), t)).collect()); + let t = Type::Ctr(adt.name.clone(), adt.vars.iter().cloned().map(Type::Var).collect()); + let t = t.subst(&s); + Ok((s, t)) +} + +fn infer_match_cases( + mut env: TypeEnv, + book: &Book, + types: &ProgramTypes, + adt: &Adt, + arms: &[MatchRule], + adt_s: &Subst, + var_gen: &mut VarGen, +) -> Result<(Subst, Type), String> { + maybe_grow(|| { + if let Some(((ctr_nam, vars, bod), rest)) = arms.split_first() { + let ctr = &adt.ctrs[ctr_nam.as_ref().unwrap()]; + // One fresh var per field, we later unify with the expected type. + let tvs = vars.iter().map(|_| var_gen.fresh()).collect::>(); + + // Infer the body and unify the inferred field types with the expected. + let old_bnd = env.add_binds(vars.iter().zip(tvs.iter().map(|tv| Scheme(vec![], tv.clone())))); + let (s1, t1) = infer(&mut env, book, types, bod, var_gen)?; + env.pop_binds(old_bnd); + let inf_ts = tvs.into_iter().map(|tv| tv.subst(&s1)).collect::>(); + let exp_ts = ctr.fields.iter().map(|f| f.typ.subst(adt_s)).collect::>(); + let s2 = unify_fields(inf_ts.iter().zip(exp_ts.iter()), bod)?; + + // Recurse and unify with the other arms. + let s = s2.compose(s1); + let (s_rest, t_rest) = infer_match_cases(env.subst(&s), book, types, adt, rest, adt_s, var_gen)?; + let (t_final, s_final) = unify_term(&t1, &t_rest, bod)?; + + Ok((s_final.compose(s_rest).compose(s), t_final)) + } else { + Ok((Subst::default(), var_gen.fresh())) + } + }) +} + +fn unify_fields<'a>(ts: impl Iterator, ctx: &Term) -> Result { + let ss = ts.map(|(inf, exp)| unify_term(inf, exp, ctx)).collect::, _>>()?; + let mut s = Subst::default(); + for (_, s2) in ss.into_iter().rev() { + s = s.compose(s2); + } + Ok(s) +} + +fn unify_term(t1: &Type, t2: &Type, ctx: &Term) -> Result<(Type, Subst), String> { + match unify(t1, t2) { + Ok((t, s)) => Ok((t, s)), + Err(msg) => Err(format!("In {ctx}:\n Can't unify '{t1}' and '{t2}'.{msg}")), + } +} + +fn unify(t1: &Type, t2: &Type) -> Result<(Type, Subst), String> { + maybe_grow(|| match (t1, t2) { + (t, Type::Hole) | (Type::Hole, t) => Ok((t.clone(), Subst::default())), + (t, Type::Var(x)) | (Type::Var(x), t) => { + // Try to bind variable `x` to `t` + if let Type::Var(y) = t { + if y == x { + // Don't bind a variable to itself + return Ok((t.clone(), Subst::default())); + } + } + // Occurs check + if t.free_type_vars().contains(x) { + return Err(format!(" Variable '{x}' occurs in '{t}'")); + } + Ok((t.clone(), Subst(BTreeMap::from([(x.clone(), t.clone())])))) + } + (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { + let (t1, s1) = unify(l1, l2)?; + let (t2, s2) = unify(&r1.subst(&s1), &r2.subst(&s1))?; + Ok((Type::Arr(Box::new(t1), Box::new(t2)), s2.compose(s1))) + } + (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { + let mut s = Subst::default(); + let mut ts = vec![]; + for (t1, t2) in ts1.iter().zip(ts2.iter()) { + let (t, s2) = unify(t1, t2)?; + ts.push(t); + s = s.compose(s2); + } + Ok((Type::Ctr(name1.clone(), ts), s)) + } + (Type::Tup(els1), Type::Tup(els2)) if els1.len() == els2.len() => { + let mut s = Subst::default(); + let mut ts = vec![]; + for (t1, t2) in els1.iter().zip(els2.iter()) { + let (t, s2) = unify(t1, t2)?; + ts.push(t); + s = s.compose(s2); + } + Ok((Type::Tup(ts), s)) + } + t @ ((Type::U24, Type::U24) + | (Type::F24, Type::F24) + | (Type::I24, Type::I24) + | (Type::None, Type::None)) => Ok((t.0.clone(), Subst::default())), + (Type::Number(t1), Type::Number(t2)) => { + let (t, s) = unify(t1, t2)?; + Ok((Type::Number(Box::new(t)), s)) + } + (Type::Number(tn), Type::Integer(ti)) | (Type::Integer(ti), Type::Number(tn)) => { + let (t, s) = unify(ti, tn)?; + Ok((Type::Integer(Box::new(t)), s)) + } + (Type::Integer(t1), Type::Integer(t2)) => { + let (t, s) = unify(t1, t2)?; + Ok((Type::Integer(Box::new(t)), s)) + } + (Type::Number(t1) | Type::Integer(t1), t2 @ (Type::U24 | Type::I24 | Type::F24)) + | (t2 @ (Type::U24 | Type::I24 | Type::F24), Type::Number(t1) | Type::Integer(t1)) => { + let (t, s) = unify(t1, t2)?; + Ok((t, s)) + } + + (Type::Any, t) | (t, Type::Any) => { + let mut s = Subst::default(); + // Recurse to assign variables to `Any` as well + for child in t.children() { + let (_, s2) = unify(&Type::Any, child)?; + s = s2.compose(s); + } + Ok((Type::Any, s)) + } + + _ => Err(String::new()), + }) +} + +/// Specializes the inferred type against the type annotation. +/// This way, the annotation can be less general than the inferred type. +/// +/// It also forces inferred 'Any' to the annotated, inferred types to +/// annotated 'Any' and fills 'Hole' with the inferred type. +/// +/// Errors if the first type is not a superset of the second type. +fn specialize(inf: &Type, ann: &Type) -> Result { + fn merge_specialization(inf: &Type, exp: &Type, s: &mut Subst) -> Result { + maybe_grow(|| match (inf, exp) { + // These rules have to come before + (t, Type::Hole) => Ok(t.clone()), + (Type::Hole, _) => unreachable!("Hole should never appear in the inferred type"), + + (_inf, Type::Any) => Ok(Type::Any), + (Type::Any, exp) => Ok(exp.clone()), + + (Type::Var(x), new) => { + if let Some(old) = s.0.get(x) { + if old == new { + Ok(new.clone()) + } else { + Err(format!(" Inferred type variable '{x}' must be both '{old}' and '{new}'")) + } + } else { + s.0.insert(x.clone(), new.clone()); + Ok(new.clone()) + } + } + + (Type::Arr(l1, r1), Type::Arr(l2, r2)) => { + let l = merge_specialization(l1, l2, s)?; + let r = merge_specialization(r1, r2, s)?; + Ok(Type::Arr(Box::new(l), Box::new(r))) + } + (Type::Ctr(name1, ts1), Type::Ctr(name2, ts2)) if name1 == name2 && ts1.len() == ts2.len() => { + let mut ts = vec![]; + for (t1, t2) in ts1.iter().zip(ts2.iter()) { + let t = merge_specialization(t1, t2, s)?; + ts.push(t); + } + Ok(Type::Ctr(name1.clone(), ts)) + } + (Type::Tup(ts1), Type::Tup(ts2)) if ts1.len() == ts2.len() => { + let mut ts = vec![]; + for (t1, t2) in ts1.iter().zip(ts2.iter()) { + let t = merge_specialization(t1, t2, s)?; + ts.push(t); + } + Ok(Type::Tup(ts)) + } + (Type::Number(t1), Type::Number(t2)) => Ok(Type::Number(Box::new(merge_specialization(t1, t2, s)?))), + (Type::Integer(t1), Type::Integer(t2)) => Ok(Type::Integer(Box::new(merge_specialization(t1, t2, s)?))), + (Type::U24, Type::U24) | (Type::F24, Type::F24) | (Type::I24, Type::I24) | (Type::None, Type::None) => { + Ok(inf.clone()) + } + _ => Err(String::new()), + }) + } + + // Refresh the variable names to avoid conflicts when unifying + // Names of type vars in the annotation have nothing to do with names in the inferred type. + let var_gen = &mut VarGen::default(); + let inf2 = inf.generalize(&TypeEnv::default()).instantiate(var_gen); + let ann2 = ann.generalize(&TypeEnv::default()).instantiate(var_gen); + + let (t, s) = unify(&inf2, &ann2) + .map_err(|e| format!("Type Error: Expected function type '{ann}' but found '{inf}'.{e}"))?; + let t = t.subst(&s); + + // Merge the inferred specialization with the expected type. + // This is done to cast to/from `Any` and `_` types. + let mut merge_s = Subst::default(); + let t2 = merge_specialization(&t, ann, &mut merge_s).map_err(|e| { + format!("Type Error: Annotated type '{ann}' is not a subtype of inferred type '{inf2}'.{e}") + })?; + + Ok(t2.subst(&merge_s)) +} + +impl std::fmt::Display for Subst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Subst {{")?; + for (x, y) in &self.0 { + writeln!(f, " {x} => {y},")?; + } + write!(f, "}}") + } +} diff --git a/src/fun/check/unbound_refs.rs b/src/fun/check/unbound_refs.rs index 1aa4cd5e5..f78fee9de 100644 --- a/src/fun/check/unbound_refs.rs +++ b/src/fun/check/unbound_refs.rs @@ -7,7 +7,6 @@ use std::collections::HashSet; impl Ctx<'_> { pub fn check_unbound_refs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); for def in self.book.defs.values() { let mut unbounds = HashSet::new(); for rule in def.rules.iter() { diff --git a/src/fun/check/unbound_vars.rs b/src/fun/check/unbound_vars.rs index 61b6a36e1..41b744890 100644 --- a/src/fun/check/unbound_vars.rs +++ b/src/fun/check/unbound_vars.rs @@ -3,7 +3,7 @@ use crate::{ fun::{transform::desugar_bend, Ctx, Name, Pattern, Term}, maybe_grow, }; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::HashMap; #[derive(Debug, Clone)] pub enum UnboundVarErr { @@ -14,16 +14,11 @@ pub enum UnboundVarErr { impl Ctx<'_> { /// Checks that there are no unbound variables in all definitions. pub fn check_unbound_vars(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for (def_name, def) in self.book.defs.iter_mut() { let mut errs = Vec::new(); for rule in &mut def.rules { - let mut scope = HashMap::new(); - for pat in &rule.pats { - pat.binds().for_each(|nam| push_scope(nam.as_ref(), &mut scope)); - } - + // Note: Using a Vec instead of a Map is a deliberate optimization. + let mut scope = rule.pats.iter().flat_map(|pat| pat.binds()).map(|x| x.as_ref()).collect::>(); rule.body.check_unbound_vars(&mut scope, &mut errs); } @@ -42,7 +37,7 @@ impl Term { pub fn check_unbound_vars<'a>( &'a mut self, - scope: &mut HashMap<&'a Name, u64>, + scope: &mut Vec>, errs: &mut Vec, ) { let mut globals = HashMap::new(); @@ -59,13 +54,13 @@ impl Term { /// Globals has how many times a global var name was declared and used. pub fn check_uses<'a>( term: &'a mut Term, - scope: &mut HashMap<&'a Name, u64>, + scope: &mut Vec>, globals: &mut HashMap, errs: &mut Vec, ) { maybe_grow(move || match term { Term::Var { nam } => { - if !scope.contains_key(nam) { + if !scope_contains(nam, scope) { errs.push(UnboundVarErr::Local(nam.clone())); *term = Term::Err; } @@ -80,11 +75,11 @@ pub fn check_uses<'a>( } for (child, binds) in term.children_mut_with_binds() { for bind in binds.clone() { - push_scope(bind.as_ref(), scope); + scope.push(bind.as_ref()); } check_uses(child, scope, globals, errs); - for bind in binds { - pop_scope(bind.as_ref(), scope); + for _ in binds { + scope.pop(); } } } @@ -104,20 +99,8 @@ pub fn check_global_binds(pat: &Pattern, globals: &mut HashMap(nam: Option<&'a Name>, scope: &mut HashMap<&'a Name, u64>) { - if let Some(nam) = nam { - *scope.entry(nam).or_default() += 1; - } -} - -fn pop_scope<'a>(nam: Option<&'a Name>, scope: &mut HashMap<&'a Name, u64>) { - if let Some(nam) = nam { - let Entry::Occupied(n_declarations) = scope.entry(nam).and_modify(|e| *e -= 1) else { unreachable!() }; - - if *n_declarations.get() == 0 { - n_declarations.remove(); - } - } +fn scope_contains(nam: &Name, scope: &[Option<&Name>]) -> bool { + scope.iter().rev().any(|scope_nam| scope_nam == nam) } impl std::fmt::Display for UnboundVarErr { diff --git a/src/fun/display.rs b/src/fun/display.rs index ad54a761e..36ee63d88 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -1,4 +1,4 @@ -use super::{Book, Definition, FanKind, Name, Num, Op, Pattern, Rule, Tag, Term}; +use super::{Book, Definition, FanKind, Name, Num, Op, Pattern, Rule, Tag, Term, Type}; use crate::maybe_grow; use std::{fmt, ops::Deref, sync::atomic::AtomicU64}; @@ -222,6 +222,7 @@ impl Rule { impl fmt::Display for Definition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { namegen_reset(); + writeln!(f, "{}{}: {}", if !self.check { "unchecked " } else { "" }, self.name, self.typ)?; write!(f, "{}", DisplayJoin(|| self.rules.iter().map(|x| x.display(&self.name)), "\n")) } } @@ -273,8 +274,6 @@ impl fmt::Display for Op { Op::POW => write!(f, "**"), Op::SHR => write!(f, ">>"), Op::SHL => write!(f, "<<"), - Op::LOG => todo!(), - Op::ATN => todo!(), Op::LE => write!(f, "<="), Op::GE => write!(f, ">="), } @@ -292,6 +291,44 @@ impl Tag { } } +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + maybe_grow(|| match self { + Type::Hole => write!(f, "_"), + Type::Var(nam) => write!(f, "{nam}"), + Type::Arr(lft, rgt) => write!(f, "({} -> {})", lft, rgt.display_arrow()), + Type::Ctr(nam, args) => { + if args.is_empty() { + write!(f, "{nam}") + } else { + write!(f, "({nam} {})", DisplayJoin(|| args.iter(), " ")) + } + } + Type::Number(t) => write!(f, "(Number {t})"), + Type::Integer(t) => write!(f, "(Integer {t})"), + Type::U24 => write!(f, "u24"), + Type::I24 => write!(f, "i24"), + Type::F24 => write!(f, "f24"), + Type::Any => write!(f, "Any"), + Type::None => write!(f, "None"), + Type::Tup(els) => write!(f, "({})", DisplayJoin(|| els.iter(), ", ")), + }) + } +} + +impl Type { + pub fn display_arrow(&self) -> impl fmt::Display + '_ { + maybe_grow(|| { + DisplayFn(move |f| match self { + Type::Arr(lft, rgt) => { + write!(f, "{} -> {}", lft, rgt.display_arrow()) + } + _ => write!(f, "{}", self), + }) + }) + } +} + fn var_as_str(nam: &Option) -> &str { nam.as_ref().map_or("*", Name::deref) } diff --git a/src/fun/load_book.rs b/src/fun/load_book.rs index a88c45097..1f084c591 100644 --- a/src/fun/load_book.rs +++ b/src/fun/load_book.rs @@ -1,5 +1,5 @@ use super::{ - parser::{ParseBook, TermParser}, + parser::{FunParser, ParseBook}, Book, Name, Source, SourceKind, }; use crate::{ @@ -41,7 +41,7 @@ pub fn load_to_book( pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result { book.source = Name::new(origin.to_string_lossy()); - TermParser::new(code).parse_book(book, false).map_err(|err| { + FunParser::new(book.source.clone(), code, false).parse_book(book).map_err(|err| { let mut diagnostics = Diagnostics::default(); let span = TextSpan::from_byte_span(code, err.span.0..err.span.1); let source = @@ -50,7 +50,3 @@ pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result

Result { - do_parse_book(code, origin, ParseBook::builtins())?.to_fun() -} diff --git a/src/fun/mod.rs b/src/fun/mod.rs index b88b5c246..a4ad06e61 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -6,7 +6,11 @@ use crate::{ use indexmap::{IndexMap, IndexSet}; use interner::global::{GlobalPool, GlobalString}; use itertools::Itertools; -use std::{borrow::Cow, hash::Hash, ops::Deref}; +use std::{ + borrow::Cow, + hash::Hash, + ops::{Deref, Range}, +}; pub mod builtins; pub mod check; @@ -64,6 +68,8 @@ pub type Constructors = IndexMap; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Definition { pub name: Name, + pub typ: Type, + pub check: bool, pub rules: Vec, pub source: Source, } @@ -93,10 +99,27 @@ pub enum SourceKind { #[derive(Debug, Clone)] pub struct HvmDefinition { pub name: Name, + pub typ: Type, pub body: hvm::ast::Net, pub source: Source, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Type { + Any, + Hole, + Var(Name), + Ctr(Name, Vec), + Arr(Box, Box), + Tup(Vec), + U24, + F24, + I24, + None, + Number(Box), + Integer(Box), +} + /// A pattern matching rule of a definition. #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] pub struct Rule { @@ -237,10 +260,6 @@ pub enum Op { XOR, SHL, SHR, - /// atan(a, b) - ATN, - /// log_a(b) - LOG, // a^b POW, /// Less than or equal @@ -280,15 +299,26 @@ pub enum Tag { /// A user defined datatype #[derive(Debug, Clone)] pub struct Adt { - pub ctrs: IndexMap>, + pub name: Name, + pub vars: Vec, + pub ctrs: IndexMap, pub source: Source, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] +pub struct AdtCtr { + pub name: Name, + pub typ: Type, + pub fields: Vec, +} + +#[derive(Debug, Clone)] pub struct CtrField { pub nam: Name, pub rec: bool, + pub typ: Type, } + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Name(GlobalString); @@ -804,6 +834,26 @@ impl Term { } } + /// Substitutes the occurrences of a type constructor in the term with the given name. + pub fn subst_type_ctrs(&mut self, from: &Name, to: &Name) { + maybe_grow(|| { + match self { + Term::Def { def, nxt: _ } => { + def.typ.subst_ctr(from, to); + } + Term::With { typ, bod: _ } => { + if typ == from { + *typ = to.clone(); + } + } + _ => (), + } + for child in self.children_mut() { + child.subst_type_ctrs(from, to); + } + }); + } + /// Substitute the occurrence of an unscoped variable with the given term. pub fn subst_unscoped(&mut self, from: &Name, to: &Term) { maybe_grow(|| { @@ -1018,6 +1068,15 @@ impl Pattern { Pattern::Ctr(_, x) | Pattern::Fan(_, _, x) | Pattern::Lst(x) => x.iter().any(|x| x.has_unscoped()), } } + + pub fn has_nested(&self) -> bool { + for child in self.children() { + if matches!(child, Pattern::Ctr(_, _) | Pattern::Fan(_, _, _) | Pattern::Lst(_)) { + return true; + } + } + false + } } impl Rule { @@ -1027,14 +1086,10 @@ impl Rule { } impl Definition { - pub fn new(name: Name, rules: Vec, source: Source) -> Self { - Self { name, rules, source } - } - - pub fn new_gen(name: Name, rules: Vec, source: Source) -> Self { + pub fn new_gen(name: Name, rules: Vec, source: Source, check: bool) -> Self { let kind = if source.is_builtin() { SourceKind::Builtin } else { SourceKind::Generated }; let source = Source { kind, ..source }; - Self { name, rules, source } + Self { name, typ: Type::Hole, check, rules, source } } pub fn is_builtin(&self) -> bool { @@ -1064,6 +1119,56 @@ impl Definition { } } +impl Type { + /// Substitutes the occurrences of a type constructor with the given name. + /// Substitutes both `Var` and `Ctr` types since `Var` could be referring to + /// an unresolved type constructor. + pub fn subst_ctr(&mut self, from: &Name, to: &Name) { + maybe_grow(|| { + match self { + Type::Var(nam) => { + if nam == from { + *nam = to.clone(); + } + } + Type::Ctr(nam, _) => { + if nam == from { + *nam = to.clone(); + } + } + _ => (), + }; + for child in self.children_mut() { + child.subst_ctr(from, to); + } + }) + } + + pub fn children(&self) -> impl Iterator { + multi_iterator!(ChildrenIter { Zero, One, Two, Vec }); + match self { + Type::Any | Type::None | Type::Hole | Type::I24 | Type::F24 | Type::U24 | Type::Var(_) => { + ChildrenIter::Zero([]) + } + Type::Number(t) | Type::Integer(t) => ChildrenIter::One([t.as_ref()]), + Type::Arr(lft, rgt) => ChildrenIter::Two([lft.as_ref(), rgt.as_ref()]), + Type::Tup(els) | Type::Ctr(_, els) => ChildrenIter::Vec(els), + } + } + + pub fn children_mut(&mut self) -> impl Iterator { + multi_iterator!(ChildrenIter { Zero, One, Two, Vec }); + match self { + Type::Any | Type::None | Type::Hole | Type::I24 | Type::F24 | Type::U24 | Type::Var(_) => { + ChildrenIter::Zero([]) + } + Type::Number(t) | Type::Integer(t) => ChildrenIter::One([t.as_mut()]), + Type::Arr(lft, rgt) => ChildrenIter::Two([lft.as_mut(), rgt.as_mut()]), + Type::Tup(els) | Type::Ctr(_, els) => ChildrenIter::Vec(els), + } + } +} + impl Name { pub fn new<'a, V: Into>>(value: V) -> Name { Name(STRINGS.get(value)) @@ -1134,6 +1239,13 @@ impl Source { pub fn is_local(&self) -> bool { matches!(self.kind, SourceKind::User) } + + pub fn from_file_span(file: &Name, txt: &str, span: Range, builtin: bool) -> Self { + let span = Some(TextSpan::from_byte_span(txt, span)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = Some(file.to_string()); + Source { file, span, kind } + } } impl Default for Source { diff --git a/src/fun/net_to_term.rs b/src/fun/net_to_term.rs index 764b4d333..5ac6aebd9 100644 --- a/src/fun/net_to_term.rs +++ b/src/fun/net_to_term.rs @@ -302,14 +302,14 @@ impl Reader<'_> { hvm::hvm::OP_GT => Op::GT, hvm::hvm::OP_AND => { if typ == hvm::hvm::TY_F24 { - Op::ATN + todo!("Implement readback of atan2") } else { Op::AND } } hvm::hvm::OP_OR => { if typ == hvm::hvm::TY_F24 { - Op::LOG + todo!("Implement readback of log") } else { Op::OR } diff --git a/src/fun/parser.rs b/src/fun/parser.rs index fadea7430..7e1c63aaa 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1,22 +1,18 @@ -use std::ops::Range; - use crate::{ - diagnostics::{TextLocation, TextSpan}, fun::{ - display::DisplayFn, Adt, Adts, Constructors, CtrField, FanKind, HvmDefinition, HvmDefinitions, MatchRule, - Name, Num, Op, Pattern, Rule, Source, Tag, Term, STRINGS, + display::DisplayFn, Adt, AdtCtr, Adts, Constructors, CtrField, FanKind, HvmDefinition, HvmDefinitions, + MatchRule, Name, Num, Op, Pattern, Rule, Source, SourceKind, Tag, Term, Type, STRINGS, }, - imp::{parser::PyParser, Enum, RepeatedNames, Variant}, + imp::parser::ImpParser, imports::{Import, ImportCtx, ImportType}, maybe_grow, }; use highlight_error::highlight_error; use indexmap::IndexMap; use itertools::Itertools; +use std::ops::Range; use TSPL::{ParseError, Parser}; -use super::SourceKind; - type FunDefinition = super::Definition; type ImpDefinition = crate::imp::Definition; @@ -41,7 +37,7 @@ pub struct ParseBook { /// Imported packages to be loaded in the program pub import_ctx: ImportCtx, - /// Source of the book + /// File path that the book was loaded from. pub source: Name, } @@ -60,246 +56,331 @@ impl ParseBook { } } -// Bend grammar description: -// ::= ( | )* -// ::= "type" "=" ( | "(" ()* ")" )+ -// ::= ("(" * ")" | *) "=" -// ::= "(" * ")" | | | "(" ("," )+ ")" -// ::= -// | | | | | | | | | | -// | | | | | | | | | -// ::= ? ("λ"|"@") -// ::= ? ("λ"|"@") "$" -// ::= "(" ")" -// ::= "(" ("," )+ ")" -// ::= ? "(" ()+ ")" -// ::= "(" ")" -// ::= "use" "=" ";"? -// ::= "let" "=" ";"? -// ::= "with" "{" "}" -// ::= "ask" "=" ";" | -// ::= "let" "(" ("," )+ ")" "=" ";"? -// ::= "let" ? "{" (","? )+ "}" "=" ";"? -// ::= "[" ( ","?)* "]" -// ::= "\"" (escape sequence | [^"])* "\"" -// ::= "'" (escape sequence | [^']) "'" -// ::= "match" ? "{" + "}" -// ::= "fold" ? "{" + "}" -// ::= ( "=" ) | -// ::= "with" ( ("=" )? ","?)+ -// ::= "|"? ":" ";"? -// ::= "switch" ? "{" + "}" -// ::= "|"? (|"_") ":" ";"? -// ::= "bend" ( ","?)+ "{" "when" ":" "else" ":" "}" -// ::= -// ::= "$" -// ::= | "*" -// ::= "*" -// ::= "#" -// ::= [_\-./a-zA-Z0-9]+ -// ::= ([0-9]+ | "0x"[0-9a-fA-F]+ | "0b"[01]+) -// ::= ( "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<<" | ">>" | "<" | ">" | "&" | "|" | "^" | "**" ) - pub type ParseResult = std::result::Result; -pub struct TermParser<'i> { +pub struct FunParser<'i> { + file: Name, input: &'i str, index: usize, + builtin: bool, } -impl<'a> TermParser<'a> { - pub fn new(input: &'a str) -> Self { - Self { input, index: 0 } +impl<'a> FunParser<'a> { + pub fn new(file: Name, input: &'a str, builtin: bool) -> Self { + Self { file, input, index: 0, builtin } } /* AST parsing functions */ - pub fn parse_book(&mut self, default_book: ParseBook, builtin: bool) -> ParseResult { + pub fn parse_book(&mut self, default_book: ParseBook) -> ParseResult { let mut book = default_book; let mut indent = self.advance_newlines()?; - let mut last_rule = None; while !self.is_eof() { - let ini_idx = *self.index(); - // Record type definition - if self.try_parse_keyword("object") { - let mut prs = PyParser { input: self.input, index: *self.index() }; - let (obj, nxt_indent) = prs.parse_object(indent)?; + if self.starts_with_keyword("object") { + let ini_idx = *self.index(); + let mut prs = ImpParser { + file: self.file.clone(), + input: self.input, + index: *self.index(), + builtin: self.builtin, + }; + let (adt, nxt_indent) = prs.parse_object(indent)?; self.index = prs.index; let end_idx = *self.index(); - self.add_object(obj, &mut book, ini_idx..end_idx, builtin)?; + self.add_type_def(adt, &mut book, ini_idx..end_idx)?; indent = nxt_indent; - last_rule = None; continue; } // Imp function definition - if self.try_parse_keyword("def") { - let mut prs = PyParser { input: self.input, index: *self.index() }; - let (def, nxt_indent) = prs.parse_def(indent)?; + if self.starts_with_keyword("def") { + let ini_idx = *self.index(); + let mut prs = + ImpParser { file: self.file.clone(), input: self.input, index: ini_idx, builtin: self.builtin }; + let (def, nxt_indent) = prs.parse_function_def(indent)?; self.index = prs.index; let end_idx = *self.index(); - self.add_imp_def(def, &mut book, ini_idx..end_idx, builtin)?; + self.add_imp_def(def, &mut book, ini_idx..end_idx)?; indent = nxt_indent; - last_rule = None; continue; } // Fun/Imp type definition - if self.try_parse_keyword("type") { - self.skip_trivia(); - let rewind_index = self.index; - - let _ = self.labelled(|p| p.parse_top_level_name(), "datatype name")?; + if self.starts_with_keyword("type") { + fn starts_with_imp_type(p: &mut FunParser) -> ParseResult<()> { + p.parse_keyword("type")?; + p.skip_trivia_inline()?; + p.parse_top_level_name()?; + p.skip_trivia_inline()?; + if p.starts_with(":") || p.starts_with("(") { + Ok(()) + } else { + Err(ParseError::new((0, 0), "")) + } + } - // Imp type definition - if self.starts_with(":") { - let mut prs = PyParser { input: self.input, index: rewind_index }; - let (r#enum, nxt_indent) = prs.parse_type(indent)?; + let ini_idx = *self.index(); + let is_imp = starts_with_imp_type(self).is_ok(); + self.index = ini_idx; + if is_imp { + // Imp type definition + let mut prs = ImpParser { + file: self.file.clone(), + input: self.input, + index: *self.index(), + builtin: self.builtin, + }; + let (adt, nxt_indent) = prs.parse_type_def(indent)?; self.index = prs.index; let end_idx = *self.index(); - self.add_imp_type(r#enum, &mut book, ini_idx..end_idx, builtin)?; + self.add_type_def(adt, &mut book, ini_idx..end_idx)?; indent = nxt_indent; - last_rule = None; continue; - // Fun type definition } else { - self.index = rewind_index; - let (nam, ctrs) = self.parse_datatype()?; + // Fun type definition + let adt = self.parse_type_def()?; let end_idx = *self.index(); - - let span = Some(TextSpan::from_byte_span(self.input(), ini_idx..end_idx)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - - let adt = Adt { ctrs, source }; - self.add_fun_type(&mut book, nam, adt, ini_idx..end_idx)?; + self.add_type_def(adt, &mut book, ini_idx..end_idx)?; indent = self.advance_newlines()?; - last_rule = None; continue; } } // HVM native function definition - if self.try_parse_keyword("hvm") { - let def = self.parse_hvm(builtin)?; + if self.starts_with_keyword("hvm") { + let ini_idx = self.index; + let mut prs = + ImpParser { file: self.file.clone(), input: self.input, index: self.index, builtin: self.builtin }; + let (def, nxt_indent) = prs.parse_hvm()?; + *self.index() = prs.index; let end_idx = *self.index(); self.add_hvm(def, &mut book, ini_idx..end_idx)?; - indent = self.advance_newlines()?; - last_rule = None; + indent = nxt_indent; continue; } // Import declaration - if self.try_parse_keyword("from") { - self.skip_trivia(); + if self.starts_with_keyword("from") { let import = self.parse_from_import()?; book.import_ctx.add_import(import); indent = self.advance_newlines()?; - last_rule = None; continue; } - if self.try_parse_keyword("import") { - self.skip_trivia(); + if self.starts_with_keyword("import") { let imports = self.parse_import()?; for imp in imports { book.import_ctx.add_import(imp); } indent = self.advance_newlines()?; - last_rule = None; continue; } // Fun function definition let ini_idx = *self.index(); - let (name, rule) = self.parse_rule()?; + let def = self.parse_fun_def()?; let end_idx = *self.index(); - if let Some(def) = book.imp_defs.get(&name) { - let msg = Self::redefinition_of_function_msg(def.source.is_builtin(), &name); - return self.with_ctx(Err(msg), ini_idx..end_idx); - } - - self.add_fun_def(&name, rule, builtin, &last_rule, &mut book, ini_idx..end_idx)?; + self.add_fun_def(def, &mut book, ini_idx..end_idx)?; indent = self.advance_newlines()?; - last_rule = Some(name); } Ok(book) } - fn parse_datatype(&mut self) -> ParseResult<(Name, IndexMap>)> { - // type name = ctr (| ctr)* + fn parse_type_def(&mut self) -> ParseResult { + // type (name var1 ... varN) = ctr (| ctr)* + let ini_idx = self.index; + self.parse_keyword("type")?; self.skip_trivia(); - let name = self.labelled(|p| p.parse_top_level_name(), "datatype name")?; - self.consume("=")?; - let mut ctrs = vec![self.parse_datatype_ctr(&name)?]; + + let name; + let vars; + if self.try_consume("(") { + // parens around name and vars + self.skip_trivia(); + name = self.parse_restricted_name("Datatype")?; + vars = self + .labelled(|p| p.list_like(|p| p.parse_var_name(), "", ")", "", false, 0), "Type variable or ')'")?; + self.consume("=")?; + } else { + // no parens + name = self.parse_restricted_name("Datatype")?; + vars = self + .labelled(|p| p.list_like(|p| p.parse_var_name(), "", "=", "", false, 0), "Type variable or '='")?; + } + + let mut ctrs = vec![self.parse_type_ctr(&name, &vars)?]; while self.try_consume("|") { - ctrs.push(self.parse_datatype_ctr(&name)?); + ctrs.push(self.parse_type_ctr(&name, &vars)?); } - let ctrs = ctrs.into_iter().collect(); - Ok((name, ctrs)) + let ctrs = ctrs.into_iter().map(|ctr| (ctr.name.clone(), ctr)).collect::>(); + + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let adt = Adt { name, vars, ctrs, source }; + Ok(adt) } - fn parse_datatype_ctr(&mut self, typ_name: &Name) -> ParseResult<(Name, Vec)> { - // (name ('~'? field)*) + fn parse_type_ctr(&mut self, type_name: &Name, type_vars: &[Name]) -> ParseResult { + // '(' name (( '~'? field) | ('~'? '('field (':' type)? ')') )* ')' // name + self.skip_trivia(); + let ini_idx = *self.index(); if self.try_consume("(") { + // name and optionally fields + self.skip_trivia(); let ctr_name = self.parse_top_level_name()?; - let ctr_name = Name::new(format!("{typ_name}/{ctr_name}")); + let ctr_name = Name::new(format!("{type_name}/{ctr_name}")); - fn parse_field(p: &mut TermParser) -> ParseResult { - let rec = p.try_consume("~"); - p.skip_trivia(); - let nam = p.labelled(|p| p.parse_bend_name(), "datatype constructor field")?; - Ok(CtrField { nam, rec }) - } + let fields = self.list_like(|p| p.parse_type_ctr_field(), "", ")", "", false, 0)?; + let field_types = fields.iter().map(|f| f.typ.clone()).collect::>(); + let end_idx = *self.index(); + self.check_repeated_ctr_fields(&fields, &ctr_name, ini_idx..end_idx)?; - let fields = self.list_like(parse_field, "", ")", "", false, 0)?; - if let Some(field) = fields.find_repeated_names().into_iter().next() { - let msg = format!("Found a repeated field '{field}' in constructor {ctr_name}."); - return self.expected_message(&msg)?; + let typ = make_ctr_type(type_name.clone(), &field_types, type_vars); + let ctr = AdtCtr { name: ctr_name, typ, fields }; + Ok(ctr) + } else { + // just name + let name = self.parse_restricted_name("Datatype constructor")?; + let name = Name::new(format!("{type_name}/{name}")); + let typ = make_ctr_type(type_name.clone(), &[], type_vars); + let ctr = AdtCtr { name, typ, fields: vec![] }; + Ok(ctr) + } + } + + fn parse_type_ctr_field(&mut self) -> ParseResult { + let rec = self.try_consume("~"); + + let nam; + let typ; + if self.try_consume("(") { + nam = self.parse_var_name()?; + if self.try_consume(":") { + typ = self.parse_type_term()?; + } else { + typ = Type::Any; } - Ok((ctr_name, fields)) + self.consume(")")?; } else { - // name - let name = self.labelled(|p| p.parse_top_level_name(), "datatype constructor name")?; - let name = Name::new(format!("{typ_name}/{name}")); - Ok((name, vec![])) + nam = self.parse_var_name()?; + typ = Type::Any; } + Ok(CtrField { nam, typ, rec }) } - fn parse_hvm(&mut self, builtin: bool) -> ParseResult { - self.skip_trivia_inline()?; - let name = self.parse_bend_name()?; - self.skip_trivia_inline()?; - self.consume_exactly(":")?; - self.consume_new_line()?; - // TODO: This will have the wrong index + fn parse_fun_def(&mut self) -> ParseResult { let ini_idx = *self.index(); - let mut p = hvm::ast::CoreParser::new(&self.input[*self.index()..]); - let body = p.parse_net()?; - *self.index() = ini_idx + *p.index(); - let end_idx = *self.index(); - let span = Some(TextSpan::from_byte_span(self.input(), ini_idx..end_idx)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = None; // should we pass the book's source here? - let source = Source { file, span, kind }; + // Try to parse signature + if let Ok((name, args, check, typ)) = self.parse_def_sig() { + if self.try_consume("=") { + // Single rule with signature + let body = self.parse_term()?; + let pats = args.into_iter().map(|nam| Pattern::Var(Some(nam))).collect(); + let rules = vec![Rule { pats, body }]; + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let def = FunDefinition { name, typ, check, rules, source }; + Ok(def) + } else { + // Multiple rules with signature + let mut rules = vec![]; + let (_, rule) = self.parse_rule()?; + rules.push(rule); + while self.starts_with_rule(&name) { + let (_, rule) = self.parse_rule()?; + rules.push(rule); + } + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let def = FunDefinition { name, typ, check, rules, source }; + Ok(def) + } + } else { + // Was not a signature, backtrack and read the name from the first rule + self.index = ini_idx; + // No signature, don't check by default + let check = self.parse_checked(false); + let mut rules = vec![]; + let (name, rule) = self.parse_rule()?; + rules.push(rule); + while self.starts_with_rule(&name) { + let (_, rule) = self.parse_rule()?; + rules.push(rule); + } + let end_idx = *self.index(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let def = FunDefinition { name, typ: Type::Any, check, rules, source }; + Ok(def) + } + } - let def = HvmDefinition { name: name.clone(), body, source }; - Ok(def) + /// Parses a function definition signature. + /// Returns the name, name of the arguments and the type of the function. + fn parse_def_sig(&mut self) -> ParseResult<(Name, Vec, bool, Type)> { + // '(' name ((arg | '(' arg (':' type)? ')'))* ')' ':' type + // name ((arg | '(' arg (':' type)? ')'))* ':' type + // Signature, check by default + let check = self.parse_checked(true); + let (name, args, typ) = if self.try_consume("(") { + let name = self.parse_top_level_name()?; + let args = self.list_like(|p| p.parse_def_sig_arg(), "", ")", "", false, 0)?; + self.consume(":")?; + let typ = self.parse_type_term()?; + (name, args, typ) + } else { + let name = self.parse_top_level_name()?; + let args = self.list_like(|p| p.parse_def_sig_arg(), "", ":", "", false, 0)?; + let typ = self.parse_type_term()?; + (name, args, typ) + }; + let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let typ = make_fn_type(arg_types, typ); + Ok((name, args, check, typ)) + } + + fn parse_def_sig_arg(&mut self) -> ParseResult<(Name, Type)> { + // name + // '(' name ')' + // '(' name ':' type ')' + if self.try_consume("(") { + let name = self.parse_var_name()?; + let typ = if self.try_consume(":") { self.parse_type_term()? } else { Type::Any }; + self.consume(")")?; + Ok((name, typ)) + } else { + let name = self.parse_var_name()?; + Ok((name, Type::Any)) + } + } + + fn parse_checked(&mut self, default: bool) -> bool { + if self.try_parse_keyword("checked") { + true + } else if self.try_parse_keyword("unchecked") { + false + } else { + default + } } fn parse_from_import(&mut self) -> ParseResult { // from path import package // from path import (a, b) // from path import * + self.parse_keyword("from")?; + self.skip_trivia_inline()?; + let path = self.parse_restricted_name("Path")?; + self.skip_trivia_inline()?; + self.consume("import")?; + self.skip_trivia_inline()?; let relative = path.starts_with("./") | path.starts_with("../"); @@ -319,6 +400,8 @@ impl<'a> TermParser<'a> { fn parse_import(&mut self) -> ParseResult> { // import path // import (path/a, path/b) + self.parse_keyword("import")?; + self.skip_trivia_inline()?; let new_import = |import: Name, alias: Option, relative: bool| -> Import { let (path, import) = match import.rsplit_once('/') { @@ -343,10 +426,13 @@ impl<'a> TermParser<'a> { fn parse_rule_lhs(&mut self) -> ParseResult<(Name, Vec)> { if self.try_consume_exactly("(") { self.skip_trivia(); - let name = self.labelled(|p| p.parse_top_level_name(), "function name")?; + let name = self.parse_restricted_name("Function")?; let pats = self.list_like(|p| p.parse_pattern(false), "", ")", "", false, 0)?; Ok((name, pats)) } else { + // Rule without parens + // Here we use a different label for the error because this is + // the last alternative case for top-level definitions. let name = self.labelled(|p| p.parse_top_level_name(), "top-level definition")?; let mut pats = vec![]; self.skip_trivia(); @@ -359,6 +445,7 @@ impl<'a> TermParser<'a> { } fn parse_rule(&mut self) -> ParseResult<(Name, Rule)> { + self.skip_trivia(); let (name, pats) = self.parse_rule_lhs()?; self.consume("=")?; @@ -369,6 +456,29 @@ impl<'a> TermParser<'a> { Ok((name, rule)) } + fn starts_with_rule(&mut self, expected_name: &Name) -> bool { + let ini_idx = *self.index(); + self.skip_trivia(); + let res = self.parse_rule_lhs(); + if !self.try_consume("=") { + self.index = ini_idx; + return false; + } + self.index = ini_idx; + if let Ok((name, _)) = res { + if &name == expected_name { + // Found rule with the expected name + true + } else { + // Found rule with a different name + false + } + } else { + // Not a rule + false + } + } + fn parse_pattern(&mut self, simple: bool) -> ParseResult { maybe_grow(|| { let (tag, unexpected_tag) = self.parse_tag()?; @@ -438,7 +548,7 @@ impl<'a> TermParser<'a> { unexpected_tag(self)?; self.advance_one(); self.skip_trivia(); - let name = self.parse_bend_name()?; + let name = self.parse_var_name()?; return Ok(Pattern::Chn(name)); } @@ -488,11 +598,9 @@ impl<'a> TermParser<'a> { // jk, actually a tuple if self.starts_with(",") && opr == Op::MUL { - let mut els = vec![Term::Era]; - while self.try_consume(",") { - els.push(self.parse_term()?); - } - self.consume(")")?; + self.consume_exactly(",")?; + let tail = self.list_like(|p| p.parse_term(), "", ")", ",", true, 1)?; + let els = [Term::Era].into_iter().chain(tail).collect(); return Ok(Term::Fan { fan: FanKind::Tup, tag: tag.unwrap_or(Tag::Static), els }); } @@ -570,7 +678,7 @@ impl<'a> TermParser<'a> { self.advance_one(); unexpected_tag(self)?; self.skip_trivia(); - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; return Ok(Term::Link { nam }); } @@ -621,7 +729,7 @@ impl<'a> TermParser<'a> { if self.try_parse_keyword("use") { unexpected_tag(self)?; self.skip_trivia(); - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; self.consume("=")?; let val = self.parse_term()?; self.try_consume(";"); @@ -654,41 +762,9 @@ impl<'a> TermParser<'a> { // Def if self.try_parse_keyword("def") { self.skip_trivia(); - let (cur_name, rule) = self.parse_rule()?; - let mut rules = vec![rule]; - // the current index to backtrack in case of fail to parse the next rule. - let mut nxt_term = *self.index(); - loop { - self.skip_trivia(); - // save the start position of the rule that can be a next def term. - let nxt_def = *self.index(); - match self.parse_rule() { - Ok((name, rule)) => { - if name == "def" { - // parse the nxt def term. - self.index = nxt_def; - let span = Some(TextSpan::from_byte_span(self.input(), nxt_def..*self.index())); - let source = Source { span, file: None, kind: SourceKind::User }; - let def = FunDefinition::new(name, rules, source); - return Ok(Term::Def { def, nxt: Box::new(self.parse_term()?) }); - } - if name == cur_name { - rules.push(rule); - // save the current position. - nxt_term = *self.index(); - } else { - let cur = *self.index(); - let msg = format!("Expected a rule with name '{cur_name}'."); - return self.with_ctx(Err(msg), nxt_def..cur); - } - } - // if failed it is a term. - Err(_) => break self.index = nxt_term, - } - } + let mut def = self.parse_fun_def()?; + def.source.kind = SourceKind::Generated; let nxt = self.parse_term()?; - let span = Some(TextSpan::from_byte_span(self.input(), nxt_term..*self.index())); - let def = FunDefinition::new(cur_name, rules, Source { span, file: None, kind: SourceKind::User }); return Ok(Term::Def { def, nxt: Box::new(nxt) }); } @@ -798,7 +874,7 @@ impl<'a> TermParser<'a> { unexpected_tag(self)?; let args = self.list_like( |p| { - let bind = p.parse_bend_name()?; + let bind = p.parse_var_name()?; let init = if p.try_consume("=") { p.parse_term()? } else { Term::Var { nam: bind.clone() } }; Ok((bind, init)) }, @@ -835,7 +911,7 @@ impl<'a> TermParser<'a> { self.skip_trivia(); let typ = self.parse_top_level_name()?; self.skip_trivia(); - let var = self.parse_bend_name()?; + let var = self.parse_var_name()?; self.try_consume(";"); let bod = self.parse_term()?; return Ok(Term::Open { typ, var, bod: Box::new(bod) }); @@ -843,7 +919,7 @@ impl<'a> TermParser<'a> { // Var unexpected_tag(self)?; - let nam = self.labelled(|p| p.parse_bend_name(), "term")?; + let nam = self.labelled(|p| p.parse_var_name(), "term")?; Ok(Term::Var { nam }) }) } @@ -854,7 +930,7 @@ impl<'a> TermParser<'a> { if p.try_consume_exactly("*") { Ok(None) } else { - let nam = p.parse_bend_name()?; + let nam = p.parse_var_name()?; Ok(Some(nam)) } }, @@ -872,16 +948,15 @@ impl<'a> TermParser<'a> { && !self.peek_many(2).is_some_and(|x| x.chars().nth(1).unwrap().is_ascii_digit()) { let msg = "Tagged terms not supported for hvm32.".to_string(); - return self.with_ctx(Err(msg), index..index + 1); + return self.err_msg_spanned(&msg, index..index + 1); } else { None }; let end_index = self.index; - let has_tag = tag.is_some(); - Ok((tag, move |slf: &mut Self| { - if has_tag { - let msg = "\x1b[1m- unexpected tag:\x1b[0m".to_string(); - slf.with_ctx(Err(msg), index..end_index) + Ok((tag.clone(), move |slf: &mut Self| { + if let Some(tag) = tag { + let msg = format!("Unexpected tag '{tag}'"); + slf.err_msg_spanned(&msg, index..end_index) } else { Ok(()) } @@ -908,7 +983,7 @@ impl<'a> TermParser<'a> { /// A named arg with non-optional name. fn parse_named_arg(&mut self) -> ParseResult<(Option, Term)> { - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; self.skip_trivia(); if self.starts_with("=") { self.advance_one(); @@ -940,59 +1015,88 @@ impl<'a> TermParser<'a> { Ok((nam, vec![], bod)) } - fn add_fun_def( - &mut self, - name: &Name, - rule: Rule, - builtin: bool, - last_rule: &Option, - book: &mut ParseBook, - span: Range, - ) -> ParseResult<()> { - match (book.fun_defs.get_mut(name), last_rule) { - // Continuing with a new rule to the current definition - (Some(def), Some(last_rule)) if last_rule == name => { - def.rules.push(rule); - if let Some(s) = &mut def.source.span { - s.end = TextLocation::from_byte_loc(self.input(), span.end); + fn parse_type_term(&mut self) -> ParseResult { + let mut left = self.parse_type_atom()?; + self.skip_trivia(); + while self.try_consume_exactly("->") { + let right = self.parse_type_term()?; + left = Type::Arr(Box::new(left), Box::new(right)); + } + Ok(left) + } + + /// Parses a type without an ending arrow. + /// Either an atom, a tuple, a ctr or a parenthesized type. + fn parse_type_atom(&mut self) -> ParseResult { + self.skip_trivia(); + if self.try_parse_keyword("Any") { + Ok(Type::Any) + } else if self.try_parse_keyword("None") { + Ok(Type::None) + } else if self.try_parse_keyword("_") { + Ok(Type::Hole) + } else if self.try_parse_keyword("u24") { + Ok(Type::U24) + } else if self.try_parse_keyword("i24") { + Ok(Type::I24) + } else if self.try_parse_keyword("f24") { + Ok(Type::F24) + } else if self.try_consume_exactly("(") { + // Tuple, constructor or parenthesized expression + let ini_idx = *self.index(); + let head = self.parse_type_term()?; + self.skip_trivia(); + if self.try_consume_exactly(")") { + // Parens + Ok(head) + } else if self.try_consume_exactly(",") { + // Tuple + let mut types = vec![head]; + loop { + types.push(self.parse_type_term()?); + self.skip_trivia(); + if !self.try_consume_exactly(",") { + break; + } } + self.consume(")")?; + Ok(Type::Tup(types)) + } else { + // Constructor + let Type::Var(nam) = head else { + let end_idx = *self.index(); + // TODO: This is not a good error message + return self.expected_spanned("type constructor", ini_idx..end_idx); + }; + let mut args = vec![]; + // We know there's at least one argument, otherwise it would go in the parens case. + while !self.try_consume(")") { + args.push(self.parse_type_term()?); + self.skip_trivia(); + } + Ok(Type::Ctr(nam, args)) } - // Trying to add a new rule to a previous definition, coming from a different rule. - (Some(def), Some(_)) => { - let msg = Self::redefinition_of_function_msg(def.is_builtin(), name); - return self.with_ctx(Err(msg), span); - } - // Trying to add a new rule to a previous definition, coming from another kind of top-level. - (Some(def), None) => { - let msg = Self::redefinition_of_function_msg(def.is_builtin(), name); - return self.with_ctx(Err(msg), span); - } - // Adding the first rule of a new definition - (None, _) => { - self.check_top_level_redefinition(name, book, span.clone())?; - let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); - let file = Some(book.source.to_string()); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let source = Source { file, span, kind }; - book.fun_defs.insert(name.clone(), FunDefinition::new(name.clone(), vec![rule], source)); - } + } else { + // Variable + // TODO: This will show "expected Name" instead of "expected type" + let nam = self.parse_var_name()?; + Ok(Type::Var(nam)) } + } + + fn add_fun_def(&mut self, def: FunDefinition, book: &mut ParseBook, span: Range) -> ParseResult<()> { + self.check_top_level_redefinition(&def.name, book, span)?; + book.fun_defs.insert(def.name.clone(), def); Ok(()) } fn add_imp_def( &mut self, - mut def: crate::imp::Definition, + def: crate::imp::Definition, book: &mut ParseBook, span: Range, - builtin: bool, ) -> ParseResult<()> { - self.check_top_level_redefinition(&def.name, book, span.clone())?; - let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - def.source = source; + self.check_top_level_redefinition(&def.name, book, span)?; book.imp_defs.insert(def.name.clone(), def); Ok(()) } @@ -1003,78 +1107,22 @@ impl<'a> TermParser<'a> { Ok(()) } - fn add_imp_type( - &mut self, - enum_: Enum, - book: &mut ParseBook, - range: Range, - builtin: bool, - ) -> ParseResult<()> { - self.check_type_redefinition(&enum_.name, book, range.clone())?; - - let span = Some(TextSpan::from_byte_span(self.input(), range.start..range.end)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - - let mut adt = Adt { ctrs: Default::default(), source }; - for variant in enum_.variants { - self.check_top_level_redefinition(&enum_.name, book, range.clone())?; - book.ctrs.insert(variant.name.clone(), enum_.name.clone()); - adt.ctrs.insert(variant.name, variant.fields); - } - book.adts.insert(enum_.name.clone(), adt); - Ok(()) - } - - fn add_fun_type( - &mut self, - book: &mut ParseBook, - nam: Name, - adt: Adt, - span: Range, - ) -> ParseResult<()> { - if book.adts.contains_key(&nam) { - let msg = TermParser::redefinition_of_type_msg(&nam); - return self.with_ctx(Err(msg), span); - } else { - for ctr in adt.ctrs.keys() { - if let Some(builtin) = book.contains_builtin_def(ctr) { - let msg = TermParser::redefinition_of_function_msg(builtin, ctr); - return self.expected_and("function", &msg); - } - match book.ctrs.entry(ctr.clone()) { - indexmap::map::Entry::Vacant(e) => _ = e.insert(nam.clone()), - indexmap::map::Entry::Occupied(e) => { - let msg = TermParser::redefinition_of_constructor_msg(e.key()); - return self.with_ctx(Err(msg), span); - } + fn add_type_def(&mut self, adt: Adt, book: &mut ParseBook, span: Range) -> ParseResult<()> { + self.check_type_redefinition(&adt.name, book, span.clone())?; + for ctr in adt.ctrs.keys() { + if let Some(builtin) = book.contains_builtin_def(ctr) { + let msg = FunParser::redefinition_of_function_msg(builtin, ctr); + return self.err_msg_spanned(&msg, span); + } + match book.ctrs.entry(ctr.clone()) { + indexmap::map::Entry::Vacant(e) => _ = e.insert(adt.name.clone()), + indexmap::map::Entry::Occupied(e) => { + let msg = FunParser::redefinition_of_constructor_msg(e.key()); + return self.err_msg_spanned(&msg, span); } } - book.adts.insert(nam.clone(), adt); } - Ok(()) - } - - fn add_object( - &mut self, - obj: Variant, - book: &mut ParseBook, - span: Range, - builtin: bool, - ) -> ParseResult<()> { - self.check_type_redefinition(&obj.name, book, span.clone())?; - self.check_top_level_redefinition(&obj.name, book, span.clone())?; - - let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); - let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; - let file = Some(book.source.to_string()); - let source = Source { file, span, kind }; - - let mut adt = Adt { ctrs: Default::default(), source }; - book.ctrs.insert(obj.name.clone(), obj.name.clone()); - adt.ctrs.insert(obj.name.clone(), obj.fields); - book.adts.insert(obj.name, adt); + book.adts.insert(adt.name.clone(), adt); Ok(()) } @@ -1086,15 +1134,15 @@ impl<'a> TermParser<'a> { ) -> ParseResult<()> { if let Some(builtin) = book.contains_builtin_def(name) { let msg = Self::redefinition_of_function_msg(builtin, name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } if book.ctrs.contains_key(name) { let msg = Self::redefinition_of_constructor_msg(name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } if book.hvm_defs.contains_key(name) { let msg = Self::redefinition_of_hvm_msg(false, name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } Ok(()) } @@ -1107,13 +1155,13 @@ impl<'a> TermParser<'a> { ) -> ParseResult<()> { if book.adts.contains_key(name) { let msg = Self::redefinition_of_type_msg(name); - return self.with_ctx(Err(msg), span); + return self.err_msg_spanned(&msg, span); } Ok(()) } } -impl<'a> Parser<'a> for TermParser<'a> { +impl<'a> Parser<'a> for FunParser<'a> { fn input(&mut self) -> &'a str { self.input } @@ -1200,6 +1248,16 @@ pub fn is_num_char(c: char) -> bool { "0123456789+-".contains(c) } +pub fn make_fn_type(args: Vec, ret: Type) -> Type { + args.into_iter().rfold(ret, |acc, typ| Type::Arr(Box::new(typ), Box::new(acc))) +} + +pub fn make_ctr_type(type_name: Name, fields: &[Type], vars: &[Name]) -> Type { + let typ = Type::Ctr(type_name, vars.iter().cloned().map(Type::Var).collect()); + let typ = fields.iter().rfold(typ, |acc, typ| Type::Arr(Box::new(typ.clone()), Box::new(acc))); + typ +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Indent { Val(isize), @@ -1224,20 +1282,9 @@ impl Indent { } } -impl<'a> ParserCommons<'a> for TermParser<'a> {} +impl<'a> ParserCommons<'a> for FunParser<'a> {} pub trait ParserCommons<'a>: Parser<'a> { - /// Generates an error message that does not print expected terms. - fn expected_message(&mut self, msg: &str) -> ParseResult { - let ini_idx = *self.index(); - let end_idx = *self.index() + 1; - - let is_eof = self.is_eof(); - let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); - let msg = format!("\x1b[1m- information:\x1b[0m {}\n\x1b[1m- location:\x1b[0m{}", msg, detected); - self.with_ctx(Err(msg), ini_idx..end_idx) - } - fn labelled(&mut self, parser: impl Fn(&mut Self) -> ParseResult, label: &str) -> ParseResult { match parser(self) { Ok(val) => Ok(val), @@ -1247,18 +1294,18 @@ pub trait ParserCommons<'a>: Parser<'a> { fn parse_restricted_name(&mut self, kind: &str) -> ParseResult { let ini_idx = *self.index(); - let name = self.take_while(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/'); + let name = self.take_while(is_name_char); if name.is_empty() { - self.expected("name")? + self.expected(&format!("{kind} name"))? } let name = Name::new(name.to_owned()); let end_idx = *self.index(); if name.contains("__") { let msg = format!("{kind} names are not allowed to contain \"__\"."); - self.with_ctx(Err(msg), ini_idx..end_idx) + self.err_msg_spanned(&msg, ini_idx..end_idx) } else if name.starts_with("//") { let msg = format!("{kind} names are not allowed to start with \"//\"."); - self.with_ctx(Err(msg), ini_idx..end_idx) + self.err_msg_spanned(&msg, ini_idx..end_idx) } else { Ok(name) } @@ -1268,7 +1315,7 @@ pub trait ParserCommons<'a>: Parser<'a> { self.parse_restricted_name("Top-level") } - fn parse_bend_name(&mut self) -> ParseResult { + fn parse_var_name(&mut self) -> ParseResult { self.parse_restricted_name("Variable") } @@ -1330,7 +1377,7 @@ pub trait ParserCommons<'a>: Parser<'a> { while let Some(c) = self.peek_one() { if c == '\t' { let idx = *self.index(); - return self.with_ctx(Err("Tabs are not accepted for indentation.".to_string()), idx..idx); + return self.err_msg_spanned("Tabs are not accepted for indentation.", idx..idx + 1); } if " ".contains(c) { self.advance_one(); @@ -1389,6 +1436,7 @@ pub trait ParserCommons<'a>: Parser<'a> { self.with_ctx(Err(msg), span) } + /// Same as `expected_spanned` but adds an information message before the expected message. fn expected_spanned_and(&mut self, exp: &str, msg: &str, span: Range) -> ParseResult { let is_eof = self.is_eof(); let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); @@ -1399,6 +1447,15 @@ pub trait ParserCommons<'a>: Parser<'a> { self.with_ctx(Err(msg), span) } + /// If the parser result is an error, adds code location information to the error message. + fn err_msg_spanned(&mut self, msg: &str, span: Range) -> ParseResult { + let is_eof = self.is_eof(); + let eof_msg = if is_eof { " end of input" } else { "" }; + let msg = format!("{msg}\nLocation:{eof_msg}"); + self.with_ctx(Err(msg), span) + } + + /// If the parser result is an error, adds highlighted code context to the message. fn with_ctx(&mut self, res: Result, span: Range) -> ParseResult { res.map_err(|msg| { let ctx = highlight_error(span.start, span.end, self.input()); @@ -1429,12 +1486,7 @@ pub trait ParserCommons<'a>: Parser<'a> { } fn try_parse_keyword(&mut self, keyword: &str) -> bool { - if !self.starts_with(keyword) { - return false; - } - let input = &self.input()[*self.index() + keyword.len()..]; - let next_is_name = input.chars().next().map_or(false, is_name_char); - if !next_is_name { + if self.starts_with_keyword(keyword) { self.consume_exactly(keyword).unwrap(); true } else { @@ -1455,6 +1507,16 @@ pub trait ParserCommons<'a>: Parser<'a> { } } + fn starts_with_keyword(&mut self, keyword: &str) -> bool { + if self.starts_with(keyword) { + let input = &self.input()[*self.index() + keyword.len()..]; + let next_is_name = input.chars().next().map_or(false, is_name_char); + !next_is_name + } else { + false + } + } + /// Parses a list-like structure like "[x1, x2, x3,]". /// Since a list is always well terminated, we consume newlines. /// @@ -1466,7 +1528,7 @@ pub trait ParserCommons<'a>: Parser<'a> { /// `min_els` determines how many elements must be parsed at minimum. fn list_like( &mut self, - parser: impl Fn(&mut Self) -> ParseResult, + mut parser: impl FnMut(&mut Self) -> ParseResult, start: &str, end: &str, sep: &str, @@ -1674,7 +1736,7 @@ pub trait ParserCommons<'a>: Parser<'a> { fn num_range_err(&mut self, ini_idx: usize, typ: &str) -> ParseResult { let msg = format!("\x1b[1mNumber literal outside of range for {}.\x1b[0m", typ); let end_idx = *self.index(); - self.with_ctx(Err(msg), ini_idx..end_idx) + self.err_msg_spanned(&msg, ini_idx..end_idx) } /// Parses up to 4 base64 characters surrounded by "`". @@ -1704,6 +1766,22 @@ pub trait ParserCommons<'a>: Parser<'a> { Ok(result) } + fn check_repeated_ctr_fields( + &mut self, + fields: &[CtrField], + ctr_name: &Name, + span: Range, + ) -> ParseResult<()> { + for i in 0..fields.len() { + let field = &fields[i]; + if fields.iter().skip(i + 1).any(|a: &CtrField| a.nam == field.nam) { + let msg = format!("Found a repeated field '{}' in constructor {}.", field.nam, ctr_name); + return self.err_msg_spanned(&msg, span); + } + } + Ok(()) + } + fn redefinition_of_function_msg(builtin: bool, function_name: &str) -> String { if builtin { format!("Redefinition of builtin (function) '{function_name}'.") diff --git a/src/fun/term_to_net.rs b/src/fun/term_to_net.rs index 4a07bc816..7f80ef13f 100644 --- a/src/fun/term_to_net.rs +++ b/src/fun/term_to_net.rs @@ -16,8 +16,6 @@ use std::{ pub struct ViciousCycleErr; pub fn book_to_hvm(book: &Book, diags: &mut Diagnostics) -> Result<(hvm::ast::Book, Labels), Diagnostics> { - diags.start_pass(); - let mut hvm_book = hvm::ast::Book { defs: Default::default() }; let mut labels = Labels::default(); @@ -193,8 +191,8 @@ impl<'t, 'l> EncodeTermState<'t, 'l> { } // Partially apply with snd, flip (fst, Term::Num { val }) => { - if [Op::POW, Op::ATN, Op::LOG].contains(opr) { - // POW, ATN and LOG share tags with AND, OR and XOR, so don't flip or results will be wrong + if let Op::POW = opr { + // POW shares tags with AND, so don't flip or results will be wrong let opr_val = hvm::ast::Numb(hvm::hvm::Numb::new_sym(opr.to_native_tag()).0); let oper = Place::Tree(LoanedMut::new(Tree::Num { val: opr_val })); let node1 = self.new_opr(); @@ -473,8 +471,6 @@ impl Op { Op::SHL => hvm::hvm::OP_SHL, Op::SHR => hvm::hvm::OP_SHR, - Op::ATN => hvm::hvm::OP_AND, - Op::LOG => hvm::hvm::OP_OR, Op::POW => hvm::hvm::OP_XOR, Op::LE => hvm::hvm::OP_GT, diff --git a/src/fun/transform/apply_args.rs b/src/fun/transform/apply_args.rs index f1bf8b9a3..0def4bf17 100644 --- a/src/fun/transform/apply_args.rs +++ b/src/fun/transform/apply_args.rs @@ -15,8 +15,6 @@ impl Ctx<'_> { /// main = (λx1 λx2 λx3 (MainBody x1 x2 x3) arg1 arg2 arg3) /// ``` pub fn apply_args(&mut self, args: Option>) -> Result<(), Diagnostics> { - self.info.start_pass(); - if let Some(entrypoint) = &self.book.entrypoint { let main_def = &mut self.book.defs[entrypoint]; diff --git a/src/fun/transform/definition_merge.rs b/src/fun/transform/definition_merge.rs index 54e9b8ef5..a35a34454 100644 --- a/src/fun/transform/definition_merge.rs +++ b/src/fun/transform/definition_merge.rs @@ -42,10 +42,12 @@ impl Book { // it's based on. // This could be done by having SourceKind::Generated contain a Vec or Vec. let any_def_name = equal_defs.iter().next().unwrap(); // we know we can unwrap since equal_defs.len() > 1 - let source = self.defs[any_def_name].source.clone(); // Add the merged def - let new_def = Definition::new_gen(new_name.clone(), vec![Rule { pats: vec![], body: term }], source); + let source = self.defs[any_def_name].source.clone(); + let rules = vec![Rule { pats: vec![], body: term }]; + // Note: This will erase types, so type checking needs to come before this. + let new_def = Definition::new_gen(new_name.clone(), rules, source, false); self.defs.insert(new_name.clone(), new_def); // Remove the old ones and write the map of old names to new ones. for name in equal_defs { diff --git a/src/fun/transform/definition_pruning.rs b/src/fun/transform/definition_pruning.rs index 8f7b9b586..4063b84e1 100644 --- a/src/fun/transform/definition_pruning.rs +++ b/src/fun/transform/definition_pruning.rs @@ -30,7 +30,9 @@ impl Ctx<'_> { if let Some(main) = &self.book.entrypoint { let def = self.book.defs.get(main).unwrap(); used.insert(main.clone(), Used::Main); - self.book.find_used_definitions_from_term(&def.rule().body, Used::Main, &mut used); + for rule in def.rules.iter() { + self.book.find_used_definitions_from_term(&rule.body, Used::Main, &mut used); + } } // Get the functions that are accessible from non-builtins. @@ -41,7 +43,9 @@ impl Ctx<'_> { } else { used.insert(def.name.clone(), Used::NonBuiltin); } - self.book.find_used_definitions_from_term(&def.rule().body, Used::NonBuiltin, &mut used); + for rule in def.rules.iter() { + self.book.find_used_definitions_from_term(&rule.body, Used::NonBuiltin, &mut used); + } } } for def in self.book.hvm_defs.values() { diff --git a/src/fun/transform/desugar_bend.rs b/src/fun/transform/desugar_bend.rs index 3d0753c1f..3b34156a4 100644 --- a/src/fun/transform/desugar_bend.rs +++ b/src/fun/transform/desugar_bend.rs @@ -10,12 +10,13 @@ const NEW_FN_SEP: &str = "__bend"; impl Ctx<'_> { pub fn desugar_bend(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); let mut new_defs = IndexMap::new(); for def in self.book.defs.values_mut() { let mut fresh = 0; for rule in def.rules.iter_mut() { - if let Err(err) = rule.body.desugar_bend(&def.name, &mut fresh, &mut new_defs, &def.source) { + if let Err(err) = + rule.body.desugar_bend(&def.name, &mut fresh, &mut new_defs, def.source.clone(), def.check) + { self.info.add_function_error(err, def.name.clone(), def.source.clone()); break; } @@ -34,12 +35,13 @@ impl Term { def_name: &Name, fresh: &mut usize, new_defs: &mut IndexMap, - source: &Source, + source: Source, + check: bool, ) -> Result<(), String> { maybe_grow(|| { // Recursively encode bends in the children for child in self.children_mut() { - child.desugar_bend(def_name, fresh, new_defs, source)?; + child.desugar_bend(def_name, fresh, new_defs, source.clone(), check)?; } // Convert a bend into a new recursive function and call it. @@ -87,7 +89,7 @@ impl Term { let body = Term::rfold_lams(body, free_vars.iter().cloned().map(Some)); // Make a definition from the new function - let def = Definition::new(new_nam.clone(), vec![Rule { pats: vec![], body }], source.clone()); + let def = Definition::new_gen(new_nam.clone(), vec![Rule { pats: vec![], body }], source, check); new_defs.insert(new_nam.clone(), def); // Call the new function in the original term. diff --git a/src/fun/transform/desugar_fold.rs b/src/fun/transform/desugar_fold.rs index 492cc9ed1..fe5886f4c 100644 --- a/src/fun/transform/desugar_fold.rs +++ b/src/fun/transform/desugar_fold.rs @@ -28,20 +28,21 @@ impl Ctx<'_> { /// } /// ``` pub fn desugar_fold(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - let mut new_defs = vec![]; for def in self.book.defs.values_mut() { let mut fresh = 0; for rule in def.rules.iter_mut() { - let res = rule.body.desugar_fold( - &def.name, - &mut fresh, - &mut new_defs, - &self.book.ctrs, - &self.book.adts, - &def.source, - ); + let mut ctx = DesugarFoldCtx { + def_name: &def.name, + fresh: &mut fresh, + new_defs: &mut new_defs, + ctrs: &self.book.ctrs, + adts: &self.book.adts, + source: def.source.clone(), + check: def.check, + }; + + let res = rule.body.desugar_fold(&mut ctx); if let Err(e) = res { self.info.add_function_error(e, def.name.clone(), def.source.clone()); } @@ -54,19 +55,21 @@ impl Ctx<'_> { } } +struct DesugarFoldCtx<'a> { + pub def_name: &'a Name, + pub fresh: &'a mut usize, + pub new_defs: &'a mut Vec, + pub ctrs: &'a Constructors, + pub adts: &'a Adts, + pub source: Source, + pub check: bool, +} + impl Term { - pub fn desugar_fold( - &mut self, - def_name: &Name, - fresh: &mut usize, - new_defs: &mut Vec, - ctrs: &Constructors, - adts: &Adts, - source: &Source, - ) -> Result<(), String> { + fn desugar_fold(&mut self, ctx: &mut DesugarFoldCtx<'_>) -> Result<(), String> { maybe_grow(|| { for child in self.children_mut() { - child.desugar_fold(def_name, fresh, new_defs, ctrs, adts, source)?; + child.desugar_fold(ctx)?; } if let Term::Fold { .. } = self { @@ -90,19 +93,19 @@ impl Term { } let free_vars = free_vars.into_iter().collect::>(); - let new_nam = Name::new(format!("{}__fold{}", def_name, fresh)); - *fresh += 1; + let new_nam = Name::new(format!("{}__fold{}", ctx.def_name, ctx.fresh)); + *ctx.fresh += 1; // Substitute the implicit recursive calls to call the new function let ctr = arms[0].0.as_ref().unwrap(); - let adt_nam = ctrs.get(ctr).unwrap(); - let ctrs = &adts.get(adt_nam).unwrap().ctrs; + let adt_nam = ctx.ctrs.get(ctr).unwrap(); + let ctrs = &ctx.adts.get(adt_nam).unwrap().ctrs; for arm in arms.iter_mut() { let ctr = arm.0.as_ref().unwrap(); let recursive = arm .1 .iter() - .zip(ctrs.get(ctr).unwrap()) + .zip(&ctrs.get(ctr).unwrap().fields) .filter_map(|(var, field)| if field.rec { Some(var.as_ref().unwrap().clone()) } else { None }) .collect::>(); arm.2.call_recursive(&new_nam, &recursive, &free_vars); @@ -121,8 +124,13 @@ impl Term { let body = Term::rfold_lams(body, free_vars.iter().map(|nam| Some(nam.clone()))); let body = Term::lam(Pattern::Var(Some(x_nam)), body); - let def = Definition::new(new_nam.clone(), vec![Rule { pats: vec![], body }], source.clone()); - new_defs.push(def); + let def = Definition::new_gen( + new_nam.clone(), + vec![Rule { pats: vec![], body }], + ctx.source.clone(), + ctx.check, + ); + ctx.new_defs.push(def); // Call the new function let call = Term::call(Term::Ref { nam: new_nam.clone() }, [std::mem::take(arg.as_mut())]); diff --git a/src/fun/transform/desugar_match_defs.rs b/src/fun/transform/desugar_match_defs.rs index da7939d0b..39bbb0c9c 100644 --- a/src/fun/transform/desugar_match_defs.rs +++ b/src/fun/transform/desugar_match_defs.rs @@ -15,8 +15,6 @@ pub enum DesugarMatchDefErr { impl Ctx<'_> { /// Converts equational-style pattern matching function definitions into trees of match terms. pub fn desugar_match_defs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for (def_name, def) in self.book.defs.iter_mut() { let errs = def.desugar_match_def(&self.book.ctrs, &self.book.adts); for err in errs { @@ -460,8 +458,8 @@ fn switch_rule( let old_args = args.split_off(1); let mut new_arms = vec![]; - for (ctr, fields) in &adts[&adt_name].ctrs { - let new_args = fields.iter().map(|f| Name::new(format!("{}.{}", arg, f.nam))); + for (ctr_nam, ctr) in &adts[&adt_name].ctrs { + let new_args = ctr.fields.iter().map(|f| Name::new(format!("{}.{}", arg, f.nam))); let args = new_args.clone().chain(old_args.clone()).collect(); let mut new_rules = vec![]; @@ -472,7 +470,7 @@ fn switch_rule( // (Ctr pat0_0 ... pat0_m) pat1 ... patN: body // becomes // pat0_0 ... pat0_m pat1 ... patN: body - Pattern::Ctr(found_ctr, new_pats) if ctr == found_ctr => { + Pattern::Ctr(found_ctr, new_pats) if ctr_nam == found_ctr => { let pats = new_pats.iter().cloned().chain(old_pats).collect(); let body = rule.body.clone(); let rule = Rule { pats, body }; @@ -488,7 +486,7 @@ fn switch_rule( let pats = new_pats.chain(old_pats.clone()).collect(); let mut body = rule.body.clone(); let reconstructed_var = - Term::call(Term::Ref { nam: ctr.clone() }, new_args.clone().map(|nam| Term::Var { nam })); + Term::call(Term::Ref { nam: ctr_nam.clone() }, new_args.clone().map(|nam| Term::Var { nam })); if let Some(var) = var { body = Term::Use { nam: Some(var.clone()), val: Box::new(reconstructed_var), nxt: Box::new(body) }; @@ -501,11 +499,11 @@ fn switch_rule( } if new_rules.is_empty() { - return Err(DesugarMatchDefErr::AdtNotExhaustive { adt: adt_name, ctr: ctr.clone() }); + return Err(DesugarMatchDefErr::AdtNotExhaustive { adt: adt_name, ctr: ctr_nam.clone() }); } let body = simplify_rule_match(args, new_rules, with.clone(), ctrs, adts)?; - new_arms.push((Some(ctr.clone()), new_args.map(Some).collect(), body)); + new_arms.push((Some(ctr_nam.clone()), new_args.map(Some).collect(), body)); } // Linearize previously matched vars and current args. @@ -566,7 +564,7 @@ impl Pattern { match self { Pattern::Var(_) | Pattern::Chn(_) => Type::Any, Pattern::Ctr(ctr_nam, _) => { - let adt_nam = ctrs.get(ctr_nam).expect("Unknown constructor '{ctr_nam}'"); + let adt_nam = ctrs.get(ctr_nam).unwrap_or_else(|| panic!("Unknown constructor '{ctr_nam}'")); Type::Adt(adt_nam.clone()) } Pattern::Fan(is_tup, tag, args) => Type::Fan(*is_tup, tag.clone(), args.len()), diff --git a/src/fun/transform/desugar_open.rs b/src/fun/transform/desugar_open.rs index 0c662f162..e402c28fd 100644 --- a/src/fun/transform/desugar_open.rs +++ b/src/fun/transform/desugar_open.rs @@ -6,8 +6,6 @@ use crate::{ impl Ctx<'_> { pub fn desugar_open(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { for rule in def.rules.iter_mut() { if let Err(err) = rule.body.desugar_open(&self.book.adts) { diff --git a/src/fun/transform/desugar_with_blocks.rs b/src/fun/transform/desugar_with_blocks.rs index e163781f5..bcf5ff04f 100644 --- a/src/fun/transform/desugar_with_blocks.rs +++ b/src/fun/transform/desugar_with_blocks.rs @@ -8,8 +8,6 @@ use std::collections::HashSet; impl Ctx<'_> { /// Converts `ask` terms inside `with` blocks into calls to a monadic bind operation. pub fn desugar_with_blocks(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - let def_names = self.book.defs.keys().cloned().collect::>(); for def in self.book.defs.values_mut() { @@ -34,11 +32,6 @@ impl Term { if let Term::With { typ, bod } = self { bod.desugar_with_blocks(Some(typ), def_names)?; let wrap_ref = Term::r#ref(&format!("{typ}/wrap")); - // let wrap_ref = if def_names.contains(&wrap_nam) { - // Term::r#ref(&wrap_nam) - // } else { - // return Err(format!("Could not find definition {wrap_nam} for type {typ}")); - // }; *self = Term::Use { nam: Some(Name::new("wrap")), val: Box::new(wrap_ref), nxt: std::mem::take(bod) }; } diff --git a/src/fun/transform/encode_adts.rs b/src/fun/transform/encode_adts.rs index 9ef6c62cd..5dad66381 100644 --- a/src/fun/transform/encode_adts.rs +++ b/src/fun/transform/encode_adts.rs @@ -1,5 +1,5 @@ use crate::{ - fun::{Book, Definition, Name, Num, Pattern, Rule, Source, Term}, + fun::{Book, Definition, Name, Num, Pattern, Rule, Source, SourceKind, Term, Type}, AdtEncoding, }; @@ -10,14 +10,14 @@ impl Book { let mut tags = vec![]; for (adt_name, adt) in self.adts.iter() { - for (ctr_idx, (ctr_name, fields)) in adt.ctrs.iter().enumerate() { + for (ctr_idx, (ctr_name, ctr)) in adt.ctrs.iter().enumerate() { let ctrs: Vec<_> = adt.ctrs.keys().cloned().collect(); let body = match adt_encoding { - AdtEncoding::Scott => encode_ctr_scott(fields.iter().map(|f| &f.nam), ctrs, ctr_name), + AdtEncoding::Scott => encode_ctr_scott(ctr.fields.iter().map(|f| &f.nam), ctrs, ctr_name), AdtEncoding::NumScott => { let tag = make_tag(adt_name == ctr_name, ctr_name); - let body = encode_ctr_num_scott(fields.iter().map(|f| &f.nam), &tag); + let body = encode_ctr_num_scott(ctr.fields.iter().map(|f| &f.nam), &tag); let tag_def = make_tag_def(ctr_idx, &tag, adt.source.clone()); tags.push((tag, tag_def)); body @@ -25,7 +25,13 @@ impl Book { }; let rules = vec![Rule { pats: vec![], body }]; - let def = Definition::new(ctr_name.clone(), rules, adt.source.clone()); + let def = Definition { + name: ctr_name.clone(), + typ: ctr.typ.clone(), + check: true, + rules, + source: adt.source.clone(), + }; defs.push((ctr_name.clone(), def)); } } @@ -66,6 +72,8 @@ fn encode_ctr_num_scott<'a>(ctr_args: impl DoubleEndedIterator } fn make_tag_def(ctr_idx: usize, tag: &Name, source: Source) -> Definition { - let tag_rule = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; - Definition::new_gen(tag.clone(), tag_rule, source) + let rules = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; + let kind = if source.is_builtin() { SourceKind::Builtin } else { SourceKind::Generated }; + let source = Source { kind, ..source }; + Definition { name: tag.clone(), typ: Type::U24, check: true, rules, source } } diff --git a/src/fun/transform/expand_main.rs b/src/fun/transform/expand_main.rs index c2126eb52..70bc09ec1 100644 --- a/src/fun/transform/expand_main.rs +++ b/src/fun/transform/expand_main.rs @@ -40,13 +40,16 @@ impl Term { Term::Ref { nam } => { if seen.contains(nam) { // Don't expand recursive references - } else { + } else if let Some(def) = book.defs.get(nam) { + // Regular function, expand seen.push(nam.clone()); - let mut body = book.defs.get(nam).unwrap().rule().body.clone(); + let mut body = def.rule().body.clone(); body.rename_unscoped(globals_count, &mut HashMap::new()); *self = body; self.expand_ref_return(book, seen, globals_count); seen.pop().unwrap(); + } else { + // Not a regular function, don't expand } } Term::Fan { els, .. } | Term::List { els } => { @@ -65,7 +68,7 @@ impl Term { // If the argument is a 0-ary constructor, we don't need to expand it. if let Term::Ref { nam } = arg { if let Some(adt_nam) = book.ctrs.get(nam) { - if book.adts.get(adt_nam).unwrap().ctrs.get(nam).unwrap().is_empty() { + if book.adts.get(adt_nam).unwrap().ctrs.get(nam).unwrap().fields.is_empty() { continue; } } diff --git a/src/fun/transform/fix_match_defs.rs b/src/fun/transform/fix_match_defs.rs index c7a6e1b1f..fc6f188e6 100644 --- a/src/fun/transform/fix_match_defs.rs +++ b/src/fun/transform/fix_match_defs.rs @@ -8,8 +8,6 @@ impl Ctx<'_> { /// /// Does not check exhaustiveness of rules and type mismatches. (Inter-ctr/type proprieties) pub fn fix_match_defs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { let mut errs = vec![]; @@ -82,7 +80,7 @@ impl Pattern { fn check_good_ctr(&self, ctrs: &Constructors, adts: &Adts, errs: &mut Vec) { if let Pattern::Ctr(nam, args) = self { if let Some(adt) = ctrs.get(nam) { - let expected_arity = adts[adt].ctrs[nam].len(); + let expected_arity = adts[adt].ctrs[nam].fields.len(); let found_arity = args.len(); if expected_arity != found_arity { errs.push(format!( diff --git a/src/fun/transform/fix_match_terms.rs b/src/fun/transform/fix_match_terms.rs index 754cc88f8..6467ddb38 100644 --- a/src/fun/transform/fix_match_terms.rs +++ b/src/fun/transform/fix_match_terms.rs @@ -37,13 +37,11 @@ impl Ctx<'_> { /// * If either included one of the cases more than once (including wildcard patterns), we'd get a warning. /// ```hvm /// match x { - /// Cons: (A x.h x.t) + /// Cons x.h x.t: (A x.h x.t) /// Nil: let %matched = (Foo y); switch %matched { 0: B; 1: C; _: D } /// } /// ``` pub fn fix_match_terms(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - for def in self.book.defs.values_mut() { for rule in def.rules.iter_mut() { let errs = rule.body.fix_match_terms(&self.book.ctrs, &self.book.adts); @@ -159,15 +157,15 @@ impl Term { // Build the match arms, with all constructors let mut new_rules = vec![]; - for (ctr, fields) in adt_ctrs.iter() { - let fields = fields.iter().map(|f| Some(match_field(&bnd, &f.nam))).collect::>(); - let body = if let Some(Some(body)) = bodies.remove(ctr) { + for (ctr_nam, ctr) in adt_ctrs.iter() { + let fields = ctr.fields.iter().map(|f| Some(match_field(&bnd, &f.nam))).collect::>(); + let body = if let Some(Some(body)) = bodies.remove(ctr_nam) { body } else { - errs.push(FixMatchErr::NonExhaustiveMatch { typ: adt_nam.clone(), missing: ctr.clone() }); + errs.push(FixMatchErr::NonExhaustiveMatch { typ: adt_nam.clone(), missing: ctr_nam.clone() }); Term::Err }; - new_rules.push((Some(ctr.clone()), fields, body)); + new_rules.push((Some(ctr_nam.clone()), fields, body)); } *arms = new_rules; return; @@ -248,7 +246,7 @@ fn fixed_match_arms<'a>( if let Some(var) = &rules[rule_idx].0 { new_body = Term::Use { nam: Some(var.clone()), - val: Box::new(rebuild_ctr(bnd, ctr, &adts[adt_nam].ctrs[&**ctr])), + val: Box::new(rebuild_ctr(bnd, ctr, &adts[adt_nam].ctrs[&**ctr].fields)), nxt: Box::new(new_body), }; } diff --git a/src/fun/transform/float_combinators.rs b/src/fun/transform/float_combinators.rs index f7870cc5e..2476adf35 100644 --- a/src/fun/transform/float_combinators.rs +++ b/src/fun/transform/float_combinators.rs @@ -45,10 +45,11 @@ impl Book { } let source = def.source.clone(); + let check = def.check; let body = &mut def.rule_mut().body; ctx.reset(); ctx.def_size = body.size(); - body.float_combinators(&mut ctx, def_name, source.clone()); + body.float_combinators(&mut ctx, def_name, source, check); } self.defs.extend(ctx.combinators.into_iter().map(|(nam, (_, def))| (nam, def))); @@ -84,11 +85,17 @@ impl<'b> FloatCombinatorsCtx<'b> { } impl Term { - fn float_combinators(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, source: Source) { + fn float_combinators( + &mut self, + ctx: &mut FloatCombinatorsCtx, + def_name: &Name, + source: Source, + check: bool, + ) { maybe_grow(|| { // Recursively float the grandchildren terms. for child in self.float_children_mut() { - child.float_combinators(ctx, def_name, source.clone()); + child.float_combinators(ctx, def_name, source.clone(), check); } let mut size = self.size(); @@ -104,14 +111,21 @@ impl Term { if child.is_combinator() && child_size > 0 && (!child_is_safe || extract_for_size) { ctx.def_size -= child_size; size -= child_size; - child.float(ctx, def_name, source.clone(), child_is_safe); + child.float(ctx, def_name, source.clone(), check, child_is_safe); } } }) } /// Inserts a new definition for the given term in the combinators map. - fn float(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, source: Source, is_safe: bool) { + fn float( + &mut self, + ctx: &mut FloatCombinatorsCtx, + def_name: &Name, + source: Source, + check: bool, + is_safe: bool, + ) { let comb_name = Name::new(format!("{}{}{}", def_name, NAME_SEP, ctx.name_gen)); ctx.name_gen += 1; @@ -119,7 +133,7 @@ impl Term { let extracted_term = std::mem::replace(self, comb_ref); let rules = vec![Rule { body: extracted_term, pats: Vec::new() }]; - let rule = Definition::new_gen(comb_name.clone(), rules, source); + let rule = Definition::new_gen(comb_name.clone(), rules, source, check); ctx.combinators.insert(comb_name, (is_safe, rule)); } } diff --git a/src/fun/transform/lift_local_defs.rs b/src/fun/transform/lift_local_defs.rs index 4cff00dd5..4682deb38 100644 --- a/src/fun/transform/lift_local_defs.rs +++ b/src/fun/transform/lift_local_defs.rs @@ -13,7 +13,7 @@ impl Book { for (name, def) in self.defs.iter_mut() { let mut gen = 0; for rule in def.rules.iter_mut() { - rule.body.lift_local_defs(name, &mut defs, &mut gen); + rule.body.lift_local_defs(name, def.check, &mut defs, &mut gen); } } self.defs.extend(defs); @@ -27,14 +27,20 @@ impl Rule { } impl Term { - pub fn lift_local_defs(&mut self, parent: &Name, defs: &mut IndexMap, gen: &mut usize) { + pub fn lift_local_defs( + &mut self, + parent: &Name, + check: bool, + defs: &mut IndexMap, + gen: &mut usize, + ) { maybe_grow(|| match self { Term::Def { def, nxt } => { let local_name = Name::new(format!("{}__local_{}_{}", parent, gen, def.name)); for rule in def.rules.iter_mut() { - rule.body.lift_local_defs(&local_name, defs, gen); + rule.body.lift_local_defs(&local_name, check, defs, gen); } - nxt.lift_local_defs(parent, defs, gen); + nxt.lift_local_defs(parent, check, defs, gen); *gen += 1; let inner_defs = @@ -46,12 +52,12 @@ impl Term { apply_closure(&mut rules, &fvs); - let new_def = Definition::new_gen(local_name.clone(), rules, source); + let new_def = Definition::new_gen(local_name.clone(), rules, source, check); defs.insert(local_name.clone(), new_def); } _ => { for child in self.children_mut() { - child.lift_local_defs(parent, defs, gen); + child.lift_local_defs(parent, check, defs, gen); } } }) diff --git a/src/fun/transform/mod.rs b/src/fun/transform/mod.rs index dc702c100..d2c4968a3 100644 --- a/src/fun/transform/mod.rs +++ b/src/fun/transform/mod.rs @@ -18,6 +18,7 @@ pub mod lift_local_defs; pub mod linearize_matches; pub mod linearize_vars; pub mod resolve_refs; +pub mod resolve_type_ctrs; pub mod resugar_list; pub mod resugar_string; pub mod unique_names; diff --git a/src/fun/transform/resolve_refs.rs b/src/fun/transform/resolve_refs.rs index 0f9e29879..19cd35bd9 100644 --- a/src/fun/transform/resolve_refs.rs +++ b/src/fun/transform/resolve_refs.rs @@ -18,8 +18,6 @@ impl Ctx<'_> { /// /// Postcondition: Refs are encoded as refs, with the correct def id. pub fn resolve_refs(&mut self) -> Result<(), Diagnostics> { - self.info.start_pass(); - let def_names = self.book.defs.keys().cloned().chain(self.book.hvm_defs.keys().cloned()).collect::>(); for (def_name, def) in &mut self.book.defs { diff --git a/src/fun/transform/resolve_type_ctrs.rs b/src/fun/transform/resolve_type_ctrs.rs new file mode 100644 index 000000000..a8ac534d9 --- /dev/null +++ b/src/fun/transform/resolve_type_ctrs.rs @@ -0,0 +1,61 @@ +use crate::{ + diagnostics::Diagnostics, + fun::{Adts, Ctx, Type}, + maybe_grow, +}; + +impl Ctx<'_> { + /// Resolves type constructors in the book. + pub fn resolve_type_ctrs(&mut self) -> Result<(), Diagnostics> { + for def in self.book.defs.values_mut() { + let res = def.typ.resolve_type_ctrs(&self.book.adts); + self.info.take_rule_err(res, def.name.clone()); + } + + let adts = self.book.adts.clone(); + for adt in self.book.adts.values_mut() { + for ctr in adt.ctrs.values_mut() { + let res = ctr.typ.resolve_type_ctrs(&adts); + self.info.take_rule_err(res, ctr.name.clone()); + } + } + + self.info.fatal(()) + } +} + +impl Type { + /// Resolves type constructors in the type. + pub fn resolve_type_ctrs(&mut self, adts: &Adts) -> Result<(), String> { + maybe_grow(|| { + match self { + Type::Var(nam) => { + // If the variable actually refers to a type, we change the type to a constructor. + if adts.contains_key(nam) { + *self = Type::Ctr(nam.clone(), vec![]); + } + } + Type::Ctr(name, args) => { + if !adts.contains_key(name) { + return Err(format!("Found unknown type constructor '{name}'.")); + } + for arg in args { + arg.resolve_type_ctrs(adts)?; + } + } + Type::Tup(els) => { + for el in els { + el.resolve_type_ctrs(adts)?; + } + } + Type::Arr(lft, rgt) => { + lft.resolve_type_ctrs(adts)?; + rgt.resolve_type_ctrs(adts)?; + } + Type::Number(t) | Type::Integer(t) => t.resolve_type_ctrs(adts)?, + Type::Any | Type::Hole | Type::None | Type::U24 | Type::I24 | Type::F24 => {} + } + Ok(()) + }) + } +} diff --git a/src/hvm/check_net_size.rs b/src/hvm/check_net_size.rs index 82a2abedf..152666f1d 100644 --- a/src/hvm/check_net_size.rs +++ b/src/hvm/check_net_size.rs @@ -14,9 +14,6 @@ pub fn check_net_sizes( CompilerTarget::Cuda => (MAX_NET_SIZE_CUDA, "Cuda"), _ => (MAX_NET_SIZE_C, "C"), }; - - diagnostics.start_pass(); - for (name, net) in &book.defs { let nodes = count_nodes(net); if nodes > net_size_bound { diff --git a/src/hvm/mutual_recursion.rs b/src/hvm/mutual_recursion.rs index cf00c1b53..53c112eab 100644 --- a/src/hvm/mutual_recursion.rs +++ b/src/hvm/mutual_recursion.rs @@ -16,8 +16,6 @@ type RefSet = IndexSet; pub struct Graph(IndexMap); pub fn check_cycles(book: &Book, diagnostics: &mut Diagnostics) -> Result<(), Diagnostics> { - diagnostics.start_pass(); - let graph = Graph::from(book); let cycles = graph.cycles(); diff --git a/src/imp/mod.rs b/src/imp/mod.rs index 41f020610..0976f3903 100644 --- a/src/imp/mod.rs +++ b/src/imp/mod.rs @@ -3,8 +3,7 @@ mod order_kwargs; pub mod parser; pub mod to_fun; -use crate::fun::{CtrField, Name, Num, Op, Source}; -use indexmap::{IndexMap, IndexSet}; +use crate::fun::{Name, Num, Op, Source, Type}; use interner::global::GlobalString; #[derive(Clone, Debug)] @@ -201,29 +200,17 @@ pub enum Stmt { Err, } -// {name} "{" {field}* "}" -#[derive(Clone, Debug)] -pub struct Variant { - pub name: Name, - pub fields: Vec, -} - // "def" {name} "(" {params} ")" ":" {body} #[derive(Clone, Debug)] pub struct Definition { pub name: Name, - pub params: Vec, + pub typ: Type, + pub check: bool, + pub args: Vec, pub body: Stmt, pub source: Source, } -// "type" {name} ":" {variant}* -#[derive(Clone, Debug)] -pub struct Enum { - pub name: Name, - pub variants: Vec, -} - impl InPlaceOp { pub fn to_lang_op(self) -> Op { match self { @@ -238,23 +225,3 @@ impl InPlaceOp { } } } - -pub trait RepeatedNames { - fn find_repeated_names(&self) -> IndexSet; -} - -impl RepeatedNames for Vec { - fn find_repeated_names(&self) -> IndexSet { - let mut count = IndexMap::new(); - for field in self.iter() { - *count.entry(field.nam.clone()).or_insert(0) += 1; - } - count.into_iter().filter_map(|(name, count)| if count > 1 { Some(name) } else { None }).collect() - } -} - -impl RepeatedNames for Variant { - fn find_repeated_names(&self) -> IndexSet { - self.fields.find_repeated_names() - } -} diff --git a/src/imp/order_kwargs.rs b/src/imp/order_kwargs.rs index 1482841b2..82a626b1f 100644 --- a/src/imp/order_kwargs.rs +++ b/src/imp/order_kwargs.rs @@ -215,7 +215,7 @@ fn get_args_def_or_ctr(name: &Name, book: &ParseBook, use_map: &IndexMap { +pub struct ImpParser<'i> { + pub file: Name, pub input: &'i str, pub index: usize, + pub builtin: bool, } -impl<'a> PyParser<'a> { - pub fn new(input: &'a str) -> Self { - Self { input, index: 0 } +impl<'a> ImpParser<'a> { + pub fn new(file: Name, input: &'a str, builtin: bool) -> Self { + Self { file, input, index: 0, builtin } } -} -impl<'a> ParserCommons<'a> for PyParser<'a> {} + pub fn parse_function_def(&mut self, indent: Indent) -> ParseResult<(Definition, Indent)> { + // def name(arg1: type1, arg2: type2, ...) -> type: + // body + if indent != Indent::Val(0) { + let msg = "Indentation error. Functions defined with 'def' must be at the start of the line."; + let idx = *self.index(); + return self.with_ctx(Err(msg), idx..idx + 1); + } + // TODO: checked vs unchecked functions + let (mut def, nxt_indent) = self.parse_def_aux(indent)?; + def.source.kind = if self.builtin { SourceKind::Builtin } else { SourceKind::User }; + Ok((def, nxt_indent)) + } -impl<'a> Parser<'a> for PyParser<'a> { - fn input(&mut self) -> &'a str { - self.input + pub fn parse_type_def(&mut self, mut indent: Indent) -> ParseResult<(Adt, Indent)> { + if indent != Indent::Val(0) { + let msg = "Indentation error. Types defined with 'type' must be at the start of the line."; + let idx = *self.index(); + return self.with_ctx(Err(msg), idx..idx + 1); + } + let ini_idx = *self.index(); + + self.parse_keyword("type")?; + self.skip_trivia_inline()?; + + let type_name = self.parse_restricted_name("datatype")?; + self.skip_trivia_inline()?; + + let type_vars = if self.try_consume_exactly("(") { + self.list_like(|p| p.parse_var_name(), "", ")", ",", true, 0)? + } else { + vec![] + }; + self.skip_trivia_inline()?; + + self.consume_exactly(":")?; + self.consume_new_line()?; + indent.enter_level(); + self.consume_indent_exactly(indent)?; + + let mut ctrs = Vec::new(); + let mut nxt_indent = indent; + while nxt_indent == indent { + ctrs.push(self.parse_type_def_variant(&type_name, &type_vars)?); + if !self.is_eof() { + self.consume_new_line()?; + } + nxt_indent = self.consume_indent_at_most(indent)?; + } + indent.exit_level(); + + let ctrs = ctrs.into_iter().map(|ctr| (ctr.name.clone(), ctr)).collect(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let adt = Adt { name: type_name, vars: type_vars, ctrs, source }; + + Ok((adt, nxt_indent)) } - fn index(&mut self) -> &mut usize { - &mut self.index + pub fn parse_object(&mut self, indent: Indent) -> ParseResult<(Adt, Indent)> { + // object Pair(a, b) { fst: a, snd: b } + if indent != Indent::Val(0) { + let msg = "Indentation error. Types defined with 'object' must be at the start of the line."; + let idx = *self.index(); + return self.with_ctx(Err(msg), idx..idx + 1); + } + let ini_idx = *self.index(); + + self.parse_keyword("object")?; + self.skip_trivia_inline()?; + + let name = self.parse_top_level_name()?; + self.skip_trivia_inline()?; + + let type_vars = if self.starts_with("(") { + self.list_like(|p| p.parse_var_name(), "(", ")", ",", true, 0)? + } else { + vec![] + }; + self.skip_trivia_inline()?; + + let fields = if self.starts_with("{") { + self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)? + } else { + vec![] + }; + let field_types = fields.iter().map(|f| f.typ.clone()).collect::>(); + + let end_idx = *self.index(); + self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; + + if !self.is_eof() { + self.consume_new_line()?; + } + let nxt_indent = self.advance_newlines()?; + + let typ = make_ctr_type(name.clone(), &field_types, &type_vars); + let ctr = AdtCtr { name: name.clone(), typ, fields }; + + let ctrs = [(name.clone(), ctr)].into_iter().collect(); + let source = Source::from_file_span(&self.file, self.input, ini_idx..end_idx, self.builtin); + let adt = Adt { name, vars: type_vars, ctrs, source }; + Ok((adt, nxt_indent)) } - /// Generates an error message for parsing failures, including the highlighted context. - /// - /// Override to have our own error message. - fn expected(&mut self, exp: &str) -> ParseResult { + pub fn parse_hvm(&mut self) -> ParseResult<(HvmDefinition, Indent)> { let ini_idx = *self.index(); - let end_idx = *self.index() + 1; - self.expected_spanned(exp, ini_idx..end_idx) + + self.parse_keyword("hvm")?; + self.skip_trivia_inline()?; + + let name = self.parse_var_name()?; + self.skip_trivia_inline()?; + + let typ = self.parse_return_type()?.unwrap_or(Type::Any); + let typ = make_fn_type(vec![], typ); + self.skip_trivia_inline()?; + + self.consume_exactly(":")?; + self.consume_new_line()?; + + // TODO: This will have the wrong index + let net_idx = *self.index(); + let mut p = hvm::ast::CoreParser::new(&self.input[net_idx..]); + let body = p.parse_net()?; + *self.index() = net_idx + *p.index(); + + let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let def = HvmDefinition { name: name.clone(), typ, body, source }; + let nxt_indent = self.advance_newlines()?; + Ok((def, nxt_indent)) } - /// Consumes an instance of the given string, erroring if it is not found. - /// - /// Override to have our own error message. - fn consume(&mut self, text: &str) -> ParseResult<()> { - self.skip_trivia(); - if self.input().get(*self.index()..).unwrap_or_default().starts_with(text) { - *self.index() += text.len(); - Ok(()) + fn parse_type_def_variant(&mut self, type_name: &Name, type_vars: &[Name]) -> ParseResult { + let ini_idx = *self.index(); + let name = self.parse_top_level_name()?; + let name = Name::new(format!("{type_name}/{name}")); + self.skip_trivia_inline()?; + + let fields = if self.try_consume_exactly("{") { + self.list_like(|p| p.parse_variant_field(), "", "}", ",", true, 0)? } else { - self.expected(format!("'{text}'").as_str()) - } + vec![] + }; + let field_types = fields.iter().map(|f| f.typ.clone()).collect::>(); + let end_idx = *self.index(); + self.check_repeated_ctr_fields(&fields, &name, ini_idx..end_idx)?; + + let typ = make_ctr_type(type_name.clone(), &field_types, type_vars); + Ok(AdtCtr { name, typ, fields }) } - fn skip_trivia(&mut self) { - while let Some(c) = self.peek_one() { - if c.is_ascii_whitespace() { - self.advance_one(); - continue; - } - if c == '#' { - while let Some(c) = self.peek_one() { - if c != '\n' { - self.advance_one(); - } else { - break; - } - } - self.advance_one(); // Skip the newline character as well - continue; - } - break; - } + fn parse_variant_field(&mut self) -> ParseResult { + let rec = self.try_consume_exactly("~"); + self.skip_trivia_inline()?; + + let nam = self.parse_var_name()?; + self.skip_trivia_inline()?; + + let typ = if self.try_consume_exactly(":") { self.parse_type_expr()? } else { Type::Any }; + + Ok(CtrField { nam, typ, rec }) } -} -impl<'a> PyParser<'a> { - /// ? - /// fn parse_primary_expr(&mut self, inline: bool) -> ParseResult { if inline { self.skip_trivia_inline()?; @@ -86,12 +190,12 @@ impl<'a> PyParser<'a> { self.skip_trivia(); } if self.try_parse_keyword("lambda") | self.try_consume_exactly("λ") { - fn parse_lam_var(p: &mut PyParser) -> ParseResult<(Name, bool)> { + fn parse_lam_var(p: &mut ImpParser) -> ParseResult<(Name, bool)> { if p.starts_with("$") { p.advance_one(); - Ok((p.parse_bend_name()?, true)) + Ok((p.parse_var_name()?, true)) } else { - Ok((p.parse_bend_name()?, false)) + Ok((p.parse_var_name()?, false)) } } let names = self.list_like(|p| parse_lam_var(p), "", ":", ",", false, 1)?; @@ -126,7 +230,7 @@ impl<'a> PyParser<'a> { } else if self.starts_with("$") { // Unscoped var self.advance_one(); - Ok(Expr::Chn { nam: self.parse_bend_name()? }) + Ok(Expr::Chn { nam: self.parse_var_name()? }) } else if self.starts_with("*") { // Era self.advance_one(); @@ -137,7 +241,7 @@ impl<'a> PyParser<'a> { Ok(Expr::Num { val: self.parse_number()? }) } else { // Var - let nam = self.labelled(|p| p.parse_bend_name(), "expression")?; + let nam = self.labelled(|p| p.parse_var_name(), "expression")?; Ok(Expr::Var { nam }) } } else { @@ -270,7 +374,7 @@ impl<'a> PyParser<'a> { fn data_kwarg(&mut self) -> ParseResult<(Name, Expr)> { self.skip_trivia(); - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; self.consume(":")?; let expr = self.parse_expr(false, false)?; Ok((nam, expr)) @@ -297,7 +401,7 @@ impl<'a> PyParser<'a> { if self.try_parse_keyword("for") { // Comprehension self.skip_trivia(); - let bind = self.parse_bend_name()?; + let bind = self.parse_var_name()?; self.skip_trivia(); self.parse_keyword("in")?; let iter = self.parse_expr(false, false)?; @@ -435,25 +539,25 @@ impl<'a> PyParser<'a> { /// Parses a statement and returns the indentation of the next statement. fn parse_statement(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { maybe_grow(|| { - if self.try_parse_keyword("return") { + if self.starts_with_keyword("return") { self.parse_return() - } else if self.try_parse_keyword("def") { + } else if self.starts_with_keyword("def") { self.parse_local_def(indent) - } else if self.try_parse_keyword("if") { + } else if self.starts_with_keyword("if") { self.parse_if(indent) - } else if self.try_parse_keyword("match") { + } else if self.starts_with_keyword("match") { self.parse_match(indent) - } else if self.try_parse_keyword("switch") { + } else if self.starts_with_keyword("switch") { self.parse_switch(indent) - } else if self.try_parse_keyword("fold") { + } else if self.starts_with_keyword("fold") { self.parse_fold(indent) - } else if self.try_parse_keyword("bend") { + } else if self.starts_with_keyword("bend") { self.parse_bend(indent) - } else if self.try_parse_keyword("with") { + } else if self.starts_with_keyword("with") { self.parse_with(indent) - } else if self.try_parse_keyword("open") { + } else if self.starts_with_keyword("open") { self.parse_open(indent) - } else if self.try_parse_keyword("use") { + } else if self.starts_with_keyword("use") { self.parse_use(indent) } else { self.parse_assign(indent) @@ -559,18 +663,25 @@ impl<'a> PyParser<'a> { } fn parse_return(&mut self) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("return")?; + let term = self.parse_expr(true, true)?; self.skip_trivia_inline()?; + self.try_consume_exactly(";"); if !self.is_eof() { self.consume_new_line()?; } let indent = self.advance_newlines()?; + Ok((Stmt::Return { term: Box::new(term) }, indent)) } fn parse_if(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { - let cond = self.parse_expr(true, false)?; + self.parse_keyword("if")?; + self.skip_trivia_inline()?; + + let cond = self.parse_expr(true, true)?; self.skip_trivia_inline()?; self.consume_exactly(":")?; indent.enter_level(); @@ -633,8 +744,12 @@ impl<'a> PyParser<'a> { } fn parse_match(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("match")?; + self.skip_trivia_inline()?; + let (bnd, arg) = self.parse_match_arg()?; self.skip_trivia_inline()?; + let (with_bnd, with_arg) = self.parse_with_clause()?; self.consume_new_line()?; indent.enter_level(); @@ -687,7 +802,7 @@ impl<'a> PyParser<'a> { } fn parse_with_arg(&mut self) -> ParseResult<(Option, Expr)> { - let bind = self.parse_bend_name()?; + let bind = self.parse_var_name()?; self.skip_trivia_inline()?; if self.try_consume("=") { let arg = self.parse_expr(false, false)?; @@ -703,7 +818,7 @@ impl<'a> PyParser<'a> { let pat = if self.try_consume_exactly("_") { None } else { - let nam = self.labelled(|p| p.parse_bend_name(), "name or '_'")?; + let nam = self.labelled(|p| p.parse_var_name(), "name or '_'")?; Some(nam) }; self.skip_trivia_inline()?; @@ -720,8 +835,12 @@ impl<'a> PyParser<'a> { } fn parse_switch(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("switch")?; + self.skip_trivia_inline()?; + let (bnd, arg) = self.parse_match_arg()?; self.skip_trivia_inline()?; + let (with_bnd, with_arg) = self.parse_with_clause()?; indent.enter_level(); @@ -797,9 +916,13 @@ impl<'a> PyParser<'a> { /// /// ... fn parse_fold(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("fold")?; + self.skip_trivia_inline()?; + // Actually identical to match, except the return let (bind, arg) = self.parse_match_arg()?; self.skip_trivia_inline()?; + let (with_bnd, with_arg) = self.parse_with_clause()?; self.consume_new_line()?; indent.enter_level(); @@ -830,6 +953,9 @@ impl<'a> PyParser<'a> { /// "else" ":" /// fn parse_bend(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("bend")?; + self.skip_trivia_inline()?; + let args = self.list_like(|p| p.parse_match_arg(), "", ":", ",", true, 1)?; let (bind, init) = args.into_iter().unzip(); self.consume_new_line()?; @@ -837,8 +963,11 @@ impl<'a> PyParser<'a> { self.consume_indent_exactly(*indent).or(self.expected_spanned("'when'", self.index..self.index + 1))?; self.parse_keyword("when")?; - let cond = self.parse_expr(true, false)?; self.skip_trivia_inline()?; + + let cond = self.parse_expr(true, true)?; + self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); @@ -891,9 +1020,12 @@ impl<'a> PyParser<'a> { /// /// ? fn parse_with(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("with")?; self.skip_trivia_inline()?; - let typ = self.parse_bend_name()?; + + let typ = self.parse_var_name()?; self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); @@ -957,7 +1089,7 @@ impl<'a> PyParser<'a> { } else if self.starts_with("$") { self.advance_one(); self.skip_trivia_inline()?; - let nam = self.parse_bend_name()?; + let nam = self.parse_var_name()?; Ok(AssignPattern::Chn(nam)) } else if self.starts_with("(") { self.advance_one(); @@ -965,159 +1097,209 @@ impl<'a> PyParser<'a> { self.consume(")")?; Ok(assign) } else { - Ok(AssignPattern::Var(self.parse_bend_name()?)) + Ok(AssignPattern::Var(self.parse_var_name()?)) } } /// "open" {typ} ":" {var} ";"? {nxt} fn parse_open(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("open")?; self.skip_trivia_inline()?; - let typ = self.labelled(|p| p.parse_bend_name(), "type name")?; + + let typ = self.labelled(|p| p.parse_var_name(), "type name")?; self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.skip_trivia_inline()?; - let var = self.labelled(|p| p.parse_bend_name(), "variable name")?; + + let var = self.labelled(|p| p.parse_var_name(), "variable name")?; self.skip_trivia_inline()?; + self.try_consume_exactly(";"); self.consume_new_line()?; self.consume_indent_exactly(*indent)?; + let (nxt, nxt_indent) = self.parse_statement(indent)?; + let stmt = Stmt::Open { typ, var, nxt: Box::new(nxt) }; Ok((stmt, nxt_indent)) } fn parse_use(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { + self.parse_keyword("use")?; self.skip_trivia_inline()?; - let nam = self.parse_bend_name()?; + + let nam = self.parse_var_name()?; self.skip_trivia_inline()?; + self.consume_exactly("=")?; self.skip_trivia_inline()?; + let bod = self.parse_expr(true, true)?; self.skip_trivia_inline()?; + self.try_consume_exactly(";"); self.consume_new_line()?; self.consume_indent_exactly(*indent)?; + let (nxt, nxt_indent) = self.parse_statement(indent)?; + let stmt = Stmt::Use { nam, val: Box::new(bod), nxt: Box::new(nxt) }; Ok((stmt, nxt_indent)) } fn parse_local_def(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> { - let (def, mut nxt_indent) = self.parse_def_aux(*indent)?; + // TODO: checked vs unchecked functions + let (mut def, mut nxt_indent) = self.parse_def_aux(*indent)?; + def.source.kind = if self.builtin { SourceKind::Builtin } else { SourceKind::Generated }; let (nxt, nxt_indent) = self.parse_statement(&mut nxt_indent)?; let stmt = Stmt::LocalDef { def: Box::new(def), nxt: Box::new(nxt) }; Ok((stmt, nxt_indent)) } - pub fn parse_def(&mut self, indent: Indent) -> ParseResult<(Definition, Indent)> { - if indent != Indent::Val(0) { - let msg = "Indentation error. Functions defined with 'def' must be at the start of the line."; - let idx = *self.index(); - return self.with_ctx(Err(msg), idx..idx + 1); - } - self.parse_def_aux(indent) + /// Parses a type expression, returning the type and the type variables. + fn parse_type_expr(&mut self) -> ParseResult { + // TODO: We should probably not have it all be inline or not inline. + // For example, in tuple types or constructors, we could have line breaks. + maybe_grow(|| { + self.skip_trivia_inline()?; + let ini_idx = *self.index(); + let lft = if self.try_parse_keyword("Any") { + Type::Any + } else if self.try_parse_keyword("None") { + Type::None + } else if self.try_parse_keyword("_") { + Type::Hole + } else if self.try_parse_keyword("u24") { + Type::U24 + } else if self.try_parse_keyword("i24") { + Type::I24 + } else if self.try_parse_keyword("f24") { + Type::F24 + } else if self.try_consume_exactly("(") { + // Tuple or parenthesized expression + self.skip_trivia(); + let head = self.parse_type_expr()?; + self.skip_trivia(); + if self.try_consume_exactly(")") { + // Parens + head + } else if self.starts_with(",") { + // Tuple + let mut types = vec![head]; + loop { + self.consume_exactly(",")?; + types.push(self.parse_type_expr()?); + if self.try_consume_exactly(")") { + break; + } + if !self.starts_with(",") { + return self.expected("',' or ')'"); + } + } + Type::Tup(types) + } else { + let end_idx = *self.index(); + return self.expected_spanned("tuple type or parenthesized type", ini_idx..end_idx); + } + } else { + // Variable or Constructor + // TODO: This will show "expected Name" instead of "expected type" + let name = self.parse_var_name()?; + self.skip_trivia_inline()?; + if self.try_consume_exactly("(") { + // Constructor with arguments + // name "(" (type ("," type)* ","?)? ")" + + let args = self.list_like(|p| p.parse_type_expr(), "", ")", ",", true, 0)?; + Type::Ctr(name, args) + } else { + // Variable + Type::Var(name) + } + }; + + // Handle arrow types + self.skip_trivia_inline()?; + if self.try_consume_exactly("->") { + let rgt = self.parse_type_expr()?; + Ok(Type::Arr(Box::new(lft), Box::new(rgt))) + } else { + Ok(lft) + } + }) } fn parse_def_aux(&mut self, mut indent: Indent) -> ParseResult<(Definition, Indent)> { + let ini_idx = *self.index(); + self.parse_keyword("def")?; self.skip_trivia_inline()?; + + let check = if self.try_parse_keyword("unchecked") { + (false, true) + } else if self.try_parse_keyword("checked") { + (true, false) + } else { + (false, false) + }; + self.skip_trivia_inline()?; + let name = self.parse_top_level_name()?; self.skip_trivia_inline()?; - let params = if self.starts_with("(") { - self.list_like(|p| p.parse_bend_name(), "(", ")", ",", true, 0)? + + let args = if self.try_consume_exactly("(") { + self.list_like(|p| p.parse_def_arg(), "", ")", ",", true, 0)? } else { vec![] }; self.skip_trivia_inline()?; - self.consume_exactly(":")?; - self.consume_new_line()?; - indent.enter_level(); - - self.consume_indent_exactly(indent)?; - let (body, nxt_indent) = self.parse_statement(&mut indent)?; - indent.exit_level(); - - // Temporary source, should be overwritten later - let source = Source { file: None, span: None, kind: SourceKind::Generated }; - let def = Definition { name, params, body, source }; - Ok((def, nxt_indent)) - } + let (args, arg_types): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - pub fn parse_type(&mut self, mut indent: Indent) -> ParseResult<(Enum, Indent)> { - if indent != Indent::Val(0) { - let msg = "Indentation error. Types defined with 'type' must be at the start of the line."; - let idx = *self.index(); - return self.with_ctx(Err(msg), idx..idx + 1); - } - - self.skip_trivia_inline()?; - let typ_name = self.parse_top_level_name()?; + let ret_type = self.parse_return_type()?; self.skip_trivia_inline()?; + self.consume_exactly(":")?; self.consume_new_line()?; indent.enter_level(); - self.consume_indent_exactly(indent)?; - let mut variants = Vec::new(); - let mut nxt_indent = indent; - while nxt_indent == indent { - variants.push(self.parse_enum_variant(&typ_name)?); - if !self.is_eof() { - self.consume_new_line()?; - } - nxt_indent = self.consume_indent_at_most(indent)?; - } + + let (body, nxt_indent) = self.parse_statement(&mut indent)?; indent.exit_level(); - let enum_ = Enum { name: typ_name, variants }; - Ok((enum_, nxt_indent)) - } + // If any annotation, check by default, otherwise unchecked by default + let check = if check.0 { + true + } else if check.1 { + false + } else { + ret_type.is_some() || arg_types.iter().any(|t| t.is_some()) + }; + let arg_types = arg_types.into_iter().map(|t| t.unwrap_or(Type::Any)).collect::>(); + let typ = make_fn_type(arg_types, ret_type.unwrap_or(Type::Any)); - pub fn parse_enum_variant(&mut self, typ_name: &Name) -> ParseResult { - let ctr_name = self.parse_top_level_name()?; - let ctr_name = Name::new(format!("{typ_name}/{ctr_name}")); - let mut fields = Vec::new(); - self.skip_trivia_inline()?; - if self.starts_with("{") { - fields = self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)?; - } - if let Some(field) = fields.find_repeated_names().into_iter().next() { - let msg = format!("Found a repeated field '{field}' in constructor {ctr_name}."); - return self.expected_message(&msg); - } - Ok(Variant { name: ctr_name, fields }) + // Note: The source kind gets replaced later (generated if a local def, user otherwise) + let source = Source::from_file_span(&self.file, self.input, ini_idx..self.index, self.builtin); + let def = Definition { name, args, typ, check, body, source }; + Ok((def, nxt_indent)) } - pub fn parse_object(&mut self, indent: Indent) -> ParseResult<(Variant, Indent)> { - if indent != Indent::Val(0) { - let msg = "Indentation error. Types defined with 'object' must be at the start of the line."; - let idx = *self.index(); - return self.with_ctx(Err(msg), idx..idx + 1); - } - - self.skip_trivia_inline()?; - let name = self.parse_top_level_name()?; + fn parse_def_arg(&mut self) -> ParseResult<(Name, Option)> { + let name = self.parse_var_name()?; self.skip_trivia_inline()?; - let fields = if self.starts_with("{") { - self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)? + if self.try_consume_exactly(":") { + let typ = self.parse_type_expr()?; + Ok((name, Some(typ))) } else { - vec![] - }; - if let Some(field) = fields.find_repeated_names().into_iter().next() { - let msg = format!("Found a repeated field '{field}' in object {name}."); - return self.expected_message(&msg); + Ok((name, None)) } - if !self.is_eof() { - self.consume_new_line()?; - } - let nxt_indent = self.advance_newlines()?; - Ok((Variant { name, fields }, nxt_indent)) } - fn parse_variant_field(&mut self) -> ParseResult { - let rec = self.try_consume_exactly("~"); - self.skip_trivia(); - let nam = self.parse_bend_name()?; - Ok(CtrField { nam, rec }) + fn parse_return_type(&mut self) -> ParseResult> { + if self.try_consume_exactly("->") { + Ok(Some(self.parse_type_expr()?)) + } else { + Ok(None) + } } fn expected_indent(&mut self, expected: Indent, got: Indent) -> ParseResult { @@ -1146,6 +1328,61 @@ impl<'a> PyParser<'a> { } } +impl<'a> ParserCommons<'a> for ImpParser<'a> {} + +impl<'a> Parser<'a> for ImpParser<'a> { + fn input(&mut self) -> &'a str { + self.input + } + + fn index(&mut self) -> &mut usize { + &mut self.index + } + + /// Generates an error message for parsing failures, including the highlighted context. + /// + /// Override to have our own error message. + fn expected(&mut self, exp: &str) -> ParseResult { + let ini_idx = *self.index(); + let end_idx = *self.index() + 1; + self.expected_spanned(exp, ini_idx..end_idx) + } + + /// Consumes an instance of the given string, erroring if it is not found. + /// + /// Override to have our own error message. + fn consume(&mut self, text: &str) -> ParseResult<()> { + self.skip_trivia(); + if self.input().get(*self.index()..).unwrap_or_default().starts_with(text) { + *self.index() += text.len(); + Ok(()) + } else { + self.expected(format!("'{text}'").as_str()) + } + } + + fn skip_trivia(&mut self) { + while let Some(c) = self.peek_one() { + if c.is_ascii_whitespace() { + self.advance_one(); + continue; + } + if c == '#' { + while let Some(c) = self.peek_one() { + if c != '\n' { + self.advance_one(); + } else { + break; + } + } + self.advance_one(); // Skip the newline character as well + continue; + } + break; + } + } +} + impl Op { fn precedence(&self) -> usize { match self { @@ -1166,10 +1403,9 @@ impl Op { Op::DIV => 7, Op::REM => 7, Op::POW => 8, - Op::ATN => todo!(), - Op::LOG => todo!(), } } + fn max_precedence() -> usize { 8 } diff --git a/src/imp/to_fun.rs b/src/imp/to_fun.rs index 114427e79..bbbc3204d 100644 --- a/src/imp/to_fun.rs +++ b/src/imp/to_fun.rs @@ -50,9 +50,15 @@ impl Definition { }; let rule = - fun::Rule { pats: self.params.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(), body }; + fun::Rule { pats: self.args.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(), body }; - let def = fun::Definition::new(self.name, vec![rule], self.source); + let def = fun::Definition { + name: self.name, + typ: self.typ, + check: self.check, + rules: vec![rule], + source: self.source, + }; Ok(def) } } diff --git a/src/imports/book.rs b/src/imports/book.rs index c6ea1676e..b491b85cc 100644 --- a/src/imports/book.rs +++ b/src/imports/book.rs @@ -1,9 +1,12 @@ use super::{BindMap, ImportsMap, PackageLoader}; use crate::{ diagnostics::{Diagnostics, DiagnosticsConfig}, - fun::{parser::ParseBook, Adt, Book, Definition, HvmDefinition, Name, Rule, Source, SourceKind, Term}, - imp::{self, Expr, Stmt}, + fun::{ + parser::ParseBook, Adt, AdtCtr, Book, Definition, HvmDefinition, Name, Rule, Source, SourceKind, Term, + }, + imp::{self, Expr, MatchArm, Stmt}, imports::packages::Packages, + maybe_grow, }; use indexmap::{map::Entry, IndexMap}; use itertools::Itertools; @@ -29,11 +32,16 @@ impl ParseBook { ) -> Result { let diag = &mut Diagnostics::new(diag_config); let pkgs = &mut Packages::new(self); + // Load all the imports recursively, saving them in `pkgs`. + // `book` is the root book with the entry point. let mut book = pkgs.load_imports(&mut loader, diag)?; + // Apply the imports to the book book.apply_imports(None, diag, pkgs)?; + diag.fatal(())?; eprint!("{}", diag); + // Convert the parse-level AST into the internal functional representation. let mut book = book.to_fun()?; // Process terms that contains constructors names and can't be updated by `desugar_use`. @@ -63,8 +71,6 @@ impl ParseBook { diag: &mut Diagnostics, pkgs: &mut Packages, ) -> Result<(), Diagnostics> { - diag.start_pass(); - let sources = self.import_ctx.sources().into_iter().cloned().collect_vec(); for src in sources { @@ -101,8 +107,7 @@ impl ParseBook { self.add_imported_hvm_def(def, diag); } } - - diag.fatal(()) + Ok(()) } /// Applies a chain of `use bind = src` to every local definition. @@ -112,7 +117,6 @@ impl ParseBook { // Can not be done outside the function because of the borrow checker. // Just serves to pass only the import map of the first call to `apply_imports_go`. let main_imports = main_imports.unwrap_or(&self.import_ctx.map); - let mut local_imports = BindMap::new(); let mut adt_imports = BindMap::new(); @@ -151,15 +155,17 @@ impl ParseBook { for (_, def) in self.local_defs_mut() { def.apply_binds(true, &local_imports); - def.apply_types(&adt_imports); + def.apply_type_binds(&adt_imports); } } /// Applying the necessary naming transformations to the book ADTs, - /// adding `use ctr = ctr_src` chains to every local definition. + /// adding `use ctr = ctr_src` chains to every local definition and + /// substituting the name of type ctrs for the canonical ones. fn apply_adts(&mut self, src: &Name, main_imports: &ImportsMap) { let adts = std::mem::take(&mut self.adts); let mut new_adts = IndexMap::new(); + let mut adts_map = vec![]; let mut ctrs_map = IndexMap::new(); let mut new_ctrs = IndexMap::new(); @@ -168,13 +174,14 @@ impl ParseBook { for (mut name, mut adt) in adts { if adt.source.is_local() { adt.source.kind = SourceKind::Imported; + let old_name = name.clone(); name = Name::new(format!("{}/{}", src, name)); let mangle_name = !main_imports.contains_source(&name); let mut mangle_adt_name = mangle_name; - for (ctr, f) in std::mem::take(&mut adt.ctrs) { - let mut ctr_name = Name::new(format!("{}/{}", src, ctr)); + for (old_nam, ctr) in std::mem::take(&mut adt.ctrs) { + let mut ctr_name = Name::new(format!("{}/{}", src, old_nam)); let mangle_ctr = mangle_name && !main_imports.contains_source(&ctr_name); @@ -183,23 +190,38 @@ impl ParseBook { ctr_name = Name::new(format!("__{}", ctr_name)); } + let ctr = AdtCtr { name: ctr_name.clone(), ..ctr }; new_ctrs.insert(ctr_name.clone(), name.clone()); - ctrs_map.insert(ctr, ctr_name.clone()); - adt.ctrs.insert(ctr_name, f); + ctrs_map.insert(old_nam, ctr_name.clone()); + adt.ctrs.insert(ctr_name, ctr); } if mangle_adt_name { name = Name::new(format!("__{}", name)); } + + adts_map.push((old_name, name.clone())); } new_adts.insert(name.clone(), adt); } - // Applies the binds for the new constructor names for every definition. - // As ADTs names are not used in the syntax, we don't bind their new names. + // Apply the binds for the type constructors in the constructor types + for (_, adt) in &mut new_adts { + for (_, ctr) in &mut adt.ctrs { + for (from, to) in &adts_map { + ctr.typ.subst_ctr(from, to); + } + } + } + + let adts_map = adts_map.into_iter().collect::>(); for (_, def) in self.local_defs_mut() { + // Applies the binds for the new constructor names for every definition. def.apply_binds(true, &ctrs_map); + + // Apply the binds for the type constructors in the def types and in the `def` terms. + def.apply_type_binds(&adts_map); } self.adts = new_adts; @@ -308,11 +330,13 @@ trait Def { *def_name = new_name; } - /// Should apply the binds to the definition, - /// and if there are possible constructor names on it, rename rule patterns. + /// Applies the binds for definition names by placing `use` terms. + /// + /// If we know that the bind map doesn't contain any constructor names, + /// we skip renaming rule patterns. fn apply_binds(&mut self, maybe_constructor: bool, binds: &BindMap); - fn apply_types(&mut self, types: &BindMap); + fn apply_type_binds(&mut self, binds: &BindMap); fn source(&self) -> &Source; fn source_mut(&mut self) -> &mut Source; @@ -340,19 +364,12 @@ impl Def for Definition { } } - fn apply_types(&mut self, types: &BindMap) { - fn rename_with_type(bod: &mut Term, types: &BindMap) { - if let Term::With { typ, .. } = bod { - if let Some(alias) = types.get(typ).cloned() { - *typ = alias; - } + fn apply_type_binds(&mut self, binds: &BindMap) { + for (from, to) in binds.iter().rev() { + self.typ.subst_ctr(from, to); + for rule in &mut self.rules { + rule.body.subst_type_ctrs(from, to); } - for child in bod.children_mut() { - rename_with_type(child, types); - } - } - for rule in self.rules.iter_mut() { - rename_with_type(&mut rule.body, types); } } @@ -375,84 +392,90 @@ impl Def for imp::Definition { self.body = bod.fold_uses(binds.iter().rev()); } - fn apply_types(&mut self, types: &BindMap) { - fn rename_with_type(bod: &mut Stmt, types: &BindMap) { - match bod { - Stmt::With { typ, bod, nxt } => { - if let Some(alias) = types.get(typ).cloned() { - *typ = alias - } - rename_with_type(bod, types); - if let Some(nxt) = nxt { - rename_with_type(nxt, types); - } - } + fn apply_type_binds(&mut self, binds: &BindMap) { + fn subst_type_ctrs_stmt(stmt: &mut Stmt, from: &Name, to: &Name) { + maybe_grow(|| match stmt { Stmt::Assign { nxt, .. } => { if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::InPlace { nxt, .. } => { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } Stmt::If { then, otherwise, nxt, .. } => { - rename_with_type(then, types); - rename_with_type(otherwise, types); + subst_type_ctrs_stmt(then, from, to); + subst_type_ctrs_stmt(otherwise, from, to); if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Match { arms, nxt, .. } => { - for arm in arms { - rename_with_type(&mut arm.rgt, types); + for MatchArm { lft: _, rgt } in arms { + subst_type_ctrs_stmt(rgt, from, to); } if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Switch { arms, nxt, .. } => { for arm in arms { - rename_with_type(arm, types); + subst_type_ctrs_stmt(arm, from, to); } if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Bend { step, base, nxt, .. } => { - rename_with_type(step, types); - rename_with_type(base, types); + subst_type_ctrs_stmt(step, from, to); + subst_type_ctrs_stmt(base, from, to); if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Fold { arms, nxt, .. } => { - for arm in arms { - rename_with_type(&mut arm.rgt, types); + for MatchArm { lft: _, rgt } in arms { + subst_type_ctrs_stmt(rgt, from, to); } if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); + } + } + Stmt::With { typ, bod, nxt } => { + if typ == from { + *typ = to.clone(); + } + subst_type_ctrs_stmt(bod, from, to); + if let Some(nxt) = nxt { + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Ask { nxt, .. } => { if let Some(nxt) = nxt { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } } Stmt::Return { .. } => {} - Stmt::Open { nxt, .. } => { - rename_with_type(nxt, types); + Stmt::Open { typ, nxt, .. } => { + if typ == from { + *typ = to.clone(); + } + subst_type_ctrs_stmt(nxt, from, to); } Stmt::Use { nxt, .. } => { - rename_with_type(nxt, types); + subst_type_ctrs_stmt(nxt, from, to); } Stmt::LocalDef { def, nxt } => { - def.apply_types(types); - rename_with_type(nxt, types); + def.apply_type_binds(&[(from.clone(), to.clone())].into_iter().collect()); + subst_type_ctrs_stmt(nxt, from, to); } Stmt::Err => {} - } + }) + } + for (from, to) in binds.iter().rev() { + self.typ.subst_ctr(from, to); + subst_type_ctrs_stmt(&mut self.body, from, to); } - rename_with_type(&mut self.body, types); } fn source(&self) -> &Source { @@ -472,7 +495,11 @@ impl Def for HvmDefinition { /// Do nothing, can not apply binds to a HvmDefinition. fn apply_binds(&mut self, _maybe_constructor: bool, _binds: &BindMap) {} - fn apply_types(&mut self, _types: &BindMap) {} + fn apply_type_binds(&mut self, binds: &BindMap) { + for (from, to) in binds.iter().rev() { + self.typ.subst_ctr(from, to); + } + } fn source(&self) -> &Source { &self.source diff --git a/src/imports/packages.rs b/src/imports/packages.rs index b77085d56..668f5ca92 100644 --- a/src/imports/packages.rs +++ b/src/imports/packages.rs @@ -11,6 +11,7 @@ pub struct Packages { /// Map from source name to parsed book. pub books: IndexMap>, /// Already loaded ADTs information to be used when applying ADT binds. + /// Source path -> ADT names -> constructor names. pub loaded_adts: IndexMap>>, /// Queue of books indexes that still needs to load its imports. load_queue: VecDeque, @@ -32,10 +33,7 @@ impl Packages { loader: &mut impl PackageLoader, diag: &mut Diagnostics, ) -> Result { - diag.start_pass(); - self.load_imports_go(0, None, loader)?; - while let Some(idx) = self.load_queue.pop_front() { let parent_dir = { let book = self.books[idx].borrow(); diff --git a/src/lib.rs b/src/lib.rs index 88f189bf4..89cbde222 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,7 +60,6 @@ pub fn compile_book( } if opts.inline { - diagnostics.start_pass(); if let Err(e) = inline_hvm_book(&mut hvm_book) { diagnostics.add_book_error(format!("During inlining:\n{:ERR_INDENT_SIZE$}{}", "", e)); } @@ -120,6 +119,7 @@ pub fn desugar_book( // Auto match linearization ctx.book.make_var_names_unique(); ctx.book.desugar_use(); + match opts.linearize_matches { OptLevel::Disabled => (), OptLevel::Alt => ctx.book.linearize_match_binds(), @@ -128,6 +128,10 @@ pub fn desugar_book( // Manual match linearization ctx.book.linearize_match_with(); + if opts.type_check { + type_check_book(&mut ctx)?; + } + ctx.book.encode_matches(opts.adt_encoding); // sanity check @@ -165,6 +169,13 @@ pub fn desugar_book( } } +pub fn type_check_book(ctx: &mut Ctx) -> Result<(), Diagnostics> { + ctx.check_untyped_terms()?; + ctx.resolve_type_ctrs()?; + ctx.type_check()?; + Ok(()) +} + pub fn run_book( mut book: Book, run_opts: RunOpts, @@ -362,6 +373,9 @@ pub struct CompileOpts { /// Enables [hvm::check_net_size]. pub check_net_size: bool, + /// Enables [type_check_book]. + pub type_check: bool, + /// Determines the encoding of constructors and matches. pub adt_encoding: AdtEncoding, } @@ -376,8 +390,9 @@ impl CompileOpts { prune: true, float_combinators: true, merge: true, - inline: true, linearize_matches: OptLevel::Enabled, + type_check: true, + inline: true, check_net_size: self.check_net_size, adt_encoding: self.adt_encoding, } @@ -394,6 +409,7 @@ impl CompileOpts { float_combinators: false, merge: false, inline: false, + type_check: self.type_check, check_net_size: self.check_net_size, adt_encoding: self.adt_encoding, } @@ -426,6 +442,7 @@ impl Default for CompileOpts { merge: false, inline: false, check_net_size: true, + type_check: true, adt_encoding: AdtEncoding::NumScott, } } diff --git a/src/main.rs b/src/main.rs index b69ea3196..6b8d55d7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,7 +105,7 @@ struct RunArgs { #[arg(help = "Path to the input file")] path: PathBuf, - #[arg(value_parser = |arg: &str| bend::fun::parser::TermParser::new(arg).parse_term())] + #[arg(value_parser = |arg: &str| bend::fun::parser::FunParser::new(Name::new(""), arg, false).parse_term())] arguments: Option>, } @@ -188,6 +188,8 @@ pub enum OptArgs { NoCheckNetSize, AdtScott, AdtNumScott, + TypeCheck, + NoTypeCheck, } fn compile_opts_from_cli(args: &Vec, compiler_target: CompilerTarget) -> CompileOpts { @@ -213,6 +215,8 @@ fn compile_opts_from_cli(args: &Vec, compiler_target: CompilerTarget) - NoInline => opts.inline = false, CheckNetSize => opts.check_net_size = true, NoCheckNetSize => opts.check_net_size = false, + TypeCheck => opts.type_check = true, + NoTypeCheck => opts.type_check = false, LinearizeMatches => opts.linearize_matches = OptLevel::Enabled, LinearizeMatchesAlt => opts.linearize_matches = OptLevel::Alt, diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index 618e55270..b47270d62 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -1,7 +1,20 @@ +//! This module runs snapshot tests for compiling and running Bend programs. +//! +//! The result of each test is saved as a snapshot and used as golden output +//! for future tests. This allows us to test regressions in compilation and +//! have a history of how certain programs compiled and ran. +//! +//! These tests use `cargo-insta`. To run the tests, run `cargo insta test`. +//! If there are any changes to the snapshots, they'll be highlighted by the +//! CLI tool. Then, run `cargo insta review` to review these changes. + use bend::{ compile_book, desugar_book, diagnostics::{Diagnostics, DiagnosticsConfig, Severity}, - fun::{load_book::do_parse_book_default, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Name}, + fun::{ + load_book::do_parse_book, net_to_term::net_to_term, parser::ParseBook, term_to_net::Labels, Book, Ctx, + Name, + }, hvm::hvm_book_show_pretty, imports::DefaultLoader, load_to_book, @@ -28,6 +41,10 @@ const TESTS_PATH: &str = "/tests/golden_tests/"; type RunFn = dyn Fn(&str, &Path) -> Result; +pub fn parse_book_single_file(code: &str, origin: &Path) -> Result { + do_parse_book(code, origin, ParseBook::builtins())?.to_fun() +} + fn run_single_golden_test(path: &Path, run: &[&RunFn]) -> Result<(), String> { println!("{}", path.display()); let code = std::fs::read_to_string(path).map_err(|e| e.to_string())?; @@ -92,10 +109,11 @@ fn run_golden_test_dir_multiple(test_name: &str, run: &[&RunFn]) { and what to save as a snapshot. */ +/// Compiles a file with regular compilation options. #[test] fn compile_file() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default(); let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Allow, ..Default::default() }; @@ -104,10 +122,11 @@ fn compile_file() { }) } +/// Compiles a file with `-Oall` option. #[test] fn compile_file_o_all() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig { recursion_cycle: Severity::Warning, @@ -120,10 +139,11 @@ fn compile_file_o_all() { }) } +/// Compiles a file with `-Ono-all` option. #[test] fn compile_file_o_no_all() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default().set_no_all(); let diagnostics_cfg = DiagnosticsConfig::default(); let res = compile_book(&mut book, compile_opts, diagnostics_cfg, None)?; @@ -131,11 +151,12 @@ fn compile_file_o_no_all() { }) } +/// Runs a file, but with linear readback enabled. #[test] fn linear_readback() { run_golden_test_dir(function_name!(), &|code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; + let book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig::default(); let (term, _, diags) = run_book( @@ -152,13 +173,15 @@ fn linear_readback() { }); } +/// Runs a file with regular compilation options, but rejecting all warnings. +/// Runs once for each ADT encoding. #[test] fn run_file() { run_golden_test_dir_multiple( function_name!(), &[(&|code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; + let book = parse_book_single_file(code, path)?; let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Allow, ..DiagnosticsConfig::new(Severity::Error, true) @@ -178,6 +201,7 @@ fn run_file() { ) } +/// Runs bend programs, all sharing a common lib to test the import system. #[test] fn import_system() { run_golden_test_dir_multiple( @@ -202,27 +226,7 @@ fn import_system() { ) } -#[test] -#[ignore = "while lazy execution is not implemented for hvm32"] -fn run_lazy() { - run_golden_test_dir(function_name!(), &|_code, _path| { - todo!() - /* let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; - let compile_opts = CompileOpts::default_lazy(); - let diagnostics_cfg = DiagnosticsConfig { - recursion_cycle: Severity::Allow, - unused_definition: Severity::Allow, - ..DiagnosticsConfig::new(Severity::Error, true) - }; - let run_opts = RunOpts::lazy(); - - let (term, _, diags) = run_book(book, run_opts, compile_opts, diagnostics_cfg, None)?; - let res = format!("{diags}{term}"); - Ok(res) */ - }) -} - +/// Reads back an HVM net. #[test] fn readback_hvm() { run_golden_test_dir(function_name!(), &|code, _| { @@ -236,6 +240,7 @@ fn readback_hvm() { }) } +/// Runs compilation up to fixing, simplifying and linearizing matches. #[test] fn simplify_matches() { run_golden_test_dir(function_name!(), &|code, path| { @@ -243,18 +248,19 @@ fn simplify_matches() { irrefutable_match: Severity::Allow, ..DiagnosticsConfig::new(Severity::Error, true) }; - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let mut ctx = Ctx::new(&mut book, diagnostics_cfg); ctx.check_shared_names(); - ctx.set_entrypoint(); ctx.book.encode_adts(AdtEncoding::NumScott); ctx.fix_match_defs()?; ctx.desugar_open()?; ctx.book.encode_builtins(); ctx.resolve_refs()?; + ctx.resolve_type_ctrs()?; ctx.desugar_match_defs()?; ctx.fix_match_terms()?; + ctx.book.lift_local_defs(); ctx.desugar_bend()?; ctx.desugar_fold()?; ctx.desugar_with_blocks()?; @@ -273,31 +279,16 @@ fn simplify_matches() { }) } -#[test] -fn parse_file() { - run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; - let mut ctx = Ctx::new(&mut book, Default::default()); - ctx.set_entrypoint(); - ctx.book.encode_adts(AdtEncoding::NumScott); - ctx.book.encode_builtins(); - ctx.resolve_refs().expect("Resolve refs"); - ctx.desugar_match_defs().expect("Desugar match defs"); - ctx.prune(false); - Ok(book.to_string()) - }) -} - +/// Runs compilation up to encoding `match` terms as lambdas. #[test] fn encode_pattern_match() { run_golden_test_dir(function_name!(), &|code, path| { let mut result = String::new(); for adt_encoding in [AdtEncoding::Scott, AdtEncoding::NumScott] { let diagnostics_cfg = DiagnosticsConfig::default(); - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let mut ctx = Ctx::new(&mut book, diagnostics_cfg); ctx.check_shared_names(); - ctx.set_entrypoint(); ctx.book.encode_adts(adt_encoding); ctx.fix_match_defs()?; ctx.desugar_open()?; @@ -305,6 +296,7 @@ fn encode_pattern_match() { ctx.resolve_refs()?; ctx.desugar_match_defs()?; ctx.fix_match_terms()?; + ctx.book.lift_local_defs(); ctx.desugar_bend()?; ctx.desugar_fold()?; ctx.desugar_with_blocks()?; @@ -327,6 +319,22 @@ fn encode_pattern_match() { }) } +/// Parses a file, but does not desugar or compile it. +#[test] +fn parse_file() { + run_golden_test_dir(function_name!(), &|code, path| { + let mut book = parse_book_single_file(code, path)?; + let mut ctx = Ctx::new(&mut book, Default::default()); + ctx.set_entrypoint(); + ctx.book.encode_adts(AdtEncoding::NumScott); + ctx.book.encode_builtins(); + ctx.resolve_refs().expect("Resolve refs"); + ctx.prune(false); + Ok(book.to_string()) + }) +} + +/// Runs compilation up to the last term-level pass (`bend desugar` command). #[test] fn desugar_file() { run_golden_test_dir(function_name!(), &|code, path| { @@ -335,12 +343,13 @@ fn desugar_file() { unused_definition: Severity::Allow, ..DiagnosticsConfig::new(Severity::Error, true) }; - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; desugar_book(&mut book, compile_opts, diagnostics_cfg, None)?; Ok(book.to_string()) }) } +/// Runs a file that is expected to hang. #[test] #[ignore = "bug - the subprocess created by run_book leaks"] fn hangs() { @@ -348,7 +357,7 @@ fn hangs() { run_golden_test_dir(function_name!(), &move |code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; + let book = parse_book_single_file(code, path)?; let compile_opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig::new(Severity::Allow, false); @@ -367,10 +376,11 @@ fn hangs() { }) } +/// Compiles a file with a custom entrypoint. #[test] fn compile_entrypoint() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; book.entrypoint = Some(Name::new("foo")); let diagnostics_cfg = DiagnosticsConfig { ..DiagnosticsConfig::new(Severity::Error, true) }; let res = compile_book(&mut book, CompileOpts::default(), diagnostics_cfg, None)?; @@ -378,12 +388,13 @@ fn compile_entrypoint() { }) } +/// Runs a file with a custom entrypoint. #[test] #[ignore = "while execution with different entrypoints is not implemented for hvm32"] fn run_entrypoint() { run_golden_test_dir(function_name!(), &|code, path| { let _guard = RUN_MUTEX.lock().unwrap(); - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; book.entrypoint = Some(Name::new("foo")); let compile_opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig { ..DiagnosticsConfig::new(Severity::Error, true) }; @@ -394,6 +405,7 @@ fn run_entrypoint() { }) } +/// Runs a Bend CLI command. #[test] fn cli() { run_golden_test_dir(function_name!(), &|_code, path| { @@ -414,49 +426,35 @@ fn cli() { }) } +/// Compiles a file to check for mutual recursion. #[test] fn mutual_recursion() { run_golden_test_dir(function_name!(), &|code, path| { let diagnostics_cfg = DiagnosticsConfig { recursion_cycle: Severity::Error, ..DiagnosticsConfig::new(Severity::Allow, true) }; - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts { merge: true, ..CompileOpts::default() }; let res = compile_book(&mut book, opts, diagnostics_cfg, None)?; Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } +/// Runs a file that uses IO. #[test] fn io() { - run_golden_test_dir_multiple( - function_name!(), - &[ - /* (&|code, path| { - let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; - let compile_opts = CompileOpts::default_lazy(); - let diagnostics_cfg = DiagnosticsConfig::default_lazy(); - let Output { status, stdout, stderr } = - run_book(book, None, RunOpts::lazy(), compile_opts, diagnostics_cfg, None)?; - let stderr = String::from_utf8_lossy(&stderr); - let status = if !status.success() { format!("\n{status}") } else { String::new() }; - let stdout = String::from_utf8_lossy(&stdout); - Ok(format!("Lazy mode:\n{}{}{}", stderr, status, stdout)) - }), */ - (&|code, path| { - let _guard = RUN_MUTEX.lock().unwrap(); - let book = do_parse_book_default(code, path)?; - let compile_opts = CompileOpts::default(); - let diagnostics_cfg = DiagnosticsConfig::default(); - let (term, _, diags) = - run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); - let res = format!("{diags}{term}"); - Ok(format!("Strict mode:\n{res}")) - }), - ], - ) + run_golden_test_dir(function_name!(), &|code, path| { + let _guard = RUN_MUTEX.lock().unwrap(); + let book = parse_book_single_file(code, path)?; + let compile_opts = CompileOpts::default(); + let diagnostics_cfg = DiagnosticsConfig::default(); + let (term, _, diags) = + run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); + let res = format!("{diags}{term}"); + Ok(format!("Strict mode:\n{res}")) + }) } +/// Runs all examples in the examples folder. #[test] fn examples() -> Result<(), Diagnostics> { let examples_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"); @@ -472,16 +470,12 @@ fn examples() -> Result<(), Diagnostics> { eprintln!("Testing {}", path.display()); let code = std::fs::read_to_string(path).map_err(|e| e.to_string())?; - let res = match do_parse_book_default(&code, path) { - Ok(book) => { - let compile_opts = CompileOpts::default(); - let diagnostics_cfg = DiagnosticsConfig::default(); - let (term, _, diags) = - run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); - format!("{diags}{term}") - } - Err(e) => e.to_string(), - }; + let book = parse_book_single_file(&code, path).unwrap(); + let compile_opts = CompileOpts::default(); + let diagnostics_cfg = DiagnosticsConfig::default(); + let (term, _, diags) = + run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); + let res = format!("{diags}{term}"); let mut settings = insta::Settings::clone_current(); settings.set_prepend_module_to_snapshot(false); @@ -496,10 +490,11 @@ fn examples() -> Result<(), Diagnostics> { Ok(()) } +/// Test that the Scott encoding correctly triggers unused definition warnings. #[test] fn scott_triggers_unused() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts::default(); let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Error, ..DiagnosticsConfig::default() }; @@ -509,10 +504,12 @@ fn scott_triggers_unused() { } // TODO: also run the long string file to test the readback +/// Compiles a file that is very large and takes a long time to compile. +/// Only outputs if compilation worked without errors. #[test] fn compile_long() { run_golden_test_dir(function_name!(), &|code, path| { - let mut book = do_parse_book_default(code, path)?; + let mut book = parse_book_single_file(code, path)?; let opts = CompileOpts::default().set_all(); let diagnostics_cfg = DiagnosticsConfig { recursion_cycle: Severity::Warning, diff --git a/tests/golden_tests/io/load.bend b/tests/golden_tests/io/load.bend index efc8e016a..85d1cde6f 100644 --- a/tests/golden_tests/io/load.bend +++ b/tests/golden_tests/io/load.bend @@ -1,6 +1,11 @@ -(Main) = +(Main): (IO (Result String u24)) = use path = "tests/golden_tests/io/load.txt" with IO { ask file = (IO/FS/read_file path) - (String/decode_utf8 file) + match file { + Result/Err: + (wrap (Result/Err (file.val))) + Result/Ok: + (wrap (Result/Ok (String/decode_utf8 file.val))) + } } diff --git a/tests/golden_tests/io/load_fail.bend b/tests/golden_tests/io/load_fail.bend index d43fccdd8..c10856c17 100644 --- a/tests/golden_tests/io/load_fail.bend +++ b/tests/golden_tests/io/load_fail.bend @@ -1,6 +1,11 @@ -(Main) = +(Main) : (IO (Result String u24)) = use path = "tests/golden_tests/io/missing_dir/load_fail.txt" with IO { ask file = (IO/FS/read_file path) - (String/decode_utf8 file) + match file { + Result/Err: + (wrap (Result/Err (file.val))) + Result/Ok: + (wrap (Result/Ok (String/decode_utf8 file.val))) + } } diff --git a/tests/golden_tests/io/read_line_eof.bend b/tests/golden_tests/io/read_line_eof.bend index 877b63308..a42f5ca95 100644 --- a/tests/golden_tests/io/read_line_eof.bend +++ b/tests/golden_tests/io/read_line_eof.bend @@ -1,6 +1,17 @@ -def main: +def main() -> IO(String): with IO: - fd <- IO/done_on_err(IO/FS/open("tests/golden_tests/io/eof.txt", "r")) - bytes <- IO/FS/read_line(fd) - txt = String/decode_utf8(bytes) - return wrap(txt) + fd <- IO/FS/open("tests/golden_tests/io/eof.txt", "r") + match fd: + case Result/Err: + return wrap(append("Err: ", u24/to_string(fd.val))) + case Result/Ok: + bytes <- IO/FS/read_line(fd.val) + match bytes: + case Result/Err: + return wrap(append("Err: ", u24/to_string(bytes.val))) + case Result/Ok: + txt = String/decode_utf8(bytes.val) + return wrap(txt) + +append (String/Nil) str = str +append (String/Cons x xs) str = (String/Cons x (append xs str)) diff --git a/tests/golden_tests/io/store.bend b/tests/golden_tests/io/store.bend index ca55eb508..7d50f08af 100644 --- a/tests/golden_tests/io/store.bend +++ b/tests/golden_tests/io/store.bend @@ -1,6 +1,5 @@ -(Main) = +(Main) : (IO (Result None u24)) = use path = "tests/golden_tests/io/store.txt" with IO { - ask res = (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) - res + (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) } diff --git a/tests/golden_tests/io/store_fail.bend b/tests/golden_tests/io/store_fail.bend index ef5b3086e..4743e087f 100644 --- a/tests/golden_tests/io/store_fail.bend +++ b/tests/golden_tests/io/store_fail.bend @@ -1,6 +1,5 @@ -(Main) = +(Main) : (IO (Result None u24)) = use path = "tests/golden_tests/io/missing_dir/store_fail.txt" with IO { - ask res = (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) - res + (IO/FS/write_file path (String/encode_utf8 "(Main) = 0")) } diff --git a/tests/golden_tests/parse_file/redefinition_with_type_between.bend b/tests/golden_tests/parse_file/redefinition_with_type_between.bend index 8dc6f0c42..682e19143 100644 --- a/tests/golden_tests/parse_file/redefinition_with_type_between.bend +++ b/tests/golden_tests/parse_file/redefinition_with_type_between.bend @@ -1,3 +1,3 @@ A = 0 -type B = B +type MyType = MyType A = 1 diff --git a/tests/golden_tests/run_file/checked_scott_encoding.bend b/tests/golden_tests/run_file/checked_scott_encoding.bend new file mode 100644 index 000000000..aa7bc2d6f --- /dev/null +++ b/tests/golden_tests/run_file/checked_scott_encoding.bend @@ -0,0 +1,5 @@ +checked (scott_concat a b) = (a + λh λt λcons λnil (cons h (scott_concat t b)) + b +) +main = scott_concat \ No newline at end of file diff --git a/tests/golden_tests/run_file/num_cast.bend b/tests/golden_tests/run_file/num_cast.bend index cd93d43dd..c86939127 100644 --- a/tests/golden_tests/run_file/num_cast.bend +++ b/tests/golden_tests/run_file/num_cast.bend @@ -1,48 +1,34 @@ -main = +main: _ = use inf = (** 9.0 (** 9.0 9.0)) ( - (to_u24 0), - (to_u24 1), - (to_u24 123456), - (to_u24 -1), - (to_u24 +1), - (to_u24 +0), - (to_u24 +400), - (to_u24 1.0), - (to_u24 1.5), - (to_u24 -3.0), - (to_u24 -3.5), - (to_u24 inf), # inf - (to_u24 (* -1.0 inf)), # -inf - (to_u24 (/ inf inf)), # nan (inf/inf) + (i24/to_u24 -1), + (i24/to_u24 +1), + (i24/to_u24 +0), + (i24/to_u24 +400), + (f24/to_u24 1.0), + (f24/to_u24 1.5), + (f24/to_u24 -3.0), + (f24/to_u24 -3.5), + (f24/to_u24 inf), # inf + (f24/to_u24 (* -1.0 inf)), # -inf + (f24/to_u24 (/ inf inf)), # nan (inf/inf) 99999, - (to_i24 0), - (to_i24 1), - (to_i24 123456), - (to_i24 -1), - (to_i24 +1), - (to_i24 +0), - (to_i24 +400), - (to_i24 1.0), - (to_i24 1.5), - (to_i24 -3.0), - (to_i24 -3.5), - (to_i24 inf), # inf - (to_i24 (* -1.0 inf)), # -inf - (to_i24 (/ inf inf)), # nan (inf/inf) + (u24/to_i24 0), + (u24/to_i24 1), + (u24/to_i24 123456), + (f24/to_i24 1.0), + (f24/to_i24 1.5), + (f24/to_i24 -3.0), + (f24/to_i24 -3.5), + (f24/to_i24 inf), # inf + (f24/to_i24 (* -1.0 inf)), # -inf + (f24/to_i24 (/ inf inf)), # nan (inf/inf) 99999, - (to_f24 0), - (to_f24 1), - (to_f24 123456), - (to_f24 -1), - (to_f24 +1), - (to_f24 +0), - (to_f24 +400), - (to_f24 1.0), - (to_f24 1.5), - (to_f24 -3.0), - (to_f24 -3.5), - (to_f24 inf), # inf - (to_f24 (* -1.0 inf)), # -inf - (to_f24 (/ inf inf)) # nan (inf/inf) + (u24/to_f24 0), + (u24/to_f24 1), + (u24/to_f24 123456), + (i24/to_f24 -1), + (i24/to_f24 +1), + (i24/to_f24 +0), + (i24/to_f24 +400) ) \ No newline at end of file diff --git a/tests/golden_tests/run_file/superposed_is_even.bend b/tests/golden_tests/run_file/superposed_is_even.bend index 2f2a4c2d4..6fb60e0b0 100644 --- a/tests/golden_tests/run_file/superposed_is_even.bend +++ b/tests/golden_tests/run_file/superposed_is_even.bend @@ -1,18 +1,18 @@ -type N = (S pred) | Z -type B = T | F +type MyNat = (S pred) | Z +type MyBool = T | F -(Not B/T) = B/F -(Not B/F) = B/T +(Not MyBool/T) = MyBool/F +(Not MyBool/F) = MyBool/T (IsEven n) = match n { - N/S: (Not (IsEven n.pred)) - N/Z: B/T + MyNat/S: (Not (IsEven n.pred)) + MyNat/Z: MyBool/T } -N0 = N/Z -N1 = (N/S N0) -N2 = (N/S N1) -N3 = (N/S N2) +N0 = MyNat/Z +N1 = (MyNat/S N0) +N2 = (MyNat/S N1) +N3 = (MyNat/S N2) Main = (IsEven {{N0 N1} {N2 N3}}) diff --git a/tests/snapshots/cli__compile_pre_reduce.bend.snap b/tests/snapshots/cli__compile_pre_reduce.bend.snap index 35e3536c3..b5b6cf12e 100644 --- a/tests/snapshots/cli__compile_pre_reduce.bend.snap +++ b/tests/snapshots/cli__compile_pre_reduce.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/compile_pre_reduce.bend --- error: invalid value 'pre-reduce' for '-O ' - [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott] + [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott, type-check, no-type-check] For more information, try '--help'. diff --git a/tests/snapshots/cli__compile_wrong_opt.bend.snap b/tests/snapshots/cli__compile_wrong_opt.bend.snap index 947d8daf7..58742bb05 100644 --- a/tests/snapshots/cli__compile_wrong_opt.bend.snap +++ b/tests/snapshots/cli__compile_wrong_opt.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/compile_wrong_opt.bend --- error: invalid value 'foo' for '-O ' - [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott] + [possible values: all, no-all, eta, no-eta, prune, no-prune, linearize-matches, linearize-matches-alt, no-linearize-matches, float-combinators, no-float-combinators, merge, no-merge, inline, no-inline, check-net-size, no-check-net-size, adt-scott, adt-num-scott, type-check, no-type-check] tip: a similar value exists: 'float-combinators' diff --git a/tests/snapshots/cli__desugar_bool_scott.bend.snap b/tests/snapshots/cli__desugar_bool_scott.bend.snap index 713942c37..7052bb85c 100644 --- a/tests/snapshots/cli__desugar_bool_scott.bend.snap +++ b/tests/snapshots/cli__desugar_bool_scott.bend.snap @@ -2,8 +2,11 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_bool_scott.bend --- +unchecked main: Any (main) = λa λ* a +Boolean/True: Boolean (Boolean/True) = λa λ* a +Boolean/False: Boolean (Boolean/False) = λ* λa a diff --git a/tests/snapshots/cli__desugar_float_combinators.bend.snap b/tests/snapshots/cli__desugar_float_combinators.bend.snap index 5f3bf79b8..e65464916 100644 --- a/tests/snapshots/cli__desugar_float_combinators.bend.snap +++ b/tests/snapshots/cli__desugar_float_combinators.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_float_combinators.bend --- +unchecked Z: Any (Z) = λ* λa a +unchecked S: Any (S) = λa λb let {c d} = b; λe (c (a d e)) +unchecked get: Any (get) = λa (a get__C0 0) +unchecked main: Any (main) = (get (S (S Z))) +unchecked get__C0: _ (get__C0) = λa (+ a 1) diff --git a/tests/snapshots/cli__desugar_linearize_matches.bend.snap b/tests/snapshots/cli__desugar_linearize_matches.bend.snap index b55ac394b..6ce94cb07 100644 --- a/tests/snapshots/cli__desugar_linearize_matches.bend.snap +++ b/tests/snapshots/cli__desugar_linearize_matches.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_linearize_matches.bend --- +unchecked main: Any (main) = λa λb λc (switch a { 0: λd λ* d; _: λ* λ* λe e; } b c) diff --git a/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap b/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap index c84d47536..dc83c7fec 100644 --- a/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap +++ b/tests/snapshots/cli__desugar_linearize_matches_alt.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_linearize_matches_alt.bend --- +unchecked main: Any (main) = λa switch a { 0: λb b; _: λ* λc c; } diff --git a/tests/snapshots/cli__desugar_merge.bend.snap b/tests/snapshots/cli__desugar_merge.bend.snap index 3df1b698b..784d18150 100644 --- a/tests/snapshots/cli__desugar_merge.bend.snap +++ b/tests/snapshots/cli__desugar_merge.bend.snap @@ -7,6 +7,8 @@ input_file: tests/golden_tests/cli/desugar_merge.bend In definition 'Z': Definition is unused. +unchecked F__M_Z: _ (F__M_Z) = λ* λa a +unchecked main: Any (main) = λ* λa a diff --git a/tests/snapshots/cli__desugar_prune.bend.snap b/tests/snapshots/cli__desugar_prune.bend.snap index c136f21bb..a228dd463 100644 --- a/tests/snapshots/cli__desugar_prune.bend.snap +++ b/tests/snapshots/cli__desugar_prune.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_prune.bend --- +unchecked main: Any (main) = * diff --git a/tests/snapshots/compile_file__error_data_def_name.bend.snap b/tests/snapshots/compile_file__error_data_def_name.bend.snap index cd3ddfcf8..b4c30720c 100644 --- a/tests/snapshots/compile_file__error_data_def_name.bend.snap +++ b/tests/snapshots/compile_file__error_data_def_name.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/compile_file/error_data_def_name.bend Errors: In tests/golden_tests/compile_file/error_data_def_name.bend : Redefinition of constructor 'A/A'. +Location:  2 | A/A = 0 diff --git a/tests/snapshots/compile_file__just_data.bend.snap b/tests/snapshots/compile_file__just_data.bend.snap index e8c8a198d..937857d9c 100644 --- a/tests/snapshots/compile_file__just_data.bend.snap +++ b/tests/snapshots/compile_file__just_data.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/just_data.bend --- Errors: In tests/golden_tests/compile_file/just_data.bend : -- expected: datatype name +- expected: Datatype name - detected: end of input  1 | type  diff --git a/tests/snapshots/compile_file__just_paren.bend.snap b/tests/snapshots/compile_file__just_paren.bend.snap index 473cdde5a..1a36d8623 100644 --- a/tests/snapshots/compile_file__just_paren.bend.snap +++ b/tests/snapshots/compile_file__just_paren.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/just_paren.bend --- Errors: In tests/golden_tests/compile_file/just_paren.bend : -- expected: function name +- expected: Function name - detected: end of input  2 | (  diff --git a/tests/snapshots/compile_file__missing_adt_eq.bend.snap b/tests/snapshots/compile_file__missing_adt_eq.bend.snap index d73423579..508bc39ad 100644 --- a/tests/snapshots/compile_file__missing_adt_eq.bend.snap +++ b/tests/snapshots/compile_file__missing_adt_eq.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/missing_adt_eq.bend --- Errors: In tests/golden_tests/compile_file/missing_adt_eq.bend : -- expected: '=' +- expected: Type variable or '=' - detected: end of input  1 | type Adt  diff --git a/tests/snapshots/compile_file__missing_ctrs.bend.snap b/tests/snapshots/compile_file__missing_ctrs.bend.snap index 820876baa..1ecd93d7d 100644 --- a/tests/snapshots/compile_file__missing_ctrs.bend.snap +++ b/tests/snapshots/compile_file__missing_ctrs.bend.snap @@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/missing_ctrs.bend --- Errors: In tests/golden_tests/compile_file/missing_ctrs.bend : -- expected: datatype constructor name +- expected: Datatype constructor name - detected: end of input  1 | type Adt =  diff --git a/tests/snapshots/compile_file__number_too_large.bend.snap b/tests/snapshots/compile_file__number_too_large.bend.snap index fefe7c825..04ec3558d 100644 --- a/tests/snapshots/compile_file__number_too_large.bend.snap +++ b/tests/snapshots/compile_file__number_too_large.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/compile_file/number_too_large.bend Errors: In tests/golden_tests/compile_file/number_too_large.bend : Number literal outside of range for U24. +Location: end of input  1 | main = 0x10000000 diff --git a/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap b/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap index 671f5a591..56239fdbb 100644 --- a/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap +++ b/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/compile_file/top_level_name_slashslash.bend Errors: In tests/golden_tests/compile_file/top_level_name_slashslash.bend : Top-level names are not allowed to start with "//". +Location:  4 | def //thisshouldfail(): diff --git a/tests/snapshots/compile_file_o_all__adt_string.bend.snap b/tests/snapshots/compile_file_o_all__adt_string.bend.snap index c491410d8..0738e2dc5 100644 --- a/tests/snapshots/compile_file_o_all__adt_string.bend.snap +++ b/tests/snapshots/compile_file_o_all__adt_string.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/compile_file_o_all/adt_string.bend Errors: In tests/golden_tests/compile_file_o_all/adt_string.bend : Redefinition of builtin (type) 'String'. +Location:  1 | type String = S  2 |   3 | main = String/S diff --git a/tests/snapshots/desugar_file__ask_branch.bend.snap b/tests/snapshots/desugar_file__ask_branch.bend.snap index c6a5750a3..dc7177059 100644 --- a/tests/snapshots/desugar_file__ask_branch.bend.snap +++ b/tests/snapshots/desugar_file__ask_branch.bend.snap @@ -2,34 +2,50 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/ask_branch.bend --- -(undefer) = λa (a λb b) - +IO/MAGIC: (u24, u24) (IO/MAGIC) = (13683217, 16719857) +IO/wrap: (a -> (IO a)) (IO/wrap) = λa (IO/Done IO/MAGIC a) +IO/bind: ((IO a) -> ((c -> c) -> a -> (IO b)) -> (IO b)) (IO/bind) = λa λb (a IO/bind__C2 b) +undefer: (((a -> a) -> b) -> b) +(undefer) = λa (a λb b) + +unchecked main: Any (main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc λd (c d) IO/wrap)) +IO/Done: ((u24, u24) -> a -> (IO a)) (IO/Done) = λa λb λc (c IO/Done/tag a b) +IO/Call: ((u24, u24) -> String -> Any -> ((Result Any (IOError Any)) -> (IO a)) -> (IO a)) (IO/Call) = λa λb λc λd λe (e IO/Call/tag a b c d) +Bool/T: Bool (Bool/T) = λa (a Bool/T/tag) +Bool/F: Bool (Bool/F) = λa (a Bool/F/tag) +IO/Done/tag: u24 (IO/Done/tag) = 0 +IO/Call/tag: u24 (IO/Call/tag) = 1 +Bool/T/tag: u24 (Bool/T/tag) = 0 +Bool/F/tag: u24 (Bool/F/tag) = 1 +IO/bind__C0: _ (IO/bind__C0) = λ* λa λb (undefer b a) -(IO/bind__C1) = λ* λ* λa λb λc λd (IO/Call IO/MAGIC a b λe (IO/bind (c e) d)) +IO/bind__C1: _ +(IO/bind__C1) = λ* λa λb λc λd λe (IO/Call a b c λf (IO/bind (d f) e)) +IO/bind__C2: _ (IO/bind__C2) = λa switch a { 0: IO/bind__C0; _: IO/bind__C1; } diff --git a/tests/snapshots/desugar_file__bind_syntax.bend.snap b/tests/snapshots/desugar_file__bind_syntax.bend.snap index eb850cbe9..55b6fd2b7 100644 --- a/tests/snapshots/desugar_file__bind_syntax.bend.snap +++ b/tests/snapshots/desugar_file__bind_syntax.bend.snap @@ -2,38 +2,56 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/bind_syntax.bend --- -(undefer) = λa (a λb b) - +unchecked Result/bind: Any (Result/bind) = λa λb (a Result/bind__C2 b) +unchecked safe_div: Any (safe_div) = λa λb (switch b { 0: λ* (Result/Err (String/Cons 68 (String/Cons 105 (String/Cons 118 (String/Cons 32 (String/Cons 98 (String/Cons 121 (String/Cons 32 (String/Cons 48 String/Nil))))))))); _: safe_div__C0; } a) +unchecked safe_rem: Any (safe_rem) = λa λb (switch b { 0: λ* (Result/Err (String/Cons 77 (String/Cons 111 (String/Cons 100 (String/Cons 32 (String/Cons 98 (String/Cons 121 (String/Cons 32 (String/Cons 48 String/Nil))))))))); _: safe_rem__C0; } a) +unchecked Main: Any (Main) = (Result/bind (safe_div 3 2) λa (a λb (Result/bind (safe_rem b 0) λc (c λd d)))) +undefer: (((a -> a) -> b) -> b) +(undefer) = λa (a λb b) + +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) +Result/Ok: (b -> (Result b a)) (Result/Ok) = λa λb (b Result/Ok/tag a) +Result/Err: (a -> (Result b a)) (Result/Err) = λa λb (b Result/Err/tag a) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 +Result/Ok/tag: u24 (Result/Ok/tag) = 0 +Result/Err/tag: u24 (Result/Err/tag) = 1 +unchecked Result/bind__C0: _ (Result/bind__C0) = λa λb (undefer b a) +unchecked Result/bind__C1: _ (Result/bind__C1) = λ* λa λ* (Result/Err a) +unchecked Result/bind__C2: _ (Result/bind__C2) = λa switch a { 0: Result/bind__C0; _: Result/bind__C1; } +unchecked safe_div__C0: _ (safe_div__C0) = λa λb (Result/Ok (/ b (+ a 1))) +unchecked safe_rem__C0: _ (safe_rem__C0) = λa λb (Result/Ok (% b (+ a 1))) diff --git a/tests/snapshots/desugar_file__combinators.bend.snap b/tests/snapshots/desugar_file__combinators.bend.snap index 58ce05b16..e261f6ac9 100644 --- a/tests/snapshots/desugar_file__combinators.bend.snap +++ b/tests/snapshots/desugar_file__combinators.bend.snap @@ -2,44 +2,65 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/combinators.bend --- +unchecked foo: Any (foo) = λa λ* λ* (foo a) +unchecked bar: Any (bar) = λa λb (a bar b) +unchecked List/ignore: Any (List/ignore) = λa λ* (a List/ignore__C1) +unchecked baz: Any (baz) = {0 1 2 3 λa a foo} +unchecked qux: Any (qux) = {0 qux} +unchecked clax: Any (clax) = (λa a clax__C0) +unchecked tup: Any (tup) = (tup, 1, 0) +unchecked list: Any (list) = (List/Cons 0 list__C0) +unchecked A: Any (A) = λa (A__C0 a) +unchecked B: Any (B) = λa (B__C0 a) +unchecked Main: Any (Main) = (List/Cons 0 (List/Cons list List/Nil)) +List/Nil: (List a) (List/Nil) = λa (a List/Nil/tag) +List/Cons: (a -> (List a) -> (List a)) (List/Cons) = λa λb λc (c List/Cons/tag a b) +List/Nil/tag: u24 (List/Nil/tag) = 0 +List/Cons/tag: u24 (List/Cons/tag) = 1 +unchecked A__C0: _ (A__C0) = let {a b} = A; λc (a b c) +unchecked B__C0: _ (B__C0) = let (a, b) = B; λc (a b c) +unchecked List/ignore__C0: _ (List/ignore__C0) = λ* λ* λa (List/ignore a List/ignore) +unchecked List/ignore__C1: _ (List/ignore__C1) = λa switch a { 0: 0; _: List/ignore__C0; } +unchecked clax__C0: _ (clax__C0) = λ* λ* λ* λa (clax a) +unchecked list__C0: _ (list__C0) = (List/Cons list List/Nil) diff --git a/tests/snapshots/desugar_file__deref_loop.bend.snap b/tests/snapshots/desugar_file__deref_loop.bend.snap index 4cedbf623..33630f986 100644 --- a/tests/snapshots/desugar_file__deref_loop.bend.snap +++ b/tests/snapshots/desugar_file__deref_loop.bend.snap @@ -2,20 +2,29 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/deref_loop.bend --- +unchecked foo: Any (foo) = λa (a foo__C1) +unchecked bar: Any (bar) = (foo 1) +unchecked main: Any (main) = (foo 0) +nat/succ: (Any -> nat) (nat/succ) = λa λb (b nat/succ/tag a) +nat/zero: nat (nat/zero) = λa (a nat/zero/tag) +nat/succ/tag: u24 (nat/succ/tag) = 0 +nat/zero/tag: u24 (nat/zero/tag) = 1 +unchecked foo__C0: _ (foo__C0) = λ* (bar 0) +unchecked foo__C1: _ (foo__C1) = λa switch a { 0: λb b; _: foo__C0; } diff --git a/tests/snapshots/desugar_file__dup_linearization.bend.snap b/tests/snapshots/desugar_file__dup_linearization.bend.snap index 7aaacbb2b..ac3a8441d 100644 --- a/tests/snapshots/desugar_file__dup_linearization.bend.snap +++ b/tests/snapshots/desugar_file__dup_linearization.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/dup_linearization.bend --- +unchecked main: Any (main) = let {a b c d e} = *; (a, e, d, c, b) diff --git a/tests/snapshots/desugar_file__local_def_shadow.bend.snap b/tests/snapshots/desugar_file__local_def_shadow.bend.snap index 551da5ee9..95bde9e9a 100644 --- a/tests/snapshots/desugar_file__local_def_shadow.bend.snap +++ b/tests/snapshots/desugar_file__local_def_shadow.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/local_def_shadow.bend --- +unchecked main: Any (main) = 1 +unchecked main__local_0_A__local_0_B: _ (main__local_0_A__local_0_B) = 0 +unchecked main__local_1_A__local_1_B: _ (main__local_1_A__local_1_B) = 1 +unchecked main__local_1_A: _ (main__local_1_A) = main__local_1_A__local_1_B +unchecked main__local_0_A: _ (main__local_0_A) = main__local_0_A__local_0_B diff --git a/tests/snapshots/desugar_file__main_aux.bend.snap b/tests/snapshots/desugar_file__main_aux.bend.snap index 4de950351..cfa74850c 100644 --- a/tests/snapshots/desugar_file__main_aux.bend.snap +++ b/tests/snapshots/desugar_file__main_aux.bend.snap @@ -2,10 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/main_aux.bend --- +unchecked main: Any (main) = (main__local_0_aux 89 2) +unchecked main__local_0_aux__local_0_aux__local_0_aux: _ (main__local_0_aux__local_0_aux__local_0_aux) = λa λb (+ b a) +unchecked main__local_0_aux__local_0_aux: _ (main__local_0_aux__local_0_aux) = λa λb (main__local_0_aux__local_0_aux__local_0_aux a b) +unchecked main__local_0_aux: _ (main__local_0_aux) = λa λb (main__local_0_aux__local_0_aux a b) diff --git a/tests/snapshots/desugar_file__mapper_syntax.bend.snap b/tests/snapshots/desugar_file__mapper_syntax.bend.snap index 4423c04ff..060499a74 100644 --- a/tests/snapshots/desugar_file__mapper_syntax.bend.snap +++ b/tests/snapshots/desugar_file__mapper_syntax.bend.snap @@ -2,66 +2,101 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/mapper_syntax.bend --- +Map/empty: (Map a) (Map/empty) = Map/Leaf +Map/get: ((Map a) -> u24 -> (a, (Map a))) (Map/get) = λa λb (a Map/get__C5 b) +Map/set: ((Map a) -> u24 -> a -> (Map a)) (Map/set) = λa λb λc (a Map/set__C10 b c) +Map/map: ((Map a) -> u24 -> (a -> a) -> (Map a)) (Map/map) = λa λb λc (a Map/map__C5 b c) +unreachable: Any +(unreachable) = * + +unchecked main: Any (main) = let (c, d) = (Map/get (Map/map (Map/map (Map/set (Map/set Map/empty 0 3) 1 4) 1 λa (+ a 1)) 1 λb (* b 2)) 1); let (e, *) = (Map/get d 0); ((λf (+ f 1) 1), c, e) +Map/Node: (a -> (Map a) -> (Map a) -> (Map a)) (Map/Node) = λa λb λc λd (d Map/Node/tag a b c) +Map/Leaf: (Map a) (Map/Leaf) = λa (a Map/Leaf/tag) +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 +Map/get__C0: _ (Map/get__C0) = λa λb λc λd let (e, f) = (Map/get c (/ a 2)); (e, (Map/Node b f d)) +Map/get__C1: _ (Map/get__C1) = λ* λa λb λc λd let (e, f) = (Map/get d (/ a 2)); (e, (Map/Node b c f)) +Map/get__C2: _ (Map/get__C2) = λa let {b c} = a; λd λe λf (switch (% b 2) { 0: Map/get__C0; _: Map/get__C1; } c d e f) +Map/get__C3: _ (Map/get__C3) = λ* λ* λa let {b c} = a; λd λe (b, (Map/Node c d e)) +Map/get__C4: _ (Map/get__C4) = λa λb λc λd let {e f} = d; (switch (== 0 e) { 0: Map/get__C2; _: Map/get__C3; } f a b c) -(Map/get__C5) = λa switch a { 0: Map/get__C4; _: λ* λ* (*, Map/Leaf); } +Map/get__C5: _ +(Map/get__C5) = λa switch a { 0: Map/get__C4; _: λ* λ* (unreachable, Map/Leaf); } +Map/map__C0: _ (Map/map__C0) = λa λb λc λd λe (Map/Node c (Map/map d (/ a 2) b) e) +Map/map__C1: _ (Map/map__C1) = λ* λa λb λc λd λe (Map/Node c d (Map/map e (/ a 2) b)) +Map/map__C2: _ (Map/map__C2) = λa let {b c} = a; λd λe λf λg (switch (% b 2) { 0: Map/map__C0; _: Map/map__C1; } c d e f g) +Map/map__C3: _ (Map/map__C3) = λ* λ* λa λb λc λd (Map/Node (a b) c d) +Map/map__C4: _ (Map/map__C4) = λa λb λc λd let {e f} = d; λg (switch (== 0 e) { 0: Map/map__C2; _: Map/map__C3; } f g a b c) +Map/map__C5: _ (Map/map__C5) = λa switch a { 0: Map/map__C4; _: λ* λ* λ* Map/Leaf; } +Map/set__C0: _ (Map/set__C0) = λa λb λc λd λe (Map/Node c (Map/set d (/ a 2) b) e) +Map/set__C1: _ (Map/set__C1) = λ* λa λb λc λd λe (Map/Node c d (Map/set e (/ a 2) b)) +Map/set__C10: _ (Map/set__C10) = λa switch a { 0: Map/set__C8; _: Map/set__C9; } +Map/set__C2: _ (Map/set__C2) = λa let {b c} = a; λd λe λf λg (switch (% b 2) { 0: Map/set__C0; _: Map/set__C1; } c d e f g) +Map/set__C3: _ (Map/set__C3) = λ* λ* λa λ* λb λc (Map/Node a b c) -(Map/set__C4) = λa λb (Map/Node * (Map/set Map/Leaf (/ a 2) b) Map/Leaf) +Map/set__C4: _ +(Map/set__C4) = λa λb (Map/Node unreachable (Map/set Map/Leaf (/ a 2) b) Map/Leaf) -(Map/set__C5) = λ* λa λb (Map/Node * Map/Leaf (Map/set Map/Leaf (/ a 2) b)) +Map/set__C5: _ +(Map/set__C5) = λ* λa λb (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ a 2) b)) +Map/set__C6: _ (Map/set__C6) = λa let {b c} = a; λd (switch (% b 2) { 0: Map/set__C4; _: Map/set__C5; } c d) +Map/set__C7: _ (Map/set__C7) = λ* λ* λa (Map/Node a Map/Leaf Map/Leaf) +Map/set__C8: _ (Map/set__C8) = λa λb λc λd let {e f} = d; λg (switch (== 0 e) { 0: Map/set__C2; _: Map/set__C3; } f g a b c) +Map/set__C9: _ (Map/set__C9) = λ* λa let {b c} = a; λd (switch (== 0 b) { 0: Map/set__C6; _: Map/set__C7; } c d) diff --git a/tests/snapshots/desugar_file__switch_with_use.bend.snap b/tests/snapshots/desugar_file__switch_with_use.bend.snap index c565c0ade..2a7b24b60 100644 --- a/tests/snapshots/desugar_file__switch_with_use.bend.snap +++ b/tests/snapshots/desugar_file__switch_with_use.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/switch_with_use.bend --- +unchecked main: Any (main) = λa λb λc λ* λ* (switch c { 0: λd d; _: λe λf (e f); } (a b)) diff --git a/tests/snapshots/desugar_file__tree_syntax.bend.snap b/tests/snapshots/desugar_file__tree_syntax.bend.snap index 12d0870e6..ac61c380b 100644 --- a/tests/snapshots/desugar_file__tree_syntax.bend.snap +++ b/tests/snapshots/desugar_file__tree_syntax.bend.snap @@ -2,36 +2,53 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/tree_syntax.bend --- +unchecked fun0: Any (fun0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3)))) +unchecked fun1: Any (fun1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *))) +unchecked fun2: Any (fun2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *))) +unchecked fun3: Any (fun3) = (Tree/Leaf 1) +unchecked fun4: Any (fun4) = λa switch a { 0: (Tree/Leaf 0); _: fun4__C0; } +unchecked main: Any (main) = * +unchecked imp0: Any (imp0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3)))) +unchecked imp1: Any (imp1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *))) +unchecked imp2: Any (imp2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *))) +unchecked imp3: Any (imp3) = (Tree/Leaf 1) +unchecked imp4: (Any -> Any) (imp4) = λa switch a { 0: (Tree/Leaf 0); _: imp4__C0; } +Tree/Node: ((Tree a) -> (Tree a) -> (Tree a)) (Tree/Node) = λa λb λc (c Tree/Node/tag a b) +Tree/Leaf: (a -> (Tree a)) (Tree/Leaf) = λa λb (b Tree/Leaf/tag a) +Tree/Node/tag: u24 (Tree/Node/tag) = 0 +Tree/Leaf/tag: u24 (Tree/Leaf/tag) = 1 +unchecked fun4__C0: _ (fun4__C0) = λa let {b c} = a; (Tree/Node (fun4 b) (fun4 c)) +unchecked imp4__C0: _ (imp4__C0) = λa let {b c} = a; (Tree/Node (imp4 b) (imp4 c)) diff --git a/tests/snapshots/desugar_file__use_id.bend.snap b/tests/snapshots/desugar_file__use_id.bend.snap index 903fd3977..73bce83c7 100644 --- a/tests/snapshots/desugar_file__use_id.bend.snap +++ b/tests/snapshots/desugar_file__use_id.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/use_id.bend --- +unchecked Main: Any (Main) = (λa a 2 3 (λb b 2 3) (λc c 2 3 (λd d 2 3))) diff --git a/tests/snapshots/desugar_file__use_shadow.bend.snap b/tests/snapshots/desugar_file__use_shadow.bend.snap index c69f8530c..3bfce84dd 100644 --- a/tests/snapshots/desugar_file__use_shadow.bend.snap +++ b/tests/snapshots/desugar_file__use_shadow.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/use_shadow.bend --- +unchecked main: Any (main) = λa let {b c} = a; λd (b c d) diff --git a/tests/snapshots/desugar_file__used_once_names.bend.snap b/tests/snapshots/desugar_file__used_once_names.bend.snap index 813ec54aa..904a4ea19 100644 --- a/tests/snapshots/desugar_file__used_once_names.bend.snap +++ b/tests/snapshots/desugar_file__used_once_names.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/used_once_names.bend --- +unchecked foo: Any (foo) = λa λb λc let {d e} = c; (a b (d e)) +unchecked main: Any (main) = (foo 2 3 λa a) diff --git a/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap b/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap index 4ccd0b476..f1125de0e 100644 --- a/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap +++ b/tests/snapshots/encode_pattern_match__adt_tup_era.bend.snap @@ -3,17 +3,24 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/adt_tup_era.bend --- Scott +unchecked Foo: Any (Foo) = λa (a λb λc (b λd λ* λ* d c)) +unchecked Main: Any (Main) = (Foo (Tuple/Pair 1 5)) +Tuple/Pair: (Any -> Any -> Tuple) (Tuple/Pair) = λa λb λc (c a b) NumScott +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: λc λd (c λe switch e { 0: λf λ* λ* f; _: *; } d); _: *; }) +unchecked Main: Any (Main) = (Foo (Tuple/Pair 1 5)) +Tuple/Pair: (Any -> Any -> Tuple) (Tuple/Pair) = λa λb λc (c Tuple/Pair/tag a b) +Tuple/Pair/tag: u24 (Tuple/Pair/tag) = 0 diff --git a/tests/snapshots/encode_pattern_match__and3.bend.snap b/tests/snapshots/encode_pattern_match__and3.bend.snap index 14ca7da2c..372d1ec55 100644 --- a/tests/snapshots/encode_pattern_match__and3.bend.snap +++ b/tests/snapshots/encode_pattern_match__and3.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/and3.bend --- Scott +unchecked And: Any (And) = λa let (b, c, d) = a; (b λe λf (e λg (g Bool/T Bool/F) λ* Bool/F f) λ* λ* Bool/F c d) +unchecked main: Any (main) = (And (Bool/F, Bool/T, Bool/F)) +Bool/T: Bool (Bool/T) = λa λ* a +Bool/F: Bool (Bool/F) = λ* λb b NumScott +unchecked And: Any (And) = λa let (b, c, d) = a; (b λe switch e { 0: λf λg (f λh switch h { 0: λi (i λj switch j { 0: Bool/T; _: λ* Bool/F; }); _: λ* λ* Bool/F; } g); _: λ* λ* λ* Bool/F; } c d) +unchecked main: Any (main) = (And (Bool/F, Bool/T, Bool/F)) +Bool/T: Bool (Bool/T) = λa (a Bool/T/tag) +Bool/F: Bool (Bool/F) = λa (a Bool/F/tag) +Bool/T/tag: u24 (Bool/T/tag) = 0 +Bool/F/tag: u24 (Bool/F/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__bool.bend.snap b/tests/snapshots/encode_pattern_match__bool.bend.snap index 57db838ae..fe60384ad 100644 --- a/tests/snapshots/encode_pattern_match__bool.bend.snap +++ b/tests/snapshots/encode_pattern_match__bool.bend.snap @@ -3,35 +3,51 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/bool.bend --- Scott +unchecked not: Any (not) = λa (a bool/false bool/true) +unchecked and: Any (and) = λa (a λb (b bool/true bool/false) λd (d bool/false bool/false)) +unchecked and2: Any (and2) = λa (a λb b λd let * = d; bool/false) +unchecked and3: Any (and3) = λa (a λb (b bool/true bool/false) λd let * = d; bool/false) +unchecked and4: Any (and4) = λa (a λb (b bool/true bool/false) λd let * = d; bool/false) +bool/true: bool (bool/true) = λa λ* a +bool/false: bool (bool/false) = λ* λb b NumScott +unchecked not: Any (not) = λa (a λb switch b { 0: bool/false; _: λ* bool/true; }) +unchecked and: Any (and) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf (f λh switch h { 0: bool/false; _: λ* bool/false; }); }) +unchecked and2: Any (and2) = λa (a λb switch b { 0: λc c; _: λ* λe let * = e; bool/false; }) +unchecked and3: Any (and3) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf let * = f; bool/false; }) +unchecked and4: Any (and4) = λa (a λb switch b { 0: λc (c λe switch e { 0: bool/true; _: λ* bool/false; }); _: λ* λf let * = f; bool/false; }) +bool/true: bool (bool/true) = λa (a bool/true/tag) +bool/false: bool (bool/false) = λa (a bool/false/tag) +bool/true/tag: u24 (bool/true/tag) = 0 +bool/false/tag: u24 (bool/false/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__bool_tup.bend.snap b/tests/snapshots/encode_pattern_match__bool_tup.bend.snap index c8b2e15ba..ce956fc53 100644 --- a/tests/snapshots/encode_pattern_match__bool_tup.bend.snap +++ b/tests/snapshots/encode_pattern_match__bool_tup.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/bool_tup.bend --- Scott +unchecked foo: Any (foo) = λa let (b, c) = a; (b λd d λ* Bool/F c) +unchecked main: Any (main) = (foo (Bool/F, Bool/T)) +Bool/T: Bool (Bool/T) = λa λ* a +Bool/F: Bool (Bool/F) = λ* λb b NumScott +unchecked foo: Any (foo) = λa let (b, c) = a; (b λd switch d { 0: λe e; _: λ* λ* Bool/F; } c) +unchecked main: Any (main) = (foo (Bool/F, Bool/T)) +Bool/T: Bool (Bool/T) = λa (a Bool/T/tag) +Bool/F: Bool (Bool/F) = λa (a Bool/F/tag) +Bool/T/tag: u24 (Bool/T/tag) = 0 +Bool/F/tag: u24 (Bool/F/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__box.bend.snap b/tests/snapshots/encode_pattern_match__box.bend.snap index 02f4208b3..214ba620e 100644 --- a/tests/snapshots/encode_pattern_match__box.bend.snap +++ b/tests/snapshots/encode_pattern_match__box.bend.snap @@ -3,13 +3,18 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/box.bend --- Scott +unchecked unbox: Any (unbox) = λa (a λb b) +box/new: (Any -> box) (box/new) = λa λb (b a) NumScott +unchecked unbox: Any (unbox) = λa (a λb switch b { 0: λc c; _: *; }) +box/new: (Any -> box) (box/new) = λa λb (b box/new/tag a) +box/new/tag: u24 (box/new/tag) = 0 diff --git a/tests/snapshots/encode_pattern_match__common.bend.snap b/tests/snapshots/encode_pattern_match__common.bend.snap index 802b979f2..2b9fb1647 100644 --- a/tests/snapshots/encode_pattern_match__common.bend.snap +++ b/tests/snapshots/encode_pattern_match__common.bend.snap @@ -3,105 +3,156 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/common.bend --- Scott +Box/Filled: (Any -> Box) (Box/Filled) = λa λb λ* (b a) +Box/Empty: Box (Box/Empty) = λ* λb b +Option/Some: (Any -> Option) (Option/Some) = λa λb λ* (b a) +Option/None: Option (Option/None) = λ* λb b +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb λ* (b a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λ* λc (c a) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc λ* (c a b) +List_/Nil: List_ (List_/Nil) = λ* λb b +Bool/True: Bool (Bool/True) = λa λ* a +Bool/False: Bool (Bool/False) = λ* λb b +Light/Red: Light (Light/Red) = λa λ* λ* a +Light/Yellow: Light (Light/Yellow) = λ* λb λ* b +Light/Green: Light (Light/Green) = λ* λ* λc c +Direction/North: Direction (Direction/North) = λa λ* λ* λ* a +Direction/South: Direction (Direction/South) = λ* λb λ* λ* b +Direction/East: Direction (Direction/East) = λ* λ* λc λ* c +Direction/West: Direction (Direction/West) = λ* λ* λ* λd d NumScott +Box/Filled: (Any -> Box) (Box/Filled) = λa λb (b Box/Filled/tag a) +Box/Empty: Box (Box/Empty) = λa (a Box/Empty/tag) +Option/Some: (Any -> Option) (Option/Some) = λa λb (b Option/Some/tag a) +Option/None: Option (Option/None) = λa (a Option/None/tag) +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb (b Result_/Ok/tag a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λb (b Result_/Err/tag a) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) +List_/Nil: List_ (List_/Nil) = λa (a List_/Nil/tag) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +Light/Red: Light (Light/Red) = λa (a Light/Red/tag) +Light/Yellow: Light (Light/Yellow) = λa (a Light/Yellow/tag) +Light/Green: Light (Light/Green) = λa (a Light/Green/tag) +Direction/North: Direction (Direction/North) = λa (a Direction/North/tag) +Direction/South: Direction (Direction/South) = λa (a Direction/South/tag) +Direction/East: Direction (Direction/East) = λa (a Direction/East/tag) +Direction/West: Direction (Direction/West) = λa (a Direction/West/tag) +Box/Filled/tag: u24 (Box/Filled/tag) = 0 +Box/Empty/tag: u24 (Box/Empty/tag) = 1 +Option/Some/tag: u24 (Option/Some/tag) = 0 +Option/None/tag: u24 (Option/None/tag) = 1 +Result_/Ok/tag: u24 (Result_/Ok/tag) = 0 +Result_/Err/tag: u24 (Result_/Err/tag) = 1 +List_/Cons/tag: u24 (List_/Cons/tag) = 0 +List_/Nil/tag: u24 (List_/Nil/tag) = 1 +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 +Light/Red/tag: u24 (Light/Red/tag) = 0 +Light/Yellow/tag: u24 (Light/Yellow/tag) = 1 +Light/Green/tag: u24 (Light/Green/tag) = 2 +Direction/North/tag: u24 (Direction/North/tag) = 0 +Direction/South/tag: u24 (Direction/South/tag) = 1 +Direction/East/tag: u24 (Direction/East/tag) = 2 +Direction/West/tag: u24 (Direction/West/tag) = 3 diff --git a/tests/snapshots/encode_pattern_match__concat.bend.snap b/tests/snapshots/encode_pattern_match__concat.bend.snap index 6d634e7e2..6e87c70fc 100644 --- a/tests/snapshots/encode_pattern_match__concat.bend.snap +++ b/tests/snapshots/encode_pattern_match__concat.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/concat.bend --- Scott +unchecked String/concat: Any (String/concat) = λ* λb b +unchecked main: Any (main) = (String/concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa λ* a +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λ* λd (d a b) NumScott +unchecked String/concat: Any (String/concat) = λ* λb b +unchecked main: Any (main) = (String/concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__concat_def.bend.snap b/tests/snapshots/encode_pattern_match__concat_def.bend.snap index ac6c42c13..8ae37a61c 100644 --- a/tests/snapshots/encode_pattern_match__concat_def.bend.snap +++ b/tests/snapshots/encode_pattern_match__concat_def.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/concat_def.bend --- Scott +unchecked concat: Any (concat) = λa (a λb b λd λe λf (String/Cons d (concat e f))) +unchecked main: Any (main) = (concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa λ* a +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λ* λd (d a b) NumScott +unchecked concat: Any (concat) = λa (a λb switch b { 0: λc c; _: λ* λe λf λg (String/Cons e (concat f g)); }) +unchecked main: Any (main) = (concat (String/Cons 97 (String/Cons 98 String/Nil)) (String/Cons 99 (String/Cons 100 String/Nil))) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__def_tups.bend.snap b/tests/snapshots/encode_pattern_match__def_tups.bend.snap index 92edc1f2d..5f75be015 100644 --- a/tests/snapshots/encode_pattern_match__def_tups.bend.snap +++ b/tests/snapshots/encode_pattern_match__def_tups.bend.snap @@ -3,11 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/def_tups.bend --- Scott +unchecked go: Any (go) = λa let (b, c) = a; let (d, e) = c; let (f, g) = e; let (h, i) = g; (+ (+ (+ (+ i h) f) d) b) +unchecked main: Any (main) = (go (1, (2, (3, (4, 5))))) NumScott +unchecked go: Any (go) = λa let (b, c) = a; let (d, e) = c; let (f, g) = e; let (h, i) = g; (+ (+ (+ (+ i h) f) d) b) +unchecked main: Any (main) = (go (1, (2, (3, (4, 5))))) diff --git a/tests/snapshots/encode_pattern_match__definition_merge.bend.snap b/tests/snapshots/encode_pattern_match__definition_merge.bend.snap index b673bacce..48f8b8b29 100644 --- a/tests/snapshots/encode_pattern_match__definition_merge.bend.snap +++ b/tests/snapshots/encode_pattern_match__definition_merge.bend.snap @@ -3,31 +3,45 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/definition_merge.bend --- Scott +unchecked Foo: Any (Foo) = λa (a λb (b λc (c λf (f 1 1) λg (g 2 2)) λh (h λk (k 1 1) λl (l 2 2))) λm (m λn (n λq (q 3 3) λr (r 3 3)) λs (s λv (v 3 3) λw (w 3 3)))) +Either/Left: (Any -> Either) (Either/Left) = λa λb λ* (b a) +Either/Right: (Any -> Either) (Either/Right) = λa λ* λc (c a) +Bool/True: Bool (Bool/True) = λa λ* a +Bool/False: Bool (Bool/False) = λ* λb b NumScott +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: λc (c λd switch d { 0: λe (e λh switch h { 0: λi (i λj switch j { 0: 1; _: λ* 1; }); _: λ* λk (k λl switch l { 0: 2; _: λ* 2; }); }); _: λ* λm (m λp switch p { 0: λq (q λr switch r { 0: 1; _: λ* 1; }); _: λ* λs (s λt switch t { 0: 2; _: λ* 2; }); }); }); _: λ* λu (u λv switch v { 0: λw (w λz switch z { 0: λab (ab λbb switch bb { 0: 3; _: λ* 3; }); _: λ* λcb (cb λdb switch db { 0: 3; _: λ* 3; }); }); _: λ* λeb (eb λhb switch hb { 0: λib (ib λjb switch jb { 0: 3; _: λ* 3; }); _: λ* λkb (kb λlb switch lb { 0: 3; _: λ* 3; }); }); }); }) +Either/Left: (Any -> Either) (Either/Left) = λa λb (b Either/Left/tag a) +Either/Right: (Any -> Either) (Either/Right) = λa λb (b Either/Right/tag a) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +Either/Left/tag: u24 (Either/Left/tag) = 0 +Either/Right/tag: u24 (Either/Right/tag) = 1 +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__expr.bend.snap b/tests/snapshots/encode_pattern_match__expr.bend.snap index 69afdf370..4b4ce406b 100644 --- a/tests/snapshots/encode_pattern_match__expr.bend.snap +++ b/tests/snapshots/encode_pattern_match__expr.bend.snap @@ -3,81 +3,120 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/expr.bend --- Scott +Expr/Var: (Any -> Expr) (Expr/Var) = λa λb λ* λ* λ* λ* λ* λ* λ* λ* (b a) +Expr/Num: (Any -> Expr) (Expr/Num) = λa λ* λc λ* λ* λ* λ* λ* λ* λ* (c a) +Expr/App: (Any -> Any -> Expr) (Expr/App) = λa λb λ* λ* λe λ* λ* λ* λ* λ* λ* (e a b) +Expr/Fun: (Any -> Any -> Expr) (Expr/Fun) = λa λb λ* λ* λ* λf λ* λ* λ* λ* λ* (f a b) +Expr/If: (Any -> Any -> Any -> Expr) (Expr/If) = λa λb λc λ* λ* λ* λ* λh λ* λ* λ* λ* (h a b c) +Expr/Let: (Any -> Any -> Any -> Expr) (Expr/Let) = λa λb λc λ* λ* λ* λ* λ* λi λ* λ* λ* (i a b c) +Expr/Dup: (Any -> Any -> Any -> Any -> Expr) (Expr/Dup) = λa λb λc λd λ* λ* λ* λ* λ* λ* λk λ* λ* (k a b c d) +Expr/Tup: (Any -> Any -> Expr) (Expr/Tup) = λa λb λ* λ* λ* λ* λ* λ* λ* λj λ* (j a b) +Expr/Op2: (Any -> Any -> Any -> Expr) (Expr/Op2) = λa λb λc λ* λ* λ* λ* λ* λ* λ* λ* λl (l a b c) +Op/Add: Op (Op/Add) = λa λ* λ* λ* a +Op/Sub: Op (Op/Sub) = λ* λb λ* λ* b +Op/Mul: Op (Op/Mul) = λ* λ* λc λ* c +Op/Div: Op (Op/Div) = λ* λ* λ* λd d NumScott +Expr/Var: (Any -> Expr) (Expr/Var) = λa λb (b Expr/Var/tag a) +Expr/Num: (Any -> Expr) (Expr/Num) = λa λb (b Expr/Num/tag a) +Expr/App: (Any -> Any -> Expr) (Expr/App) = λa λb λc (c Expr/App/tag a b) +Expr/Fun: (Any -> Any -> Expr) (Expr/Fun) = λa λb λc (c Expr/Fun/tag a b) +Expr/If: (Any -> Any -> Any -> Expr) (Expr/If) = λa λb λc λd (d Expr/If/tag a b c) +Expr/Let: (Any -> Any -> Any -> Expr) (Expr/Let) = λa λb λc λd (d Expr/Let/tag a b c) +Expr/Dup: (Any -> Any -> Any -> Any -> Expr) (Expr/Dup) = λa λb λc λd λe (e Expr/Dup/tag a b c d) +Expr/Tup: (Any -> Any -> Expr) (Expr/Tup) = λa λb λc (c Expr/Tup/tag a b) +Expr/Op2: (Any -> Any -> Any -> Expr) (Expr/Op2) = λa λb λc λd (d Expr/Op2/tag a b c) +Op/Add: Op (Op/Add) = λa (a Op/Add/tag) +Op/Sub: Op (Op/Sub) = λa (a Op/Sub/tag) +Op/Mul: Op (Op/Mul) = λa (a Op/Mul/tag) +Op/Div: Op (Op/Div) = λa (a Op/Div/tag) +Expr/Var/tag: u24 (Expr/Var/tag) = 0 +Expr/Num/tag: u24 (Expr/Num/tag) = 1 +Expr/App/tag: u24 (Expr/App/tag) = 2 +Expr/Fun/tag: u24 (Expr/Fun/tag) = 3 +Expr/If/tag: u24 (Expr/If/tag) = 4 +Expr/Let/tag: u24 (Expr/Let/tag) = 5 +Expr/Dup/tag: u24 (Expr/Dup/tag) = 6 +Expr/Tup/tag: u24 (Expr/Tup/tag) = 7 +Expr/Op2/tag: u24 (Expr/Op2/tag) = 8 +Op/Add/tag: u24 (Op/Add/tag) = 0 +Op/Sub/tag: u24 (Op/Sub/tag) = 1 +Op/Mul/tag: u24 (Op/Mul/tag) = 2 +Op/Div/tag: u24 (Op/Div/tag) = 3 diff --git a/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap b/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap index eada49386..e80843335 100644 --- a/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap +++ b/tests/snapshots/encode_pattern_match__flatten_era_pat.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/flatten_era_pat.bend --- Scott +unchecked Fn1: Any (Fn1) = λa λ* let (*, d) = a; let (e, *) = d; e +unchecked Fn2: Any (Fn2) = λa let (*, c) = a; let (*, e) = c; let (f, *) = e; f +unchecked Fn3: Any (Fn3) = λa let (b, c) = a; (switch b { 0: λ* λe let * = e; 0; _: λg λ* λi let * = i; (+ g 1); } c) +unchecked main: Any (main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0) NumScott +unchecked Fn1: Any (Fn1) = λa λ* let (*, d) = a; let (e, *) = d; e +unchecked Fn2: Any (Fn2) = λa let (*, c) = a; let (*, e) = c; let (f, *) = e; f +unchecked Fn3: Any (Fn3) = λa let (b, c) = a; (switch b { 0: λ* λe let * = e; 0; _: λg λ* λi let * = i; (+ g 1); } c) +unchecked main: Any (main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0) diff --git a/tests/snapshots/encode_pattern_match__full_map.bend.snap b/tests/snapshots/encode_pattern_match__full_map.bend.snap index aac39459c..ea9797b3b 100644 --- a/tests/snapshots/encode_pattern_match__full_map.bend.snap +++ b/tests/snapshots/encode_pattern_match__full_map.bend.snap @@ -3,43 +3,69 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/full_map.bend --- Scott -(Map/get) = λa (a λb let {b b_2 b_3 b_4} = b; λc let {c c_2 c_3} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3 e_4} = e; switch (== 0 e) { 0: switch (% e_2 2) { 0: let (f, g) = (Map/get c (/ e_3 2)); (f, (Map/Node b g d)); _: λ* let (i, j) = (Map/get d_2 (/ e_4 2)); (i, (Map/Node b_2 c_2 j)); }; _: λ* (b_3, (Map/Node b_4 c_3 d_3)); } λ* (*, Map/Leaf)) +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get) = λa (a λb let {b b_2 b_3 b_4} = b; λc let {c c_2 c_3} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3 e_4} = e; switch (== 0 e) { 0: switch (% e_2 2) { 0: let (f, g) = (Map/get c (/ e_3 2)); (f, (Map/Node b g d)); _: λ* let (i, j) = (Map/get d_2 (/ e_4 2)); (i, (Map/Node b_2 c_2 j)); }; _: λ* (b_3, (Map/Node b_4 c_3 d_3)); } λ* (unreachable, Map/Leaf)) +unreachable: Any +(unreachable) = * + +unchecked prng: (Any -> Any) (prng) = λa let {a a_2} = a; let {b b_2} = (^ a (<< a_2 13)); let {c c_2} = (^ b (>> b_2 17)); (^ c (<< c_2 5)) +unchecked fullMap: Any (fullMap) = (fullMap__bend0 14) +unchecked test: (Any -> Any) (test) = λa (test__bend0 0 a) +unchecked main: Any (main) = (test fullMap) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λa λb λc λd λ* (d a b c) +Map/Leaf: (Map T) (Map/Leaf) = λ* λb b +unchecked fullMap__bend0: _ (fullMap__bend0) = λa let {a a_2 a_3} = a; switch (> a 0) { 0: Map/Leaf; _: λ* (Map/Node 1 (fullMap__bend0 (- a_2 1)) (fullMap__bend0 (- a_3 1))); } +unchecked test__bend0: _ (test__bend0) = λa let {a a_2 a_3} = a; switch (< a 1000) { 0: λ* 0; _: λ* λd let (e, f) = (Map/get d (% (prng a_2) 4096)); (+ e (test__bend0 (+ a_3 1) f)); } NumScott -(Map/get) = λa (a λb switch b { 0: λc let {c c_2 c_3 c_4} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3} = e; λf let {f f_2 f_3 f_4} = f; switch (== 0 f) { 0: switch (% f_2 2) { 0: let (g, h) = (Map/get d (/ f_3 2)); (g, (Map/Node c h e)); _: λ* let (j, k) = (Map/get e_2 (/ f_4 2)); (j, (Map/Node c_2 d_2 k)); }; _: λ* (c_3, (Map/Node c_4 d_3 e_3)); }; _: λ* λ* (*, Map/Leaf); }) +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get) = λa (a λb switch b { 0: λc let {c c_2 c_3 c_4} = c; λd let {d d_2 d_3} = d; λe let {e e_2 e_3} = e; λf let {f f_2 f_3 f_4} = f; switch (== 0 f) { 0: switch (% f_2 2) { 0: let (g, h) = (Map/get d (/ f_3 2)); (g, (Map/Node c h e)); _: λ* let (j, k) = (Map/get e_2 (/ f_4 2)); (j, (Map/Node c_2 d_2 k)); }; _: λ* (c_3, (Map/Node c_4 d_3 e_3)); }; _: λ* λ* (unreachable, Map/Leaf); }) + +unreachable: Any +(unreachable) = * +unchecked prng: (Any -> Any) (prng) = λa let {a a_2} = a; let {b b_2} = (^ a (<< a_2 13)); let {c c_2} = (^ b (>> b_2 17)); (^ c (<< c_2 5)) +unchecked fullMap: Any (fullMap) = (fullMap__bend0 14) +unchecked test: (Any -> Any) (test) = λa (test__bend0 0 a) +unchecked main: Any (main) = (test fullMap) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λa λb λc λd (d Map/Node/tag a b c) +Map/Leaf: (Map T) (Map/Leaf) = λa (a Map/Leaf/tag) +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 +unchecked fullMap__bend0: _ (fullMap__bend0) = λa let {a a_2 a_3} = a; switch (> a 0) { 0: Map/Leaf; _: λ* (Map/Node 1 (fullMap__bend0 (- a_2 1)) (fullMap__bend0 (- a_3 1))); } +unchecked test__bend0: _ (test__bend0) = λa let {a a_2 a_3} = a; switch (< a 1000) { 0: λ* 0; _: λ* λd let (e, f) = (Map/get d (% (prng a_2) 4096)); (+ e (test__bend0 (+ a_3 1) f)); } diff --git a/tests/snapshots/encode_pattern_match__is_some_some.bend.snap b/tests/snapshots/encode_pattern_match__is_some_some.bend.snap index 45d789362..0a3d13e9c 100644 --- a/tests/snapshots/encode_pattern_match__is_some_some.bend.snap +++ b/tests/snapshots/encode_pattern_match__is_some_some.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/is_some_some.bend --- Scott +unchecked some_some: Any (some_some) = λa (a λb (b λ* 1 0) 0) +unchecked main: Any (main) = (some_some (Option/Some 1)) +Option/Some: (Any -> Option) (Option/Some) = λa λb λ* (b a) +Option/None: Option (Option/None) = λ* λb b NumScott +unchecked some_some: Any (some_some) = λa (a λb switch b { 0: λc (c λd switch d { 0: λ* 1; _: λ* 0; }); _: λ* 0; }) +unchecked main: Any (main) = (some_some (Option/Some 1)) +Option/Some: (Any -> Option) (Option/Some) = λa λb (b Option/Some/tag a) +Option/None: Option (Option/None) = λa (a Option/None/tag) +Option/Some/tag: u24 (Option/Some/tag) = 0 +Option/None/tag: u24 (Option/None/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap b/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap index 6be31d664..ab9648b40 100644 --- a/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_merge_sort.bend.snap @@ -3,55 +3,81 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_merge_sort.bend --- Scott +unchecked If: Any (If) = λa (a λb λc let * = c; b λf λg let * = f; g) +unchecked Pure: Any (Pure) = λa (List_/Cons a List_/Nil) +unchecked Map: Any (Map) = λa (a λb λc λd let {e e_2} = d; (List_/Cons (e b) (Map c e_2)) λf let * = f; List_/Nil) +unchecked MergeSort: Any (MergeSort) = λa λb (Unpack a (Map b Pure)) +unchecked Unpack: Any (Unpack) = λa λb (b λc λd λe (d λf λg λh let {h h_2} = h; λi (Unpack h (MergePair h_2 (List_/Cons i (List_/Cons f g)))) λ* λk k e c) λ* List_/Nil a) +unchecked MergePair: Any (MergePair) = λa λb (b λc λd λe (d λf λg λh let {h h_2} = h; λi (List_/Cons (Merge h i f) (MergePair h_2 g)) λ* λk (List_/Cons k List_/Nil) e c) λ* List_/Nil a) +unchecked Merge: Any (Merge) = λa λb (b λc λd λe λf (f λh let {h h_2 h_3} = h; λi let {i i_2} = i; λj let {j j_2 j_3} = j; λk let {k k_2 k_3} = k; λl let {l l_2} = l; (If (j k h) (List_/Cons k_2 (Merge j_2 l (List_/Cons h_2 i))) (List_/Cons h_3 (Merge j_3 (List_/Cons k_3 l_2) i_2))) λ* λp λq (List_/Cons p q) e c d) λ* λs s a) +Bool/True: Bool (Bool/True) = λa λ* a +Bool/False: Bool (Bool/False) = λ* λb b +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc λ* (c a b) +List_/Nil: List_ (List_/Nil) = λ* λb b NumScott +unchecked If: Any (If) = λa (a λb switch b { 0: λc λd let * = d; c; _: λ* λg λh let * = g; h; }) +unchecked Pure: Any (Pure) = λa (List_/Cons a List_/Nil) +unchecked Map: Any (Map) = λa (a λb switch b { 0: λc λd λe let {f f_2} = e; (List_/Cons (f c) (Map d f_2)); _: λ* λg let * = g; List_/Nil; }) +unchecked MergeSort: Any (MergeSort) = λa λb (Unpack a (Map b Pure)) +unchecked Unpack: Any (Unpack) = λa λb (b λc switch c { 0: λd λe λf (e λg switch g { 0: λh λi λj let {j j_2} = j; λk (Unpack j (MergePair j_2 (List_/Cons k (List_/Cons h i)))); _: λ* λ* λm m; } f d); _: λ* λ* List_/Nil; } a) +unchecked MergePair: Any (MergePair) = λa λb (b λc switch c { 0: λd λe λf (e λg switch g { 0: λh λi λj let {j j_2} = j; λk (List_/Cons (Merge j k h) (MergePair j_2 i)); _: λ* λ* λm (List_/Cons m List_/Nil); } f d); _: λ* λ* List_/Nil; } a) +unchecked Merge: Any (Merge) = λa λb (b λc switch c { 0: λd λe λf λg (g λi switch i { 0: λj let {j j_2 j_3} = j; λk let {k k_2} = k; λl let {l l_2 l_3} = l; λm let {m m_2 m_3} = m; λn let {n n_2} = n; (If (l m j) (List_/Cons m_2 (Merge l_2 n (List_/Cons j_2 k))) (List_/Cons j_3 (Merge l_3 (List_/Cons m_3 n_2) k_2))); _: λ* λ* λr λs (List_/Cons r s); } f d e); _: λ* λ* λu u; } a) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) +List_/Nil: List_ (List_/Nil) = λa (a List_/Nil/tag) +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 +List_/Cons/tag: u24 (List_/Cons/tag) = 0 +List_/Nil/tag: u24 (List_/Nil/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap index 9dd3fd6d3..dade87c53 100644 --- a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_fn.bend.snap @@ -3,15 +3,21 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_fn.bend --- Scott +unchecked main: Any (main) = * +unchecked Foo: Any (Foo) = λa (a 0 λ* λ* 1) +unchecked Bar: Any (Bar) = λa (a 1 λ* λ* 0) NumScott +unchecked main: Any (main) = * +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: 0; _: λ* λ* λ* 1; }) +unchecked Bar: Any (Bar) = λa (a λb switch b { 0: 1; _: λ* λ* λ* 0; }) diff --git a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap index edf0b7a65..69db62589 100644 --- a/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap +++ b/tests/snapshots/encode_pattern_match__list_str_encoding_undeclared_map.bend.snap @@ -3,7 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_map.bend --- Scott +unchecked main: Any (main) = λa λb ((a 2 λ* λ* 1), (b 2 λ* λ* 1)) NumScott +unchecked main: Any (main) = λa λb ((a λc switch c { 0: 2; _: λ* λ* λ* 1; }), (b λg switch g { 0: 2; _: λ* λ* λ* 1; })) diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap index bb3caa307..2f78b566f 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_in_arm.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_in_arm.bend --- Scott +unchecked main: Any (main) = λ* λ$x $x +bool/T: bool (bool/T) = λa λ* a +bool/F: bool (bool/F) = λ* λb b NumScott +unchecked main: Any (main) = λ* λ$x $x +bool/T: bool (bool/T) = λa (a bool/T/tag) +bool/F: bool (bool/F) = λa (a bool/F/tag) +bool/T/tag: u24 (bool/T/tag) = 0 +bool/F/tag: u24 (bool/F/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap index 0d83206e0..74e369db9 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_lambda.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_lambda.bend --- Scott +unchecked main: Any (main) = (Maybe/Some 1 λ$x * λa a $x) +Maybe/None: Maybe (Maybe/None) = λa λ* a +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λ* λc (c a) NumScott +unchecked main: Any (main) = (Maybe/Some 1 λa switch a { 0: λ$x *; _: λ* λb b; } $x) +Maybe/None: Maybe (Maybe/None) = λa (a Maybe/None/tag) +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λb (b Maybe/Some/tag a) +Maybe/None/tag: u24 (Maybe/None/tag) = 0 +Maybe/Some/tag: u24 (Maybe/Some/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap b/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap index 7ac3cdabd..b983ba126 100644 --- a/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_adt_unscoped_var.bend.snap @@ -3,27 +3,39 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_var.bend --- Scott +unchecked Foo: Any (Foo) = λ$x (Maybe/Some 1 $x λa a) +unchecked Bar: Any (Bar) = (Maybe/Some 1 $x λa a λ$x *) +unchecked main: Any (main) = * +Maybe/None: Maybe (Maybe/None) = λa λ* a +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λ* λc (c a) NumScott +unchecked Foo: Any (Foo) = λ$x (Maybe/Some 1 λa switch a { 0: $x; _: λ* λb b; }) +unchecked Bar: Any (Bar) = (Maybe/Some 1 λa switch a { 0: $x; _: λ* λb b; } λ$x *) +unchecked main: Any (main) = * +Maybe/None: Maybe (Maybe/None) = λa (a Maybe/None/tag) +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λb (b Maybe/Some/tag a) +Maybe/None/tag: u24 (Maybe/None/tag) = 0 +Maybe/Some/tag: u24 (Maybe/Some/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap b/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap index a39592681..0f9c26225 100644 --- a/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_auto_linearization.bend.snap @@ -3,25 +3,36 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_auto_linearization.bend --- Scott +unchecked switch_linearization: Any (switch_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; switch a_2 { 0: let {e e_2} = 2; let {f g} = e; (b_2 e_2 f g (a_3, b_3) c d); _: λh let {i i_2} = 2; let {j k} = i; (h b_4 i_2 j k (a_4, b_5) c_2 d_2); } +unchecked match_linearization: Any (match_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; (a_2 λe let {f f_2} = 2; let {g h} = f; (e b_2 f_2 g h (a_3, b_3) c d) let {i i_2} = 2; let {j k} = i; (b_4 i_2 j k (a_4, b_5) c_2 d_2)) +unchecked switch_shadowed_field: Any (switch_shadowed_field) = λa switch a { 0: λb b; _: λc λ* c; } +unchecked match_shadowed_field: Any (match_shadowed_field) = λa (a λb λc (List/Cons b c) λd λe λ* λ* (List/Cons d e)) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λa λb λ* λd (d a b) NumScott +unchecked switch_linearization: Any (switch_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; switch a_2 { 0: let {e e_2} = 2; let {f g} = e; (b_2 e_2 f g (a_3, b_3) c d); _: λh let {i i_2} = 2; let {j k} = i; (h b_4 i_2 j k (a_4, b_5) c_2 d_2); } +unchecked match_linearization: Any (match_linearization) = λa let {a a_2 a_3 a_4} = a; λb let {b b_2 b_3 b_4 b_5} = b; let (c, d) = (a, b); let {d d_2} = d; let {c c_2} = c; (a_2 λe switch e { 0: λf let {g g_2} = 2; let {h i} = g; (f b_2 g_2 h i (a_3, b_3) c d); _: λ* let {j j_2} = 2; let {k l} = j; (b_4 j_2 k l (a_4, b_5) c_2 d_2); }) +unchecked switch_shadowed_field: Any (switch_shadowed_field) = λa switch a { 0: λb b; _: λc λ* c; } +unchecked match_shadowed_field: Any (match_shadowed_field) = λa (a λb switch b { 0: λc λd (List/Cons c d); _: λ* λe λf λ* λ* (List/Cons e f); }) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λa λb λc (c List/Cons/tag a b) +List/Cons/tag: u24 (List/Cons/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_bind.bend.snap b/tests/snapshots/encode_pattern_match__match_bind.bend.snap index 0b0da60b0..79a24af10 100644 --- a/tests/snapshots/encode_pattern_match__match_bind.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_bind.bend.snap @@ -3,11 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_bind.bend --- Scott +unchecked cheese: Any (cheese) = switch (+ 2 3) { 0: 653323; _: λa let {a a_2} = a; (+ (+ a 1) a_2); } +unchecked main: Any (main) = cheese NumScott +unchecked cheese: Any (cheese) = switch (+ 2 3) { 0: 653323; _: λa let {a a_2} = a; (+ (+ a 1) a_2); } +unchecked main: Any (main) = cheese diff --git a/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap b/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap index e70f1f0d6..6a7c7b57c 100644 --- a/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_num_adt_tup_parser.bend.snap @@ -3,35 +3,51 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_num_adt_tup_parser.bend --- Scott +unchecked Parse: Any (Parse) = λa λb (b λc (Result_/Err (String/Nil, c)) λd λe λf (switch (- d 10) { 0: λg λh (Result_/Ok (0, h, g)); _: λi λj λk (switch (- i 29) { 0: λl λm (Result_/Ok (40, m, l)); _: λn λo λp (switch n { 0: λq λr (Result_/Ok (41, r, q)); _: λs λt λu (Result_/Err ((String/Cons (+ s 42) u), t)); } o p); } j k); } f e) a) +unchecked main: Any (main) = (Parse * (String/Cons 40 (String/Cons 43 String/Nil)) λc let (d, e, f) = c; (d, (Parse f e)) λg (Result_/Err g)) +String/Nil: String (String/Nil) = λa λ* a +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λ* λd (d a b) +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb λ* (b a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λ* λc (c a) NumScott +unchecked Parse: Any (Parse) = λa λb (b λc switch c { 0: λd (Result_/Err (String/Nil, d)); _: λ* λe λf λg (switch (- e 10) { 0: λh λi (Result_/Ok (0, i, h)); _: λj λk λl (switch (- j 29) { 0: λm λn (Result_/Ok (40, n, m)); _: λo λp λq (switch o { 0: λr λs (Result_/Ok (41, s, r)); _: λt λu λv (Result_/Err ((String/Cons (+ t 42) v), u)); } p q); } k l); } g f); } a) +unchecked main: Any (main) = (Parse * (String/Cons 40 (String/Cons 43 String/Nil)) λc switch c { 0: λd let (e, f, g) = d; (e, (Parse g f)); _: λ* λh (Result_/Err h); }) +String/Nil: String (String/Nil) = λa (a String/Nil/tag) +String/Cons: (u24 -> String -> String) (String/Cons) = λa λb λc (c String/Cons/tag a b) +Result_/Ok: (Any -> Result_) (Result_/Ok) = λa λb (b Result_/Ok/tag a) +Result_/Err: (Any -> Result_) (Result_/Err) = λa λb (b Result_/Err/tag a) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 +Result_/Ok/tag: u24 (Result_/Ok/tag) = 0 +Result_/Err/tag: u24 (Result_/Err/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap b/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap index a3125d796..13036ccb2 100644 --- a/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_num_pred.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_num_pred.bend --- Scott +unchecked pred: Any (pred) = λa switch a { 0: 0; _: λb b; } +unchecked pred2: Any (pred2) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc c; }; } +unchecked pred3: Any (pred3) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc switch c { 0: 0; _: λd d; }; }; } +unchecked zero: Any (zero) = λa switch a { 0: 1; _: λb switch b { 0: 0; _: λ* 0; }; } +unchecked main: Any (main) = * NumScott +unchecked pred: Any (pred) = λa switch a { 0: 0; _: λb b; } +unchecked pred2: Any (pred2) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc c; }; } +unchecked pred3: Any (pred3) = λa switch a { 0: 0; _: λb switch b { 0: 0; _: λc switch c { 0: 0; _: λd d; }; }; } +unchecked zero: Any (zero) = λa switch a { 0: 1; _: λb switch b { 0: 0; _: λ* 0; }; } +unchecked main: Any (main) = * diff --git a/tests/snapshots/encode_pattern_match__match_syntax.bend.snap b/tests/snapshots/encode_pattern_match__match_syntax.bend.snap index 2a92b75c1..be146e147 100644 --- a/tests/snapshots/encode_pattern_match__match_syntax.bend.snap +++ b/tests/snapshots/encode_pattern_match__match_syntax.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/match_syntax.bend --- Scott +unchecked head: Any (head) = λa (a λb λ* b List_/Nil) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc λ* (c a b) +List_/Nil: List_ (List_/Nil) = λ* λb b NumScott +unchecked head: Any (head) = λa (a λb switch b { 0: λc λ* c; _: λ* List_/Nil; }) +List_/Cons: (Any -> Any -> List_) (List_/Cons) = λa λb λc (c List_/Cons/tag a b) +List_/Nil: List_ (List_/Nil) = λa (a List_/Nil/tag) +List_/Cons/tag: u24 (List_/Cons/tag) = 0 +List_/Nil/tag: u24 (List_/Nil/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap b/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap index c56283456..035a4e1e8 100644 --- a/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap +++ b/tests/snapshots/encode_pattern_match__merge_recursive.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/merge_recursive.bend --- Scott +unchecked foo_1: Any (foo_1) = λa (a foo_2) +unchecked foo_2: Any (foo_2) = λa λb (a, b) +unchecked bar_1: Any (bar_1) = λa (a bar_2) +unchecked bar_2: Any (bar_2) = λa λb (a, b) NumScott +unchecked foo_1: Any (foo_1) = λa (a foo_2) +unchecked foo_2: Any (foo_2) = λa λb (a, b) +unchecked bar_1: Any (bar_1) = λa (a bar_2) +unchecked bar_2: Any (bar_2) = λa λb (a, b) diff --git a/tests/snapshots/encode_pattern_match__no_patterns.bend.snap b/tests/snapshots/encode_pattern_match__no_patterns.bend.snap index 4b1948738..1d3dcb99e 100644 --- a/tests/snapshots/encode_pattern_match__no_patterns.bend.snap +++ b/tests/snapshots/encode_pattern_match__no_patterns.bend.snap @@ -3,15 +3,21 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/no_patterns.bend --- Scott +unchecked Id: Any (Id) = λa a +unchecked Id2: Any (Id2) = λa a +unchecked Pair: Any (Pair) = λa λb (a, b) NumScott +unchecked Id: Any (Id) = λa a +unchecked Id2: Any (Id2) = λa a +unchecked Pair: Any (Pair) = λa λb (a, b) diff --git a/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap b/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap index b7ecfc64a..b438c6c19 100644 --- a/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap +++ b/tests/snapshots/encode_pattern_match__non_matching_fst_arg.bend.snap @@ -3,19 +3,27 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/non_matching_fst_arg.bend --- Scott +unchecked Foo: Any (Foo) = λa λb (b λc let {c c_2} = c; (Foo c c_2) λd d a) +bool/true: bool (bool/true) = λa λ* a +bool/false: bool (bool/false) = λ* λb b NumScott +unchecked Foo: Any (Foo) = λa λb (b λc switch c { 0: λd let {d d_2} = d; (Foo d d_2); _: λ* λe e; } a) +bool/true: bool (bool/true) = λa (a bool/true/tag) +bool/false: bool (bool/false) = λa (a bool/false/tag) +bool/true/tag: u24 (bool/true/tag) = 0 +bool/false/tag: u24 (bool/false/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap b/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap index 91b91f7eb..07545f0b9 100644 --- a/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap +++ b/tests/snapshots/encode_pattern_match__ntup_sum.bend.snap @@ -3,11 +3,15 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/ntup_sum.bend --- Scott +unchecked ntupSum: Any (ntupSum) = λa let (b, c, d, e, f) = a; (+ b (+ c (+ d (+ e f)))) +unchecked main: Any (main) = (ntupSum (1, 3, 3, 2, 1)) NumScott +unchecked ntupSum: Any (ntupSum) = λa let (b, c, d, e, f) = a; (+ b (+ c (+ d (+ e f)))) +unchecked main: Any (main) = (ntupSum (1, 3, 3, 2, 1)) diff --git a/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap b/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap index 3a81eeb10..a28748f6b 100644 --- a/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap +++ b/tests/snapshots/encode_pattern_match__pattern_match_encoding.bend.snap @@ -3,41 +3,60 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/pattern_match_encoding.bend --- Scott +unchecked Foo: Any (Foo) = λa (a λ* 100 λ* 200 λ* 200 λ* λ* 200 λ* λ* 200) +unchecked main: Any (main) = (Foo MyType/A 2) +MyType/A: (Any -> MyType) (MyType/A) = λa λb λ* λ* λ* λ* (b a) +MyType/B: (Any -> MyType) (MyType/B) = λa λ* λc λ* λ* λ* (c a) +MyType/C: (Any -> MyType) (MyType/C) = λa λ* λ* λd λ* λ* (d a) +MyType/D: (Any -> Any -> MyType) (MyType/D) = λa λb λ* λ* λ* λf λ* (f a b) +MyType/E: (Any -> Any -> MyType) (MyType/E) = λa λb λ* λ* λ* λ* λg (g a b) NumScott +unchecked Foo: Any (Foo) = λa (a λb switch b { 0: λ* 100; _: λd switch d { 0: λ* 200; _: λf switch f { 0: λ* 200; _: λh switch h { 0: λ* λ* 200; _: λ* λ* λ* 200; }; }; }; }) +unchecked main: Any (main) = (Foo MyType/A 2) +MyType/A: (Any -> MyType) (MyType/A) = λa λb (b MyType/A/tag a) +MyType/B: (Any -> MyType) (MyType/B) = λa λb (b MyType/B/tag a) +MyType/C: (Any -> MyType) (MyType/C) = λa λb (b MyType/C/tag a) +MyType/D: (Any -> Any -> MyType) (MyType/D) = λa λb λc (c MyType/D/tag a b) +MyType/E: (Any -> Any -> MyType) (MyType/E) = λa λb λc (c MyType/E/tag a b) +MyType/A/tag: u24 (MyType/A/tag) = 0 +MyType/B/tag: u24 (MyType/B/tag) = 1 +MyType/C/tag: u24 (MyType/C/tag) = 2 +MyType/D/tag: u24 (MyType/D/tag) = 3 +MyType/E/tag: u24 (MyType/E/tag) = 4 diff --git a/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap b/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap index 0cfe4b49a..f9b8f4dfb 100644 --- a/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap +++ b/tests/snapshots/encode_pattern_match__switch_in_switch_arg.bend.snap @@ -3,7 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/switch_in_switch_arg.bend --- Scott +unchecked main: Any (main) = λa switch switch a { 0: 0; _: λb b; } { 0: 0; _: λc (+ c 1); } NumScott +unchecked main: Any (main) = λa switch switch a { 0: 0; _: λb b; } { 0: 0; _: λc (+ c 1); } diff --git a/tests/snapshots/encode_pattern_match__var_only.bend.snap b/tests/snapshots/encode_pattern_match__var_only.bend.snap index 65c06bc22..8ef45eb22 100644 --- a/tests/snapshots/encode_pattern_match__var_only.bend.snap +++ b/tests/snapshots/encode_pattern_match__var_only.bend.snap @@ -3,23 +3,33 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/var_only.bend --- Scott +unchecked Foo: Any (Foo) = λa λ* λc (c a) +unchecked main: Any (main) = λ* Foo +Bool/False: Bool (Bool/False) = λa λ* a +Bool/True: Bool (Bool/True) = λ* λb b NumScott +unchecked Foo: Any (Foo) = λa λ* λc (c a) +unchecked main: Any (main) = λ* Foo +Bool/False: Bool (Bool/False) = λa (a Bool/False/tag) +Bool/True: Bool (Bool/True) = λa (a Bool/True/tag) +Bool/False/tag: u24 (Bool/False/tag) = 0 +Bool/True/tag: u24 (Bool/True/tag) = 1 diff --git a/tests/snapshots/encode_pattern_match__weekday.bend.snap b/tests/snapshots/encode_pattern_match__weekday.bend.snap index 298584978..4ab9c2349 100644 --- a/tests/snapshots/encode_pattern_match__weekday.bend.snap +++ b/tests/snapshots/encode_pattern_match__weekday.bend.snap @@ -3,49 +3,72 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/encode_pattern_match/weekday.bend --- Scott +unchecked main: Any (main) = (λa a Weekday/Saturday) +Weekday/Monday: Weekday (Weekday/Monday) = λa λ* λ* λ* λ* λ* λ* a +Weekday/Tuesday: Weekday (Weekday/Tuesday) = λ* λb λ* λ* λ* λ* λ* b +Weekday/Wednesday: Weekday (Weekday/Wednesday) = λ* λ* λc λ* λ* λ* λ* c +Weekday/Thursday: Weekday (Weekday/Thursday) = λ* λ* λ* λd λ* λ* λ* d +Weekday/Friday: Weekday (Weekday/Friday) = λ* λ* λ* λ* λe λ* λ* e +Weekday/Saturday: Weekday (Weekday/Saturday) = λ* λ* λ* λ* λ* λf λ* f +Weekday/Sunday: Weekday (Weekday/Sunday) = λ* λ* λ* λ* λ* λ* λg g NumScott +unchecked main: Any (main) = (λa a Weekday/Saturday) +Weekday/Monday: Weekday (Weekday/Monday) = λa (a Weekday/Monday/tag) +Weekday/Tuesday: Weekday (Weekday/Tuesday) = λa (a Weekday/Tuesday/tag) +Weekday/Wednesday: Weekday (Weekday/Wednesday) = λa (a Weekday/Wednesday/tag) +Weekday/Thursday: Weekday (Weekday/Thursday) = λa (a Weekday/Thursday/tag) +Weekday/Friday: Weekday (Weekday/Friday) = λa (a Weekday/Friday/tag) +Weekday/Saturday: Weekday (Weekday/Saturday) = λa (a Weekday/Saturday/tag) +Weekday/Sunday: Weekday (Weekday/Sunday) = λa (a Weekday/Sunday/tag) +Weekday/Monday/tag: u24 (Weekday/Monday/tag) = 0 +Weekday/Tuesday/tag: u24 (Weekday/Tuesday/tag) = 1 +Weekday/Wednesday/tag: u24 (Weekday/Wednesday/tag) = 2 +Weekday/Thursday/tag: u24 (Weekday/Thursday/tag) = 3 +Weekday/Friday/tag: u24 (Weekday/Friday/tag) = 4 +Weekday/Saturday/tag: u24 (Weekday/Saturday/tag) = 5 +Weekday/Sunday/tag: u24 (Weekday/Sunday/tag) = 6 diff --git a/tests/snapshots/examples__list.bend.snap b/tests/snapshots/examples__list.bend.snap index c22021d0a..ca010cac8 100644 --- a/tests/snapshots/examples__list.bend.snap +++ b/tests/snapshots/examples__list.bend.snap @@ -2,4 +2,4 @@ source: tests/golden_tests.rs input_file: examples/list.bend --- -5 +λa (a Result/Ok/tag 5) diff --git a/tests/snapshots/io__load.bend.snap b/tests/snapshots/io__load.bend.snap index 392c27c54..d875f76e9 100644 --- a/tests/snapshots/io__load.bend.snap +++ b/tests/snapshots/io__load.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/load.bend --- Strict mode: -"Contents\n" +λa (a IO/Done/tag IO/MAGIC λb (b Result/Ok/tag "Contents\n")) diff --git a/tests/snapshots/io__load_fail.bend.snap b/tests/snapshots/io__load_fail.bend.snap index 5106f7eaa..24919534b 100644 --- a/tests/snapshots/io__load_fail.bend.snap +++ b/tests/snapshots/io__load_fail.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/load_fail.bend --- Strict mode: -1 +λa (a IO/Done/tag IO/MAGIC λb (b Result/Err/tag +2)) diff --git a/tests/snapshots/io__store.bend.snap b/tests/snapshots/io__store.bend.snap index aeb5109a7..9b77cee4f 100644 --- a/tests/snapshots/io__store.bend.snap +++ b/tests/snapshots/io__store.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/store.bend --- Strict mode: -λa (a Result/Ok/tag [40, 77, 97, 105, 110, 41, 32, 61, 32, 48]) +λa (a IO/Done/tag IO/MAGIC λb (b Result/Ok/tag *)) diff --git a/tests/snapshots/io__store_fail.bend.snap b/tests/snapshots/io__store_fail.bend.snap index 80b262705..73e2559a7 100644 --- a/tests/snapshots/io__store_fail.bend.snap +++ b/tests/snapshots/io__store_fail.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/io/store_fail.bend --- Strict mode: -λa (a Result/Err/tag λb (b 2 +2)) +λa (a IO/Done/tag IO/MAGIC λb (b Result/Err/tag +2)) diff --git a/tests/snapshots/parse_file__era.bend.snap b/tests/snapshots/parse_file__era.bend.snap index 3f2e17e1a..7c3b1bb61 100644 --- a/tests/snapshots/parse_file__era.bend.snap +++ b/tests/snapshots/parse_file__era.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/era.bend --- +unchecked Main: Any (Main) = * diff --git a/tests/snapshots/parse_file__fun_def.bend.snap b/tests/snapshots/parse_file__fun_def.bend.snap index 98c4381f2..26748c7c4 100644 --- a/tests/snapshots/parse_file__fun_def.bend.snap +++ b/tests/snapshots/parse_file__fun_def.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fun_def.bend --- -(main) = let base = 0; def (aux) = λ%arg0 match %arg0 = %arg0 { List/Nil: base; List/Cons %arg0.head %arg0.tail: use tail = %arg0.tail; use head = %arg0.head; (+ head (aux tail)); }(aux (List/Cons 1 (List/Cons 2 (List/Cons 3 List/Nil)))) +unchecked main: Any +(main) = let base = 0; def (aux (List/Nil)) = base(aux (List/Cons head tail)) = (+ head (aux tail))(aux (List/Cons 1 (List/Cons 2 (List/Cons 3 List/Nil)))) diff --git a/tests/snapshots/parse_file__fun_def_name.bend.snap b/tests/snapshots/parse_file__fun_def_name.bend.snap index e2a6af2e6..50230aca9 100644 --- a/tests/snapshots/parse_file__fun_def_name.bend.snap +++ b/tests/snapshots/parse_file__fun_def_name.bend.snap @@ -4,5 +4,6 @@ input_file: tests/golden_tests/parse_file/fun_def_name.bend --- Errors: In tests/golden_tests/parse_file/fun_def_name.bend : -Expected a rule with name 'aux'. - 4 | aux2 (List/Cons head tail) = (+ head (aux tail)) +Redefinition of builtin (constructor) 'List/Cons'. +Location: + 4 | aux2 (List/Cons head tail) = (+ head (aux tail)) diff --git a/tests/snapshots/parse_file__imp_map.bend.snap b/tests/snapshots/parse_file__imp_map.bend.snap index 3f04e2682..053384a3c 100644 --- a/tests/snapshots/parse_file__imp_map.bend.snap +++ b/tests/snapshots/parse_file__imp_map.bend.snap @@ -2,18 +2,29 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/imp_map.bend --- +Map/empty: (Map T) (Map/empty) = Map/Leaf -(Map/get) = λ%arg0 λ%arg1 use key = %arg1; use map = %arg0; match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get map key) = match map = map { Map/Leaf: (unreachable, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } -(Map/set) = λ%arg0 λ%arg1 λ%arg2 use value = %arg2; use key = %arg1; use map = %arg0; match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +Map/set: ((Map T) -> u24 -> T -> (Map T)) +(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node unreachable (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +unreachable: Any +(unreachable) = * + +unchecked main: Any (main) = let x = (Map/set (Map/set Map/empty 2 1) 3 2); let (map/get%1, x) = (Map/get x 2); let y = (id map/get%1); let z = 4; let x = (Map/set x z 4); let (map/get%0, x) = (Map/get x z); (+ y map/get%0) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λvalue λleft λright λ%x (%x Map/Node/tag value left right) +Map/Leaf: (Map T) (Map/Leaf) = λ%x (%x Map/Leaf/tag) +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 diff --git a/tests/snapshots/parse_file__imp_program.bend.snap b/tests/snapshots/parse_file__imp_program.bend.snap index da62166e1..88f5b969d 100644 --- a/tests/snapshots/parse_file__imp_program.bend.snap +++ b/tests/snapshots/parse_file__imp_program.bend.snap @@ -2,66 +2,101 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/imp_program.bend --- +Map/empty: (Map T) (Map/empty) = Map/Leaf -(Map/get) = λ%arg0 λ%arg1 use key = %arg1; use map = %arg0; match map = map { Map/Leaf: (*, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } +Map/get: ((Map T) -> u24 -> (T, (Map T))) +(Map/get map key) = match map = map { Map/Leaf: (unreachable, map); Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/Node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/Node map.value map.left rest)); }; _ _-1: (map.value, map); }; } -(Map/set) = λ%arg0 λ%arg1 λ%arg2 use value = %arg2; use key = %arg1; use map = %arg0; match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node * (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node * Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +Map/set: ((Map T) -> u24 -> T -> (Map T)) +(Map/set map key value) = match map = map { Map/Node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/Node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/Node value map.left map.right); }; Map/Leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/Node unreachable (Map/set Map/Leaf (/ key 2) value) Map/Leaf); _ _-1: (Map/Node unreachable Map/Leaf (Map/set Map/Leaf (/ key 2) value)); }; _ _-1: (Map/Node value Map/Leaf Map/Leaf); }; } +unreachable: Any +(unreachable) = * + +unchecked symbols: Any (symbols) = let x = (Map/set (Map/set Map/empty 49 5) 2 3); let x = (Map/set x 49 2); let x = (Map/set x 2 3); let (map/get%0, x) = (Map/get x 49); (+ map/get%0 8293490) +unchecked mk_point: Any (mk_point) = (Point/Point 1 2) -(identity) = λ%arg0 use x = %arg0; x +unchecked identity: (Any -> Any) +(identity x) = x -(inc) = λ%arg0 use n = %arg0; let n = (+ n 1); n +unchecked inc: (Any -> Any) +(inc n) = let n = (+ n 1); n -(inc_list) = λ%arg0 use list = %arg0; fold %iter = list { List/Nil: List/Nil; List/Cons: let x = %iter.head; (List/Cons (+ x 1) %iter.tail); } +unchecked inc_list: (Any -> Any) +(inc_list list) = fold %iter = list { List/Nil: List/Nil; List/Cons: let x = %iter.head; (List/Cons (+ x 1) %iter.tail); } +unchecked lam: Any (lam) = λx λy x -(do_match) = λ%arg0 use b = %arg0; match b = b { Bool/True: 1; Bool/False: 0; } +unchecked do_match: (Any -> Any) +(do_match b) = match b = b { Bool/True: 1; Bool/False: 0; } +unchecked true: Any (true) = Bool/True -(fib) = λ%arg0 use n = %arg0; switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; } +unchecked fib: (Any -> Any) +(fib n) = switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; } -(swt) = λ%arg0 use n = %arg0; switch n = n { 0: 42; _ n-1: 1; } +unchecked swt: (Any -> Any) +(swt n) = switch n = n { 0: 42; _ n-1: 1; } -(fld) = λ%arg0 use list = %arg0; fold list = list { List/Cons: 1; List/Nil: 2; } +unchecked fld: (Any -> Any) +(fld list) = fold list = list { List/Cons: 1; List/Nil: 2; } +unchecked bnd: Any (bnd) = bend x = 0, { when (< x 10): (List/Cons x (fork (+ x 1))); else: List/Nil } +unchecked era: Any (era) = let * = (+ 2 3); let the_expr_killer = *; (the_expr_killer 9) +unchecked sup: Any (sup) = let x = {(List/Cons 1 (List/Cons 2 List/Nil)) (List/Cons 3 (List/Cons 4 (List/Cons 5 (List/Cons 6 List/Nil))))}; x +unchecked main: Any (main) = with IO { ask x = IO.read; x } +List/Nil: (List T) (List/Nil) = λ%x (%x List/Nil/tag) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λhead λtail λ%x (%x List/Cons/tag head tail) +Map/Node: (T -> (Map T) -> (Map T) -> (Map T)) (Map/Node) = λvalue λleft λright λ%x (%x Map/Node/tag value left right) +Map/Leaf: (Map T) (Map/Leaf) = λ%x (%x Map/Leaf/tag) +Point/Point: (Any -> Any -> Point) (Point/Point) = λx λy λ%x (%x Point/Point/tag x y) +Bool/True: Bool (Bool/True) = λ%x (%x Bool/True/tag) +Bool/False: Bool (Bool/False) = λ%x (%x Bool/False/tag) +List/Nil/tag: u24 (List/Nil/tag) = 0 +List/Cons/tag: u24 (List/Cons/tag) = 1 +Map/Node/tag: u24 (Map/Node/tag) = 0 +Map/Leaf/tag: u24 (Map/Leaf/tag) = 1 +Point/Point/tag: u24 (Point/Point/tag) = 0 +Bool/True/tag: u24 (Bool/True/tag) = 0 +Bool/False/tag: u24 (Bool/False/tag) = 1 diff --git a/tests/snapshots/parse_file__multi_line_comment.bend.snap b/tests/snapshots/parse_file__multi_line_comment.bend.snap index 0782b826f..9c672edde 100644 --- a/tests/snapshots/parse_file__multi_line_comment.bend.snap +++ b/tests/snapshots/parse_file__multi_line_comment.bend.snap @@ -2,22 +2,32 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/multi_line_comment.bend --- -(X) = λ%arg0 λ%arg1 λ%arg2 use x = %arg2; x +unchecked X: Any +(X x x x) = x -(String/is_empty) = λ%arg0 use s = %arg0; match s = s { String/Nil: 1; String/Cons: 0; } +unchecked String/is_empty: Any +(String/is_empty s) = match s = s { String/Nil: 1; String/Cons: 0; } +unchecked main: Any (main) = 0 -(String/not_empty) = λ%arg0 use s = %arg0; match s = s { String/Nil: 0; String/Cons: 1; } +unchecked String/not_empty: (Any -> Any) +(String/not_empty s) = match s = s { String/Nil: 0; String/Cons: 1; } +Foo/Foo: (Any -> Foo) (Foo/Foo) = λfoo λ%x (%x Foo/Foo/tag foo) +Bar: (Any -> Bar) (Bar) = λbar λ%x (%x Bar/tag bar) +V/V: V (V/V) = λ%x (%x V/V/tag) +Foo/Foo/tag: u24 (Foo/Foo/tag) = 0 +Bar/tag: u24 (Bar/tag) = 0 +V/V/tag: u24 (V/V/tag) = 0 diff --git a/tests/snapshots/parse_file__redefinition_builtin.bend.snap b/tests/snapshots/parse_file__redefinition_builtin.bend.snap index ee51c4004..2df6576de 100644 --- a/tests/snapshots/parse_file__redefinition_builtin.bend.snap +++ b/tests/snapshots/parse_file__redefinition_builtin.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/parse_file/redefinition_builtin.bend Errors: In tests/golden_tests/parse_file/redefinition_builtin.bend : Redefinition of builtin (function) 'Map/get'. +Location: end of input  1 | def Map/get(m):  2 |  return m  diff --git a/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap b/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap index 5c6e24669..4580960a1 100644 --- a/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap +++ b/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend Errors: In tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend : Redefinition of builtin (constructor) 'String/Cons'. +Location: end of input  1 | def String/Cons(x):  2 |  return x  diff --git a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap index b31d21b8a..5b9813ec5 100644 --- a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap +++ b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap @@ -5,5 +5,6 @@ input_file: tests/golden_tests/parse_file/redefinition_fun_imp.bend Errors: In tests/golden_tests/parse_file/redefinition_fun_imp.bend : Redefinition of function 'A'. +Location: end of input  3 | def A:  4 |  return 0 diff --git a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap index da1ddb800..036a1c3e5 100644 --- a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap +++ b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_imp_fun.bend Errors: In tests/golden_tests/parse_file/redefinition_imp_fun.bend : Redefinition of function 'A'. +Location: end of input  5 | (A) = 1 diff --git a/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap b/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap index 189f008db..5e17b4b34 100644 --- a/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap +++ b/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap @@ -5,5 +5,6 @@ input_file: tests/golden_tests/parse_file/redefinition_type_with_object.bend Errors: In tests/golden_tests/parse_file/redefinition_type_with_object.bend : Redefinition of builtin (type) 'IO'. +Location: end of input  1 | object IO { run }  diff --git a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap index 807d09e32..3da28ff91 100644 --- a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_with_def_between.bend Errors: In tests/golden_tests/parse_file/redefinition_with_def_between.bend : Redefinition of function 'A'. +Location: end of input  4 | (A) = @x x diff --git a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap index 005d6a4a2..2d75ae64a 100644 --- a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_with_object_between.bend Errors: In tests/golden_tests/parse_file/redefinition_with_object_between.bend : Redefinition of function 'A'. +Location: end of input  3 | A = 1 diff --git a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap index 7280f6a08..4165c1645 100644 --- a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/redefinition_with_type_between.bend Errors: In tests/golden_tests/parse_file/redefinition_with_type_between.bend : Redefinition of function 'A'. +Location:  3 | A = 1 diff --git a/tests/snapshots/parse_file__repeated_adt_name.bend.snap b/tests/snapshots/parse_file__repeated_adt_name.bend.snap index 434fc2a0c..5b68a2698 100644 --- a/tests/snapshots/parse_file__repeated_adt_name.bend.snap +++ b/tests/snapshots/parse_file__repeated_adt_name.bend.snap @@ -5,6 +5,7 @@ input_file: tests/golden_tests/parse_file/repeated_adt_name.bend Errors: In tests/golden_tests/parse_file/repeated_adt_name.bend : Redefinition of type 'Foo'. +Location:  2 | type Foo = B  3 |   4 | main = * diff --git a/tests/snapshots/parse_file__repeated_datatype_name.bend.snap b/tests/snapshots/parse_file__repeated_datatype_name.bend.snap index 8e23b5667..71e7d11ad 100644 --- a/tests/snapshots/parse_file__repeated_datatype_name.bend.snap +++ b/tests/snapshots/parse_file__repeated_datatype_name.bend.snap @@ -4,7 +4,6 @@ input_file: tests/golden_tests/parse_file/repeated_datatype_name.bend --- Errors: In tests/golden_tests/parse_file/repeated_datatype_name.bend : -- information: Found a repeated field 'Expr' in constructor Expr/Plus. -- location: - 3 | | (Plus Expr Expr) - +Found a repeated field 'Expr' in constructor Expr/Plus. +Location: + 3 | | (Plus Expr Expr) diff --git a/tests/snapshots/parse_file__scape_chars.bend.snap b/tests/snapshots/parse_file__scape_chars.bend.snap index f626e7b78..8d84f22fa 100644 --- a/tests/snapshots/parse_file__scape_chars.bend.snap +++ b/tests/snapshots/parse_file__scape_chars.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/scape_chars.bend --- +unchecked main: Any (main) = (String/Cons 92 (String/Cons 32 (String/Cons 10 (String/Cons 32 (String/Cons 9 (String/Cons 32 (String/Cons 34 String/Nil))))))) +String/Nil: String (String/Nil) = λ%x (%x String/Nil/tag) +String/Cons: (u24 -> String -> String) (String/Cons) = λhead λtail λ%x (%x String/Cons/tag head tail) +String/Nil/tag: u24 (String/Nil/tag) = 0 +String/Cons/tag: u24 (String/Cons/tag) = 1 diff --git a/tests/snapshots/parse_file__tab.bend.snap b/tests/snapshots/parse_file__tab.bend.snap index a41337cec..4eb2782e0 100644 --- a/tests/snapshots/parse_file__tab.bend.snap +++ b/tests/snapshots/parse_file__tab.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/parse_file/tab.bend Errors: In tests/golden_tests/parse_file/tab.bend : Tabs are not accepted for indentation. - 2 |  x = 2 +Location: + 2 |  x = 2 diff --git a/tests/snapshots/parse_file__tuple_assign.bend.snap b/tests/snapshots/parse_file__tuple_assign.bend.snap index c87d16ff4..b2fc1aa27 100644 --- a/tests/snapshots/parse_file__tuple_assign.bend.snap +++ b/tests/snapshots/parse_file__tuple_assign.bend.snap @@ -2,4 +2,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_assign.bend --- +unchecked main: Any (main) = let (first, second) = (1, (2, 3)); second diff --git a/tests/snapshots/parse_file__tuple_commas.bend.snap b/tests/snapshots/parse_file__tuple_commas.bend.snap index c429d7111..dad4c062f 100644 --- a/tests/snapshots/parse_file__tuple_commas.bend.snap +++ b/tests/snapshots/parse_file__tuple_commas.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_commas.bend --- +unchecked main: Any (main) = let tup = ((fst 1 λx x), 2); (tup, 3, (4, 5)) -(fst) = λ%arg0 λ%arg1 use y = %arg1; use x = %arg0; x +unchecked fst: (Any -> Any -> Any) +(fst x y) = x diff --git a/tests/snapshots/parse_file__tuple_need_parens.bend.snap b/tests/snapshots/parse_file__tuple_need_parens.bend.snap index 17e7cd8fc..9b8c1fcb4 100644 --- a/tests/snapshots/parse_file__tuple_need_parens.bend.snap +++ b/tests/snapshots/parse_file__tuple_need_parens.bend.snap @@ -2,8 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_need_parens.bend --- -Errors: -In tests/golden_tests/parse_file/tuple_need_parens.bend : -- expected: ':' -- detected: - 2 | if 1, 2: +unchecked main: Any +(main) = switch %pred = (1, 2) { 0: (String/Cons 104 (String/Cons 109 (String/Cons 109 (String/Cons 109 String/Nil)))); _ %pred-1: (String/Cons 110 (String/Cons 111 (String/Cons 116 String/Nil))); } + +String/Nil: String +(String/Nil) = λ%x (%x String/Nil/tag) + +String/Cons: (u24 -> String -> String) +(String/Cons) = λhead λtail λ%x (%x String/Cons/tag head tail) + +String/Nil/tag: u24 +(String/Nil/tag) = 0 + +String/Cons/tag: u24 +(String/Cons/tag) = 1 diff --git a/tests/snapshots/run_file__checked_scott_encoding.bend.snap b/tests/snapshots/run_file__checked_scott_encoding.bend.snap new file mode 100644 index 000000000..f8986b3ff --- /dev/null +++ b/tests/snapshots/run_file__checked_scott_encoding.bend.snap @@ -0,0 +1,7 @@ +--- +source: tests/golden_tests.rs +input_file: tests/golden_tests/run_file/checked_scott_encoding.bend +--- +Errors: +In λa λb (a λe λf λg λh (g e (scott_concat f b)) b): + Can't unify '(e -> c -> j)' and '(((d -> e -> (d -> j -> k) -> g -> k) -> c -> m) -> c -> m)'. Variable 'e' occurs in '((d -> e -> (d -> j -> k) -> g -> k) -> c -> m)' diff --git a/tests/snapshots/run_file__num_cast.bend.snap b/tests/snapshots/run_file__num_cast.bend.snap index 320e6cb24..d781ab5c4 100644 --- a/tests/snapshots/run_file__num_cast.bend.snap +++ b/tests/snapshots/run_file__num_cast.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/num_cast.bend --- NumScott: -(0, (1, (123456, (16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (-1, (+1, (+0, (+400, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, (400.000, (1.000, (1.500, (-3.000, (-3.500, (inf, (-inf, NaN))))))))))))))))))))))))))))))))))))))))))) +(16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, 400.000))))))))))))))))))))))))))))) Scott: -(0, (1, (123456, (16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (-1, (+1, (+0, (+400, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, (400.000, (1.000, (1.500, (-3.000, (-3.500, (inf, (-inf, NaN))))))))))))))))))))))))))))))))))))))))))) +(16777215, (1, (0, (400, (1, (1, (0, (0, (16777215, (0, (0, (99999, (+0, (+1, (+123456, (+1, (+1, (-3, (-3, (+8388607, (-8388608, (+0, (99999, (0.000, (1.000, (123456.000, (-1.000, (1.000, (0.000, 400.000))))))))))))))))))))))))))))) diff --git a/tests/snapshots/run_file__override_list_ctr.bend.snap b/tests/snapshots/run_file__override_list_ctr.bend.snap index 789f8c5ef..732e5bede 100644 --- a/tests/snapshots/run_file__override_list_ctr.bend.snap +++ b/tests/snapshots/run_file__override_list_ctr.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/run_file/override_list_ctr.bend Errors: In tests/golden_tests/run_file/override_list_ctr.bend : Redefinition of builtin (constructor) 'List/Nil'. +Location:  1 | List/Nil = * diff --git a/tests/snapshots/run_file__override_str_ctr.bend.snap b/tests/snapshots/run_file__override_str_ctr.bend.snap index 34d01367a..4c30deca1 100644 --- a/tests/snapshots/run_file__override_str_ctr.bend.snap +++ b/tests/snapshots/run_file__override_str_ctr.bend.snap @@ -5,4 +5,5 @@ input_file: tests/golden_tests/run_file/override_str_ctr.bend Errors: In tests/golden_tests/run_file/override_str_ctr.bend : Redefinition of builtin (constructor) 'String/Cons'. +Location:  1 | String/Cons = * diff --git a/tests/snapshots/run_file__superposed_is_even.bend.snap b/tests/snapshots/run_file__superposed_is_even.bend.snap index 811a010a5..eafb5fcd1 100644 --- a/tests/snapshots/run_file__superposed_is_even.bend.snap +++ b/tests/snapshots/run_file__superposed_is_even.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/superposed_is_even.bend --- NumScott: -{{B/T B/F} {B/T B/F}} +{{MyBool/T MyBool/F} {MyBool/T MyBool/F}} Scott: -{{B/T B/F} {B/T B/F}} +{{MyBool/T MyBool/F} {MyBool/T MyBool/F}} diff --git a/tests/snapshots/simplify_matches__adt_tup_era.bend.snap b/tests/snapshots/simplify_matches__adt_tup_era.bend.snap index 33931e508..323df4bdb 100644 --- a/tests/snapshots/simplify_matches__adt_tup_era.bend.snap +++ b/tests/snapshots/simplify_matches__adt_tup_era.bend.snap @@ -2,10 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/adt_tup_era.bend --- +unchecked Foo: Any (Foo) = λa match a { Tuple/Pair b c: (match b { Tuple/Pair d e: λf d; } c); } +unchecked Main: Any (Main) = (Foo (Tuple/Pair 1 5)) +Tuple/Pair: (Any -> Any -> Tuple) (Tuple/Pair) = λa λb λc (c Tuple/Pair/tag a b) +Tuple/Pair/tag: u24 (Tuple/Pair/tag) = 0 diff --git a/tests/snapshots/simplify_matches__already_flat.bend.snap b/tests/snapshots/simplify_matches__already_flat.bend.snap index 1e11decca..276be4380 100644 --- a/tests/snapshots/simplify_matches__already_flat.bend.snap +++ b/tests/snapshots/simplify_matches__already_flat.bend.snap @@ -2,50 +2,74 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/already_flat.bend --- +unchecked Rule1: Any (Rule1) = λa a +unchecked Rule2: Any (Rule2) = λa λb b +unchecked Rule3: Any (Rule3) = λa λb λc λd (a b c d) +unchecked Rule4: Any (Rule4) = λa match a { Foo/CtrA: λb b; Foo/CtrB c: c; } +unchecked Rule5: Any (Rule5) = λa match a { Bar/CtrA1 b: λc let d = c; (b d); Bar/CtrA2 e f: λg let h = g; (e f h); Bar/CtrA3 i: λj let k = j; (match k { Baz/CtrB0: λl (Bar/CtrA3 l); Baz/CtrB1 m: λn (Bar/CtrA3 n m); Baz/CtrB2 o: λp (Bar/CtrA3 p (Baz/CtrB2 o)); Baz/CtrB3 q: λr (r q); } i); } +unchecked Rule6: Any (Rule6) = λa a +Foo/CtrA: Foo (Foo/CtrA) = λa (a Foo/CtrA/tag) +Foo/CtrB: (Any -> Foo) (Foo/CtrB) = λa λb (b Foo/CtrB/tag a) +Bar/CtrA1: (Any -> Bar) (Bar/CtrA1) = λa λb (b Bar/CtrA1/tag a) +Bar/CtrA2: (Any -> Any -> Bar) (Bar/CtrA2) = λa λb λc (c Bar/CtrA2/tag a b) +Bar/CtrA3: (Any -> Bar) (Bar/CtrA3) = λa λb (b Bar/CtrA3/tag a) +Baz/CtrB0: Baz (Baz/CtrB0) = λa (a Baz/CtrB0/tag) +Baz/CtrB1: (Any -> Baz) (Baz/CtrB1) = λa λb (b Baz/CtrB1/tag a) +Baz/CtrB2: (Any -> Baz) (Baz/CtrB2) = λa λb (b Baz/CtrB2/tag a) +Baz/CtrB3: (Any -> Baz) (Baz/CtrB3) = λa λb (b Baz/CtrB3/tag a) +Foo/CtrA/tag: u24 (Foo/CtrA/tag) = 0 +Foo/CtrB/tag: u24 (Foo/CtrB/tag) = 1 +Bar/CtrA1/tag: u24 (Bar/CtrA1/tag) = 0 +Bar/CtrA2/tag: u24 (Bar/CtrA2/tag) = 1 +Bar/CtrA3/tag: u24 (Bar/CtrA3/tag) = 2 +Baz/CtrB0/tag: u24 (Baz/CtrB0/tag) = 0 +Baz/CtrB1/tag: u24 (Baz/CtrB1/tag) = 1 +Baz/CtrB2/tag: u24 (Baz/CtrB2/tag) = 2 +Baz/CtrB3/tag: u24 (Baz/CtrB3/tag) = 3 diff --git a/tests/snapshots/simplify_matches__bits_dec.bend.snap b/tests/snapshots/simplify_matches__bits_dec.bend.snap index d8d452d25..12adb4c6b 100644 --- a/tests/snapshots/simplify_matches__bits_dec.bend.snap +++ b/tests/snapshots/simplify_matches__bits_dec.bend.snap @@ -2,16 +2,23 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/bits_dec.bend --- +unchecked Data.Bits.dec: Any (Data.Bits.dec) = λa match a { Data/Bits/e: Data/Bits/e; Data/Bits/o b: match b { Data/Bits/e: Data/Bits/e; Data/Bits/o c: (Data/Bits/i (Data.Bits.dec c)); Data/Bits/i d: (Data/Bits/i (Data.Bits.dec d)); }; Data/Bits/i e: match e { Data/Bits/e: (Data/Bits/o Data/Bits/e); Data/Bits/o f: (Data/Bits/o f); Data/Bits/i g: (Data/Bits/o g); }; } +Data/Bits/e: Data/Bits (Data/Bits/e) = λa (a Data/Bits/e/tag) +Data/Bits/o: (Any -> Data/Bits) (Data/Bits/o) = λa λb (b Data/Bits/o/tag a) +Data/Bits/i: (Any -> Data/Bits) (Data/Bits/i) = λa λb (b Data/Bits/i/tag a) +Data/Bits/e/tag: u24 (Data/Bits/e/tag) = 0 +Data/Bits/o/tag: u24 (Data/Bits/o/tag) = 1 +Data/Bits/i/tag: u24 (Data/Bits/i/tag) = 2 diff --git a/tests/snapshots/simplify_matches__complex_with_case.bend.snap b/tests/snapshots/simplify_matches__complex_with_case.bend.snap index c01798f0c..e21173c2e 100644 --- a/tests/snapshots/simplify_matches__complex_with_case.bend.snap +++ b/tests/snapshots/simplify_matches__complex_with_case.bend.snap @@ -2,14 +2,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/complex_with_case.bend --- +unchecked map: Any (map) = λa λb (match b { Tree_/Node c d e f: λg (Tree_/Node (map g c) (map g d) (map g e) (map g f)); Tree_/Leaf h: λi (Tree_/Leaf (i h)); } a) +unchecked main: Any (main) = map +Tree_/Node: (Any -> Any -> Any -> Any -> Tree_) (Tree_/Node) = λa λb λc λd λe (e Tree_/Node/tag a b c d) +Tree_/Leaf: (Any -> Tree_) (Tree_/Leaf) = λa λb (b Tree_/Leaf/tag a) +Tree_/Node/tag: u24 (Tree_/Node/tag) = 0 +Tree_/Leaf/tag: u24 (Tree_/Leaf/tag) = 1 diff --git a/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap b/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap index bba06a8f8..de945c1f6 100644 --- a/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap +++ b/tests/snapshots/simplify_matches__double_unwrap_box.bend.snap @@ -2,10 +2,14 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/double_unwrap_box.bend --- +unchecked DoubleUnbox: Any (DoubleUnbox) = λa match a { Boxed/Box b: match b { Boxed/Box c: λd let e = d; let f = e; c; }; } +unchecked Main: Any (Main) = (DoubleUnbox (Boxed/Box (Boxed/Box 0)) 5) +Boxed/Box: (Any -> Boxed) (Boxed/Box) = λa λb (b Boxed/Box/tag a) +Boxed/Box/tag: u24 (Boxed/Box/tag) = 0 diff --git a/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap b/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap index 47244b401..12acb88c8 100644 --- a/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap +++ b/tests/snapshots/simplify_matches__double_unwrap_maybe.bend.snap @@ -2,14 +2,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/double_unwrap_maybe.bend --- +unchecked DoubleUnwrap: Any (DoubleUnwrap) = λa match a { Maybe/Some b: match b { Maybe/Some c: λd let e = d; let f = e; c; Maybe/None: λg let h = g; let i = h; i; }; Maybe/None: λj let k = j; k; } +unchecked Main: Any (Main) = (DoubleUnwrap (Maybe/Some Maybe/None) 5) +Maybe/Some: (Any -> Maybe) (Maybe/Some) = λa λb (b Maybe/Some/tag a) +Maybe/None: Maybe (Maybe/None) = λa (a Maybe/None/tag) +Maybe/Some/tag: u24 (Maybe/Some/tag) = 0 +Maybe/None/tag: u24 (Maybe/None/tag) = 1 diff --git a/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap b/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap index f5be98805..addcb36b1 100644 --- a/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap +++ b/tests/snapshots/simplify_matches__flatten_with_terminal.bend.snap @@ -2,14 +2,20 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/flatten_with_terminal.bend --- +unchecked Foo: Any (Foo) = λa switch a { 0: λb let c = b; match c { A_t/A d: match d { B_t/B: B_t/B; }; }; _ e: λf let g = f; *; } +unchecked main: Any (main) = (Foo 2 (A_t/A B_t/B)) +A_t/A: (Any -> A_t) (A_t/A) = λa λb (b A_t/A/tag a) +B_t/B: B_t (B_t/B) = λa (a B_t/B/tag) +A_t/A/tag: u24 (A_t/A/tag) = 0 +B_t/B/tag: u24 (B_t/B/tag) = 0 diff --git a/tests/snapshots/simplify_matches__irrefutable_case.bend.snap b/tests/snapshots/simplify_matches__irrefutable_case.bend.snap index 4691e6a79..455013a7e 100644 --- a/tests/snapshots/simplify_matches__irrefutable_case.bend.snap +++ b/tests/snapshots/simplify_matches__irrefutable_case.bend.snap @@ -1,17 +1,24 @@ --- source: tests/golden_tests.rs -input_file: tests/golden_tests/simplify_matches/redundant_cases.bend +input_file: tests/golden_tests/simplify_matches/irrefutable_case.bend --- +unchecked l: Any (l) = 1001 +unchecked v1: Any (v1) = l +unchecked v2: Any (v2) = l +unchecked v3: Any (v3) = 2002 +unchecked v4: Any (v4) = 3003 +unchecked v5: Any (v5) = (λa a 5005) +unchecked main: Any (main) = (v1, v2, v3, v4, v5) diff --git a/tests/snapshots/simplify_matches__linearize_match_all.bend.snap b/tests/snapshots/simplify_matches__linearize_match_all.bend.snap index 5235f9c7d..5e74ffce1 100644 --- a/tests/snapshots/simplify_matches__linearize_match_all.bend.snap +++ b/tests/snapshots/simplify_matches__linearize_match_all.bend.snap @@ -2,30 +2,44 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/linearize_match_all.bend --- +unchecked A: Any (A) = λa switch a { 0: λb λc (b c); _ d: λe λf (d e f); } +unchecked B: Any (B) = λa λb λc (switch c { 0: λd λe (d e); _ f: λg λh (g h f); } a b) +unchecked C: Any (C) = λa λb λc switch c { 0: (a b); _ d: (a b d); } +unchecked D: Any (D) = λa switch a { 0: λb λc c; _ d: λe λf (d f); } +unchecked E: Any (E) = λa match a { ConsList/Cons b c: λd let e = d; (match e { ConsList/Cons f g: λh λi (h i f g); ConsList/Nil: λj λk (ConsList/Cons j k ConsList/Nil); } b c); ConsList/Nil: λl let m = l; (ConsList/Nil m); } +unchecked A2: Any (A2) = λa match a { ConsList/Cons b c: λd λe (b c d e); ConsList/Nil: λf λg (f g); } +unchecked B2: Any (B2) = λa λb λc (match c { ConsList/Cons d e: λf λg (f g d e); ConsList/Nil: λh λi (h i); } a b) +unchecked C2: Any (C2) = λa λb λc match c { ConsList/Cons d e: (a b d e); ConsList/Nil: (a b); } +unchecked D2: Any (D2) = λa match a { ConsList/Cons b c: λd λe (b c d e); ConsList/Nil: λf λg (f g); } +unchecked main: Any (main) = * +ConsList/Cons: (Any -> Any -> ConsList) (ConsList/Cons) = λa λb λc (c ConsList/Cons/tag a b) +ConsList/Nil: ConsList (ConsList/Nil) = λa (a ConsList/Nil/tag) +ConsList/Cons/tag: u24 (ConsList/Cons/tag) = 0 +ConsList/Nil/tag: u24 (ConsList/Nil/tag) = 1 diff --git a/tests/snapshots/simplify_matches__match_str.bend.snap b/tests/snapshots/simplify_matches__match_str.bend.snap index e34400d53..0fd9ec75a 100644 --- a/tests/snapshots/simplify_matches__match_str.bend.snap +++ b/tests/snapshots/simplify_matches__match_str.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/match_str.bend --- +unchecked is_as: Any (is_as) = λa match a { String/Nil: 1; String/Cons b c: (switch (- b 65) { 0: λd match d { String/Nil: 0; String/Cons e f: (switch (- e 115) { 0: λg match g { String/Nil: 2; String/Cons h i: 0; }; _ j: λk 0; } f); }; _ l: λm (switch (- l 31) { 0: λn match n { String/Nil: 0; String/Cons o p: (switch (- o 115) { 0: λq match q { String/Nil: 2; String/Cons r s: 0; }; _ t: λu 0; } p); }; _ v: λw 0; } m); } c); } +unchecked main: Any (main) = * diff --git a/tests/snapshots/simplify_matches__nested.bend.snap b/tests/snapshots/simplify_matches__nested.bend.snap index 05ada9d73..0f4e03554 100644 --- a/tests/snapshots/simplify_matches__nested.bend.snap +++ b/tests/snapshots/simplify_matches__nested.bend.snap @@ -2,24 +2,35 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested.bend --- +unchecked Rule: Any (Rule) = λa match a { Foo/CtrA b c: (match c { Bar/CtrB1 d: λe (e d); Bar/CtrB2 f g: λh (match f { Baz/CtrC: λi λj (i j); } h g); } b); Foo/CtrB k: k; } +Foo/CtrA: (Any -> Any -> Foo) (Foo/CtrA) = λa λb λc (c Foo/CtrA/tag a b) +Foo/CtrB: (Any -> Foo) (Foo/CtrB) = λa λb (b Foo/CtrB/tag a) +Bar/CtrB1: (Any -> Bar) (Bar/CtrB1) = λa λb (b Bar/CtrB1/tag a) +Bar/CtrB2: (Any -> Any -> Bar) (Bar/CtrB2) = λa λb λc (c Bar/CtrB2/tag a b) +Baz/CtrC: Baz (Baz/CtrC) = λa (a Baz/CtrC/tag) +Foo/CtrA/tag: u24 (Foo/CtrA/tag) = 0 +Foo/CtrB/tag: u24 (Foo/CtrB/tag) = 1 +Bar/CtrB1/tag: u24 (Bar/CtrB1/tag) = 0 +Bar/CtrB2/tag: u24 (Bar/CtrB2/tag) = 1 +Baz/CtrC/tag: u24 (Baz/CtrC/tag) = 0 diff --git a/tests/snapshots/simplify_matches__nested2.bend.snap b/tests/snapshots/simplify_matches__nested2.bend.snap index d4d731234..1ec4282ea 100644 --- a/tests/snapshots/simplify_matches__nested2.bend.snap +++ b/tests/snapshots/simplify_matches__nested2.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested2.bend --- +unchecked Foo: Any (Foo) = λa λb (match b { List/Nil: λc (c List/Nil); List/Cons d e: λf (match e { List/Nil: λg λh (g (List/Cons h List/Nil)); List/Cons i j: λk λl (k l i j); } f d); } a) +List/Nil: (List T) (List/Nil) = λa (a List/Nil/tag) +List/Cons: (T -> (List T) -> (List T)) (List/Cons) = λa λb λc (c List/Cons/tag a b) +List/Nil/tag: u24 (List/Nil/tag) = 0 +List/Cons/tag: u24 (List/Cons/tag) = 1 diff --git a/tests/snapshots/simplify_matches__nested_0ary.bend.snap b/tests/snapshots/simplify_matches__nested_0ary.bend.snap index 2f188c932..60efc52cc 100644 --- a/tests/snapshots/simplify_matches__nested_0ary.bend.snap +++ b/tests/snapshots/simplify_matches__nested_0ary.bend.snap @@ -2,12 +2,17 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/nested_0ary.bend --- +unchecked Unpack: Any (Unpack) = λa λb (match b { list/Cons c d: λe (match d { list/Cons f g: λh λi (h (list/Cons i (list/Cons f g))); list/Nil: λj λk k; } e c); list/Nil: λl list/Nil; } a) +list/Cons: (Any -> Any -> list) (list/Cons) = λa λb λc (c list/Cons/tag a b) +list/Nil: list (list/Nil) = λa (a list/Nil/tag) +list/Cons/tag: u24 (list/Cons/tag) = 0 +list/Nil/tag: u24 (list/Nil/tag) = 1 diff --git a/tests/snapshots/simplify_matches__redundant_with_era.bend.snap b/tests/snapshots/simplify_matches__redundant_with_era.bend.snap index b7c6e710b..75981be88 100644 --- a/tests/snapshots/simplify_matches__redundant_with_era.bend.snap +++ b/tests/snapshots/simplify_matches__redundant_with_era.bend.snap @@ -2,6 +2,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/redundant_with_era.bend --- +unchecked Fn2: Any (Fn2) = λa switch a { 0: λb let c = b; let (d, e) = c; let (f, g) = e; g; _ h: λi let j = i; let (k, l) = j; let (m, n) = l; n; } +unchecked main: Any (main) = * diff --git a/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap b/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap index d75933f7f..676abe503 100644 --- a/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap +++ b/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap @@ -6,6 +6,3 @@ input_file: tests/golden_tests/simplify_matches/wrong_fn_arity.bend In tests/golden_tests/simplify_matches/wrong_fn_arity.bend : In definition 'Foo': Incorrect pattern matching rule arity. Expected 3 args, found 0. - -Other diagnostics: -File has no 'main' definition.