Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sc-518] Add non-duping use expressions #239

Merged
merged 3 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,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();
Expand Down
8 changes: 7 additions & 1 deletion src/term/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,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 => {}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/term/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions src/term/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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 } => {
Expand Down Expand Up @@ -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, .. } => {
Expand Down Expand Up @@ -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()]),
Expand All @@ -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()]),
Expand Down Expand Up @@ -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))])
}
Expand Down Expand Up @@ -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()))])
}
Expand Down Expand Up @@ -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))])
}
Expand Down Expand Up @@ -611,6 +623,7 @@ impl Term {
| Term::Sup { .. }
| Term::Lst { .. }
| Term::Dup { .. }
| Term::Use { .. }
| Term::App { .. }
| Term::Opx { .. }
| Term::Lam { .. }
Expand All @@ -634,6 +647,7 @@ impl Term {
| Term::Sup { .. }
| Term::Lst { .. }
| Term::Dup { .. }
| Term::Use { .. }
| Term::App { .. }
| Term::Opx { .. }
| Term::Lam { .. }
Expand Down
3 changes: 2 additions & 1 deletion src/term/net_to_term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 => {}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/term/parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub enum Token {
#[token("let")]
Let,

#[token("use")]
Use,

#[token("match")]
Match,

Expand Down Expand Up @@ -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}"),
Expand Down
13 changes: 12 additions & 1 deletion src/term/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<_>>();
Expand Down Expand Up @@ -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,
))
})
}
Expand Down
1 change: 1 addition & 0 deletions src/term/term_to_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
40 changes: 40 additions & 0 deletions src/term/transform/apply_use.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
});
}
}
1 change: 1 addition & 0 deletions src/term/transform/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/term/transform/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tests/golden_tests/compile_file/missing_pat.hvm
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
main = @x match x {
: *
}
}
7 changes: 7 additions & 0 deletions tests/golden_tests/desugar_file/use_id.hvm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(Main) =
use id =
use id = 2
(@x x id use id = 3; id)
;
use id = (id id)
(id id)
2 changes: 1 addition & 1 deletion tests/snapshots/compile_file__missing_pat.hvm.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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 | : *
5 changes: 5 additions & 0 deletions tests/snapshots/desugar_file__use_id.hvm.snap
Original file line number Diff line number Diff line change
@@ -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