diff --git a/CHANGELOG.md b/CHANGELOG.md index 6799e5317..1459f496d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project does not currently adhere to a particular versioning scheme. - Expand references to functions generated by the `float_combinators` pass inside the main function. ([#642][gh-642]) - Expand references inside constructors in the main function. ([#643][gh-643]) - Fix readback when hvm net has `a{n}` or `x{n}` vars. ([#659][gh-659]) +- Fix `wrap` inside `with` being deferred applied like a variable. ([#664][gh-664]) ### Added @@ -23,6 +24,7 @@ and this project does not currently adhere to a particular versioning scheme. - Add error message when input file is not found. ([#513][gh-513]) - Add `List/filter` and `String/{equals, filter}` builtins. - Add IO functions for loading dynamically linked libraries (`IO/DyLib/open`, `IO/DyLib/call`, `IO/DyLib/close`). ([#621][gh-621]) +- Add builtin functions `Tree/to_list`, `Tree/reverse`, `DiffList/concat` and `DiffList/wrap`. ([#662][gh-662]) ### Changed @@ -420,4 +422,6 @@ and this project does not currently adhere to a particular versioning scheme. [gh-648]: https://github.com/HigherOrderCO/Bend/issues/648 [gh-657]: https://github.com/HigherOrderCO/Bend/issues/657 [gh-659]: https://github.com/HigherOrderCO/Bend/pull/659 +[gh-662]: https://github.com/HigherOrderCO/Bend/pull/662 +[gh-664]: https://github.com/HigherOrderCO/Bend/issues/664 [Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD diff --git a/src/fun/check/unbound_vars.rs b/src/fun/check/unbound_vars.rs index cae0620d5..09d653dc0 100644 --- a/src/fun/check/unbound_vars.rs +++ b/src/fun/check/unbound_vars.rs @@ -38,8 +38,12 @@ impl Ctx<'_> { impl Term { /// Checks that all variables are bound. - /// Precondition: References have been resolved, implicit binds have been solved. - + /// + /// Precondition: References have been resolved, implicit binds + /// have been solved. + /// + /// `check_unbound_vars` should be called once before this, since + /// the original names will be lost after. pub fn check_unbound_vars<'a>( &'a mut self, scope: &mut HashMap<&'a Name, u64>, @@ -127,17 +131,17 @@ impl std::fmt::Display for UnboundVarErr { if var == desugar_bend::RECURSIVE_KW { write!( f, - "Unbound variable '{}'.\n Note: '{}' is only a keyword inside the 'when' arm of a 'bend'.", + "Unbound name '{}'.\n Note: '{}' is only a keyword inside the 'when' arm of a 'bend'.", var, desugar_bend::RECURSIVE_KW ) } else if let Some((pre, suf)) = var.rsplit_once('-') { write!( f, - "Unbound variable '{var}'. If you wanted to subtract '{pre}' from '{suf}', you must separate it with spaces ('{pre} - {suf}') since '-' is a valid name character." + "Unbound name '{var}'. If you wanted to subtract '{pre}' from '{suf}', you must separate it with spaces ('{pre} - {suf}') since '-' is a valid name character." ) } else { - write!(f, "Unbound variable '{var}'.") + write!(f, "Unbound name '{var}'.") } } UnboundVarErr::Global { var, declared, used } => match (declared, used) { diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 3046070f3..0b62f08ff 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -759,6 +759,11 @@ impl<'a> TermParser<'a> { let typ = self.parse_name()?; self.consume("{")?; let bod = self.parse_term()?; + let bod = Term::Use { + nam: Some(Name::new("wrap")), + val: Box::new(Term::Var { nam: Name::new(format!("{typ}/wrap")) }), + nxt: Box::new(bod), + }; self.consume("}")?; return Ok(Term::With { typ: Name::new(typ), bod: Box::new(bod) }); } diff --git a/src/fun/transform/desugar_use.rs b/src/fun/transform/desugar_use.rs index 0fdc2c5cf..243b64b0c 100644 --- a/src/fun/transform/desugar_use.rs +++ b/src/fun/transform/desugar_use.rs @@ -13,6 +13,8 @@ impl Book { /// /// // Transforms to: /// (λx x λx x λx x) + /// + /// Should only be used after `make_var_names_unique`. /// ``` pub fn desugar_use(&mut self) { for def in self.defs.values_mut() { diff --git a/src/fun/transform/desugar_with_blocks.rs b/src/fun/transform/desugar_with_blocks.rs index 14df5781e..6f1229992 100644 --- a/src/fun/transform/desugar_with_blocks.rs +++ b/src/fun/transform/desugar_with_blocks.rs @@ -33,13 +33,7 @@ impl Term { maybe_grow(|| { 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) }; + *self = std::mem::take(bod); } if let Term::Ask { pat, val, nxt } = self { diff --git a/src/fun/transform/fix_match_terms.rs b/src/fun/transform/fix_match_terms.rs index 149741198..5e558644e 100644 --- a/src/fun/transform/fix_match_terms.rs +++ b/src/fun/transform/fix_match_terms.rs @@ -22,7 +22,7 @@ impl Ctx<'_> { /// /// Example: /// For the program - /// ```hvm + /// ```bend /// data MyList = (Cons h t) | Nil /// match x { /// Cons: (A x.h x.t) @@ -35,9 +35,9 @@ impl Ctx<'_> { /// * The bind `%matched-2` will be generated and stored in the switch term. /// * If either was missing one of the match cases (a number or a constructor), we'd get an error. /// * If either included one of the cases more than once (including wildcard patterns), we'd get a warning. - /// ```hvm + /// ```bend /// 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 } /// } /// ``` diff --git a/src/fun/transform/unique_names.rs b/src/fun/transform/unique_names.rs index 7be960393..1e537a5c3 100644 --- a/src/fun/transform/unique_names.rs +++ b/src/fun/transform/unique_names.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; impl Book { /// Makes all variables in each definition have a new unique name. /// Skips unbound variables. + /// /// Precondition: Definition references have been resolved. pub fn make_var_names_unique(&mut self) { for def in self.defs.values_mut() { @@ -32,7 +33,7 @@ pub struct UniqueNameGenerator { } impl UniqueNameGenerator { - // Recursively assign an id to each variable in the term, then convert each id into a unique name. + /// Recursively assign an id to each variable in the term, then convert each id into a unique name. pub fn unique_names_in_term(&mut self, term: &mut Term) { // Note: we can't use the children iterators here because we mutate the binds, // which are shared across multiple children. @@ -189,14 +190,16 @@ impl UniqueNameGenerator { } } - fn use_var(&self, nam: &Name) -> Name { + fn use_var(&mut self, nam: &Name) -> Name { if let Some(vars) = self.name_map.get(nam) { let var_id = *vars.last().unwrap(); Name::from(var_id) } else { // Skip unbound variables. // With this, we can use this function before checking for unbound vars. - nam.clone() + let nam = Name::from(self.name_count); + self.name_count += 1; + nam } } } diff --git a/src/imp/parser.rs b/src/imp/parser.rs index fe6b94bbc..85227b5d7 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -900,6 +900,11 @@ impl<'a> PyParser<'a> { self.consume_indent_exactly(*indent)?; let (bod, nxt_indent) = self.parse_statement(indent)?; + let bod = Stmt::Use { + nam: Name::new("wrap"), + val: Box::new(Expr::Var { nam: Name::new(format!("{typ}/wrap")) }), + nxt: Box::new(bod), + }; indent.exit_level(); if nxt_indent == *indent { diff --git a/src/lib.rs b/src/lib.rs index 2af6bf988..1e7f1e433 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,10 +113,13 @@ pub fn desugar_book( ctx.desugar_bend()?; ctx.desugar_fold()?; - ctx.desugar_with_blocks()?; ctx.check_unbound_vars()?; + ctx.book.make_var_names_unique(); + ctx.book.desugar_use(); + ctx.desugar_with_blocks()?; + // Auto match linearization ctx.book.make_var_names_unique(); ctx.book.desugar_use(); diff --git a/tests/golden_tests/compile_file/ask_outside_do.bend b/tests/golden_tests/compile_file/ask_outside_do.bend index e17ddddb5..cb2c79ecd 100644 --- a/tests/golden_tests/compile_file/ask_outside_do.bend +++ b/tests/golden_tests/compile_file/ask_outside_do.bend @@ -1 +1 @@ -main = ask x = (Result/Ok x); x +main = ask x = (Result/Ok 0); x diff --git a/tests/golden_tests/compile_file_o_all/cyclic_dup.bend b/tests/golden_tests/compile_file_o_all/cyclic_dup.bend index 421e8e8c3..b09b78881 100644 --- a/tests/golden_tests/compile_file_o_all/cyclic_dup.bend +++ b/tests/golden_tests/compile_file_o_all/cyclic_dup.bend @@ -1 +1 @@ -main = let {x1 x2} = y1; let {y1 y2} = x1; (x2 y2) +main = let {$x1 x2} = $y1; let {$y1 y2} = $x1; (x2 y2) diff --git a/tests/golden_tests/desugar_file/bind_syntax.bend b/tests/golden_tests/desugar_file/bind_syntax.bend index 3b7d55d99..debbc52af 100644 --- a/tests/golden_tests/desugar_file/bind_syntax.bend +++ b/tests/golden_tests/desugar_file/bind_syntax.bend @@ -1,6 +1,8 @@ Result/bind (Result/Ok val) f = ((undefer f) val) Result/bind err _ = err +Result/wrap x = (Result/Ok x) + safe_div a b = switch b { 0: (Result/Err "Div by 0") _: (Result/Ok (/ a b)) diff --git a/tests/golden_tests/run_file/do_block_mixed.bend b/tests/golden_tests/run_file/do_block_mixed.bend index 12db894f5..e4298b5ba 100644 --- a/tests/golden_tests/run_file/do_block_mixed.bend +++ b/tests/golden_tests/run_file/do_block_mixed.bend @@ -4,6 +4,8 @@ Result/bind r nxt = match r { Result/Err: r } +Result/wrap x = (Result/Ok x) + main = with Result { let x = 1 let y = (Result/Ok x) diff --git a/tests/golden_tests/run_file/recursive_bind.bend b/tests/golden_tests/run_file/recursive_bind.bend index db57f17df..3537a89cb 100644 --- a/tests/golden_tests/run_file/recursive_bind.bend +++ b/tests/golden_tests/run_file/recursive_bind.bend @@ -1,6 +1,8 @@ Result/bind (Result/Ok val) f = ((undefer f) val) Result/bind err _ = err +Result/wrap x = (Result/Ok x) + Bar x = (Result/Err 0) Foo x y = with Result { diff --git a/tests/golden_tests/run_file/strict_monad_fn.bend b/tests/golden_tests/run_file/strict_monad_fn.bend index df14bca8e..ba148ddd5 100644 --- a/tests/golden_tests/run_file/strict_monad_fn.bend +++ b/tests/golden_tests/run_file/strict_monad_fn.bend @@ -3,6 +3,9 @@ Result/bind = @val @nxt match val { Result/Ok: ((undefer nxt) val.val) Result/Err: (Result/Err val.val) } + +Result/wrap x = (Result/Ok x) + Result/foo x y = with Result { ask a = (Result/Ok x) diff --git a/tests/snapshots/cli__warn_and_err.bend.snap b/tests/snapshots/cli__warn_and_err.bend.snap index e972dc58c..56af3ae60 100644 --- a/tests/snapshots/cli__warn_and_err.bend.snap +++ b/tests/snapshots/cli__warn_and_err.bend.snap @@ -8,4 +8,4 @@ input_file: tests/golden_tests/cli/warn_and_err.bend Errors: In definition 'Main': - Unbound variable 'a'. + Unbound name 'a'. diff --git a/tests/snapshots/compile_file__ask_outside_do.bend.snap b/tests/snapshots/compile_file__ask_outside_do.bend.snap index d8c0ab4b8..1344f41f9 100644 --- a/tests/snapshots/compile_file__ask_outside_do.bend.snap +++ b/tests/snapshots/compile_file__ask_outside_do.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/ask_outside_do.bend --- Errors: In definition 'main': - Monadic bind operation 'x <- ...' used outside of a `do` block. + Monadic bind operation 'a <- ...' used outside of a `do` block. diff --git a/tests/snapshots/compile_file__unbound_var.bend.snap b/tests/snapshots/compile_file__unbound_var.bend.snap index a53e420c1..2d5fcdd33 100644 --- a/tests/snapshots/compile_file__unbound_var.bend.snap +++ b/tests/snapshots/compile_file__unbound_var.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/unbound_var.bend --- Errors: In definition 'main': - Unbound variable 'a'. + Unbound name 'a'. diff --git a/tests/snapshots/compile_file__unbound_var_scope.bend.snap b/tests/snapshots/compile_file__unbound_var_scope.bend.snap index 2976e0743..0b2e52348 100644 --- a/tests/snapshots/compile_file__unbound_var_scope.bend.snap +++ b/tests/snapshots/compile_file__unbound_var_scope.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/unbound_var_scope.bend --- Errors: In definition 'main': - Unbound variable 'b'. + Unbound name 'b'. diff --git a/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap b/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap index 549c276db..bc61b94cc 100644 --- a/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap +++ b/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/unbound_with_tup_pattern.bend --- Errors: In definition 'Foo': - Unbound variable 'a'. + Unbound name 'a'. diff --git a/tests/snapshots/compile_file__warn_and_err.bend.snap b/tests/snapshots/compile_file__warn_and_err.bend.snap index 3e331175a..e959d1385 100644 --- a/tests/snapshots/compile_file__warn_and_err.bend.snap +++ b/tests/snapshots/compile_file__warn_and_err.bend.snap @@ -8,4 +8,4 @@ input_file: tests/golden_tests/compile_file/warn_and_err.bend Errors: In definition 'Main': - Unbound variable 'a'. + Unbound name 'a'. diff --git a/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap b/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap index 726c4d9a4..d0aa4330e 100644 --- a/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap +++ b/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap @@ -4,5 +4,5 @@ input_file: tests/golden_tests/compile_file_o_all/bad_parens_making_erased_let.b --- Errors: In definition 'main': - Unbound variable 'two'. - Unbound variable 'qua'. + Unbound name 'two'. + Unbound name 'qua'. diff --git a/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap b/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap index 6114b7b49..5cc9025fa 100644 --- a/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap +++ b/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap @@ -3,5 +3,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/cyclic_dup.bend --- Errors: -In definition 'main': - Unbound variable 'y1'. +In compiled inet 'main': + Found term that compiles into an inet with a vicious cycle diff --git a/tests/snapshots/desugar_file__ask_branch.bend.snap b/tests/snapshots/desugar_file__ask_branch.bend.snap index c6a5750a3..1d4b7ac2a 100644 --- a/tests/snapshots/desugar_file__ask_branch.bend.snap +++ b/tests/snapshots/desugar_file__ask_branch.bend.snap @@ -10,7 +10,7 @@ input_file: tests/golden_tests/desugar_file/ask_branch.bend (IO/bind) = λa λb (a IO/bind__C2 b) -(main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc λd (c d) IO/wrap)) +(main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc (IO/wrap c))) (IO/Done) = λa λb λc (c IO/Done/tag a b) diff --git a/tests/snapshots/desugar_file__bind_syntax.bend.snap b/tests/snapshots/desugar_file__bind_syntax.bend.snap index eb850cbe9..fb2efbdc3 100644 --- a/tests/snapshots/desugar_file__bind_syntax.bend.snap +++ b/tests/snapshots/desugar_file__bind_syntax.bend.snap @@ -6,6 +6,8 @@ input_file: tests/golden_tests/desugar_file/bind_syntax.bend (Result/bind) = λa λb (a Result/bind__C2 b) +(Result/wrap) = λa (Result/Ok a) + (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) (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) diff --git a/tests/snapshots/parse_file__imp_program.bend.snap b/tests/snapshots/parse_file__imp_program.bend.snap index da62166e1..622d454ae 100644 --- a/tests/snapshots/parse_file__imp_program.bend.snap +++ b/tests/snapshots/parse_file__imp_program.bend.snap @@ -8,6 +8,10 @@ input_file: tests/golden_tests/parse_file/imp_program.bend (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); }; } +(IO/MAGIC) = (13683217, 16719857) + +(IO/wrap) = λ%arg0 use x = %arg0; (IO/Done IO/MAGIC x) + (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) (mk_point) = (Point/Point 1 2) @@ -36,7 +40,7 @@ input_file: tests/golden_tests/parse_file/imp_program.bend (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 -(main) = with IO { ask x = IO.read; x } +(main) = with IO { use wrap = IO/wrap; ask x = IO.read; x } (List/Nil) = λ%x (%x List/Nil/tag) @@ -46,6 +50,8 @@ input_file: tests/golden_tests/parse_file/imp_program.bend (Map/Leaf) = λ%x (%x Map/Leaf/tag) +(IO/Done) = λmagic λexpr λ%x (%x IO/Done/tag magic expr) + (Point/Point) = λx λy λ%x (%x Point/Point/tag x y) (Bool/True) = λ%x (%x Bool/True/tag) @@ -60,6 +66,8 @@ input_file: tests/golden_tests/parse_file/imp_program.bend (Map/Leaf/tag) = 1 +(IO/Done/tag) = 0 + (Point/Point/tag) = 0 (Bool/True/tag) = 0 diff --git a/tests/snapshots/run_file__unbound_wrap.bend.snap b/tests/snapshots/run_file__unbound_wrap.bend.snap index a154a25f4..1db66b0a2 100644 --- a/tests/snapshots/run_file__unbound_wrap.bend.snap +++ b/tests/snapshots/run_file__unbound_wrap.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/run_file/unbound_wrap.bend --- Errors: In definition 'main': - Reference to undefined function 'Maybe/wrap' + Unbound name 'Maybe/wrap'.