mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-08-16 15:20:40 +03:00
Wip stacker and drop impl
This commit is contained in:
parent
0b98642d96
commit
6263b642bb
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -221,6 +221,7 @@ dependencies = [
|
||||
"interner",
|
||||
"itertools",
|
||||
"logos",
|
||||
"stacker",
|
||||
"stdext",
|
||||
"walkdir",
|
||||
]
|
||||
|
@ -29,6 +29,7 @@ indexmap = "2.2.3"
|
||||
interner = "0.2.1"
|
||||
itertools = "0.11.0"
|
||||
logos = "0.14.0"
|
||||
stacker = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.34.0"
|
||||
|
5
aircraft.hvm2
Normal file
5
aircraft.hvm2
Normal file
File diff suppressed because one or more lines are too long
4
gen.hvm
Normal file
4
gen.hvm
Normal file
@ -0,0 +1,4 @@
|
||||
MakeLongStr 0 acc = acc
|
||||
MakeLongStr 1+p acc = (String.cons '*' (MakeLongStr p acc))
|
||||
|
||||
main = (MakeLongStr 16339 String.nil)
|
1
huge_str.hvm
Normal file
1
huge_str.hvm
Normal file
File diff suppressed because one or more lines are too long
@ -80,7 +80,7 @@ pub fn create_host(book: Arc<Book>, labels: Arc<Labels>, compile_opts: CompileOp
|
||||
term.resugar_builtins();
|
||||
|
||||
readback_errors.extend(resugar_errs);
|
||||
if let Term::Str { val } = term {
|
||||
if let Term::Str { ref val } = term {
|
||||
println!("{val}");
|
||||
}
|
||||
}
|
||||
|
@ -75,46 +75,48 @@ impl Pattern {
|
||||
|
||||
impl Term {
|
||||
pub fn check_ctrs_arities(&self, arities: &HashMap<Name, usize>) -> Result<(), MatchErr> {
|
||||
match self {
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args {
|
||||
arg.check_ctrs_arities(arities)?;
|
||||
}
|
||||
for rule in rules {
|
||||
for pat in &rule.pats {
|
||||
pat.check_ctrs_arities(arities)?;
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match self {
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args {
|
||||
arg.check_ctrs_arities(arities)?;
|
||||
}
|
||||
for rule in rules {
|
||||
for pat in &rule.pats {
|
||||
pat.check_ctrs_arities(arities)?;
|
||||
}
|
||||
rule.body.check_ctrs_arities(arities)?;
|
||||
}
|
||||
rule.body.check_ctrs_arities(arities)?;
|
||||
}
|
||||
}
|
||||
Term::Let { pat, val, nxt } => {
|
||||
pat.check_ctrs_arities(arities)?;
|
||||
val.check_ctrs_arities(arities)?;
|
||||
nxt.check_ctrs_arities(arities)?;
|
||||
}
|
||||
Term::Let { pat, val, nxt } => {
|
||||
pat.check_ctrs_arities(arities)?;
|
||||
val.check_ctrs_arities(arities)?;
|
||||
nxt.check_ctrs_arities(arities)?;
|
||||
}
|
||||
|
||||
Term::Lst { els } => {
|
||||
for el in els {
|
||||
el.check_ctrs_arities(arities)?;
|
||||
Term::Lst { els } => {
|
||||
for el in els {
|
||||
el.check_ctrs_arities(arities)?;
|
||||
}
|
||||
}
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.check_ctrs_arities(arities)?;
|
||||
snd.check_ctrs_arities(arities)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_ctrs_arities(arities)?,
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => {}
|
||||
}
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.check_ctrs_arities(arities)?;
|
||||
snd.check_ctrs_arities(arities)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_ctrs_arities(arities)?,
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => {}
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -33,41 +33,43 @@ impl Definition {
|
||||
|
||||
impl Term {
|
||||
pub fn check_match_arity(&self) -> Result<(), MatchErr> {
|
||||
match self {
|
||||
Term::Mat { args, rules } => {
|
||||
let expected = args.len();
|
||||
for rule in rules {
|
||||
let found = rule.pats.len();
|
||||
if found != expected {
|
||||
return Err(MatchErr::ArityMismatch(found, expected));
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match self {
|
||||
Term::Mat { args, rules } => {
|
||||
let expected = args.len();
|
||||
for rule in rules {
|
||||
let found = rule.pats.len();
|
||||
if found != expected {
|
||||
return Err(MatchErr::ArityMismatch(found, expected));
|
||||
}
|
||||
rule.body.check_match_arity()?;
|
||||
}
|
||||
rule.body.check_match_arity()?;
|
||||
}
|
||||
}
|
||||
|
||||
Term::Lst { els } => {
|
||||
for el in els {
|
||||
el.check_match_arity()?;
|
||||
Term::Lst { els } => {
|
||||
for el in els {
|
||||
el.check_match_arity()?;
|
||||
}
|
||||
}
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. }
|
||||
| Term::Let { val: fst, nxt: snd, .. } => {
|
||||
fst.check_match_arity()?;
|
||||
snd.check_match_arity()?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_match_arity()?,
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => {}
|
||||
}
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. }
|
||||
| Term::Let { val: fst, nxt: snd, .. } => {
|
||||
fst.check_match_arity()?;
|
||||
snd.check_match_arity()?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_match_arity()?,
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => {}
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -67,41 +67,43 @@ impl Pattern {
|
||||
|
||||
impl Term {
|
||||
pub fn check_unbound_pats(&self, is_ctr: &impl Fn(&Name) -> bool) -> Result<(), UnboundCtrErr> {
|
||||
match self {
|
||||
Term::Let { pat, val, nxt } => {
|
||||
pat.check_unbounds(is_ctr)?;
|
||||
val.check_unbound_pats(is_ctr)?;
|
||||
nxt.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args {
|
||||
arg.check_unbound_pats(is_ctr)?;
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match self {
|
||||
Term::Let { pat, val, nxt } => {
|
||||
pat.check_unbounds(is_ctr)?;
|
||||
val.check_unbound_pats(is_ctr)?;
|
||||
nxt.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
for rule in rules {
|
||||
for pat in &rule.pats {
|
||||
pat.check_unbounds(is_ctr)?;
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args {
|
||||
arg.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
for rule in rules {
|
||||
for pat in &rule.pats {
|
||||
pat.check_unbounds(is_ctr)?;
|
||||
}
|
||||
rule.body.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
rule.body.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.check_unbound_pats(is_ctr)?;
|
||||
snd.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_unbound_pats(is_ctr)?,
|
||||
Term::Lst { .. } => unreachable!(),
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Era
|
||||
| Term::Err => (),
|
||||
}
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.check_unbound_pats(is_ctr)?;
|
||||
snd.check_unbound_pats(is_ctr)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.check_unbound_pats(is_ctr)?,
|
||||
Term::Lst { .. } => unreachable!(),
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Era
|
||||
| Term::Err => (),
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,44 @@ pub enum Term {
|
||||
Err,
|
||||
}
|
||||
|
||||
impl Drop for Term {
|
||||
fn drop(&mut self) {
|
||||
let mut stack = vec![self];
|
||||
|
||||
while let Some(term) = stack.pop() {
|
||||
match term {
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => {
|
||||
stack.push(bod);
|
||||
}
|
||||
Term::Let { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
stack.push(fst);
|
||||
stack.push(snd);
|
||||
}
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args.iter_mut() {
|
||||
stack.push(arg);
|
||||
}
|
||||
|
||||
for Rule { body, .. } in rules.iter_mut() {
|
||||
stack.push(body);
|
||||
}
|
||||
}
|
||||
Term::Lst { els } => {
|
||||
for el in els.iter_mut() {
|
||||
stack.push(el);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Pattern {
|
||||
Var(Option<Name>),
|
||||
|
@ -58,162 +58,166 @@ pub struct Reader<'a> {
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
fn read_term(&mut self, next: Port) -> Term {
|
||||
if self.dup_paths.is_none() && !self.seen.insert(next) {
|
||||
self.error(ReadbackError::Cyclic);
|
||||
return Term::Var { nam: Name::from("...") };
|
||||
}
|
||||
|
||||
let node = next.node();
|
||||
|
||||
let term = match &self.net.node(node).kind {
|
||||
Era => {
|
||||
// Only the main port actually exists in an ERA, the aux ports are just an artifact of this representation.
|
||||
debug_assert!(next.slot() == 0);
|
||||
Term::Era
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
if self.dup_paths.is_none() && !self.seen.insert(next) {
|
||||
self.error(ReadbackError::Cyclic);
|
||||
return Term::Var { nam: Name::from("...") };
|
||||
}
|
||||
// If we're visiting a con node...
|
||||
Con { lab } => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a lambda.
|
||||
0 => {
|
||||
let nam = self.namegen.decl_name(self.net, Port(node, 1));
|
||||
let bod = self.read_term(self.net.enter_port(Port(node, 2)));
|
||||
Term::Lam { tag: self.labels.con.to_tag(*lab), nam, bod: Box::new(bod) }
|
||||
|
||||
let node = next.node();
|
||||
|
||||
let term = match &self.net.node(node).kind {
|
||||
Era => {
|
||||
// Only the main port actually exists in an ERA, the aux ports are just an artifact of this representation.
|
||||
debug_assert!(next.slot() == 0);
|
||||
Term::Era
|
||||
}
|
||||
// If we're visiting a port 1, then it is a variable.
|
||||
1 => Term::Var { nam: self.namegen.var_name(next) },
|
||||
// If we're visiting a port 2, then it is an application.
|
||||
2 => {
|
||||
let fun = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let arg = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
Term::App { tag: self.labels.con.to_tag(*lab), fun: Box::new(fun), arg: Box::new(arg) }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Mat => match next.slot() {
|
||||
2 => {
|
||||
// Read the matched expression
|
||||
let scrutinee = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
// If we're visiting a con node...
|
||||
Con { lab } => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a lambda.
|
||||
0 => {
|
||||
let nam = self.namegen.decl_name(self.net, Port(node, 1));
|
||||
let bod = self.read_term(self.net.enter_port(Port(node, 2)));
|
||||
Term::Lam { tag: self.labels.con.to_tag(*lab), nam, bod: Box::new(bod) }
|
||||
}
|
||||
// If we're visiting a port 1, then it is a variable.
|
||||
1 => Term::Var { nam: self.namegen.var_name(next) },
|
||||
// If we're visiting a port 2, then it is an application.
|
||||
2 => {
|
||||
let fun = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let arg = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
Term::App { tag: self.labels.con.to_tag(*lab), fun: Box::new(fun), arg: Box::new(arg) }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Mat => match next.slot() {
|
||||
2 => {
|
||||
// Read the matched expression
|
||||
let scrutinee = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
|
||||
// Read the pattern matching node
|
||||
let sel_node = self.net.enter_port(Port(node, 1)).node();
|
||||
// Read the pattern matching node
|
||||
let sel_node = self.net.enter_port(Port(node, 1)).node();
|
||||
|
||||
// We expect the pattern matching node to be a CON
|
||||
let sel_kind = &self.net.node(sel_node).kind;
|
||||
if *sel_kind != (Con { lab: None }) {
|
||||
// TODO: Is there any case where we expect a different node type here on readback?
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::native_num_match(scrutinee, Term::Era, Term::Era, None)
|
||||
} else {
|
||||
let zero_term = self.read_term(self.net.enter_port(Port(sel_node, 1)));
|
||||
let succ_term = self.read_term(self.net.enter_port(Port(sel_node, 2)));
|
||||
// We expect the pattern matching node to be a CON
|
||||
let sel_kind = &self.net.node(sel_node).kind;
|
||||
if *sel_kind != (Con { lab: None }) {
|
||||
// TODO: Is there any case where we expect a different node type here on readback?
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::native_num_match(scrutinee, Term::Era, Term::Era, None)
|
||||
} else {
|
||||
let zero_term = self.read_term(self.net.enter_port(Port(sel_node, 1)));
|
||||
let mut succ_term = self.read_term(self.net.enter_port(Port(sel_node, 2)));
|
||||
|
||||
match succ_term {
|
||||
Term::Lam { nam, bod, .. } => Term::native_num_match(scrutinee, zero_term, *bod, Some(nam)),
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::native_num_match(scrutinee, zero_term, succ_term, None)
|
||||
match &mut succ_term {
|
||||
Term::Lam { nam, bod, .. } => {
|
||||
Term::native_num_match(scrutinee, zero_term, std::mem::take(bod), Some(nam.take()))
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::native_num_match(scrutinee, zero_term, succ_term, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::Err
|
||||
}
|
||||
},
|
||||
Ref { def_name } => {
|
||||
if def_name.is_generated() {
|
||||
// Dereference generated names since the user is not aware of them
|
||||
let def = &self.book.defs[def_name];
|
||||
let mut term = def.rule().body.clone();
|
||||
term.fix_names(&mut self.namegen.id_counter, self.book);
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericMatch);
|
||||
Term::Err
|
||||
}
|
||||
},
|
||||
Ref { def_name } => {
|
||||
if def_name.is_generated() {
|
||||
// Dereference generated names since the user is not aware of them
|
||||
let def = &self.book.defs[def_name];
|
||||
let mut term = def.rule().body.clone();
|
||||
term.fix_names(&mut self.namegen.id_counter, self.book);
|
||||
|
||||
term
|
||||
} else {
|
||||
Term::Ref { nam: def_name.clone() }
|
||||
term
|
||||
} else {
|
||||
Term::Ref { nam: def_name.clone() }
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we're visiting a fan node...
|
||||
Dup { lab } => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a pair.
|
||||
0 => {
|
||||
if let Some(dup_paths) = &mut self.dup_paths {
|
||||
let stack = dup_paths.entry(*lab).or_default();
|
||||
if let Some(slot) = stack.pop() {
|
||||
// Since we had a paired Dup in the path to this Sup,
|
||||
// we "decay" the superposition according to the original direction we came from the Dup.
|
||||
let term = self.read_term(self.net.enter_port(Port(node, slot)));
|
||||
self.dup_paths.as_mut().unwrap().get_mut(lab).unwrap().push(slot);
|
||||
Some(term)
|
||||
// If we're visiting a fan node...
|
||||
Dup { lab } => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a pair.
|
||||
0 => {
|
||||
if let Some(dup_paths) = &mut self.dup_paths {
|
||||
let stack = dup_paths.entry(*lab).or_default();
|
||||
if let Some(slot) = stack.pop() {
|
||||
// Since we had a paired Dup in the path to this Sup,
|
||||
// we "decay" the superposition according to the original direction we came from the Dup.
|
||||
let term = self.read_term(self.net.enter_port(Port(node, slot)));
|
||||
self.dup_paths.as_mut().unwrap().get_mut(lab).unwrap().push(slot);
|
||||
Some(term)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
.unwrap_or_else(|| {
|
||||
// If no Dup with same label in the path, we can't resolve the Sup, so keep it as a term.
|
||||
self.decay_or_get_ports(node).map_or_else(
|
||||
|(fst, snd)| Term::Sup {
|
||||
tag: self.labels.dup.to_tag(Some(*lab)),
|
||||
fst: Box::new(fst),
|
||||
snd: Box::new(snd),
|
||||
},
|
||||
|term| term,
|
||||
)
|
||||
})
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
// If no Dup with same label in the path, we can't resolve the Sup, so keep it as a term.
|
||||
self.decay_or_get_ports(node).map_or_else(
|
||||
|(fst, snd)| Term::Sup {
|
||||
tag: self.labels.dup.to_tag(Some(*lab)),
|
||||
fst: Box::new(fst),
|
||||
snd: Box::new(snd),
|
||||
},
|
||||
|term| term,
|
||||
)
|
||||
})
|
||||
// If we're visiting a port 1 or 2, then it is a variable.
|
||||
// Also, that means we found a dup, so we store it to read later.
|
||||
1 | 2 => {
|
||||
if let Some(dup_paths) = &mut self.dup_paths {
|
||||
dup_paths.entry(*lab).or_default().push(next.slot());
|
||||
let term = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
self.dup_paths.as_mut().unwrap().entry(*lab).or_default().pop().unwrap();
|
||||
term
|
||||
} else {
|
||||
if self.seen_fans.insert(node) {
|
||||
self.scope.insert(node);
|
||||
}
|
||||
Term::Var { nam: self.namegen.var_name(next) }
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Num { val } => Term::Num { val: *val },
|
||||
Op2 { opr } => match next.slot() {
|
||||
2 => {
|
||||
let fst = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let snd = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
|
||||
Term::Opx { op: Op::from_hvmc_label(*opr), fst: Box::new(fst), snd: Box::new(snd) }
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
Term::Err
|
||||
}
|
||||
},
|
||||
Rot => {
|
||||
self.error(ReadbackError::ReachedRoot);
|
||||
Term::Err
|
||||
}
|
||||
// If we're visiting a port 1 or 2, then it is a variable.
|
||||
// Also, that means we found a dup, so we store it to read later.
|
||||
1 | 2 => {
|
||||
if let Some(dup_paths) = &mut self.dup_paths {
|
||||
dup_paths.entry(*lab).or_default().push(next.slot());
|
||||
let term = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
self.dup_paths.as_mut().unwrap().entry(*lab).or_default().pop().unwrap();
|
||||
term
|
||||
} else {
|
||||
Tup => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a Tup.
|
||||
0 => self
|
||||
.decay_or_get_ports(node)
|
||||
.map_or_else(|(fst, snd)| Term::Tup { fst: Box::new(fst), snd: Box::new(snd) }, |term| term),
|
||||
// If we're visiting a port 1 or 2, then it is a variable.
|
||||
1 | 2 => {
|
||||
if self.seen_fans.insert(node) {
|
||||
self.scope.insert(node);
|
||||
}
|
||||
Term::Var { nam: self.namegen.var_name(next) }
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Num { val } => Term::Num { val: *val },
|
||||
Op2 { opr } => match next.slot() {
|
||||
2 => {
|
||||
let fst = self.read_term(self.net.enter_port(Port(node, 0)));
|
||||
let snd = self.read_term(self.net.enter_port(Port(node, 1)));
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
Term::Opx { op: Op::from_hvmc_label(*opr), fst: Box::new(fst), snd: Box::new(snd) }
|
||||
}
|
||||
_ => {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
Term::Err
|
||||
}
|
||||
},
|
||||
Rot => {
|
||||
self.error(ReadbackError::ReachedRoot);
|
||||
Term::Err
|
||||
}
|
||||
Tup => match next.slot() {
|
||||
// If we're visiting a port 0, then it is a Tup.
|
||||
0 => self
|
||||
.decay_or_get_ports(node)
|
||||
.map_or_else(|(fst, snd)| Term::Tup { fst: Box::new(fst), snd: Box::new(snd) }, |term| term),
|
||||
// If we're visiting a port 1 or 2, then it is a variable.
|
||||
1 | 2 => {
|
||||
if self.seen_fans.insert(node) {
|
||||
self.scope.insert(node);
|
||||
}
|
||||
Term::Var { nam: self.namegen.var_name(next) }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
term
|
||||
term
|
||||
})
|
||||
}
|
||||
|
||||
/// Enters both ports 1 and 2 of a node,
|
||||
|
@ -64,189 +64,191 @@ impl<'a> EncodeTermState<'a> {
|
||||
/// `global_vars` has the same information for global lambdas. Must be linked outside this function.
|
||||
/// Expects variables to be affine, refs to be stored as Refs and all names to be bound.
|
||||
fn encode_term(&mut self, term: &Term, up: Port) -> Option<Port> {
|
||||
match term {
|
||||
// A lambda becomes to a con node. Ports:
|
||||
// - 0: points to where the lambda occurs.
|
||||
// - 1: points to the lambda variable.
|
||||
// - 2: points to the lambda body.
|
||||
// core: (var_use bod)
|
||||
Term::Lam { tag, nam, bod } => {
|
||||
let fun = self.inet.new_node(Con { lab: self.labels.con.generate(tag) });
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match term {
|
||||
// A lambda becomes to a con node. Ports:
|
||||
// - 0: points to where the lambda occurs.
|
||||
// - 1: points to the lambda variable.
|
||||
// - 2: points to the lambda body.
|
||||
// core: (var_use bod)
|
||||
Term::Lam { tag, nam, bod } => {
|
||||
let fun = self.inet.new_node(Con { lab: self.labels.con.generate(tag) });
|
||||
|
||||
self.push_scope(nam, Port(fun, 1));
|
||||
let bod = self.encode_term(bod, Port(fun, 2));
|
||||
self.pop_scope(nam, Port(fun, 1));
|
||||
self.link_local(Port(fun, 2), bod);
|
||||
self.push_scope(nam, Port(fun, 1));
|
||||
let bod = self.encode_term(bod, Port(fun, 2));
|
||||
self.pop_scope(nam, Port(fun, 1));
|
||||
self.link_local(Port(fun, 2), bod);
|
||||
|
||||
Some(Port(fun, 0))
|
||||
Some(Port(fun, 0))
|
||||
}
|
||||
// core: (var_use bod)
|
||||
Term::Chn { tag, nam, bod } => {
|
||||
let fun = self.inet.new_node(Con { lab: self.labels.con.generate(tag) });
|
||||
self.global_vars.entry(nam.clone()).or_default().0 = Port(fun, 1);
|
||||
let bod = self.encode_term(bod, Port(fun, 2));
|
||||
self.link_local(Port(fun, 2), bod);
|
||||
Some(Port(fun, 0))
|
||||
}
|
||||
// An application becomes to a con node too. Ports:
|
||||
// - 0: points to the function being applied.
|
||||
// - 1: points to the function's argument.
|
||||
// - 2: points to where the application occurs.
|
||||
// core: & fun ~ (arg ret) (fun not necessarily main port)
|
||||
Term::App { tag, fun, arg } => {
|
||||
let app = self.inet.new_node(Con { lab: self.labels.con.generate(tag) });
|
||||
|
||||
let fun = self.encode_term(fun, Port(app, 0));
|
||||
self.link_local(Port(app, 0), fun);
|
||||
|
||||
let arg = self.encode_term(arg, Port(app, 1));
|
||||
self.link_local(Port(app, 1), arg);
|
||||
|
||||
Some(Port(app, 2))
|
||||
}
|
||||
// core: & cond ~ (zero succ) ret
|
||||
Term::Mat { args, rules } => {
|
||||
// At this point should be only simple num matches.
|
||||
let arg = args.iter().next().unwrap();
|
||||
debug_assert!(matches!(rules[0].pats[..], [Pattern::Num(NumCtr::Num(0))]));
|
||||
debug_assert!(matches!(rules[1].pats[..], [Pattern::Num(NumCtr::Succ(1, None))]));
|
||||
|
||||
let if_ = self.inet.new_node(Mat);
|
||||
|
||||
let cond = self.encode_term(arg, Port(if_, 0));
|
||||
self.link_local(Port(if_, 0), cond);
|
||||
|
||||
let zero = &rules[0].body;
|
||||
let succ = &rules[1].body;
|
||||
|
||||
let sel = self.inet.new_node(Con { lab: None });
|
||||
self.inet.link(Port(sel, 0), Port(if_, 1));
|
||||
let zero = self.encode_term(zero, Port(sel, 1));
|
||||
self.link_local(Port(sel, 1), zero);
|
||||
|
||||
let succ = self.encode_term(succ, Port(sel, 2));
|
||||
self.link_local(Port(sel, 2), succ);
|
||||
|
||||
Some(Port(if_, 2))
|
||||
}
|
||||
// A dup becomes a dup node too. Ports:
|
||||
// - 0: points to the value projected.
|
||||
// - 1: points to the occurrence of the first variable.
|
||||
// - 2: points to the occurrence of the second variable.
|
||||
// core: & val ~ {lab fst snd} (val not necessarily main port)
|
||||
Term::Dup { fst, snd, val, nxt, tag } => {
|
||||
let dup = self.inet.new_node(Dup { lab: self.labels.dup.generate(tag).unwrap() });
|
||||
|
||||
let val = self.encode_term(val, Port(dup, 0));
|
||||
self.link_local(Port(dup, 0), val);
|
||||
|
||||
self.push_scope(fst, Port(dup, 1));
|
||||
self.push_scope(snd, Port(dup, 2));
|
||||
let nxt = self.encode_term(nxt, up);
|
||||
self.pop_scope(snd, Port(dup, 2));
|
||||
self.pop_scope(fst, Port(dup, 1));
|
||||
|
||||
nxt
|
||||
}
|
||||
Term::Var { nam } => {
|
||||
// We assume this variable to be valid, bound and correctly scoped.
|
||||
// This pass must be done before.
|
||||
debug_assert!(
|
||||
self.scope.contains_key(nam),
|
||||
"Unbound variable {nam}. Expected this check to be already done"
|
||||
);
|
||||
let var_stack = &self.scope[nam];
|
||||
let cur_var = *var_stack.last().unwrap();
|
||||
let (declare_port, use_port) = self.vars.get_mut(cur_var).unwrap();
|
||||
debug_assert!(use_port.is_none(), "Variable {nam} used more than once");
|
||||
self.inet.link(up, *declare_port);
|
||||
*use_port = Some(up);
|
||||
Some(*declare_port)
|
||||
}
|
||||
Term::Lnk { nam } => {
|
||||
self.global_vars.entry(nam.clone()).or_default().1 = up;
|
||||
None
|
||||
}
|
||||
// core: @def_id
|
||||
Term::Ref { nam: def_name } => {
|
||||
let node = self.inet.new_node(Ref { def_name: def_name.clone() });
|
||||
self.inet.link(Port(node, 1), Port(node, 2));
|
||||
self.inet.link(up, Port(node, 0));
|
||||
Some(Port(node, 0))
|
||||
}
|
||||
Term::Let { pat: Pattern::Tup(box Pattern::Var(l_nam), box Pattern::Var(r_nam)), val, nxt } => {
|
||||
let dup = self.inet.new_node(Tup);
|
||||
|
||||
let val = self.encode_term(val, Port(dup, 0));
|
||||
self.link_local(Port(dup, 0), val);
|
||||
|
||||
self.push_scope(l_nam, Port(dup, 1));
|
||||
self.push_scope(r_nam, Port(dup, 2));
|
||||
let nxt = self.encode_term(nxt, up);
|
||||
self.pop_scope(r_nam, Port(dup, 2));
|
||||
self.pop_scope(l_nam, Port(dup, 1));
|
||||
|
||||
nxt
|
||||
}
|
||||
Term::Let { pat: Pattern::Var(None), val, nxt } => {
|
||||
let nod = self.inet.new_node(Era);
|
||||
let val = self.encode_term(val, Port(nod, 0));
|
||||
|
||||
self.link_local(Port(nod, 0), val);
|
||||
|
||||
self.encode_term(nxt, up)
|
||||
}
|
||||
Term::Let { .. } => unreachable!(), // Removed in earlier pass
|
||||
Term::Sup { tag, fst, snd } => {
|
||||
let sup = self.inet.new_node(Dup { lab: self.labels.dup.generate(tag).unwrap() });
|
||||
|
||||
let fst = self.encode_term(fst, Port(sup, 1));
|
||||
self.link_local(Port(sup, 1), fst);
|
||||
|
||||
let snd = self.encode_term(snd, Port(sup, 2));
|
||||
self.link_local(Port(sup, 2), snd);
|
||||
|
||||
Some(Port(sup, 0))
|
||||
}
|
||||
Term::Era => {
|
||||
let era = self.inet.new_node(Era);
|
||||
self.inet.link(Port(era, 1), Port(era, 2));
|
||||
Some(Port(era, 0))
|
||||
}
|
||||
// core: #val
|
||||
Term::Num { val } => {
|
||||
let node = self.inet.new_node(Num { val: *val });
|
||||
// This representation only has nodes of arity 2, so we connect the two aux ports that are not used.
|
||||
self.inet.link(Port(node, 1), Port(node, 2));
|
||||
Some(Port(node, 0))
|
||||
}
|
||||
Term::Str { .. } => unreachable!(), // Removed in desugar str
|
||||
Term::Lst { .. } => unreachable!(), // Removed in desugar list
|
||||
// core: & fst ~ <op snd ret>
|
||||
Term::Opx { op, fst, snd } => {
|
||||
let opx = self.inet.new_node(Op2 { opr: op.to_hvmc_label() });
|
||||
|
||||
let fst_port = self.encode_term(fst, Port(opx, 0));
|
||||
self.link_local(Port(opx, 0), fst_port);
|
||||
|
||||
let snd_port = self.encode_term(snd, Port(opx, 1));
|
||||
self.link_local(Port(opx, 1), snd_port);
|
||||
|
||||
Some(Port(opx, 2))
|
||||
}
|
||||
Term::Tup { fst, snd } => {
|
||||
let tup = self.inet.new_node(Tup);
|
||||
|
||||
let fst = self.encode_term(fst, Port(tup, 1));
|
||||
self.link_local(Port(tup, 1), fst);
|
||||
|
||||
let snd = self.encode_term(snd, Port(tup, 2));
|
||||
self.link_local(Port(tup, 2), snd);
|
||||
|
||||
Some(Port(tup, 0))
|
||||
}
|
||||
Term::Err => unreachable!(),
|
||||
}
|
||||
// core: (var_use bod)
|
||||
Term::Chn { tag, nam, bod } => {
|
||||
let fun = self.inet.new_node(Con { lab: self.labels.con.generate(tag) });
|
||||
self.global_vars.entry(nam.clone()).or_default().0 = Port(fun, 1);
|
||||
let bod = self.encode_term(bod, Port(fun, 2));
|
||||
self.link_local(Port(fun, 2), bod);
|
||||
Some(Port(fun, 0))
|
||||
}
|
||||
// An application becomes to a con node too. Ports:
|
||||
// - 0: points to the function being applied.
|
||||
// - 1: points to the function's argument.
|
||||
// - 2: points to where the application occurs.
|
||||
// core: & fun ~ (arg ret) (fun not necessarily main port)
|
||||
Term::App { tag, fun, arg } => {
|
||||
let app = self.inet.new_node(Con { lab: self.labels.con.generate(tag) });
|
||||
|
||||
let fun = self.encode_term(fun, Port(app, 0));
|
||||
self.link_local(Port(app, 0), fun);
|
||||
|
||||
let arg = self.encode_term(arg, Port(app, 1));
|
||||
self.link_local(Port(app, 1), arg);
|
||||
|
||||
Some(Port(app, 2))
|
||||
}
|
||||
// core: & cond ~ (zero succ) ret
|
||||
Term::Mat { args, rules } => {
|
||||
// At this point should be only simple num matches.
|
||||
let arg = args.iter().next().unwrap();
|
||||
debug_assert!(matches!(rules[0].pats[..], [Pattern::Num(NumCtr::Num(0))]));
|
||||
debug_assert!(matches!(rules[1].pats[..], [Pattern::Num(NumCtr::Succ(1, None))]));
|
||||
|
||||
let if_ = self.inet.new_node(Mat);
|
||||
|
||||
let cond = self.encode_term(arg, Port(if_, 0));
|
||||
self.link_local(Port(if_, 0), cond);
|
||||
|
||||
let zero = &rules[0].body;
|
||||
let succ = &rules[1].body;
|
||||
|
||||
let sel = self.inet.new_node(Con { lab: None });
|
||||
self.inet.link(Port(sel, 0), Port(if_, 1));
|
||||
let zero = self.encode_term(zero, Port(sel, 1));
|
||||
self.link_local(Port(sel, 1), zero);
|
||||
|
||||
let succ = self.encode_term(succ, Port(sel, 2));
|
||||
self.link_local(Port(sel, 2), succ);
|
||||
|
||||
Some(Port(if_, 2))
|
||||
}
|
||||
// A dup becomes a dup node too. Ports:
|
||||
// - 0: points to the value projected.
|
||||
// - 1: points to the occurrence of the first variable.
|
||||
// - 2: points to the occurrence of the second variable.
|
||||
// core: & val ~ {lab fst snd} (val not necessarily main port)
|
||||
Term::Dup { fst, snd, val, nxt, tag } => {
|
||||
let dup = self.inet.new_node(Dup { lab: self.labels.dup.generate(tag).unwrap() });
|
||||
|
||||
let val = self.encode_term(val, Port(dup, 0));
|
||||
self.link_local(Port(dup, 0), val);
|
||||
|
||||
self.push_scope(fst, Port(dup, 1));
|
||||
self.push_scope(snd, Port(dup, 2));
|
||||
let nxt = self.encode_term(nxt, up);
|
||||
self.pop_scope(snd, Port(dup, 2));
|
||||
self.pop_scope(fst, Port(dup, 1));
|
||||
|
||||
nxt
|
||||
}
|
||||
Term::Var { nam } => {
|
||||
// We assume this variable to be valid, bound and correctly scoped.
|
||||
// This pass must be done before.
|
||||
debug_assert!(
|
||||
self.scope.contains_key(nam),
|
||||
"Unbound variable {nam}. Expected this check to be already done"
|
||||
);
|
||||
let var_stack = &self.scope[nam];
|
||||
let cur_var = *var_stack.last().unwrap();
|
||||
let (declare_port, use_port) = self.vars.get_mut(cur_var).unwrap();
|
||||
debug_assert!(use_port.is_none(), "Variable {nam} used more than once");
|
||||
self.inet.link(up, *declare_port);
|
||||
*use_port = Some(up);
|
||||
Some(*declare_port)
|
||||
}
|
||||
Term::Lnk { nam } => {
|
||||
self.global_vars.entry(nam.clone()).or_default().1 = up;
|
||||
None
|
||||
}
|
||||
// core: @def_id
|
||||
Term::Ref { nam: def_name } => {
|
||||
let node = self.inet.new_node(Ref { def_name: def_name.clone() });
|
||||
self.inet.link(Port(node, 1), Port(node, 2));
|
||||
self.inet.link(up, Port(node, 0));
|
||||
Some(Port(node, 0))
|
||||
}
|
||||
Term::Let { pat: Pattern::Tup(box Pattern::Var(l_nam), box Pattern::Var(r_nam)), val, nxt } => {
|
||||
let dup = self.inet.new_node(Tup);
|
||||
|
||||
let val = self.encode_term(val, Port(dup, 0));
|
||||
self.link_local(Port(dup, 0), val);
|
||||
|
||||
self.push_scope(l_nam, Port(dup, 1));
|
||||
self.push_scope(r_nam, Port(dup, 2));
|
||||
let nxt = self.encode_term(nxt, up);
|
||||
self.pop_scope(r_nam, Port(dup, 2));
|
||||
self.pop_scope(l_nam, Port(dup, 1));
|
||||
|
||||
nxt
|
||||
}
|
||||
Term::Let { pat: Pattern::Var(None), val, nxt } => {
|
||||
let nod = self.inet.new_node(Era);
|
||||
let val = self.encode_term(val, Port(nod, 0));
|
||||
|
||||
self.link_local(Port(nod, 0), val);
|
||||
|
||||
self.encode_term(nxt, up)
|
||||
}
|
||||
Term::Let { .. } => unreachable!(), // Removed in earlier pass
|
||||
Term::Sup { tag, fst, snd } => {
|
||||
let sup = self.inet.new_node(Dup { lab: self.labels.dup.generate(tag).unwrap() });
|
||||
|
||||
let fst = self.encode_term(fst, Port(sup, 1));
|
||||
self.link_local(Port(sup, 1), fst);
|
||||
|
||||
let snd = self.encode_term(snd, Port(sup, 2));
|
||||
self.link_local(Port(sup, 2), snd);
|
||||
|
||||
Some(Port(sup, 0))
|
||||
}
|
||||
Term::Era => {
|
||||
let era = self.inet.new_node(Era);
|
||||
self.inet.link(Port(era, 1), Port(era, 2));
|
||||
Some(Port(era, 0))
|
||||
}
|
||||
// core: #val
|
||||
Term::Num { val } => {
|
||||
let node = self.inet.new_node(Num { val: *val });
|
||||
// This representation only has nodes of arity 2, so we connect the two aux ports that are not used.
|
||||
self.inet.link(Port(node, 1), Port(node, 2));
|
||||
Some(Port(node, 0))
|
||||
}
|
||||
Term::Str { .. } => unreachable!(), // Removed in desugar str
|
||||
Term::Lst { .. } => unreachable!(), // Removed in desugar list
|
||||
// core: & fst ~ <op snd ret>
|
||||
Term::Opx { op, fst, snd } => {
|
||||
let opx = self.inet.new_node(Op2 { opr: op.to_hvmc_label() });
|
||||
|
||||
let fst_port = self.encode_term(fst, Port(opx, 0));
|
||||
self.link_local(Port(opx, 0), fst_port);
|
||||
|
||||
let snd_port = self.encode_term(snd, Port(opx, 1));
|
||||
self.link_local(Port(opx, 1), snd_port);
|
||||
|
||||
Some(Port(opx, 2))
|
||||
}
|
||||
Term::Tup { fst, snd } => {
|
||||
let tup = self.inet.new_node(Tup);
|
||||
|
||||
let fst = self.encode_term(fst, Port(tup, 1));
|
||||
self.link_local(Port(tup, 1), fst);
|
||||
|
||||
let snd = self.encode_term(snd, Port(tup, 2));
|
||||
self.link_local(Port(tup, 2), snd);
|
||||
|
||||
Some(Port(tup, 0))
|
||||
}
|
||||
Term::Err => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn push_scope(&mut self, name: &Option<Name>, decl_port: Port) {
|
||||
|
@ -43,7 +43,10 @@ impl Term {
|
||||
| Term::Err => (),
|
||||
|
||||
Term::Let { .. } => {
|
||||
let Term::Let { pat, mut val, mut nxt } = std::mem::take(self) else { unreachable!() };
|
||||
let Term::Let { pat, val, nxt } = self else { unreachable!() };
|
||||
let pat = pat.clone();
|
||||
let mut val = std::mem::take(val);
|
||||
let mut nxt = std::mem::take(nxt);
|
||||
|
||||
val.desugar_let_destructors();
|
||||
nxt.desugar_let_destructors();
|
||||
|
@ -24,44 +24,46 @@ impl Ctx<'_> {
|
||||
|
||||
impl Term {
|
||||
fn linearize_simple_matches(&mut self, lift_all_vars: bool) -> Result<(), MatchErr> {
|
||||
match self {
|
||||
Term::Mat { args: _, rules } => {
|
||||
for rule in rules.iter_mut() {
|
||||
rule.body.linearize_simple_matches(lift_all_vars).unwrap();
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match self {
|
||||
Term::Mat { args: _, rules } => {
|
||||
for rule in rules.iter_mut() {
|
||||
rule.body.linearize_simple_matches(lift_all_vars).unwrap();
|
||||
}
|
||||
lift_match_vars(self, lift_all_vars);
|
||||
}
|
||||
lift_match_vars(self, lift_all_vars);
|
||||
}
|
||||
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => {
|
||||
bod.linearize_simple_matches(lift_all_vars)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => {
|
||||
bod.linearize_simple_matches(lift_all_vars)?;
|
||||
}
|
||||
|
||||
Term::Let { pat: Pattern::Var(..), val: fst, nxt: snd }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. } => {
|
||||
fst.linearize_simple_matches(lift_all_vars)?;
|
||||
snd.linearize_simple_matches(lift_all_vars)?;
|
||||
}
|
||||
Term::Let { pat: Pattern::Var(..), val: fst, nxt: snd }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. } => {
|
||||
fst.linearize_simple_matches(lift_all_vars)?;
|
||||
snd.linearize_simple_matches(lift_all_vars)?;
|
||||
}
|
||||
|
||||
Term::Lst { .. } => unreachable!(),
|
||||
Term::Let { pat, .. } => {
|
||||
unreachable!("Destructor let expression should have been desugared already. {pat}")
|
||||
}
|
||||
Term::Lst { .. } => unreachable!(),
|
||||
Term::Let { pat, .. } => {
|
||||
unreachable!("Destructor let expression should have been desugared already. {pat}")
|
||||
}
|
||||
|
||||
Term::Str { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Var { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era => {}
|
||||
Term::Str { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Var { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era => {}
|
||||
|
||||
Term::Err => todo!(),
|
||||
};
|
||||
Term::Err => todo!(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,10 @@ fn term_to_affine(term: &mut Term, inst_count: &mut HashMap<Name, u64>) {
|
||||
if val.has_unscoped() {
|
||||
term_to_affine(val, inst_count);
|
||||
|
||||
let Term::Let { val, nxt, .. } = std::mem::take(term) else { unreachable!() };
|
||||
let Term::Let { val, nxt, .. } = term else { unreachable!() };
|
||||
let val = std::mem::take(val);
|
||||
let nxt = std::mem::take(nxt);
|
||||
|
||||
*term = Term::Let { pat: Pattern::Var(None), val, nxt };
|
||||
|
||||
return;
|
||||
@ -86,7 +89,8 @@ fn term_to_affine(term: &mut Term, inst_count: &mut HashMap<Name, u64>) {
|
||||
if val.has_unscoped() {
|
||||
term_to_affine(val, inst_count);
|
||||
} else {
|
||||
let Term::Let { nxt, .. } = std::mem::take(term) else { unreachable!() };
|
||||
let Term::Let { nxt, .. } = term else { unreachable!() };
|
||||
let nxt = std::mem::take(nxt);
|
||||
*term = *nxt;
|
||||
}
|
||||
}
|
||||
|
@ -49,82 +49,86 @@ impl Term {
|
||||
main: Option<&Name>,
|
||||
scope: &mut HashMap<&'a Name, usize>,
|
||||
) -> Result<(), ReferencedMainErr> {
|
||||
match self {
|
||||
Term::Lam { nam, bod, .. } => {
|
||||
push_scope(nam.as_ref(), scope);
|
||||
bod.resolve_refs(def_names, main, scope)?;
|
||||
pop_scope(nam.as_ref(), scope);
|
||||
}
|
||||
Term::Let { pat: Pattern::Var(nam), val, nxt } => {
|
||||
val.resolve_refs(def_names, main, scope)?;
|
||||
push_scope(nam.as_ref(), scope);
|
||||
nxt.resolve_refs(def_names, main, scope)?;
|
||||
pop_scope(nam.as_ref(), scope);
|
||||
}
|
||||
Term::Let { pat, val, nxt } => {
|
||||
val.resolve_refs(def_names, main, scope)?;
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match self {
|
||||
Term::Lam { nam, bod, .. } => {
|
||||
push_scope(nam.as_ref(), scope);
|
||||
bod.resolve_refs(def_names, main, scope)?;
|
||||
pop_scope(nam.as_ref(), scope);
|
||||
}
|
||||
Term::Let { pat: Pattern::Var(nam), val, nxt } => {
|
||||
val.resolve_refs(def_names, main, scope)?;
|
||||
push_scope(nam.as_ref(), scope);
|
||||
nxt.resolve_refs(def_names, main, scope)?;
|
||||
pop_scope(nam.as_ref(), scope);
|
||||
}
|
||||
Term::Let { pat, val, nxt } => {
|
||||
val.resolve_refs(def_names, main, scope)?;
|
||||
|
||||
for nam in pat.binds() {
|
||||
push_scope(Some(nam), scope);
|
||||
for nam in pat.binds() {
|
||||
push_scope(Some(nam), scope);
|
||||
}
|
||||
|
||||
nxt.resolve_refs(def_names, main, scope)?;
|
||||
|
||||
for nam in pat.binds() {
|
||||
pop_scope(Some(nam), scope);
|
||||
}
|
||||
}
|
||||
Term::Dup { tag: _, fst, snd, val, nxt } => {
|
||||
val.resolve_refs(def_names, main, scope)?;
|
||||
push_scope(fst.as_ref(), scope);
|
||||
push_scope(snd.as_ref(), scope);
|
||||
nxt.resolve_refs(def_names, main, scope)?;
|
||||
pop_scope(fst.as_ref(), scope);
|
||||
pop_scope(snd.as_ref(), scope);
|
||||
}
|
||||
|
||||
nxt.resolve_refs(def_names, main, scope)?;
|
||||
// If variable not defined, we check if it's a ref and swap if it is.
|
||||
Term::Var { nam } => {
|
||||
if is_var_in_scope(nam, scope) {
|
||||
if let Some(main) = main {
|
||||
if nam == main {
|
||||
return Err(ReferencedMainErr);
|
||||
}
|
||||
}
|
||||
|
||||
for nam in pat.binds() {
|
||||
pop_scope(Some(nam), scope);
|
||||
}
|
||||
}
|
||||
Term::Dup { tag: _, fst, snd, val, nxt } => {
|
||||
val.resolve_refs(def_names, main, scope)?;
|
||||
push_scope(fst.as_ref(), scope);
|
||||
push_scope(snd.as_ref(), scope);
|
||||
nxt.resolve_refs(def_names, main, scope)?;
|
||||
pop_scope(fst.as_ref(), scope);
|
||||
pop_scope(snd.as_ref(), scope);
|
||||
}
|
||||
|
||||
// If variable not defined, we check if it's a ref and swap if it is.
|
||||
Term::Var { nam } => {
|
||||
if is_var_in_scope(nam, scope) {
|
||||
if let Some(main) = main {
|
||||
if nam == main {
|
||||
return Err(ReferencedMainErr);
|
||||
if def_names.contains(nam) || CORE_BUILTINS.contains(&nam.0.as_ref()) {
|
||||
*self = Term::r#ref(nam);
|
||||
}
|
||||
}
|
||||
}
|
||||
Term::Chn { bod, .. } => bod.resolve_refs(def_names, main, scope)?,
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.resolve_refs(def_names, main, scope)?;
|
||||
snd.resolve_refs(def_names, main, scope)?;
|
||||
}
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args {
|
||||
arg.resolve_refs(def_names, main, scope)?;
|
||||
}
|
||||
for rule in rules {
|
||||
for nam in rule.pats.iter().flat_map(|p| p.bind_or_eras()) {
|
||||
push_scope(nam.as_ref(), scope);
|
||||
}
|
||||
|
||||
if def_names.contains(nam) || CORE_BUILTINS.contains(&nam.0.as_ref()) {
|
||||
*self = Term::r#ref(nam);
|
||||
rule.body.resolve_refs(def_names, main, scope)?;
|
||||
|
||||
for nam in rule.pats.iter().flat_map(|p| p.bind_or_eras()).rev() {
|
||||
pop_scope(nam.as_ref(), scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Term::Chn { bod, .. } => bod.resolve_refs(def_names, main, scope)?,
|
||||
Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.resolve_refs(def_names, main, scope)?;
|
||||
snd.resolve_refs(def_names, main, scope)?;
|
||||
}
|
||||
Term::Mat { args, rules } => {
|
||||
for arg in args {
|
||||
arg.resolve_refs(def_names, main, scope)?;
|
||||
}
|
||||
for rule in rules {
|
||||
for nam in rule.pats.iter().flat_map(|p| p.bind_or_eras()) {
|
||||
push_scope(nam.as_ref(), scope);
|
||||
}
|
||||
|
||||
rule.body.resolve_refs(def_names, main, scope)?;
|
||||
|
||||
for nam in rule.pats.iter().flat_map(|p| p.bind_or_eras()).rev() {
|
||||
pop_scope(nam.as_ref(), scope);
|
||||
}
|
||||
Term::Lst { .. } => unreachable!("Should have been desugared already"),
|
||||
Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. } | Term::Str { .. } | Term::Era | Term::Err => {
|
||||
()
|
||||
}
|
||||
}
|
||||
Term::Lst { .. } => unreachable!("Should have been desugared already"),
|
||||
Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. } | Term::Str { .. } | Term::Era | Term::Err => (),
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,15 +17,13 @@ impl Term {
|
||||
} => {
|
||||
head.resugar_strings();
|
||||
tail.resugar_strings();
|
||||
let head = std::mem::take(head);
|
||||
let mut tail = std::mem::take(tail);
|
||||
|
||||
if ctr == SCONS
|
||||
&& let Term::Num { val } = head
|
||||
&& let Term::Str { val: tail } = tail
|
||||
{
|
||||
// If well formed string, add the next character to the string we're building
|
||||
let head = unsafe { char::from_u32_unchecked(val as u32) }.to_string();
|
||||
let head = unsafe { char::from_u32_unchecked(*val as u32) }.to_string();
|
||||
let str = head + &tail;
|
||||
*self = Term::str(&str);
|
||||
} else {
|
||||
@ -33,8 +31,12 @@ impl Term {
|
||||
|
||||
// Create `(Cons head Nil)` instead of `(Cons head "")`
|
||||
if matches!(&tail, Term::Str { val } if val.is_empty()) {
|
||||
tail = Term::r#ref(SNIL);
|
||||
*tail = Term::r#ref(SNIL);
|
||||
}
|
||||
|
||||
let head = std::mem::take(head);
|
||||
let tail = std::mem::take(tail);
|
||||
|
||||
*self = Term::call(Term::Ref { nam: ctr.clone() }, [head, tail]);
|
||||
}
|
||||
}
|
||||
@ -88,16 +90,16 @@ impl Term {
|
||||
head.resugar_lists();
|
||||
tail.resugar_lists();
|
||||
let head = std::mem::take(head);
|
||||
let tail = std::mem::take(tail);
|
||||
|
||||
if ctr == LCONS
|
||||
&& let Term::Lst { els: tail } = tail
|
||||
{
|
||||
// If well formed list, cons the next element to the list being formed
|
||||
let mut els = vec![head];
|
||||
els.extend(tail);
|
||||
els.extend(std::mem::take(tail));
|
||||
*self = Term::Lst { els };
|
||||
} else {
|
||||
let tail = std::mem::take(tail);
|
||||
*self = Term::call(Term::Ref { nam: ctr.clone() }, [head, tail]);
|
||||
}
|
||||
}
|
||||
|
@ -39,38 +39,40 @@ impl Term {
|
||||
///
|
||||
/// See `[simplify_match_expression]` for more information.
|
||||
pub fn simplify_matches(&mut self, ctrs: &Constructors, adts: &Adts) -> Result<(), MatchErr> {
|
||||
match self {
|
||||
Term::Mat { args, rules } => {
|
||||
let (new_args, extracted) = extract_args(args);
|
||||
stacker::maybe_grow(1024 * 32, 1024 * 1024, move || {
|
||||
match self {
|
||||
Term::Mat { args, rules } => {
|
||||
let (new_args, extracted) = extract_args(args);
|
||||
|
||||
*self = simplify_match_expression(&new_args, rules, ctrs, adts)?;
|
||||
*self = bind_extracted_args(extracted, std::mem::take(self));
|
||||
}
|
||||
|
||||
Term::Lst { els } => {
|
||||
for el in els {
|
||||
el.simplify_matches(ctrs, adts)?;
|
||||
let term = simplify_match_expression(&new_args, rules, ctrs, adts)?;
|
||||
*self = bind_extracted_args(extracted, term);
|
||||
}
|
||||
|
||||
Term::Lst { els } => {
|
||||
for el in els {
|
||||
el.simplify_matches(ctrs, adts)?;
|
||||
}
|
||||
}
|
||||
Term::Let { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.simplify_matches(ctrs, adts)?;
|
||||
snd.simplify_matches(ctrs, adts)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.simplify_matches(ctrs, adts)?,
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era => (),
|
||||
Term::Err => unreachable!(),
|
||||
}
|
||||
Term::Let { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Tup { fst, snd }
|
||||
| Term::Dup { val: fst, nxt: snd, .. }
|
||||
| Term::Sup { fst, snd, .. }
|
||||
| Term::Opx { fst, snd, .. } => {
|
||||
fst.simplify_matches(ctrs, adts)?;
|
||||
snd.simplify_matches(ctrs, adts)?;
|
||||
}
|
||||
Term::Lam { bod, .. } | Term::Chn { bod, .. } => bod.simplify_matches(ctrs, adts)?,
|
||||
Term::Var { .. }
|
||||
| Term::Lnk { .. }
|
||||
| Term::Num { .. }
|
||||
| Term::Str { .. }
|
||||
| Term::Ref { .. }
|
||||
| Term::Era => (),
|
||||
Term::Err => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user