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:
imaqtkatt 2024-03-18 17:56:01 +00:00 committed by GitHub
commit 13f08ea26b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 115 additions and 5 deletions

View File

@ -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)

View File

@ -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();

View File

@ -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 => {}
}
}

View File

@ -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)

View File

@ -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 { .. }

View File

@ -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 => {}
}
}

View File

@ -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}"),

View File

@ -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,
))
})
}

View File

@ -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());

View 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();
}
}
});
}
}

View File

@ -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,

View File

@ -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;

View File

@ -1,3 +1,3 @@
main = @x match x {
: *
}
}

View File

@ -0,0 +1,7 @@
(Main) =
use id =
use id = 2
(@x x id use id = 3; id)
;
use id = (id id)
(id id)

View File

@ -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>
 2 | : *

View 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)))