Wip stacker and drop impl

This commit is contained in:
LunaAmora 2024-02-28 15:00:48 -03:00
parent 0b98642d96
commit 6263b642bb
18 changed files with 628 additions and 549 deletions

1
Cargo.lock generated
View File

@ -221,6 +221,7 @@ dependencies = [
"interner",
"itertools",
"logos",
"stacker",
"stdext",
"walkdir",
]

View File

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

File diff suppressed because one or more lines are too long

4
gen.hvm Normal file
View 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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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]);
}
}

View File

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