mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-17 14:47:21 +03:00
Merge pull request #239 from HigherOrderCO/feature/sc-518/add-non-duping-use-expressions
[sc-518] Add non-duping use expressions
This commit is contained in:
commit
13f08ea26b
15
README.md
15
README.md
@ -89,6 +89,21 @@ A let term binds some value to the next term, in this case `(* result 2)`:
|
||||
let result = (+ 1 2); (* result 2)
|
||||
```
|
||||
|
||||
The `use` term inlines clones of some value to the next term:
|
||||
```rs
|
||||
use result = (+ 1 2); (* result result)
|
||||
|
||||
// Equivalent to
|
||||
(* (+ 1 2) (+ 1 2))
|
||||
```
|
||||
The same term with `let` duplicates the value:
|
||||
```rs
|
||||
let result = (+ 1 2); (* result result)
|
||||
|
||||
// Equivalent to
|
||||
let {result_1 result_2} = (+ 1 2); (* result_1 result_2)
|
||||
```
|
||||
|
||||
It is possible to define tuples:
|
||||
```rs
|
||||
tup = (2, 2)
|
||||
|
@ -68,6 +68,7 @@ pub fn desugar_book(
|
||||
ctx.check_shared_names();
|
||||
ctx.set_entrypoint();
|
||||
ctx.apply_args(args)?;
|
||||
ctx.book.apply_use();
|
||||
|
||||
ctx.book.encode_adts(opts.adt_encoding);
|
||||
ctx.book.encode_builtins();
|
||||
|
@ -65,7 +65,13 @@ impl Term {
|
||||
rule.body.encode_builtins();
|
||||
}
|
||||
}
|
||||
Term::Var { .. } | Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. } | Term::Era | Term::Err => {}
|
||||
Term::Use { .. }
|
||||
| Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Era
|
||||
| Term::Err => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,9 @@ impl fmt::Display for Term {
|
||||
Term::Let { pat, val, nxt } => {
|
||||
write!(f, "let {} = {}; {}", pat, val, nxt)
|
||||
}
|
||||
Term::Use { nam, val, nxt } => {
|
||||
write!(f, "use {} = {}; {}", nam, val, nxt)
|
||||
}
|
||||
Term::Ref { nam: def_name } => write!(f, "{def_name}"),
|
||||
Term::App { tag, fun, arg } => {
|
||||
write!(f, "{}({} {})", tag.display_padded(), fun.display_app(tag), arg)
|
||||
|
@ -96,6 +96,11 @@ pub enum Term {
|
||||
val: Box<Term>,
|
||||
nxt: Box<Term>,
|
||||
},
|
||||
Use {
|
||||
nam: Name,
|
||||
val: Box<Term>,
|
||||
nxt: Box<Term>,
|
||||
},
|
||||
App {
|
||||
tag: Tag,
|
||||
fun: Box<Term>,
|
||||
@ -303,6 +308,7 @@ impl Clone for Term {
|
||||
Self::Chn { tag, nam, bod } => Self::Chn { tag: tag.clone(), nam: nam.clone(), bod: bod.clone() },
|
||||
Self::Lnk { nam } => Self::Lnk { nam: nam.clone() },
|
||||
Self::Let { pat, val, nxt } => Self::Let { pat: pat.clone(), val: val.clone(), nxt: nxt.clone() },
|
||||
Self::Use { nam, val, nxt } => Self::Use { nam: nam.clone(), val: val.clone(), nxt: nxt.clone() },
|
||||
Self::App { tag, fun, arg } => Self::App { tag: tag.clone(), fun: fun.clone(), arg: arg.clone() },
|
||||
Self::Tup { els } => Self::Tup { els: els.clone() },
|
||||
Self::Dup { tag, bnd, val, nxt } => {
|
||||
@ -330,6 +336,7 @@ impl Drop for Term {
|
||||
stack.push(std::mem::take(bod.as_mut()));
|
||||
}
|
||||
Term::Let { val: fst, nxt: snd, .. }
|
||||
| Term::Use { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
@ -449,6 +456,7 @@ impl Term {
|
||||
Term::Mat { args, rules } => ChildrenIter::Mat(args.iter().chain(rules.iter().map(|r| &r.body))),
|
||||
Term::Tup { els } | Term::Sup { els, .. } | Term::Lst { els } => ChildrenIter::Vec(els),
|
||||
Term::Let { val: fst, nxt: snd, .. }
|
||||
| Term::Use { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => ChildrenIter::Two([fst.as_ref(), snd.as_ref()]),
|
||||
@ -471,6 +479,7 @@ impl Term {
|
||||
}
|
||||
Term::Tup { els } | Term::Sup { els, .. } | Term::Lst { els } => ChildrenIter::Vec(els),
|
||||
Term::Let { val: fst, nxt: snd, .. }
|
||||
| Term::Use { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => ChildrenIter::Two([fst.as_mut(), snd.as_mut()]),
|
||||
@ -510,6 +519,7 @@ impl Term {
|
||||
Term::Let { pat, val, nxt, .. } => {
|
||||
ChildrenIter::Two([(val.as_ref(), BindsIter::Zero([])), (nxt.as_ref(), BindsIter::Pat(pat.binds()))])
|
||||
}
|
||||
Term::Use { .. } => ChildrenIter::Zero([]),
|
||||
Term::Dup { bnd, val, nxt, .. } => {
|
||||
ChildrenIter::Two([(val.as_ref(), BindsIter::Zero([])), (nxt.as_ref(), BindsIter::Dup(bnd))])
|
||||
}
|
||||
@ -546,6 +556,7 @@ impl Term {
|
||||
Term::Let { pat, val, nxt, .. } => {
|
||||
ChildrenIter::Two([(val.as_mut(), BindsIter::Zero([])), (nxt.as_mut(), BindsIter::Pat(pat.binds()))])
|
||||
}
|
||||
Term::Use { .. } => ChildrenIter::Zero([]),
|
||||
Term::Dup { bnd, val, nxt, .. } => {
|
||||
ChildrenIter::Two([(val.as_mut(), BindsIter::Zero([])), (nxt.as_mut(), BindsIter::Dup(bnd.iter()))])
|
||||
}
|
||||
@ -584,6 +595,7 @@ impl Term {
|
||||
(val.as_mut(), BindsIter::Zero([])),
|
||||
(nxt.as_mut(), BindsIter::Pat(pat.binds_mut())),
|
||||
]),
|
||||
Term::Use { .. } => ChildrenIter::Zero([]),
|
||||
Term::Dup { bnd, val, nxt, .. } => {
|
||||
ChildrenIter::Two([(val.as_mut(), BindsIter::Zero([])), (nxt.as_mut(), BindsIter::Dup(bnd))])
|
||||
}
|
||||
@ -611,6 +623,7 @@ impl Term {
|
||||
| Term::Sup { .. }
|
||||
| Term::Lst { .. }
|
||||
| Term::Dup { .. }
|
||||
| Term::Use { .. }
|
||||
| Term::App { .. }
|
||||
| Term::Opx { .. }
|
||||
| Term::Lam { .. }
|
||||
@ -634,6 +647,7 @@ impl Term {
|
||||
| Term::Sup { .. }
|
||||
| Term::Lst { .. }
|
||||
| Term::Dup { .. }
|
||||
| Term::Use { .. }
|
||||
| Term::App { .. }
|
||||
| Term::Opx { .. }
|
||||
| Term::Lam { .. }
|
||||
|
@ -331,6 +331,7 @@ impl Term {
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.insert_split(split, threshold)? + snd.insert_split(split, threshold)?
|
||||
}
|
||||
Term::Use { .. } => unreachable!(),
|
||||
Term::Sup { els, .. } | Term::Tup { els } => {
|
||||
let mut n = 0;
|
||||
for el in els {
|
||||
@ -418,7 +419,7 @@ impl Term {
|
||||
rule.body.fix_names(id_counter, book);
|
||||
}
|
||||
}
|
||||
Term::Let { .. } | Term::Lst { .. } => unreachable!(),
|
||||
Term::Let { .. } | Term::Use { .. } | Term::Lst { .. } => unreachable!(),
|
||||
Term::Var { .. } | Term::Lnk { .. } | Term::Num { .. } | Term::Str { .. } | Term::Era | Term::Err => {}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ pub enum Token {
|
||||
#[token("let")]
|
||||
Let,
|
||||
|
||||
#[token("use")]
|
||||
Use,
|
||||
|
||||
#[token("match")]
|
||||
Match,
|
||||
|
||||
@ -265,6 +268,7 @@ impl fmt::Display for Token {
|
||||
Self::Lambda => write!(f, "λ"),
|
||||
Self::Dollar => write!(f, "$"),
|
||||
Self::Let => write!(f, "let"),
|
||||
Self::Use => write!(f, "use"),
|
||||
Self::Match => write!(f, "match"),
|
||||
Self::Equals => write!(f, "="),
|
||||
Self::Num(num) => write!(f, "{num}"),
|
||||
|
@ -267,6 +267,16 @@ where
|
||||
.map(|((pat, val), nxt)| Term::Let { pat, val: Box::new(val), nxt: Box::new(nxt) })
|
||||
.boxed();
|
||||
|
||||
// use a = val ';'? nxt
|
||||
let use_ = just(Token::Use)
|
||||
.ignore_then(name())
|
||||
.then_ignore(just(Token::Equals))
|
||||
.then(term.clone())
|
||||
.then_ignore(term_sep.clone())
|
||||
.then(term.clone())
|
||||
.map(|((nam, val), nxt)| Term::Use { nam, val: Box::new(val), nxt: Box::new(nxt) })
|
||||
.boxed();
|
||||
|
||||
let match_arg = name().then_ignore(just(Token::Equals)).or_not().then(term.clone());
|
||||
let match_args =
|
||||
match_arg.separated_by(list_sep.clone()).at_least(1).allow_trailing().collect::<Vec<_>>();
|
||||
@ -349,7 +359,8 @@ where
|
||||
// OBS: `num_op` has to be before app, idk why?
|
||||
// OBS: `app` has to be before `tup` to not overflow on huge app terms
|
||||
// TODO: What happens on huge `tup` and other terms?
|
||||
num_op, app, tup, global_var, var, number, list, str, chr, sup, global_lam, lam, dup, let_, match_, era,
|
||||
num_op, app, tup, global_var, var, number, list, str, chr, sup, global_lam, lam, dup, let_, use_,
|
||||
match_, era,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -215,6 +215,7 @@ impl EncodeTermState<'_> {
|
||||
self.encode_term(nxt, up)
|
||||
}
|
||||
Term::Let { .. } => unreachable!(), // Removed in earlier pass
|
||||
Term::Use { .. } => unreachable!(), // Removed in earlier pass
|
||||
Term::Sup { tag, els } => {
|
||||
let lab = self.labels.dup.generate(tag).unwrap();
|
||||
let (main, aux) = self.make_node_list(Dup { lab }, els.len());
|
||||
|
40
src/term/transform/apply_use.rs
Normal file
40
src/term/transform/apply_use.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::term::{Book, Term};
|
||||
|
||||
impl Book {
|
||||
/// Inline copies of the declared bind in the `use` expression.
|
||||
///
|
||||
/// Example:
|
||||
/// ```hvml
|
||||
/// use id = λx x
|
||||
/// (id id id)
|
||||
///
|
||||
/// // Transforms to:
|
||||
/// (λx x λx x λx x)
|
||||
/// ```
|
||||
pub fn apply_use(&mut self) {
|
||||
for def in self.defs.values_mut() {
|
||||
for rule in def.rules.iter_mut() {
|
||||
rule.body.apply_use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn apply_use(&mut self) {
|
||||
Term::recursive_call(move || match self {
|
||||
Term::Use { nam, val, nxt } => {
|
||||
val.apply_use();
|
||||
nxt.apply_use();
|
||||
|
||||
nxt.subst(nam, val);
|
||||
*self = std::mem::take(nxt);
|
||||
}
|
||||
other => {
|
||||
for children in other.children_mut() {
|
||||
children.apply_use();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -53,6 +53,7 @@ impl Term {
|
||||
Term::Chn { .. } | Term::Lnk { .. } => false,
|
||||
Term::Str { .. } | Term::Lst { .. } => false,
|
||||
Term::Let { .. } => false,
|
||||
Term::Use { .. } => unreachable!(),
|
||||
Term::App { .. } => false,
|
||||
Term::Dup { .. } => false,
|
||||
Term::Opx { .. } => false,
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod apply_args;
|
||||
pub mod apply_use;
|
||||
pub mod definition_merge;
|
||||
pub mod definition_pruning;
|
||||
pub mod desugar_implicit_match_binds;
|
||||
|
@ -1,3 +1,3 @@
|
||||
main = @x match x {
|
||||
: *
|
||||
}
|
||||
}
|
||||
|
7
tests/golden_tests/desugar_file/use_id.hvm
Normal file
7
tests/golden_tests/desugar_file/use_id.hvm
Normal file
@ -0,0 +1,7 @@
|
||||
(Main) =
|
||||
use id =
|
||||
use id = 2
|
||||
(@x x id use id = 3; id)
|
||||
;
|
||||
use id = (id id)
|
||||
(id id)
|
@ -3,5 +3,5 @@ source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/compile_file/missing_pat.hvm
|
||||
---
|
||||
Errors:
|
||||
At tests/golden_tests/compile_file/missing_pat.hvm:2:3: found ':' expected '(', '#', '$', <Name>, '[', '{', 'λ', 'let', 'match', '*', '|', <Num>+, <Num>, <Char>, or <String>
|
||||
At tests/golden_tests/compile_file/missing_pat.hvm:2:3: found ':' expected '(', '#', '$', <Name>, '[', '{', 'λ', 'let', 'use', 'match', '*', '|', <Num>+, <Num>, <Char>, or <String>
|
||||
[0m 2 | [4m[31m:[0m *[0m
|
||||
|
5
tests/snapshots/desugar_file__use_id.hvm.snap
Normal file
5
tests/snapshots/desugar_file__use_id.hvm.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/desugar_file/use_id.hvm
|
||||
---
|
||||
(Main) = (λa a 2 3 (λb b 2 3) (λc c 2 3 (λd d 2 3)))
|
Loading…
Reference in New Issue
Block a user