Expand refs in main, don't float combinators in main

This commit is contained in:
Nicolas Abril 2024-06-05 19:02:44 +02:00
parent c97e47107d
commit 94cada901d
38 changed files with 250 additions and 143 deletions

View File

@ -0,0 +1,121 @@
use crate::{
fun::{Book, Name, Pattern, Term},
maybe_grow,
};
use std::collections::HashMap;
impl Book {
/// Expands the main function so that it is not just a reference.
/// While technically correct, directly returning a reference is never what users want.
pub fn expand_main(&mut self) {
if self.entrypoint.is_none() {
return;
}
let main = self.defs.get_mut(self.entrypoint.as_ref().unwrap()).unwrap();
let mut main_bod = std::mem::take(&mut main.rule_mut().body);
let mut seen = vec![self.entrypoint.as_ref().unwrap().clone()];
main_bod.expand_ref_return(self, &mut seen, &mut 0);
let main = self.defs.get_mut(self.entrypoint.as_ref().unwrap()).unwrap();
main.rule_mut().body = main_bod;
}
}
impl Term {
/// Expands references in the main function that are in "return" position.
///
/// This applies to:
/// - When main returns a reference.
/// - When main returns a lambda whose body is a reference.
/// - When main returns a pair or superposition and one of its elements is a reference.
///
/// Only expand recursive functions once.
pub fn expand_ref_return(&mut self, book: &Book, seen: &mut Vec<Name>, globals_count: &mut usize) {
maybe_grow(|| match self {
Term::Ref { nam } => {
if seen.contains(nam) {
// Don't expand recursive references
} else {
seen.push(nam.clone());
let mut body = book.defs.get(nam).unwrap().rule().body.clone();
body.rename_unscoped(globals_count, &mut HashMap::new());
*self = body;
self.expand_ref_return(book, seen, globals_count);
seen.pop().unwrap();
}
}
Term::Fan { els, .. } | Term::List { els } => {
for el in els {
el.expand_ref_return(book, seen, globals_count);
}
}
Term::Lam { bod: nxt, .. }
| Term::With { bod: nxt, .. }
| Term::Open { bod: nxt, .. }
| Term::Let { nxt, .. }
| Term::Ask { nxt, .. }
| Term::Use { nxt, .. } => nxt.expand_ref_return(book, seen, globals_count),
Term::Var { .. }
| Term::Link { .. }
| Term::App { .. }
| Term::Num { .. }
| Term::Nat { .. }
| Term::Str { .. }
| Term::Oper { .. }
| Term::Mat { .. }
| Term::Swt { .. }
| Term::Fold { .. }
| Term::Bend { .. }
| Term::Era
| Term::Err => {}
})
}
}
impl Term {
/// Since expanded functions can contain unscoped variables, and
/// unscoped variable names must be unique, we need to rename them
/// to avoid conflicts.
fn rename_unscoped(&mut self, unscoped_count: &mut usize, unscoped_map: &mut HashMap<Name, Name>) {
match self {
Term::Let { pat, .. } | Term::Lam { pat, .. } => pat.rename_unscoped(unscoped_count, unscoped_map),
Term::Link { nam } => rename_unscoped(nam, unscoped_count, unscoped_map),
_ => {
// Isn't an unscoped bind or use, do nothing, just recurse.
}
}
for child in self.children_mut() {
child.rename_unscoped(unscoped_count, unscoped_map);
}
}
}
impl Pattern {
fn rename_unscoped(&mut self, unscoped_count: &mut usize, unscoped_map: &mut HashMap<Name, Name>) {
maybe_grow(|| {
match self {
Pattern::Chn(nam) => rename_unscoped(nam, unscoped_count, unscoped_map),
_ => {
// Pattern isn't an unscoped bind, just recurse.
}
}
for child in self.children_mut() {
child.rename_unscoped(unscoped_count, unscoped_map);
}
})
}
}
/// Generates a new name for an unscoped variable.
fn rename_unscoped(nam: &mut Name, unscoped_count: &mut usize, unscoped_map: &mut HashMap<Name, Name>) {
if let Some(new_nam) = unscoped_map.get(nam) {
*nam = new_nam.clone();
} else {
let new_nam = Name::new(format!("{nam}%{}", unscoped_count));
unscoped_map.insert(nam.clone(), new_nam.clone());
*unscoped_count += 1;
*nam = new_nam;
}
}

View File

@ -33,6 +33,15 @@ impl Book {
let mut ctx = FloatCombinatorsCtx::new(&book, max_size);
for (def_name, def) in self.defs.iter_mut() {
// Don't float combinators in the main entrypoint.
// This avoids making programs unexpectedly too lazy,
// returning just a reference without executing anything.
if let Some(main) = self.entrypoint.as_ref() {
if def_name == main {
continue;
}
}
let builtin = def.builtin;
let body = &mut def.rule_mut().body;
ctx.reset();

View File

@ -10,6 +10,7 @@ pub mod desugar_with_blocks;
pub mod encode_adts;
pub mod encode_match_terms;
pub mod expand_generated;
pub mod expand_main;
pub mod fix_match_defs;
pub mod fix_match_terms;
pub mod float_combinators;

View File

@ -148,6 +148,8 @@ pub fn desugar_book(
ctx.book.merge_definitions();
}
ctx.book.expand_main();
ctx.book.make_var_names_unique();
if !ctx.info.has_errors() {

View File

@ -0,0 +1,15 @@
# Despite having an unused variable in main, `map(tt,l)` should not be extracted into a new definition.
def map(fn, list):
fold list:
case List/Cons:
return List/Cons(fn(list.head), list.tail)
case List/Nil:
return []
def tt(x):
return x*2
def main():
l = [5,6,7,8]
k = [1,2,3,4]
return map(tt,l)

View File

@ -10,8 +10,6 @@ input_file: tests/golden_tests/cli/compile_all.bend
@Pair/Pair = (a (b ((0 (a (b c))) c)))
@main = b
& @Pair.get ~ (@main__C0 (a b))
& @Pair/Pair ~ (40 (2 a))
@main__C0 = ($([+] $(a b)) (a b))
@main = d
& @Pair.get ~ (($([+] $(a b)) (a b)) (c d))
& @Pair/Pair ~ (40 (2 c))

View File

@ -2,7 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/cli/desugar_bool_scott.bend
---
(main) = Boolean/True
(main) = λa λ* a
(Boolean/True) = λa λ* a

View File

@ -8,10 +8,6 @@ input_file: tests/golden_tests/cli/desugar_float_combinators.bend
(get) = λa (a get__C0 0)
(main) = (get main__C1)
(main) = (get (S (S Z)))
(get__C0) = λa (+ a 1)
(main__C0) = (S Z)
(main__C1) = (S main__C0)

View File

@ -8,4 +8,4 @@ input_file: tests/golden_tests/cli/desugar_merge.bend
(F__M_Z) = λ* λa a
(main) = F__M_Z
(main) = λ* λa a

View File

@ -27,15 +27,6 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
&!@Gen.go ~ (a (b c))
&!@Gen.go ~ (d (e f))
@Main__C0 = a
& @Gen ~ (4 a)
@Main__C1 = a
& @Reverse ~ (@Main__C0 a)
@Main__C2 = a
& @Sort ~ (@Main__C1 a)
@Map_/Both = (a (b ((@Map_/Both/tag (a (b c))) c)))
@Map_/Both/tag = 2
@ -173,5 +164,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
@ToMap__C3 = (?((@Map_/Free @ToMap__C2) a) a)
@main = a
& @Sum ~ (@Main__C2 a)
@main = d
& @Sum ~ (c d)
& @Sort ~ (b c)
& @Reverse ~ (a b)
& @Gen ~ (4 a)

View File

@ -4,9 +4,9 @@ input_file: tests/golden_tests/cli/run_pretty.bend
---
Result:
λa switch a = a {
0: λa switch a {
0: λb switch b = b {
0: "ba";
_: λ* "ta";
_: "ta";
};
_: λ* "ata";
}

View File

@ -2,8 +2,6 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/addition.bend
---
@main = a
& @main__C0 ~ (8 a)
@main__C0 = (a b)
@main = c
& (a b) ~ (8 c)
& $(1 $([+] $(a b))) ~ [+1]

View File

@ -2,7 +2,5 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/church_one.bend
---
@main = b
& @main__C0 ~ ((* (a a)) b)
@main__C0 = ((a (b c)) ({(c d) a} (b d)))
@main = f
& ((a (b c)) ({(c d) a} (b d))) ~ ((* (e e)) f)

View File

@ -2,17 +2,8 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/elif.bend
---
@main = d
& @main__C3 ~ (a (b (c d)))
& $(1 a) ~ [<2]
& $(2 b) ~ [>3]
& $(2 c) ~ [=2]
@main__C0 = (?((0 (* 2)) a) a)
@main__C1 = (a (?((@main__C0 (* (* 3))) (a b)) b))
@main__C2 = (a (b (?((@main__C1 (* (* (* 4)))) (a (b c))) c)))
@main__C3 = a
& $(2 ?((@main__C2 (* (* (* (* 1))))) a)) ~ [=1]
@main = j
& $(2 ?(((d (e (?(((b (?(((?((0 (* 2)) a) a) (* (* 3))) (b c)) c)) (* (* (* 4)))) (d (e f))) f))) (* (* (* (* 1))))) (g (h (i j))))) ~ [=1]
& $(1 g) ~ [<2]
& $(2 h) ~ [>3]
& $(2 i) ~ [=2]

View File

@ -2,12 +2,6 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/fst_snd.bend
---
@main = a
& @main__C2 ~ (@main__C1 a)
@main__C0 = ((a *) a)
@main__C1 = a
& @main__C0 ~ (((1 3) 2) a)
@main__C2 = ((* a) a)
@main = d
& ((* a) a) ~ (c d)
& ((b *) b) ~ (((1 3) 2) c)

View File

@ -2,11 +2,9 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/lets.bend
---
@main = @main__C0
& (a a) ~ *
@main__C0 = n
& (a a) ~ (f (i (m n)))
& (d d) ~ {(e f) e}
& (c c) ~ {(g (h i)) {g h}}
& (b b) ~ {(j (k (l m))) {j {k l}}}
@main = o
& (a a) ~ (g (j (n o)))
& (e e) ~ *
& (d d) ~ {(f g) f}
& (c c) ~ {(h (i j)) {h i}}
& (b b) ~ {(k (l (m n))) {k {l m}}}

View File

@ -18,4 +18,4 @@ input_file: tests/golden_tests/compile_file/ref_to_ref.bend
@C1 = @B4
@main = ((@A1 @B1) @C1)
@main = ((1 2) 2)

View File

@ -2,6 +2,4 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/switch_in_switch_arg.bend
---
@main = (?((0 (a a)) ?((0 @main__C0) b)) b)
@main__C0 = ($([+1] a) a)
@main = (?((0 (a a)) ?((0 ($([+1] b) b)) c)) c)

View File

@ -7,3 +7,5 @@ input_file: tests/golden_tests/compile_file/vicious_circles.bend
Found term that compiles into an inet with a vicious cycle
In compiled inet 'dup_self':
Found term that compiles into an inet with a vicious cycle
In compiled inet 'main':
Found term that compiles into an inet with a vicious cycle

View File

@ -2,8 +2,6 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file_o_all/addition.bend
---
@main = a
& @main__C0 ~ (8 a)
@main__C0 = (a b)
@main = c
& (a b) ~ (8 c)
& $(1 $([+] $(a b))) ~ [+1]

View File

@ -29,10 +29,8 @@ input_file: tests/golden_tests/compile_file_o_all/ex2.bend
& @O ~ (b c)
& @O ~ (a b)
@main = a
& @run ~ (@main__C0 a)
@main__C0 = a
@main = b
& @run ~ (a b)
& @c2 ~ (@I (@E a))
@run = ((@runO (@runI (@E a))) a)

View File

@ -2,9 +2,5 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file_o_all/exp.bend
---
@main = a
& @main__C1 ~ (@main__C0 a)
@main__C0 = ({(b c) (a b)} (a c))
@main__C1 = ({(b c) (a b)} (a c))
@main = g
& ({(b c) (a b)} (a c)) ~ (({(e f) (d e)} (d f)) g)

View File

@ -2,6 +2,4 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file_o_all/linearize_match.bend
---
@main = (?(((a a) @main__C0) b) b)
@main__C0 = ($([+] $(a b)) (a b))
@main = (?(((a a) ($([+] $(b c)) (b c))) d) d)

View File

@ -36,10 +36,6 @@ input_file: tests/golden_tests/compile_file_o_all/list_merge_sort.bend
@MergePair__C4 = (?((@MergePair__C3 (* (* @List_/Nil))) a) a)
@MergeSort = (a (b d))
& @Unpack ~ (a (c d))
& @Map ~ (b (@Pure c))
@Merge__C0 = ({b {g l}} ({h q} ({(a (b c)) {e m}} ({a {d n}} ({f o} t)))))
& @If ~ (c (k (s t)))
& @List_/Cons ~ (d (j k))
@ -75,4 +71,6 @@ input_file: tests/golden_tests/compile_file_o_all/list_merge_sort.bend
@Unpack__C3 = (?((@Unpack__C2 (* (* @List_/Nil))) a) a)
@main = @MergeSort
@main = (a (b d))
& @Unpack ~ (a (c d))
& @Map ~ (b (@Pure c))

View File

@ -2,8 +2,4 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file_o_all/match_mult_linearization.bend
---
@main = (?((@main__C0 @main__C1) a) a)
@main__C0 = ($([+] $(a $([+] $(b c)))) (a (b c)))
@main__C1 = ($([+] $(a $([+] $(b $([+] $(c d)))))) (a (b (c d))))
@main = (?((($([+] $(a $([+] $(b c)))) (a (b c))) ($([+] $(d $([+] $(e $([+] $(f g)))))) (d (e (f g))))) h) h)

View File

@ -10,7 +10,7 @@ input_file: tests/golden_tests/desugar_file/bind_syntax.bend
(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)
(Main) = (Result/bind Main__C3 Main__C2)
(Main) = (Result/bind (safe_div 3 2) λa (a λb (Result/bind (safe_rem b 0) λc (c λd d))))
(String/Nil) = λa (a String/Nil/tag)
@ -28,14 +28,6 @@ input_file: tests/golden_tests/desugar_file/bind_syntax.bend
(Result/Err/tag) = 1
(Main__C0) = λa (a λb b)
(Main__C1) = λa (Result/bind (safe_rem a 0) Main__C0)
(Main__C2) = λa (a Main__C1)
(Main__C3) = (safe_div 3 2)
(Result/bind__C0) = λa λb (undefer b a)
(Result/bind__C1) = λ* λa λ* (Result/Err a)

View File

@ -22,7 +22,7 @@ input_file: tests/golden_tests/desugar_file/combinators.bend
(B) = λa (B__C0 a)
(Main) = list
(Main) = (List/Cons 0 list__C0)
(List/Nil) = λa (a List/Nil/tag)

View File

@ -10,7 +10,7 @@ input_file: tests/golden_tests/desugar_file/mapper_syntax.bend
(Map/map) = λa λb λc (a Map/map__C5 b c)
(main) = let (a, b) = main__C8; let (c, *) = (Map/get b 0); (main__C7, a, c)
(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 λb λc λd (d Map/Node/tag a b c)
@ -65,21 +65,3 @@ input_file: tests/golden_tests/desugar_file/mapper_syntax.bend
(Map/set__C8) = λa λb λc λd λe let {f g} = e; (switch (== 0 f) { 0: Map/set__C2; _: Map/set__C3; } d g a b c)
(Map/set__C9) = λ* λa λb let {c d} = b; (switch (== 0 c) { 0: Map/set__C6; _: Map/set__C7; } a d)
(main__C0) = (Map/set Map/empty 0 3)
(main__C1) = λa (+ a 1)
(main__C2) = (Map/set main__C0 1 4)
(main__C3) = λa (* a 2)
(main__C4) = (Map/map main__C2 1 main__C1)
(main__C5) = (Map/map main__C4 1 main__C3)
(main__C6) = λa (+ a 1)
(main__C7) = (main__C6 1)
(main__C8) = (Map/get main__C5 1)

View File

@ -2,6 +2,4 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/desugar_file/switch_with_use.bend
---
(main) = λa λb λc λ* λ* (switch c { 0: λd d; _: main__C0; } (a b))
(main__C0) = λa λb (a b)
(main) = λa λb λc λ* λ* (switch c { 0: λd d; _: λe λf (e f); } (a b))

View File

@ -3,7 +3,7 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/adt_option_and.bend
---
NumScott:
λa (a λa switch a { 0: λa λb (b λa switch a { 0: λa λb (Option/Some (b, a)); _: λ* λ* Option/None; } a); _: λ* λ* Option/None; })
λa (a λb switch b = b { 0: λc λd (d λe switch e = e { 0: λf λg λh (h Option/Some/tag λi f); _: λ* Option/None; } c); _: λ* Option/None; })
Scott:
λa (a λa λb (b λa λb (Option/Some (b, a)) λ* Option/None a) λ* Option/None)
λa (a λb λc (c λd λe λf λ* (f λg d) λ* Option/None b) λ* Option/None)

View File

@ -2,5 +2,16 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/exp.bend
---
Errors:
Failed to parse result from HVM.
NumScott:
Warnings:
During readback:
Unable to interpret the HVM result as a valid Bend term. (Reached Root)
λa λb (<Invalid> λ$c $c $c)
Scott:
Warnings:
During readback:
Unable to interpret the HVM result as a valid Bend term. (Reached Root)
λa λb (<Invalid> λ$c $c $c)

View File

@ -3,7 +3,7 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/imp_empty_literals.bend
---
NumScott:
[]
λa (a List/Nil/tag)
Scott:
[]
λa λ* a

View File

@ -3,7 +3,15 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/linearize_match.bend
---
NumScott:
λa switch a = a { 0: λb b; _: λb (+ a-1 b); }
Warnings:
During readback:
Encountered an invalid numeric operation.
λa switch a = a { 0: λb b; _: λd <Invalid>; }
Scott:
λa switch a = a { 0: λb b; _: λb (+ a-1 b); }
Warnings:
During readback:
Encountered an invalid numeric operation.
λa switch a = a { 0: λb b; _: λd <Invalid>; }

View File

@ -3,7 +3,7 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/mapper_syntax.bend
---
NumScott:
((λa (+ a 1) 1), (10, 3))
(2, (10, 3))
Scott:
((λa (+ a 1) 1), (10, 3))
(2, (10, 3))

View File

@ -3,7 +3,15 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/match_mult_linearization.bend
---
NumScott:
λa switch a = a { 0: λa λb λc (+ (+ a b) c); _: λb λc λd (+ (+ (+ a-1 b) c) d); }
Warnings:
During readback:
Encountered an invalid numeric operation. (2 occurrences)
λa switch a = a { 0: λb λc λd <Invalid>; _: λf λg λh <Invalid>; }
Scott:
λa switch a = a { 0: λa λb λc (+ (+ a b) c); _: λb λc λd (+ (+ (+ a-1 b) c) d); }
Warnings:
During readback:
Encountered an invalid numeric operation. (2 occurrences)
λa switch a = a { 0: λb λc λd <Invalid>; _: λf λg λh <Invalid>; }

View File

@ -3,7 +3,7 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/match_num_succ_complex.bend
---
NumScott:
[[5, 5, 0, 12, 0, 6], [5, 5, 0, 12, 0, 6]]
[[5, 5, *, *, *, *], [5, 5, *, *, *, *]]
Scott:
[[5, 5, 0, 12, 0, 6], [5, 5, 0, 12, 0, 6]]
[[5, 5, *, *, *, *], [5, 5, *, *, *, *]]

View File

@ -3,7 +3,7 @@ source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/queue.bend
---
NumScott:
λa λ* (a 1 (Cons 2 (Cons 3 Nil)))
λa λ* (a 1 λb λ* (b 2 λc λ* (c 3 Nil)))
Scott:
λa λ* (a 1 (Cons 2 (Cons 3 Nil)))
λa λ* (a 1 λb λ* (b 2 λc λ* (c 3 Nil)))

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/unused_main_var.bend
---
NumScott:
[10, 12, 14, 16]
Scott:
[10, 12, 14, 16]