From 6c1c9ac50414fcf7557a29aa9f1755774f35ac19 Mon Sep 17 00:00:00 2001 From: Atul Bhosale Date: Sun, 30 Jun 2019 23:55:58 +0530 Subject: [PATCH] Format code using 'cargo fmt' --- src/alpha.rs | 483 +++++++----- src/ast.rs | 97 ++- src/ast_walk.rs | 584 ++++++++------ src/beta.rs | 240 +++--- src/core_forms.rs | 859 +++++++++++---------- src/core_macro_forms.rs | 265 ++++--- src/core_qq_forms.rs | 890 +++++++++++++--------- src/core_type_forms.rs | 404 +++++----- src/earley.rs | 1219 ++++++++++++++++++++---------- src/form.rs | 40 +- src/grammar.rs | 368 +++++---- src/macros/macros.rs | 103 +-- src/macros/mod.rs | 2 +- src/macros/reification_macros.rs | 30 +- src/main.rs | 465 ++++++++---- src/name.rs | 62 +- src/read.rs | 95 ++- src/runtime/core_values.rs | 85 ++- src/runtime/eval.rs | 148 ++-- src/runtime/mod.rs | 4 +- src/runtime/reify.rs | 334 +++++--- src/ty.rs | 247 +++--- src/ty_compare.rs | 645 ++++++++++------ src/unparse.rs | 101 ++- src/util/assoc.rs | 262 ++++--- src/util/err.rs | 2 +- src/util/mbe.rs | 832 +++++++++++++------- src/util/mod.rs | 2 +- src/walk_mode.rs | 334 ++++---- 29 files changed, 5712 insertions(+), 3490 deletions(-) diff --git a/src/alpha.rs b/src/alpha.rs index dc4254f..019b672 100644 --- a/src/alpha.rs +++ b/src/alpha.rs @@ -4,43 +4,71 @@ // `freshen` gets a value ready for destructuring. // `freshen_rec` gets a value and its pattern ready for destructuring. -use name::*; -use util::mbe::EnvMBE; use ast::Ast; use ast::Ast::*; +use name::*; use util::assoc::Assoc; +use util::mbe::EnvMBE; // A renaming that only affects names at the "current" quotation level #[derive(Clone, Debug, PartialEq)] pub struct Ren { env: Assoc, - q_lev: i16 + q_lev: i16, } impl Ren { pub fn find(&self, n: Name) -> Option<&Ast> { - if self.q_lev == 0 { self.env.find(&n) } else { None } + if self.q_lev == 0 { + self.env.find(&n) + } else { + None + } } pub fn unset(self, n: Name) -> Ren { if self.q_lev == 0 { - Ren { env: self.env.unset(&n), q_lev: self.q_lev } - } else { self } + Ren { + env: self.env.unset(&n), + q_lev: self.q_lev, + } + } else { + self + } } pub fn set_assoc(self, other: &Ren) -> Ren { if self.q_lev == other.q_lev { - Ren { env: self.env.set_assoc(&other.env), q_lev: self.q_lev } + Ren { + env: self.env.set_assoc(&other.env), + q_lev: self.q_lev, + } } else { self } } pub fn q_more(&self, by: u8) -> Ren { - Ren { env: self.env.clone(), q_lev: self.q_lev + i16::from(by) } + Ren { + env: self.env.clone(), + q_lev: self.q_lev + i16::from(by), + } } pub fn q_less(&self, by: u8) -> Ren { - Ren { env: self.env.clone(), q_lev: self.q_lev - i16::from(by) } + Ren { + env: self.env.clone(), + q_lev: self.q_lev - i16::from(by), + } + } + pub fn new() -> Ren { + Ren { + env: Assoc::new(), + q_lev: 0, + } + } + pub fn single(n: Name, a: Ast) -> Ren { + Ren { + env: Assoc::new().set(n, a), + q_lev: 0, + } } - pub fn new() -> Ren { Ren { env: Assoc::new(), q_lev: 0 } } - pub fn single(n: Name, a: Ast) -> Ren { Ren { env: Assoc::new().set(n, a), q_lev: 0} } } impl From> for Ren { @@ -53,30 +81,35 @@ fn substitute_rec(node: &Ast, cur_node_contents: &EnvMBE, env: &Ren) -> Ast match *node { Node(ref f, ref new_parts, ref export) => { //let new_cnc = parts.clone(); - Node(f.clone(), - new_parts.marched_map( - &mut |_, marched_parts: &EnvMBE, part: &Ast| - substitute_rec(part, marched_parts, env)), - export.clone()) - } - VariableReference(n) => { - env.find(n).unwrap_or(&node.clone()).clone() + Node( + f.clone(), + new_parts.marched_map(&mut |_, marched_parts: &EnvMBE, part: &Ast| { + substitute_rec(part, marched_parts, env) + }), + export.clone(), + ) } + VariableReference(n) => env.find(n).unwrap_or(&node.clone()).clone(), ExtendEnv(ref body, ref beta) => { let mut new_env = env.clone(); for bound_name in ::beta::bound_from_beta(beta, cur_node_contents, 0) { new_env = new_env.unset(bound_name); } - ExtendEnv(Box::new(substitute_rec(body, cur_node_contents, &new_env)), beta.clone()) + ExtendEnv( + Box::new(substitute_rec(body, cur_node_contents, &new_env)), + beta.clone(), + ) } - QuoteMore(ref body, pos) => { - QuoteMore(Box::new(substitute_rec(body, cur_node_contents, &env.q_more(1))), pos) - }, - QuoteLess(ref body, depth) => { - QuoteLess(Box::new(substitute_rec(body, cur_node_contents, &env.q_less(depth))), depth) - }, - _ => node.clone() + QuoteMore(ref body, pos) => QuoteMore( + Box::new(substitute_rec(body, cur_node_contents, &env.q_more(1))), + pos, + ), + QuoteLess(ref body, depth) => QuoteLess( + Box::new(substitute_rec(body, cur_node_contents, &env.q_less(depth))), + depth, + ), + _ => node.clone(), } } @@ -95,44 +128,45 @@ pub fn substitute(node: &Ast, env: &Assoc) -> Ast { fn mentioned_in_import(parts: &EnvMBE) -> Vec { fn process_ast(a: &Ast, v: &mut Vec) { match *a { - Node(_,_,_) => {} // new scope + Node(_, _, _) => {} // new scope ExtendEnv(ref body, ref beta) => { let mut beta_mentions = beta.names_mentioned_and_bound(); v.append(&mut beta_mentions); process_ast(&*body, v); } // TODO: does it make sense to mention a name underneath a quotation? - QuoteMore(ref body, _) | QuoteLess(ref body, _) => { process_ast(body, v) } + QuoteMore(ref body, _) | QuoteLess(ref body, _) => process_ast(body, v), Trivial | Atom(_) | VariableReference(_) => {} // no beta - Shape(_) | IncompleteNode(_) => { panic!("ICE: shouldn't be needed") } + Shape(_) | IncompleteNode(_) => panic!("ICE: shouldn't be needed"), } } let mut res = vec![]; - parts.map(&mut |a| { process_ast(a, &mut res) }); + parts.map(&mut |a| process_ast(a, &mut res)); res } fn freshen_rec(node: &Ast, renamings: &EnvMBE<(Ast, Ren)>, env: Ren) -> Ast { // `env` is used to update the references to those atoms to match match *node { - Node(_, _, _) => { substitute_rec(node, &EnvMBE::new(), &env) } - VariableReference(n) => { - env.find(n).unwrap_or(&node.clone()).clone() - } + Node(_, _, _) => substitute_rec(node, &EnvMBE::new(), &env), + VariableReference(n) => env.find(n).unwrap_or(&node.clone()).clone(), ExtendEnv(ref body, ref beta) => { let new_env = env.set_assoc(&beta.extract_from_mbe(renamings, &|x: &(_, Ren)| &x.1)); - ExtendEnv(Box::new( - freshen_rec(body, renamings, new_env)), beta.clone()) + ExtendEnv( + Box::new(freshen_rec(body, renamings, new_env)), + beta.clone(), + ) } QuoteMore(ref body, pos) => { QuoteMore(Box::new(freshen_rec(body, renamings, env.q_more(1))), pos) } - QuoteLess(ref body, depth) => { - QuoteLess(Box::new(freshen_rec(body, renamings, env.q_less(depth))), depth) - } - Atom(_) | Trivial | IncompleteNode(_) | Shape(_) => node.clone() + QuoteLess(ref body, depth) => QuoteLess( + Box::new(freshen_rec(body, renamings, env.q_less(depth))), + depth, + ), + Atom(_) | Trivial | IncompleteNode(_) | Shape(_) => node.clone(), } } @@ -140,7 +174,8 @@ thread_local! { pub static freshening_enabled: ::std::cell::RefCell = ::std::cell::RefCell::new(true); } -pub fn freshen(a: &Ast) -> Ast { // TODO: I think this shouldn't take a reference for performance +pub fn freshen(a: &Ast) -> Ast { + // TODO: I think this shouldn't take a reference for performance if freshening_enabled.with(|f| *f.borrow()) { match a { &Node(ref f, ref p, ref export) => { @@ -149,13 +184,17 @@ pub fn freshen(a: &Ast) -> Ast { // TODO: I think this shouldn't take a referenc // ...needs to have its binders freshend: let fresh_ast_and_rens = freshen_binders_inside_node(p, &mentioned); - Node(f.clone(), + Node( + f.clone(), fresh_ast_and_rens.marched_map( - &mut |_, marched: &EnvMBE<(Ast, Ren)>, &(ref part, _)| - freshen_rec(part, marched, Ren::new())), - export.clone()) + &mut |_, marched: &EnvMBE<(Ast, Ren)>, &(ref part, _)| { + freshen_rec(part, marched, Ren::new()) + }, + ), + export.clone(), + ) } - non_node => non_node.clone() + non_node => non_node.clone(), } } else { a.clone() @@ -166,7 +205,9 @@ pub fn freshen_with(lhs: &Ast, rhs: &Ast) -> (Ast, Ast) { if freshening_enabled.with(|f| *f.borrow()) { match (lhs, rhs) { (&Node(ref f, ref p_lhs, ref export), &Node(ref f_rhs, ref p_rhs, ref export_rhs)) => { - if f != f_rhs || export != export_rhs { return (lhs.clone(), rhs.clone()); } + if f != f_rhs || export != export_rhs { + return (lhs.clone(), rhs.clone()); + } // Every part that gets mentioned inside this node... let mentioned = mentioned_in_import(p_lhs); // (if it matters which `p_{l,r}hs` we used, the match below will be `None`) @@ -174,49 +215,70 @@ pub fn freshen_with(lhs: &Ast, rhs: &Ast) -> (Ast, Ast) { match freshen_binders_inside_node_with(p_lhs, p_rhs, &mentioned) { Some(fresh_ast_and_rens) => { let new_p_lhs = fresh_ast_and_rens.marched_map( - &mut |_, marched: &EnvMBE<(Ast, Ren, Ast, Ren)>, &(ref parts, _, _, _)| - freshen_rec(parts, - &marched.map(&mut |q| (q.0.clone(), q.1.clone())), - Ren::new())); + &mut |_, + marched: &EnvMBE<(Ast, Ren, Ast, Ren)>, + &(ref parts, _, _, _)| { + freshen_rec( + parts, + &marched.map(&mut |q| (q.0.clone(), q.1.clone())), + Ren::new(), + ) + }, + ); let new_p_rhs = fresh_ast_and_rens.marched_map( - &mut |_, marched: &EnvMBE<(Ast, Ren, Ast, Ren)>, &(_, _, ref parts, _)| - freshen_rec(parts, - &marched.map(&mut |q| (q.2.clone(), q.3.clone())), - Ren::new())); - (Node(f.clone(), new_p_lhs, export.clone()), - Node(f.clone(), new_p_rhs, export.clone())) - + &mut |_, + marched: &EnvMBE<(Ast, Ren, Ast, Ren)>, + &(_, _, ref parts, _)| { + freshen_rec( + parts, + &marched.map(&mut |q| (q.2.clone(), q.3.clone())), + Ren::new(), + ) + }, + ); + ( + Node(f.clone(), new_p_lhs, export.clone()), + Node(f.clone(), new_p_rhs, export.clone()), + ) } - None => (lhs.clone(), rhs.clone()) // No destructuring will be performed! + None => (lhs.clone(), rhs.clone()), // No destructuring will be performed! } } - _ => (lhs.clone(), rhs.clone()) // No binding + _ => (lhs.clone(), rhs.clone()), // No binding } } else { (lhs.clone(), rhs.clone()) // Freshening is disabled } } - -pub fn freshen_binders_inside_node(parts: &EnvMBE, mentioned: &[Name]) - -> EnvMBE<(Ast, Ren)> { - parts.named_map( - &mut |n: &Name, a: &Ast| { - if mentioned.contains(n) { freshen_binders(a) } else { (a.clone(), Ren::new()) }}) +pub fn freshen_binders_inside_node(parts: &EnvMBE, mentioned: &[Name]) -> EnvMBE<(Ast, Ren)> { + parts.named_map(&mut |n: &Name, a: &Ast| { + if mentioned.contains(n) { + freshen_binders(a) + } else { + (a.clone(), Ren::new()) + } + }) } -pub fn freshen_binders_inside_node_with(p_lhs: &EnvMBE, p_rhs: &EnvMBE, men: &[Name]) - -> Option> { - if ! p_lhs.can_map_with(p_rhs) { return None; } - p_lhs.named_map_with( - p_rhs, - &|n: &Name, a_lhs: &Ast, a_rhs: &Ast| { +pub fn freshen_binders_inside_node_with( + p_lhs: &EnvMBE, + p_rhs: &EnvMBE, + men: &[Name], +) -> Option> { + if !p_lhs.can_map_with(p_rhs) { + return None; + } + p_lhs + .named_map_with(p_rhs, &|n: &Name, a_lhs: &Ast, a_rhs: &Ast| { if men.contains(n) { freshen_binders_with(a_lhs, a_rhs).ok_or(()) } else { Ok((a_lhs.clone(), Ren::new(), a_rhs.clone(), Ren::new())) } - }).lift_result().ok() + }) + .lift_result() + .ok() } /// Returns an `Ast` like `a`, but with fresh `Atom`s @@ -226,7 +288,10 @@ pub fn freshen_binders(a: &Ast) -> (Ast, Ren) { Trivial | VariableReference(_) => (a.clone(), Ren::new()), Atom(old_name) => { let new_name = old_name.freshen(); - (Atom(new_name), Ren::single(old_name, VariableReference(new_name))) + ( + Atom(new_name), + Ren::single(old_name, VariableReference(new_name)), + ) } Node(ref f, ref parts, ref export) => { if export == &::beta::ExportBeta::Nothing { @@ -235,19 +300,22 @@ pub fn freshen_binders(a: &Ast) -> (Ast, Ren) { let exported = export.names_mentioned(); // Unmentioned atoms shouldn't be touched let fresh_pairs = freshen_binders_inside_node(parts, &exported); - let fresh_ast = fresh_pairs.map(&mut |&(ref a, _) : &(Ast, _)| a.clone()); + let fresh_ast = fresh_pairs.map(&mut |&(ref a, _): &(Ast, _)| a.clone()); let renaming = export.extract_from_mbe(&fresh_pairs, &|&(_, ref r): &(_, Ren)| &r); (Node(f.clone(), fresh_ast, export.clone()), renaming) } - IncompleteNode(_) | Shape(_) => { panic!("ICE: didn't think this was needed") } + IncompleteNode(_) | Shape(_) => panic!("ICE: didn't think this was needed"), QuoteMore(ref body, pos) => { - let (a,r) = freshen_binders(body); (QuoteMore(Box::new(a), pos), r.q_less(1)) + let (a, r) = freshen_binders(body); + (QuoteMore(Box::new(a), pos), r.q_less(1)) } QuoteLess(ref body, depth) => { - let (a,r) = freshen_binders(body); (QuoteLess(Box::new(a), depth), r.q_more(depth)) + let (a, r) = freshen_binders(body); + (QuoteLess(Box::new(a), depth), r.q_more(depth)) } - ExtendEnv(ref sub, ref beta) => { // We're only looking at `Atom`s, so this is transparent + ExtendEnv(ref sub, ref beta) => { + // We're only looking at `Atom`s, so this is transparent let (new_sub, subst) = freshen_binders(&*sub); (ExtendEnv(Box::new(new_sub), beta.clone()), subst) } @@ -255,22 +323,31 @@ pub fn freshen_binders(a: &Ast) -> (Ast, Ren) { } /// Like `freshen_binders`, but to unite two `Ast`s with identical structure (else returns `None`). -pub fn freshen_binders_with(lhs: &Ast, rhs: &Ast) -> Option<(Ast, Ren, Ast, Ren)>{ +pub fn freshen_binders_with(lhs: &Ast, rhs: &Ast) -> Option<(Ast, Ren, Ast, Ren)> { match (lhs, rhs) { (&Trivial, &Trivial) | (&VariableReference(_), &VariableReference(_)) => { Some((lhs.clone(), Ren::new(), rhs.clone(), Ren::new())) - }, + } (&Atom(old_name_lhs), &Atom(old_name_rhs)) => { let new_name = old_name_lhs.freshen(); - Some((Atom(new_name), Ren::single(old_name_lhs, VariableReference(new_name)), - Atom(new_name), Ren::single(old_name_rhs, VariableReference(new_name)))) + Some(( + Atom(new_name), + Ren::single(old_name_lhs, VariableReference(new_name)), + Atom(new_name), + Ren::single(old_name_rhs, VariableReference(new_name)), + )) } // TODO: Handle matching `'[let (a,b) = ⋯]'` against the pattern `'[let ,[p], = ⋯]'` !! - (&Node(ref f, ref parts_lhs, ref export), - &Node(ref f_rhs, ref parts_rhs, ref export_rhs)) => { - if f != f_rhs || export != export_rhs { return None } + ( + &Node(ref f, ref parts_lhs, ref export), + &Node(ref f_rhs, ref parts_rhs, ref export_rhs), + ) => { + if f != f_rhs || export != export_rhs { + return None; + } - if export == &::beta::ExportBeta::Nothing { // short-circuit: + if export == &::beta::ExportBeta::Nothing { + // short-circuit: return Some((lhs.clone(), Ren::new(), rhs.clone(), Ren::new())); } let exported = export.names_mentioned(); // Unmentioned atoms shouldn't be touched @@ -279,96 +356,126 @@ pub fn freshen_binders_with(lhs: &Ast, rhs: &Ast) -> Option<(Ast, Ren, Ast, Ren) Some(fresh_pairs) => { let fresh_ast_lhs = fresh_pairs.map(&mut |&(ref a, _, _, _)| a.clone()); let fresh_ast_rhs = fresh_pairs.map(&mut |&(_, _, ref a, _)| a.clone()); - let ren_lhs = export.extract_from_mbe(&fresh_pairs, &|t: &(_,Ren,_,_)| &t.1); - let ren_rhs = export.extract_from_mbe(&fresh_pairs, &|t: &(_,_,_,Ren)| &t.3); - Some((Node(f.clone(), fresh_ast_lhs, export.clone()), ren_lhs, - Node(f.clone(), fresh_ast_rhs, export.clone()), ren_rhs)) + let ren_lhs = export.extract_from_mbe(&fresh_pairs, &|t: &(_, Ren, _, _)| &t.1); + let ren_rhs = export.extract_from_mbe(&fresh_pairs, &|t: &(_, _, _, Ren)| &t.3); + Some(( + Node(f.clone(), fresh_ast_lhs, export.clone()), + ren_lhs, + Node(f.clone(), fresh_ast_rhs, export.clone()), + ren_rhs, + )) } - None => { None } + None => None, } } - (&QuoteMore(ref body_lhs, pos), &QuoteMore(ref body_rhs, pos_rhs)) - if pos == pos_rhs => { + (&QuoteMore(ref body_lhs, pos), &QuoteMore(ref body_rhs, pos_rhs)) if pos == pos_rhs => { match freshen_binders_with(&*body_lhs, &*body_rhs) { - Some((n_lhs, ren_lhs, n_rhs, ren_rhs)) => { - Some((QuoteMore(Box::new(n_lhs), pos), ren_lhs.q_less(1), - QuoteMore(Box::new(n_rhs), pos), ren_rhs.q_less(1))) - } - None => None + Some((n_lhs, ren_lhs, n_rhs, ren_rhs)) => Some(( + QuoteMore(Box::new(n_lhs), pos), + ren_lhs.q_less(1), + QuoteMore(Box::new(n_rhs), pos), + ren_rhs.q_less(1), + )), + None => None, } } (&QuoteLess(ref body_lhs, depth), &QuoteLess(ref body_rhs, depth_rhs)) - if depth == depth_rhs => { + if depth == depth_rhs => + { match freshen_binders_with(&*body_lhs, &*body_rhs) { - Some((n_lhs, ren_lhs, n_rhs, ren_rhs)) => { - Some((QuoteLess(Box::new(n_lhs), depth), ren_lhs.q_more(depth), - QuoteLess(Box::new(n_rhs), depth), ren_rhs.q_more(depth))) - } - None => None + Some((n_lhs, ren_lhs, n_rhs, ren_rhs)) => Some(( + QuoteLess(Box::new(n_lhs), depth), + ren_lhs.q_more(depth), + QuoteLess(Box::new(n_rhs), depth), + ren_rhs.q_more(depth), + )), + None => None, } } - (&IncompleteNode(_), _) | (&Shape(_), _) => { panic!("ICE: didn't think this was needed") } + (&IncompleteNode(_), _) | (&Shape(_), _) => panic!("ICE: didn't think this was needed"), (&ExtendEnv(ref sub_lhs, ref beta), &ExtendEnv(ref sub_rhs, ref beta_rhs)) => { - if beta != beta_rhs { return None; } + if beta != beta_rhs { + return None; + } // We're only looking at `Atom`s, so this is transparent match freshen_binders_with(&*sub_lhs, &*sub_rhs) { - Some((n_lhs, ren_lhs, n_rhs, ren_rhs)) => { - Some((ExtendEnv(Box::new(n_lhs), beta.clone()), ren_lhs, - ExtendEnv(Box::new(n_rhs), beta.clone()), ren_rhs)) - } - None => None + Some((n_lhs, ren_lhs, n_rhs, ren_rhs)) => Some(( + ExtendEnv(Box::new(n_lhs), beta.clone()), + ren_lhs, + ExtendEnv(Box::new(n_rhs), beta.clone()), + ren_rhs, + )), + None => None, } } - _ => None // Match failure + _ => None, // Match failure } } - #[test] fn basic_substitution() { ::name::enable_fake_freshness(true); - assert_eq!(substitute( + assert_eq!( + substitute( &ast!({"Expr" "apply" : "rator" => (vr "a"), "rand" => [(vr "b"), (vr "c")]}), - &assoc_n!("x" => ast!((vr "y")), "buchanan" => ast!((vr "lincoln")))), - ast!({"Expr" "apply" : "rator" => (vr "a"), "rand" => [(vr "b"), (vr "c")]})); + &assoc_n!("x" => ast!((vr "y")), "buchanan" => ast!((vr "lincoln"))) + ), + ast!({"Expr" "apply" : "rator" => (vr "a"), "rand" => [(vr "b"), (vr "c")]}) + ); - assert_eq!(substitute( + assert_eq!( + substitute( &ast!({"Expr" "apply" : "rator" => (vr "buchanan"), "rand" => [(vr "buchanan"), (vr "c")]}), - &assoc_n!("x" => ast!((vr "y")), "buchanan" => ast!((vr "lincoln")))), - ast!({"Expr" "apply" : "rator" => (vr "lincoln"), "rand" => [(vr "lincoln"), (vr "c")]})); + &assoc_n!("x" => ast!((vr "y")), "buchanan" => ast!((vr "lincoln"))) + ), + ast!({"Expr" "apply" : "rator" => (vr "lincoln"), "rand" => [(vr "lincoln"), (vr "c")]}) + ); - - assert_eq!(substitute( + assert_eq!( + substitute( &ast!({"Expr" "lambda" : "param" => ["b", "x"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), "rand" => [(vr "a"), (vr "b"), (vr "c")]})}), - &assoc_n!("a" => ast!((vr "A")), "b" => ast!((vr "B")), "c" => ast!((vr "C")))), + &assoc_n!("a" => ast!((vr "A")), "b" => ast!((vr "B")), "c" => ast!((vr "C"))) + ), ast!({"Expr" "lambda" : "param" => ["b", "x"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "A"), (vr "b"), (vr "C")]})})); + "rand" => [(vr "A"), (vr "b"), (vr "C")]})}) + ); } #[test] fn basic_binder_freshening() { ::name::enable_fake_freshness(true); - assert_eq!(freshen_binders(&ast!((vr "a"))), (ast!((vr "a")), Ren::new())); + assert_eq!( + freshen_binders(&ast!((vr "a"))), + (ast!((vr "a")), Ren::new()) + ); - assert_eq!(freshen_binders(&ast!("a")), - (ast!("a🍅"), Ren::from(assoc_n!("a" => ast!((vr "a🍅")))))); + assert_eq!( + freshen_binders(&ast!("a")), + ( + ast!("a🍅"), + Ren::from(assoc_n!("a" => ast!((vr "a🍅")))) + ) + ); - assert_eq!(freshen_binders( - &ast!({ "Pat" "enum_pat" => [* ["component"]] : + assert_eq!( + freshen_binders(&ast!({ "Pat" "enum_pat" => [* ["component"]] : "name" => "[ignored]", "component" => ["a", "b"] })), - (ast!({ "Pat" "enum_pat" => [* ["component"]] : + ( + ast!({ "Pat" "enum_pat" => [* ["component"]] : "name" => "[ignored]", "component" => ["a🍅", "b🍅"] }), - Ren::from(assoc_n!("a" => ast!((vr "a🍅")), "b" => ast!((vr "b🍅")))))); + Ren::from(assoc_n!("a" => ast!((vr "a🍅")), "b" => ast!((vr "b🍅")))) + ) + ); } #[test] @@ -376,8 +483,7 @@ fn basic_freshening() { ::name::enable_fake_freshness(true); assert_eq!( - freshen( - &ast!({"Expr" "lambda" : + freshen(&ast!({"Expr" "lambda" : "param" => ["a", "b"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), @@ -386,38 +492,39 @@ fn basic_freshening() { "param" => ["a🍅", "b🍅"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "a🍅"), (vr "b🍅"), (vr "c"), (vr "d")]})})); + "rand" => [(vr "a🍅"), (vr "b🍅"), (vr "c"), (vr "d")]})}) + ); assert_eq!( - freshen( - &ast!({"Expr" "match" : - "scrutinee" => (vr "x"), - "p" => [@"arm" "a", "b"], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a")), - (import ["p" = "scrutinee"] (vr "x"))] - })), + freshen(&ast!({"Expr" "match" : + "scrutinee" => (vr "x"), + "p" => [@"arm" "a", "b"], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a")), + (import ["p" = "scrutinee"] (vr "x"))] + })), ast!({"Expr" "match" : "scrutinee" => (vr "x"), "p" => [@"arm" "a🍅", "b🍅"], "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a🍅")), - (import ["p" = "scrutinee"] (vr "x"))]})); + (import ["p" = "scrutinee"] (vr "x"))]}) + ); // Test that importing non-atoms works assert_eq!( - freshen( - &ast!({"Expr" "match" : - "scrutinee" => (vr "x"), - "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : - "name" => "[ignored]", "component" => ["a"] - }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a"))] - })), + freshen(&ast!({"Expr" "match" : + "scrutinee" => (vr "x"), + "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : + "name" => "[ignored]", "component" => ["a"] + }], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a"))] + })), ast!({"Expr" "match" : "scrutinee" => (vr "x"), "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : "name" => "[ignored]", "component" => ["a🍅"] }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a🍅"))]})); + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a🍅"))]}) + ); //TODO: test more! } @@ -428,7 +535,8 @@ fn basic_freshening_with() { assert_eq!( freshen_with(&ast!({"Type" "Int" :}), &ast!({"Type" "Float" :})), - (ast!({"Type" "Int" :}), ast!({"Type" "Float" :}))); + (ast!({"Type" "Int" :}), ast!({"Type" "Float" :})) + ); assert_eq!( freshen_with( @@ -441,18 +549,21 @@ fn basic_freshening_with() { "param" => ["j", "k"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "j"), (vr "k"), (vr "x"), (vr "x")]})})), - - (ast!({"Expr" "lambda" : + "rand" => [(vr "j"), (vr "k"), (vr "x"), (vr "x")]})}) + ), + ( + ast!({"Expr" "lambda" : "param" => ["a🍅", "b🍅"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), "rand" => [(vr "a🍅"), (vr "b🍅"), (vr "c"), (vr "d")]})}), - ast!({"Expr" "lambda" : + ast!({"Expr" "lambda" : "param" => ["a🍅", "b🍅"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "a🍅"), (vr "b🍅"), (vr "x"), (vr "x")]})}))); + "rand" => [(vr "a🍅"), (vr "b🍅"), (vr "x"), (vr "x")]})}) + ) + ); assert_eq!( freshen_with( @@ -469,43 +580,51 @@ fn basic_freshening_with() { "name" => "[ignored]", "component" => ["x"] }], "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "x"))] - })), - (ast!({"Expr" "match" : + }) + ), + ( + ast!({"Expr" "match" : "scrutinee" => (vr "x"), "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : "name" => "[ignored]", "component" => ["a🍅"] }], "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a🍅"))]}), - ast!({"Expr" "match" : + ast!({"Expr" "match" : "scrutinee" => (vr "x"), "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : "name" => "[ignored]", "component" => ["a🍅"] }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a🍅"))]}))); + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "a🍅"))]}) + ) + ); - // Terms that don't match are unaffected - assert_eq!( - freshen_with( - &ast!({"Expr" "lambda" : + // Terms that don't match are unaffected + assert_eq!( + freshen_with( + &ast!({"Expr" "lambda" : "param" => ["a", "b"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), "rand" => [(vr "a"), (vr "b"), (vr "c"), (vr "d")]})}), - &ast!({"Expr" "lambda" : + &ast!({"Expr" "lambda" : "param" => ["abc"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "a"), (vr "b"), (vr "x"), (vr "x")]})})), - (ast!({"Expr" "lambda" : + "rand" => [(vr "a"), (vr "b"), (vr "x"), (vr "x")]})}) + ), + ( + ast!({"Expr" "lambda" : "param" => ["a", "b"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), "rand" => [(vr "a"), (vr "b"), (vr "c"), (vr "d")]})}), - ast!({"Expr" "lambda" : + ast!({"Expr" "lambda" : "param" => ["abc"], "body" => (import [* ["param" : "[ignored]"]] {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "a"), (vr "b"), (vr "x"), (vr "x")]})}))); + "rand" => [(vr "a"), (vr "b"), (vr "x"), (vr "x")]})}) + ) + ); } #[test] @@ -514,19 +633,19 @@ fn mu_substitution() { "body" => (import [* [prot "param"]] (vr "T")) }); assert_eq!(freshen(&trivial_mu), trivial_mu); - assert_eq!(substitute(&trivial_mu, &assoc_n!("T" => ast!((vr "S")))), + assert_eq!( + substitute(&trivial_mu, &assoc_n!("T" => ast!((vr "S")))), ast!( { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "S"))], - "body" => (import [* [prot "param"]] (vr "S")) })) + "body" => (import [* [prot "param"]] (vr "S")) }) + ) } - #[test] fn alpha_quote_more_or_less() { ::name::enable_fake_freshness(true); assert_eq!( - freshen( - &ast!({"Expr" "lambda" : + freshen(&ast!({"Expr" "lambda" : "param" => ["a", "b"], "body" => (import [* ["param" : "[ignored]"]] (++ true {"Expr" "apply" : "rator" => (vr "f"), @@ -535,11 +654,11 @@ fn alpha_quote_more_or_less() { "param" => ["a🍅", "b🍅"], "body" => (import [* ["param" : "[ignored]"]] (++ true {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "a"), (vr "b")]}))})); + "rand" => [(vr "a"), (vr "b")]}))}) + ); assert_eq!( - freshen( - &ast!({"Expr" "lambda" : + freshen(&ast!({"Expr" "lambda" : "param" => ["a", "b"], "body" => (import [* ["param" : "[ignored]"]] (++ true (-- 1 {"Expr" "apply" : "rator" => (vr "f"), @@ -548,8 +667,6 @@ fn alpha_quote_more_or_less() { "param" => ["a🍅", "b🍅"], "body" => (import [* ["param" : "[ignored]"]] (++ true (-- 1 {"Expr" "apply" : "rator" => (vr "f"), - "rand" => [(vr "a🍅"), (vr "b🍅")]})))})); - - - + "rand" => [(vr "a🍅"), (vr "b🍅")]})))}) + ); } diff --git a/src/ast.rs b/src/ast.rs index 2b71f88..2078ea3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -6,11 +6,11 @@ #![macro_use] -use util::mbe::EnvMBE; -use name::*; use beta::{Beta, ExportBeta}; -use std::iter; +use name::*; use std::fmt; +use std::iter; +use util::mbe::EnvMBE; // TODO: This really ought to be an `Rc` around an `enum` custom_derive! { @@ -45,24 +45,26 @@ pub use self::Ast::*; impl fmt::Debug for Ast { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Trivial => { write!(f, "⨉") }, - Atom(ref n) => { write!(f, "∘{:#?}∘", n) }, - VariableReference(ref v) => { write!(f, "{:#?}", v) } + Trivial => write!(f, "⨉"), + Atom(ref n) => write!(f, "∘{:#?}∘", n), + VariableReference(ref v) => write!(f, "{:#?}", v), Shape(ref v) => { write!(f, "(")?; let mut first = true; for elt in v { - if !first { write!(f, " ")? } + if !first { + write!(f, " ")? + } elt.fmt(f)?; first = false; } write!(f, ")") - }, + } Node(ref form, ref body, ref export) => { write!(f, "{{ ({}); {:#?}", form.name.sp(), body)?; match *export { ::beta::ExportBeta::Nothing => {} - _ => write!(f, " ⇑{:#?}", export)? + _ => write!(f, " ⇑{:#?}", export)?, } write!(f, "}}") } @@ -73,15 +75,9 @@ impl fmt::Debug for Ast { write!(f, "neg``{:#?}``", body) } } - QuoteLess(ref body, depth) => { - write!(f, ",,({}){:#?},,", depth, body) - } - IncompleteNode(ref body) => { - write!(f, "{{ INCOMPLETE; {:#?} }}", body) - } - ExtendEnv(ref body, ref beta) => { - write!(f, "{:#?}↓{:#?}", body, beta) - } + QuoteLess(ref body, depth) => write!(f, ",,({}){:#?},,", depth, body), + IncompleteNode(ref body) => write!(f, "{{ INCOMPLETE; {:#?} }}", body), + ExtendEnv(ref body, ref beta) => write!(f, "{:#?}↓{:#?}", body, beta), } } } @@ -90,20 +86,23 @@ impl fmt::Debug for Ast { impl fmt::Display for Ast { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Atom(ref n) => { write!(f, "{}", n.print()) }, - VariableReference(ref v) => { write!(f, "{}", v.print()) } + Atom(ref n) => write!(f, "{}", n.print()), + VariableReference(ref v) => write!(f, "{}", v.print()), Node(ref form, ref body, _) => { let s = ::unparse::unparse_mbe( - &form.grammar, self, body, &::core_forms::get_core_forms()); + &form.grammar, + self, + body, + &::core_forms::get_core_forms(), + ); write!(f, "{}", s) } - ExtendEnv(ref body, _) => { write!(f, "{}↓", body) } - _ => write!(f, "{:#?}", self) + ExtendEnv(ref body, _) => write!(f, "{}↓", body), + _ => write!(f, "{:#?}", self), } } } - impl Ast { // TODO: this ought to at least warn if we're losing anything other than `Shape` pub fn flatten(&self) -> EnvMBE { @@ -116,22 +115,24 @@ impl Ast { accum = accum.combine_overriding(&sub_a.flatten()) } accum - }, - IncompleteNode(ref env) => { env.clone() } + } + IncompleteNode(ref env) => env.clone(), Node(ref _f, ref _body, ref _export) => { // TODO: think about what should happen when // `Scope` contains a `Scope` without an intervening `Named` panic!("I don't know what to do here!") - }, + } QuoteMore(ref body, _) => body.flatten(), QuoteLess(ref body, _) => body.flatten(), - ExtendEnv(ref body, _) => body.flatten() + ExtendEnv(ref body, _) => body.flatten(), } } // TODO: use this more - pub fn destructure(&self, expd_form: ::std::rc::Rc<::form::Form>) - -> Option<::util::mbe::EnvMBE> { + pub fn destructure( + &self, + expd_form: ::std::rc::Rc<::form::Form>, + ) -> Option<::util::mbe::EnvMBE> { match self { Node(ref f, ref parts, _) => { if f == &expd_form { @@ -141,35 +142,32 @@ impl Ast { _ => {} } None - } - // TODO: I think we have a lot of places where we ought to use this function: pub fn node_parts(&self) -> &EnvMBE { match *self { Node(_, ref body, _) => body, - _ => panic!("ICE") + _ => panic!("ICE"), } } pub fn node_form(&self) -> &::form::Form { match *self { Node(ref form, _, _) => form, - _ => panic!("ICE") + _ => panic!("ICE"), } } } // This is used by combine::many, which is used by the Star parser impl iter::FromIterator for Ast { - fn from_iter>(i: I) -> Self { - IncompleteNode( - EnvMBE::new_from_anon_repeat( - i.into_iter().map(|a| a.flatten()).collect())) + fn from_iter>(i: I) -> Self { + IncompleteNode(EnvMBE::new_from_anon_repeat( + i.into_iter().map(|a| a.flatten()).collect(), + )) } } - /* * This is also sort of a test of MBE, since we need `Ast`s to make them with the macros * @@ -183,29 +181,28 @@ impl iter::FromIterator for Ast { fn combine_from_kleene_star() { use std::iter::FromIterator; - let parse_parts = vec![ast!({ - "b" => "8.0"}), - ast!({ - "a" => ["1", "2"], "b" => "8.1"}), - ast!({ - "a" => ["1", "2", "3"], "b" => "8.2"})]; + let parse_parts = vec![ + ast!({ - "b" => "8.0"}), + ast!({ - "a" => ["1", "2"], "b" => "8.1"}), + ast!({ - "a" => ["1", "2", "3"], "b" => "8.2"}), + ]; let parsed = Ast::from_iter(parse_parts); - let mut expected_mbe = - mbe!("a" => [@"triple" [], ["1", "2"], ["1", "2", "3"]], + let mut expected_mbe = mbe!("a" => [@"triple" [], ["1", "2"], ["1", "2", "3"]], "b" => [@"triple" "8.0", "8.1", "8.2"]); expected_mbe.anonimize_repeat(n("triple")); assert_eq!(parsed, IncompleteNode(expected_mbe)); } - #[test] fn star_construction() { let env = mbe!( "a" => ["1", "2"]); assert_eq!( ast!( { - "x" => [* env =>("a") env : (, env.get_leaf_or_panic(&n("a")).clone())]} ), - ast!( { - "x" => ["1", "2"] })); - - + ast!( { - "x" => ["1", "2"] }) + ); let env = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]); @@ -213,8 +210,8 @@ fn star_construction() { ast!( { - "x" => [* env =>("a", "b") env : ((, env.get_leaf_or_panic(&n("b")).clone()) (, env.get_leaf_or_panic(&n("a")).clone()))]} ), - ast!( { - "x" => [("11" "1"), ("22" "2")] })); - + ast!( { - "x" => [("11" "1"), ("22" "2")] }) + ); } #[test] diff --git a/src/ast_walk.rs b/src/ast_walk.rs index 0d703e0..e961875 100644 --- a/src/ast_walk.rs +++ b/src/ast_walk.rs @@ -1,4 +1,3 @@ - /* We often need to walk an `Ast` while maintaining an environment. This seems to be true at typecheck time and at runtime. @@ -65,22 +64,22 @@ Everything should be ambidextrous under quasiquotation, because all syntax should be constructable and matchable. */ -use std::rc::Rc; -use std::cell::RefCell; -use name::*; -use util::assoc::Assoc; -use util::mbe::EnvMBE; use ast::Ast; use ast::Ast::*; use beta::*; -use runtime::{reify, eval}; -use walk_mode::{WalkMode, WalkElt, Dir}; +use name::*; +use runtime::{eval, reify}; +use std::cell::RefCell; +use std::rc::Rc; +use util::assoc::Assoc; +use util::mbe::EnvMBE; +use walk_mode::{Dir, WalkElt, WalkMode}; /// A closed `Elt`; an `Elt` paired with an environment with which to interpret its free names. #[derive(Clone, Debug, PartialEq)] -pub struct Clo { +pub struct Clo { pub it: Elt, - pub env: Assoc + pub env: Assoc, } impl Clo { @@ -89,8 +88,8 @@ impl Clo { // we cut out the bits of the environments that are the same. let o_different_env = other.env.cut_common(&self.env); - let o_renaming = o_different_env.keyed_map_borrow_f( - &mut |name, _| VariableReference(name.freshen())); + let o_renaming = + o_different_env.keyed_map_borrow_f(&mut |name, _| VariableReference(name.freshen())); // if !o_renaming.empty() { println!("MERGE: {}", o_renaming); } @@ -98,11 +97,15 @@ impl Clo { for (o_name, o_val) in o_different_env.iter_pairs() { fresh_o_env = fresh_o_env.set( ::core_forms::vr_to_name(o_renaming.find(o_name).unwrap()), // HACK: VR -> Name - Elt::from_ast(&::alpha::substitute(&Elt::to_ast(o_val), &o_renaming))); + Elt::from_ast(&::alpha::substitute(&Elt::to_ast(o_val), &o_renaming)), + ); } - (self.it, Elt::from_ast(&::alpha::substitute(&Elt::to_ast(&other.it), &o_renaming)), - self.env.set_assoc(&fresh_o_env)) + ( + self.it, + Elt::from_ast(&::alpha::substitute(&Elt::to_ast(&other.it), &o_renaming)), + self.env.set_assoc(&fresh_o_env), + ) } } @@ -115,166 +118,174 @@ thread_local! { * `walk_ctxt` is used as an environment, * and by betas to access other parts of the current node. */ -pub fn walk(a: &Ast, walk_ctxt: &LazyWalkReses) - -> Result<::Out, Mode::Err> { layer_watch!{ ast_walk_layer : - // TODO: can we get rid of the & in front of our arguments and save the cloning? - // TODO: this has a lot of direction-specific runtime hackery. - // Maybe we want separate positive and negative versions? - let (a, walk_ctxt) = match *a { - // HACK: We want to process EE before pre_match before everything else. - // This probably means we should find a way to get rid of pre_match. - // But we can't just swap `a` and the ctxt when `a` is LiteralLike and the ctxt isn't. - - ExtendEnv(_,_) => { (a.clone(), walk_ctxt.clone()) } - _ => Mode::D::pre_walk(a.clone(), walk_ctxt.clone()) - }; +pub fn walk( + a: &Ast, + walk_ctxt: &LazyWalkReses, +) -> Result<::Out, Mode::Err> { + layer_watch! { ast_walk_layer : + // TODO: can we get rid of the & in front of our arguments and save the cloning? + // TODO: this has a lot of direction-specific runtime hackery. + // Maybe we want separate positive and negative versions? + let (a, walk_ctxt) = match *a { + // HACK: We want to process EE before pre_match before everything else. + // This probably means we should find a way to get rid of pre_match. + // But we can't just swap `a` and the ctxt when `a` is LiteralLike and the ctxt isn't. + + ExtendEnv(_,_) => { (a.clone(), walk_ctxt.clone()) } + _ => Mode::D::pre_walk(a.clone(), walk_ctxt.clone()) + }; - // ld!(ast_walk_layer, "{} {}", Mode::name(), a); - // lc!(ast_walk_layer, " from: {}", walk_ctxt.this_ast); - // match walk_ctxt.env.find(&negative_ret_val()) { - // Some(ref ctxt) => lc!(ast_walk_layer, " ctxt: {}", ctxt), _ => {}}; - // lc!(ast_walk_layer, " in: {:#?}", walk_ctxt.env.map_borrow_f(&mut |_| "…")); + // ld!(ast_walk_layer, "{} {}", Mode::name(), a); + // lc!(ast_walk_layer, " from: {}", walk_ctxt.this_ast); + // match walk_ctxt.env.find(&negative_ret_val()) { + // Some(ref ctxt) => lc!(ast_walk_layer, " ctxt: {}", ctxt), _ => {}}; + // lc!(ast_walk_layer, " in: {:#?}", walk_ctxt.env.map_borrow_f(&mut |_| "…")); + + let literally : Option = // If we're under a wrapper, `this_ast` might not be a Node + match a { + QuoteMore(_,_) | QuoteLess(_,_) | ExtendEnv(_,_) => { + match walk_ctxt.this_ast { + // `this_ast` might be `NotWalked` (and non-literal) if under `switch_mode`. + // It's weird, but seems to be the right thing + Node(ref f, _, _) => Some(Mode::get_walk_rule(f).is_literally()), + _ => None + } + } + _ => None + }; - let literally : Option = // If we're under a wrapper, `this_ast` might not be a Node match a { - QuoteMore(_,_) | QuoteLess(_,_) | ExtendEnv(_,_) => { - match walk_ctxt.this_ast { - // `this_ast` might be `NotWalked` (and non-literal) if under `switch_mode`. - // It's weird, but seems to be the right thing - Node(ref f, _, _) => Some(Mode::get_walk_rule(f).is_literally()), - _ => None + Node(ref f, ref parts, _) => { + let new_walk_ctxt = walk_ctxt.switch_ast(parts, a.clone()); + // certain walks only work on certain kinds of AST nodes + match Mode::get_walk_rule(f) { + Custom(ref ts_fn) => ts_fn(new_walk_ctxt), + Body(n) => walk(parts.get_leaf(n).unwrap(), &new_walk_ctxt), + LiteralLike => Mode::walk_quasi_literally(a.clone(), &new_walk_ctxt), + NotWalked => panic!("ICE: {:#?} should not be walked at all!", a) } } - _ => None - }; - - match a { - Node(ref f, ref parts, _) => { - let new_walk_ctxt = walk_ctxt.switch_ast(parts, a.clone()); - // certain walks only work on certain kinds of AST nodes - match Mode::get_walk_rule(f) { - Custom(ref ts_fn) => ts_fn(new_walk_ctxt), - Body(n) => walk(parts.get_leaf(n).unwrap(), &new_walk_ctxt), - LiteralLike => Mode::walk_quasi_literally(a.clone(), &new_walk_ctxt), - NotWalked => panic!("ICE: {:#?} should not be walked at all!", a) - } - } - IncompleteNode(ref parts) => { panic!("ICE: {:#?} isn't a complete node", parts)} - - VariableReference(n) => { Mode::walk_var(n, &walk_ctxt) } - Atom(n) => { Mode::walk_atom(n, &walk_ctxt) } - - // TODO: we need to preserve these in LiteralLike contexts!! - - // So do we just set the context element at the wrong level and then grab it for the shift? - // I guess so. - QuoteMore(ref body, pos_inside) => { - let oeh_m = Mode::D::oeh_if_negative(); - let old_ctxt_elt = walk_ctxt.maybe__context_elt(); - - let currently_positive = oeh_m.is_none(); // kinda a hack for "Is `Mode` positive?" - - // Negative modes at quotation does some weird stuff. For example: - // `match e { `[Expr | (add 5 ,[Expr <[Nat]< | a],)]` => ⋯}` - // ^--- `quote_more` here (`get_res` produces `Expr <[Nat]<`), - // which we already knew. - // ^--- `quote_less`, and we get {a => Expr <[Nat]<} - // We need to smuggle out what we know at each `quote_less` (there might be many), - // so that `a` winds up bound to `Expr <[Nat]<` on the RHS. - - // If the quotation (outside) is negative, we need to unsquirrel no matter the inside. - // If both are positive, return the result (so the form can do `Nat` → `Expr <[Nat]<`). - // Otherwise, the context (expected type) is the result. - - if pos_inside == currently_positive { // stay in the same mode? - let inner_walk_ctxt = walk_ctxt.clone() - .quote_more(oeh_m.clone()); - let res = maybe_literally__walk(&a, body, inner_walk_ctxt, old_ctxt_elt, - literally)?; - - match oeh_m { - None => Ok(res), // positive walk, result is useful. Otherwise, unsquirrel: - Some(oeh) => { Ok( Mode::env_as_out((*oeh.borrow()).clone()) ) } - } - } else { - let inner_walk_ctxt = walk_ctxt.clone() - .switch_mode::().quote_more(oeh_m.clone()); - let _ = maybe_literally__walk(&a, body, inner_walk_ctxt, old_ctxt_elt, - literally)?; - - match oeh_m { - // HACK: just return the context element (and massage the type) - None => Mode::walk_var(negative_ret_val(), &walk_ctxt), - Some(oeh) => { Ok( Mode::env_as_out((*oeh.borrow()).clone()) ) } + IncompleteNode(ref parts) => { panic!("ICE: {:#?} isn't a complete node", parts)} + + VariableReference(n) => { Mode::walk_var(n, &walk_ctxt) } + Atom(n) => { Mode::walk_atom(n, &walk_ctxt) } + + // TODO: we need to preserve these in LiteralLike contexts!! + + // So do we just set the context element at the wrong level and then grab it for the shift? + // I guess so. + QuoteMore(ref body, pos_inside) => { + let oeh_m = Mode::D::oeh_if_negative(); + let old_ctxt_elt = walk_ctxt.maybe__context_elt(); + + let currently_positive = oeh_m.is_none(); // kinda a hack for "Is `Mode` positive?" + + // Negative modes at quotation does some weird stuff. For example: + // `match e { `[Expr | (add 5 ,[Expr <[Nat]< | a],)]` => ⋯}` + // ^--- `quote_more` here (`get_res` produces `Expr <[Nat]<`), + // which we already knew. + // ^--- `quote_less`, and we get {a => Expr <[Nat]<} + // We need to smuggle out what we know at each `quote_less` (there might be many), + // so that `a` winds up bound to `Expr <[Nat]<` on the RHS. + + // If the quotation (outside) is negative, we need to unsquirrel no matter the inside. + // If both are positive, return the result (so the form can do `Nat` → `Expr <[Nat]<`). + // Otherwise, the context (expected type) is the result. + + if pos_inside == currently_positive { // stay in the same mode? + let inner_walk_ctxt = walk_ctxt.clone() + .quote_more(oeh_m.clone()); + let res = maybe_literally__walk(&a, body, inner_walk_ctxt, old_ctxt_elt, + literally)?; + + match oeh_m { + None => Ok(res), // positive walk, result is useful. Otherwise, unsquirrel: + Some(oeh) => { Ok( Mode::env_as_out((*oeh.borrow()).clone()) ) } + } + } else { + let inner_walk_ctxt = walk_ctxt.clone() + .switch_mode::().quote_more(oeh_m.clone()); + let _ = maybe_literally__walk(&a, body, inner_walk_ctxt, old_ctxt_elt, + literally)?; + + match oeh_m { + // HACK: just return the context element (and massage the type) + None => Mode::walk_var(negative_ret_val(), &walk_ctxt), + Some(oeh) => { Ok( Mode::env_as_out((*oeh.borrow()).clone()) ) } + } } } - } - QuoteLess(ref body, depth) => { - let old_ctxt_elt = walk_ctxt.maybe__context_elt(); - - let mut oeh = None; - let mut walk_ctxt = walk_ctxt.clone(); - for _ in 0..depth { - let (oeh_new, walk_ctxt_new) = walk_ctxt.quote_less(); - oeh = oeh_new; - walk_ctxt = walk_ctxt_new; - } + QuoteLess(ref body, depth) => { + let old_ctxt_elt = walk_ctxt.maybe__context_elt(); + + let mut oeh = None; + let mut walk_ctxt = walk_ctxt.clone(); + for _ in 0..depth { + let (oeh_new, walk_ctxt_new) = walk_ctxt.quote_less(); + oeh = oeh_new; + walk_ctxt = walk_ctxt_new; + } - let res = maybe_literally__walk(&a, body, walk_ctxt, old_ctxt_elt, literally)?; + let res = maybe_literally__walk(&a, body, walk_ctxt, old_ctxt_elt, literally)?; - squirrel_away::(oeh, res.clone()); + squirrel_away::(oeh, res.clone()); - Ok(res) - } + Ok(res) + } - Trivial | Shape(_) => { - panic!("ICE: {:#?} is not a walkable AST", a); - } + Trivial | Shape(_) => { + panic!("ICE: {:#?} is not a walkable AST", a); + } - // TODO: `env_from_beta` only works in positive modes... what should we do otherwise? - ExtendEnv(ref body, ref beta) => { - let new_env = if Mode::automatically_extend_env() { - walk_ctxt.env.set_assoc( - &env_from_beta(beta, &walk_ctxt)?) - } else { - walk_ctxt.env.clone() - }; - // print!("↓↓↓↓: {:#?}\n : {:#?}\n", beta, new_env.map(|_| "…")); - - let new__walk_ctxt = walk_ctxt.with_environment(new_env); - let new__walk_ctxt = // If the RHS is also binding, assume it's the same - // TODO: we should make this only happen if we're actually negative. - // The context element is sometimes leftover from a previous negative walk. - match walk_ctxt.env.find(&negative_ret_val()) - .map(::Elt::to_ast) { - Some(ExtendEnv(ref rhs_body, _)) => { - new__walk_ctxt.with_context( - ::Elt::from_ast(&*rhs_body)) + // TODO: `env_from_beta` only works in positive modes... what should we do otherwise? + ExtendEnv(ref body, ref beta) => { + let new_env = if Mode::automatically_extend_env() { + walk_ctxt.env.set_assoc( + &env_from_beta(beta, &walk_ctxt)?) + } else { + walk_ctxt.env.clone() + }; + // print!("↓↓↓↓: {:#?}\n : {:#?}\n", beta, new_env.map(|_| "…")); + + let new__walk_ctxt = walk_ctxt.with_environment(new_env); + let new__walk_ctxt = // If the RHS is also binding, assume it's the same + // TODO: we should make this only happen if we're actually negative. + // The context element is sometimes leftover from a previous negative walk. + match walk_ctxt.env.find(&negative_ret_val()) + .map(::Elt::to_ast) { + Some(ExtendEnv(ref rhs_body, _)) => { + new__walk_ctxt.with_context( + ::Elt::from_ast(&*rhs_body)) + } + _ => new__walk_ctxt + }; + + fn extract__ee_body(e: ::Elt) + -> ::Elt { + match e.to_ast() { + ExtendEnv(ref body, _) => { ::Elt::from_ast(&*body) } + _ => { e } // Match will fail } - _ => new__walk_ctxt - }; - - fn extract__ee_body(e: ::Elt) - -> ::Elt { - match e.to_ast() { - ExtendEnv(ref body, _) => { ::Elt::from_ast(&*body) } - _ => { e } // Match will fail } - } - maybe_literally__walk(&a, body, new__walk_ctxt, - walk_ctxt.maybe__context_elt().map(extract__ee_body::), literally) + maybe_literally__walk(&a, body, new__walk_ctxt, + walk_ctxt.maybe__context_elt().map(extract__ee_body::), literally) + } } } -}} +} /// If a `Node` is `LiteralLike`, its imports and [un]quotes should be, too! -fn maybe_literally__walk(a: &Ast, body: &Ast, walk_ctxt: LazyWalkReses, - ctxt_elt: Option, literally: Option) - -> Result<::Out, Mode::Err> { +fn maybe_literally__walk( + a: &Ast, + body: &Ast, + walk_ctxt: LazyWalkReses, + ctxt_elt: Option, + literally: Option, +) -> Result<::Out, Mode::Err> { let walk_ctxt = match ctxt_elt { Some(e) => walk_ctxt.with_context(e), - None => walk_ctxt + None => walk_ctxt, }; // It might be right to assume that it's true if the mode is quasiquotation if literally.expect("ICE: unable to determine literalness") { @@ -299,17 +310,24 @@ pub enum WalkRule { /// Only valid in modes where `Ast`s can be converted to `::Elt`s. LiteralLike, /// "this form should not ever be walked". - NotWalked + NotWalked, } impl WalkRule { - fn is_literally(&self) -> bool { match self { LiteralLike => true, _ => false } } + fn is_literally(&self) -> bool { + match self { + LiteralLike => true, + _ => false, + } + } } // trait bounds on parameters and functions are not yet supported by `Reifiable!` impl reify::Reifiable for WalkRule { // doesn't need to be parameterized because it will be opaque. I think!? - fn ty_name() -> Name { n("walk_rule") } + fn ty_name() -> Name { + n("walk_rule") + } fn reify(&self) -> eval::Value { match *self { @@ -317,23 +335,23 @@ impl reify::Reifiable for WalkRule { Body(ref n) => val!(enum "Body", (, n.reify())), Custom(ref lwr_to_out) => val!(enum "Custom", (, reify::reify_1ary_function(lwr_to_out.clone()))), - LiteralLike => val!(enum "LiteralLike",) + LiteralLike => val!(enum "LiteralLike",), } } fn reflect(v: &eval::Value) -> Self { extract!((v) eval::Value::Enum = (ref choice, ref parts) => - if choice.is("NotWalked") { - WalkRule::NotWalked - } else if choice.is("Body") { - WalkRule::Body(Name::reflect(&parts[0])) - } else if choice.is("Custom") { - WalkRule::Custom(reify::reflect_1ary_function(parts[0].clone())) - } else if choice.is("LiteralLike") { - WalkRule::LiteralLike - } else { - panic!("ICE in WalkRule reflection") - }) + if choice.is("NotWalked") { + WalkRule::NotWalked + } else if choice.is("Body") { + WalkRule::Body(Name::reflect(&parts[0])) + } else if choice.is("Custom") { + WalkRule::Custom(reify::reflect_1ary_function(parts[0].clone())) + } else if choice.is("LiteralLike") { + WalkRule::LiteralLike + } else { + panic!("ICE in WalkRule reflection") + }) } } @@ -343,12 +361,11 @@ impl ::std::fmt::Debug for WalkRule { NotWalked => write!(f, "NotWalked"), Body(ref n) => write!(f, "Body({})", n), Custom(_) => write!(f, "Custom(-)"), - LiteralLike => write!(f, "LiteralLike") + LiteralLike => write!(f, "LiteralLike"), } } } - pub use self::WalkRule::*; /** An environment of walked things. */ @@ -358,13 +375,15 @@ pub type ResEnv = Assoc; #[derive(Debug)] pub struct LazilyWalkedTerm { pub term: Ast, - pub res: RefCell::Out, Mode::Err>>> + pub res: RefCell::Out, Mode::Err>>>, } // trait bounds on parameters are not yet supported by `Reifiable!` impl reify::Reifiable for LazilyWalkedTerm { // doesn't need to be parameterized because it will be opaque. I think!? - fn ty_name() -> Name { n("lazily_walked_term") } + fn ty_name() -> Name { + n("lazily_walked_term") + } fn reify(&self) -> eval::Value { val!(struct "term" => (, self.term.reify()), "res" => (, self.res.reify())) @@ -381,32 +400,41 @@ impl reify::Reifiable for LazilyWalkedTerm { // We only implement this because lazy-rust is unstable impl LazilyWalkedTerm { pub fn new(t: &Ast) -> Rc> { - Rc::new(LazilyWalkedTerm { term: t.clone(), res: RefCell::new(None) }) + Rc::new(LazilyWalkedTerm { + term: t.clone(), + res: RefCell::new(None), + }) } /** Get the result of walking this term (memoized) */ - fn get_res(&self, cur_node_contents: &LazyWalkReses) - -> Result<::Out, Mode::Err> { + fn get_res( + &self, + cur_node_contents: &LazyWalkReses, + ) -> Result<::Out, Mode::Err> { self.memoized(&|| walk::(&self.term, cur_node_contents)) } - fn memoized(&self, f: &dyn Fn() -> Result<::Out, Mode::Err>) - -> Result<::Out, Mode::Err> { + fn memoized( + &self, + f: &dyn Fn() -> Result<::Out, Mode::Err>, + ) -> Result<::Out, Mode::Err> { let result = self.res.borrow_mut().take().unwrap_or_else(f); - * self.res.borrow_mut() = Some(result.clone()); + *self.res.borrow_mut() = Some(result.clone()); result } fn clear_memo(&self) { - * self.res.borrow_mut() = None; + *self.res.borrow_mut() = None; } } -pub type OutEnvHandle = Rc::Elt>>>; +pub type OutEnvHandle = Rc::Elt>>>; /// Only does anything if `Mode` is negative. -pub fn squirrel_away(opt_oeh: Option>, - more_env: ::Out) { +pub fn squirrel_away( + opt_oeh: Option>, + more_env: ::Out, +) { if let Some(oeh) = opt_oeh { let new_env = oeh.borrow().set_assoc(&Mode::out_as_env(more_env)); *oeh.borrow_mut() = new_env; @@ -439,13 +467,15 @@ pub struct LazyWalkReses { pub this_ast: Ast, - pub extra_info: Mode::ExtraInfo + pub extra_info: Mode::ExtraInfo, } // trait bounds on parameters are not yet supported by `Reifiable!` impl reify::Reifiable for LazyWalkReses { // doesn't need to be parameterized because it will be opaque. I think!? - fn ty_name() -> Name { n("lazy_walked_reses") } + fn ty_name() -> Name { + n("lazy_walked_reses") + } fn reify(&self) -> eval::Value { val!(struct "parts" => (, self.parts.reify()), "env" => (, self.env.reify()), @@ -475,20 +505,20 @@ impl reify::Reifiable for LazyWalkReses { } } - - impl LazyWalkReses { - pub fn new(env: ResEnv, // TODO: we could get rid of the middle argument - parts_unwalked: &EnvMBE, - this_ast: Ast) - -> LazyWalkReses { + pub fn new( + env: ResEnv, // TODO: we could get rid of the middle argument + parts_unwalked: &EnvMBE, + this_ast: Ast, + ) -> LazyWalkReses { LazyWalkReses { env: env, - more_quoted_env: vec![], less_quoted_env: vec![], + more_quoted_env: vec![], + less_quoted_env: vec![], less_quoted_out_env: vec![], parts: parts_unwalked.map(&mut LazilyWalkedTerm::new), this_ast: this_ast, - extra_info: ::std::default::Default::default() + extra_info: ::std::default::Default::default(), } } @@ -499,32 +529,39 @@ impl LazyWalkReses { more_quoted_env: vec![], less_quoted_env: vec![], less_quoted_out_env: vec![], - parts: EnvMBE::new(), this_ast: ast!("wrapper"), - extra_info: ::std::default::Default::default() + parts: EnvMBE::new(), + this_ast: ast!("wrapper"), + extra_info: ::std::default::Default::default(), } } - pub fn new_mq_wrapper(env: ResEnv, - mqe: Vec>) -> LazyWalkReses { + pub fn new_mq_wrapper( + env: ResEnv, + mqe: Vec>, + ) -> LazyWalkReses { LazyWalkReses { env: env, more_quoted_env: mqe, less_quoted_env: vec![], less_quoted_out_env: vec![], // If we want a `lqe`, we need to fill this in, too! - parts: EnvMBE::new(), this_ast: ast!("wrapper"), - extra_info: ::std::default::Default::default() + parts: EnvMBE::new(), + this_ast: ast!("wrapper"), + extra_info: ::std::default::Default::default(), } } pub fn switch_ast(self, parts: &EnvMBE, this_ast: Ast) -> LazyWalkReses { LazyWalkReses { - parts: parts.map(&mut LazilyWalkedTerm::new), this_ast: this_ast, .. self + parts: parts.map(&mut LazilyWalkedTerm::new), + this_ast: this_ast, + ..self } } pub fn this_form(&self) -> Rc<::form::Form> { match self.this_ast { - Node(ref f, _, _) => f.clone(), _ => panic!("ICE") + Node(ref f, _, _) => f.clone(), + _ => panic!("ICE"), } } @@ -535,17 +572,23 @@ impl LazyWalkReses { /// Like `get_res`, but for subforms that are repeated at depth 1. Sort of a hack. pub fn get_rep_res(&self, part_name: Name) -> Result::Out>, Mode::Err> { - self.parts.get_rep_leaf_or_panic(part_name) - .iter().map( |&lwt| lwt.get_res(self)).collect() + self.parts + .get_rep_leaf_or_panic(part_name) + .iter() + .map(|&lwt| lwt.get_res(self)) + .collect() } /// Like `get_res`, but with `depth` levels of repetition, and calling `f` to flatten the result pub fn flatten_res_at_depth( - &self, part_name: Name, depth: u8, - f: &dyn Fn(Vec<::Out>) -> ::Out) - -> Result<::Out, Mode::Err> { + &self, + part_name: Name, + depth: u8, + f: &dyn Fn(Vec<::Out>) -> ::Out, + ) -> Result<::Out, Mode::Err> { self.parts.map_flatten_rep_leaf_or_panic( - part_name, depth, + part_name, + depth, &|lwt: &Rc>| -> Result<::Out, Mode::Err> { return lwt.get_res(self); }, @@ -555,7 +598,8 @@ impl LazyWalkReses { accum.push(elt?); } Ok(f(accum)) - }) + }, + ) } /*/** Like `get_rep_res`, but doesn't panic if the name is absent. */ @@ -581,8 +625,11 @@ impl LazyWalkReses { } pub fn get_rep_term(&self, part_name: Name) -> Vec { - self.parts.get_rep_leaf_or_panic(part_name) - .iter().map( |&lwt| lwt.term.clone()).collect() + self.parts + .get_rep_leaf_or_panic(part_name) + .iter() + .map(|&lwt| lwt.term.clone()) + .collect() } /** Only sensible for negative walks */ @@ -598,27 +645,35 @@ impl LazyWalkReses { /// Change the context (by editing the environment). Only sensible for negative walks. /// Don't do `.with_context(…).with_environment(…)` pub fn with_context(&self, e: Mode::Elt) -> LazyWalkReses { - LazyWalkReses { env: self.env.set(negative_ret_val(), e), .. (*self).clone() } + LazyWalkReses { + env: self.env.set(negative_ret_val(), e), + ..(*self).clone() + } } /// Change the whole environment pub fn with_environment(&self, env: ResEnv) -> LazyWalkReses { - LazyWalkReses { env: env, .. (*self).clone() } + LazyWalkReses { + env: env, + ..(*self).clone() + } } /// Clear the memo table; important if you're re-evaluating the same term, /// but have changed the environment pub fn clear_memo(&self) { - self.parts.map(&mut |lwt: &Rc>| lwt.clear_memo()); + self.parts + .map(&mut |lwt: &Rc>| lwt.clear_memo()); } /// Switch to a different mode with the same `Elt` type. - pub fn switch_mode>(&self) - -> LazyWalkReses { + pub fn switch_mode>( + &self, + ) -> LazyWalkReses { let new_parts: EnvMBE>> = - self.parts.map( - &mut |part: &Rc>| - LazilyWalkedTerm::::new(&part.term)); + self.parts.map(&mut |part: &Rc>| { + LazilyWalkedTerm::::new(&part.term) + }); LazyWalkReses:: { parts: new_parts, env: self.env.clone(), @@ -626,12 +681,15 @@ impl LazyWalkReses { less_quoted_env: self.less_quoted_env.clone(), less_quoted_out_env: self.less_quoted_out_env.clone(), this_ast: self.this_ast.clone(), - extra_info: self.extra_info.clone() + extra_info: self.extra_info.clone(), } } pub fn quote_more(mut self, oeh: Option>) -> LazyWalkReses { - let env = self.more_quoted_env.pop().unwrap_or_else(Mode::Elt::core_env); + let env = self + .more_quoted_env + .pop() + .unwrap_or_else(Mode::Elt::core_env); let more_quoted_env = self.more_quoted_env; self.less_quoted_env.push(self.env); let less_quoted_env = self.less_quoted_env; @@ -639,22 +697,35 @@ impl LazyWalkReses { self.less_quoted_out_env.push(oeh); let less_quoted_out_env = self.less_quoted_out_env; - LazyWalkReses { env, more_quoted_env, less_quoted_env, less_quoted_out_env, .. self } + LazyWalkReses { + env, + more_quoted_env, + less_quoted_env, + less_quoted_out_env, + ..self + } } /// Shift to a less-quoted level. If the OEH is non-`None`, you need to call `squirrel_away`. - pub fn quote_less(mut self) -> (Option>, LazyWalkReses){ - let env = self.less_quoted_env.pop().unwrap_or_else(Mode::Elt::core_env); + pub fn quote_less(mut self) -> (Option>, LazyWalkReses) { + let env = self + .less_quoted_env + .pop() + .unwrap_or_else(Mode::Elt::core_env); let less_quoted_env = self.less_quoted_env; - let out_env : Option> = self.less_quoted_out_env.pop().unwrap(); + let out_env: Option> = self.less_quoted_out_env.pop().unwrap(); let less_quoted_out_env = self.less_quoted_out_env; self.more_quoted_env.push(self.env); let more_quoted_env = self.more_quoted_env; let res = LazyWalkReses { - env, less_quoted_env, more_quoted_env, less_quoted_out_env, .. self + env, + less_quoted_env, + more_quoted_env, + less_quoted_out_env, + ..self }; (out_env, res) @@ -664,10 +735,13 @@ impl LazyWalkReses { * Keeps the same environment. */ pub fn march_parts(&self, driving_names: &[Name]) -> Vec> { - let marched = self.parts.march_all(driving_names); + let marched = self.parts.march_all(driving_names); let mut res = vec![]; for marched_parts in marched { - res.push(LazyWalkReses{ parts: marched_parts, .. self.clone() }); + res.push(LazyWalkReses { + parts: marched_parts, + ..self.clone() + }); } res } @@ -684,32 +758,48 @@ impl LazyWalkReses { /** Combines `march_parts` and `with_context`. `new_contexts` should have the same length * as the repetition marched. */ - pub fn march_parts_with(&self, driving_names: &[Name], new_contexts: Vec) - -> Option>> { - let marched = self.parts.march_all(driving_names); - if marched.len() != new_contexts.len() { return None; } + pub fn march_parts_with( + &self, + driving_names: &[Name], + new_contexts: Vec, + ) -> Option>> { + let marched = self.parts.march_all(driving_names); + if marched.len() != new_contexts.len() { + return None; + } let mut res = vec![]; for (marched_parts, ctx) in marched.into_iter().zip(new_contexts.into_iter()) { - res.push(LazyWalkReses{env: self.env.set(negative_ret_val(), ctx), - parts: marched_parts, .. self.clone()}); + res.push(LazyWalkReses { + env: self.env.set(negative_ret_val(), ctx), + parts: marched_parts, + ..self.clone() + }); } Some(res) } pub fn map_terms(self, f: &mut F) -> Result, E> - where F: FnMut(Name, &Ast) -> Result { + where + F: FnMut(Name, &Ast) -> Result, + { use std::clone::Clone; Ok(LazyWalkReses { - parts: self.parts.named_map( - &mut |n: &Name, lwt: &Rc>| - Ok(LazilyWalkedTerm::new(&f(*n, &lwt.term)?))).lift_result()?, - .. self + parts: self + .parts + .named_map(&mut |n: &Name, lwt: &Rc>| { + Ok(LazilyWalkedTerm::new(&f(*n, &lwt.term)?)) + }) + .lift_result()?, + ..self }) } /** Like `get_rep_res`, but with a different context for each repetition */ - pub fn get_rep_res_with(&self, n: Name, new_contexts: Vec) - -> Result::Out>, Mode::Err> { + pub fn get_rep_res_with( + &self, + n: Name, + new_contexts: Vec, + ) -> Result::Out>, Mode::Err> { if let Some(sub_parts) = self.march_parts_with(&[n], new_contexts) { //Some(sub_parts.iter().map(|sp| sp.get_res(n)).collect()) let mut res = vec![]; @@ -730,12 +820,13 @@ fn quote_more_and_less() { assoc_n!("a" => ty!({"Type" "Nat" :})), // we'll pretend this is under an unquote or something: &mbe!("body" => "bind_me"), - ast!("[ignored]")); + ast!("[ignored]"), + ); let parts = parts.with_context(ty!({"Type" "Int" :})); - let interpolation_accumulator = Rc::new(::std::cell::RefCell::new( - Assoc::::new())); + let interpolation_accumulator = + Rc::new(::std::cell::RefCell::new(Assoc::::new())); assert_eq!(parts.env.find(&n("a")), Some(&ty!({"Type" "Nat" :}))); @@ -751,8 +842,9 @@ fn quote_more_and_less() { // the other thing `unquote` needs to do; save the result for out-of-band retrieval squirrel_away::<::ty::UnpackTy>(squirreler, res); - - // check that we successfully squirreled it away: - assert_eq!(*interpolation_accumulator.borrow(), assoc_n!("bind_me" => ty!({"Type" "Int" :}))); + assert_eq!( + *interpolation_accumulator.borrow(), + assoc_n!("bind_me" => ty!({"Type" "Int" :})) + ); } diff --git a/src/beta.rs b/src/beta.rs index a0fd9ed..ab5a019 100644 --- a/src/beta.rs +++ b/src/beta.rs @@ -1,14 +1,13 @@ #![macro_use] -use std::fmt; +use alpha::Ren; +use ast::{Ast, Atom, ExtendEnv, VariableReference}; +use ast_walk::{LazilyWalkedTerm, LazyWalkReses}; use name::*; -use ast_walk::{LazyWalkReses, LazilyWalkedTerm}; +use std::fmt; use util::assoc::Assoc; -use ast::{Ast,Atom,VariableReference,ExtendEnv}; use util::mbe::EnvMBE; use walk_mode::Dir; -use alpha::Ren; - custom_derive! { /** @@ -62,21 +61,15 @@ pub use self::Beta::*; impl fmt::Debug for Beta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Nothing => { write!(f, "∅") }, - Shadow(ref lhs, ref rhs) => { write!(f, "({:#?} ▷ {:#?})", lhs, rhs) }, + Nothing => write!(f, "∅"), + Shadow(ref lhs, ref rhs) => write!(f, "({:#?} ▷ {:#?})", lhs, rhs), ShadowAll(ref sub_beta, ref drivers) => { write!(f, "( {:#?} ▷ ... by {:#?})", sub_beta, drivers) } - Basic(ref name, ref ty) => { write!(f, "{:#?}:{:#?}", name, ty) } - SameAs(ref name, ref ty_source) => { - write!(f, "{:#?}={:#?}", name, ty_source) - } - Underspecified(ref name) => { - write!(f, "∀{:#?}", name) - } - Protected(ref name) => { - write!(f, "↫{:#?}", name) - } + Basic(ref name, ref ty) => write!(f, "{:#?}:{:#?}", name, ty), + SameAs(ref name, ref ty_source) => write!(f, "{:#?}={:#?}", name, ty_source), + Underspecified(ref name) => write!(f, "∀{:#?}", name), + Protected(ref name) => write!(f, "↫{:#?}", name), } } } @@ -84,71 +77,75 @@ impl fmt::Debug for Beta { impl Beta { pub fn names_mentioned(&self) -> Vec { match *self { - Nothing => { vec![] } + Nothing => vec![], Shadow(ref lhs, ref rhs) => { let mut res = lhs.names_mentioned(); let mut r_res = rhs.names_mentioned(); res.append(&mut r_res); res } - ShadowAll(_, ref drivers) => { drivers.clone() } - Basic(n, v) => { vec![n, v] } - SameAs(n, v_source) => { vec![n, v_source] } - Underspecified(n) => { vec![n] } - Protected(n) => { vec![n] } + ShadowAll(_, ref drivers) => drivers.clone(), + Basic(n, v) => vec![n, v], + SameAs(n, v_source) => vec![n, v_source], + Underspecified(n) => vec![n], + Protected(n) => vec![n], } } // `Protected` doens't actually bind, so we shouldn't rename under it! pub fn names_mentioned_and_bound(&self) -> Vec { match *self { - Nothing | Protected(_) => { vec![] } + Nothing | Protected(_) => vec![], Shadow(ref lhs, ref rhs) => { let mut res = lhs.names_mentioned_and_bound(); let mut r_res = rhs.names_mentioned_and_bound(); res.append(&mut r_res); res } - ShadowAll(ref sub, _) => { sub.names_mentioned_and_bound() } // drivers is too broad! - Basic(n, v) => { vec![n, v] } - SameAs(n, v_source) => { vec![n, v_source] } - Underspecified(n) => { vec![n] } + ShadowAll(ref sub, _) => sub.names_mentioned_and_bound(), // drivers is too broad! + Basic(n, v) => vec![n, v], + SameAs(n, v_source) => vec![n, v_source], + Underspecified(n) => vec![n], } } // alpha::freshen_binders wants this to extract from complex payloads, hence `f` pub fn extract_from_mbe( - &self, parts: &EnvMBE, f: &dyn Fn(&T) -> &Ren) -> Ren { + &self, + parts: &EnvMBE, + f: &dyn Fn(&T) -> &Ren, + ) -> Ren { match *self { - Nothing => { Ren::new() } - Shadow(ref lhs, ref rhs) => { - lhs.extract_from_mbe(parts, f).set_assoc(&rhs.extract_from_mbe(parts, f)) - } + Nothing => Ren::new(), + Shadow(ref lhs, ref rhs) => lhs + .extract_from_mbe(parts, f) + .set_assoc(&rhs.extract_from_mbe(parts, f)), ShadowAll(ref sub_beta, ref drivers) => { let mut res = Ren::new(); - for parts in parts.march_all(drivers) { // Maybe `march_all` should memoize? + for parts in parts.march_all(drivers) { + // Maybe `march_all` should memoize? res = res.set_assoc(&sub_beta.extract_from_mbe(&parts, f)); } res } - Basic(n_s, _) | SameAs(n_s, _) | Underspecified(n_s) | Protected(n_s)=> { + Basic(n_s, _) | SameAs(n_s, _) | Underspecified(n_s) | Protected(n_s) => { f(parts.get_leaf_or_panic(&n_s)).clone() } } } - - } /// Find the environment represented by `b`. /// `SameAs` and `Basic` nodes will cause walking in `Mode`, which should be positive. /// TODO: Unfortunately, this means that they don't work well in the subtyping walk, for instance. -pub fn env_from_beta(b: &Beta, parts: &LazyWalkReses) - -> Result, Mode::Err> { +pub fn env_from_beta( + b: &Beta, + parts: &LazyWalkReses, +) -> Result, Mode::Err> { // TODO: figure out why we *do* get called (during subtyping, apparently) //if !Mode::D::is_positive() { panic!("ICE: e_f_b on {:#?} in {} (negative)", b, Mode::name())} match *b { - Nothing => { Ok(Assoc::new()) } + Nothing => Ok(Assoc::new()), Shadow(ref lhs, ref rhs) => { Ok(env_from_beta::(&*lhs, parts)? .set_assoc(&env_from_beta::(&*rhs, parts)?)) @@ -161,16 +158,21 @@ pub fn env_from_beta(b: &Beta, parts: &LazyWalkRese Ok(res) } Basic(name_source, ty_source) => { - if let LazilyWalkedTerm {term: Atom(ref name), ..} - = **parts.parts.get_leaf_or_panic(&name_source) { + if let LazilyWalkedTerm { + term: Atom(ref name), + .. + } = **parts.parts.get_leaf_or_panic(&name_source) + { //let LazilyWalkedTerm {term: ref ty_stx, ..} // = **parts.parts.get_leaf_or_panic(ty_source); let ty = parts.get_res(ty_source)?; Ok(Assoc::new().set(*name, Mode::out_as_elt(ty.clone()))) } else { - panic!("User error: {:#?} is supposed to supply names, but is not an Atom.", - parts.parts.get_leaf_or_panic(&name_source).term) + panic!( + "User error: {:#?} is supposed to supply names, but is not an Atom.", + parts.parts.get_leaf_or_panic(&name_source).term + ) } } @@ -183,9 +185,11 @@ pub fn env_from_beta(b: &Beta, parts: &LazyWalkRese // Do the actual work: let res = Mode::Negated::out_as_env( - parts.switch_mode::() + parts + .switch_mode::() .with_context(Mode::out_as_elt(ctxt)) - .get_res(name_source)?); + .get_res(name_source)?, + ); // ... and then check that it's the right set of names! // Somewhat awkward (but not unsound!) run-time error in the case that @@ -202,14 +206,19 @@ pub fn env_from_beta(b: &Beta, parts: &LazyWalkRese let mut count = 0; for (k, _) in res.iter_pairs() { if !expected_res_keys.contains(k) { - panic!("{} was exported (we only wanted {:#?} via {:#?})", - k, expected_res_keys, parts.get_term(res_source)); + panic!( + "{} was exported (we only wanted {:#?} via {:#?})", + k, + expected_res_keys, + parts.get_term(res_source) + ); // TODO: make this an `Err`. And test it with ill-formed `Form`s } count += 1; } - if count != expected_res_keys.len() { // TODO: Likewise: + if count != expected_res_keys.len() { + // TODO: Likewise: panic!("expected {:?} exports, got {}", expected_res_keys, count) } } @@ -218,29 +227,41 @@ pub fn env_from_beta(b: &Beta, parts: &LazyWalkRese } Underspecified(ref name_source) => { - if let LazilyWalkedTerm {term: Atom(ref name), ..} - = **parts.parts.get_leaf_or_panic(name_source) { + if let LazilyWalkedTerm { + term: Atom(ref name), + .. + } = **parts.parts.get_leaf_or_panic(name_source) + { Ok(Assoc::new().set(*name, Mode::underspecified(*name))) } else { - panic!("{:#?} is supposed to supply names, but is not an Atom.", - parts.parts.get_leaf_or_panic(name_source).term) + panic!( + "{:#?} is supposed to supply names, but is not an Atom.", + parts.parts.get_leaf_or_panic(name_source).term + ) } } Protected(ref name_source) => { // Since protection isn't binding, it gets variable references instead - if let LazilyWalkedTerm {term: ExtendEnv(ref boxed_vr,_), ..} - = **parts.parts.get_leaf_or_panic(name_source) { + if let LazilyWalkedTerm { + term: ExtendEnv(ref boxed_vr, _), + .. + } = **parts.parts.get_leaf_or_panic(name_source) + { use walk_mode::WalkElt; // HACK: rely on the fact that `walk_var` // won't recursively substitute until it "hits bottom" // Drop the variable reference right into the environment. - Ok(Assoc::new().set(::core_forms::vr_to_name(&*boxed_vr), - Mode::Elt::from_ast(&*boxed_vr))) + Ok(Assoc::new().set( + ::core_forms::vr_to_name(&*boxed_vr), + Mode::Elt::from_ast(&*boxed_vr), + )) } else { - panic!("{:#?} is supposed to supply names, but is not an EE(VR()).", - parts.parts.get_leaf_or_panic(name_source).term) + panic!( + "{:#?} is supposed to supply names, but is not an EE(VR()).", + parts.parts.get_leaf_or_panic(name_source).term + ) } } } @@ -265,12 +286,12 @@ custom_derive! { impl fmt::Debug for ExportBeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ExportBeta::Nothing => { write!(f, "∅") }, - ExportBeta::Shadow(ref lhs, ref rhs) => { write!(f, "({:#?} ▷ {:#?})", lhs, rhs) }, + ExportBeta::Nothing => write!(f, "∅"), + ExportBeta::Shadow(ref lhs, ref rhs) => write!(f, "({:#?} ▷ {:#?})", lhs, rhs), ExportBeta::ShadowAll(ref sub_beta, ref drivers) => { write!(f, "( {:#?} ▷ ... by {:#?})", sub_beta, drivers) } - ExportBeta::Use(ref name) => { write!(f, "{:#?}", name) } + ExportBeta::Use(ref name) => write!(f, "{:#?}", name), } } } @@ -278,37 +299,39 @@ impl fmt::Debug for ExportBeta { impl ExportBeta { pub fn names_mentioned(&self) -> Vec { match *self { - ExportBeta::Nothing => { vec![] } + ExportBeta::Nothing => vec![], ExportBeta::Shadow(ref lhs, ref rhs) => { let mut res = lhs.names_mentioned(); let mut r_res = rhs.names_mentioned(); res.append(&mut r_res); res } - ExportBeta::ShadowAll(_, ref drivers) => { drivers.clone() } - ExportBeta::Use(n) => { vec![n] } + ExportBeta::ShadowAll(_, ref drivers) => drivers.clone(), + ExportBeta::Use(n) => vec![n], } } // This has an overly-specific type to match implementation details of alpha::freshen_binders. // Not sure if we need a generalization, though. pub fn extract_from_mbe( - &self, parts: &EnvMBE, f: &dyn Fn(&T) -> &Ren) -> Ren { + &self, + parts: &EnvMBE, + f: &dyn Fn(&T) -> &Ren, + ) -> Ren { match *self { - ExportBeta::Nothing => { Ren::new() } - ExportBeta::Shadow(ref lhs, ref rhs) => { - lhs.extract_from_mbe(parts, f).set_assoc(&rhs.extract_from_mbe(parts, f)) - } + ExportBeta::Nothing => Ren::new(), + ExportBeta::Shadow(ref lhs, ref rhs) => lhs + .extract_from_mbe(parts, f) + .set_assoc(&rhs.extract_from_mbe(parts, f)), ExportBeta::ShadowAll(ref sub_beta, ref drivers) => { let mut res = Ren::new(); - for parts in parts.march_all(drivers) { // Maybe `march_all` should memoize? + for parts in parts.march_all(drivers) { + // Maybe `march_all` should memoize? res = res.set_assoc(&sub_beta.extract_from_mbe(&parts, f)); } res } - ExportBeta::Use(n_s) => { - f(parts.get_leaf_or_panic(&n_s)).clone() - } + ExportBeta::Use(n_s) => f(parts.get_leaf_or_panic(&n_s)).clone(), } } } @@ -323,17 +346,19 @@ fn names_exported_by(ast: &Ast, quote_depth: i16) -> Vec { if quote_depth <= 0 { bound_from_export_beta(export, sub_parts, quote_depth) } else { - sub_parts.map_reduce(&|a: &Ast| names_exported_by(a, quote_depth), - &|v1, v2| v1.clone().tap(|v1| v1.append(&mut v2.clone())), - vec![]) + sub_parts.map_reduce( + &|a: &Ast| names_exported_by(a, quote_depth), + &|v1, v2| v1.clone().tap(|v1| v1.append(&mut v2.clone())), + vec![], + ) } } - Ast::QuoteMore(ref body, _) => names_exported_by(body, quote_depth+1), - Ast::QuoteLess(ref body, _) => names_exported_by(body, quote_depth-1), + Ast::QuoteMore(ref body, _) => names_exported_by(body, quote_depth + 1), + Ast::QuoteLess(ref body, _) => names_exported_by(body, quote_depth - 1), ref ast if quote_depth <= 0 => { panic!("ICE: beta SameAs refers to an invalid AST node: {}", ast) } - _ => { vec![] } + _ => vec![], } } @@ -341,7 +366,7 @@ fn names_exported_by(ast: &Ast, quote_depth: i16) -> Vec { // It's a runtime error if the definition of a form causes `env_from_beta` to diverge from this. pub fn bound_from_beta(b: &Beta, parts: &EnvMBE<::ast::Ast>, quote_depth: i16) -> Vec { match *b { - Nothing => { vec![] } + Nothing => vec![], Shadow(ref lhs, ref rhs) => { let mut res = bound_from_beta(&*lhs, parts, quote_depth); let mut res_r = bound_from_beta(&*rhs, parts, quote_depth); @@ -355,10 +380,11 @@ pub fn bound_from_beta(b: &Beta, parts: &EnvMBE<::ast::Ast>, quote_depth: i16) - } res } - SameAs(ref n_s, _) => { // Can be a non-atom + SameAs(ref n_s, _) => { + // Can be a non-atom names_exported_by(parts.get_leaf_or_panic(n_s), quote_depth) } - Protected(ref _n_s) => { vec![] } // Non-binding + Protected(ref _n_s) => vec![], // Non-binding Basic(ref n_s, _) | Underspecified(ref n_s) => { vec![::core_forms::ast_to_name(parts.get_leaf_or_panic(n_s))] } @@ -366,10 +392,13 @@ pub fn bound_from_beta(b: &Beta, parts: &EnvMBE<::ast::Ast>, quote_depth: i16) - } // Like just taking the keys from `env_from_export_beta`, but faster and non-failing -pub fn bound_from_export_beta(b: &ExportBeta, parts: &EnvMBE<::ast::Ast>, quote_depth: i16) - -> Vec { +pub fn bound_from_export_beta( + b: &ExportBeta, + parts: &EnvMBE<::ast::Ast>, + quote_depth: i16, +) -> Vec { match *b { - ExportBeta::Nothing => { vec![] } + ExportBeta::Nothing => vec![], ExportBeta::Shadow(ref lhs, ref rhs) => { let mut res = bound_from_export_beta(&*lhs, parts, quote_depth); let mut res_r = bound_from_export_beta(&*rhs, parts, quote_depth); @@ -379,32 +408,36 @@ pub fn bound_from_export_beta(b: &ExportBeta, parts: &EnvMBE<::ast::Ast>, quote_ ExportBeta::ShadowAll(ref sub_beta, ref drivers) => { let mut res = vec![]; for ref sub_parts in parts.march_all(drivers) { - res.append(&mut bound_from_export_beta(&*sub_beta, sub_parts, quote_depth)); + res.append(&mut bound_from_export_beta( + &*sub_beta, + sub_parts, + quote_depth, + )); } res } - ExportBeta::Use(ref n_s) => { // Can be a non-atom + ExportBeta::Use(ref n_s) => { + // Can be a non-atom names_exported_by(parts.get_leaf_or_panic(n_s), quote_depth) } } } - // TODO NOW: make this return the atom-freshened node (possibly freshening recursive nodes) // We keep a table, keyed on leaf names and actual atoms, to keep track of the freshening. // This means that shadowing in leaf-named atom set doesn't get separated. // (e.g. `.[a : Int a : Int . ⋯].` freshens to `.[a🍅 : Int a🍅 : Int . ⋯].`). // As long as betas can't select a different shadowing direction, this isn't a problem. -pub fn freshening_from_beta(b: &Beta, parts: &EnvMBE<::ast::Ast>, - memo: &mut ::std::collections::HashMap<(Name, Name), Name>) - -> Assoc { +pub fn freshening_from_beta( + b: &Beta, + parts: &EnvMBE<::ast::Ast>, + memo: &mut ::std::collections::HashMap<(Name, Name), Name>, +) -> Assoc { match *b { - Nothing => { Assoc::new() } - Shadow(ref lhs, ref rhs) => { - freshening_from_beta(&*lhs, parts, memo) - .set_assoc(&freshening_from_beta(&*rhs, parts, memo)) - } + Nothing => Assoc::new(), + Shadow(ref lhs, ref rhs) => freshening_from_beta(&*lhs, parts, memo) + .set_assoc(&freshening_from_beta(&*rhs, parts, memo)), ShadowAll(ref sub_beta, ref drivers) => { let mut res = Assoc::new(); for parts in parts.march_all(drivers) { @@ -412,12 +445,18 @@ pub fn freshening_from_beta(b: &Beta, parts: &EnvMBE<::ast::Ast>, } res } - Protected(_n_s) => { unimplemented!("Not hard, just not used yet")} + Protected(_n_s) => unimplemented!("Not hard, just not used yet"), Basic(n_s, _) | SameAs(n_s, _) | Underspecified(n_s) => { let this_name = ::core_forms::ast_to_name(parts.get_leaf_or_panic(&n_s)); - Assoc::new().set(this_name, ::ast::VariableReference(*memo.entry((n_s, this_name)) - .or_insert_with(||{ this_name.freshen() }))) + Assoc::new().set( + this_name, + ::ast::VariableReference( + *memo + .entry((n_s, this_name)) + .or_insert_with(|| this_name.freshen()), + ), + ) } } } @@ -431,4 +470,3 @@ pub fn freshening_from_beta(b: &Beta, parts: &EnvMBE<::ast::Ast>, // fn beta_with_negative_quasiquote() { // // } - diff --git a/src/core_forms.rs b/src/core_forms.rs index 288e722..9853a41 100644 --- a/src/core_forms.rs +++ b/src/core_forms.rs @@ -1,18 +1,17 @@ // This virtual machine kills cyber-fascists. - -use name::*; -use grammar::{SynEnv, FormPat}; -use grammar::FormPat::*; -use form::Form; -use util::assoc::Assoc; use ast::*; -use std::rc::Rc; -use ty::*; -use runtime::eval::*; use ast_walk::WalkRule::*; +use core_type_forms::*; +use form::Form; +use grammar::FormPat::*; +use grammar::{FormPat, SynEnv}; +use name::*; use num::bigint::ToBigInt; -use core_type_forms::*; // type forms are kinda bulky +use runtime::eval::*; +use std::rc::Rc; +use ty::*; +use util::assoc::Assoc; // type forms are kinda bulky // Core forms! // @@ -20,19 +19,27 @@ use core_type_forms::*; // type forms are kinda bulky // // Unseemly programs have expressions and types (and probably kinds, too). - pub fn ast_to_name(ast: &Ast) -> Name { - match *ast { Atom(n) => n, _ => { panic!("ICE: {:#?} is not an atom", ast) } } + match *ast { + Atom(n) => n, + _ => panic!("ICE: {:#?} is not an atom", ast), + } } pub fn vr_to_name(ast: &Ast) -> Name { - match *ast { VariableReference(n) => n, _ => { panic!("ICE: {:#?} is not a vr", ast) } } + match *ast { + VariableReference(n) => n, + _ => panic!("ICE: {:#?} is not a vr", ast), + } } /// Remove an `ExtendEnv` without respecting its binding behavior. /// This is safe if directly inside a `Node` that was just freshened. /// (TODO: think about what "just" means here. It's super-subtle!) pub fn strip_ee(a: &Ast) -> &Ast { - match *a { ExtendEnv(ref body, _) => (&**body), _ => panic!("ICE: malformed thing") } + match *a { + ExtendEnv(ref body, _) => (&**body), + _ => panic!("ICE: malformed thing"), + } } /// This is the Unseemly language. @@ -54,80 +61,79 @@ pub fn make_core_syn_env() -> SynEnv { // Unseemly expressions let main_expr_forms = forms_to_form_pat![ typed_form!("lambda", - /* syntax */ /* TODO: add comma separators to the syntax! */ - (delim ".[", "[", [ - (star [(named "param", aat), (lit ":"), - (named "p_t", (call "Type"))]), (lit "."), - (named "body", - (import [* ["param" : "p_t"]], (call "Expr")))]), - /* type */ - cust_rc_box!( move | part_types | { - let lambda_type : Ty = - ty!({ find_type(&ctf_0, "fn") ; - "param" => [* part_types =>("param") part_types : - (, part_types.get_res(n("p_t"))?.concrete() )], - "ret" => (, part_types.get_res(n("body"))?.concrete() )}); - Ok(lambda_type)}), - /* evaluation */ - cust_rc_box!( move | part_values | { - Ok(Function(Rc::new(Closure { - body: strip_ee(part_values.get_term_ref(n("body"))).clone(), - params: - part_values.get_rep_term(n("param")).iter().map(ast_to_name).collect(), - env: part_values.env - }))) - })), - + /* syntax */ /* TODO: add comma separators to the syntax! */ + (delim ".[", "[", [ + (star [(named "param", aat), (lit ":"), + (named "p_t", (call "Type"))]), (lit "."), + (named "body", + (import [* ["param" : "p_t"]], (call "Expr")))]), + /* type */ + cust_rc_box!( move | part_types | { + let lambda_type : Ty = + ty!({ find_type(&ctf_0, "fn") ; + "param" => [* part_types =>("param") part_types : + (, part_types.get_res(n("p_t"))?.concrete() )], + "ret" => (, part_types.get_res(n("body"))?.concrete() )}); + Ok(lambda_type)}), + /* evaluation */ + cust_rc_box!( move | part_values | { + Ok(Function(Rc::new(Closure { + body: strip_ee(part_values.get_term_ref(n("body"))).clone(), + params: + part_values.get_rep_term(n("param")).iter().map(ast_to_name).collect(), + env: part_values.env + }))) + })), typed_form!("apply", /* function application*/ - (delim "(", "(", [(named "rator", (call "Expr")), - (star (named "rand", (call "Expr")))]), - cust_rc_box!(move | part_types | { - use walk_mode::WalkMode; - let return_type = ::ty_compare::Subtype::underspecified(n("")); - - // The `rator` must be a function that takes the `rand`s as arguments: - let _ = ::ty_compare::must_subtype( - &ty!({ "Type" "fn" : - "param" => (,seq part_types.get_rep_res(n("rand"))? - .iter().map(|t| t.concrete()).collect::>() ), - "ret" => (, return_type.concrete() )}), - &part_types.get_res(n("rator"))?, - part_types.env.clone()) - .map_err(|e| ::util::err::sp(e, part_types.this_ast.clone()))?; - - - // TODO: write a test that exercises this (it's used in the prelude) - // What return type made that work? - ::ty_compare::unification.with(|unif| { - let res = ::ty_compare::resolve( - ::ast_walk::Clo{ it: return_type, env: part_types.env.clone()}, - &unif.borrow()); - - // Canonicalize the type in its environment: - let res = ::ty_compare::canonicalize(&res.it, res.env); - res.map_err(|e| ::util::err::sp(e, part_types.this_ast.clone())) - }) - }), - cust_rc_box!( move | part_values | { - match part_values.get_res(n("rator"))? { - Function(clos) => { - let mut new_env = clos.env.clone(); - for (p, v) in clos.params.iter().zip( - part_values.get_rep_res(n("rand"))?) { - new_env = new_env.set(*p, v); - } - - ::runtime::eval::eval(&clos.body, new_env) - }, - BuiltInFunction(::runtime::eval::BIF(f)) => { - Ok(f(part_values.get_rep_res(n("rand"))?)) - } - other => { - panic!("Type soundness bug: attempted to invoke {:#?} - as if it were a function", other) + (delim "(", "(", [(named "rator", (call "Expr")), + (star (named "rand", (call "Expr")))]), + cust_rc_box!(move | part_types | { + use walk_mode::WalkMode; + let return_type = ::ty_compare::Subtype::underspecified(n("")); + + // The `rator` must be a function that takes the `rand`s as arguments: + let _ = ::ty_compare::must_subtype( + &ty!({ "Type" "fn" : + "param" => (,seq part_types.get_rep_res(n("rand"))? + .iter().map(|t| t.concrete()).collect::>() ), + "ret" => (, return_type.concrete() )}), + &part_types.get_res(n("rator"))?, + part_types.env.clone()) + .map_err(|e| ::util::err::sp(e, part_types.this_ast.clone()))?; + + + // TODO: write a test that exercises this (it's used in the prelude) + // What return type made that work? + ::ty_compare::unification.with(|unif| { + let res = ::ty_compare::resolve( + ::ast_walk::Clo{ it: return_type, env: part_types.env.clone()}, + &unif.borrow()); + + // Canonicalize the type in its environment: + let res = ::ty_compare::canonicalize(&res.it, res.env); + res.map_err(|e| ::util::err::sp(e, part_types.this_ast.clone())) + }) + }), + cust_rc_box!( move | part_values | { + match part_values.get_res(n("rator"))? { + Function(clos) => { + let mut new_env = clos.env.clone(); + for (p, v) in clos.params.iter().zip( + part_values.get_rep_res(n("rand"))?) { + new_env = new_env.set(*p, v); } + + ::runtime::eval::eval(&clos.body, new_env) + }, + BuiltInFunction(::runtime::eval::BIF(f)) => { + Ok(f(part_values.get_rep_res(n("rand"))?)) } - })), + other => { + panic!("Type soundness bug: attempted to invoke {:#?} + as if it were a function", other) + } + } + })), typed_form!("match", [(lit "match"), (named "scrutinee", (call "Expr")), (delim "{", "{", @@ -172,70 +178,69 @@ pub fn make_core_syn_env() -> SynEnv { }) ), /* Note that we inconveniently require the user to specify the type. - "real" languages infer the type from the (required-to-be-unique) - component name. */ + "real" languages infer the type from the (required-to-be-unique) + component name. */ typed_form!("enum_expr", - [(delim "+[", "[", [(named "name", aat), - (star (named "component", (call "Expr")))]), - (lit ":"), (named "t", (call "Type"))], - /* Typesynth: */ - cust_rc_box!( move | part_types | { - let res : Ty = part_types.get_res(n("t"))?; - expect_ty_node!( (res ; find_type(&ctf_2, "enum") ; &part_types.this_ast) - enum_type_parts; - { - for enum_type_part in enum_type_parts.march_all(&[n("name")]) { - if &part_types.get_term(n("name")) - != enum_type_part.get_leaf_or_panic(&n("name")) { - continue; // not the right arm - } + [(delim "+[", "[", [(named "name", aat), + (star (named "component", (call "Expr")))]), + (lit ":"), (named "t", (call "Type"))], + /* Typesynth: */ + cust_rc_box!( move | part_types | { + let res : Ty = part_types.get_res(n("t"))?; + expect_ty_node!( (res ; find_type(&ctf_2, "enum") ; &part_types.this_ast) + enum_type_parts; + { + for enum_type_part in enum_type_parts.march_all(&[n("name")]) { + if &part_types.get_term(n("name")) + != enum_type_part.get_leaf_or_panic(&n("name")) { + continue; // not the right arm + } - let component_types : Vec = - enum_type_part.get_rep_leaf_or_panic(n("component")) - .iter().map(|a| Ty::new((*a).clone())).collect(); + let component_types : Vec = + enum_type_part.get_rep_leaf_or_panic(n("component")) + .iter().map(|a| Ty::new((*a).clone())).collect(); - // TODO: check that they're the same length! + // TODO: check that they're the same length! - for (t, expected_t) in part_types.get_rep_res(n("component"))? - .iter().zip(component_types) { - ty_exp!(t, &expected_t, part_types.this_ast); - } - return Ok(res.clone()); + for (t, expected_t) in part_types.get_rep_res(n("component"))? + .iter().zip(component_types) { + ty_exp!(t, &expected_t, part_types.this_ast); } - - ty_err!(NonexistentEnumArm - (ast_to_name(&part_types.get_term(n("name"))), res) - at part_types.this_ast); + return Ok(res.clone()); } - ) - }), - /* Evaluate: */ - cust_rc_box!( move | part_values | { - Ok(Enum(ast_to_name(&part_values.get_term(n("name"))), - part_values.get_rep_res(n("component"))?)) - })), - typed_form!("struct_expr", - (delim "*[", "[", - (star [(named "component_name", aat), (lit ":"), - (named "component", (call "Expr"))])), - cust_rc_box!( move | part_types | { - Ok(ty!({ find_type(&ctf_3, "struct") ; - "component_name" => (@"c" ,seq part_types.get_rep_term(n("component_name"))), - "component" => (@"c" ,seq part_types.get_rep_res(n("component"))? - .into_iter().map(|c : Ty| c.concrete())) - })) - }), - cust_rc_box!( move | part_values | { - let mut res = Assoc::new(); - for component_parts in part_values.march_parts(&[n("component")]) { - res = res.set(ast_to_name(&component_parts.get_term(n("component_name"))), - component_parts.get_res(n("component"))?); + ty_err!(NonexistentEnumArm + (ast_to_name(&part_types.get_term(n("name"))), res) + at part_types.this_ast); } + ) + }), + /* Evaluate: */ + cust_rc_box!( move | part_values | { + Ok(Enum(ast_to_name(&part_values.get_term(n("name"))), + part_values.get_rep_res(n("component"))?)) + })), + typed_form!("struct_expr", + (delim "*[", "[", + (star [(named "component_name", aat), (lit ":"), + (named "component", (call "Expr"))])), + cust_rc_box!( move | part_types | { + Ok(ty!({ find_type(&ctf_3, "struct") ; + "component_name" => (@"c" ,seq part_types.get_rep_term(n("component_name"))), + "component" => (@"c" ,seq part_types.get_rep_res(n("component"))? + .into_iter().map(|c : Ty| c.concrete())) + })) + }), + cust_rc_box!( move | part_values | { + let mut res = Assoc::new(); - Ok(Struct(res)) - })), + for component_parts in part_values.march_parts(&[n("component")]) { + res = res.set(ast_to_name(&component_parts.get_term(n("component_name"))), + component_parts.get_res(n("component"))?); + } + Ok(Struct(res)) + })), /* e.g. * let_type * pair = mu lhs rhs. {l: lhs, r: rhs} @@ -243,20 +248,19 @@ pub fn make_core_syn_env() -> SynEnv { * in ... */ typed_form!("let_type", - [(lit "let_type"), - (named "type_kind_stx", (anyways "*")), - (star [(named "type_name", aat), - (lit "="), - (named "type_def", (import [* ["type_name" = "type_def"]], (call "Type")))]), - (lit "in"), - (named "body", (import [* ["type_name" = "type_def"]], (call "Expr")))], - Body(n("body")), - // HACK: like `Body(n("body"))`, but ignoring the binding, since it's type-level. - // This feels like it ought to be better-handled by `beta`, or maybe a kind system. - cust_rc_box!( move | let_type_parts | { - eval(strip_ee(&let_type_parts.get_term(n("body"))), let_type_parts.env) - })), - + [(lit "let_type"), + (named "type_kind_stx", (anyways "*")), + (star [(named "type_name", aat), + (lit "="), + (named "type_def", (import [* ["type_name" = "type_def"]], (call "Type")))]), + (lit "in"), + (named "body", (import [* ["type_name" = "type_def"]], (call "Expr")))], + Body(n("body")), + // HACK: like `Body(n("body"))`, but ignoring the binding, since it's type-level. + // This feels like it ought to be better-handled by `beta`, or maybe a kind system. + cust_rc_box!( move | let_type_parts | { + eval(strip_ee(&let_type_parts.get_term(n("body"))), let_type_parts.env) + })), /* e.g. where List = ∀ X. μ List. enum { Nil(), Cons(X, List<[X]<) } * .[x : List <[X]< . match (unfold x) ... ]. * (unfold is needed because `match` wants an `enum`, not a `μ`) @@ -283,7 +287,6 @@ pub fn make_core_syn_env() -> SynEnv { }) }), Body(n("body"))), - /* e.g. where List = ∀ X. μ List. enum { Nil (), Cons (X, List<[X]<) } * (.[x : List <[X]< . ...]. (fold +[Nil]+) ) : List<[X]< */ @@ -308,7 +311,6 @@ pub fn make_core_syn_env() -> SynEnv { Ok(goal_type) }), Body(n("body"))), - typed_form!("forall_expr", [(lit "forall"), (star (named "param", aat)), (lit "."), (named "body", (import [* [forall "param"]], (call "Expr")))], @@ -320,11 +322,9 @@ pub fn make_core_syn_env() -> SynEnv { })) }), Body(n("body"))), - - ::core_qq_forms::quote(/*positive=*/true) + ::core_qq_forms::quote(/*positive=*/ true) ]; - let main_pat_forms = forms_to_form_pat_export![ negative_typed_form!("enum_pat", (delim "+[", "[", [(named "name", aat), @@ -446,8 +446,9 @@ pub fn make_core_syn_env() -> SynEnv { "Pat" => Rc::new(Biased(Rc::new(main_pat_forms), Rc::new(AnyAtomicToken))), "Expr" => Rc::new(Biased(Rc::new(main_expr_forms), Rc::new(VarRef))), "Ident" => Rc::new(AnyAtomicToken) - ).set_assoc(&ctf).set_assoc(&cmf) /* throw in the types and macros! */ - + ) + .set_assoc(&ctf) + .set_assoc(&cmf) /* throw in the types and macros! */ } /** @@ -455,7 +456,6 @@ pub fn make_core_syn_env() -> SynEnv { * In the "real world", programmers look up forms by syntax, using a parser. */ pub fn find_form(se: &SynEnv, nt: &str, form_name: &str) -> Rc
{ - fn find_form_rec(f: &FormPat, form_name: &str) -> Option> { match *f { Scope(ref f, _) => { @@ -468,13 +468,19 @@ pub fn find_form(se: &SynEnv, nt: &str, form_name: &str) -> Rc { Alt(ref vf) => { for f in vf { let res = find_form_rec(f, form_name); - if res.is_some() { return res; } + if res.is_some() { + return res; + } } None } Biased(ref lhs, ref rhs) => { let l_res = find_form_rec(lhs, form_name); - if l_res.is_some() { l_res } else { find_form_rec(rhs, form_name) } + if l_res.is_some() { + l_res + } else { + find_form_rec(rhs, form_name) + } } _ => None, } @@ -485,7 +491,6 @@ pub fn find_form(se: &SynEnv, nt: &str, form_name: &str) -> Rc { .unwrap_or_else(|| panic!(format!("{:#?} not found in {:#?}", form_name, pat))) } - // Inserts a new form into a grammar in the "sensible" place // (underneath any number of `Biased`s, as a new arm of an `Alt`). // Macros will usually want to do this to extend an existing NT. @@ -509,9 +514,9 @@ pub fn add_form_at_the_alt(outer: Rc, inner: &FormPat) -> Option { let mut my_subs: Vec> = subs.clone(); my_subs.push(Rc::new(inner.clone())); - return Some(Alt(my_subs)) + return Some(Alt(my_subs)); } - _ => None + _ => None, } } @@ -532,40 +537,43 @@ pub fn find(nt: &str, name: &str) -> Rc { } // Deprecated; use `::core_forms::find` instead (keep it qualified!) -pub fn find_core_form(nt: &str, name: &str) -> Rc { find(nt, name) } +pub fn find_core_form(nt: &str, name: &str) -> Rc { + find(nt, name) +} pub fn get_core_forms() -> SynEnv { core_forms.with(|cf| cf.clone()) } - - #[test] fn form_grammar() { let cse = make_core_syn_env(); - use read::*; use read::DelimChar::*; + use read::*; - assert_eq!(::grammar::parse(&form_pat!((call "Type")), - &cse.clone(), - &tokens!([""; "Ident" "->" "Ident"])), - Ok(ast!({ find_form(&cse, "Type", "fn"); + assert_eq!( + ::grammar::parse( + &form_pat!((call "Type")), + &cse.clone(), + &tokens!([""; "Ident" "->" "Ident"]) + ), + Ok(ast!({ find_form(&cse, "Type", "fn"); ["ret" => {find_form(&cse, "Type", "Ident") ; []}, - "param" => [{find_form(&cse, "Type", "Ident") ; []}]]}))); + "param" => [{find_form(&cse, "Type", "Ident") ; []}]]})) + ); } - #[test] fn form_expect_node() { let ast = ast!({ find_core_form("Expr", "apply"); ["rand" => [(vr "f")], "rator" => (vr "x")]}); let _: Result<(), ()> = expect_node!( - ( ast ; find_core_form("Expr", "apply")) env; //expect_f = "rand", expect_x = "rator"; - { - assert_eq!(env.get_rep_leaf_or_panic(n("rand")), vec![&ast!((vr "f"))]); - assert_eq!(env.get_leaf_or_panic(&n("rator")), &ast!((vr "x"))); - Ok(()) - }); + ( ast ; find_core_form("Expr", "apply")) env; //expect_f = "rand", expect_x = "rator"; + { + assert_eq!(env.get_rep_leaf_or_panic(n("rand")), vec![&ast!((vr "f"))]); + assert_eq!(env.get_leaf_or_panic(&n("rator")), &ast!((vr "x"))); + Ok(()) + }); } #[test] @@ -577,23 +585,31 @@ fn form_type() { let lam = find_core_form("Expr", "lambda"); let fun = find_core_form("Type", "fn"); + assert_eq!( + synth_type(&ast!( (vr "X") ), simple_ty_env.clone()), + Ok(ty!({ + find_core_form("Type", "Int"); + })) + ); - assert_eq!(synth_type(&ast!( (vr "X") ), simple_ty_env.clone()), - Ok(ty!( { find_core_form("Type", "Int") ; }))); - - assert_eq!(synth_type(&ast!( + assert_eq!( + synth_type( + &ast!( { lam.clone() ; "param" => [@"p" "y"], "p_t" => [@"p" { find_core_form("Type", "Nat") ; }], "body" => (import [* [ "param" : "p_t" ]] (vr "X"))}), - simple_ty_env.clone()), + simple_ty_env.clone() + ), Ok(ty!({ fun.clone() ; "param" => [{ find_core_form("Type", "Nat") ; }], - "ret" => { find_core_form("Type", "Int") ; }}))); + "ret" => { find_core_form("Type", "Int") ; }})) + ); } #[test] -fn type_apply_with_subtype() { // Application can perform subtyping +fn type_apply_with_subtype() { + // Application can perform subtyping let nat_ty = ty!({ "Type" "Nat" : }); @@ -609,16 +625,23 @@ fn type_apply_with_subtype() { // Application can perform subtyping "param" => [ (vr "T") ], "ret" => (vr "T") })})); - assert_eq!(synth_type(&ast!( + assert_eq!( + synth_type( + &ast!( { "Expr" "apply" : "rator" => (vr "nat_to_nat") , "rand" => [ (vr "N") ]}), - ty_env.clone()), - Ok(nat_ty.clone())); + ty_env.clone() + ), + Ok(nat_ty.clone()) + ); - assert_eq!(synth_type(&ast!( + assert_eq!( + synth_type( + &ast!( { "Expr" "apply" : "rator" => (vr "∀t_t_to_t") , "rand" => [ (vr "N") ]}), - ty_env.clone()), - Ok(nat_ty.clone())); - + ty_env.clone() + ), + Ok(nat_ty.clone()) + ); } #[test] @@ -627,35 +650,44 @@ fn form_eval() { "w" => val!(i 99), "b" => val!(b false)); - assert_eq!(eval(&ast!((vr "x")), simple_env.clone()), - Ok(Int(18.to_bigint().unwrap()))); + assert_eq!( + eval(&ast!((vr "x")), simple_env.clone()), + Ok(Int(18.to_bigint().unwrap())) + ); // (λy.w) x - assert_eq!(eval(&ast!( - { "Expr" "apply" : - "rator" => - { "Expr" "lambda" : - "param" => [@"p" "y"], - "p_t" => [@"p" "Int"], - "body" => (import [* [ "param" : "p_t" ]] (vr "w"))}, - "rand" => [(vr "x")] - }), - simple_env.clone()), - Ok(Int(99.to_bigint().unwrap()))); + assert_eq!( + eval( + &ast!( + { "Expr" "apply" : + "rator" => + { "Expr" "lambda" : + "param" => [@"p" "y"], + "p_t" => [@"p" "Int"], + "body" => (import [* [ "param" : "p_t" ]] (vr "w"))}, + "rand" => [(vr "x")] + }), + simple_env.clone() + ), + Ok(Int(99.to_bigint().unwrap())) + ); // (λy.y) x - assert_eq!(eval(&ast!( - { "Expr" "apply" : - "rator" => - { "Expr" "lambda" : - "param" => [@"p" "y"], - "p_t" => [@"p" "Int"], - "body" => (import [* [ "param" : "p_t" ]] (vr "y"))}, - "rand" => [(vr "x")] - }), - simple_env.clone()), - Ok(Int(18.to_bigint().unwrap()))); - + assert_eq!( + eval( + &ast!( + { "Expr" "apply" : + "rator" => + { "Expr" "lambda" : + "param" => [@"p" "y"], + "p_t" => [@"p" "Int"], + "body" => (import [* [ "param" : "p_t" ]] (vr "y"))}, + "rand" => [(vr "x")] + }), + simple_env.clone() + ), + Ok(Int(18.to_bigint().unwrap())) + ); } #[test] @@ -672,24 +704,33 @@ fn alg_type() { }); // Typecheck enum pattern - assert_eq!(neg_synth_type(&ast!( - { "Pat" "enum_pat" : - "name" => "Jefferson", - "component" => ["abc", "def"] - }), - mt_ty_env.set(negative_ret_val(), my_enum.clone())), - Ok(Assoc::new().set(n("abc"), ty!({"Type" "Int":})).set(n("def"), ty!({"Type" "Nat":})))); + assert_eq!( + neg_synth_type( + &ast!( + { "Pat" "enum_pat" : + "name" => "Jefferson", + "component" => ["abc", "def"] + }), + mt_ty_env.set(negative_ret_val(), my_enum.clone()) + ), + Ok(Assoc::new() + .set(n("abc"), ty!({"Type" "Int":})) + .set(n("def"), ty!({"Type" "Nat":}))) + ); // Typecheck enum expression - assert_eq!(synth_type(&ast!( - { "Expr" "enum_expr" : - "name" => "Jefferson", - "component" => [(vr "x"), (vr "n")], - "t" => (, my_enum.concrete() ) - }), - simple_ty_env.clone()), - Ok(my_enum.clone())); - + assert_eq!( + synth_type( + &ast!( + { "Expr" "enum_expr" : + "name" => "Jefferson", + "component" => [(vr "x"), (vr "n")], + "t" => (, my_enum.concrete() ) + }), + simple_ty_env.clone() + ), + Ok(my_enum.clone()) + ); let my_struct = ty!({ "Type" "struct" : "component_name" => [@"c" "x", "y"], @@ -697,76 +738,98 @@ fn alg_type() { }); // Typecheck struct pattern - assert_eq!(neg_synth_type(&ast!( + assert_eq!( + neg_synth_type( + &ast!( { "Pat" "struct_pat" : "component_name" => [@"c" "y", "x"], "component" => [@"c" "yy", "xx"] }), - mt_ty_env.set(negative_ret_val(), my_struct.clone())), - Ok(assoc_n!("yy" => ty!({"Type" "Float" :}), "xx" => ty!({"Type" "Int":})))); + mt_ty_env.set(negative_ret_val(), my_struct.clone()) + ), + Ok(assoc_n!("yy" => ty!({"Type" "Float" :}), "xx" => ty!({"Type" "Int":}))) + ); // Typecheck struct expression // TODO: currently {x: integer, y: float} ≠ {y: float, x: integer} // Implement proper type equality! - assert_eq!(synth_type(&ast!( + assert_eq!( + synth_type( + &ast!( { "Expr" "struct_expr" : "component_name" => [@"c" "x", "y"], "component" => [@"c" (vr "x"), (vr "f")] }), - simple_ty_env.clone()), - Ok(my_struct)); + simple_ty_env.clone() + ), + Ok(my_struct) + ); // Simple match... - assert_eq!(synth_type(&ast!({ "Expr" "match" : + assert_eq!( + synth_type( + &ast!({ "Expr" "match" : "scrutinee" => (vr "f"), "p" => [@"arm" "my_new_name", "unreachable"], "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "my_new_name")), (import ["p" = "scrutinee"] (vr "f"))] }), - simple_ty_env.clone()), - Ok(ty!({"Type" "Float" :}))); - - assert_m!(synth_type(&ast!({ "Expr" "match" : - "scrutinee" => (vr "n"), - "p" => [@"arm" "my_new_name", "unreachable"], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "my_new_name")), - (import ["p" = "scrutinee"] (vr "f"))] - }), - simple_ty_env.clone()), - ty_err_p!(Mismatch(_,_))); + simple_ty_env.clone() + ), + Ok(ty!({"Type" "Float" :})) + ); - assert_m!(synth_type(&ast!({ "Expr" "match" : - "scrutinee" => (vr "my_enum"), - "p" => [@"arm" { "Pat" "enum_pat" : - "name" => "Hamilton", "component" => ["ii"] // Never gonna be president... - }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "ii"))] - }), - simple_ty_env.set(n("my_enum"), my_enum.clone())), - ty_err_p!(NonexistentEnumArm(_,_)) + assert_m!( + synth_type( + &ast!({ "Expr" "match" : + "scrutinee" => (vr "n"), + "p" => [@"arm" "my_new_name", "unreachable"], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "my_new_name")), + (import ["p" = "scrutinee"] (vr "f"))] + }), + simple_ty_env.clone() + ), + ty_err_p!(Mismatch(_, _)) ); + assert_m!( + synth_type( + &ast!({ "Expr" "match" : + "scrutinee" => (vr "my_enum"), + "p" => [@"arm" { "Pat" "enum_pat" : + "name" => "Hamilton", "component" => ["ii"] // Never gonna be president... + }], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "ii"))] + }), + simple_ty_env.set(n("my_enum"), my_enum.clone()) + ), + ty_err_p!(NonexistentEnumArm(_, _)) + ); - assert_eq!(synth_type(&ast!({ "Expr" "match" : - "scrutinee" => (vr "my_enum"), - "p" => [@"arm" - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "Adams", "component" => ["ii"] - }, - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "Jefferson", "component" => ["ii", "bb"] - }, - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "Burr", "component" => ["xx", "yy"] - }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "ii")), - (import ["p" = "scrutinee"] (vr "ii")), - (import ["p" = "scrutinee"] (vr "x"))] - }), - simple_ty_env.set(n("my_enum"), my_enum.clone())), - Ok(ty!({"Type" "Int":}))); + assert_eq!( + synth_type( + &ast!({ "Expr" "match" : + "scrutinee" => (vr "my_enum"), + "p" => [@"arm" + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "Adams", "component" => ["ii"] + }, + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "Jefferson", "component" => ["ii", "bb"] + }, + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "Burr", "component" => ["xx", "yy"] + }], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "ii")), + (import ["p" = "scrutinee"] (vr "ii")), + (import ["p" = "scrutinee"] (vr "x"))] + }), + simple_ty_env.set(n("my_enum"), my_enum.clone()) + ), + Ok(ty!({"Type" "Int":})) + ); } #[test] @@ -777,21 +840,29 @@ fn alg_eval() { let simple_env = assoc_n!("x" => val!(i 18), "w" => val!(i 99), "b" => val!(b false)); // Evaluate enum pattern - assert_eq!(neg_eval(&ast!( - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "choice1", - "component" => ["abc", "def"] - }), - mt_env.set(negative_ret_val(), val!(enum "choice1", (i 9006), (b true)))), - Ok(assoc_n!("abc" => val!(i 9006), "def" => val!(b true)))); + assert_eq!( + neg_eval( + &ast!( + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "choice1", + "component" => ["abc", "def"] + }), + mt_env.set(negative_ret_val(), val!(enum "choice1", (i 9006), (b true))) + ), + Ok(assoc_n!("abc" => val!(i 9006), "def" => val!(b true))) + ); - assert_eq!(neg_eval(&ast!( - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "choice1", - "component" => ["abc", "def"] - }), - mt_env.set(negative_ret_val(), val!(enum "choice0", (i 12321)))), - Err(())); + assert_eq!( + neg_eval( + &ast!( + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "choice1", + "component" => ["abc", "def"] + }), + mt_env.set(negative_ret_val(), val!(enum "choice0", (i 12321))) + ), + Err(()) + ); // Evaluate enum expression @@ -805,62 +876,76 @@ fn alg_eval() { let enum_e = find_form(&cse, "Expr", "enum_expr"); let choice1_e = ast!( - { enum_e.clone() ; - "name" => "choice1", - "component" => [(vr "x"), (vr "b")], - "t" => (, my_enum_t.clone()) - }); + { enum_e.clone() ; + "name" => "choice1", + "component" => [(vr "x"), (vr "b")], + "t" => (, my_enum_t.clone()) + }); - assert_eq!(eval(&choice1_e, simple_env.clone()), - Ok(val!(enum "choice1", (i 18), (b false)))); + assert_eq!( + eval(&choice1_e, simple_env.clone()), + Ok(val!(enum "choice1", (i 18), (b false))) + ); // Evaluate struct pattern - assert_eq!(neg_eval(&ast!( - { "Pat" "struct_pat" => [* ["component"]] : - "component_name" => [@"c" "x", "y"], - "component" => [@"c" "xx", "yy"] - }), - mt_env.set(negative_ret_val(), - Struct(assoc_n!("x" => val!(i 0), "y" => val!(b true))))), - Ok(assoc_n!("xx" => val!(i 0), "yy" => val!(b true)))); + assert_eq!( + neg_eval( + &ast!( + { "Pat" "struct_pat" => [* ["component"]] : + "component_name" => [@"c" "x", "y"], + "component" => [@"c" "xx", "yy"] + }), + mt_env.set( + negative_ret_val(), + Struct(assoc_n!("x" => val!(i 0), "y" => val!(b true))) + ) + ), + Ok(assoc_n!("xx" => val!(i 0), "yy" => val!(b true))) + ); // Evaluate match - assert_eq!(eval(&ast!({ "Expr" "match" : - "scrutinee" => (vr "x"), - "p" => [@"arm" "my_new_name", "unreachable"], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "my_new_name")), - (import ["p" = "scrutinee"] (vr "x"))] - }), - simple_env.clone()), - Ok(val!(i 18))); - - assert_eq!(eval(&ast!({ "Expr" "match" : - "scrutinee" => (, choice1_e), - "p" => [@"arm" - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "choice2", "component" => ["xx", "yy"] - }, - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "choice1", "component" => ["ii", "bb"] - }, - { "Pat" "enum_pat" => [* ["component"]] : - "name" => "choice0", "component" => ["ii"] - }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "yy")), - (import ["p" = "scrutinee"] (vr "bb")), - (import ["p" = "scrutinee"] (vr "ii"))] - }), - simple_env.clone()), - Ok(val!(b false))); -} + assert_eq!( + eval( + &ast!({ "Expr" "match" : + "scrutinee" => (vr "x"), + "p" => [@"arm" "my_new_name", "unreachable"], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "my_new_name")), + (import ["p" = "scrutinee"] (vr "x"))] + }), + simple_env.clone() + ), + Ok(val!(i 18)) + ); + assert_eq!( + eval( + &ast!({ "Expr" "match" : + "scrutinee" => (, choice1_e), + "p" => [@"arm" + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "choice2", "component" => ["xx", "yy"] + }, + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "choice1", "component" => ["ii", "bb"] + }, + { "Pat" "enum_pat" => [* ["component"]] : + "name" => "choice0", "component" => ["ii"] + }], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "yy")), + (import ["p" = "scrutinee"] (vr "bb")), + (import ["p" = "scrutinee"] (vr "ii"))] + }), + simple_env.clone() + ), + Ok(val!(b false)) + ); +} #[test] fn recursive_types() { - let int_list_ty = - ty!( { "Type" "mu_type" : + let int_list_ty = ty!( { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "IntList"))], "body" => (import [* [prot "param"]] { "Type" "enum" : "name" => [@"c" "Nil", "Cons"], @@ -877,10 +962,13 @@ fn recursive_types() { ); // `IntList` shouldn't substitute - assert_eq!(synth_type(&ast!((vr "il_direct")), ty_env.clone()), Ok(int_list_ty.clone())); + assert_eq!( + synth_type(&ast!((vr "il_direct")), ty_env.clone()), + Ok(int_list_ty.clone()) + ); // I don't want these tests to depend on alpha-equivalence, so just disable freshening here. - without_freshening!{ + without_freshening! { // Test that unfolding a type produces one that's "twice as large", minus the outer mu assert_eq!(synth_type( &ast!({"Expr" "unfold" : "body" => (vr "il_direct")}), ty_env.clone()), @@ -928,18 +1016,20 @@ fn recursive_types() { }; // Test that missing an unfold fails - assert_m!(synth_type( + assert_m!( + synth_type( &ast!( { "Expr" "match" : - "scrutinee" => (vr "il_direct") , - "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : - "name" => "Cons", - "component" => ["car", "cdr"], - "t" => (vr "IntList") - }], - "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "car"))] - }), - ty_env.clone()), - ty_err_p!(UnableToDestructure(_,name_enum)), + "scrutinee" => (vr "il_direct") , + "p" => [@"arm" { "Pat" "enum_pat" => [* ["component"]] : + "name" => "Cons", + "component" => ["car", "cdr"], + "t" => (vr "IntList") + }], + "arm" => [@"arm" (import ["p" = "scrutinee"] (vr "car"))] + }), + ty_env.clone() + ), + ty_err_p!(UnableToDestructure(_, name_enum)), name_enum == n("enum") ); } @@ -947,23 +1037,28 @@ fn recursive_types() { #[test] fn use__let_type() { // Basic usage: - assert_eq!(synth_type(&ast!( { "Expr" "let_type" : - "type_name" => [@"t" "T"], - "type_def" => [@"t" { "Type" "Nat" :}], - "body" => (import [* ["type_name" = "type_def"]] { "Expr" "lambda" : - "param" => [@"p" "x"], - "p_t" => [@"p" (vr "T")], - "body" => (import [* ["param" : "p_t"]] (vr "x")) - }) - }), Assoc::new()), - Ok(ty!( { "Type" "fn" : "param" => [ {"Type" "Nat" :}], "ret" => {"Type" "Nat" :}}))); + assert_eq!( + synth_type( + &ast!( { "Expr" "let_type" : + "type_name" => [@"t" "T"], + "type_def" => [@"t" { "Type" "Nat" :}], + "body" => (import [* ["type_name" = "type_def"]] { "Expr" "lambda" : + "param" => [@"p" "x"], + "p_t" => [@"p" (vr "T")], + "body" => (import [* ["param" : "p_t"]] (vr "x")) + }) + }), + Assoc::new() + ), + Ok(ty!( { "Type" "fn" : "param" => [ {"Type" "Nat" :}], "ret" => {"Type" "Nat" :}})) + ); // useless type, but self-referential: - let trivial_mu_type = - ty!( { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "T"))], - "body" => (import [* [prot "param"]] (vr "T")) }).concrete(); + let trivial_mu_type = ty!( { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "T"))], + "body" => (import [* [prot "param"]] (vr "T")) }) + .concrete(); - without_freshening!{ + without_freshening! { // Recursive usage: assert_eq!(synth_type(&ast!( { "Expr" "let_type" : "type_name" => [@"t" "T"], @@ -980,16 +1075,21 @@ fn use__let_type() { } // Basic usage, evaluated: - assert_m!(eval(&ast!( { "Expr" "let_type" : - "type_name" => [@"t" "T"], - "type_def" => [@"t" { "Type" "Nat" :}], - "body" => (import [* ["type_name" = "type_def"]] { "Expr" "lambda" : - "param" => [@"p" "x"], - "p_t" => [@"p" (vr "T")], - "body" => (import [* ["param" : "p_t"]] (vr "x")) - }) - }), Assoc::new()), - Ok(_)); + assert_m!( + eval( + &ast!( { "Expr" "let_type" : + "type_name" => [@"t" "T"], + "type_def" => [@"t" { "Type" "Nat" :}], + "body" => (import [* ["type_name" = "type_def"]] { "Expr" "lambda" : + "param" => [@"p" "x"], + "p_t" => [@"p" (vr "T")], + "body" => (import [* ["param" : "p_t"]] (vr "x")) + }) + }), + Assoc::new() + ), + Ok(_) + ); } #[test] @@ -1003,5 +1103,6 @@ fn use_insert_form_pat() { assoc_n!("Pat" => Rc::new(form_pat!((impossible))), "Expr" => Rc::new(form_pat!( (biased (biased aat, aat), - (biased (alt (lit "a"), (lit "b"), (lit "c")), aat)))))); + (biased (alt (lit "a"), (lit "b"), (lit "c")), aat))))) + ); } diff --git a/src/core_macro_forms.rs b/src/core_macro_forms.rs index 8409fe6..4c4fe11 100644 --- a/src/core_macro_forms.rs +++ b/src/core_macro_forms.rs @@ -1,20 +1,20 @@ +use ast::{Ast, Atom, Node}; use ast_walk::LazyWalkReses; -use grammar::FormPat::*; +use ast_walk::WalkRule::{Custom, LiteralLike, NotWalked}; +use core_forms::ast_to_name; +use core_type_forms::{less_quoted_ty, more_quoted_ty}; +use form::EitherPN::Both; +use form::Form; use grammar::FormPat; +use grammar::FormPat::*; use grammar::SynEnv; -use std::rc::Rc; use name::*; -use form::Form; -use form::EitherPN::{Both}; -use ast::{Ast, Node, Atom}; -use ast_walk::WalkRule::{NotWalked, LiteralLike, Custom}; -use runtime::reify::Reifiable; use runtime::eval::Closure; +use runtime::reify::Reifiable; +use std::rc::Rc; +use ty::{SynthTy, Ty}; use util::assoc::Assoc; -use ty::{Ty, SynthTy}; use walk_mode::{WalkElt, WalkMode}; -use core_type_forms::{more_quoted_ty, less_quoted_ty}; -use core_forms::ast_to_name; // Macros! // @@ -106,11 +106,14 @@ macro_rules! syntax_syntax { // This means that we can just generate a type for them at the location of invocation. fn macro_type(forall_ty_vars: &[Name], arguments: Assoc, output: Ty) -> Ty { let mut components = vec![]; - for (k,v) in arguments.iter_pairs() { + for (k, v) in arguments.iter_pairs() { components.push(mbe!("component_name" => (, Atom(*k)), "component" => (, v.to_ast()))); } - let argument_struct = Node(::core_forms::find_core_form("Type", "struct"), - ::util::mbe::EnvMBE::new_from_anon_repeat(components), ::beta::ExportBeta::Nothing); + let argument_struct = Node( + ::core_forms::find_core_form("Type", "struct"), + ::util::mbe::EnvMBE::new_from_anon_repeat(components), + ::beta::ExportBeta::Nothing, + ); let mac_fn = ast!({"Type" "fn" : "param" => [(, argument_struct)], "ret" => (, output.to_ast()) @@ -126,9 +129,12 @@ fn macro_type(forall_ty_vars: &[Name], arguments: Assoc, output: Ty) - } } -fn type_macro_invocation(macro_name: Name, parts: &LazyWalkReses<::ty::SynthTy>, - expected_return: Ty, grammar: &FormPat) - -> Result, ::ty::TypeError> { +fn type_macro_invocation( + macro_name: Name, + parts: &LazyWalkReses<::ty::SynthTy>, + expected_return: Ty, + grammar: &FormPat, +) -> Result, ::ty::TypeError> { // Typecheck the subterms, and then quote them: let mut q_arguments = Assoc::new(); @@ -153,7 +159,9 @@ fn type_macro_invocation(macro_name: Name, parts: &LazyWalkReses<::ty::SynthTy>, let _ = ::ty_compare::must_subtype( ¯o_type(&[], q_arguments.clone(), expected_return), &SynthTy::walk_var(macro_name, &parts)?, - parts.env.clone()).map_err(|e| ::util::err::sp(e, parts.this_ast.clone()))?; + parts.env.clone(), + ) + .map_err(|e| ::util::err::sp(e, parts.this_ast.clone()))?; Ok(q_arguments) } @@ -166,7 +174,7 @@ fn macro_invocation(grammar: FormPat, macro_name: Name, export_names: Vec) let grammar1 = grammar.clone(); let grammar2 = grammar.clone(); Rc::new(Form { - name: n("macro_invocation"), // TODO: maybe generate a fresh name? + name: n("macro_invocation"), // TODO: maybe generate a fresh name? grammar: Rc::new(grammar.clone()), // For pretty-printing type_compare: ::form::Both(NotWalked, NotWalked), // Invoked at typechecking time. @@ -174,15 +182,19 @@ fn macro_invocation(grammar: FormPat, macro_name: Name, export_names: Vec) // ∀ T . [*[x : Nt <[T]< ⋯ ]* -> Nt <[T]<] // ... which you can imagine is the type of the implementation of the macro synth_type: ::form::Both( - cust_rc_box!( move |parts| { + cust_rc_box!(move |parts| { let return_type = ::ty_compare::Subtype::underspecified(n("")); let _ = type_macro_invocation(macro_name, &parts, return_type.clone(), &grammar1)?; // What return type made that work? let q_result = ::ty_compare::unification.with(|unif| { let resolved = ::ty_compare::resolve( - ::ast_walk::Clo{ it: return_type, env: parts.env.clone()}, - &unif.borrow()); + ::ast_walk::Clo { + it: return_type, + env: parts.env.clone(), + }, + &unif.borrow(), + ); // Canonicalize the type in its environment: let resolved = ::ty_compare::canonicalize(&resolved.it, resolved.env); @@ -191,42 +203,48 @@ fn macro_invocation(grammar: FormPat, macro_name: Name, export_names: Vec) less_quoted_ty(&q_result, Some(n("Expr")), &parts.this_ast) }), - cust_rc_box!( move |parts| { + cust_rc_box!(move |parts| { // From the macro's point of view, its parts are all positive; // they all produce (well, expand to), rather than consume, syntax. let parts_positive = parts.switch_mode::(); let expected_return_type = more_quoted_ty(parts.context_elt(), n("Pat")); - let arguments = type_macro_invocation(macro_name, &parts_positive, - expected_return_type, &grammar2)?; + let arguments = type_macro_invocation( + macro_name, + &parts_positive, + expected_return_type, + &grammar2, + )?; // What argument types made that work? - let mut res : Assoc = Assoc::new(); + let mut res: Assoc = Assoc::new(); ::ty_compare::unification.with(|unif| { for binder in &export_names { let ty = arguments.find_or_panic(binder); let binder_clo = ::ty_compare::resolve( - ::ast_walk::Clo{ - it: ty.clone(), - env: parts.env.clone() - }, - &unif.borrow()); + ::ast_walk::Clo { + it: ty.clone(), + env: parts.env.clone(), + }, + &unif.borrow(), + ); let binder_ty = ::ty_compare::canonicalize(&binder_clo.it, binder_clo.env) .map_err(|e| ::util::err::sp(e, parts.this_ast.clone()))?; - for (ty_n, ty) - in parts.with_context(binder_ty).get_res(*binder)?.iter_pairs() { - res = res.set(*ty_n, - less_quoted_ty(ty, Some(n("Pat")), &parts.this_ast)?); + for (ty_n, ty) in + parts.with_context(binder_ty).get_res(*binder)?.iter_pairs() + { + res = res + .set(*ty_n, less_quoted_ty(ty, Some(n("Pat")), &parts.this_ast)?); } } Ok(res) }) - }) + }), ), eval: ::form::Both(NotWalked, NotWalked), // Macros should be expanded first! - quasiquote: ::form::Both(LiteralLike, LiteralLike) + quasiquote: ::form::Both(LiteralLike, LiteralLike), }) } @@ -429,11 +447,11 @@ pub fn make_core_macro_forms() -> SynEnv { assoc_n!("Syntax" => Rc::new(grammar_grammar)) } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct ExpandMacros {} } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct UnusedNegativeExpandMacros {} } @@ -446,7 +464,10 @@ fn expand_macro(parts: ::ast_walk::LazyWalkReses) -> Result) -> Result &'static str { "MExpand" } + fn name() -> &'static str { + "MExpand" + } type Elt = Ast; type Negated = UnusedNegativeExpandMacros; - type Err = (); // TODO: should be the same as runtime errors + type Err = (); // TODO: should be the same as runtime errors type D = ::walk_mode::Positive; type ExtraInfo = (); @@ -470,41 +493,58 @@ impl WalkMode for ExpandMacros { LiteralLike } } - fn automatically_extend_env() -> bool { true } + fn automatically_extend_env() -> bool { + true + } } impl WalkMode for UnusedNegativeExpandMacros { - fn name() -> &'static str { "XXXXX" } + fn name() -> &'static str { + "XXXXX" + } type Elt = Ast; type Negated = ExpandMacros; type Err = (); type D = ::walk_mode::Positive; type ExtraInfo = (); - fn get_walk_rule(_: &Form) -> ::ast_walk::WalkRule { panic!("ICE") } - fn automatically_extend_env() -> bool { panic!("ICE") } + fn get_walk_rule(_: &Form) -> ::ast_walk::WalkRule { + panic!("ICE") + } + fn automatically_extend_env() -> bool { + panic!("ICE") + } } pub fn expand(ast: &Ast, env: Assoc) -> Result { ::ast_walk::walk::(ast, &LazyWalkReses::new_wrapper(env)) } - #[test] fn formpat_reflection() { - use ::runtime::eval::eval_top; use core_forms::find_form; + use runtime::eval::eval_top; let macro_forms = make_core_macro_forms(); assert_eq!( - FormPat::reflect(&eval_top( - &ast!({find_form(¯o_forms, "Syntax", "impossible"); })).unwrap()), - Impossible); + FormPat::reflect( + &eval_top(&ast!({ + find_form(¯o_forms, "Syntax", "impossible"); + })) + .unwrap() + ), + Impossible + ); assert_eq!( - FormPat::reflect(&eval_top( - &ast!({find_form(¯o_forms, "Syntax", "literal"); "body" => - {"Expr" "quote_expr" : "nt" => "Expr", "body" => (++ true "<--->")} - })).unwrap()), - Literal(n("<--->"))); + FormPat::reflect( + &eval_top( + &ast!({find_form(¯o_forms, "Syntax", "literal"); "body" => + {"Expr" "quote_expr" : "nt" => "Expr", "body" => (++ true "<--->")} + }) + ) + .unwrap() + ), + Literal(n("<--->")) + ); } #[test] @@ -526,9 +566,12 @@ fn macro_definitions() { "ty_annot" => {"Type" "Int" :} } } - }), env.clone()), + }), + env.clone() + ), Ok(assoc_n!("x" => ::runtime::reify::sequence_type__of( - &int_expr_type)))); + &int_expr_type))) + ); let t_expr_type = ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), "arg" => [(vr "T")] @@ -541,7 +584,8 @@ fn macro_definitions() { }); assert_eq!( - ::ty::neg_synth_type(&ast!( + ::ty::neg_synth_type( + &ast!( {"Syntax" "scope" : "param" => ["T", "S"], "syntax" => (import [* [forall "param"]] {"Syntax" "seq" => [* ["elt"]] : @@ -560,14 +604,16 @@ fn macro_definitions() { "macro_name" => "some_macro", "implementation" => (import [* [forall "param"]] (import ["syntax" = "unused_type"] (vr "ie"))) - }), env.clone()), + }), + env.clone() + ), Ok(assoc_n!( "some_macro" => macro_type(&vec![n("T"), n("S")], assoc_n!("body" => s_expr_type.clone(), "binding" => t_pat_type.clone(), "val" => t_expr_type.clone()), - int_expr_type.clone())))); - + int_expr_type.clone()))) + ); } #[test] @@ -579,23 +625,34 @@ fn macro_types() { let t_expr_type = ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), "arg" => [(vr "T")] }); - assert_eq!(macro_type(&vec![], assoc_n!("a" => int_expr_type.clone()), int_expr_type.clone()), - ty!({"Type" "fn" : + assert_eq!( + macro_type( + &vec![], + assoc_n!("a" => int_expr_type.clone()), + int_expr_type.clone() + ), + ty!({"Type" "fn" : "param" => [{"Type" "struct" : "component" => [@"c" (, int_expr_type.concrete())], "component_name" => [@"c" "a"] }], - "ret" => (, int_expr_type.concrete() )})); + "ret" => (, int_expr_type.concrete() )}) + ); assert_eq!( - macro_type(&vec![n("T")], assoc_n!("a" => t_expr_type.clone()), t_expr_type.clone()), - ty!({"Type" "forall_type" : + macro_type( + &vec![n("T")], + assoc_n!("a" => t_expr_type.clone()), + t_expr_type.clone() + ), + ty!({"Type" "forall_type" : "param" => ["T"], "body" => (import [* [forall "param"]] {"Type" "fn" : "param" => [{"Type" "struct" : "component" => [@"c" (, t_expr_type.concrete())], "component_name" => [@"c" "a"] }], - "ret" => (, t_expr_type.concrete() )})})); + "ret" => (, t_expr_type.concrete() )})}) + ); } #[test] @@ -649,8 +706,10 @@ fn type_basic_macro_invocation() { n("basic_int_macro"), vec![]) ; "a" => (vr "int_var") }), - env.clone()), - Ok(ty!({ "Type" "Int" :}))); + env.clone() + ), + Ok(ty!({ "Type" "Int" :})) + ); assert_eq!( ::ty::synth_type( @@ -660,8 +719,10 @@ fn type_basic_macro_invocation() { n("basic_t_macro"), vec![]) ; "a" => (vr "nat_var") }), - env.clone()), - Ok(ty!({ "Type" "Nat" :}))); + env.clone() + ), + Ok(ty!({ "Type" "Nat" :})) + ); assert_m!( ::ty::synth_type( @@ -671,8 +732,10 @@ fn type_basic_macro_invocation() { n("basic_int_macro"), vec![]) ; "a" => (vr "nat_var") }), - env.clone()), - Err(_)); + env.clone() + ), + Err(_) + ); assert_eq!( ::ty::neg_synth_type( @@ -682,8 +745,10 @@ fn type_basic_macro_invocation() { n("basic_pattern_macro"), vec![n("a")]) => ["a"]; "a" => "should_be_nat" }), - env.clone().set(negative_ret_val(), ty!({"Type" "Nat" :}))), - Ok(assoc_n!("should_be_nat" => ty!({"Type" "Nat" :})))); + env.clone().set(negative_ret_val(), ty!({"Type" "Nat" :})) + ), + Ok(assoc_n!("should_be_nat" => ty!({"Type" "Nat" :}))) + ); assert_eq!( ::ty::synth_type( @@ -698,8 +763,10 @@ fn type_basic_macro_invocation() { "binding" => "x", "body" => (import ["binding" = "val"] (vr "x")) }), - env.clone()), - Ok(ty!({ "Type" "Nat" :}))); + env.clone() + ), + Ok(ty!({ "Type" "Nat" :})) + ); assert_eq!( ::ty::neg_synth_type( @@ -714,14 +781,15 @@ fn type_basic_macro_invocation() { "body" => "x", "cond_expr" => (import ["body" : "t"] (vr "x")) }), - env.set(negative_ret_val(), ty!({"Type" "Int" :})).clone()), - Ok(assoc_n!("x" => ty!({ "Type" "Int" :})))); - + env.set(negative_ret_val(), ty!({"Type" "Int" :})).clone() + ), + Ok(assoc_n!("x" => ty!({ "Type" "Int" :}))) + ); } #[test] fn type_ddd_macro() { - let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); + let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); let pat_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Pat" }); let t_expr_type = ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), "arg" => [(vr "T")] @@ -743,24 +811,23 @@ fn type_ddd_macro() { s_expr_type.clone())); assert_eq!( - ::ty::synth_type( - &ast!({ - macro_invocation( - form_pat!([(lit "invoke let_like_macro"), - (star (named "val", (call "Expr"))), - (star (named "binding", (call "Pat"))), - (named "body", (import [* ["binding" = "val"]], (call "Expr")))]), - n("let_like_macro"), vec![]) ; - "val" => [@"arm" (vr "nat_var"), (vr "nat_var")], - "binding" => [@"arm" "x1", "x2"], - "body" => (import [* ["binding" = "val"]] (vr "x1")) - }), - env.clone()), - Ok(ty!({ "Type" "Nat" :}))); + ::ty::synth_type( + &ast!({ + macro_invocation( + form_pat!([(lit "invoke let_like_macro"), + (star (named "val", (call "Expr"))), + (star (named "binding", (call "Pat"))), + (named "body", (import [* ["binding" = "val"]], (call "Expr")))]), + n("let_like_macro"), vec![]) ; + "val" => [@"arm" (vr "nat_var"), (vr "nat_var")], + "binding" => [@"arm" "x1", "x2"], + "body" => (import [* ["binding" = "val"]] (vr "x1")) + }), + env.clone() + ), + Ok(ty!({ "Type" "Nat" :})) + ); } - #[test] -fn expand_basic_macros() { - -} +fn expand_basic_macros() {} diff --git a/src/core_qq_forms.rs b/src/core_qq_forms.rs index ef93535..4aa738b 100644 --- a/src/core_qq_forms.rs +++ b/src/core_qq_forms.rs @@ -1,14 +1,14 @@ +use ast::Ast; +use ast_walk::squirrel_away; +use ast_walk::WalkRule::*; +use core_forms::vr_to_name; +use core_type_forms::{less_quoted_ty, more_quoted_ty, nt_is_positive, nt_to_type}; +use form::{Both, Form, Negative, Positive}; +use grammar::{FormPat, SynEnv}; +use name::*; +use runtime::eval::{Destructure, Eval, QQuote, QQuoteDestr}; use std::rc::Rc; use ty::Ty; -use name::*; -use runtime::eval::{Eval, Destructure, QQuote, QQuoteDestr}; -use grammar::{SynEnv, FormPat}; -use form::{Form, Positive, Negative, Both}; -use core_forms::vr_to_name; -use core_type_forms::{nt_to_type, nt_is_positive, less_quoted_ty, more_quoted_ty}; -use ast_walk::WalkRule::*; -use ast_walk::squirrel_away; -use ast::Ast; use util::assoc::Assoc; use walk_mode::WalkMode; @@ -53,16 +53,11 @@ use walk_mode::WalkMode; // Note that it doesn't matter whether the boundary is a quotation or an unquotation! // The phase only matters inasmuch as variables don't leave their phase. - - // There's a nice-seeming syntax for determining what `unquote` does when quotation is nested. // However, it would require some weird additional power for the parser: // '[Expr | '[Expr | ,[Expr | …], ,,[Expr | …],,]']' // OTOH, if you are using `,,,,[],,,,`, something has gone terribly wrong. - - - // Opacity! // Basically, ` ,[Expr <[T]< | a ], ` dumps an expression with the type `T` into the type checker, // but at a different phase from where `T` was defined. @@ -72,40 +67,46 @@ use walk_mode::WalkMode; // We just need to write a walk for it (and annotate those `mu_type`s with a depth) fn adjust_opacity(t: &Ty, env: Assoc, delta: i32) -> Ty { - let ctxt = ::ast_walk::LazyWalkReses{ - extra_info: delta, .. ::ast_walk::LazyWalkReses::new_wrapper(env)}; + let ctxt = ::ast_walk::LazyWalkReses { + extra_info: delta, + ..::ast_walk::LazyWalkReses::new_wrapper(env) + }; ::ast_walk::walk::(&t.concrete(), &ctxt).unwrap() } fn remove_opacity(t: &Ty, delta: i32) -> Ty { - if delta > 0 { panic!("ICE") } + if delta > 0 { + panic!("ICE") + } // The environment doesn't matter when removing opacity adjust_opacity(t, Assoc::new(), delta) } - // This walk is for one very simple purpose: to add `mu` around unbound names. -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct MuProtect {} } // Sadly, we have to define a negative version, even though it's never used. -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct UnusedNegativeMuProtect {} } fn change_mu_opacity(parts: ::ast_walk::LazyWalkReses) -> Result { let delta = parts.extra_info; - let opacity = &parts.maybe_get_term(n("opacity_for_different_phase")).map( - |a| ::core_forms::ast_to_name(&a).sp().parse::().unwrap()); + let opacity = &parts + .maybe_get_term(n("opacity_for_different_phase")) + .map(|a| ::core_forms::ast_to_name(&a).sp().parse::().unwrap()); - if let Some(opacity) = opacity { - if opacity + delta < 0 { panic!("ICE: unwrapped too far")} + if let Some(opacity) = opacity { + if opacity + delta < 0 { + panic!("ICE: unwrapped too far") + } if opacity + delta == 0 { if let ::ast::ExtendEnv(node, _) = parts.get_term(n("body")) { - return Ok(Ty(*node)) + return Ok(Ty(*node)); } else { panic!("ICE: mal-formed mu_type") } @@ -113,19 +114,22 @@ fn change_mu_opacity(parts: ::ast_walk::LazyWalkReses) -> Result { - if let Some(opacity) = opacity { - mu_parts.add_leaf(n("opacity_for_different_phase"), - Ast::Atom(n(&(opacity+delta).to_string()))); + if let Some(opacity) = opacity { + mu_parts.add_leaf( + n("opacity_for_different_phase"), + Ast::Atom(n(&(opacity + delta).to_string())), + ); } Ok(Ty(Ast::Node(f, mu_parts, export))) } - _ => panic!("ICE") + _ => panic!("ICE"), } - } impl WalkMode for MuProtect { - fn name() -> &'static str { "MProt" } + fn name() -> &'static str { + "MProt" + } type Elt = Ty; type Negated = UnusedNegativeMuProtect; type Err = (); @@ -139,30 +143,39 @@ impl WalkMode for MuProtect { LiteralLike } } - fn automatically_extend_env() -> bool { true } + fn automatically_extend_env() -> bool { + true + } fn walk_var(name: Name, parts: &::ast_walk::LazyWalkReses) -> Result { - if parts.extra_info <= 0 { return Ok(Ty(::ast::VariableReference(name))) } - Ok(parts.env.find(&name).map(Clone::clone).unwrap_or_else(|| + if parts.extra_info <= 0 { + return Ok(Ty(::ast::VariableReference(name))); + } + Ok(parts.env.find(&name).map(Clone::clone).unwrap_or_else(|| { ty!({"Type" "mu_type" : "opacity_for_different_phase" => (, Ast::Atom(n(&parts.extra_info.to_string()))), "param" => [(import [prot "param"] (, Ast::VariableReference(name)))], - "body" => (import [* [prot "param"]] (, Ast::VariableReference(name)))}))) + "body" => (import [* [prot "param"]] (, Ast::VariableReference(name)))}) + })) } } impl WalkMode for UnusedNegativeMuProtect { - fn name() -> &'static str { "XXXXX" } + fn name() -> &'static str { + "XXXXX" + } type Elt = Ty; type Negated = MuProtect; type Err = (); type D = ::walk_mode::Positive; type ExtraInfo = i32; - fn get_walk_rule(_: &Form) -> ::ast_walk::WalkRule { panic!("ICE") } - fn automatically_extend_env() -> bool { panic!("ICE") } + fn get_walk_rule(_: &Form) -> ::ast_walk::WalkRule { + panic!("ICE") + } + fn automatically_extend_env() -> bool { + panic!("ICE") + } } - - // Technically, we could have the parser decide whether `unquote` is allowed. // (It only makes sense inside a `quote`.) // However, this would leave us with one `unquote` form available per level of quotation @@ -170,9 +183,14 @@ impl WalkMode for UnusedNegativeMuProtect { /// Generate a (depth-1) unquoting form. /// `pos_quot` is true iff the quotation itself (and thus the interpolation) is positive. pub fn unquote(nt: Name, pos_quot: bool) -> Rc { - Rc::new(FormPat::Scope(unquote_form(nt, pos_quot, 1), - if pos_quot {::beta::ExportBeta::Nothing} - else {::beta::ExportBeta::Use(n("body"))})) + Rc::new(FormPat::Scope( + unquote_form(nt, pos_quot, 1), + if pos_quot { + ::beta::ExportBeta::Nothing + } else { + ::beta::ExportBeta::Use(n("body")) + }, + )) } pub fn unquote_form(nt: Name, pos_quot: bool, depth: u8) -> Rc { @@ -292,11 +310,13 @@ pub fn unquote_form(nt: Name, pos_quot: bool, depth: u8) -> Rc { }) } - // Macro By Example transcription. TODO: currently positive-only pub fn dotdotdot(nt: Name) -> Rc { - Rc::new(FormPat::Scope(dotdotdot_form(nt), ::beta::ExportBeta::Nothing)) + Rc::new(FormPat::Scope( + dotdotdot_form(nt), + ::beta::ExportBeta::Nothing, + )) } pub fn dotdotdot_form(nt: Name) -> Rc { @@ -305,84 +325,88 @@ pub fn dotdotdot_form(nt: Name) -> Rc { grammar: Rc::new(form_pat!((delim "...[", "[", [(star (named "driver", varref)), (lit ">>"), (named "body", (call_by_name nt))]))), type_compare: Positive(NotWalked), // this is not a type form - synth_type: Positive( - cust_rc_box!(| ddd_parts | { - let drivers = ddd_parts.get_rep_term(n("driver")); - let mut walked_env = Assoc::new(); + synth_type: Positive(cust_rc_box!(|ddd_parts| { + let drivers = ddd_parts.get_rep_term(n("driver")); + let mut walked_env = Assoc::new(); - let repeats = match ddd_parts.env.find(&::core_forms::vr_to_name(&drivers[0])) { - Some(&Ty(::ast::Node(ref form, ref parts, _))) if form.name == n("tuple") => { - parts.get_rep_leaf_or_panic(n("component")).len() - } - Some(other_t) => { - ty_err!(UnableToDestructure(other_t.clone(), n("tuple")) + let repeats = match ddd_parts.env.find(&::core_forms::vr_to_name(&drivers[0])) { + Some(&Ty(::ast::Node(ref form, ref parts, _))) if form.name == n("tuple") => { + parts.get_rep_leaf_or_panic(n("component")).len() + } + Some(other_t) => { + ty_err!(UnableToDestructure(other_t.clone(), n("tuple")) at ddd_parts.this_ast); - } - _ => ty_err!(UnboundName(::core_forms::vr_to_name(&drivers[0])) - at ddd_parts.this_ast) - }; - - for i in 0..repeats { - for (name, ty) in ddd_parts.env.iter_pairs() { - if drivers.contains(&::ast::VariableReference(*name)) { - walked_env = walked_env.set( - *name, - match ty { - Ty(::ast::Node(ref form, ref parts, _)) - if form.name == n("tuple") => { - Ty(parts.get_rep_leaf_or_panic(n("component"))[i].clone()) - } - t => - ty_err!(UnableToDestructure(t.clone(), n("tuple")) - at t.0) - }); - } else { - walked_env = walked_env.set(*name, ty.clone()); - } + } + _ => ty_err!(UnboundName(::core_forms::vr_to_name(&drivers[0])) + at ddd_parts.this_ast), + }; + + for i in 0..repeats { + for (name, ty) in ddd_parts.env.iter_pairs() { + if drivers.contains(&::ast::VariableReference(*name)) { + walked_env = walked_env.set( + *name, + match ty { + Ty(::ast::Node(ref form, ref parts, _)) + if form.name == n("tuple") => + { + Ty(parts.get_rep_leaf_or_panic(n("component"))[i].clone()) + } + t => ty_err!(UnableToDestructure(t.clone(), n("tuple")) + at t.0), + }, + ); + } else { + walked_env = walked_env.set(*name, ty.clone()); } } - ddd_parts.with_environment(walked_env).get_res(n("body")) } - )), + ddd_parts.with_environment(walked_env).get_res(n("body")) + })), eval: Positive(NotWalked), - quasiquote: Positive( - cust_rc_box!(| ddd_parts | { - use ::runtime::eval::{Value,Sequence}; - use walk_mode::WalkElt; - - let drivers = ddd_parts.get_rep_term(n("driver")); - - // TODO: the typechecker should reject dotdotdotds with no drivers, - // or where a driver isn't in scope. - let count = match *ddd_parts.env.find_or_panic( - &::core_forms::vr_to_name(&drivers[0])) { - Sequence(ref contents) => { contents.len() } - _ => { panic!("ICE: type error")} - }; - let mut reps = vec![]; - - for i in 0..count { - let mut walked_env = Assoc::new(); - for (n, val) in ddd_parts.env.iter_pairs() { - let walked_val = if drivers.contains(&::ast::VariableReference(*n)) { - match *val { - Sequence(ref contents) => (*contents[i]).clone(), - _ => panic!("ICE: type error") - } - } else { - val.clone() - }; - walked_env = walked_env.set(*n, walked_val); - } - - ddd_parts.clear_memo(); - reps.push(ddd_parts.with_environment(walked_env).get_res(n("body"))?.to_ast()); + quasiquote: Positive(cust_rc_box!(|ddd_parts| { + use runtime::eval::{Sequence, Value}; + use walk_mode::WalkElt; + + let drivers = ddd_parts.get_rep_term(n("driver")); + + // TODO: the typechecker should reject dotdotdotds with no drivers, + // or where a driver isn't in scope. + let count = match *ddd_parts + .env + .find_or_panic(&::core_forms::vr_to_name(&drivers[0])) + { + Sequence(ref contents) => contents.len(), + _ => panic!("ICE: type error"), + }; + let mut reps = vec![]; + + for i in 0..count { + let mut walked_env = Assoc::new(); + for (n, val) in ddd_parts.env.iter_pairs() { + let walked_val = if drivers.contains(&::ast::VariableReference(*n)) { + match *val { + Sequence(ref contents) => (*contents[i]).clone(), + _ => panic!("ICE: type error"), + } + } else { + val.clone() + }; + walked_env = walked_env.set(*n, walked_val); } - // HACK: this signals to `LiteralLike` that it needs to splice the sequence - Ok(Value::from_ast(&::ast::Shape(reps))) + ddd_parts.clear_memo(); + reps.push( + ddd_parts + .with_environment(walked_env) + .get_res(n("body"))? + .to_ast(), + ); } - )) + + // HACK: this signals to `LiteralLike` that it needs to splice the sequence + Ok(Value::from_ast(&::ast::Shape(reps))) + })), }) } @@ -395,21 +419,21 @@ pub fn dotdotdot_form(nt: Name) -> Rc { // Furthermore, the direction of the walk is determined by the direction of the original quotation. pub fn quote(pos: bool) -> Rc { - use ::grammar::FormPat; - use ::grammar::FormPat::*; + use grammar::FormPat; + use grammar::FormPat::*; let perform_quotation = move |se: SynEnv, starter_info: Ast| -> SynEnv { let starter_nt = match starter_info { ::ast::IncompleteNode(ref parts) => vr_to_name(&parts.get_leaf_or_panic(&n("nt"))), - _ => panic!("ICE: malformed quotation") + _ => panic!("ICE: malformed quotation"), }; fn already_has_unquote(fp: &FormPat) -> bool { match *fp { - Alt(ref parts) => { parts.iter().any(|sub_fp| already_has_unquote(&*sub_fp)) }, + Alt(ref parts) => parts.iter().any(|sub_fp| already_has_unquote(&*sub_fp)), Biased(ref plan_a, ref plan_b) => { already_has_unquote(&*plan_a) || already_has_unquote(&*plan_b) } - Scope(ref f, _) => { f.name == n("unquote") } - _ => false + Scope(ref f, _) => f.name == n("unquote"), + _ => false, } } @@ -418,19 +442,26 @@ pub fn quote(pos: bool) -> Rc { se.keyed_map_borrow_f(&mut |nt: &Name, nt_def: &Rc| { if already_has_unquote(nt_def) // HACK: this is to avoid hitting "starterer". TODO: find a better way - || (nt != &n("Expr") && nt != &n("Pat") && nt != &n("Type")) { + || (nt != &n("Expr") && nt != &n("Pat") && nt != &n("Type")) + { nt_def.clone() } else { // TODO: maybe we should only insert `dotdotdot` in repetition positions? - Rc::new(Biased(unquote(*nt, pos), Rc::new(Biased(dotdotdot(*nt), nt_def.clone())))) - }}) - .set(n("starterer_nt"), - Rc::new(form_pat!( + Rc::new(Biased( + unquote(*nt, pos), + Rc::new(Biased(dotdotdot(*nt), nt_def.clone())), + )) + } + }) + .set( + n("starterer_nt"), + Rc::new(form_pat!( // HACK: The `nt` from outside isn't in the same Scope, it seems: [(named "nt", (anyways (, ::ast::VariableReference(starter_nt)))), (alt [], (delim "<[", "[", (named "ty_annot", (call "Type")))), (lit "|"), - (named "body", (++ pos_inside (call_by_name starter_nt)))]))) + (named "body", (++ pos_inside (call_by_name starter_nt)))])), + ) }; // TODO #4: the following hardcodes positive walks as `Expr` and negative walks as `Pat`. @@ -440,91 +471,93 @@ pub fn quote(pos: bool) -> Rc { grammar: Rc::new(form_pat!((delim "'[", "[", [(extend (named "nt", varref), "starterer_nt", perform_quotation)]))), type_compare: ::form::Both(NotWalked, NotWalked), // Not a type - synth_type: - if pos { - ::form::Positive(cust_rc_box!(|quote_parts| { - if nt_is_positive(vr_to_name("e_parts.get_term(n("nt")))) { - // TODO #9: if the user provides an annotation, check it! - Ok(ty!({"Type" "type_apply" : - "type_rator" => - (, nt_to_type(vr_to_name( - "e_parts.get_term(n("nt")))).concrete() ), - "arg" => [(, - remove_opacity("e_parts.get_res(n("body"))?, -1).concrete() - )] - })) - - } else { - // TODO: if the user accidentally omits the annotation, - // provide a good error message. - let expected_type = "e_parts.get_res(n("ty_annot"))?; - - // We're looking at things 1 level deeper: - let prot_expected_type = adjust_opacity( - expected_type, quote_parts.env.clone(), 1); - - // TODO: do we need this result environment somewhere? - // Note that `Pat <[Point]<` (as opposed to `Pat <:[x: Real, y: Real]<:`) - // is what we want! - // In other words, syntax types don't care about positive vs. negative! - // There's a longer argument in the form of code to this effect elsewhere, - // but it boils down to this: environments can always be managed directly, - // by introducing and referencing bindings. - let _ = "e_parts.with_context(prot_expected_type) - .get_res(n("body")); - - Ok(ty!({"Type" "type_apply" : + synth_type: if pos { + ::form::Positive(cust_rc_box!(|quote_parts| { + if nt_is_positive(vr_to_name("e_parts.get_term(n("nt")))) { + // TODO #9: if the user provides an annotation, check it! + Ok(ty!({"Type" "type_apply" : + "type_rator" => + (, nt_to_type(vr_to_name( + "e_parts.get_term(n("nt")))).concrete() ), + "arg" => [(, + remove_opacity("e_parts.get_res(n("body"))?, -1).concrete() + )] + })) + } else { + // TODO: if the user accidentally omits the annotation, + // provide a good error message. + let expected_type = "e_parts.get_res(n("ty_annot"))?; + + // We're looking at things 1 level deeper: + let prot_expected_type = + adjust_opacity(expected_type, quote_parts.env.clone(), 1); + + // TODO: do we need this result environment somewhere? + // Note that `Pat <[Point]<` (as opposed to `Pat <:[x: Real, y: Real]<:`) + // is what we want! + // In other words, syntax types don't care about positive vs. negative! + // There's a longer argument in the form of code to this effect elsewhere, + // but it boils down to this: environments can always be managed directly, + // by introducing and referencing bindings. + let _ = "e_parts + .with_context(prot_expected_type) + .get_res(n("body")); + + Ok(ty!({"Type" "type_apply" : "type_rator" => (, nt_to_type(vr_to_name( "e_parts.get_term(n("nt")))).concrete() ), "arg" => [ (,expected_type.concrete()) ]})) + } + })) + } else { + ::form::Negative(cust_rc_box!(|quote_parts| { + // There's no need for a type annotation + let nt = vr_to_name("e_parts.get_term(n("nt"))); + if nt_is_positive(nt) { + // TODO #9: check that this matches the type annotation, if provided! + quote_parts.get_res(n("body")) + } else { + let new_context = + less_quoted_ty(quote_parts.context_elt(), Some(nt), "e_parts.this_ast)?; + // TODO #9: check that this matches the type annotation, if provided! + quote_parts.with_context(new_context).get_res(n("body")) + } + })) + }, + eval: if pos { + Positive(cust_rc_box!(|quote_parts| { + let mq_parts = quote_parts.switch_mode::().quote_more(None); + match mq_parts.get_term_ref(n("body")) { + // Strip the `QuoteMore`: + ::ast::QuoteMore(ref a, _) => ::ast_walk::walk::(&*a, &mq_parts), + _ => panic!("ICE"), + } + })) + } else { + Negative(cust_rc_box!(|quote_parts| { + let context = quote_parts.context_elt().clone(); + + let mq_parts = quote_parts + .switch_mode::() + .quote_more(None) + .with_context(context); + match mq_parts.get_term_ref(n("body")) { + // Strip the `QuoteMore`: + ::ast::QuoteMore(ref body, _) => { + ::ast_walk::walk::(&*body, &mq_parts) } - })) - } else { - ::form::Negative(cust_rc_box!(|quote_parts| { - // There's no need for a type annotation - let nt = vr_to_name("e_parts.get_term(n("nt"))); - if nt_is_positive(nt) { - // TODO #9: check that this matches the type annotation, if provided! - quote_parts.get_res(n("body")) - } else { - let new_context = less_quoted_ty( - quote_parts.context_elt(), Some(nt), - "e_parts.this_ast)?; - // TODO #9: check that this matches the type annotation, if provided! - quote_parts.with_context(new_context).get_res(n("body")) - } - })) - }, - eval: - if pos { - Positive(cust_rc_box!(|quote_parts| { - let mq_parts = quote_parts.switch_mode::().quote_more(None); - match mq_parts.get_term_ref(n("body")) { // Strip the `QuoteMore`: - ::ast::QuoteMore(ref a, _) => ::ast_walk::walk::(&*a, &mq_parts), - _ => panic!("ICE") - } - })) - } else { - Negative(cust_rc_box!(|quote_parts| { - let context = quote_parts.context_elt().clone(); - - let mq_parts = quote_parts.switch_mode::().quote_more(None) - .with_context(context); - match mq_parts.get_term_ref(n("body")) { // Strip the `QuoteMore`: - ::ast::QuoteMore(ref body, _) - => ::ast_walk::walk::(&*body, &mq_parts), - _ => panic!("ICE") - } - })) - }, - quasiquote: Both(LiteralLike, LiteralLike) + _ => panic!("ICE"), + } + })) + }, + quasiquote: Both(LiteralLike, LiteralLike), }) } #[test] fn quote_unquote_eval_basic() { - use ::runtime::eval::Value; + use runtime::eval::Value; let pos = true; let neg = false; @@ -538,45 +571,73 @@ fn quote_unquote_eval_basic() { "qn" => val!(i 6) ); - fn eval_two_phased(expr: &Ast, env: Assoc, qenv: Assoc) - -> Result { - ::ast_walk::walk::(expr, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv])) + fn eval_two_phased( + expr: &Ast, + env: Assoc, + qenv: Assoc, + ) -> Result { + ::ast_walk::walk::( + expr, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]), + ) } - fn destr_two_phased(pat: &Ast, env: Assoc, qenv: Assoc, ctxt: Value) - -> Result, ()> { - ::ast_walk::walk::(pat, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv]).with_context(ctxt)) + fn destr_two_phased( + pat: &Ast, + env: Assoc, + qenv: Assoc, + ctxt: Value, + ) -> Result, ()> { + ::ast_walk::walk::( + pat, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]).with_context(ctxt), + ) } - assert_eq!(eval_two_phased(&ast!({quote(pos) ; "nt" => (vr "Expr"), + assert_eq!( + eval_two_phased( + &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true (vr "qn"))}), - env.clone(), qenv.clone()), - Ok(val!(ast (vr "qn")))); - + env.clone(), + qenv.clone() + ), + Ok(val!(ast (vr "qn"))) + ); - assert_eq!(eval_two_phased(&ast!({quote(pos) ; "nt" => (vr "Expr"), + assert_eq!( + eval_two_phased( + &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {unquote_form(n("Expr"), true, 1) ; "nt" => (vr "Expr"), "body" => (-- 1 (vr "en"))})}), - env.clone(), qenv.clone()), - Ok(val!(ast (vr "qn")))); + env.clone(), + qenv.clone() + ), + Ok(val!(ast (vr "qn"))) + ); assert_eq!( - destr_two_phased(&ast!({quote(neg) ; "nt" => (vr "Expr"), "body" => (++ false (vr "qn"))}), - env.clone(), qenv.clone(), - val!(ast (vr "qn"))), - Ok(Assoc::::new())); + destr_two_phased( + &ast!({quote(neg) ; "nt" => (vr "Expr"), "body" => (++ false (vr "qn"))}), + env.clone(), + qenv.clone(), + val!(ast (vr "qn")) + ), + Ok(Assoc::::new()) + ); // '[Expr | match qn { x => qn }]' - assert_m!(eval_two_phased( + assert_m!( + eval_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "match" : "scrutinee" => (vr "qn"), "p" => [@"c" "x"], "arm" => [@"c" (import ["p" = "scrutinee"] (vr "qn"))]})}), - env.clone(), qenv.clone()), - Ok(_)); + env.clone(), + qenv.clone() + ), + Ok(_) + ); } #[test] @@ -595,127 +656,173 @@ fn quote_type_basic() { let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); let pat_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Pat" }); - fn synth_type_two_phased(expr: &Ast, env: Assoc, qenv: Assoc) - -> ::ty::TypeResult { - ::ast_walk::walk::<::ty::SynthTy>(expr, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv])) + fn synth_type_two_phased( + expr: &Ast, + env: Assoc, + qenv: Assoc, + ) -> ::ty::TypeResult { + ::ast_walk::walk::<::ty::SynthTy>( + expr, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]), + ) } // '[Expr | qn]' - assert_eq!(synth_type_two_phased( + assert_eq!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true (vr "qn"))}), - env.clone(), qenv.clone()), + env.clone(), + qenv.clone() + ), Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]}))); + "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]})) + ); // '[Expr | match qn { x => qn }]' - assert_eq!(synth_type_two_phased( + assert_eq!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "match" : "scrutinee" => (vr "qn"), "p" => [@"c" "x"], "arm" => [@"c" (import ["p" = "scrutinee"] (vr "qn"))]})}), - env.clone(), qenv.clone()), + env.clone(), + qenv.clone() + ), Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]}))); - + "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]})) + ); // previously unaccessed environments default to the core values/types // '[Expr | '[Expr | five]']' - assert_eq!(synth_type_two_phased( - &ast!( + assert_eq!( + synth_type_two_phased( + &ast!( {quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true (vr "five"))})}), - env.clone(), qenv.clone()), + env.clone(), + qenv.clone() + ), Ok(ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Int" :}]}]}))); + "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Int" :}]}]})) + ); // '[Expr <[Nat]< | qn]' // With type annotation, same result: - assert_eq!(synth_type_two_phased( + assert_eq!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true (vr "qn")), "ty_annot" => {"Type" "Nat" :}}), - env.clone(), qenv.clone()), + env.clone(), + qenv.clone() + ), Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]}))); + "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]})) + ); // '[Expr | n]' - assert_m!(synth_type_two_phased( + assert_m!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true (vr "n"))}), - env.clone(), qenv.clone()), - ty_err_p!(UnboundName(_))); + env.clone(), + qenv.clone() + ), + ty_err_p!(UnboundName(_)) + ); // '[Expr | { x: qn y: qn }]' - assert_eq!(synth_type_two_phased( + assert_eq!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), - "body" => (++ true {"Expr" "struct_expr" : - "component_name" => [@"c" "x", "y"], - "component" => [@"c" (vr "qn"), (vr "qn")] - })}), - env.clone(), qenv.clone()), + "body" => (++ true {"Expr" "struct_expr" : + "component_name" => [@"c" "x", "y"], + "component" => [@"c" (vr "qn"), (vr "qn")] + })}), + env.clone(), + qenv.clone() + ), Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{ "Type" "struct" : - "component_name" => [@"c" "x", "y"], - "component" => [@"c" {"Type" "Nat":}, {"Type" "Nat" :}] - }]}))); - - fn unpack_type_two_phased(pat: &Ast, env: Assoc, qenv: Assoc, ctxt: Ty) - -> Result, ::ty::TypeError> { - ::ast_walk::walk::<::ty::UnpackTy>(pat, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv]).with_context(ctxt)) + "type_rator" => (,expr_type.clone()), "arg" => [{ "Type" "struct" : + "component_name" => [@"c" "x", "y"], + "component" => [@"c" {"Type" "Nat":}, {"Type" "Nat" :}] + }]})) + ); + + fn unpack_type_two_phased( + pat: &Ast, + env: Assoc, + qenv: Assoc, + ctxt: Ty, + ) -> Result, ::ty::TypeError> { + ::ast_walk::walk::<::ty::UnpackTy>( + pat, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]).with_context(ctxt), + ) } // A trivial pattern containing an expression // '[Expr <[ struct {} ]< | *[]* ]' - assert_eq!(unpack_type_two_phased(&ast!({quote(neg) ; + assert_eq!( + unpack_type_two_phased( + &ast!({quote(neg) ; "nt" => (vr "Expr"), "ty_annot" => {"Type" "struct": "component_name" => [@"c"], "component" => [@"c"]}, "body" => (++ true {"Expr" "struct_expr": "component_name" => [@"c"], "component" => [@"c"]})}), - env.clone(), qenv.clone(), + env.clone(), + qenv.clone(), ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{ "Type" "struct" : - "component_name" => [@"c"], "component" => [@"c"] - }]})), - Ok(assoc_n!())); + "type_rator" => (,expr_type.clone()), "arg" => [{ "Type" "struct" : + "component_name" => [@"c"], "component" => [@"c"] + }]}) + ), + Ok(assoc_n!()) + ); // A trivial pattern containing a pattern // '[Pat <[ struct {} ]< | *[]* ]' - assert_eq!(unpack_type_two_phased(&ast!({quote(neg) ; + assert_eq!( + unpack_type_two_phased( + &ast!({quote(neg) ; "nt" => (vr "Pat"), "ty_annot" => {"Type" "struct": "component_name" => [@"c"], "component" => [@"c"]}, "body" => (++ false {"Pat" "struct_pat": "component_name" => [@"c"], "component" => [@"c"]})}), - env.clone(), qenv.clone(), + env.clone(), + qenv.clone(), ty!({"Type" "type_apply" : - "type_rator" => (,pat_type.clone()), "arg" => [{ "Type" "struct" : - "component_name" => [@"c"], "component" => [@"c"] - }]})), - Ok(assoc_n!())); + "type_rator" => (,pat_type.clone()), "arg" => [{ "Type" "struct" : + "component_name" => [@"c"], "component" => [@"c"] + }]}) + ), + Ok(assoc_n!()) + ); // A slightly-less trivial pattern containing a pattern (but still no unquotes) // '[Pat <[ struct {x: Nat} ]< | *[x: qfoo]* ]' - assert_eq!(unpack_type_two_phased(&ast!({quote(neg) ; + assert_eq!( + unpack_type_two_phased( + &ast!({quote(neg) ; "nt" => (vr "Pat"), "ty_annot" => {"Type" "struct": "component_name" => [@"c" "x"], "component" => [@"c" {"Type" "Nat" :}]}, "body" => (++ false {"Pat" "struct_pat": "component_name" => [@"c" "x"], "component" => [@"c" "qfoo"]})}), - env.clone(), qenv.clone(), + env.clone(), + qenv.clone(), ty!({"Type" "type_apply" : - "type_rator" => (,pat_type.clone()), "arg" => [{ "Type" "struct" : - "component_name" => [@"c" "x"], "component" => [@"c" {"Type" "Nat" :}] - }]})), - Ok(assoc_n!())); - + "type_rator" => (,pat_type.clone()), "arg" => [{ "Type" "struct" : + "component_name" => [@"c" "x"], "component" => [@"c" {"Type" "Nat" :}] + }]}) + ), + Ok(assoc_n!()) + ); } - - #[test] fn quote_unquote_type_basic() { let pos = true; @@ -724,12 +831,18 @@ fn quote_unquote_type_basic() { let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); let pat_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Pat" }); - assert_eq!(::ty::synth_type(&ast!( + assert_eq!( + ::ty::synth_type( + &ast!( {quote(pos) ; "nt" => (vr "Pat"), "ty_annot" => {"Type" "Nat" :}, "body" => (++ false "x")}), - Assoc::new()), - Ok(ty!({"Type" "type_apply" : "type_rator" => (,pat_type.clone()), - "arg" => [{"Type" "Nat" :}]}))); + Assoc::new() + ), + Ok( + ty!({"Type" "type_apply" : "type_rator" => (,pat_type.clone()), + "arg" => [{"Type" "Nat" :}]}) + ) + ); let env = assoc_n!( "T" => ty!((vr "T")), // we're under a `forall T . ⋯` @@ -755,34 +868,46 @@ fn quote_unquote_type_basic() { "qT" => adjust_opacity(&ty!((vr "T")), Assoc::new(), 1) ); - fn synth_type_two_phased(expr: &Ast, env: Assoc, qenv: Assoc) - -> ::ty::TypeResult { - ::ast_walk::walk::<::ty::SynthTy>(expr, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv])) + fn synth_type_two_phased( + expr: &Ast, + env: Assoc, + qenv: Assoc, + ) -> ::ty::TypeResult { + ::ast_walk::walk::<::ty::SynthTy>( + expr, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]), + ) } // An expression containing an expression // '[Expr | // *[x: ,[Expr | en], y: ,[Expr | ef], z: baz]* ]' - assert_eq!(synth_type_two_phased(&ast!({quote(pos) ; - "nt" => (vr "Expr"), - "body" => (++ true {"Expr" "struct_expr": - "component_name" => [@"c" "x", "y", "z"], - "component" => [@"c" - {unquote_form(n("Expr"), pos, 1) ; - "nt" => (vr "Expr"), "body" => (-- 1 (vr "en"))}, - {unquote_form(n("Expr"), pos, 1) ; - "nt" => (vr "Expr"), "body" => (-- 1 (vr "ef"))}, - (vr "qn") - ]})}), - env.clone(), qenv.clone()), - Ok(ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), + assert_eq!( + synth_type_two_phased( + &ast!({quote(pos) ; + "nt" => (vr "Expr"), + "body" => (++ true {"Expr" "struct_expr": + "component_name" => [@"c" "x", "y", "z"], + "component" => [@"c" + {unquote_form(n("Expr"), pos, 1) ; + "nt" => (vr "Expr"), "body" => (-- 1 (vr "en"))}, + {unquote_form(n("Expr"), pos, 1) ; + "nt" => (vr "Expr"), "body" => (-- 1 (vr "ef"))}, + (vr "qn") + ]})}), + env.clone(), + qenv.clone() + ), + Ok( + ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "struct": "component_name" => [@"c" "x", "y", "z"], - "component" => [@"c" {"Type" "Nat" :}, {"Type" "Float" :}, {"Type" "Nat" :}]}]}))); - + "component" => [@"c" {"Type" "Nat" :}, {"Type" "Float" :}, {"Type" "Nat" :}]}]}) + ) + ); // '[Expr | (,[Expr | eT_to_T], ,[Expr | eT],) ]' - assert_eq!(synth_type_two_phased( + assert_eq!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "apply": @@ -790,48 +915,66 @@ fn quote_unquote_type_basic() { (-- 1 (vr "eT_to_T"))}, "rand" => [{unquote_form(n("Expr"), pos, 1) ; "nt" => (vr "Expr"), "body" => (-- 1 (vr "eT"))}]})}), - env.clone(), qenv.clone()), - Ok(ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), - "arg" => [(vr "T")]}))); - - fn unpack_type_two_phased(pat: &Ast, env: Assoc, qenv: Assoc, ctxt: Ty) - -> Result, ::ty::TypeError> { - ::ast_walk::walk::<::ty::UnpackTy>(pat, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv]).with_context(ctxt)) + env.clone(), + qenv.clone() + ), + Ok( + ty!({"Type" "type_apply" : "type_rator" => (,expr_type.clone()), + "arg" => [(vr "T")]}) + ) + ); + + fn unpack_type_two_phased( + pat: &Ast, + env: Assoc, + qenv: Assoc, + ctxt: Ty, + ) -> Result, ::ty::TypeError> { + ::ast_walk::walk::<::ty::UnpackTy>( + pat, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]).with_context(ctxt), + ) } // A pattern containing a pattern // '[Pat <[ struct {x : Nat y : Float z : Nat} ]< | // *[x: ,[Pat | foo], y: ,[Pat | bar], z: baz]* ]' - assert_eq!(unpack_type_two_phased(&ast!({quote(neg) ; - "nt" => (vr "Pat"), - "ty_annot" => {"Type" "struct": - "component_name" => [@"c" "x", "y", "z"], - "component" => [@"c" {"Type" "Nat" :}, {"Type" "Float" :}, {"Type" "Nat" :}]}, - "body" => (++ false {"Pat" "struct_pat": - "component_name" => [@"c" "x", "y", "z"], - "component" => [@"c" - {unquote_form(n("Pat"), neg, 1) ; "nt" => (vr "Pat"), "body" => (-- 1 "foo")}, - {unquote_form(n("Pat"), neg, 1) ; "nt" => (vr "Pat"), "body" => (-- 1 "bar")}, - "baz" - ]})}), - env.clone(), qenv.clone(), + assert_eq!( + unpack_type_two_phased( + &ast!({quote(neg) ; + "nt" => (vr "Pat"), + "ty_annot" => {"Type" "struct": + "component_name" => [@"c" "x", "y", "z"], + "component" => [@"c" {"Type" "Nat" :}, {"Type" "Float" :}, {"Type" "Nat" :}]}, + "body" => (++ false {"Pat" "struct_pat": + "component_name" => [@"c" "x", "y", "z"], + "component" => [@"c" + {unquote_form(n("Pat"), neg, 1) ; "nt" => (vr "Pat"), "body" => (-- 1 "foo")}, + {unquote_form(n("Pat"), neg, 1) ; "nt" => (vr "Pat"), "body" => (-- 1 "bar")}, + "baz" + ]})}), + env.clone(), + qenv.clone(), ty!({"Type" "type_apply" : - "type_rator" => (,pat_type.clone()), "arg" => [{ "Type" "struct" : - "component_name" => [@"c" "x", "y", "z"], - "component" => [@"c" {"Type" "Nat" :}, {"Type" "Float" :}, {"Type" "Nat" :}] - }]})), + "type_rator" => (,pat_type.clone()), "arg" => [{ "Type" "struct" : + "component_name" => [@"c" "x", "y", "z"], + "component" => [@"c" {"Type" "Nat" :}, {"Type" "Float" :}, {"Type" "Nat" :}] + }]}) + ), Ok(assoc_n!( "foo" => ty!({"Type" "type_apply" : "arg" => [{"Type" "Nat" :}], "type_rator" => (,pat_type.clone())}), "bar" => ty!({"Type" "type_apply" : "arg" => [{"Type" "Float" :}], - "type_rator" => (,pat_type.clone())})))); + "type_rator" => (,pat_type.clone())}))) + ); // Interpolate a pattern // '[Expr | match qn { ,[Pat <[Nat]< | pn], => qn }] - assert_eq!(synth_type_two_phased(&ast!({quote(pos) ; + assert_eq!( + synth_type_two_phased( + &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "match": "scrutinee" => (vr "qn"), @@ -842,13 +985,18 @@ fn quote_unquote_type_basic() { "arg" => {"Type" "Nat" :}}, "body" => (-- 1 (vr "pn"))}], "arm" => [@"c" (import ["p" = "scrutinee"] (vr "qn"))]})}), - env.clone(), qenv.clone()), - Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{ "Type" "Nat" :}]}))); + env.clone(), + qenv.clone() + ), + Ok(ty!({"Type" "type_apply" : + "type_rator" => (,expr_type.clone()), "arg" => [{ "Type" "Nat" :}]})) + ); // Interpolate a pattern, with abstract types // '[Expr | match qT { ,[Pat <[T]< | pT], => qT }] - assert_eq!(synth_type_two_phased(&ast!({quote(pos) ; + assert_eq!( + synth_type_two_phased( + &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "match": "scrutinee" => (vr "qT"), @@ -859,21 +1007,23 @@ fn quote_unquote_type_basic() { "arg" => (vr "T")}, "body" => (-- 1 (vr "pT"))}], "arm" => [@"c" (vr "qT")]})}), - env.clone(), qenv.clone()), - Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [(vr "T")]}))); + env.clone(), + qenv.clone() + ), + Ok(ty!({"Type" "type_apply" : + "type_rator" => (,expr_type.clone()), "arg" => [(vr "T")]})) + ); } #[test] fn unquote_type_basic() { - use ::ast_walk::{walk, LazyWalkReses, OutEnvHandle}; + use ast_walk::{walk, LazyWalkReses, OutEnvHandle}; let pos = true; let _neg = false; let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); let _pat_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Pat" }); - let env = assoc_n!( "n" => ty!({"Type" "Nat" :}), "en" => ty!({"Type" "type_apply" : @@ -884,8 +1034,11 @@ fn unquote_type_basic() { "qn" => ty!({"Type" "Nat" :}) ); - fn synth_type_two_phased_lq(expr: &Ast, env: Assoc, qenv: Assoc) - -> ::ty::TypeResult { + fn synth_type_two_phased_lq( + expr: &Ast, + env: Assoc, + qenv: Assoc, + ) -> ::ty::TypeResult { let parts = LazyWalkReses::new_wrapper(env); let qparts = parts.quote_more(None).with_environment(qenv); @@ -895,13 +1048,16 @@ fn unquote_type_basic() { // ,[Expr | en ], let res = synth_type_two_phased_lq( &ast!({unquote_form(n("Expr"), pos, 1) ; - "nt" => (vr "Expr"), "body" => (-- 1 (vr "en"))}), env, qenv); + "nt" => (vr "Expr"), "body" => (-- 1 (vr "en"))}), + env, + qenv, + ); assert_eq!(res, Ok(ty!({"Type" "Nat" :}))); } #[test] fn use_dotdotdot() { - use ::runtime::eval::Value; + use runtime::eval::Value; let pos = true; let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); @@ -934,25 +1090,33 @@ fn use_dotdotdot() { "qns" => val!(seq (ast (vr "qn")) (ast (vr "qnn"))) ); - let expr_type = ast!({::core_type_forms::get__abstract_parametric_type() ; "name" => "Expr" }); - fn synth_type_two_phased(expr: &Ast, env: Assoc, qenv: Assoc) - -> ::ty::TypeResult { - ::ast_walk::walk::<::ty::SynthTy>(expr, &::ast_walk::LazyWalkReses::new_mq_wrapper( - env, vec![qenv])) + fn synth_type_two_phased( + expr: &Ast, + env: Assoc, + qenv: Assoc, + ) -> ::ty::TypeResult { + ::ast_walk::walk::<::ty::SynthTy>( + expr, + &::ast_walk::LazyWalkReses::new_mq_wrapper(env, vec![qenv]), + ) } - - fn eval_two_phased(expr: &Ast, eval_env: Assoc, eval_qenv: Assoc) - -> Result { - ::ast_walk::walk::(expr, &::ast_walk::LazyWalkReses::new_mq_wrapper( - eval_env, vec![eval_qenv])) + fn eval_two_phased( + expr: &Ast, + eval_env: Assoc, + eval_qenv: Assoc, + ) -> Result { + ::ast_walk::walk::( + expr, + &::ast_walk::LazyWalkReses::new_mq_wrapper(eval_env, vec![eval_qenv]), + ) } - // '[Expr | match qn { x => ...[qns >> ,[Expr | qns],]... }]' - assert_eq!(synth_type_two_phased( + assert_eq!( + synth_type_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "match" : "scrutinee" => (vr "qn"), @@ -962,10 +1126,13 @@ fn use_dotdotdot() { "driver" => [(vr "qns")], "body" => {unquote_form(n("Expr"), pos, 1) ; "body" => (vr "qns")}})]})}), - env.clone(), qenv.clone()), + env.clone(), + qenv.clone() + ), Ok(ty!({"Type" "type_apply" : - "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]}))); - without_freshening!{ + "type_rator" => (,expr_type.clone()), "arg" => [{"Type" "Nat" :}]})) + ); + without_freshening! { assert_eq!(eval_two_phased( &ast!({quote(pos) ; "nt" => (vr "Expr"), "body" => (++ true {"Expr" "match" : @@ -983,12 +1150,9 @@ fn use_dotdotdot() { "arm" => [@"c" (import ["p" = "scrutinee"] (vr "qn")), (import ["p" = "scrutinee"] (vr "qnn"))]}))); } - } // TODO: // '[Expr | ( ,[Expr | could_be_anything ], five)]' #[test] -fn subtyping_under_negative_quote() { - -} +fn subtyping_under_negative_quote() {} diff --git a/src/core_type_forms.rs b/src/core_type_forms.rs index 2100d5d..43d7802 100644 --- a/src/core_type_forms.rs +++ b/src/core_type_forms.rs @@ -10,7 +10,7 @@ * Therefore, I used `enum` and `struct` instead of × and +. */ - /* +/* There are two similar things we should distinguish! (1) syntax for types, as written by the user in an `Ast` (2) types themselves, the result of type synthesis, often stored in `Ty` @@ -46,19 +46,19 @@ It is at this point that I am reminded of a passage: — Douglas Hofstadter, Gödel, Escher, Bach: an Eternal Golden Braid */ -use std::rc::Rc; -use grammar::{SynEnv, FormPat}; -use form::{Form, simple_form, BiDiWR, Positive, Both}; -use grammar::FormPat::*; -use ast_walk::{WalkRule, walk}; +use ast::*; use ast_walk::WalkRule::*; -use walk_mode::{WalkMode, NegativeWalkMode}; -use name::*; +use ast_walk::{walk, WalkRule}; use core_forms::{ast_to_name, vr_to_name}; -use ty::{Ty, synth_type, TyErr, SynthTy}; +use form::{simple_form, BiDiWR, Both, Form, Positive}; +use grammar::FormPat::*; +use grammar::{FormPat, SynEnv}; +use name::*; +use std::rc::Rc; +use ty::{synth_type, SynthTy, Ty, TyErr}; use ty_compare::{Canonicalize, Subtype}; -use ast::*; -use ::util::assoc::Assoc; +use util::assoc::Assoc; +use walk_mode::{NegativeWalkMode, WalkMode}; //TODO #3: I think we need to extend `Form` with `synth_kind`... pub fn type_defn(form_name: &str, p: FormPat) -> Rc { @@ -68,19 +68,23 @@ pub fn type_defn(form_name: &str, p: FormPat) -> Rc { type_compare: Both(LiteralLike, LiteralLike), synth_type: Positive(LiteralLike), quasiquote: Both(LiteralLike, LiteralLike), - eval: Positive(NotWalked) + eval: Positive(NotWalked), }) } -fn type_defn_complex(form_name: &str, p: FormPat, sy: WalkRule, - tc: BiDiWR) -> Rc { +fn type_defn_complex( + form_name: &str, + p: FormPat, + sy: WalkRule, + tc: BiDiWR, +) -> Rc { Rc::new(Form { name: n(form_name), grammar: Rc::new(p), type_compare: tc, synth_type: Positive(sy), quasiquote: Both(LiteralLike, LiteralLike), - eval: Positive(NotWalked) + eval: Positive(NotWalked), }) } @@ -101,122 +105,151 @@ pub fn get__abstract_parametric_type() -> Rc { abstract_parametric_type.with(|a_p_t| a_p_t.clone()) } - pub fn make_core_syn_env_types() -> SynEnv { /* Regarding the value/type/kind hierarchy, Benjamin Pierce generously assures us that - "For programming languages ... three levels have proved sufficient." */ + "For programming languages ... three levels have proved sufficient." */ /* kinds */ let _type_kind = simple_form("Type", form_pat!((lit "*"))); - let _higher_kind = simple_form("higher", form_pat!( + let _higher_kind = simple_form( + "higher", + form_pat!( (delim "k[", "[", - [ (star (named "param", (call "kind"))), (lit "->"), (named "res", (call "kind"))]))); - + [ (star (named "param", (call "kind"))), (lit "->"), (named "res", (call "kind"))])), + ); /* types */ - let fn_type = - type_defn_complex("fn", - form_pat!((delim "[", "[", + let fn_type = type_defn_complex( + "fn", + form_pat!((delim "[", "[", [ (star (named "param", (call "Type"))), (lit "->"), (named "ret", (call "Type") ) ])), - LiteralLike, // synth is normal - Both(LiteralLike, - cust_rc_box!(move |fn_parts| { - let actual = fn_parts.context_elt().concrete(); - let actual_parts = Subtype::context_match( - &fn_parts.this_ast, &actual, fn_parts.env.clone())?; - - let expd_params = fn_parts.get_rep_term(n("param")); - let actl_params = actual_parts.get_rep_leaf_or_panic(n("param")); - if expd_params.len() != actl_params.len() { - return Err(TyErr::LengthMismatch( - actl_params.iter().map(|&a| Ty(a.clone())).collect(), - expd_params.len())); - } - for (p_expected, p_got) in expd_params.iter().zip(actl_params.iter()) { - // Parameters have reversed subtyping: - let _ : ::util::assoc::Assoc = walk::( - *p_got, &fn_parts.with_context(Ty::new(p_expected.clone())))?; - } - - walk::(&fn_parts.get_term(n("ret")), - &fn_parts.with_context( - Ty::new(actual_parts.get_leaf_or_panic(&n("ret")).clone()))) - })) - ); + LiteralLike, // synth is normal + Both( + LiteralLike, + cust_rc_box!(move |fn_parts| { + let actual = fn_parts.context_elt().concrete(); + let actual_parts = + Subtype::context_match(&fn_parts.this_ast, &actual, fn_parts.env.clone())?; + + let expd_params = fn_parts.get_rep_term(n("param")); + let actl_params = actual_parts.get_rep_leaf_or_panic(n("param")); + if expd_params.len() != actl_params.len() { + return Err(TyErr::LengthMismatch( + actl_params.iter().map(|&a| Ty(a.clone())).collect(), + expd_params.len(), + )); + } + for (p_expected, p_got) in expd_params.iter().zip(actl_params.iter()) { + // Parameters have reversed subtyping: + let _: ::util::assoc::Assoc = walk::( + *p_got, + &fn_parts.with_context(Ty::new(p_expected.clone())), + )?; + } - let enum_type = - type_defn("enum", form_pat!([(lit "enum"), + walk::( + &fn_parts.get_term(n("ret")), + &fn_parts + .with_context(Ty::new(actual_parts.get_leaf_or_panic(&n("ret")).clone())), + ) + }), + ), + ); + + let enum_type = type_defn( + "enum", + form_pat!([(lit "enum"), (delim "{", "{", (star [(named "name", aat), - (delim "(", "(", (star (named "component", (call "Type"))))]))])); + (delim "(", "(", (star (named "component", (call "Type"))))]))]), + ); - let struct_type = - type_defn_complex("struct", form_pat!( + let struct_type = type_defn_complex( + "struct", + form_pat!( [(lit "struct"), (delim "{", "{", (star [(named "component_name", aat), (lit ":"), (named "component", (call "Type"))]))]), - LiteralLike, // synth is normal - Both( - LiteralLike, - cust_rc_box!(move |struct_parts| { - let actual_struct_parts = Subtype::context_match(&struct_parts.this_ast, - &struct_parts.context_elt().concrete(), struct_parts.env.clone())?; - - for (got_name, got_ty) in - actual_struct_parts.get_rep_leaf_or_panic(n("component_name")).iter() - .zip(actual_struct_parts.get_rep_leaf_or_panic(n("component"))) { - let mut found = false; - for (exp_name, exp_ty) in - struct_parts.get_rep_term(n("component_name")).iter() - .zip(struct_parts.get_rep_term(n("component"))) { - if ast_to_name(got_name) != ast_to_name(exp_name) { - continue; - } - found = true; - let _ = walk::( - &got_ty, &struct_parts.with_context(Ty(exp_ty.clone())))?; - } - if !found { - return Err(TyErr::NonexistentStructField( - ast_to_name(&got_name), struct_parts.context_elt().clone())); + LiteralLike, // synth is normal + Both( + LiteralLike, + cust_rc_box!(move |struct_parts| { + let actual_struct_parts = Subtype::context_match( + &struct_parts.this_ast, + &struct_parts.context_elt().concrete(), + struct_parts.env.clone(), + )?; + + for (got_name, got_ty) in actual_struct_parts + .get_rep_leaf_or_panic(n("component_name")) + .iter() + .zip(actual_struct_parts.get_rep_leaf_or_panic(n("component"))) + { + let mut found = false; + for (exp_name, exp_ty) in struct_parts + .get_rep_term(n("component_name")) + .iter() + .zip(struct_parts.get_rep_term(n("component"))) + { + if ast_to_name(got_name) != ast_to_name(exp_name) { + continue; } + found = true; + let _ = walk::( + &got_ty, + &struct_parts.with_context(Ty(exp_ty.clone())), + )?; + } + if !found { + return Err(TyErr::NonexistentStructField( + ast_to_name(&got_name), + struct_parts.context_elt().clone(), + )); } + } - Ok(assoc_n!()) - }))); + Ok(assoc_n!()) + }), + ), + ); - let tuple_type = - type_defn("tuple", - form_pat!((delim "**[", "[", (star (named "component", (call "Type")))))); + let tuple_type = type_defn( + "tuple", + form_pat!((delim "**[", "[", (star (named "component", (call "Type"))))), + ); - let forall_type = - type_defn_complex("forall_type", - form_pat!([(lit "forall"), (star (named "param", aat)), (lit "."), + let forall_type = type_defn_complex( + "forall_type", + form_pat!([(lit "forall"), (star (named "param", aat)), (lit "."), (named "body", (import [* [forall "param"]], (call "Type")))]), - LiteralLike, // synth is normal - Both( - LiteralLike, - cust_rc_box!(move |forall_parts| { - match Subtype::context_match( - &forall_parts.this_ast, - &forall_parts.context_elt().concrete(), - forall_parts.env.clone()) { - // ∀ X. ⋯ <: ∀ Y. ⋯ ? (so force X=Y) - Ok(actual_forall_parts) => { - let actl_inner_body = - actual_forall_parts.get_leaf_or_panic(&n("body")); - - walk::(&forall_parts.get_term(n("body")), - &forall_parts.with_context(Ty::new(actl_inner_body.clone()))) - } - // ∀ X. ⋯ <: ⋯ ? (so try to specialize X) - Err(_) => { - // `import [forall "param"]` handles the specialization, - // and we leave the context element alone - walk::(&forall_parts.get_term(n("body")), &forall_parts) - } + LiteralLike, // synth is normal + Both( + LiteralLike, + cust_rc_box!(move |forall_parts| { + match Subtype::context_match( + &forall_parts.this_ast, + &forall_parts.context_elt().concrete(), + forall_parts.env.clone(), + ) { + // ∀ X. ⋯ <: ∀ Y. ⋯ ? (so force X=Y) + Ok(actual_forall_parts) => { + let actl_inner_body = actual_forall_parts.get_leaf_or_panic(&n("body")); + + walk::( + &forall_parts.get_term(n("body")), + &forall_parts.with_context(Ty::new(actl_inner_body.clone())), + ) + } + // ∀ X. ⋯ <: ⋯ ? (so try to specialize X) + Err(_) => { + // `import [forall "param"]` handles the specialization, + // and we leave the context element alone + walk::(&forall_parts.get_term(n("body")), &forall_parts) } - }))); + } + }), + ), + ); /* This behaves slightly differently than the `mu` from Pierce's book, * because we need to support mutual recursion. @@ -224,10 +257,10 @@ pub fn make_core_syn_env_types() -> SynEnv { * The only thing that `mu` actually does is suppress substitution, * to prevent the attempted generation of an infinite type. */ - let mu_type = type_defn_complex("mu_type", + let mu_type = type_defn_complex( + "mu_type", form_pat!([(lit "mu_type"), (star (named "param", (import [prot "param"], varref))), (lit "."), (named "body", (import [* [prot "param"]], (call "Type")))]), - LiteralLike, Both( LiteralLike, @@ -235,7 +268,8 @@ pub fn make_core_syn_env_types() -> SynEnv { let rhs_mu_parts = Subtype::context_match( &mu_parts.this_ast, &mu_parts.context_elt().concrete(), - mu_parts.env.clone())?; + mu_parts.env.clone(), + )?; let rhs_body = rhs_mu_parts.get_leaf_or_panic(&n("body")); @@ -243,29 +277,37 @@ pub fn make_core_syn_env_types() -> SynEnv { let l_params = mu_parts.get_rep_term(n("param")); if r_params.len() != l_params.len() { return Err(TyErr::LengthMismatch( - r_params.iter().map(|a| Ty((*a).clone())).collect(), l_params.len())); + r_params.iter().map(|a| Ty((*a).clone())).collect(), + l_params.len(), + )); } // Apply the Amber rule; assume the `mu`ed names are subtypes to subtype the bodies let mut amber_environment = mu_parts.env.clone(); for (&ee_r, ee_l) in r_params.iter().zip(l_params.iter()) { - let (p_r, p_l) = if let (ExtendEnv(r,_), ExtendEnv(l, _)) = (ee_r, ee_l) { + let (p_r, p_l) = if let (ExtendEnv(r, _), ExtendEnv(l, _)) = (ee_r, ee_l) { (&**r, &**l) } else { panic!("ICE: ill-formed mu_type") }; if p_r == p_l // short-circuit if the names are the same... || mu_parts.env.find(&vr_to_name(&*p_r)) // ...or Amber assumed so already - == Some(&Ty(p_l.clone())) { continue; } + == Some(&Ty(p_l.clone())) + { + continue; + } - amber_environment = amber_environment - .set(vr_to_name(p_r), Ty(p_l.clone())); + amber_environment = amber_environment.set(vr_to_name(p_r), Ty(p_l.clone())); } - walk::(&mu_parts.get_term(n("body")), - &mu_parts.with_environment(amber_environment) - .with_context(Ty::new(rhs_body.clone()))) - }))); - + walk::( + &mu_parts.get_term(n("body")), + &mu_parts + .with_environment(amber_environment) + .with_context(Ty::new(rhs_body.clone())), + ) + }), + ), + ); // This only makes sense inside a concrete syntax type or during typechecking. // For example, the type of the `let` macro is (where `dotdotdot_type` is `:::[]:::`): @@ -278,26 +320,29 @@ pub fn make_core_syn_env_types() -> SynEnv { // TODO: we'll need dotdotdot inside betas, also, huh? // Note that we shouldn't try a custom subtyping implementation here; // `match_dotdotdot` eliminates this form entirely. (TODO #13) - let dotdotdot_type = type_defn("dotdotdot", + let dotdotdot_type = type_defn( + "dotdotdot", form_pat!((delim ":::[", "[", [(star (named "driver", varref)), (lit ">>"), - (named "body", (call "Type"))]) )); + (named "body", (call "Type"))]) ), + ); let forall_type_0 = forall_type.clone(); - /* [Type theory alert!] - * Pierce's notion of type application is an expression, not a type; - * you just take an expression whose type is a `forall`, and then give it some arguments. - * Instead, we will just make the type system unify `forall` types with more specific types. - * But sometimes the user wants to write a more specific type, and they use this. - * - * This is, at the type level, like function application. - * We restrict the LHS to being a name, because that's "normal". Should we? - */ - let type_apply = type_defn_complex("type_apply", + /* [Type theory alert!] + * Pierce's notion of type application is an expression, not a type; + * you just take an expression whose type is a `forall`, and then give it some arguments. + * Instead, we will just make the type system unify `forall` types with more specific types. + * But sometimes the user wants to write a more specific type, and they use this. + * + * This is, at the type level, like function application. + * We restrict the LHS to being a name, because that's "normal". Should we? + */ + let type_apply = type_defn_complex( + "type_apply", // The technical term for `<[...]<` is "fish X-ray" form_pat!([(named "type_rator", (call "Type")), (delim "<[", "[", (star [(named "arg", (call "Type"))]))]), - // TODO: shouldn't it be "args"? + // TODO: shouldn't it be "args"? cust_rc_box!(move |tapp_parts| { let arg_res = tapp_parts.get_rep_res(n("arg"))?; let rator_res = tapp_parts.get_res(n("type_rator"))?; @@ -310,43 +355,53 @@ pub fn make_core_syn_env_types() -> SynEnv { // we wish to avoid aliasing problems at the type level. // In System F, this is avoided by performing capture-avoiding substitution. let mut new__tapp_parts = ::util::mbe::EnvMBE::new_from_leaves( - assoc_n!("type_rator" => VariableReference(rator_vr))); + assoc_n!("type_rator" => VariableReference(rator_vr)), + ); let mut args = vec![]; for individual__arg_res in arg_res { args.push(::util::mbe::EnvMBE::new_from_leaves( - assoc_n!("arg" => individual__arg_res.concrete()))); + assoc_n!("arg" => individual__arg_res.concrete()), + )); } new__tapp_parts.add_anon_repeat(args, None); if let Node(ref f, _, ref exp) = tapp_parts.this_ast { - Ok(Ty::new(Node(/*forall*/ f.clone(), new__tapp_parts, exp.clone()))) + Ok(Ty::new(Node( + /*forall*/ f.clone(), + new__tapp_parts, + exp.clone(), + ))) } else { panic!("ICE") } } Node(ref got_f, ref lhs_parts, ref exports) - if got_f == &get__abstract_parametric_type() => { + if got_f == &get__abstract_parametric_type() => + { // Like the above; don't descend into `Expr` - let mut new__tapp_parts = ::util::mbe::EnvMBE::new_from_leaves( - assoc_n!("type_rator" => + let mut new__tapp_parts = + ::util::mbe::EnvMBE::new_from_leaves(assoc_n!("type_rator" => Node(got_f.clone(), lhs_parts.clone(), exports.clone()))); let mut args = vec![]; for individual__arg_res in arg_res { args.push(::util::mbe::EnvMBE::new_from_leaves( - assoc_n!("arg" => individual__arg_res.concrete()))); + assoc_n!("arg" => individual__arg_res.concrete()), + )); } new__tapp_parts.add_anon_repeat(args, None); if let Node(ref f, _, ref exp) = tapp_parts.this_ast { - Ok(Ty::new(Node(/*forall*/ f.clone(), new__tapp_parts, exp.clone()))) + Ok(Ty::new(Node( + /*forall*/ f.clone(), + new__tapp_parts, + exp.clone(), + ))) } else { panic!("ICE") } - } - Node(ref got_f, ref forall_type__parts, _) - if got_f == &forall_type_0 => { + Node(ref got_f, ref forall_type__parts, _) if got_f == &forall_type_0 => { // This might ought to be done by a specialized `beta`... let params = forall_type__parts.get_rep_leaf_or_panic(n("param")); if params.len() != arg_res.len() { @@ -358,9 +413,10 @@ pub fn make_core_syn_env_types() -> SynEnv { } // This bypasses the binding in the type, which is what we want: - synth_type(::core_forms::strip_ee( - forall_type__parts.get_leaf_or_panic(&n("body"))), - new__ty_env) + synth_type( + ::core_forms::strip_ee(forall_type__parts.get_leaf_or_panic(&n("body"))), + new__ty_env, + ) } _ => { @@ -368,7 +424,8 @@ pub fn make_core_syn_env_types() -> SynEnv { } } }), - Both(LiteralLike, LiteralLike)); + Both(LiteralLike, LiteralLike), + ); assoc_n!("Type" => Rc::new(Biased(Rc::new(forms_to_form_pat![ fn_type.clone(), @@ -386,7 +443,6 @@ pub fn make_core_syn_env_types() -> SynEnv { ]), Rc::new(VarRef)))) } - // TODO #4: this should be extensible for when the syntax environment is extended... // or just automatically have one type per NT. Probably the latter. pub fn nt_to_type(nt: Name) -> Ty { @@ -445,16 +501,15 @@ pub fn more_quoted_ty(t: &Ty, nt: Name) -> Ty { #[test] fn parametric_types() { - // Are plain parametric types valid? without_freshening! { // (so we don't have to compute alpha-equivalence) - assert_eq!( - synth_type(&ast!({"Type" "forall_type" : "param" => ["t"], - "body" => (import [* [forall "param"]] (vr "t"))}), - Assoc::new()), - Ok(ty!({"Type" "forall_type" : "param" => ["t"], - "body" => (import [* [forall "param"]] (vr "t"))}))); - } + assert_eq!( + synth_type(&ast!({"Type" "forall_type" : "param" => ["t"], + "body" => (import [* [forall "param"]] (vr "t"))}), + Assoc::new()), + Ok(ty!({"Type" "forall_type" : "param" => ["t"], + "body" => (import [* [forall "param"]] (vr "t"))}))); + } let ident_ty = ty!( { "Type" "Ident" : }); let nat_ty = ty!( { "Type" "Nat" : }); @@ -473,34 +528,43 @@ fn parametric_types() { let mued_ty_env = assoc_n!("unary" => ty!((vr "unary")), "binary" => ty!((vr "binary"))); // If `unary` is `mu`ed, `unary <[ ident ]<` can't be simplified. - assert_eq!(synth_type( - &ast!( { "Type" "type_apply" : + assert_eq!( + synth_type( + &ast!( { "Type" "type_apply" : "type_rator" => (vr "unary"), "arg" => [ (, ident_ty.concrete()) ]}), - mued_ty_env.clone()), + mued_ty_env.clone() + ), Ok(ty!({ "Type" "type_apply" : "type_rator" => (vr "unary"), - "arg" => [ (, ident_ty.concrete()) ]}))); + "arg" => [ (, ident_ty.concrete()) ]})) + ); // If `unary` is `mu`ed, `unary <[ [nat -> nat] ]<` can't be simplified. - assert_eq!(synth_type( - &ast!( { "Type" "type_apply" : + assert_eq!( + synth_type( + &ast!( { "Type" "type_apply" : "type_rator" => (vr "unary"), "arg" => [ { "Type" "fn" : "param" => [(, nat_ty.concrete())], "ret" => (, nat_ty.concrete())} ]}), - mued_ty_env.clone()), + mued_ty_env.clone() + ), Ok(ty!({ "Type" "type_apply" : "type_rator" => (vr "unary"), "arg" => [ { "Type" "fn" : - "param" => [(, nat_ty.concrete())], "ret" => (, nat_ty.concrete())} ]}))); + "param" => [(, nat_ty.concrete())], "ret" => (, nat_ty.concrete())} ]})) + ); // Expand the definition of `unary`. - assert_eq!(synth_type( - &ast!( { "Type" "type_apply" : + assert_eq!( + synth_type( + &ast!( { "Type" "type_apply" : "type_rator" => (vr "unary"), "arg" => [ (, ident_ty.concrete()) ]}), - para_ty_env), + para_ty_env + ), Ok(ty!({ "Type" "fn" : "param" => [(, nat_ty.concrete() )], - "ret" => (, ident_ty.concrete())}))); + "ret" => (, ident_ty.concrete())})) + ); } diff --git a/src/earley.rs b/src/earley.rs index bb31ba1..f3c329c 100644 --- a/src/earley.rs +++ b/src/earley.rs @@ -16,16 +16,16 @@ // // Also, it turns out that implementing an Earley parser goes pretty smoothly. Yay! -use grammar::{FormPat, SynEnv}; -use grammar::FormPat::*; +use ast::Ast; use grammar::plug_hole; -use read::{Token, TokenTree}; +use grammar::FormPat::*; +use grammar::{FormPat, SynEnv}; +use name::*; use read::Token::*; -use ast::Ast; -use std::rc::Rc; +use read::{Token, TokenTree}; use std::cell::RefCell; -use name::*; use std::collections::HashMap; +use std::rc::Rc; pub fn end_of_delim() -> Token { Token::Simple(n("☾end delimiter☽")) @@ -54,7 +54,7 @@ fn get_next_id() -> UniqueId { } // Specifically *not* `Clone` or `Copy` -#[derive(PartialEq,Eq)] +#[derive(PartialEq, Eq)] pub struct UniqueId(u32); impl ::std::fmt::Debug for UniqueId { @@ -63,13 +63,17 @@ impl ::std::fmt::Debug for UniqueId { } } -#[derive(PartialEq,Eq,Clone,Copy,Hash)] +#[derive(PartialEq, Eq, Clone, Copy, Hash)] pub struct UniqueIdRef(u32); impl UniqueId { - fn get_ref(&self) -> UniqueIdRef { UniqueIdRef(self.0) } + fn get_ref(&self) -> UniqueIdRef { + UniqueIdRef(self.0) + } - fn is(&self, other: UniqueIdRef) -> bool { self.0 == other.0 } + fn is(&self, other: UniqueIdRef) -> bool { + self.0 == other.0 + } } impl ::std::fmt::Debug for UniqueIdRef { @@ -80,10 +84,14 @@ impl ::std::fmt::Debug for UniqueIdRef { /// Hardcoded `nt` name for the dotdotdot form; it can be used anywhere under a + or *. /// TODO: this shouldn't be hardcoded! -pub fn special_ddd_nt() -> Name { n("dotdotdot") } +pub fn special_ddd_nt() -> Name { + n("dotdotdot") +} /// Used as a HACK to mark nodes that should be DDDed when put into an MBE -fn ddd_ast_marker() -> Name { n("⌜⋯⌟") } // TODO: gensym +fn ddd_ast_marker() -> Name { + n("⌜⋯⌟") +} // TODO: gensym fn ddd_wrap(a: Ast) -> Ast { Ast::Shape(vec![a, Ast::Atom(ddd_ast_marker())]) @@ -92,19 +100,27 @@ fn ddd_wrap(a: Ast) -> Ast { /// If `a` is a specially-marked DDD node, remove the special marker (and return true) fn ddd_unwrap(a: &Ast) -> Option { match a { - Ast::Shape(ref subs) if subs.len() == 2 && subs[1] == Ast::Atom(ddd_ast_marker()) => - Some(subs[0].clone()), - _ => None + Ast::Shape(ref subs) if subs.len() == 2 && subs[1] == Ast::Atom(ddd_ast_marker()) => { + Some(subs[0].clone()) + } + _ => None, } } - // TODO: this shouldn't be hardcoded into the parser; it should be ... how should it work? // (Maybe the `Biased`s in the grammar take care of this now?) fn reserved(nm: Name) -> bool { - Simple(nm) == end_of_delim() || nm == n("forall") || nm == n("mu_type") - || nm == n("Int") || nm == n("Ident") || nm == n("Float") || nm == n("match") - || nm == n("enum") || nm == n("struct") || nm == n("fold") || nm == n("unfold") + Simple(nm) == end_of_delim() + || nm == n("forall") + || nm == n("mu_type") + || nm == n("Int") + || nm == n("Ident") + || nm == n("Float") + || nm == n("match") + || nm == n("enum") + || nm == n("struct") + || nm == n("fold") + || nm == n("unfold") } // Hey, this doesn't need to be Reifiable! @@ -121,8 +137,6 @@ pub struct Item { ddd: bool, // Everything after this line is nonstandard, and is just here as an optimization - - /// Identity for the purposes of `wanted_by` and `local_parse` id: UniqueId, @@ -130,7 +144,6 @@ pub struct Item { /// This is mostly a convenience. done: RefCell, // Can this complete things that have asked for it? - /// For simple (leaf) nodes, we just store the resulting parse right away. /// This is a convenience so that extracting the parse tree /// doesn't require trawling through the token input @@ -149,12 +162,12 @@ pub struct Item { /// This, uh, might be an excessively cocky design. /// But searching item lists is *hard* when your Earley parser /// has so many different kings of rules! - wanted_by: Rc>> + wanted_by: Rc>>, } /// Information for parsing. It's not a parse tree, but it tells you the next step to get one. /// (Hence "local") -#[derive(PartialEq,Debug,Clone)] +#[derive(PartialEq, Debug, Clone)] enum LocalParse { /// ⊤; no information yet NothingYet, @@ -162,7 +175,7 @@ enum LocalParse { JustifiedByItem(UniqueIdRef), ParsedAtom(Ast), /// ⊥; contradiction (TMI!) - Ambiguous(Box, Box) + Ambiguous(Box, Box), } use self::LocalParse::*; @@ -172,16 +185,18 @@ impl PartialOrd for LocalParse { /// But there's also `NothingYet`, for ... (TODO: only leaves and just-started nodes?) /// ... and `Ambiguous`, when we know that there are multiple justifications for a single node fn partial_cmp(&self, other: &LocalParse) -> Option<::std::cmp::Ordering> { - use ::std::cmp::Ordering::*; - if self == other { return Some(Equal) } + use std::cmp::Ordering::*; + if self == other { + return Some(Equal); + } match (self, other) { - (&NothingYet, _) | (_, &Ambiguous(_,_)) => Some(Less), - (&Ambiguous(_,_), _) | (_, &NothingYet) => Some(Greater), + (&NothingYet, _) | (_, &Ambiguous(_, _)) => Some(Less), + (&Ambiguous(_, _), _) | (_, &NothingYet) => Some(Greater), (&JustifiedByItemPlanB(_), &JustifiedByItem(_)) => Some(Less), (&JustifiedByItem(_), &JustifiedByItemPlanB(_)) => Some(Greater), (&JustifiedByItem(_), &JustifiedByItem(_)) => None, // semantically, this ought to be `None`, but that would be a hard-to-debug logic error - _ => { panic!("Attempted to compare {:#?} and {:#?}", self, other) } + _ => panic!("Attempted to compare {:#?} and {:#?}", self, other), } } } @@ -197,24 +212,30 @@ impl Clone for Item { id: get_next_id(), done: self.done.clone(), local_parse: RefCell::new(LocalParse::NothingYet), - wanted_by: self.wanted_by.clone() + wanted_by: self.wanted_by.clone(), } } } /// Progress through the state sets // TODO: this ought to produce an Option, not a bool! -fn create_chart(rule: Rc, grammar: SynEnv, tt: &TokenTree) - -> (UniqueId, Vec>) { +fn create_chart(rule: Rc, grammar: SynEnv, tt: &TokenTree) -> (UniqueId, Vec>) { let mut chart = vec![vec![]]; let mut cur_tok = 0; let start_but_startier = get_next_id(); - let start_item = - Item{start_idx: 0, rule: rule, pos: 0, grammar: grammar, ddd: false, id: get_next_id(), - done: RefCell::new(false), local_parse: RefCell::new(LocalParse::NothingYet), - wanted_by: Rc::new(RefCell::new(vec![start_but_startier.get_ref()]))}; + let start_item = Item { + start_idx: 0, + rule: rule, + pos: 0, + grammar: grammar, + ddd: false, + id: get_next_id(), + done: RefCell::new(false), + local_parse: RefCell::new(LocalParse::NothingYet), + wanted_by: Rc::new(RefCell::new(vec![start_but_startier.get_ref()])), + }; chart[0].push(start_item); for t in &tt.t { @@ -228,11 +249,12 @@ fn create_chart(rule: Rc, grammar: SynEnv, tt: &TokenTree) fn recognize(rule: &FormPat, grammar: &SynEnv, tt: &TokenTree) -> bool { let (start_but_startier, chart) = create_chart(Rc::new(rule.clone()), grammar.clone(), tt); - chart[chart.len()-1].iter().any( - |item| - (*item.wanted_by.borrow()).iter().any(|idr| start_but_startier.is(*idr)) + chart[chart.len() - 1].iter().any(|item| { + (*item.wanted_by.borrow()) + .iter() + .any(|idr| start_but_startier.is(*idr)) && *item.done.borrow() - ) + }) } fn walk_tt(chart: &mut Vec>, t: &Token, cur_tok: &mut usize) { @@ -241,7 +263,7 @@ fn walk_tt(chart: &mut Vec>, t: &Token, cur_tok: &mut usize) { //log!("\n {:#?}\n->{:#?}\n", chart[*cur_tok], chart[*cur_tok + 1]); *cur_tok += 1; match *t { - Simple(_) => { } + Simple(_) => {} Group(_, _, ref tree) => { for sub_tok in &tree.t { walk_tt(chart, sub_tok, cur_tok); @@ -257,16 +279,21 @@ fn examine_state_set(chart: &mut Vec>, tok: Option<&Token>, cur_tok: u // If nullable items are statically identified, I think there's an optimization // where we don't re-walk old items loop { - if ! new_items_from_state_set(chart, tok, cur_tok) { break; } // reached the fixpoint? + if !new_items_from_state_set(chart, tok, cur_tok) { + break; + } // reached the fixpoint? } } -fn new_items_from_state_set(chart: &mut Vec>, tok: Option<&Token>, - cur_tok: usize) -> bool { +fn new_items_from_state_set( + chart: &mut Vec>, + tok: Option<&Token>, + cur_tok: usize, +) -> bool { let mut effect = false; for idx in 0..chart[cur_tok].len() { for (new_item, next) in chart[cur_tok][idx].examine(tok, cur_tok, chart) { - effect = merge_into_state_set(new_item, &mut chart[cur_tok + if next {1} else {0}]) + effect = merge_into_state_set(new_item, &mut chart[cur_tok + if next { 1 } else { 0 }]) || effect; } } @@ -274,11 +301,12 @@ fn new_items_from_state_set(chart: &mut Vec>, tok: Option<&Token>, } // Returns whether anything happened -fn merge_into_state_set(item: Item, items: &mut Vec) - -> bool { +fn merge_into_state_set(item: Item, items: &mut Vec) -> bool { for i in items.iter() { if i.similar(&item) { - if i.as_good_as(&item) { return false; /* no new information */ } + if i.as_good_as(&item) { + return false; /* no new information */ + } log!("improved item: {:#?} vs. {:#?}\n", item, i); i.merge(&item); return true; @@ -292,10 +320,17 @@ fn merge_into_state_set(item: Item, items: &mut Vec) impl ::std::fmt::Debug for Item { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "[({:#?}){}({:#?}.{}){}<{:#?} - {:#?}>]", - self.id, self.start_idx, self.rule, self.pos, - if *self.done.borrow() { "✓" } else { "…" }, - self.local_parse.borrow(), self.wanted_by.borrow()) + write!( + f, + "[({:#?}){}({:#?}.{}){}<{:#?} - {:#?}>]", + self.id, + self.start_idx, + self.rule, + self.pos, + if *self.done.borrow() { "✓" } else { "…" }, + self.local_parse.borrow(), + self.wanted_by.borrow() + ) } } @@ -305,10 +340,10 @@ impl Item { /// because those should be merged. fn similar<'f>(&'f self, other: &'f Item) -> bool { self.start_idx == other.start_idx - && &*self.rule as *const FormPat == &*other.rule as *const FormPat - && self.pos == other.pos - && self.grammar.almost_ptr_eq(&other.grammar) - && self.ddd == other.ddd + && &*self.rule as *const FormPat == &*other.rule as *const FormPat + && self.pos == other.pos + && self.grammar.almost_ptr_eq(&other.grammar) + && self.ddd == other.ddd } /// `false` if `other` might provide new information @@ -324,20 +359,31 @@ impl Item { } fn merge(&self, other: &Item) { - if *other.done.borrow() { *self.done.borrow_mut() = true; } + if *other.done.borrow() { + *self.done.borrow_mut() = true; + } - use ::std::cmp::Ordering::*; - let comparison = other.local_parse.borrow().partial_cmp(&*self.local_parse.borrow()); - log!("\n(For {}) Merging {:#?} and {:#?}... ", self.id.0, *other.local_parse.borrow(), - *self.local_parse.borrow()); + use std::cmp::Ordering::*; + let comparison = other + .local_parse + .borrow() + .partial_cmp(&*self.local_parse.borrow()); + log!( + "\n(For {}) Merging {:#?} and {:#?}... ", + self.id.0, + *other.local_parse.borrow(), + *self.local_parse.borrow() + ); match comparison { Some(Greater) => { *self.local_parse.borrow_mut() = other.local_parse.borrow().clone(); - }, - Some(Equal) | Some(Less) => { /* no new information */ }, + } + Some(Equal) | Some(Less) => { /* no new information */ } None => { - let amb = LocalParse::Ambiguous(Box::new(self.local_parse.borrow().clone()), - Box::new(other.local_parse.borrow().clone())); + let amb = LocalParse::Ambiguous( + Box::new(self.local_parse.borrow().clone()), + Box::new(other.local_parse.borrow().clone()), + ); *self.local_parse.borrow_mut() = amb; } } @@ -347,7 +393,8 @@ impl Item { let mut has = false; for self_wanted in self.wanted_by.borrow().iter() { if self_wanted == other_wanted { - has = true; break; + has = true; + break; } } @@ -363,51 +410,84 @@ impl Item { fn advance(&self, consume_tok: bool) -> Vec<(Item, bool)> { log!("[adv ({})]", self.id.0); - vec![(Item{ pos: self.pos + 1, .. self.clone() }, - consume_tok)] + vec![( + Item { + pos: self.pos + 1, + ..self.clone() + }, + consume_tok, + )] } fn finish_with(&self, lp: LocalParse, consume_tok: bool) -> Vec<(Item, bool)> { log!("[fin_w/ ({})]", self.id.0); - vec![(Item{ done: RefCell::new(true), local_parse: RefCell::new(lp), - pos: self.pos + 1, .. self.clone() }, - consume_tok)] + vec![( + Item { + done: RefCell::new(true), + local_parse: RefCell::new(lp), + pos: self.pos + 1, + ..self.clone() + }, + consume_tok, + )] } fn finish(&self, consume_tok: bool) -> Vec<(Item, bool)> { log!("[finish ({})]", self.id.0); - vec![(Item{ done: RefCell::new(true), local_parse: RefCell::new(LocalParse::NothingYet), - pos: self.pos + 1, .. self.clone() }, - consume_tok)] + vec![( + Item { + done: RefCell::new(true), + local_parse: RefCell::new(LocalParse::NothingYet), + pos: self.pos + 1, + ..self.clone() + }, + consume_tok, + )] } fn start(&self, rule: &Rc, cur_idx: usize, allow_ddd: bool) -> Vec<(Item, bool)> { log!("[start ({})]", self.id.0); - let mut res = vec![(Item { - start_idx: cur_idx, rule: rule.clone(), pos: 0, done: RefCell::new(false), - grammar: self.grammar.clone(), ddd: false, - local_parse: RefCell::new(LocalParse::NothingYet), id: get_next_id(), - wanted_by: Rc::new(RefCell::new(vec![self.id.get_ref()])) + let mut res = vec![( + Item { + start_idx: cur_idx, + rule: rule.clone(), + pos: 0, + done: RefCell::new(false), + grammar: self.grammar.clone(), + ddd: false, + local_parse: RefCell::new(LocalParse::NothingYet), + id: get_next_id(), + wanted_by: Rc::new(RefCell::new(vec![self.id.get_ref()])), }, - false)]; + false, + )]; if allow_ddd { if let Some(ref ddd_grammar) = self.grammar.find(&special_ddd_nt()) { - // memoize the plugging process for speed, // and because we need the results to be pointer-equal let ddd_rule = all_plugged_ddds.with(|plugged_ddds| { // (keyed on ID rather than grammars because grammars aren't `Eq`) let mut mut__plugged_ddds = plugged_ddds.borrow_mut(); - mut__plugged_ddds.entry(self.id.get_ref()) - .or_insert_with(|| plug_hole(ddd_grammar, n("body"), &rule)).clone() + mut__plugged_ddds + .entry(self.id.get_ref()) + .or_insert_with(|| plug_hole(ddd_grammar, n("body"), &rule)) + .clone() }); - res.push((Item { - start_idx: cur_idx, rule: ddd_rule, pos: 0, - done: RefCell::new(false), grammar: self.grammar.clone(), ddd: true, - local_parse: RefCell::new(LocalParse::NothingYet), - id: get_next_id(), wanted_by: Rc::new(RefCell::new(vec![self.id.get_ref()])) - }, false)) + res.push(( + Item { + start_idx: cur_idx, + rule: ddd_rule, + pos: 0, + done: RefCell::new(false), + grammar: self.grammar.clone(), + ddd: true, + local_parse: RefCell::new(LocalParse::NothingYet), + id: get_next_id(), + wanted_by: Rc::new(RefCell::new(vec![self.id.get_ref()])), + }, + false, + )) } } res @@ -415,19 +495,28 @@ impl Item { // ----------------------------------------------------------- - /// See what new items this item justifies - fn examine(&self, cur: Option<&Token>, cur_idx: usize, chart: &[Vec]) - -> Vec<(Item, bool)> { + fn examine( + &self, + cur: Option<&Token>, + cur_idx: usize, + chart: &[Vec], + ) -> Vec<(Item, bool)> { let mut res = if *self.done.borrow() { let mut waiting_satisfied = vec![]; - log!("({:#?}) done; {} items want it\n", self, (*self.wanted_by.borrow()).len()); + log!( + "({:#?}) done; {} items want it\n", + self, + (*self.wanted_by.borrow()).len() + ); for &waiting_item_id in self.wanted_by.borrow().iter() { - if let Some(waiting_item) = chart[self.start_idx].iter() - .find(|i| i.id.is(waiting_item_id)) { // It's `None` if it's the startier item - + if let Some(waiting_item) = chart[self.start_idx] + .iter() + .find(|i| i.id.is(waiting_item_id)) + { + // It's `None` if it's the startier item let me_justif = JustifiedByItem(self.id.get_ref()); @@ -442,34 +531,54 @@ impl Item { Seq(ref subs) => { if (waiting_item.pos) as usize == subs.len() { vec![] - } else { // Like `waiting_item.advance`, but with a local_parse - vec![(Item { pos: waiting_item.pos+1, - local_parse: RefCell::new(me_justif), - .. waiting_item.clone() }, false)] + } else { + // Like `waiting_item.advance`, but with a local_parse + vec![( + Item { + pos: waiting_item.pos + 1, + local_parse: RefCell::new(me_justif), + ..waiting_item.clone() + }, + false, + )] } } - Delimited(_,_,_) => { // Like `waiting_item.advance` (etc.) - vec![(Item { pos: waiting_item.pos+1, - local_parse: RefCell::new(me_justif), - .. waiting_item.clone() }, false)] - } - Plus(_) | Star(_) => { // It'll also keep going, though! - waiting_item.finish_with(me_justif, false) - } - SynImport(_,_,_) if waiting_item.pos == 0 => { - vec![(Item { pos: 1, - local_parse: RefCell::new(me_justif), - .. waiting_item.clone() }, false)] + Delimited(_, _, _) => { + // Like `waiting_item.advance` (etc.) + vec![( + Item { + pos: waiting_item.pos + 1, + local_parse: RefCell::new(me_justif), + ..waiting_item.clone() + }, + false, + )] } - Alt(_) | Call(_) | ComputeSyntax(_,_) - | Scope(_,_) | Named(_,_) | SynImport(_,_,_) | NameImport(_,_) - | QuoteDeepen(_,_) | QuoteEscape(_,_) => { + Plus(_) | Star(_) => { + // It'll also keep going, though! waiting_item.finish_with(me_justif, false) } + SynImport(_, _, _) if waiting_item.pos == 0 => vec![( + Item { + pos: 1, + local_parse: RefCell::new(me_justif), + ..waiting_item.clone() + }, + false, + )], + Alt(_) + | Call(_) + | ComputeSyntax(_, _) + | Scope(_, _) + | Named(_, _) + | SynImport(_, _, _) + | NameImport(_, _) + | QuoteDeepen(_, _) + | QuoteEscape(_, _) => waiting_item.finish_with(me_justif, false), Biased(ref _plan_a, ref plan_b) => { if &*self.rule as *const FormPat == &**plan_b as *const FormPat { - waiting_item.finish_with( - JustifiedByItemPlanB(self.id.get_ref()), false) + waiting_item + .finish_with(JustifiedByItemPlanB(self.id.get_ref()), false) } else { waiting_item.finish_with(me_justif, false) } @@ -486,9 +595,13 @@ impl Item { if !res.is_empty() { if let Some(tok) = cur { - let n = match tok { Simple(x) => x, Group(x,_,_) => x }; - best_token.with(|bt| *bt.borrow_mut() - = (cur_idx, *n, res[0].0.rule.clone(), res[0].0.pos)); + let n = match tok { + Simple(x) => x, + Group(x, _, _) => x, + }; + best_token.with(|bt| { + *bt.borrow_mut() = (cur_idx, *n, res[0].0.rule.clone(), res[0].0.pos) + }); } } @@ -497,19 +610,22 @@ impl Item { res } - fn shift_or_predict(&self, cur: Option<&Token>, cur_idx: usize, chart: &[Vec]) - -> Vec<(Item, bool)> { + fn shift_or_predict( + &self, + cur: Option<&Token>, + cur_idx: usize, + chart: &[Vec], + ) -> Vec<(Item, bool)> { // Try to shift (bump `pos`, or set `done`) or predict (`start` a new item) - match (self.pos, &*(self.rule.clone())) { // TODO: is there a better way to match in `Rc`? + match (self.pos, &*(self.rule.clone())) { + // TODO: is there a better way to match in `Rc`? (0, &Anyways(ref a)) => self.finish_with(ParsedAtom(a.clone()), false), - (_, &Impossible) => vec![], - (0, &Literal(xptd_n)) => { - match cur { - Some(&Simple(n)) if xptd_n == n => { - self.finish_with(ParsedAtom(::ast::Atom(n)), true) - } - _ => vec![] + (_, &Impossible) => vec![], + (0, &Literal(xptd_n)) => match cur { + Some(&Simple(n)) if xptd_n == n => { + self.finish_with(ParsedAtom(::ast::Atom(n)), true) } + _ => vec![], }, // TODO: without checking for `end_of_delim()`, `(plus (plus (plus one one) one) one)` // parses incorrectly (one of the `end_of_delim()`s winds up in a VarRef). @@ -518,91 +634,102 @@ impl Item { (0, &AnyToken) => { match cur { Some(&Simple(n)) if !reserved(n) => { - self.finish_with(ParsedAtom(::ast::Atom(n)), true) + self.finish_with(ParsedAtom(::ast::Atom(n)), true) } - Some(&Group(_,_,_)) => self.finish_with(ParsedAtom(::ast::Trivial), true), // TODO - _ => vec![] + Some(&Group(_, _, _)) => self.finish_with(ParsedAtom(::ast::Trivial), true), // TODO + _ => vec![], } - }, - (0, &AnyAtomicToken) => { - match cur { - Some(&Simple(n)) if !reserved(n) => { - self.finish_with(ParsedAtom(::ast::Atom(n)), true) - } - _ => vec![] + } + (0, &AnyAtomicToken) => match cur { + Some(&Simple(n)) if !reserved(n) => { + self.finish_with(ParsedAtom(::ast::Atom(n)), true) } + _ => vec![], }, - (0, &VarRef) => { - match cur { - Some(&Simple(n)) if !reserved(n) => { - self.finish_with(ParsedAtom(::ast::VariableReference(n)), true) - }, - _ => vec![] + (0, &VarRef) => match cur { + Some(&Simple(n)) if !reserved(n) => { + self.finish_with(ParsedAtom(::ast::VariableReference(n)), true) } + _ => vec![], }, // TODO: does `advance_one_token == true` just work to mean "descend here"? - (0, &Delimited(ref xptd_n, ref xptd_delim, _)) => { - match cur { - Some(&Group(ref n, ref delim, _)) if n == xptd_n && delim == xptd_delim - => self.advance(true), - _ => vec![] + (0, &Delimited(ref xptd_n, ref xptd_delim, _)) => match cur { + Some(&Group(ref n, ref delim, _)) if n == xptd_n && delim == xptd_delim => { + self.advance(true) } + _ => vec![], }, - (1, &Delimited(_, _, ref xptd_body)) => { - self.start(&xptd_body, cur_idx, false) - }, - (2, &Delimited(_,_,_)) => { + (1, &Delimited(_, _, ref xptd_body)) => self.start(&xptd_body, cur_idx, false), + (2, &Delimited(_, _, _)) => { match cur { - Some(t) if t == &end_of_delim() => { // like `.finish`, but keeping local_parse - vec![(Item{done: RefCell::new(true), pos: self.pos + 1, - local_parse: RefCell::new(self.local_parse.borrow().clone()), - .. self.clone()}, - true)]}, - _ => { vec![] } + Some(t) if t == &end_of_delim() => { + // like `.finish`, but keeping local_parse + vec![( + Item { + done: RefCell::new(true), + pos: self.pos + 1, + local_parse: RefCell::new(self.local_parse.borrow().clone()), + ..self.clone() + }, + true, + )] + } + _ => vec![], } - }, + } (pos, &Seq(ref subs)) => { if pos < subs.len() { self.start(&subs[pos as usize], cur_idx, false) - } else if pos == subs.len() { // a little like `.finish`, but without advancing - vec![(Item{ done: RefCell::new(true), .. self.clone()}, false)] + } else if pos == subs.len() { + // a little like `.finish`, but without advancing + vec![( + Item { + done: RefCell::new(true), + ..self.clone() + }, + false, + )] } else { vec![] } - }, + } (_, &Star(ref sub)) => { // Special case: the elegant thing would be to create `Star` pre-`done` - let mut res = if self.pos == 0 { // Like `.finish`, but without advancing - vec![(Item{ done: RefCell::new(true), .. self.clone()}, false)] - } else { vec![] }; + let mut res = if self.pos == 0 { + // Like `.finish`, but without advancing + vec![( + Item { + done: RefCell::new(true), + ..self.clone() + }, + false, + )] + } else { + vec![] + }; res.append(&mut self.start(&sub, cur_idx, true)); // But we can take more! res - }, - (_, &Plus(ref sub)) => { - self.start(&sub, cur_idx, true) - }, + } + (_, &Plus(ref sub)) => self.start(&sub, cur_idx, true), (0, &Alt(ref subs)) => { let mut res = vec![]; for sub in subs { res.append(&mut self.start(&(*sub), cur_idx, false)); } res - }, + } // Needs special handling elsewhere! (0, &Biased(ref plan_a, ref plan_b)) => { - let mut res = self.start(&plan_a, cur_idx, false); + let mut res = self.start(&plan_a, cur_idx, false); res.append(&mut self.start(&plan_b, cur_idx, false)); res - }, - (0, &Call(n)) => { - self.start(&self.grammar.find_or_panic(&n), cur_idx, false) - }, - (0, &Scope(ref f, _)) => { // form.grammar is a FormPat. Confusing! + } + (0, &Call(n)) => self.start(&self.grammar.find_or_panic(&n), cur_idx, false), + (0, &Scope(ref f, _)) => { + // form.grammar is a FormPat. Confusing! self.start(&f.grammar, cur_idx, false) - }, - (0, &SynImport(ref lhs, _, _)) => { - self.start(&lhs, cur_idx, false) } + (0, &SynImport(ref lhs, _, _)) => self.start(&lhs, cur_idx, false), (1, &SynImport(_, ref name, ref f)) => { // TODO: handle errors properly! Probably need to memoize, also! let partial_parse = match *self.local_parse.borrow() { @@ -611,44 +738,55 @@ impl Item { JustifiedByItem(_) | JustifiedByItemPlanB(_) => { match self.find_wanted(chart, cur_idx).c_parse(chart, cur_idx) { Ok(ast) => ast, - Err(_) => { return vec![]; } + Err(_) => { + return vec![]; + } } } }; let new_se = all_grammars.with(|grammars| { let mut mut_grammars = grammars.borrow_mut(); - mut_grammars.entry(self.id.get_ref()) // memoize - .or_insert_with(|| f.0(self.grammar.clone(), partial_parse)).clone() + mut_grammars + .entry(self.id.get_ref()) // memoize + .or_insert_with(|| f.0(self.grammar.clone(), partial_parse)) + .clone() }); - vec![(Item { start_idx: cur_idx, rule: new_se.find_or_panic(name).clone(), pos: 0, - done: RefCell::new(false), - grammar: new_se, local_parse: RefCell::new(LocalParse::NothingYet), - ddd: false, id: get_next_id(), - wanted_by: Rc::new(RefCell::new(vec![self.id.get_ref()])) - }, - false)] + vec![( + Item { + start_idx: cur_idx, + rule: new_se.find_or_panic(name).clone(), + pos: 0, + done: RefCell::new(false), + grammar: new_se, + local_parse: RefCell::new(LocalParse::NothingYet), + ddd: false, + id: get_next_id(), + wanted_by: Rc::new(RefCell::new(vec![self.id.get_ref()])), + }, + false, + )] } - (0, &ComputeSyntax(_,_)) => { panic!("TODO") }, - (0, &Named(_, ref body)) | (0, &NameImport(ref body, _)) - | (0, &QuoteDeepen(ref body, _)) | (0, &QuoteEscape(ref body, _)) => { - self.start(&body, cur_idx, false) - }, + (0, &ComputeSyntax(_, _)) => panic!("TODO"), + (0, &Named(_, ref body)) + | (0, &NameImport(ref body, _)) + | (0, &QuoteDeepen(ref body, _)) + | (0, &QuoteEscape(ref body, _)) => self.start(&body, cur_idx, false), // Rust rightly complains that this is unreachable; yay! // But how do I avoid a catch-all pattern for the pos > 0 case? //(0, _) => { panic!("ICE: unhandled FormPat") }, - _ => { vec![] } // end of a rule + _ => vec![], // end of a rule } } - fn find_wanted<'f, 'c>(&'f self, chart: &'c [Vec], done_tok: usize) - -> &'c Item { - let mut first_found : Option<&Item> = None; + fn find_wanted<'f, 'c>(&'f self, chart: &'c [Vec], done_tok: usize) -> &'c Item { + let mut first_found: Option<&Item> = None; let local_parse = self.local_parse.borrow().clone(); let desired_id = match local_parse { JustifiedByItem(id) | JustifiedByItemPlanB(id) => id, - Ambiguous(ref l, ref r) => { // HACK: this is quite ugly! + Ambiguous(ref l, ref r) => { + // HACK: this is quite ugly! let l = *l.clone(); let r = *r.clone(); log!("===Ambiguity===\n"); @@ -658,10 +796,9 @@ impl Item { *self.local_parse.borrow_mut() = r; let r_res = self.c_parse(chart, done_tok); - panic!("Ambiguity! \n{:#?}\n{:#?}\n", l_res, r_res) - }, - _ => panic!("ICE: tried to parse unjustified item: {:#?} ", self) + } + _ => panic!("ICE: tried to parse unjustified item: {:#?} ", self), }; log!("We are {:#?} at {}...\n", self, done_tok); @@ -670,8 +807,10 @@ impl Item { if i.id.is(desired_id) { match first_found { - None => { first_found = Some(i); } - Some(_) => { panic!("ICE: unacknowledged ambiguity!") } + None => { + first_found = Some(i); + } + Some(_) => panic!("ICE: unacknowledged ambiguity!"), } } } @@ -688,19 +827,21 @@ impl Item { Impossible => panic!("Impossible!"), Literal(_) | AnyToken | AnyAtomicToken | VarRef => { match self.local_parse.borrow().clone() { - ParsedAtom(a) => Ok(a), _ => { panic!("ICE: no simple parse saved")} + ParsedAtom(a) => Ok(a), + _ => panic!("ICE: no simple parse saved"), } - }, + } Delimited(_, _, _) => { // HACK: the wanted item is misaligned by a token because of the close delimiter - self.find_wanted(chart, done_tok-1).c_parse(chart, done_tok-1) + self.find_wanted(chart, done_tok - 1) + .c_parse(chart, done_tok - 1) } - Alt(_) | Biased(_, _) | Call(_) | SynImport(_,_, _) => { + Alt(_) | Biased(_, _) | Call(_) | SynImport(_, _, _) => { self.find_wanted(chart, done_tok).c_parse(chart, done_tok) - }, + } Seq(_) | Star(_) | Plus(_) => { let mut step = self; - let mut subtrees : Vec = vec![]; + let mut subtrees: Vec = vec![]; let mut pos = done_tok; // Walk over "previous incarnations" of `self` // TODO: It seems like I often have had the thought @@ -710,7 +851,9 @@ impl Item { log!("Trying to take a step...\n"); // Special case: we can't start the loop because there are 0 children - if let NothingYet = step.local_parse.borrow().clone() { break; } + if let NothingYet = step.local_parse.borrow().clone() { + break; + } let sub = step.find_wanted(chart, pos); subtrees.push(sub.c_parse(chart, pos)?); @@ -724,22 +867,27 @@ impl Item { log!("Checking {:#?}\n", i); if self.grammar.almost_ptr_eq(&i.grammar) && &*self.rule as *const FormPat == &*i.rule as *const FormPat - && step.pos - 1 == i.pos { + && step.pos - 1 == i.pos + { step = i; found = true; break; } } - if !found { panic!("ICE: Can't find item previous to {:#?}", step) } + if !found { + panic!("ICE: Can't find item previous to {:#?}", step) + } } } subtrees.reverse(); - let mut ddd_pos : Option = None; + let mut ddd_pos: Option = None; for (i, subtree) in subtrees.iter_mut().enumerate() { if let Some(unwrapped) = ddd_unwrap(&subtree) { if ddd_pos != None { - return Err(ParseError{msg: format!("Found two DDDs at {}", unwrapped)}); + return Err(ParseError { + msg: format!("Found two DDDs at {}", unwrapped), + }); } *subtree = unwrapped; ddd_pos = Some(i); @@ -750,21 +898,29 @@ impl Item { Seq(_) => Ok(Ast::Shape(subtrees)), Star(_) | Plus(_) => Ok(Ast::IncompleteNode( ::util::mbe::EnvMBE::new_from_anon_repeat_ddd( - subtrees.into_iter().map(|a| a.flatten()).collect(), ddd_pos))), - _ => { panic!("ICE: seriously, this can't happen") } + subtrees.into_iter().map(|a| a.flatten()).collect(), + ddd_pos, + ), + )), + _ => panic!("ICE: seriously, this can't happen"), } - }, - ComputeSyntax(_, _) => { panic!("TODO") }, + } + ComputeSyntax(_, _) => panic!("TODO"), Named(name, _) => { let sub_parsed = self.find_wanted(chart, done_tok).c_parse(chart, done_tok)?; Ok(Ast::IncompleteNode(::util::mbe::EnvMBE::new_from_leaves( - ::util::assoc::Assoc::single(name, sub_parsed)))) - }, + ::util::assoc::Assoc::single(name, sub_parsed), + ))) + } Scope(ref form, ref export) => { let sub_parsed = self.find_wanted(chart, done_tok).c_parse(chart, done_tok)?; // TODO #14: We should add zero-length repeats of missing `Named`s, - Ok(Ast::Node(form.clone(), sub_parsed.flatten(), export.clone())) - }, + Ok(Ast::Node( + form.clone(), + sub_parsed.flatten(), + export.clone(), + )) + } NameImport(_, ref beta) => { let sub_parsed = self.find_wanted(chart, done_tok).c_parse(chart, done_tok)?; Ok(Ast::ExtendEnv(Box::new(sub_parsed), beta.clone())) @@ -792,24 +948,29 @@ type ParseResult = Result; #[derive(PartialEq, Eq, Debug, Clone)] pub struct ParseError { - pub msg: String + pub msg: String, } pub fn parse(rule: &FormPat, grammar: &SynEnv, tt: &TokenTree) -> ParseResult { let (start_but_startier, chart) = create_chart(Rc::new(rule.clone()), grammar.clone(), tt); - let final_item = chart[chart.len()-1].iter().find( - |item| - (*item.wanted_by.borrow()).iter().any(|idr| start_but_startier.is(*idr)) - && *item.done.borrow()); + let final_item = chart[chart.len() - 1].iter().find(|item| { + (*item.wanted_by.borrow()) + .iter() + .any(|idr| start_but_startier.is(*idr)) + && *item.done.borrow() + }); log!("-------\n"); match final_item { - Some(i) => i.c_parse(&chart, chart.len()-1), - None => { - best_token.with(|bt| { - let (idx, tok, ref grammar, pos) = *bt.borrow(); - Err(ParseError{ msg: format!("Could not parse past token {} ({}) {:#?} {}", idx, tok, grammar, pos) }) + Some(i) => i.c_parse(&chart, chart.len() - 1), + None => best_token.with(|bt| { + let (idx, tok, ref grammar, pos) = *bt.borrow(); + Err(ParseError { + msg: format!( + "Could not parse past token {} ({}) {:#?} {}", + idx, tok, grammar, pos + ), }) - } + }), } } @@ -825,60 +986,107 @@ fn earley_merging() { let another_grammar = assoc_n!("a" => Rc::new(form_pat!(aat))); let mut state_set = vec![]; - let basic_item = Item{start_idx: 0, rule: Rc::new(one_rule), pos: 0, - grammar: main_grammar.clone(), ddd: false, - id: get_next_id(), done: RefCell::new(false), - local_parse: RefCell::new(LocalParse::NothingYet), - wanted_by: Rc::new(RefCell::new(vec![]))}; + let basic_item = Item { + start_idx: 0, + rule: Rc::new(one_rule), + pos: 0, + grammar: main_grammar.clone(), + ddd: false, + id: get_next_id(), + done: RefCell::new(false), + local_parse: RefCell::new(LocalParse::NothingYet), + wanted_by: Rc::new(RefCell::new(vec![])), + }; - assert_eq!(merge_into_state_set(basic_item.clone(), &mut state_set), true); + assert_eq!( + merge_into_state_set(basic_item.clone(), &mut state_set), + true + ); assert_eq!(state_set.len(), 1); // exactly the same (except a different ID) - assert_eq!(merge_into_state_set(basic_item.clone(), &mut state_set), false); + assert_eq!( + merge_into_state_set(basic_item.clone(), &mut state_set), + false + ); assert_eq!(state_set.len(), 1); // (not done yet) assert_eq!(*state_set[0].done.borrow(), false); // improvement in doneness, mergable - assert_eq!(merge_into_state_set(Item{done: RefCell::new(true), .. basic_item.clone()}, - &mut state_set), - true); + assert_eq!( + merge_into_state_set( + Item { + done: RefCell::new(true), + ..basic_item.clone() + }, + &mut state_set + ), + true + ); assert_eq!(state_set.len(), 1); // now done! assert_eq!(*state_set[0].done.borrow(), true); // not an improvement this time! - assert_eq!(merge_into_state_set(Item{done: RefCell::new(true), .. basic_item.clone()}, - &mut state_set), - false); + assert_eq!( + merge_into_state_set( + Item { + done: RefCell::new(true), + ..basic_item.clone() + }, + &mut state_set + ), + false + ); assert_eq!(state_set.len(), 1); // not as good as - assert_eq!(merge_into_state_set(Item{done: RefCell::new(false), .. basic_item.clone()}, - &mut state_set), - false); + assert_eq!( + merge_into_state_set( + Item { + done: RefCell::new(false), + ..basic_item.clone() + }, + &mut state_set + ), + false + ); assert_eq!(state_set.len(), 1); // still done! assert_eq!(*state_set[0].done.borrow(), true); // different rule - assert_eq!(merge_into_state_set(Item{rule: Rc::new(another_rule), .. basic_item.clone()}, - &mut state_set), - true); + assert_eq!( + merge_into_state_set( + Item { + rule: Rc::new(another_rule), + ..basic_item.clone() + }, + &mut state_set + ), + true + ); assert_eq!(state_set.len(), 2); // different grammar (pointer-wise!) - assert_eq!(merge_into_state_set(Item{grammar: another_grammar.clone(), .. basic_item.clone()}, - &mut state_set), - true); + assert_eq!( + merge_into_state_set( + Item { + grammar: another_grammar.clone(), + ..basic_item.clone() + }, + &mut state_set + ), + true + ); assert_eq!(state_set.len(), 3); let id1 = get_next_id().get_ref(); let id2 = get_next_id().get_ref(); let id3 = get_next_id().get_ref(); - /* + /* log!("AGA {:#?},{:#?},{:#?},{:#?}\n", (*self.done.borrow() == *other.done.borrow() || !*other.done.borrow()) , *self.local_parse.borrow() == *other.local_parse.borrow() @@ -889,37 +1097,56 @@ fn earley_merging() { ); log!(" {:#?}=?{:#?}\n", *self.local_parse.borrow(), *other.local_parse.borrow());*/ - let wanted_item = |ids| { - Item { - wanted_by: Rc::new(RefCell::new(ids)), .. basic_item.clone() - } + let wanted_item = |ids| Item { + wanted_by: Rc::new(RefCell::new(ids)), + ..basic_item.clone() }; // test self-check (this shouldn't be interesting) - assert_eq!(merge_into_state_set(wanted_item(vec![]), &mut state_set), false); + assert_eq!( + merge_into_state_set(wanted_item(vec![]), &mut state_set), + false + ); assert_eq!(state_set.len(), 3); - assert_eq!(merge_into_state_set(wanted_item(vec![id1]), &mut state_set), true); + assert_eq!( + merge_into_state_set(wanted_item(vec![id1]), &mut state_set), + true + ); assert_eq!(state_set.len(), 3); // but another one doesn't have any effect - assert_eq!(merge_into_state_set(wanted_item(vec![id1]), &mut state_set), false); + assert_eq!( + merge_into_state_set(wanted_item(vec![id1]), &mut state_set), + false + ); assert_eq!(state_set.len(), 3); - assert_eq!(merge_into_state_set(wanted_item(vec![id2]), &mut state_set), true); + assert_eq!( + merge_into_state_set(wanted_item(vec![id2]), &mut state_set), + true + ); assert_eq!(state_set.len(), 3); - assert_eq!(merge_into_state_set(wanted_item(vec![id1, id2]), &mut state_set), false); + assert_eq!( + merge_into_state_set(wanted_item(vec![id1, id2]), &mut state_set), + false + ); assert_eq!(state_set.len(), 3); - assert_eq!(merge_into_state_set(wanted_item(vec![id2, id1]), &mut state_set), false); + assert_eq!( + merge_into_state_set(wanted_item(vec![id2, id1]), &mut state_set), + false + ); assert_eq!(state_set.len(), 3); - assert_eq!(merge_into_state_set(wanted_item(vec![id2, id3]), &mut state_set), true); + assert_eq!( + merge_into_state_set(wanted_item(vec![id2, id3]), &mut state_set), + true + ); assert_eq!(state_set.len(), 3); // TODO: we ought to test the NothingYet - JustifiedByItem() / ParsedAtom() - Ambiguous lattice - } #[test] @@ -930,79 +1157,198 @@ fn earley_simple_recognition() { assert_eq!(recognize(&AnyAtomicToken, &main_grammar, &tokens!()), false); - assert_eq!(recognize(&Anyways(::ast::Trivial), &main_grammar, &tokens!()), true); + assert_eq!( + recognize(&Anyways(::ast::Trivial), &main_grammar, &tokens!()), + true + ); assert_eq!(recognize(&Seq(vec![]), &main_grammar, &tokens!()), true); - assert_eq!(recognize(&Seq(vec![Rc::new(AnyAtomicToken)]), &main_grammar, &tokens!()), false); + assert_eq!( + recognize( + &Seq(vec![Rc::new(AnyAtomicToken)]), + &main_grammar, + &tokens!() + ), + false + ); - assert_eq!(recognize(&Star(Rc::new(Impossible)), &main_grammar, &tokens!()), true); + assert_eq!( + recognize(&Star(Rc::new(Impossible)), &main_grammar, &tokens!()), + true + ); - assert_eq!(recognize(&Star(Rc::new(AnyAtomicToken)), &main_grammar, &tokens!()), true); + assert_eq!( + recognize(&Star(Rc::new(AnyAtomicToken)), &main_grammar, &tokens!()), + true + ); // 1-length strings - assert_eq!(recognize(&AnyAtomicToken, &main_grammar, &tokens!("Pierre Menard")), true); - - assert_eq!(recognize(&Impossible, &main_grammar, &tokens!("Pierre Menard")), false); + assert_eq!( + recognize(&AnyAtomicToken, &main_grammar, &tokens!("Pierre Menard")), + true + ); - assert_eq!(recognize(&Literal(n("Cervantes")), &main_grammar, &tokens!("Pierre Menard")), - false); + assert_eq!( + recognize(&Impossible, &main_grammar, &tokens!("Pierre Menard")), + false + ); - assert_eq!(recognize(&Literal(n("Cervantes")), &main_grammar, &tokens!("Cervantes")), - true); + assert_eq!( + recognize( + &Literal(n("Cervantes")), + &main_grammar, + &tokens!("Pierre Menard") + ), + false + ); - assert_eq!(recognize(&Seq(vec![Rc::new(AnyAtomicToken)]), &main_grammar, &tokens!("P.M.")), - true); + assert_eq!( + recognize( + &Literal(n("Cervantes")), + &main_grammar, + &tokens!("Cervantes") + ), + true + ); - assert_eq!(recognize(&Star(Rc::new(AnyAtomicToken)), &main_grammar, &tokens!("PM")), true); + assert_eq!( + recognize( + &Seq(vec![Rc::new(AnyAtomicToken)]), + &main_grammar, + &tokens!("P.M.") + ), + true + ); - assert_eq!(recognize(&Alt(vec![Rc::new(Impossible), Rc::new(AnyAtomicToken)]), &main_grammar, - &tokens!("Pierre Menard")), true); + assert_eq!( + recognize( + &Star(Rc::new(AnyAtomicToken)), + &main_grammar, + &tokens!("PM") + ), + true + ); - assert_eq!(recognize(&Alt(vec![Rc::new(Impossible), Rc::new(Literal(n("Cervantes")))]), - &main_grammar, &tokens!("Pierre Menard")), false); + assert_eq!( + recognize( + &Alt(vec![Rc::new(Impossible), Rc::new(AnyAtomicToken)]), + &main_grammar, + &tokens!("Pierre Menard") + ), + true + ); - assert_eq!(recognize(&Biased(Rc::new(Impossible), Rc::new(AnyAtomicToken)), &main_grammar, - &tokens!("Pierre Menard")), true); + assert_eq!( + recognize( + &Alt(vec![Rc::new(Impossible), Rc::new(Literal(n("Cervantes")))]), + &main_grammar, + &tokens!("Pierre Menard") + ), + false + ); - assert_eq!(recognize(&Biased(Rc::new(AnyAtomicToken), Rc::new(Impossible)), &main_grammar, - &tokens!("Pierre Menard")), true); + assert_eq!( + recognize( + &Biased(Rc::new(Impossible), Rc::new(AnyAtomicToken)), + &main_grammar, + &tokens!("Pierre Menard") + ), + true + ); + assert_eq!( + recognize( + &Biased(Rc::new(AnyAtomicToken), Rc::new(Impossible)), + &main_grammar, + &tokens!("Pierre Menard") + ), + true + ); // Nesting! - assert_eq!(recognize( - &Seq(vec![Rc::new(Seq(vec![Rc::new(Seq(vec![Rc::new(AnyAtomicToken)]))]))]), &main_grammar, - &tokens!("Frustrated Novelist No Good At Describing Hands")), true); - - assert_eq!(recognize( - &Alt(vec![Rc::new(Alt(vec![Rc::new(Alt(vec![Rc::new(AnyAtomicToken)]))]))]), &main_grammar, - &tokens!("(no pun intended, by the way)")), true); + assert_eq!( + recognize( + &Seq(vec![Rc::new(Seq(vec![Rc::new(Seq(vec![Rc::new( + AnyAtomicToken + )]))]))]), + &main_grammar, + &tokens!("Frustrated Novelist No Good At Describing Hands") + ), + true + ); - assert_eq!(recognize(&Plus(Rc::new(Plus(Rc::new(Plus(Rc::new(AnyAtomicToken)))))), - &main_grammar, - &tokens!("(except I might've changed it otherwise)")), true); + assert_eq!( + recognize( + &Alt(vec![Rc::new(Alt(vec![Rc::new(Alt(vec![Rc::new( + AnyAtomicToken + )]))]))]), + &main_grammar, + &tokens!("(no pun intended, by the way)") + ), + true + ); + assert_eq!( + recognize( + &Plus(Rc::new(Plus(Rc::new(Plus(Rc::new(AnyAtomicToken)))))), + &main_grammar, + &tokens!("(except I might've changed it otherwise)") + ), + true + ); // Fine, there are longer strings. - assert_eq!(recognize( - &Seq(vec![Rc::new(AnyAtomicToken), Rc::new(AnyAtomicToken), Rc::new(AnyAtomicToken)]), - &main_grammar, &tokens!("Author" "of the" "Quixote")), true); - - assert_eq!(recognize( - &Seq(vec![Rc::new(AnyAtomicToken), Rc::new(AnyAtomicToken), Rc::new(AnyAtomicToken)]), - &main_grammar, &tokens!("Author" "of" "the" "Quixote")), false); + assert_eq!( + recognize( + &Seq(vec![ + Rc::new(AnyAtomicToken), + Rc::new(AnyAtomicToken), + Rc::new(AnyAtomicToken) + ]), + &main_grammar, + &tokens!("Author" "of the" "Quixote") + ), + true + ); - assert_eq!(recognize( - &Seq(vec![Rc::new(AnyAtomicToken), Rc::new(AnyAtomicToken), Rc::new(AnyAtomicToken)]), - &main_grammar, &tokens!("Author of" "the Quixote")), false); + assert_eq!( + recognize( + &Seq(vec![ + Rc::new(AnyAtomicToken), + Rc::new(AnyAtomicToken), + Rc::new(AnyAtomicToken) + ]), + &main_grammar, + &tokens!("Author" "of" "the" "Quixote") + ), + false + ); - assert_eq!(recognize(&Plus(Rc::new(Plus(Rc::new(Plus(Rc::new(AnyAtomicToken)))))), - &main_grammar, - &tokens!("Author" "of" "the" "Quixote")), true); + assert_eq!( + recognize( + &Seq(vec![ + Rc::new(AnyAtomicToken), + Rc::new(AnyAtomicToken), + Rc::new(AnyAtomicToken) + ]), + &main_grammar, + &tokens!("Author of" "the Quixote") + ), + false + ); + assert_eq!( + recognize( + &Plus(Rc::new(Plus(Rc::new(Plus(Rc::new(AnyAtomicToken)))))), + &main_grammar, + &tokens!("Author" "of" "the" "Quixote") + ), + true + ); } #[test] @@ -1040,108 +1386,223 @@ fn earley_env_recognition() { assert_eq!(recognize(&Literal(n("a")), &env, &tokens!("b")), false); assert_eq!(recognize(&Call(n("empty")), &env, &tokens!()), true); - assert_eq!(recognize(&Call(n("empty")), &env, &tokens!("not empty!")), false); + assert_eq!( + recognize(&Call(n("empty")), &env, &tokens!("not empty!")), + false + ); - assert_eq!(recognize(&Call(n("empty_indirect")), &env, &tokens!()), true); - assert_eq!(recognize(&Call(n("empty_indirect")), &env, &tokens!("not empty!")), false); + assert_eq!( + recognize(&Call(n("empty_indirect")), &env, &tokens!()), + true + ); + assert_eq!( + recognize(&Call(n("empty_indirect")), &env, &tokens!("not empty!")), + false + ); assert_eq!(recognize(&Call(n("aaa")), &env, &tokens!()), true); assert_eq!(recognize(&Call(n("aaa")), &env, &tokens!("a")), true); assert_eq!(recognize(&Call(n("aaa")), &env, &tokens!("a" "a")), true); - assert_eq!(recognize(&Call(n("aaa")), &env, &tokens!("a" "a" "a")), true); - assert_eq!(recognize(&Call(n("aaa")), &env, &tokens!("a" "x" "a")), false); + assert_eq!( + recognize(&Call(n("aaa")), &env, &tokens!("a" "a" "a")), + true + ); + assert_eq!( + recognize(&Call(n("aaa")), &env, &tokens!("a" "x" "a")), + false + ); assert_eq!(recognize(&Call(n("aaaa")), &env, &tokens!()), false); assert_eq!(recognize(&Call(n("aaaa")), &env, &tokens!("a")), true); assert_eq!(recognize(&Call(n("aaaa")), &env, &tokens!("a" "a")), true); - assert_eq!(recognize(&Call(n("aaaa")), &env, &tokens!("a" "a" "a")), true); - assert_eq!(recognize(&Call(n("aaaa")), &env, &tokens!("a" "x" "a")), false); + assert_eq!( + recognize(&Call(n("aaaa")), &env, &tokens!("a" "a" "a")), + true + ); + assert_eq!( + recognize(&Call(n("aaaa")), &env, &tokens!("a" "x" "a")), + false + ); assert_eq!(recognize(&Call(n("aaa_indirect")), &env, &tokens!()), true); - assert_eq!(recognize(&Call(n("aaa_indirect")), &env, &tokens!("a")), true); - assert_eq!(recognize(&Call(n("aaa_indirect")), &env, &tokens!("a" "a")), true); - assert_eq!(recognize(&Call(n("aaa_indirect")), &env, &tokens!("a" "a" "a")), true); - assert_eq!(recognize(&Call(n("aaa_indirect")), &env, &tokens!("a" "x" "a")), false); + assert_eq!( + recognize(&Call(n("aaa_indirect")), &env, &tokens!("a")), + true + ); + assert_eq!( + recognize(&Call(n("aaa_indirect")), &env, &tokens!("a" "a")), + true + ); + assert_eq!( + recognize(&Call(n("aaa_indirect")), &env, &tokens!("a" "a" "a")), + true + ); + assert_eq!( + recognize(&Call(n("aaa_indirect")), &env, &tokens!("a" "x" "a")), + false + ); for l_rec in vec!["l_rec_axxx", "l_rec_axxx_hard"] { assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!("a")), true); assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!("a" "x")), true); - assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!("a" "x" "x")), true); - assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!("a" "x" "x" "x")), true); - assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!("a" "a" "x" "x")), false); + assert_eq!( + recognize(&Call(n(l_rec)), &env, &tokens!("a" "x" "x")), + true + ); + assert_eq!( + recognize(&Call(n(l_rec)), &env, &tokens!("a" "x" "x" "x")), + true + ); + assert_eq!( + recognize(&Call(n(l_rec)), &env, &tokens!("a" "a" "x" "x")), + false + ); assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!()), false); - assert_eq!(recognize(&Call(n(l_rec)), &env, &tokens!("a" "x" "a" "x")), false); + assert_eq!( + recognize(&Call(n(l_rec)), &env, &tokens!("a" "x" "a" "x")), + false + ); } } - #[test] fn basic_parsing_e() { assert_eq!(parse_top(&AnyToken, &tokens!("asdf")), Ok(ast!("asdf"))); - assert_eq!(parse_top(&Anyways(ast!("asdf")), &tokens!()), Ok(ast!("asdf"))); - - assert_eq!(parse_top(&Biased(Rc::new(Anyways(ast!("A"))), Rc::new(Anyways(ast!("B")))), - &tokens!()), - Ok(ast!("A"))); - - assert_eq!(parse_top(&Named(n("c"), Rc::new(Anyways(ast!("asdf")))), &tokens!()), - Ok(ast!({- "c" => "asdf"}))); - + assert_eq!( + parse_top(&Anyways(ast!("asdf")), &tokens!()), + Ok(ast!("asdf")) + ); - assert_eq!(parse_top(&Seq(vec![Rc::new(AnyToken)]), &tokens!("asdf")), Ok(ast_shape!("asdf"))); + assert_eq!( + parse_top( + &Biased(Rc::new(Anyways(ast!("A"))), Rc::new(Anyways(ast!("B")))), + &tokens!() + ), + Ok(ast!("A")) + ); - assert_eq!(parse_top(&Seq(vec![Rc::new(AnyToken), Rc::new(Literal(n("fork"))), Rc::new(AnyToken)]), - &tokens!("asdf" "fork" "asdf")), - Ok(ast_shape!("asdf" "fork" "asdf"))); + assert_eq!( + parse_top(&Named(n("c"), Rc::new(Anyways(ast!("asdf")))), &tokens!()), + Ok(ast!({- "c" => "asdf"})) + ); - assert_eq!(parse_top( - &Seq(vec![Rc::new(Seq(vec![Rc::new(Literal(n("aa"))), Rc::new(Literal(n("ab")))])), - Rc::new(Seq(vec![Rc::new(Literal(n("ba"))), Rc::new(Literal(n("bb")))]))]), - &tokens!("aa" "ab" "ba" "bb")), - Ok(ast_shape!(("aa" "ab") ("ba" "bb")))); + assert_eq!( + parse_top(&Seq(vec![Rc::new(AnyToken)]), &tokens!("asdf")), + Ok(ast_shape!("asdf")) + ); - parse_top(&Seq(vec![Rc::new(AnyToken), Rc::new(Literal(n("fork"))), Rc::new(AnyToken)]), - &tokens!("asdf" "knife" "asdf")).unwrap_err(); + assert_eq!( + parse_top( + &Seq(vec![ + Rc::new(AnyToken), + Rc::new(Literal(n("fork"))), + Rc::new(AnyToken) + ]), + &tokens!("asdf" "fork" "asdf") + ), + Ok(ast_shape!("asdf" "fork" "asdf")) + ); - assert_eq!(parse_top(&form_pat!([(star (named "c", (alt (lit "X"), (lit "O")))), (lit "!")]), - &tokens!("X" "O" "O" "O" "X" "X" "!")).unwrap(), - ast_shape!({- "c" => ["X", "O", "O", "O", "X", "X"]} "!")); + assert_eq!( + parse_top( + &Seq(vec![ + Rc::new(Seq(vec![ + Rc::new(Literal(n("aa"))), + Rc::new(Literal(n("ab"))) + ])), + Rc::new(Seq(vec![ + Rc::new(Literal(n("ba"))), + Rc::new(Literal(n("bb"))) + ])) + ]), + &tokens!("aa" "ab" "ba" "bb") + ), + Ok(ast_shape!(("aa" "ab") ("ba" "bb"))) + ); + parse_top( + &Seq(vec![ + Rc::new(AnyToken), + Rc::new(Literal(n("fork"))), + Rc::new(AnyToken), + ]), + &tokens!("asdf" "knife" "asdf"), + ) + .unwrap_err(); + + assert_eq!( + parse_top( + &form_pat!([(star (named "c", (alt (lit "X"), (lit "O")))), (lit "!")]), + &tokens!("X" "O" "O" "O" "X" "X" "!") + ) + .unwrap(), + ast_shape!({- "c" => ["X", "O", "O", "O", "X", "X"]} "!") + ); - assert_eq!(parse_top(&Seq(vec![Rc::new(Star(Rc::new(Named(n("c"), Rc::new(Literal(n("*"))))))), - Rc::new(Literal(n("X")))]), - &tokens!("*" "*" "*" "*" "*" "X")), - Ok(ast_shape!({- "c" => ["*", "*", "*", "*", "*"] } "X"))); + assert_eq!( + parse_top( + &Seq(vec![ + Rc::new(Star(Rc::new(Named(n("c"), Rc::new(Literal(n("*"))))))), + Rc::new(Literal(n("X"))) + ]), + &tokens!("*" "*" "*" "*" "*" "X") + ), + Ok(ast_shape!({- "c" => ["*", "*", "*", "*", "*"] } "X")) + ); - assert_m!(parse_top(&form_pat!([(star (biased (lit "a"), (lit "b"))), (lit "b")]), - &tokens!["a" "a" "b"]), - Ok(_)); + assert_m!( + parse_top( + &form_pat!([(star (biased (lit "a"), (lit "b"))), (lit "b")]), + &tokens!["a" "a" "b"] + ), + Ok(_) + ); - assert_m!(parse_top(&form_pat!([(star (biased (lit "b"), (lit "a"))), (lit "b")]), - &tokens!["a" "a" "b"]), - Ok(_)); + assert_m!( + parse_top( + &form_pat!([(star (biased (lit "b"), (lit "a"))), (lit "b")]), + &tokens!["a" "a" "b"] + ), + Ok(_) + ); } #[test] fn parse_ddd() { - let env = assoc_n!( "dotdotdot" => Rc::new(form_pat!([(delim "...(", "(", (call "body"))])), "some_toks" => Rc::new(form_pat!((star (named "p", aat))))); - assert_eq!(plug_hole(&env.find(&n("dotdotdot")).unwrap(), - n("body"), &Rc::new(form_pat!((named "p", aat)))), - Rc::new(form_pat!([(delim "...(", "(", (named "p", aat))]))); - - assert_eq!(parse(&form_pat!([(delim "...(", "(", (named "p", aat))]), - &env, &tokens!(("..." ; "b"))), - Ok(ast!(({- "p" => "b"})))); + assert_eq!( + plug_hole( + &env.find(&n("dotdotdot")).unwrap(), + n("body"), + &Rc::new(form_pat!((named "p", aat))) + ), + Rc::new(form_pat!([(delim "...(", "(", (named "p", aat))])) + ); - assert_eq!(parse(&Call(n("some_toks")), &env, &tokens!("a" "b" "c")), - Ok(ast!({- "p" => ["a", "b", "c"]}))); + assert_eq!( + parse( + &form_pat!([(delim "...(", "(", (named "p", aat))]), + &env, + &tokens!(("..." ; "b")) + ), + Ok(ast!(({- "p" => "b"}))) + ); - assert_eq!(parse(&Call(n("some_toks")), &env, &tokens!("a" ("..." ; "b" ) "c")), - Ok(ast!({- "p" => ["a" ...("b")..., "c"]}))); + assert_eq!( + parse(&Call(n("some_toks")), &env, &tokens!("a" "b" "c")), + Ok(ast!({- "p" => ["a", "b", "c"]})) + ); + assert_eq!( + parse( + &Call(n("some_toks")), + &env, + &tokens!("a" ("..." ; "b" ) "c") + ), + Ok(ast!({- "p" => ["a" ...("b")..., "c"]})) + ); } diff --git a/src/form.rs b/src/form.rs index a525877..a0aed49 100644 --- a/src/form.rs +++ b/src/form.rs @@ -1,11 +1,11 @@ #![macro_use] +use ast_walk::WalkRule; use grammar::FormPat; use name::*; -use std::fmt::{Debug,Formatter,Error}; -use util::assoc::Assoc; +use std::fmt::{Debug, Error, Formatter}; use std::rc::Rc; -use ast_walk::WalkRule; +use util::assoc::Assoc; use walk_mode::WalkMode; pub type NMap = Assoc; @@ -45,7 +45,6 @@ custom_derive! { } pub use self::EitherPN::*; - impl EitherPN, WalkRule> { pub fn pos(&self) -> &WalkRule { match *self { @@ -55,15 +54,24 @@ impl EitherPN, WalkRule> { } pub fn neg(&self) -> &WalkRule { match *self { - Negative(ref r) | Both(_, ref r)=> r, + Negative(ref r) | Both(_, ref r) => r, Positive(_) => &WalkRule::NotWalked, } } - pub fn is_pos(&self) -> bool { match *self { Negative(_) => false, _ => true }} - pub fn is_neg(&self) -> bool { match *self { Positive(_) => false, _ => true }} + pub fn is_pos(&self) -> bool { + match *self { + Negative(_) => false, + _ => true, + } + } + pub fn is_neg(&self) -> bool { + match *self { + Positive(_) => false, + _ => true, + } + } } - impl PartialEq for Form { /// pointer equality on the underlying structure! fn eq(&self, other: &Form) -> bool { @@ -71,21 +79,19 @@ impl PartialEq for Form { } } - impl Debug for Form { fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { formatter.write_str(format!("[FORM {:#?}]", self.name).as_str()) } } - pub fn simple_form(form_name: &str, p: FormPat) -> Rc { Rc::new(Form { - name: n(form_name), - grammar: Rc::new(p), - type_compare: ::form::Both(WalkRule::NotWalked, WalkRule::NotWalked), - synth_type: ::form::Positive(WalkRule::NotWalked), - eval: ::form::Positive(WalkRule::NotWalked), - quasiquote: ::form::Both(WalkRule::LiteralLike, WalkRule::LiteralLike) - }) + name: n(form_name), + grammar: Rc::new(p), + type_compare: ::form::Both(WalkRule::NotWalked, WalkRule::NotWalked), + synth_type: ::form::Positive(WalkRule::NotWalked), + eval: ::form::Positive(WalkRule::NotWalked), + quasiquote: ::form::Both(WalkRule::LiteralLike, WalkRule::LiteralLike), + }) } diff --git a/src/grammar.rs b/src/grammar.rs index 6c4bf08..0964fb9 100644 --- a/src/grammar.rs +++ b/src/grammar.rs @@ -1,15 +1,15 @@ #![macro_use] -use read::{Token, TokenTree, DelimChar, Group, Simple}; +use ast::Ast; +use ast::Ast::*; +use beta::{Beta, ExportBeta}; +use form::{simple_form, Form}; use name::*; -use form::{Form,simple_form}; +use read::{DelimChar, Group, Simple, Token, TokenTree}; use std::boxed::Box; use std::clone::Clone; -use ast::Ast; -use ast::Ast::*; -use util::assoc::Assoc; use std::rc::Rc; -use beta::{Beta, ExportBeta}; +use util::assoc::Assoc; impl Token { fn to_ast(&self) -> Ast { @@ -86,7 +86,7 @@ impl FormPat { pub fn binders(&self) -> Vec<(Name, u8)> { use tap::TapOps; match *self { - Named(n, ref body) => { vec![(n,0)].tap(|v| v.append(&mut body.binders())) } + Named(n, ref body) => vec![(n, 0)].tap(|v| v.append(&mut body.binders())), Seq(ref bodies) | Alt(ref bodies) => { let mut res = vec![]; for body in bodies { @@ -94,15 +94,18 @@ impl FormPat { } res } - Scope(_,_) => vec![], // No more bindings in this scope - Star(ref body) | Plus(ref body) => { - body.binders().into_iter().map(|(n, depth)| (n, depth+1)).collect() - } - Delimited(_,_,ref body) | ComputeSyntax(_, ref body) - | SynImport(ref body, _, _) | NameImport(ref body, _) | QuoteDeepen(ref body, _) - | QuoteEscape(ref body, _) => { - body.binders() - } + Scope(_, _) => vec![], // No more bindings in this scope + Star(ref body) | Plus(ref body) => body + .binders() + .into_iter() + .map(|(n, depth)| (n, depth + 1)) + .collect(), + Delimited(_, _, ref body) + | ComputeSyntax(_, ref body) + | SynImport(ref body, _, _) + | NameImport(ref body, _) + | QuoteDeepen(ref body, _) + | QuoteEscape(ref body, _) => body.binders(), Biased(ref body_a, ref body_b) => { body_a.binders().tap(|v| v.append(&mut body_b.binders())) } @@ -125,28 +128,33 @@ impl FormPat { match **sub { Call(nt) => Some(Some(nt)), AnyAtomicToken => Some(None), - _ => None + _ => None, } } - Named(_,_) => None, // Otherwise, skip + Named(_, _) => None, // Otherwise, skip Call(_) => None, - Scope(_,_) => None, // Only look in the current scope + Scope(_, _) => None, // Only look in the current scope Anyways(_) | Impossible | Literal(_) | AnyToken | AnyAtomicToken | VarRef => None, - Delimited(_,_,ref body) |Star(ref body) | Plus(ref body) | ComputeSyntax(_, ref body) - | SynImport(ref body, _, _) | NameImport(ref body, _) | QuoteDeepen(ref body, _) - | QuoteEscape(ref body, _) => { - body.find_named_call(n) - } + Delimited(_, _, ref body) + | Star(ref body) + | Plus(ref body) + | ComputeSyntax(_, ref body) + | SynImport(ref body, _, _) + | NameImport(ref body, _) + | QuoteDeepen(ref body, _) + | QuoteEscape(ref body, _) => body.find_named_call(n), Seq(ref bodies) | Alt(ref bodies) => { for body in bodies { let sub_fnc = body.find_named_call(n); - if sub_fnc.is_some() { return sub_fnc; } + if sub_fnc.is_some() { + return sub_fnc; + } } None } - Biased(ref body_a, ref body_b) => { - body_a.find_named_call(n).or_else(|| body_b.find_named_call(n)) - } + Biased(ref body_a, ref body_b) => body_a + .find_named_call(n) + .or_else(|| body_b.find_named_call(n)), } } } @@ -166,7 +174,9 @@ impl PartialEq for SyntaxExtension { // This kind of struct is theoretically possible to add to the `Reifiable!` macro, // but I don't feel like doing it right now impl ::runtime::reify::Reifiable for SyntaxExtension { - fn ty_name() -> Name { n("syntax_extension") } + fn ty_name() -> Name { + n("syntax_extension") + } fn reify(&self) -> ::runtime::eval::Value { ::runtime::reify::reify_2ary_function(self.0.clone()) @@ -188,124 +198,196 @@ pub type SynEnv = Assoc>; /// Currently only used for DDDs pub fn plug_hole(outer: &Rc, hole: Name, inner: &Rc) -> Rc { match **outer { - Call(n) => if n == hole { inner.clone() } else { outer.clone() }, - Anyways(_) | Impossible | Literal(_) | AnyToken | AnyAtomicToken | VarRef => { - outer.clone() - } - Seq(ref subs) => { - Rc::new(Seq(subs.iter().map(|sub| plug_hole(sub, hole, inner)).collect())) - } - Delimited(n, ch, ref body) => { Rc::new(Delimited(n, ch, plug_hole(&body, hole, inner))) } - _ => { - panic!("What are you doing? What do you even think will happen?") + Call(n) => { + if n == hole { + inner.clone() + } else { + outer.clone() + } } + Anyways(_) | Impossible | Literal(_) | AnyToken | AnyAtomicToken | VarRef => outer.clone(), + Seq(ref subs) => Rc::new(Seq(subs + .iter() + .map(|sub| plug_hole(sub, hole, inner)) + .collect())), + Delimited(n, ch, ref body) => Rc::new(Delimited(n, ch, plug_hole(&body, hole, inner))), + _ => panic!("What are you doing? What do you even think will happen?"), } } -pub use ::earley::parse; +pub use earley::parse; /** Parse `tt` with the grammar `f` in an empty syntactic environment. - `Call` patterns are errors. */ -pub fn parse_top<'fun>(f: &'fun FormPat, tt: &'fun TokenTree) - -> Result>*/ ::earley::ParseError> { - +`Call` patterns are errors. */ +pub fn parse_top<'fun>( + f: &'fun FormPat, + tt: &'fun TokenTree, +) -> Result>*/ ::earley::ParseError> { parse(f, &Assoc::new(), tt) } - use self::FormPat::*; #[test] fn basic_parsing() { - assert_eq!(parse_top(&Seq(vec![Rc::new(AnyToken)]), &tokens!("asdf")).unwrap(), ast_shape!("asdf")); - - assert_eq!(parse_top(&Seq(vec![Rc::new(AnyToken), Rc::new(Literal(n("fork"))), Rc::new(AnyToken)]), - &tokens!("asdf" "fork" "asdf")).unwrap(), - ast_shape!("asdf" "fork" "asdf")); + assert_eq!( + parse_top(&Seq(vec![Rc::new(AnyToken)]), &tokens!("asdf")).unwrap(), + ast_shape!("asdf") + ); - assert_eq!(parse_top(&Seq(vec![Rc::new(AnyToken), Rc::new(Literal(n("fork"))), Rc::new(AnyToken)]), - &tokens!("asdf" "fork" "asdf")).unwrap(), - ast_shape!("asdf" "fork" "asdf")); + assert_eq!( + parse_top( + &Seq(vec![ + Rc::new(AnyToken), + Rc::new(Literal(n("fork"))), + Rc::new(AnyToken) + ]), + &tokens!("asdf" "fork" "asdf") + ) + .unwrap(), + ast_shape!("asdf" "fork" "asdf") + ); - parse_top(&Seq(vec![Rc::new(AnyToken), Rc::new(Literal(n("fork"))), Rc::new(AnyToken)]), - &tokens!("asdf" "knife" "asdf")).unwrap_err(); + assert_eq!( + parse_top( + &Seq(vec![ + Rc::new(AnyToken), + Rc::new(Literal(n("fork"))), + Rc::new(AnyToken) + ]), + &tokens!("asdf" "fork" "asdf") + ) + .unwrap(), + ast_shape!("asdf" "fork" "asdf") + ); + + parse_top( + &Seq(vec![ + Rc::new(AnyToken), + Rc::new(Literal(n("fork"))), + Rc::new(AnyToken), + ]), + &tokens!("asdf" "knife" "asdf"), + ) + .unwrap_err(); - assert_eq!(parse_top(&Seq(vec![Rc::new(Star(Rc::new(Named(n("c"), Rc::new(Literal(n("*"))))))), - Rc::new(Literal(n("X")))]), - &tokens!("*" "*" "*" "*" "*" "X")).unwrap(), - ast_shape!({- "c" => ["*", "*", "*", "*", "*"] } "X")); + assert_eq!( + parse_top( + &Seq(vec![ + Rc::new(Star(Rc::new(Named(n("c"), Rc::new(Literal(n("*"))))))), + Rc::new(Literal(n("X"))) + ]), + &tokens!("*" "*" "*" "*" "*" "X") + ) + .unwrap(), + ast_shape!({- "c" => ["*", "*", "*", "*", "*"] } "X") + ); } #[test] fn alternation() { - assert_eq!(parse_top(&form_pat!((alt (lit "A"), (lit "B"))), &tokens!("A")), - Ok(ast!("A"))); - assert_eq!(parse_top(&form_pat!((alt (lit "A"), (lit "B"))), &tokens!("B")), - Ok(ast!("B"))); - - assert_eq!(parse_top( - &form_pat!((alt (lit "A"), (lit "B"), [(lit "X"), (lit "B")])), - &tokens!("X" "B")), - Ok(ast!(("X" "B")))); - - assert_eq!(parse_top( - &form_pat!((alt [(lit "A"), (lit "X")], (lit "B"), [(lit "A"), (lit "B")])), - &tokens!("A" "B")), - Ok(ast!(("A" "B")))); + assert_eq!( + parse_top(&form_pat!((alt (lit "A"), (lit "B"))), &tokens!("A")), + Ok(ast!("A")) + ); + assert_eq!( + parse_top(&form_pat!((alt (lit "A"), (lit "B"))), &tokens!("B")), + Ok(ast!("B")) + ); - assert_eq!(parse_top( - &form_pat!((alt (lit "A"), (lit "B"), [(lit "A"), (lit "B")])), - &tokens!("A" "B")), - Ok(ast!(("A" "B")))); + assert_eq!( + parse_top( + &form_pat!((alt (lit "A"), (lit "B"), [(lit "X"), (lit "B")])), + &tokens!("X" "B") + ), + Ok(ast!(("X" "B"))) + ); + assert_eq!( + parse_top( + &form_pat!((alt [(lit "A"), (lit "X")], (lit "B"), [(lit "A"), (lit "B")])), + &tokens!("A" "B") + ), + Ok(ast!(("A" "B"))) + ); + assert_eq!( + parse_top( + &form_pat!((alt (lit "A"), (lit "B"), [(lit "A"), (lit "B")])), + &tokens!("A" "B") + ), + Ok(ast!(("A" "B"))) + ); } #[test] fn advanced_parsing() { - assert_eq!(parse_top(&form_pat!([(star (named "c", (alt (lit "X"), (lit "O")))), (lit "!")]), - &tokens!("X" "O" "O" "O" "X" "X" "!")).unwrap(), - ast_shape!({- "c" => ["X", "O", "O", "O", "X", "X"]} "!")); + assert_eq!( + parse_top( + &form_pat!([(star (named "c", (alt (lit "X"), (lit "O")))), (lit "!")]), + &tokens!("X" "O" "O" "O" "X" "X" "!") + ) + .unwrap(), + ast_shape!({- "c" => ["X", "O", "O", "O", "X", "X"]} "!") + ); // TODO: this hits the bug where `earley.rs` doesn't like nullables in `Seq` or `Star` - assert_eq!(parse_top( + assert_eq!( + parse_top( &form_pat!((star (biased [(named "c", (anyways "ttt")), (alt (lit "X"), (lit "O"))], [(named "c", (anyways "igi")), (alt (lit "O"), (lit "H"))]))), - &tokens!("X" "O" "H" "O" "X" "H" "O")).unwrap(), - ast!({ - "c" => ["ttt", "ttt", "igi", "ttt", "ttt", "igi", "ttt"]})); + &tokens!("X" "O" "H" "O" "X" "H" "O") + ) + .unwrap(), + ast!({ - "c" => ["ttt", "ttt", "igi", "ttt", "ttt", "igi", "ttt"]}) + ); + + let ttt = simple_form( + "tictactoe", + form_pat!( [(named "c", (alt (lit "X"), (lit "O")))]), + ); + let igi = simple_form( + "igetit", + form_pat!( [(named "c", (alt (lit "O"), (lit "H")))]), + ); - - let ttt = simple_form("tictactoe", - form_pat!( [(named "c", (alt (lit "X"), (lit "O")))])); - let igi = simple_form("igetit", - form_pat!( [(named "c", (alt (lit "O"), (lit "H")))])); - - assert_eq!(parse_top( + assert_eq!( + parse_top( &form_pat!((star (named "outer", (biased (scope ttt.clone()), (scope igi.clone()))))), - &tokens!("X" "O" "H" "O" "X" "H" "O")).unwrap(), + &tokens!("X" "O" "H" "O" "X" "H" "O") + ) + .unwrap(), ast!({ - "outer" => [{ttt.clone(); ["c" => "X"]}, {ttt.clone(); ["c" => "O"]}, {igi.clone(); ["c" => "H"]}, {ttt.clone(); ["c" => "O"]}, {ttt.clone(); ["c" => "X"]}, {igi; ["c" => "H"]}, - {ttt; ["c" => "O"]}]})); - - let pair_form = simple_form("pair", form_pat!([(named "lhs", (lit "a")), - (named "rhs", (lit "b"))])); + {ttt; ["c" => "O"]}]}) + ); + + let pair_form = simple_form( + "pair", + form_pat!([(named "lhs", (lit "a")), + (named "rhs", (lit "b"))]), + ); let toks_a_b = tokens!("a" "b"); - assert_eq!(parse(&form_pat!((call "Expr")), - &assoc_n!( + assert_eq!( + parse( + &form_pat!((call "Expr")), + &assoc_n!( "other_1" => Rc::new(Scope(simple_form("o", form_pat!((lit "other"))), ::beta::ExportBeta::Nothing)), "Expr" => Rc::new(Scope(pair_form.clone(), ::beta::ExportBeta::Nothing)), "other_2" => Rc::new(Scope(simple_form("o", form_pat!((lit "otherother"))), ::beta::ExportBeta::Nothing))), - &toks_a_b).unwrap(), - ast!({pair_form ; ["rhs" => "b", "lhs" => "a"]})); + &toks_a_b + ) + .unwrap(), + ast!({pair_form ; ["rhs" => "b", "lhs" => "a"]}) + ); } - // TODO: We pretty much have to use Rc<> to store grammars in Earley // (that's fine; they're Rc<> already!). // But then, we pretty much have to store Earley rules in Rc<> also (ick!)... @@ -314,18 +396,19 @@ fn advanced_parsing() { #[test] fn extensible_parsing() { - fn static_synex(s: SynEnv, _: Ast) -> SynEnv { assoc_n!( "a" => Rc::new(form_pat!( (star (named "c", (alt (lit "AA"), [(lit "Back"), (call "o"), (lit ".")]))))), "b" => Rc::new(form_pat!((lit "BB"))) - ).set_assoc(&s) + ) + .set_assoc(&s) } - assert_eq!(parse_top(&form_pat!((extend [], "b", static_synex)), &tokens!("BB")), - Ok(ast!("BB"))); - + assert_eq!( + parse_top(&form_pat!((extend [], "b", static_synex)), &tokens!("BB")), + Ok(ast!("BB")) + ); let orig = Rc::new(assoc_n!( "o" => Rc::new(form_pat!( @@ -333,49 +416,76 @@ fn extensible_parsing() { (alt (lit "O"), [(lit "Extend"), (extend [], "a", static_synex), (lit ".")]))))))); assert_eq!( - parse(&form_pat!((call "o")), &orig, - &tokens!("O" "O" "Extend" "AA" "AA" "Back" "O" "." "AA" "." "O")).unwrap(), - ast!({- "c" => ["O", "O", ("Extend" {- "c" => ["AA", "AA", ("Back" {- "c" => ["O"]} "."), "AA"]} "."), "O"]})); + parse( + &form_pat!((call "o")), + &orig, + &tokens!("O" "O" "Extend" "AA" "AA" "Back" "O" "." "AA" "." "O") + ) + .unwrap(), + ast!({- "c" => ["O", "O", ("Extend" {- "c" => ["AA", "AA", ("Back" {- "c" => ["O"]} "."), "AA"]} "."), "O"]}) + ); assert_eq!( - parse(&form_pat!((call "o")), &orig, - &tokens!("O" "O" "Extend" "AA" "AA" "Back" "AA" "." "AA" "." "O")).is_err(), - true); + parse( + &form_pat!((call "o")), + &orig, + &tokens!("O" "O" "Extend" "AA" "AA" "Back" "AA" "." "AA" "." "O") + ) + .is_err(), + true + ); assert_eq!( - parse(&form_pat!((call "o")), &orig, - &tokens!("O" "O" "Extend" "O" "." "O")).is_err(), - true); + parse( + &form_pat!((call "o")), + &orig, + &tokens!("O" "O" "Extend" "O" "." "O") + ) + .is_err(), + true + ); let mt_syn_env = Rc::new(Assoc::new()); fn counter_synex(_: SynEnv, a: Ast) -> SynEnv { - let count = match a { IncompleteNode(mbe) => mbe, _ => panic!() } - .get_rep_leaf_or_panic(n("n")).len(); + let count = match a { + IncompleteNode(mbe) => mbe, + _ => panic!(), + } + .get_rep_leaf_or_panic(n("n")) + .len(); assoc_n!("count" => Rc::new(Literal(n(&count.to_string())))) } assert_m!( - parse(&form_pat!((extend (star (named "n", (lit "X"))), "count", counter_synex)), - &mt_syn_env, &tokens!("X" "X" "X" "4")), - Err(_)); + parse( + &form_pat!((extend (star (named "n", (lit "X"))), "count", counter_synex)), + &mt_syn_env, + &tokens!("X" "X" "X" "4") + ), + Err(_) + ); assert_eq!( - parse(&form_pat!((extend (star (named "n", (lit "X"))), "count", counter_synex)), - &mt_syn_env, &tokens!("X" "X" "X" "X" "4")), - Ok(ast!("4"))); + parse( + &form_pat!((extend (star (named "n", (lit "X"))), "count", counter_synex)), + &mt_syn_env, + &tokens!("X" "X" "X" "X" "4") + ), + Ok(ast!("4")) + ); assert_m!( - parse(&form_pat!((extend (star (named "n", (lit "X"))), "count", counter_synex)), - &mt_syn_env, &tokens!("X" "X" "X" "X" "X" "4")), - Err(_)); - - - + parse( + &form_pat!((extend (star (named "n", (lit "X"))), "count", counter_synex)), + &mt_syn_env, + &tokens!("X" "X" "X" "X" "X" "4") + ), + Err(_) + ); } - /* #[test] fn test_syn_env_parsing() as{ diff --git a/src/macros/macros.rs b/src/macros/macros.rs index 45fbf98..ef326e0 100644 --- a/src/macros/macros.rs +++ b/src/macros/macros.rs @@ -4,13 +4,15 @@ macro_rules! log { ($($e:expr),*) => { // print!( $($e),* ); - } + }; } /* Assoc */ macro_rules! expr_ify { - ($e:expr) => {$e}; + ($e:expr) => { + $e + }; } macro_rules! assoc_n { @@ -23,14 +25,15 @@ macro_rules! assoc_n { }; } - - - /* Beta */ macro_rules! beta_connector { - ( : ) => { ::beta::Basic }; - ( = ) => { ::beta::SameAs } + ( : ) => { + ::beta::Basic + }; + ( = ) => { + ::beta::SameAs + }; } macro_rules! beta { @@ -74,7 +77,6 @@ macro_rules! ebeta { }; } - /* Read */ macro_rules! tokens { @@ -94,8 +96,6 @@ macro_rules! t_elt { ($e:expr) => { Simple(::name::n($e)) } } - - /* Ast */ macro_rules! ast_shape { @@ -191,7 +191,7 @@ macro_rules! ty_err_p { // type error pattern /* EnvMBE */ /* These macros generate `EnvMBE`s, not arbitrary `EnvMBE`s, - which is a little un-abstract, but is the main usage. */ +which is a little un-abstract, but is the main usage. */ /* * Wait a second, I'm writing in Rust right now! I'll use an MBE macro to implement an MBE literal! @@ -276,7 +276,6 @@ macro_rules! mbe_one_name { } } - // Eventually, this ought to support more complex structures macro_rules! mbe { ( $( $lhs:tt => $rhs:tt ),* ) => {{ @@ -289,8 +288,6 @@ macro_rules! mbe { }} } - - /* FormPat */ // TODO #8: `ast!` and `form_pat!` are inconsistent with each other. @@ -337,16 +334,14 @@ macro_rules! form_pat { ::grammar::FormPat::Seq(vec![ $( ::std::rc::Rc::new(form_pat!($body)) ),* ])} } - - /* utility, for core_forms and core_type_forms */ // This has to be a macro for type reasons involving sizedness I don't understand. macro_rules! cust_rc_box { - ($contents:expr) => { Custom(::std::rc::Rc::new(Box::new($contents))) } + ($contents:expr) => { + Custom(::std::rc::Rc::new(Box::new($contents))) + }; } - - /* Form */ macro_rules! basic_typed_form { @@ -356,11 +351,13 @@ macro_rules! basic_typed_form { grammar: Rc::new(form_pat!($p)), type_compare: ::form::Positive(::ast_walk::WalkRule::NotWalked), synth_type: ::form::Positive($gen_type), - quasiquote: ::form::Both(::ast_walk::WalkRule::LiteralLike, - ::ast_walk::WalkRule::LiteralLike), - eval: ::form::Positive($eval) + quasiquote: ::form::Both( + ::ast_walk::WalkRule::LiteralLike, + ::ast_walk::WalkRule::LiteralLike, + ), + eval: ::form::Positive($eval), }) - } + }; } macro_rules! typed_form { @@ -370,11 +367,13 @@ macro_rules! typed_form { grammar: Rc::new(form_pat!($p)), type_compare: ::form::Positive(::ast_walk::WalkRule::NotWalked), synth_type: ::form::Positive($gen_type), - quasiquote: ::form::Both(::ast_walk::WalkRule::LiteralLike, - ::ast_walk::WalkRule::LiteralLike), - eval: ::form::Positive($eval) + quasiquote: ::form::Both( + ::ast_walk::WalkRule::LiteralLike, + ::ast_walk::WalkRule::LiteralLike, + ), + eval: ::form::Positive($eval), }) - } + }; } macro_rules! negative_typed_form { @@ -384,15 +383,15 @@ macro_rules! negative_typed_form { grammar: Rc::new(form_pat!($p)), type_compare: ::form::Positive(::ast_walk::WalkRule::NotWalked), synth_type: ::form::Negative($gen_type), - quasiquote: ::form::Both(::ast_walk::WalkRule::LiteralLike, - ::ast_walk::WalkRule::LiteralLike), - eval: ::form::Negative($eval) + quasiquote: ::form::Both( + ::ast_walk::WalkRule::LiteralLike, + ::ast_walk::WalkRule::LiteralLike, + ), + eval: ::form::Negative($eval), }) - } + }; } - - /* Value */ macro_rules! val { @@ -423,8 +422,6 @@ macro_rules! val { (, $interpolate:expr) => { $interpolate } } - - /* core_values stuff */ macro_rules! mk_type { // TODO: maybe now use find_core_form and un-thread $se? @@ -485,7 +482,6 @@ macro_rules! core_fn { } } - /* Alpha */ macro_rules! without_freshening { ($( $body:tt )*) => {{ @@ -501,37 +497,39 @@ macro_rules! without_freshening { }} } - /* for core_forms */ /* Unpacking `Ast`s into environments is a pain, so here's a macro for it*/ macro_rules! expect_node { - ( ($node:expr ; $form:expr) $env:ident ; $body:expr ) => ( + ( ($node:expr ; $form:expr) $env:ident ; $body:expr ) => { // This is tied to the signature of `Custom` if let Node(ref f, ref $env, _) = $node { if *f == $form { $body } else { // TODO: make it possible to specify which one - panic!("ICE or type error: Expected a {:#?} node, got {:#?}, which is {:#?}.", - $form, $node, *f) + panic!( + "ICE or type error: Expected a {:#?} node, got {:#?}, which is {:#?}.", + $form, $node, *f + ) } } else { - panic!("ICE or type error: Expected a {:#?} node, got {:#?}, which isn't a node.", - $form, $node) + panic!( + "ICE or type error: Expected a {:#?} node, got {:#?}, which isn't a node.", + $form, $node + ) } - ) + }; } macro_rules! expect_ty_node { - ( ($node:expr ; $form:expr ; $loc:expr) $env:ident ; $body:expr ) => ({ + ( ($node:expr ; $form:expr ; $loc:expr) $env:ident ; $body:expr ) => {{ // This is tied to the signature of `Custom` let $env = $node.destructure($form, $loc)?; $body - }) + }}; } - // TODO: this ought to have some MBE support macro_rules! destructure_node { ( ($node:expr ; $form:expr) $( $n:ident = $name:expr ),* ; $body:expr ) => ( @@ -554,7 +552,6 @@ macro_rules! forms_to_form_pat_export { } } - /* panicking destructor (when the type system should offer protection) */ macro_rules! extract { @@ -566,7 +563,6 @@ macro_rules! extract { } } - /* Reification helper (doesn't work on parameterized types...) */ macro_rules! cop_out_reifiability { @@ -590,13 +586,18 @@ macro_rules! assert_m { ($got:expr, $expected:pat, $body:expr) => {{ let got = $got; match got.clone() { - $expected => { assert!($body) } - _ => { assert!(false, "{:#?} does not match {:#?}", got, quote!($expected).as_str()) } + $expected => assert!($body), + _ => assert!( + false, + "{:#?} does not match {:#?}", + got, + quote!($expected).as_str() + ), } }}; ($got:expr, $expected:pat) => { assert_m!($got, $expected, true) - } + }; } macro_rules! layer_watch { diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 4e96c4c..68725ae 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -1,4 +1,4 @@ #![macro_use] pub mod macros; -pub mod reification_macros; \ No newline at end of file +pub mod reification_macros; diff --git a/src/macros/reification_macros.rs b/src/macros/reification_macros.rs index 3849450..61c70aa 100644 --- a/src/macros/reification_macros.rs +++ b/src/macros/reification_macros.rs @@ -1,25 +1,25 @@ #![macro_use] /* - This isn't as bad as it looks. - I mean, it's pretty bad, don't get me wrong... +This isn't as bad as it looks. +I mean, it's pretty bad, don't get me wrong... - The purpose is to generate `Reifiable` `impl`s - for any `enum` or `struct`. +The purpose is to generate `Reifiable` `impl`s + for any `enum` or `struct`. - Basically, `reify` pattern-matches to destructure the actual Rust value, - and then constructs a `Value` of the corresponding shape. +Basically, `reify` pattern-matches to destructure the actual Rust value, + and then constructs a `Value` of the corresponding shape. - And `reflect` does the opposite. +And `reflect` does the opposite. - But, in the process, I have to work around - what feels like every single limitation of `macro_rules!` in Rust, - as if I were aiming for them. +But, in the process, I have to work around + what feels like every single limitation of `macro_rules!` in Rust, + as if I were aiming for them. - Who wrote that piece of junk, anyway? +Who wrote that piece of junk, anyway? - This should be rewritten, now that user-defined derives are stable. - */ +This should be rewritten, now that user-defined derives are stable. +*/ macro_rules! Reifiable { // HACK: everything is parameterized over 't... @@ -248,9 +248,8 @@ macro_rules! Reifiable { } } - /* makes a pattern matching an enum with _n_ components, using the first _n_ - of the input names (be sure to supply enough names!) */ +of the input names (be sure to supply enough names!) */ macro_rules! choice_pat { ( ($t_car:ty $(, $t_cdr:ty)* ) ($i_car:ident $($i_cdr:ident)*) $choice:path; ($($accum:ident),*)) => { @@ -328,7 +327,6 @@ macro_rules! type_defn_wrapper { } } - macro_rules! refer_to_type { ($name:tt < $( $arg:ty ),* >) => { ast!({ "Type" "type_apply" : diff --git a/src/main.rs b/src/main.rs index 496f50b..4c41b36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,13 @@ // You shouldn't write code in Unseemly. // Instead, you should implement your programming language as Unseemly macros. -#![allow(dead_code,unused_macros,non_snake_case,unused_imports,non_upper_case_globals)] +#![allow( + dead_code, + unused_macros, + non_snake_case, + unused_imports, + non_upper_case_globals +)] // dead_code and unused_macros are hopefully temporary allowances // non_snake_case is stylistic, unused_imports is inaccurate for `cargo check` // non_upper_case_globals is stylistic; I like my thread_local!s lowercase. @@ -14,18 +20,21 @@ // unstable; only for testing // #[macro_use] extern crate log; -#[macro_use] extern crate lazy_static; +#[macro_use] +extern crate lazy_static; extern crate num; -#[macro_use] extern crate custom_derive; -#[macro_use] extern crate quote; -extern crate rustyline; -extern crate regex; +#[macro_use] +extern crate custom_derive; +#[macro_use] +extern crate quote; extern crate dirs; +extern crate regex; +extern crate rustyline; extern crate tap; -use std::path::Path; use std::fs::File; use std::io::Read; +use std::path::Path; mod macros; @@ -34,9 +43,9 @@ mod name; // should maybe be moved to `util`; `mbe` needs it mod util; mod alpha; +mod ast; mod beta; mod read; -mod ast; mod earley; mod grammar; @@ -45,51 +54,60 @@ mod unparse; mod form; mod ast_walk; -mod walk_mode; mod ty; mod ty_compare; +mod walk_mode; mod runtime; mod core_forms; -mod core_type_forms; -mod core_qq_forms; mod core_macro_forms; +mod core_qq_forms; +mod core_type_forms; +use name::{n, Name}; use runtime::core_values; -use std::cell::RefCell; -use util::assoc::Assoc; -use name::{Name, n}; -use ty::Ty; use runtime::eval::{eval, Value}; -use std::io::BufRead; use std::borrow::Cow; +use std::cell::RefCell; +use std::io::BufRead; +use ty::Ty; +use util::assoc::Assoc; thread_local! { pub static ty_env : RefCell> = RefCell::new(core_values::core_types()); pub static val_env : RefCell> = RefCell::new(core_values::core_values()); } -struct LineHelper { highlighter: rustyline::highlight::MatchingBracketHighlighter } +struct LineHelper { + highlighter: rustyline::highlight::MatchingBracketHighlighter, +} impl LineHelper { fn new() -> LineHelper { - LineHelper { highlighter: rustyline::highlight::MatchingBracketHighlighter::new() } + LineHelper { + highlighter: rustyline::highlight::MatchingBracketHighlighter::new(), + } } } impl rustyline::completion::Completer for LineHelper { type Candidate = String; - fn complete(&self, line: &str, pos: usize, _ctxt: &rustyline::Context) - -> Result<(usize, Vec), rustyline::error::ReadlineError> { + fn complete( + &self, + line: &str, + pos: usize, + _ctxt: &rustyline::Context, + ) -> Result<(usize, Vec), rustyline::error::ReadlineError> { let mut res = vec![]; - let (start, word_so_far) - = rustyline::completion::extract_word(line, pos, None, b"[({ })]"); + let (start, word_so_far) = rustyline::completion::extract_word(line, pos, None, b"[({ })]"); val_env.with(|vals| { let vals = vals.borrow(); for k in vals.iter_keys() { - if k.sp().starts_with(word_so_far) { res.push(k.sp()); } + if k.sp().starts_with(word_so_far) { + res.push(k.sp()); + } } }); Ok((start, res)) @@ -113,8 +131,10 @@ impl rustyline::highlight::Highlighter for LineHelper { self.highlighter.highlight_hint(hint) } fn highlight_candidate<'c>( - &self, candidate: &'c str, completion: rustyline::config::CompletionType) - -> Cow<'c, str> { + &self, + candidate: &'c str, + completion: rustyline::config::CompletionType, + ) -> Cow<'c, str> { self.highlighter.highlight_candidate(candidate, completion) } fn highlight_char(&self, line: &str, pos: usize) -> bool { @@ -125,11 +145,9 @@ impl rustyline::highlight::Highlighter for LineHelper { impl rustyline::Helper for LineHelper {} fn main() { - let arguments : Vec = std::env::args().collect(); - let prelude_filename = format!("{}/.unseemly_prelude", - dirs::home_dir().unwrap().display()); - let history_filename = format!("{}/.unseemly_history", - dirs::home_dir().unwrap().display()); + let arguments: Vec = std::env::args().collect(); + let prelude_filename = format!("{}/.unseemly_prelude", dirs::home_dir().unwrap().display()); + let history_filename = format!("{}/.unseemly_history", dirs::home_dir().unwrap().display()); if arguments.len() == 1 { let mut rl = rustyline::Editor::::new(); @@ -164,7 +182,8 @@ fn main() { let prelude = std::io::BufReader::new(prelude_file); for line in prelude.lines() { let line = line.unwrap(); - if comment.captures(&line).is_some() { /*comment*/ + if comment.captures(&line).is_some() { + /*comment*/ } else if let Some(caps) = assign_value.captures(&line) { if let Err(e) = assign_variable(&caps[1], &caps[2]) { println!(" Error in prelude line: {}\n {}", line, e); @@ -178,7 +197,6 @@ fn main() { println!(" [prelude loaded from {}]", prelude_filename); } - let _ = rl.load_history(&history_filename); while let Ok(line) = rl.readline("\x1b[1;36m≫\x1b[0m ") { // TODO: count delimiters, and allow line continuation! @@ -189,8 +207,7 @@ fn main() { } else if let Some(caps) = just_type.captures(&line) { type_unseemly_program(&caps[1]).map(|x| format!("{}", x)) } else if let Some(caps) = just_eval.captures(&line) { - eval_unseemly_program_without_typechecking(&caps[1]) - .map(|x| format!("{}", x)) + eval_unseemly_program_without_typechecking(&caps[1]).map(|x| format!("{}", x)) } else if let Some(caps) = canon_type.captures(&line) { canonicalize_type(&caps[1]).map(|x| format!("{}", x)) } else if let Some(caps) = assign_value.captures(&line) { @@ -199,12 +216,15 @@ fn main() { match assign_variable(&caps[2], &caps[3]) { Ok(_) => { use std::io::Write; - let mut prel_file = ::std::fs::OpenOptions::new().create(true).append(true) - .open(&prelude_filename).unwrap(); + let mut prel_file = ::std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(&prelude_filename) + .unwrap(); writeln!(prel_file, "{}", &caps[1]).unwrap(); Ok(format!("[saved to {}]", &prelude_filename)) } - Err(e) => Err(e) + Err(e) => Err(e), } } else if let Some(caps) = assign_type.captures(&line) { assign_t_var(&caps[1], &caps[2]).map(|x| format!("{}", x)) @@ -212,21 +232,23 @@ fn main() { match assign_t_var(&caps[2], &caps[3]) { Ok(_) => { use std::io::Write; - let mut prel_file = ::std::fs::OpenOptions::new().create(true).append(true) - .open(&prelude_filename).unwrap(); + let mut prel_file = ::std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(&prelude_filename) + .unwrap(); writeln!(prel_file, "{}", &caps[1]).unwrap(); Ok(format!("[saved to {}]", &prelude_filename)) } - Err(e) => Err(e) + Err(e) => Err(e), } } else { eval_unseemly_program(&line).map(|x| format!("{}", x)) }; - match result_display { Ok(v) => println!("\x1b[1;32m≉\x1b[0m {}", v), - Err(s) => println!("\x1b[1;31m✘\x1b[0m {}", s) + Err(s) => println!("\x1b[1;31m✘\x1b[0m {}", s), } } rl.save_history(&history_filename).unwrap(); @@ -243,7 +265,7 @@ fn main() { match result { Ok(v) => println!("{}", v), - Err(e) => println!("\x1b[1;31m✘\x1b[0m {:#?}", e) + Err(e) => println!("\x1b[1;31m✘\x1b[0m {:#?}", e), } } } @@ -268,12 +290,15 @@ fn assign_variable(name: &str, expr: &str) -> Result { fn assign_t_var(name: &str, t: &str) -> Result { let tokens = try!(read::read_tokens(t)); - let ast = try!(grammar::parse(&grammar::FormPat::Call(n("Type")), - &core_forms::get_core_forms(), &tokens).map_err(|e| e.msg)); + let ast = try!(grammar::parse( + &grammar::FormPat::Call(n("Type")), + &core_forms::get_core_forms(), + &tokens + ) + .map_err(|e| e.msg)); - let res = ty_env.with(|tys| { - ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e)) - }); + let res = ty_env + .with(|tys| ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e))); if let Ok(ref t) = res { ty_env.with(|tys| { @@ -288,20 +313,25 @@ fn assign_t_var(name: &str, t: &str) -> Result { fn canonicalize_type(t: &str) -> Result { let tokens = try!(read::read_tokens(t)); - let ast = try!(grammar::parse(&grammar::FormPat::Call(n("Type")), - &core_forms::get_core_forms(), &tokens).map_err(|e| e.msg)); + let ast = try!(grammar::parse( + &grammar::FormPat::Call(n("Type")), + &core_forms::get_core_forms(), + &tokens + ) + .map_err(|e| e.msg)); - ty_env.with(|tys| { - ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e)) - }) + ty_env.with(|tys| ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e))) } fn parse_unseemly_program(program: &str) -> Result { let tokens = try!(read::read_tokens(program)); - let ast = try!( - grammar::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens) - .map_err(|e| e.msg)); + let ast = try!(grammar::parse( + &core_forms::outermost_form(), + &core_forms::get_core_forms(), + &tokens + ) + .map_err(|e| e.msg)); Ok(format!("▵ {:#?}\n∴ {}\n", ast, ast)) } @@ -309,44 +339,44 @@ fn parse_unseemly_program(program: &str) -> Result { fn type_unseemly_program(program: &str) -> Result { let tokens = try!(read::read_tokens(program)); + let ast = try!(grammar::parse( + &core_forms::outermost_form(), + &core_forms::get_core_forms(), + &tokens + ) + .map_err(|e| e.msg)); - let ast = try!( - grammar::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens) - .map_err(|e| e.msg)); - - ty_env.with(|tys| { - ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e)) - }) + ty_env.with(|tys| ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e))) } fn eval_unseemly_program_without_typechecking(program: &str) -> Result { let tokens = try!(read::read_tokens(program)); - let ast : ::ast::Ast = try!( - grammar::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens) - .map_err(|e| e.msg)); + let ast: ::ast::Ast = try!(grammar::parse( + &core_forms::outermost_form(), + &core_forms::get_core_forms(), + &tokens + ) + .map_err(|e| e.msg)); - val_env.with(|vals| { - eval(&ast, vals.borrow().clone()).map_err(|_| "???".to_string()) - }) + val_env.with(|vals| eval(&ast, vals.borrow().clone()).map_err(|_| "???".to_string())) } - fn eval_unseemly_program(program: &str) -> Result { let tokens = try!(read::read_tokens(program)); - let ast : ::ast::Ast = try!( - grammar::parse(&core_forms::outermost_form(), &core_forms::get_core_forms(), &tokens) - .map_err(|e| e.msg)); + let ast: ::ast::Ast = try!(grammar::parse( + &core_forms::outermost_form(), + &core_forms::get_core_forms(), + &tokens + ) + .map_err(|e| e.msg)); let _type = try!(ty_env.with(|tys| { ty::synth_type(&ast, tys.borrow().clone()).map_err(|e| format!("{:#?}", e)) })); - - val_env.with(|vals| { - eval(&ast, vals.borrow().clone()).map_err(|_| "???".to_string()) - }) + val_env.with(|vals| eval(&ast, vals.borrow().clone()).map_err(|_| "???".to_string())) } #[test] @@ -355,113 +385,189 @@ fn simple_end_to_end_eval() { assert_eq!(eval_unseemly_program("(plus one one)"), Ok(val!(i 2))); - assert_eq!(eval_unseemly_program("(.[x : Int y : Int . (plus x y)]. one one)"), - Ok(val!(i 2))); + assert_eq!( + eval_unseemly_program("(.[x : Int y : Int . (plus x y)]. one one)"), + Ok(val!(i 2)) + ); - assert_eq!(eval_unseemly_program( - "((fix .[ again : [ -> [ Int -> Int ]] . + assert_eq!( + eval_unseemly_program( + "((fix .[ again : [ -> [ Int -> Int ]] . .[ n : Int . match (zero? n) { +[True]+ => one - +[False]+ => (times n ((again) (minus n one))) } ]. ].) five)"), - Ok(val!(i 120))); + +[False]+ => (times n ((again) (minus n one))) } ]. ].) five)" + ), + Ok(val!(i 120)) + ); } - #[test] fn end_to_end_int_list_tools() { + assert_m!( + assign_t_var( + "IntList", + "mu_type IntList . enum { Nil () Cons (Int IntList) }" + ), + Ok(_) + ); - assert_m!(assign_t_var("IntList", "mu_type IntList . enum { Nil () Cons (Int IntList) }"), - Ok(_)); - - assert_m!(assign_t_var("IntListUF", "enum { Nil () Cons (Int IntList) }"), - Ok(_)); + assert_m!( + assign_t_var("IntListUF", "enum { Nil () Cons (Int IntList) }"), + Ok(_) + ); - assert_m!(assign_variable( - "mt_ilist", "fold +[Nil]+ : enum { Nil () Cons (Int IntList) } : IntList"), - Ok(_)); + assert_m!( + assign_variable( + "mt_ilist", + "fold +[Nil]+ : enum { Nil () Cons (Int IntList) } : IntList" + ), + Ok(_) + ); - assert_m!(assign_variable("3_ilist", "fold +[Cons three mt_ilist]+ : IntListUF : IntList"), - Ok(_)); + assert_m!( + assign_variable( + "3_ilist", + "fold +[Cons three mt_ilist]+ : IntListUF : IntList" + ), + Ok(_) + ); - assert_m!(assign_variable("23_ilist", "fold +[Cons two 3_ilist]+ : IntListUF : IntList"), - Ok(_)); + assert_m!( + assign_variable( + "23_ilist", + "fold +[Cons two 3_ilist]+ : IntListUF : IntList" + ), + Ok(_) + ); - assert_m!(assign_variable("123_ilist", "fold +[Cons one 23_ilist]+ : IntListUF : IntList"), - Ok(_)); + assert_m!( + assign_variable( + "123_ilist", + "fold +[Cons one 23_ilist]+ : IntListUF : IntList" + ), + Ok(_) + ); - assert_m!(assign_variable("sum_int_list", - "(fix .[again : [-> [IntList -> Int]] . + assert_m!( + assign_variable( + "sum_int_list", + "(fix .[again : [-> [IntList -> Int]] . .[ lst : IntList . match unfold lst { - +[Nil]+ => zero +[Cons hd tl]+ => (plus hd ((again) tl))} ]. ]. )"), - Ok(_)); + +[Nil]+ => zero +[Cons hd tl]+ => (plus hd ((again) tl))} ]. ]. )" + ), + Ok(_) + ); - assert_eq!(eval_unseemly_program("(sum_int_list 123_ilist)"), Ok(val!(i 6))); + assert_eq!( + eval_unseemly_program("(sum_int_list 123_ilist)"), + Ok(val!(i 6)) + ); - assert_m!(assign_variable("int_list_len", - "(fix .[again : [-> [IntList -> Int]] . + assert_m!( + assign_variable( + "int_list_len", + "(fix .[again : [-> [IntList -> Int]] . .[ lst : IntList . match unfold lst { - +[Nil]+ => zero +[Cons hd tl]+ => (plus one ((again) tl))} ]. ].)"), - Ok(_)); - - assert_eq!(eval_unseemly_program("(int_list_len 123_ilist)"), Ok(val!(i 3))); + +[Nil]+ => zero +[Cons hd tl]+ => (plus one ((again) tl))} ]. ].)" + ), + Ok(_) + ); + assert_eq!( + eval_unseemly_program("(int_list_len 123_ilist)"), + Ok(val!(i 3)) + ); } #[test] fn end_to_end_list_tools() { assert_m!( - assign_t_var("List", "forall T . mu_type List . enum { Nil () Cons (T List <[T]<) }"), - Ok(_)); + assign_t_var( + "List", + "forall T . mu_type List . enum { Nil () Cons (T List <[T]<) }" + ), + Ok(_) + ); assert_m!( assign_t_var("ListUF", "forall T . enum { Nil () Cons (T List <[T]<) }"), - Ok(_)); + Ok(_) + ); - assert_m!(assign_variable( - "mt_list", "fold +[Nil]+ : enum { Nil () Cons (Int List <[Int]<) } : List <[Int]<"), - Ok(_)); + assert_m!( + assign_variable( + "mt_list", + "fold +[Nil]+ : enum { Nil () Cons (Int List <[Int]<) } : List <[Int]<" + ), + Ok(_) + ); assert_m!( - assign_variable("3_list", "fold +[Cons three mt_list]+ : ListUF <[Int]< : List <[Int]<"), - Ok(_)); + assign_variable( + "3_list", + "fold +[Cons three mt_list]+ : ListUF <[Int]< : List <[Int]<" + ), + Ok(_) + ); assert_m!( - assign_variable("23_list", "fold +[Cons two 3_list]+ : ListUF <[Int]< : List <[Int]<"), - Ok(_)); + assign_variable( + "23_list", + "fold +[Cons two 3_list]+ : ListUF <[Int]< : List <[Int]<" + ), + Ok(_) + ); assert_m!( - assign_variable("123_list", "fold +[Cons one 23_list]+ : ListUF <[Int]< : List <[Int]<"), - Ok(_)); + assign_variable( + "123_list", + "fold +[Cons one 23_list]+ : ListUF <[Int]< : List <[Int]<" + ), + Ok(_) + ); - assert_m!(assign_variable("list_len", - "forall S . (fix .[again : [-> [List <[S]< -> Int]] . + assert_m!( + assign_variable( + "list_len", + "forall S . (fix .[again : [-> [List <[S]< -> Int]] . .[ lst : List <[S]< . match unfold lst { +[Nil]+ => zero - +[Cons hd tl]+ => (plus one ((again) tl))} ]. ].)"), - Ok(_)); + +[Cons hd tl]+ => (plus one ((again) tl))} ]. ].)" + ), + Ok(_) + ); assert_eq!(eval_unseemly_program("(list_len 123_list)"), Ok(val!(i 3))); - assert_m!(assign_variable("map", - "forall T S . (fix .[again : [-> [List <[T]< [T -> S] -> List <[S]< ]] . + assert_m!( + assign_variable( + "map", + "forall T S . (fix .[again : [-> [List <[T]< [T -> S] -> List <[S]< ]] . .[ lst : List <[T]< f : [T -> S] . match unfold lst { +[Nil]+ => fold +[Nil]+ : ListUF <[S]< : List <[S]< +[Cons hd tl]+ => - fold +[Cons (f hd) ((again) tl f)]+ : ListUF <[S]< : List <[S]< } ]. ].)"), - Ok(_)); + fold +[Cons (f hd) ((again) tl f)]+ : ListUF <[S]< : List <[S]< } ]. ].)" + ), + Ok(_) + ); // TODO: what should even happen if you have `forall` not on the "outside"? // It should probably be an error to have a value typed with an underdetermined type. - // TODO: it's way too much of a pain to define each different expected result list. - assert_m!(eval_unseemly_program("(map 123_list .[x : Int . (plus x one)]. )"), Ok(_)); + assert_m!( + eval_unseemly_program("(map 123_list .[x : Int . (plus x one)]. )"), + Ok(_) + ); - assert_m!(eval_unseemly_program("(map 123_list .[x : Int . (equal? x two)]. )"), Ok(_)); + assert_m!( + eval_unseemly_program("(map 123_list .[x : Int . (equal? x two)]. )"), + Ok(_) + ); } #[test] @@ -471,39 +577,49 @@ fn end_to_end_quotation_basic() { Ok(_) ); - assert_m!( - eval_unseemly_program("'[Expr | (plus five five) ]'"), - Ok(_) - ); - + assert_m!(eval_unseemly_program("'[Expr | (plus five five) ]'"), Ok(_)); assert_m!( eval_unseemly_program("'[Expr | '[Expr | (plus five five) ]' ]'"), Ok(_) ); -//≫ .[s : Expr <[Int]< . '[Expr | ( ,[Expr | s], '[Expr | ,[Expr | s], ]')]' ]. - + //≫ .[s : Expr <[Int]< . '[Expr | ( ,[Expr | s], '[Expr | ,[Expr | s], ]')]' ]. } #[test] fn subtyping_direction() { // Let's check to make sure that "supertype" and "subtype" never got mixed up: - assert_m!(assign_variable("ident", "forall T . .[ a : T . a ]."), Ok(_)); + assert_m!( + assign_variable("ident", "forall T . .[ a : T . a ]."), + Ok(_) + ); assert_eq!(eval_unseemly_program("(ident five)"), Ok(val!(i 5))); - assert_m!(eval_unseemly_program("( .[ a : [Int -> Int] . a]. ident)"), Ok(_)); + assert_m!( + eval_unseemly_program("( .[ a : [Int -> Int] . a]. ident)"), + Ok(_) + ); - assert_m!(eval_unseemly_program("( .[ a : forall T . [T -> T] . a]. .[a : Int . a].)"), Err(_)); + assert_m!( + eval_unseemly_program("( .[ a : forall T . [T -> T] . a]. .[a : Int . a].)"), + Err(_) + ); assert_m!(eval_unseemly_program(".[ a : struct {} . a]."), Ok(_)); - assert_m!(eval_unseemly_program( - "( .[ a : struct {normal : Int extra : Int} . a]. *[normal : one]*)"), Err(_)); + assert_m!( + eval_unseemly_program("( .[ a : struct {normal : Int extra : Int} . a]. *[normal : one]*)"), + Err(_) + ); - assert_m!(eval_unseemly_program( - "( .[ a : struct {normal : Int} . a]. *[normal : one extra : five]*)"), Ok(_)); + assert_m!( + eval_unseemly_program( + "( .[ a : struct {normal : Int} . a]. *[normal : one extra : five]*)" + ), + Ok(_) + ); } #[test] @@ -512,61 +628,75 @@ fn end_to_end_quotation_advanced() { eval_unseemly_program( "(.[five_e : Expr <[Int]< . '[Expr | (plus five ,[Expr | five_e],) ]' ]. - '[Expr | five]')"), - eval_unseemly_program("'[Expr | (plus five five) ]'")); + '[Expr | five]')" + ), + eval_unseemly_program("'[Expr | (plus five five) ]'") + ); // Pass the wrong type (not really a test of quotation) assert_m!( type_unseemly_program( "(.[five_e : Expr <[Int]< . '[Expr | (plus five ,[Expr | five_e],) ]' ]. - '[Expr | true]')"), - Err(_)); + '[Expr | true]')" + ), + Err(_) + ); // Interpolate the wrong type assert_m!( type_unseemly_program( "(.[five_e : Expr <[Bool]< . '[Expr | (plus five ,[Expr | five_e],) ]' ]. - '[Expr | true]')"), - Err(_)); + '[Expr | true]')" + ), + Err(_) + ); // Interpolate the wrong type (no application needed to find the error) assert_m!( type_unseemly_program( - ".[five_e : Expr <[Bool]< . '[Expr | (plus five ,[Expr | five_e],) ]' ]."), - Err(_)); + ".[five_e : Expr <[Bool]< . '[Expr | (plus five ,[Expr | five_e],) ]' ]." + ), + Err(_) + ); assert_m!( eval_unseemly_program( "forall T . .[type : Type <[T]< rhs : Expr <[T]< - . '[Expr | (.[x : ,[Type <[T]< | type], . eight]. ,[Expr | rhs], )]' ]."), - Ok(_)); + . '[Expr | (.[x : ,[Type <[T]< | type], . eight]. ,[Expr | rhs], )]' ]." + ), + Ok(_) + ); - assert_m!( - eval_unseemly_program("'[Pat <[Nat]< | x]'"), - Ok(_)); + assert_m!(eval_unseemly_program("'[Pat <[Nat]< | x]'"), Ok(_)); // Actually import a pattern of quoted syntax: - assert_eq!(eval_unseemly_program( + assert_eq!( + eval_unseemly_program( "match '[Expr | (plus one two) ]' { - '[Expr <[Int]< | (plus ,[Expr <[Int]< | e], two) ]' => e }"), - Ok(val!(ast (vr "one")))); - + '[Expr <[Int]< | (plus ,[Expr <[Int]< | e], two) ]' => e }" + ), + Ok(val!(ast (vr "one"))) + ); // In order to have "traditional", non-type-annotated `let`, we want to ... reify T, I guess? // But the whole language has parametricity kinda baked in, and that seems to make it hard? // I think the solution is to build `let` into the language; // if a macro wants to have non-annotated binding, it's probably expandable to `let` anyways. - assert_m!(assign_variable("let", - "forall T S . .[binder : Pat <[T]< + assert_m!( + assign_variable( + "let", + "forall T S . .[binder : Pat <[T]< type : Type <[T]< rhs : Expr <[T]< body : Expr <[S]< . '[ Expr | (.[x : ,[Type | type], . match x { ,[Pat <[T]< | binder], => ,[Expr | body], } ]. - ,[Expr | rhs],)]' ]."), - Ok(_)); + ,[Expr | rhs],)]' ]." + ), + Ok(_) + ); without_freshening! { assert_eq!( @@ -577,5 +707,4 @@ fn end_to_end_quotation_advanced() { '[Expr <[Int]< | five]')"), eval_unseemly_program("'[Expr <[Int]< | (.[x : Int . match x {y => five}]. eight)]'")); } - } diff --git a/src/name.rs b/src/name.rs index a94dde3..a4bec8e 100644 --- a/src/name.rs +++ b/src/name.rs @@ -1,20 +1,20 @@ #![macro_use] +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use std::fmt; use std::string::String; -use std::collections::{HashMap, HashSet}; -use std::cell::RefCell; -#[derive(PartialEq,Eq,Clone,Copy,Hash)] +#[derive(PartialEq, Eq, Clone, Copy, Hash)] pub struct Name { - id: usize + id: usize, } pub struct Spelling { // No two different variables have this the same. Tomatoes may have been added: unique: String, // The original spelling that the programmer chose. - orig: String + orig: String, } thread_local! { @@ -32,7 +32,9 @@ thread_local! { } impl ::runtime::reify::Reifiable for Name { - fn ty_name() -> Name { n("Name") } + fn ty_name() -> Name { + n("Name") + } fn reify(&self) -> ::runtime::eval::Value { ::runtime::eval::Value::AbstractSyntax(::ast::Ast::Atom(*self)) @@ -46,9 +48,9 @@ impl ::runtime::reify::Reifiable for Name { // These are for isolating tests of alpha-equivalence from each other. -pub fn enable_fake_freshness(ff: bool) { +pub fn enable_fake_freshness(ff: bool) { use std::borrow::BorrowMut; - + fake_freshness.with(|fake_freshness_| { *fake_freshness_.borrow_mut() = ff; }) @@ -58,8 +60,12 @@ pub fn enable_fake_freshness(ff: bool) { // impl !Send for Name {} impl Name { - pub fn sp(self) -> String { spellings.with(|us| us.borrow()[self.id].unique.clone()) } - pub fn orig_sp(self) -> String { spellings.with(|us| us.borrow()[self.id].orig.clone()) } + pub fn sp(self) -> String { + spellings.with(|us| us.borrow()[self.id].unique.clone()) + } + pub fn orig_sp(self) -> String { + spellings.with(|us| us.borrow()[self.id].orig.clone()) + } // Printable names are unique, like `unique_spelling`s, but are assigned during printing. // This way if the compiler freshens some name a bunch of times, producing a tomato-filled mess, @@ -67,14 +73,18 @@ impl Name { pub fn print(self) -> String { printables.with(|printables_| { printables_used.with(|printables_used_| { - printables_.borrow_mut().entry(self.id).or_insert_with(|| { - let mut print_version = self.orig_sp(); - while printables_used_.borrow().contains(&print_version) { - print_version = format!("{}🥕", print_version); - } - printables_used_.borrow_mut().insert(print_version.clone()); - print_version.clone() - }).clone() + printables_ + .borrow_mut() + .entry(self.id) + .or_insert_with(|| { + let mut print_version = self.orig_sp(); + while printables_used_.borrow().contains(&print_version) { + print_version = format!("{}🥕", print_version); + } + printables_used_.borrow_mut().insert(print_version.clone()); + print_version.clone() + }) + .clone() }) }) } @@ -90,7 +100,7 @@ impl Name { } fn new(orig_spelling: &str, freshen: bool) -> Name { - use std::borrow::{BorrowMut, Borrow}; + use std::borrow::{Borrow, BorrowMut}; let fake_freshness_ = fake_freshness.with(|ff| *ff.borrow()); @@ -106,13 +116,12 @@ impl Name { unique_spelling = format!("{}🍅", orig_spelling); } - let claim_id = || { spellings.with(|spellings_| { let new_id = spellings_.borrow().len(); - spellings_.borrow_mut().push(Spelling { + spellings_.borrow_mut().push(Spelling { unique: unique_spelling.clone(), - orig: orig_spelling.to_owned() + orig: orig_spelling.to_owned(), }); new_id }) @@ -122,7 +131,10 @@ impl Name { let id = if freshen && !fake_freshness_ { claim_id() // ...don't put it in the table } else { - *id_map_.borrow_mut().entry(unique_spelling.clone()).or_insert_with(claim_id) + *id_map_ + .borrow_mut() + .entry(unique_spelling.clone()) + .or_insert_with(claim_id) }; Name { id: id } @@ -135,10 +147,8 @@ impl Name { pub fn is_name(self, n: Name) -> bool { self.sp() == n.sp() } - } - // TODO: move to `ast_walk` // TODO: using `lazy_static!` (with or without gensym) makes some tests fail. Why? /// Special name for negative `ast_walk`ing @@ -188,4 +198,4 @@ fn name_interning() { // Printable versions are first-come, first-served assert_eq!(a.freshen().print(), "a"); assert_eq!(a.print(), "a🥕"); -} \ No newline at end of file +} diff --git a/src/read.rs b/src/read.rs index 25f1d7b..f1942b6 100644 --- a/src/read.rs +++ b/src/read.rs @@ -17,34 +17,38 @@ custom_derive! { impl DelimChar { pub fn open(self) -> char { match self { - Paren => '(', SquareBracket => '[', CurlyBracket => '{' + Paren => '(', + SquareBracket => '[', + CurlyBracket => '{', } } pub fn close(self) -> char { match self { - Paren => ')', SquareBracket => ']', CurlyBracket => '}' + Paren => ')', + SquareBracket => ']', + CurlyBracket => '}', } } } use self::DelimChar::*; -#[derive(Debug,PartialEq,Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct TokenTree { - pub t: Vec + pub t: Vec, } -#[derive(Debug,PartialEq,Eq)] +#[derive(Debug, PartialEq, Eq)] pub enum Token { Simple(Name), - Group(Name, DelimChar, TokenTree) + Group(Name, DelimChar, TokenTree), } impl Token { pub fn is_just(&self, s: &str) -> bool { match *self { - Simple(ref x) if x.is(s) => { true } - _ => { false } + Simple(ref x) if x.is(s) => true, + _ => false, } } } @@ -54,14 +58,16 @@ pub use self::Token::*; // A token may start with an open delimiter, or end with a close delmiter, // but otherwise may not contain delimiters -const nondelim : &str = r"[^\[\]\(\)\{\}\s]"; -const open : &str = r"[\[\(\{]"; -const close : &str = r"[\]\)\}]"; +const nondelim: &str = r"[^\[\]\(\)\{\}\s]"; +const open: &str = r"[\[\(\{]"; +const close: &str = r"[\]\)\}]"; pub fn delim(s: &str) -> DelimChar { match s { - "(" | ")" => Paren, "[" | "]" => SquareBracket, "{" | "}" => CurlyBracket, - _ => {panic!("not a delimiter!")} + "(" | ")" => Paren, + "[" | "]" => SquareBracket, + "{" | "}" => CurlyBracket, + _ => panic!("not a delimiter!"), } } @@ -74,38 +80,50 @@ pub fn read_tokens(s: &str) -> Result { } let mut flat_tokens = token.captures_iter(s); - fn read_token_tree<'a>(flat_tokens: &mut regex::CaptureMatches<'a, 'a>) - -> Result<(TokenTree, Option<(DelimChar, &'a str)>), String> { - let mut this_level : Vec = vec![]; - loop{ + fn read_token_tree<'a>( + flat_tokens: &mut regex::CaptureMatches<'a, 'a>, + ) -> Result<(TokenTree, Option<(DelimChar, &'a str)>), String> { + let mut this_level: Vec = vec![]; + loop { match flat_tokens.next() { - None => { return Ok((TokenTree{ t: this_level }, None)) } + None => return Ok((TokenTree { t: this_level }, None)), Some(c) => { if let Some(normal) = c.name("normal") { this_level.push(Simple(n(normal.as_str()))); - } else if let (Some(_main), Some(o_del), Some(all)) - = (c.name("main_o"), c.name("open"), c.name("open_all")) { + } else if let (Some(_main), Some(o_del), Some(all)) = + (c.name("main_o"), c.name("open"), c.name("open_all")) + { let (inside, last) = read_token_tree(flat_tokens)?; if let Some(last) = last { - if format!("{}{}",last.1, o_del.as_str()) == all.as_str() { - this_level.push(Group(n(all.as_str()), - delim(o_del.as_str()), inside)); + if format!("{}{}", last.1, o_del.as_str()) == all.as_str() { + this_level.push(Group( + n(all.as_str()), + delim(o_del.as_str()), + inside, + )); } else { return Err(format!( "Unmatched delimiter names: \"{}\" is closed by \"{}\". \ Remember(this tokenizer is weird)Remember", - all.as_str(), last.1)); + all.as_str(), + last.1 + )); } } else { return Err(format!( - "Unclosed delimiter at EOF: \"{}\"", o_del.as_str())); + "Unclosed delimiter at EOF: \"{}\"", + o_del.as_str() + )); } } else if let (Some(main), Some(c_del)) = (c.name("main_c"), c.name("close")) { - return Ok((TokenTree{ t: this_level }, - Some((delim(c_del.as_str()), main.as_str())))); - } else { panic!("ICE") } - + return Ok(( + TokenTree { t: this_level }, + Some((delim(c_del.as_str()), main.as_str())), + )); + } else { + panic!("ICE") + } } } } @@ -115,19 +133,18 @@ pub fn read_tokens(s: &str) -> Result { match leftover { None => Ok(tt), - Some(l) => { Err(format!("Read error: leftover {:#?}", l)) } + Some(l) => Err(format!("Read error: leftover {:#?}", l)), } } - - - #[test] -fn simple_reading() { +fn simple_reading() { assert_eq!(read_tokens(""), Ok(tokens!())); assert_eq!(read_tokens("asdf"), Ok(tokens!("asdf"))); - assert_eq!(read_tokens("a s d-f d - f && a\na 8888"), - Ok(tokens!("a" "s" "d-f" "d" "-" "f" "&&" "a" "a" "8888"))); + assert_eq!( + read_tokens("a s d-f d - f && a\na 8888"), + Ok(tokens!("a" "s" "d-f" "d" "-" "f" "&&" "a" "a" "8888")) + ); } #[test] fn nested_reading() { @@ -137,6 +154,8 @@ fn nested_reading() { assert_eq!(read_tokens("f x(c)x"), Ok(tokens!("f" ("x";"c")))); assert_eq!(read_tokens("f x[d]x"), Ok(tokens!("f" ["x";"d"]))); assert_eq!(read_tokens("$-[()]$- x"), Ok(tokens!(["$-";("";)] "x"))); - assert_eq!(read_tokens("(#(5 6)# -[yy foo()foo aa]-)"), - Ok(tokens!((""; ("#"; "5" "6") ["-"; "yy" ("foo";) "aa"])))) + assert_eq!( + read_tokens("(#(5 6)# -[yy foo()foo aa]-)"), + Ok(tokens!((""; ("#"; "5" "6") ["-"; "yy" ("foo";) "aa"]))) + ) } diff --git a/src/runtime/core_values.rs b/src/runtime/core_values.rs index 1da8470..89fb926 100644 --- a/src/runtime/core_values.rs +++ b/src/runtime/core_values.rs @@ -1,23 +1,25 @@ use ast::Ast; -use ty::Ty; -use runtime::eval::{Value, BIF, eval}; -use runtime::eval::Value::*; -use util::assoc::Assoc; use name::*; +use runtime::eval::Value::*; +use runtime::eval::{eval, Value, BIF}; use std::rc::Rc; +use ty::Ty; +use util::assoc::Assoc; +use num::BigInt; -use num::{BigInt}; - -#[derive(Debug,Clone,PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct TypedValue { pub ty: Ast, - pub val: Value + pub val: Value, } -pub fn erase_type(tv: &TypedValue) -> Value { tv.val.clone() } -pub fn erase_value(tv: &TypedValue) -> Ty { Ty::new(tv.ty.clone()) } - +pub fn erase_type(tv: &TypedValue) -> Value { + tv.val.clone() +} +pub fn erase_value(tv: &TypedValue) -> Ty { + Ty::new(tv.ty.clone()) +} pub fn core_typed_values() -> Assoc { assoc_n!( @@ -81,21 +83,36 @@ pub fn core_values() -> Assoc { } pub fn core_types() -> Assoc { - use ::core_type_forms::get__abstract_parametric_type; - core_typed_values().map(&erase_value) - .set(n("Bool"), ty!( - {"Type" "enum" : "name" => [@"c" "True", "False"], "component" => [@"c" [], []]})) + use core_type_forms::get__abstract_parametric_type; + core_typed_values() + .map(&erase_value) + .set( + n("Bool"), + ty!( + {"Type" "enum" : "name" => [@"c" "True", "False"], "component" => [@"c" [], []]}), + ) // These need to be in the environment, not just atomic types // because we sometimes look them up internally in the compiler // in the environment, // not just as programmers, looking them up by syntax, where this whole thing is a wash. - .set(n("Pat"), ty!({get__abstract_parametric_type() ; "name" => "Pat" })) - .set(n("Type"), ty!({get__abstract_parametric_type() ; "name" => "Type" })) - .set(n("Expr"), ty!({get__abstract_parametric_type() ; "name" => "Expr" })) - .set(n("Sequence"), ty!({get__abstract_parametric_type() ; "name" => "Sequence" })) + .set( + n("Pat"), + ty!({get__abstract_parametric_type() ; "name" => "Pat" }), + ) + .set( + n("Type"), + ty!({get__abstract_parametric_type() ; "name" => "Type" }), + ) + .set( + n("Expr"), + ty!({get__abstract_parametric_type() ; "name" => "Expr" }), + ) + .set( + n("Sequence"), + ty!({get__abstract_parametric_type() ; "name" => "Sequence" }), + ) } - #[test] fn basic_core_value_evaluation() { use core_forms::find_core_form; @@ -103,19 +120,23 @@ fn basic_core_value_evaluation() { let cte = core_typed_values(); let ce = cte.map(&erase_type); - assert_eq!(eval( - &ast!({ find_core_form( "Expr", "apply") ; - "rator" => (vr "plus"), - "rand" => [ (vr "one"), (vr "one") ] - }), - ce), - Ok(Int(BigInt::from(2)))); + assert_eq!( + eval( + &ast!({ find_core_form( "Expr", "apply") ; + "rator" => (vr "plus"), + "rand" => [ (vr "one"), (vr "one") ] + }), + ce + ), + Ok(Int(BigInt::from(2))) + ); } #[test] fn fixpoint_evaluation() { - assert_eq!(eval( - &ast!( {"Expr" "apply" : "rator" => + assert_eq!( + eval( + &ast!( {"Expr" "apply" : "rator" => { "Expr" "apply" : "rator" => (vr "fix"), "rand" => [{ "Expr" "lambda" : @@ -145,6 +166,8 @@ fn fixpoint_evaluation() { "rator" => (vr "minus"), "rand" => [(vr "n"), (vr "one")]}]}]})]})})}]}, "rand" => [(vr "five")]}), - core_values()), - Ok(val!(i 120))); + core_values() + ), + Ok(val!(i 120)) + ); } diff --git a/src/runtime/eval.rs b/src/runtime/eval.rs index d2e0208..a80dbfe 100644 --- a/src/runtime/eval.rs +++ b/src/runtime/eval.rs @@ -1,28 +1,28 @@ #![macro_use] -use num::bigint::BigInt; -use util::assoc::Assoc; -use name::*; -use std::rc::Rc; use ast::Ast; -use ast_walk::{walk, WalkRule, LazyWalkReses}; -use walk_mode::{WalkMode, NegativeWalkMode}; +use ast_walk::{walk, LazyWalkReses, WalkRule}; use form::Form; +use name::*; +use num::bigint::BigInt; use std; +use std::rc::Rc; +use util::assoc::Assoc; +use walk_mode::{NegativeWalkMode, WalkMode}; /** * Values in Unseemly. */ -#[derive(Debug,Clone,PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Value { Int(BigInt), Sequence(Vec>), // TODO: switch to a different core sequence type - Function(Rc), // TODO: unsure if this Rc is needed + Function(Rc), // TODO: unsure if this Rc is needed BuiltInFunction(BIF), AbstractSyntax(Ast), // Unsure if this needs an Rc. Struct(Assoc), - Enum(Name, Vec) // A real compiler would probably tag with numbers... + Enum(Name, Vec), // A real compiler would probably tag with numbers... } pub use self::Value::*; @@ -31,7 +31,7 @@ pub use self::Value::*; pub struct Closure { pub body: Ast, pub params: Vec, - pub env: Assoc + pub env: Assoc, } // Built-in function @@ -52,23 +52,28 @@ impl Clone for BIF { impl std::fmt::Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { match *self { - Int(ref bi) => { write!(f, "{}", bi) } + Int(ref bi) => write!(f, "{}", bi), Sequence(ref seq) => { - for elt in seq { try!(write!(f, "{}", &*elt)); }; Ok(()) + for elt in seq { + try!(write!(f, "{}", &*elt)); + } + Ok(()) } - Function(_) => { write!(f, "[closure]") } - BuiltInFunction(_) => { write!(f, "[built-in function]") } - AbstractSyntax(ref ast) => { write!(f, "'[{}]'", ast) } + Function(_) => write!(f, "[closure]"), + BuiltInFunction(_) => write!(f, "[built-in function]"), + AbstractSyntax(ref ast) => write!(f, "'[{}]'", ast), Struct(ref parts) => { try!(write!(f, "*[")); - for (k,v) in parts.iter_pairs() { + for (k, v) in parts.iter_pairs() { try!(write!(f, "{}: {} ", k, v)); } write!(f, "]*") } Enum(n, ref parts) => { try!(write!(f, "+[{}", n)); - for p in parts.iter() { try!(write!(f, " {}", p)); } + for p in parts.iter() { + try!(write!(f, " {}", p)); + } write!(f, "]+") } } @@ -82,30 +87,34 @@ impl std::fmt::Debug for BIF { } impl ::walk_mode::WalkElt for Value { - fn from_ast(a: &Ast) -> Value { AbstractSyntax(a.clone()) } + fn from_ast(a: &Ast) -> Value { + AbstractSyntax(a.clone()) + } fn to_ast(&self) -> Ast { match *self { AbstractSyntax(ref a) => a.clone(), - _ => panic!("Type error: {} is not syntax", self) + _ => panic!("Type error: {} is not syntax", self), } } - fn core_env() -> Assoc { ::runtime::core_values::core_values() } + fn core_env() -> Assoc { + ::runtime::core_values::core_values() + } } - - -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct Eval {} } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct Destructure {} } impl WalkMode for Eval { - fn name() -> &'static str { "Evalu" } + fn name() -> &'static str { + "Evalu" + } type Elt = Value; type Negated = Destructure; @@ -113,19 +122,31 @@ impl WalkMode for Eval { type D = ::walk_mode::Positive; type ExtraInfo = (); - fn get_walk_rule(f: &Form) -> WalkRule { f.eval.pos().clone() } - fn automatically_extend_env() -> bool { true } + fn get_walk_rule(f: &Form) -> WalkRule { + f.eval.pos().clone() + } + fn automatically_extend_env() -> bool { + true + } fn walk_var(n: Name, cnc: &LazyWalkReses) -> Result { - Ok(cnc.env.find(&n).expect("Undefined var; did you use a type name as a value?").clone()) + Ok(cnc + .env + .find(&n) + .expect("Undefined var; did you use a type name as a value?") + .clone()) } // TODO: maybe keep this from being called? - fn underspecified(_: Name) -> Value { val!(enum "why is this here?", ) } + fn underspecified(_: Name) -> Value { + val!(enum "why is this here?", ) + } } impl WalkMode for Destructure { - fn name() -> &'static str { "Destr" } + fn name() -> &'static str { + "Destr" + } type Elt = Value; type Negated = Eval; @@ -136,20 +157,29 @@ impl WalkMode for Destructure { /// The whole point of program evaluation is that the enviornment /// isn't generateable from the source tree. /// Does that make sense? I suspect it does not. - fn get_walk_rule(f: &Form) -> WalkRule { f.eval.neg().clone() } - fn automatically_extend_env() -> bool { true } // TODO: think about this + fn get_walk_rule(f: &Form) -> WalkRule { + f.eval.neg().clone() + } + fn automatically_extend_env() -> bool { + true + } // TODO: think about this } impl NegativeWalkMode for Destructure { - fn needs_pre_match() -> bool { false } // Values don't have binding (in this mode!) + fn needs_pre_match() -> bool { + false + } // Values don't have binding (in this mode!) } impl ::walk_mode::WalkElt for Ast { - fn from_ast(a: &Ast) -> Ast { a.clone() } - fn to_ast(&self) -> Ast { self.clone() } + fn from_ast(a: &Ast) -> Ast { + a.clone() + } + fn to_ast(&self) -> Ast { + self.clone() + } } - pub fn eval_top(expr: &Ast) -> Result { eval(expr, Assoc::new()) } @@ -158,22 +188,23 @@ pub fn eval(expr: &Ast, env: Assoc) -> Result { walk::(expr, &LazyWalkReses::new_wrapper(env)) } -pub fn neg_eval(pat: &Ast, env: Assoc) - -> Result,()> { +pub fn neg_eval(pat: &Ast, env: Assoc) -> Result, ()> { walk::(pat, &LazyWalkReses::new_wrapper(env)) } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct QQuote {} } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct QQuoteDestr {} } impl WalkMode for QQuote { - fn name() -> &'static str { "QQuote" } + fn name() -> &'static str { + "QQuote" + } // Why not `Ast`? Because QQuote and Eval need to share environments. type Elt = Value; @@ -190,12 +221,18 @@ impl WalkMode for QQuote { let n_sp = &n.sp(); Ok(val!(ast n_sp)) } - fn get_walk_rule(f: &Form) -> WalkRule { f.quasiquote.pos().clone() } - fn automatically_extend_env() -> bool { false } + fn get_walk_rule(f: &Form) -> WalkRule { + f.quasiquote.pos().clone() + } + fn automatically_extend_env() -> bool { + false + } } impl WalkMode for QQuoteDestr { - fn name() -> &'static str { "QQDes" } + fn name() -> &'static str { + "QQDes" + } type Elt = Value; type Negated = QQuote; @@ -208,7 +245,10 @@ impl WalkMode for QQuoteDestr { if cnc.context_elt() == &val!(ast (vr n_sp)) { Ok(Assoc::::new()) } else { - Err(Self::qlit_mismatch_error(val!(ast (vr n_sp)), cnc.context_elt().clone())) + Err(Self::qlit_mismatch_error( + val!(ast (vr n_sp)), + cnc.context_elt().clone(), + )) } } fn walk_atom(n: Name, cnc: &LazyWalkReses) -> Result, ()> { @@ -216,18 +256,26 @@ impl WalkMode for QQuoteDestr { if cnc.context_elt() == &val!(ast n_sp) { Ok(Assoc::::new()) } else { - Err(Self::qlit_mismatch_error(val!(ast (vr n_sp)), cnc.context_elt().clone())) + Err(Self::qlit_mismatch_error( + val!(ast (vr n_sp)), + cnc.context_elt().clone(), + )) } } - fn get_walk_rule(f: &Form) -> WalkRule { f.quasiquote.neg().clone() } - fn automatically_extend_env() -> bool { false } + fn get_walk_rule(f: &Form) -> WalkRule { + f.quasiquote.neg().clone() + } + fn automatically_extend_env() -> bool { + false + } } impl NegativeWalkMode for QQuoteDestr { - fn needs_pre_match() -> bool { true } // Quoted syntax does have binding! + fn needs_pre_match() -> bool { + true + } // Quoted syntax does have binding! } - // `env` is a trap! We want a shifted `LazyWalkReses`! /* pub fn qquote(expr: &Ast, env: Assoc) -> Result { diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index f4015ea..742e8ce 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,3 +1,3 @@ -pub mod eval; pub mod core_values; -pub mod reify; \ No newline at end of file +pub mod eval; +pub mod reify; diff --git a/src/runtime/reify.rs b/src/runtime/reify.rs index 94b746f..56ab99f 100644 --- a/src/runtime/reify.rs +++ b/src/runtime/reify.rs @@ -3,8 +3,8 @@ pub use ast::Ast; pub use name::*; pub use runtime::eval::Value; -use std::rc::Rc; use num::bigint::BigInt; +use std::rc::Rc; use util::assoc::Assoc; /** This is for parts of this compiler that need to be represented as object-level values. @@ -29,17 +29,21 @@ pub trait Reifiable { /// TODO: this should return `Ty` fn ty() -> Ast { // By default, this is an opaque primitive. - Ast::Node(Rc::new(::form::Form { - name: Self::ty_name(), - grammar: Rc::new(::grammar::FormPat::Impossible), - type_compare: ::form::Positive(::ast_walk::WalkRule::NotWalked), - synth_type: ::form::Positive(::ast_walk::WalkRule::LiteralLike), - quasiquote: ::form::Both(::ast_walk::WalkRule::LiteralLike, - ::ast_walk::WalkRule::LiteralLike), - eval: ::form::Positive(::ast_walk::WalkRule::NotWalked), - }), - ::util::mbe::EnvMBE::new(), - ::beta::ExportBeta::Nothing) + Ast::Node( + Rc::new(::form::Form { + name: Self::ty_name(), + grammar: Rc::new(::grammar::FormPat::Impossible), + type_compare: ::form::Positive(::ast_walk::WalkRule::NotWalked), + synth_type: ::form::Positive(::ast_walk::WalkRule::LiteralLike), + quasiquote: ::form::Both( + ::ast_walk::WalkRule::LiteralLike, + ::ast_walk::WalkRule::LiteralLike, + ), + eval: ::form::Positive(::ast_walk::WalkRule::NotWalked), + }), + ::util::mbe::EnvMBE::new(), + ::beta::ExportBeta::Nothing, + ) } /// A name for that type, so that recursive types are okay. @@ -52,7 +56,9 @@ pub trait Reifiable { /// e.g. `Annotated_with_int<[nat]<` /// (Types using this type will use this, rather than `ty`) /// This must be customized if `ty` is, I think... - fn ty_invocation() -> Ast { Ast::VariableReference(Self::ty_name()) } + fn ty_invocation() -> Ast { + Ast::VariableReference(Self::ty_name()) + } /// The Unseemly value that corresponds to a value. fn reify(&self) -> Value; @@ -82,10 +88,12 @@ macro_rules! basic_reifiability { basic_reifiability!(BigInt, "Int", Int); impl Reifiable for bool { - fn ty_name() -> Name { n("Bool") } + fn ty_name() -> Name { + n("Bool") + } fn reify(&self) -> Value { - ::runtime::eval::Value::Enum(n( if *self {"True"} else {"False"} ) , vec![]) + ::runtime::eval::Value::Enum(n(if *self { "True" } else { "False" }), vec![]) } fn reflect(v: &Value) -> Self { @@ -95,9 +103,13 @@ impl Reifiable for bool { // note: operations for these shouldn't have BigInt semantics! impl Reifiable for usize { - fn ty_name() -> Name { n("Rust_usize") } + fn ty_name() -> Name { + n("Rust_usize") + } - fn reify(&self) -> Value { Value::Int(BigInt::from(*self)) } + fn reify(&self) -> Value { + Value::Int(BigInt::from(*self)) + } fn reflect(v: &Value) -> Self { use num::ToPrimitive; @@ -105,9 +117,13 @@ impl Reifiable for usize { } } impl Reifiable for i32 { - fn ty_name() -> Name { n("Rust_i32") } + fn ty_name() -> Name { + n("Rust_i32") + } - fn reify(&self) -> Value { Value::Int(BigInt::from(*self)) } + fn reify(&self) -> Value { + Value::Int(BigInt::from(*self)) + } fn reflect(v: &Value) -> Self { use num::ToPrimitive; @@ -115,9 +131,13 @@ impl Reifiable for i32 { } } impl Reifiable for u8 { - fn ty_name() -> Name { n("Rust_u8") } + fn ty_name() -> Name { + n("Rust_u8") + } - fn reify(&self) -> Value { Value::Int(BigInt::from(*self)) } + fn reify(&self) -> Value { + Value::Int(BigInt::from(*self)) + } fn reflect(v: &Value) -> Self { use num::ToPrimitive; @@ -125,67 +145,91 @@ impl Reifiable for u8 { } } impl Reifiable for () { - fn ty_name() -> Name { n("unit") } + fn ty_name() -> Name { + n("unit") + } - fn reify(&self) -> Value { Value::Int(BigInt::from(0)) } + fn reify(&self) -> Value { + Value::Int(BigInt::from(0)) + } - fn reflect(_: &Value) -> Self { () } + fn reflect(_: &Value) -> Self { + () + } } // This is right, right? impl Reifiable for Value { - fn ty_name() -> Name { n("any") } + fn ty_name() -> Name { + n("any") + } - fn reify(&self) -> Value { self.clone() } + fn reify(&self) -> Value { + self.clone() + } - fn reflect(v: &Value) -> Self { v.clone() } + fn reflect(v: &Value) -> Self { + v.clone() + } } // TODO: when returning traits works, just make functions `Reifiable` // TOUNDERSTAND: 'x also allows things to be owned instead?!? pub fn reify_1ary_function( - f: Rc R)>>) -> Value { - Value::BuiltInFunction(::runtime::eval::BIF(Rc::new( - move |args: Vec| ((*f)(A::reflect(&args[0]))).reify()))) + f: Rc R)>>, +) -> Value { + Value::BuiltInFunction(::runtime::eval::BIF(Rc::new(move |args: Vec| { + ((*f)(A::reflect(&args[0]))).reify() + }))) } pub fn reflect_1ary_function( - f_v: Value) -> Rc R)>> { - Rc::new(Box::new(move |a: A| + f_v: Value, +) -> Rc R)>> { + Rc::new(Box::new(move |a: A| { extract!((&f_v) - Value::BuiltInFunction = (ref bif) => R::reflect(&(*bif.0)(vec![a.reify()])); - Value::Function = (ref closure) => { - R::reflect(&::runtime::eval::eval(&closure.body, - closure.env.clone().set(closure.params[0], a.reify())).unwrap()) - }))) + Value::BuiltInFunction = (ref bif) => R::reflect(&(*bif.0)(vec![a.reify()])); + Value::Function = (ref closure) => { + R::reflect(&::runtime::eval::eval(&closure.body, + closure.env.clone().set(closure.params[0], a.reify())).unwrap()) + }) + })) } // I bet there's more of a need for reification than reflection for functions.... -pub fn reify_2ary_function( - f: Rc R)>>) -> Value { - Value::BuiltInFunction(::runtime::eval::BIF(Rc::new( - move |args: Vec| ((*f)(A::reflect(&args[0]), B::reflect(&args[1]))).reify()))) -} - -pub fn reflect_2ary_function( - f_v: Value) -> Rc R)>> { - Rc::new(Box::new(move |a: A, b: B| +pub fn reify_2ary_function< + A: Reifiable + 'static, + B: Reifiable + 'static, + R: Reifiable + 'static, +>( + f: Rc R)>>, +) -> Value { + Value::BuiltInFunction(::runtime::eval::BIF(Rc::new(move |args: Vec| { + ((*f)(A::reflect(&args[0]), B::reflect(&args[1]))).reify() + }))) +} + +pub fn reflect_2ary_function< + A: Reifiable + 'static, + B: Reifiable + 'static, + R: Reifiable + 'static, +>( + f_v: Value, +) -> Rc R)>> { + Rc::new(Box::new(move |a: A, b: B| { extract!((&f_v) - Value::BuiltInFunction = (ref bif) => - R::reflect(&(*bif.0)(vec![a.reify(), b.reify()])); - Value::Function = (ref closure) => { - R::reflect(&::runtime::eval::eval(&closure.body, - closure.env.clone().set(closure.params[0], a.reify()) - .set(closure.params[1], b.reify())).unwrap()) - }))) + Value::BuiltInFunction = (ref bif) => + R::reflect(&(*bif.0)(vec![a.reify(), b.reify()])); + Value::Function = (ref closure) => { + R::reflect(&::runtime::eval::eval(&closure.body, + closure.env.clone().set(closure.params[0], a.reify()) + .set(closure.params[1], b.reify())).unwrap()) + }) + })) } - -pub fn ty_of_1ary_function() - -> Ast { - ast!( "TODO: generate type" ) +pub fn ty_of_1ary_function() -> Ast { + ast!("TODO: generate type") } macro_rules! reify_types { @@ -201,11 +245,17 @@ macro_rules! reify_types { macro_rules! fake_reifiability { ( $underlying_type:ty ) => { impl Reifiable for $underlying_type { - fn ty_name() -> Name { n(stringify!($underlying_type)) } - fn reify(&self) -> Value { panic!() } - fn reflect(_: &Value) -> Self { panic!() } + fn ty_name() -> Name { + n(stringify!($underlying_type)) + } + fn reify(&self) -> Value { + panic!() + } + fn reflect(_: &Value) -> Self { + panic!() + } } - } + }; } /* @@ -220,12 +270,10 @@ fake_reifiability!(K); struct V {} fake_reifiability!(V); - pub fn make_reified_ty_env() -> Assoc { reify_types!(Ast, Assoc) } - /* impl Reifiable for Box R> { fn ty() -> Ast { panic!("") } @@ -236,22 +284,30 @@ impl Reifiable for Box R> { } */ - - // We can't add derive() to existing types, but we can `impl` these ourselves directly // This feels a little awkward, just dropping the `Rc`ness on the floor. // But I think `Value` has enouch `Rc` inside that nothing can go wrong... right? impl Reifiable for ::std::rc::Rc { - fn ty() -> Ast { T::ty() } + fn ty() -> Ast { + T::ty() + } - fn ty_name() -> Name { T::ty_name() } + fn ty_name() -> Name { + T::ty_name() + } - fn ty_invocation() -> Ast { T::ty_invocation() } + fn ty_invocation() -> Ast { + T::ty_invocation() + } - fn reify(&self) -> Value { (**self).reify() } + fn reify(&self) -> Value { + (**self).reify() + } - fn reflect(v: &Value) -> Self { ::std::rc::Rc::new(T::reflect(v)) } + fn reflect(v: &Value) -> Self { + ::std::rc::Rc::new(T::reflect(v)) + } } // for when we have a `Ty`, rather than a Rust type. @@ -275,8 +331,9 @@ impl Reifiable for Vec { "arg" => [(, T::ty() )]}) } - - fn ty_name() -> Name { n(&format!("Sequence_of_{}", T::ty_name())) } + fn ty_name() -> Name { + n(&format!("Sequence_of_{}", T::ty_name())) + } fn ty_invocation() -> Ast { ast!({ "Type" "type_apply" : @@ -297,32 +354,54 @@ impl Reifiable for Vec { } impl Reifiable for ::std::boxed::Box { - fn ty() -> Ast { T::ty() } + fn ty() -> Ast { + T::ty() + } - fn ty_name() -> Name { T::ty_name() } + fn ty_name() -> Name { + T::ty_name() + } - fn ty_invocation() -> Ast { T::ty_invocation() } + fn ty_invocation() -> Ast { + T::ty_invocation() + } - fn reify(&self) -> Value { (**self).reify() } + fn reify(&self) -> Value { + (**self).reify() + } - fn reflect(v: &Value) -> Self { ::std::boxed::Box::new(T::reflect(v)) } + fn reflect(v: &Value) -> Self { + ::std::boxed::Box::new(T::reflect(v)) + } } // The roundtrip will de-alias the cell, sadly. impl Reifiable for ::std::cell::RefCell { - fn ty_name() -> Name { n("Rust_RefCell") } + fn ty_name() -> Name { + n("Rust_RefCell") + } - fn reify(&self) -> Value { self.borrow().reify() } + fn reify(&self) -> Value { + self.borrow().reify() + } - fn reflect(v: &Value) -> Self { ::std::cell::RefCell::::new(T::reflect(v)) } + fn reflect(v: &Value) -> Self { + ::std::cell::RefCell::::new(T::reflect(v)) + } } impl Reifiable for ::std::marker::PhantomData { - fn ty_name() -> Name { n("PhantomData") } // Do we need to distinguish different ones? + fn ty_name() -> Name { + n("PhantomData") + } // Do we need to distinguish different ones? - fn reify(&self) -> Value { Value::Int(BigInt::from(0)) } + fn reify(&self) -> Value { + Value::Int(BigInt::from(0)) + } - fn reflect(_: &Value) -> Self { ::std::marker::PhantomData } + fn reflect(_: &Value) -> Self { + ::std::marker::PhantomData + } } // Hey, I know how to generate the implementation for this... @@ -339,7 +418,6 @@ Reifiable! { } } - /* for testing */ custom_derive! { @@ -358,24 +436,31 @@ custom_derive! { } #[derive(Debug, PartialEq, Eq, Clone)] -struct OldName<'t> { actual: Name, pd: ::std::marker::PhantomData<&'t u32> } +struct OldName<'t> { + actual: Name, + pd: ::std::marker::PhantomData<&'t u32>, +} fn new_oldname<'t>(nm: Name) -> OldName<'t> { OldName { - actual: nm, pd: ::std::marker::PhantomData + actual: nm, + pd: ::std::marker::PhantomData, } } impl<'t> Reifiable for OldName<'t> { - fn ty_name() -> Name { n("OldName") } + fn ty_name() -> Name { + n("OldName") + } - fn reify(&self) -> Value { self.actual.reify() } + fn reify(&self) -> Value { + self.actual.reify() + } fn reflect(v: &Value) -> Self { new_oldname(Name::reflect(v)) } } - custom_derive! { #[derive(Debug, PartialEq, Eq, Reifiable(lifetime), Clone)] enum BasicLifetimeEnum<'t> { @@ -383,7 +468,6 @@ custom_derive! { } } - custom_derive! { #[derive(Debug, PartialEq, Eq, Reifiable, Clone)] enum BasicEnum { @@ -402,7 +486,6 @@ custom_derive! { // TODO: just write a macro that does a really faky custom_derive by calling `Reifiable!` // around something and then putting down its definition. - #[test] fn basic_reification() { assert_eq!(BigInt::from(1800).reify(), val!(i 1800)); @@ -413,16 +496,18 @@ fn basic_reflection() { assert_eq!(BigInt::reflect(&val!(i 1800)), BigInt::from(1800)); } - #[test] fn basic_r_and_r_roundtrip() { assert_eq!(BigInt::from(90), BigInt::reflect(&BigInt::from(90).reify())); - let bsv = BasicStruct{ a: BigInt::from(4), b: BigInt::from(5) }; + let bsv = BasicStruct { + a: BigInt::from(4), + b: BigInt::from(5), + }; assert_eq!(bsv, BasicStruct::reflect(&bsv.reify())); - let nsv = NestedStruct{ x: bsv }; + let nsv = NestedStruct { x: bsv }; assert_eq!(nsv, NestedStruct::reflect(&nsv.reify())); @@ -433,31 +518,44 @@ fn basic_r_and_r_roundtrip() { assert_eq!(bev1, BasicEnum::reflect(&bev1.reify())); //assert_eq!(None, Option::reflect(&None.reify())); - assert_eq!(Some(BigInt::from(5)), Option::reflect(&Some(BigInt::from(5)).reify())); - assert_eq!(Some(bev1.clone()), Option::reflect(&Some(bev1.clone()).reify())); + assert_eq!( + Some(BigInt::from(5)), + Option::reflect(&Some(BigInt::from(5)).reify()) + ); + assert_eq!( + Some(bev1.clone()), + Option::reflect(&Some(bev1.clone()).reify()) + ); - assert_eq!(::std::rc::Rc::new(bev0.clone()), - ::std::rc::Rc::reflect(&::std::rc::Rc::new(bev0.clone()).reify())); + assert_eq!( + ::std::rc::Rc::new(bev0.clone()), + ::std::rc::Rc::reflect(&::std::rc::Rc::new(bev0.clone()).reify()) + ); - assert_eq!(::std::boxed::Box::new(bev0.clone()), - ::std::boxed::Box::reflect(&::std::boxed::Box::new(bev0.clone()).reify())); + assert_eq!( + ::std::boxed::Box::new(bev0.clone()), + ::std::boxed::Box::reflect(&::std::boxed::Box::new(bev0.clone()).reify()) + ); let bleo = BasicLifetimeEnum::Only(new_oldname(n("AlexanderHamilton"))); assert_eq!(bleo, BasicLifetimeEnum::reflect(&bleo.reify())); - let pls = ParameterizedLifetimeStruct::{ + let pls = ParameterizedLifetimeStruct:: { a: BigInt::from(10), b: false, - c: new_oldname(n("DuelCommandments")) + c: new_oldname(n("DuelCommandments")), }; - assert_eq!(pls, ParameterizedLifetimeStruct::::reflect(&pls.reify())); + assert_eq!( + pls, + ParameterizedLifetimeStruct::::reflect(&pls.reify()) + ); } #[test] fn function_r_and_r_roundtrip() { - let f = | a: BigInt | a + BigInt::from(1); + let f = |a: BigInt| a + BigInt::from(1); let f2 = reflect_1ary_function::(reify_1ary_function(Rc::new(Box::new(f)))); @@ -469,7 +567,6 @@ fake_reifiability!(T); struct S {} fake_reifiability!(S); - #[test] fn reified_types() { //"ParameterizedLifetimeStruct<[Option<[Rust_usize]< integer]<" @@ -483,21 +580,22 @@ fn reified_types() { "arg" => [ (vr "Rust_usize") ] }, (vr "Int")] - })); - + }) + ); assert_eq!( ParameterizedLifetimeStruct::<'static, T, S>::ty(), ast!({"Type" "forall_type" : - "param" => ["T", "S"], - "body" => (import [* [forall "param"]] {"Type" "mu_type" : - "param" => [(import [prot "param"] (vr "ParameterizedLifetimeStruct"))], - "body" => (import [* [prot "param"]] {"Type" "struct" : - // TODO: why did the order of fields get reversed? - "component_name" => [@"c" "c", "b", "a"], - "component" => [@"c" (vr "OldName"), (vr "S"), (vr "T")] - + "param" => ["T", "S"], + "body" => (import [* [forall "param"]] {"Type" "mu_type" : + "param" => [(import [prot "param"] (vr "ParameterizedLifetimeStruct"))], + "body" => (import [* [prot "param"]] {"Type" "struct" : + // TODO: why did the order of fields get reversed? + "component_name" => [@"c" "c", "b", "a"], + "component" => [@"c" (vr "OldName"), (vr "S"), (vr "T")] + + }) }) }) - })) + ) } diff --git a/src/ty.rs b/src/ty.rs index 74f103d..fa44bd7 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -8,14 +8,14 @@ These nodes may depend on (i.e., type annotations). */ -use ast_walk::{walk, LazyWalkReses, WalkRule}; +use ast::*; use ast_walk::WalkRule::*; -use walk_mode::WalkMode; +use ast_walk::{walk, LazyWalkReses, WalkRule}; use form::Form; -use util::assoc::Assoc; -use ast::*; use name::*; use std::rc::Rc; +use util::assoc::Assoc; +use walk_mode::WalkMode; #[derive(PartialEq, Clone)] pub struct Ty(pub Ast); @@ -27,17 +27,24 @@ impl ::std::fmt::Debug for Ty { } impl Ty { - pub fn new(a: Ast) -> Ty { Ty(a) } // TODO: deprecate in favor of `Ty()` - pub fn concrete(&self) -> Ast { // TODO: just use `Ty::to_ast()`; this name is obsolete + pub fn new(a: Ast) -> Ty { + Ty(a) + } // TODO: deprecate in favor of `Ty()` + pub fn concrete(&self) -> Ast { + // TODO: just use `Ty::to_ast()`; this name is obsolete self.0.clone() } // TODO: use this more // TODO: make `expd_form` a reference - pub fn destructure(&self, expd_form: Rc, loc: &Ast) - -> Result<::util::mbe::EnvMBE, TypeError> { - self.0.destructure(expd_form.clone()).ok_or( - ty_err_val!(UnableToDestructure(self.clone(), expd_form.name) at loc /*TODO*/)) + pub fn destructure( + &self, + expd_form: Rc, + loc: &Ast, + ) -> Result<::util::mbe::EnvMBE, TypeError> { + self.0 + .destructure(expd_form.clone()) + .ok_or(ty_err_val!(UnableToDestructure(self.clone(), expd_form.name) at loc /*TODO*/)) } } @@ -49,73 +56,110 @@ impl ::std::fmt::Display for Ty { } impl ::runtime::reify::Reifiable for Ty { - fn ty() -> Ast { Ast::ty() } + fn ty() -> Ast { + Ast::ty() + } - fn ty_name() -> Name { n("Type") } + fn ty_name() -> Name { + n("Type") + } - fn ty_invocation() -> Ast { Ast::ty_invocation() } + fn ty_invocation() -> Ast { + Ast::ty_invocation() + } - fn reify(&self) -> ::runtime::eval::Value { self.0.reify() } + fn reify(&self) -> ::runtime::eval::Value { + self.0.reify() + } - fn reflect(v: &::runtime::eval::Value) -> Self { Ty::new(Ast::reflect(v))} + fn reflect(v: &::runtime::eval::Value) -> Self { + Ty::new(Ast::reflect(v)) + } } impl ::walk_mode::WalkElt for Ty { - fn from_ast(a: &Ast) -> Ty { Ty::new(a.clone()) } - fn to_ast(&self) -> Ast { self.concrete() } + fn from_ast(a: &Ast) -> Ty { + Ty::new(a.clone()) + } + fn to_ast(&self) -> Ast { + self.concrete() + } - fn core_env() -> Assoc { ::runtime::core_values::core_types() } + fn core_env() -> Assoc { + ::runtime::core_values::core_types() + } } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct SynthTy {} } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct UnpackTy {} } impl WalkMode for SynthTy { - fn name() -> &'static str { "SynTy" } + fn name() -> &'static str { + "SynTy" + } type Elt = Ty; type Negated = UnpackTy; type Err = TypeError; type D = ::walk_mode::Positive; type ExtraInfo = (); - fn get_walk_rule(f: &Form) -> WalkRule { f.synth_type.pos().clone() } - fn automatically_extend_env() -> bool { true } + fn get_walk_rule(f: &Form) -> WalkRule { + f.synth_type.pos().clone() + } + fn automatically_extend_env() -> bool { + true + } fn walk_var(name: Name, parts: &::ast_walk::LazyWalkReses) -> Result { match parts.env.find(&name) { - None => Err(::util::err::sp(TyErr::UnboundName(name), parts.this_ast.clone())), + None => Err(::util::err::sp( + TyErr::UnboundName(name), + parts.this_ast.clone(), + )), // If name is protected, stop: Some(ty) if &Ty(VariableReference(name)) == ty => Ok(ty.clone()), - Some(ty) => synth_type(&ty.concrete(), parts.env.clone()) + Some(ty) => synth_type(&ty.concrete(), parts.env.clone()), } } // Simply protect the name; don't try to unify it. - fn underspecified(name: Name) -> Ty { Ty(VariableReference(name)) } + fn underspecified(name: Name) -> Ty { + Ty(VariableReference(name)) + } } impl WalkMode for UnpackTy { - fn name() -> &'static str { "UnpTy" } + fn name() -> &'static str { + "UnpTy" + } type Elt = Ty; type Negated = SynthTy; type Err = TypeError; type D = ::walk_mode::Negative; type ExtraInfo = (); - fn get_walk_rule(f: &Form) -> WalkRule { f.synth_type.neg().clone() } - fn automatically_extend_env() -> bool { true } + fn get_walk_rule(f: &Form) -> WalkRule { + f.synth_type.neg().clone() + } + fn automatically_extend_env() -> bool { + true + } - fn underspecified(name: Name) -> Ty { Ty(VariableReference(name)) } + fn underspecified(name: Name) -> Ty { + Ty(VariableReference(name)) + } } impl ::walk_mode::NegativeWalkMode for UnpackTy { - fn needs_pre_match() -> bool { true } + fn needs_pre_match() -> bool { + true + } } pub fn synth_type_top(expr: &Ast) -> TypeResult { @@ -126,8 +170,7 @@ pub fn synth_type(expr: &Ast, env: Assoc) -> TypeResult { walk::(expr, &LazyWalkReses::new_wrapper(env)) } -pub fn neg_synth_type(pat: &Ast, env: Assoc) - -> Result, TypeError> { +pub fn neg_synth_type(pat: &Ast, env: Assoc) -> Result, TypeError> { walk::(pat, &LazyWalkReses::new_wrapper(env)) } @@ -149,9 +192,11 @@ impl ::std::fmt::Display for TyErr { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { use self::TyErr::*; match *self { - Mismatch(ref got, ref exp) => { - write!(f, "[Mismatch] got:\n `{:#?}`\n expected:\n `{:#?}`\n", got, exp) - } + Mismatch(ref got, ref exp) => write!( + f, + "[Mismatch] got:\n `{:#?}`\n expected:\n `{:#?}`\n", + got, exp + ), LengthMismatch(ref got, exp_len) => { write!(f, "[LengthMismatch] got:\n ")?; for g in got { @@ -159,28 +204,32 @@ impl ::std::fmt::Display for TyErr { } write!(f, "\n expected {} arguments.\n", exp_len) } - NtInterpMismatch(got, exp) => { - write!(f, "[NtInterpMismatch] expected the nonterminal `{:#?}`, \ - but `{:#?}` was interpolated", - exp, got) - } - NonexistentEnumArm(got_name, ref ty) => { - write!(f, "[NonexistentEnumArm] the enum `{}` doesn't have an arm named `{:#?}`", - ty, got_name) - } - NonexistentStructField(got_name, ref ty) => { - write!(f, "[NonexistentStructField] the struct `{}` doesn't have a \ - field named `{:#?}`", - ty, got_name) - } - NonExhaustiveMatch(ref ty) => - write!(f, "[NonExhaustiveMatch] non-exhaustive match of `{}`", ty), - UnableToDestructure(ref ty, expected_name) => { - write!(f, "[UnableToDestructure] expected a `{}` type, got `{}`", expected_name, ty) - } - UnboundName(name) => { - write!(f, "[UnboundName] `{}` is not defined", name) + NtInterpMismatch(got, exp) => write!( + f, + "[NtInterpMismatch] expected the nonterminal `{:#?}`, \ + but `{:#?}` was interpolated", + exp, got + ), + NonexistentEnumArm(got_name, ref ty) => write!( + f, + "[NonexistentEnumArm] the enum `{}` doesn't have an arm named `{:#?}`", + ty, got_name + ), + NonexistentStructField(got_name, ref ty) => write!( + f, + "[NonexistentStructField] the struct `{}` doesn't have a \ + field named `{:#?}`", + ty, got_name + ), + NonExhaustiveMatch(ref ty) => { + write!(f, "[NonExhaustiveMatch] non-exhaustive match of `{}`", ty) } + UnableToDestructure(ref ty, expected_name) => write!( + f, + "[UnableToDestructure] expected a `{}` type, got `{}`", + expected_name, ty + ), + UnboundName(name) => write!(f, "[UnboundName] `{}` is not defined", name), } } } @@ -205,12 +254,12 @@ pub type TypeError = ::util::err::Spanned; pub type TypeResult = Result; - pub fn expect_type(expected: &Ty, got: &Ty, loc: &Ast) -> Result<(), TypeError> { if got != expected { Err(::util::err::Spanned { - loc: loc.clone(), body: TyErr::Mismatch(expected.clone(), got.clone()) - } ) + loc: loc.clone(), + body: TyErr::Mismatch(expected.clone(), got.clone()), + }) } else { Ok(()) } @@ -219,48 +268,70 @@ pub fn expect_type(expected: &Ty, got: &Ty, loc: &Ast) -> Result<(), TypeError> #[test] fn basic_type_synth() { let mt_ty_env = Assoc::new(); - let int_ty = ty!({ ::core_forms::find_core_form("Type", "Int") ; }); - let nat_ty = ty!({ ::core_forms::find_core_form("Type", "Nat") ; }); + let int_ty = ty!({ + ::core_forms::find_core_form("Type", "Int"); + }); + let nat_ty = ty!({ + ::core_forms::find_core_form("Type", "Nat"); + }); let simple_ty_env = mt_ty_env.set(n("x"), int_ty.clone()); let body = basic_typed_form!(aat, Body(n("body")), NotWalked); let untypeable = basic_typed_form!(aat, NotWalked, NotWalked); - assert_eq!(synth_type(&ast!((vr "x")), simple_ty_env.clone()), - Ok(int_ty.clone())); + assert_eq!( + synth_type(&ast!((vr "x")), simple_ty_env.clone()), + Ok(int_ty.clone()) + ); - assert_eq!(synth_type(&ast!({body.clone() ; + assert_eq!( + synth_type( + &ast!({body.clone() ; ["irrelevant" => {untypeable.clone() ; }, "body" => (vr "x")]}), - simple_ty_env.clone()), - Ok(int_ty.clone())); - - assert_eq!(synth_type(&ast!({body.clone() ; + simple_ty_env.clone() + ), + Ok(int_ty.clone()) + ); + + assert_eq!( + synth_type( + &ast!({body.clone() ; "type_of_new_var" => (, int_ty.concrete()), "new_var" => "y", "body" => (import ["new_var" : "type_of_new_var"] (vr "y"))}), - simple_ty_env.clone()), - Ok(int_ty.clone())); - - assert_eq!(synth_type( - &ast!( - {basic_typed_form!( - aat, - Custom(Rc::new(Box::new( - |_| Ok(ty!({ ::core_forms::find_core_form("Type", "Nat") ; }))))), - NotWalked) ; []}), - simple_ty_env.clone()), - Ok(nat_ty.clone())); - - - let chained_ty_env - = assoc_n!("a" => ty!((vr "B")), "B" => ty!((vr "C")), "C" => ty!({"Type" "Int":})); - - assert_eq!(synth_type(&ast!((vr "a")), chained_ty_env), Ok(ty!({"Type" "Int":}))); + simple_ty_env.clone() + ), + Ok(int_ty.clone()) + ); + + assert_eq!( + synth_type( + &ast!({ + basic_typed_form!( + aat, + Custom(Rc::new(Box::new(|_| Ok(ty!({ + ::core_forms::find_core_form("Type", "Nat"); + }))))), + NotWalked + ); + [] + }), + simple_ty_env.clone() + ), + Ok(nat_ty.clone()) + ); + + let chained_ty_env = + assoc_n!("a" => ty!((vr "B")), "B" => ty!((vr "C")), "C" => ty!({"Type" "Int":})); + + assert_eq!( + synth_type(&ast!((vr "a")), chained_ty_env), + Ok(ty!({"Type" "Int":})) + ); } - #[test] fn type_specialization() { let nat_ty = ty!( { "Type" "Nat" : }); diff --git a/src/ty_compare.rs b/src/ty_compare.rs index 19aa7af..a046bf8 100644 --- a/src/ty_compare.rs +++ b/src/ty_compare.rs @@ -1,14 +1,14 @@ -use ast_walk::{walk, LazyWalkReses, WalkRule, Clo}; +use ast::*; use ast_walk::WalkRule::*; -use walk_mode::WalkMode; +use ast_walk::{walk, Clo, LazyWalkReses, WalkRule}; +use core_forms::{ast_to_name, find_core_form}; use form::Form; -use util::assoc::Assoc; -use ast::*; -use ty::{Ty, TyErr}; use name::*; use std::cell::RefCell; use std::collections::HashMap; -use core_forms::{find_core_form, ast_to_name}; +use ty::{Ty, TyErr}; +use util::assoc::Assoc; +use walk_mode::WalkMode; /* Let me write down an example subtyping hierarchy, to stop myself from getting confused. ⊤ (any type/dynamic type/"dunno"/∀X.X) @@ -107,15 +107,18 @@ The other thing that subtyping has to deal with is `...[T >> T]...`. /// TODO: could this be replaced by `synth_type`? Can `canonicalize` be replaced by it, too? /// TODO: This doesn't change `env`, and none of its clients care. It should just return `Ty`. pub fn resolve(Clo { it: t, env }: Clo, unif: &HashMap>) -> Clo { - let u_f = underdetermined_form.with(|u_f| { u_f.clone() }); + let u_f = underdetermined_form.with(|u_f| u_f.clone()); let resolved = match t { Ty(VariableReference(vr)) => { match env.find(&vr).cloned() { // HACK: leave mu-protected variables alone, instead of recurring forever Some(Ty(VariableReference(new_vr))) if vr == new_vr => None, - Some(different) => Some(Clo{it: different, env: env.clone()}), - None => None + Some(different) => Some(Clo { + it: different, + env: env.clone(), + }), + None => None, } } Ty(Node(ref form, ref parts, _)) if form == &find_core_form("Type", "type_apply") => { @@ -125,11 +128,18 @@ pub fn resolve(Clo { it: t, env }: Clo, unif: &HashMap>) -> Cl let arg_terms = parts.get_rep_leaf_or_panic(n("arg")); let resolved = resolve( - Clo{it: Ty(parts.get_leaf_or_panic(&n("type_rator")).clone()), env: env.clone()}, - unif); + Clo { + it: Ty(parts.get_leaf_or_panic(&n("type_rator")).clone()), + env: env.clone(), + }, + unif, + ); match resolved { - Clo{it: Ty(VariableReference(rator_vr)), env} => { + Clo { + it: Ty(VariableReference(rator_vr)), + env, + } => { // e.g. `X<[int, Y]<` underneath `mu X. ...` // Rebuild a type_apply, but evaulate its arguments @@ -137,54 +147,78 @@ pub fn resolve(Clo { it: t, env }: Clo, unif: &HashMap>) -> Cl // we wish to avoid aliasing problems at the type level. // In System F, this is avoided by performing capture-avoiding substitution. let mut new__tapp_parts = ::util::mbe::EnvMBE::new_from_leaves( - assoc_n!("type_rator" => VariableReference(rator_vr))); + assoc_n!("type_rator" => VariableReference(rator_vr)), + ); let mut args = vec![]; for individual__arg_res in arg_terms { args.push(::util::mbe::EnvMBE::new_from_leaves( - assoc_n!("arg" => individual__arg_res.clone()))); + assoc_n!("arg" => individual__arg_res.clone()), + )); } new__tapp_parts.add_anon_repeat(args, None); - let res = Ty::new(Node(find_core_form("Type", "type_apply"), - new__tapp_parts, ::beta::ExportBeta::Nothing)); - - if res != t { Some(Clo{it: res, env: env.clone()}) } else { None } + let res = Ty::new(Node( + find_core_form("Type", "type_apply"), + new__tapp_parts, + ::beta::ExportBeta::Nothing, + )); + + if res != t { + Some(Clo { + it: res, + env: env.clone(), + }) + } else { + None + } } - Clo{it: defined_type, env} => { - match defined_type.destructure( - find_core_form("Type", "forall_type"), &t.0) { + Clo { + it: defined_type, + env, + } => { + match defined_type.destructure(find_core_form("Type", "forall_type"), &t.0) { Err(_) => None, // Broken "type_apply", but let it fail elsewhere Ok(ref got_forall) => { let params = got_forall.get_rep_leaf_or_panic(n("param")); if params.len() != arg_terms.len() { - panic!("Kind error: wrong number of arguments: {} vs {}", - params.len(), arg_terms.len()); + panic!( + "Kind error: wrong number of arguments: {} vs {}", + params.len(), + arg_terms.len() + ); } let mut actual_params = Assoc::new(); for (name, arg_term) in params.iter().zip(arg_terms) { - actual_params = actual_params.set(ast_to_name(name), - arg_term.clone()); + actual_params = + actual_params.set(ast_to_name(name), arg_term.clone()); } - Some(Clo{ + Some(Clo { it: Ty(::alpha::substitute( ::core_forms::strip_ee( - got_forall.get_leaf_or_panic(&n("body"))), - &actual_params)), - env: env.clone()}) + got_forall.get_leaf_or_panic(&n("body")), + ), + &actual_params, + )), + env: env.clone(), + }) } } } } } - Ty(Node(ref form, ref parts, _)) if form == &u_f => { // underdetermined - unif.get(&ast_to_name(parts.get_leaf_or_panic(&n("id")))).cloned() + Ty(Node(ref form, ref parts, _)) if form == &u_f => { + // underdetermined + unif.get(&ast_to_name(parts.get_leaf_or_panic(&n("id")))) + .cloned() } - _ => None + _ => None, }; - resolved.map(|clo| resolve(clo, unif)).unwrap_or(Clo{ it: t, env: env }) + resolved + .map(|clo| resolve(clo, unif)) + .unwrap_or(Clo { it: t, env: env }) } thread_local! { @@ -214,18 +248,19 @@ thread_local! { }) } - -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct Canonicalize {} } -custom_derive!{ +custom_derive! { #[derive(Copy, Clone, Debug, Reifiable)] pub struct Subtype {} } impl WalkMode for Canonicalize { - fn name() -> &'static str { "Canon" } + fn name() -> &'static str { + "Canon" + } type Elt = Ty; type Negated = Subtype; @@ -234,21 +269,27 @@ impl WalkMode for Canonicalize { type ExtraInfo = (); // Actually, always `LiteralLike`, but need to get the lifetime as long as `f`'s - fn get_walk_rule(f: &Form) -> WalkRule { f.type_compare.pos().clone() } - fn automatically_extend_env() -> bool { true } + fn get_walk_rule(f: &Form) -> WalkRule { + f.type_compare.pos().clone() + } + fn automatically_extend_env() -> bool { + true + } fn walk_var(n: Name, cnc: &LazyWalkReses) -> Result { match cnc.env.find(&n) { // If it's protected, stop: Some(t) if &Ty(VariableReference(n)) == t => Ok(t.clone()), Some(t) => canonicalize(t, cnc.env.clone()), - None => Ok(Ty(VariableReference(n))) //TODO why can this happen? + None => Ok(Ty(VariableReference(n))), //TODO why can this happen? } } } impl WalkMode for Subtype { - fn name() -> &'static str { "SubTy" } + fn name() -> &'static str { + "SubTy" + } type Elt = Ty; type Negated = Canonicalize; @@ -256,8 +297,12 @@ impl WalkMode for Subtype { type D = ::walk_mode::Negative; type ExtraInfo = (); - fn get_walk_rule(f: &Form) -> WalkRule { f.type_compare.neg().clone() } - fn automatically_extend_env() -> bool { true } + fn get_walk_rule(f: &Form) -> WalkRule { + f.type_compare.neg().clone() + } + fn automatically_extend_env() -> bool { + true + } fn underspecified(name: Name) -> Ty { ::ty_compare::next_id.with(|id| { @@ -274,12 +319,13 @@ impl WalkMode for Subtype { /// Look up the reference and keep going. fn walk_var(n: Name, cnc: &LazyWalkReses) -> Result, TyErr> { let lhs: &Ty = cnc.env.find_or_panic(&n); - if lhs == &Ty(VariableReference(n)) { // mu-protected! + if lhs == &Ty(VariableReference(n)) { + // mu-protected! return match cnc.context_elt() { // mu-protected type variables have to exactly match by name: &Ty(VariableReference(other_n)) if other_n == n => Ok(Assoc::new()), - different => Err(TyErr::Mismatch(different.clone(), lhs.clone())) - } + different => Err(TyErr::Mismatch(different.clone(), lhs.clone())), + }; } walk::(&lhs.concrete(), cnc) } @@ -287,13 +333,14 @@ impl WalkMode for Subtype { // This captures the environment, and produces a function suitable for `heal_splices__with`. // `sup` is the general type that needs specializing into `sub`. -fn match_dotdotdot<'a>(env: &'a Assoc) - -> impl Fn(&Ast, &dyn Fn() -> Vec) -> Option> + 'a { +fn match_dotdotdot<'a>( + env: &'a Assoc, +) -> impl Fn(&Ast, &dyn Fn() -> Vec) -> Option> + 'a { let env = env.clone(); move |sup: &Ast, sub_thunk: &dyn Fn() -> Vec| -> Option> { let ddd_form = ::core_forms::find("Type", "dotdotdot"); let tuple_form = ::core_forms::find("Type", "tuple"); - let undet_form = underdetermined_form.with(|u_f| { u_f.clone() }); + let undet_form = underdetermined_form.with(|u_f| u_f.clone()); // Is it a dotdotdot? let ddd_parts = sup.destructure(ddd_form.clone())?; @@ -305,21 +352,38 @@ fn match_dotdotdot<'a>(env: &'a Assoc) None => {} // False alarm; not a dotdotdot! Some(sub_parts) => { // Uh-oh, maybe we're comparing two dotdotdots? - match sub_parts.get_leaf_or_panic(&n("body")).destructure(ddd_form) { - Some(_) => { panic!("ICE: TODO: count up the stacks of :::[]:::")} - None => { return None; } // :::[]::: is a subtype of :::[]::: + match sub_parts + .get_leaf_or_panic(&n("body")) + .destructure(ddd_form) + { + Some(_) => panic!("ICE: TODO: count up the stacks of :::[]:::"), + None => { + return None; + } // :::[]::: is a subtype of :::[]::: } } } } - let drivers : Vec<(Name, Ty)> = - unification.with(|unif| { - ddd_parts.get_rep_leaf_or_panic(n("driver")).iter() - .map(|a: &&Ast| - (::core_forms::vr_to_name(*a), - resolve(Clo{it: Ty((*a).clone()), env: env.clone()}, &unif.borrow()).it)) - .collect()}); + let drivers: Vec<(Name, Ty)> = unification.with(|unif| { + ddd_parts + .get_rep_leaf_or_panic(n("driver")) + .iter() + .map(|a: &&Ast| { + ( + ::core_forms::vr_to_name(*a), + resolve( + Clo { + it: Ty((*a).clone()), + env: env.clone(), + }, + &unif.borrow(), + ) + .it, + ) + }) + .collect() + }); let expected_len = concrete_subs.len(); let mut substitution_envs = vec![]; @@ -339,17 +403,19 @@ fn match_dotdotdot<'a>(env: &'a Assoc) } else if let Some(undet_parts) = driver.0.destructure(undet_form.clone()) { unification.with(|unif| { let mut undet_components = vec![]; - undet_components.resize_with( - expected_len, || Subtype::underspecified(n("ddd_bit")).0); - unif.borrow_mut().insert(ast_to_name(undet_parts.get_leaf_or_panic(&n("id"))), + undet_components + .resize_with(expected_len, || Subtype::underspecified(n("ddd_bit")).0); + unif.borrow_mut().insert( + ast_to_name(undet_parts.get_leaf_or_panic(&n("id"))), Clo { it: ty!({"Type" "tuple" : "component" => (,seq undet_components.clone()) }), - env: env.clone() - }); + env: env.clone(), + }, + ); for i in 0..expected_len { - substitution_envs[i] - = substitution_envs[i].set(name, undet_components[i].clone()); + substitution_envs[i] = + substitution_envs[i].set(name, undet_components[i].clone()); } }) } else { @@ -362,62 +428,99 @@ fn match_dotdotdot<'a>(env: &'a Assoc) // TODO #13: This violates one of our main principles! // We should never modify code (in this case, types) before checking it. // We need to redesign `pre_match` to avoid this (we want to just change the ed) - Some(substitution_envs.iter().map(|env| ::alpha::substitute(&body, env)).collect()) + Some( + substitution_envs + .iter() + .map(|env| ::alpha::substitute(&body, env)) + .collect(), + ) } } impl ::walk_mode::NegativeWalkMode for Subtype { - fn qlit_mismatch_error(got: Ty, expd: Ty) -> Self::Err { TyErr::Mismatch(got, expd) } + fn qlit_mismatch_error(got: Ty, expd: Ty) -> Self::Err { + TyErr::Mismatch(got, expd) + } - fn needs_pre_match() -> bool { true } + fn needs_pre_match() -> bool { + true + } /// Push through all variable references and underdeterminednesses on both sides, /// returning types that are ready to compare, or `None` if they're definitionally equal fn pre_match(lhs_ty: Ty, rhs_ty: Ty, env: &Assoc) -> Option<(Clo, Clo)> { - let u_f = underdetermined_form.with(|u_f| { u_f.clone() }); + let u_f = underdetermined_form.with(|u_f| u_f.clone()); let (res_lhs, mut res_rhs) = unification.with(|unif| { // Capture the environment and resolve: - let lhs : Clo = resolve(Clo{it: lhs_ty, env: env.clone()}, &unif.borrow()).clone(); - let rhs : Clo = resolve(Clo{it: rhs_ty, env: env.clone()}, &unif.borrow()).clone(); - - let lhs_name = lhs.it.destructure(u_f.clone(), &Trivial).map( // errors get swallowed ↓ - |p| ast_to_name(p.get_leaf_or_panic(&n("id")))); - let rhs_name = rhs.it.destructure(u_f.clone(), &Trivial).map( - |p| ast_to_name(p.get_leaf_or_panic(&n("id")))); + let lhs: Clo = resolve( + Clo { + it: lhs_ty, + env: env.clone(), + }, + &unif.borrow(), + ) + .clone(); + let rhs: Clo = resolve( + Clo { + it: rhs_ty, + env: env.clone(), + }, + &unif.borrow(), + ) + .clone(); + + let lhs_name = lhs.it.destructure(u_f.clone(), &Trivial).map( + // errors get swallowed ↓ + |p| ast_to_name(p.get_leaf_or_panic(&n("id"))), + ); + let rhs_name = rhs + .it + .destructure(u_f.clone(), &Trivial) + .map(|p| ast_to_name(p.get_leaf_or_panic(&n("id")))); // print!("%%: {}\n%%: {}\n", lhs, rhs); // print!("in: {:#?}\n", env.map(|_| "…")); match (lhs_name, rhs_name) { // They are the same underdetermined type; nothing to do: - (Ok(l), Ok(r)) if l == r => { return None; } + (Ok(l), Ok(r)) if l == r => { + return None; + } // Make a determination (possibly just merging two underdetermined types): - (Ok(l), _) => { unif.borrow_mut().insert(l, rhs.clone()); return None; } - (_, Ok(r)) => { unif.borrow_mut().insert(r, lhs.clone()); return None; } + (Ok(l), _) => { + unif.borrow_mut().insert(l, rhs.clone()); + return None; + } + (_, Ok(r)) => { + unif.borrow_mut().insert(r, lhs.clone()); + return None; + } // They are (potentially) different. - _ => { Some((lhs, rhs)) } + _ => Some((lhs, rhs)), } })?; // Now, resolve `:::[]:::` subtyping match (&res_lhs.it, &mut res_rhs.it) { (&Ty(Node(_, ref lhs_body, _)), &mut Ty(Node(_, ref mut rhs_body, _))) => { - // the LHS should be the subtype (i.e. already specific), - // and the RHS should be made to match - let _ = rhs_body.heal_splices__with(lhs_body, &match_dotdotdot(env)); - } + // the LHS should be the subtype (i.e. already specific), + // and the RHS should be made to match + let _ = rhs_body.heal_splices__with(lhs_body, &match_dotdotdot(env)); + } _ => {} } Some((res_lhs, res_rhs)) - } // TODO: should unbound variable references ever be walked at all? Maybe it should panic? } pub fn canonicalize(t: &Ty, env: Assoc) -> Result { - walk::(&t.concrete(), &LazyWalkReses::::new_wrapper(env)) + walk::( + &t.concrete(), + &LazyWalkReses::::new_wrapper(env), + ) } pub fn must_subtype(sub: &Ty, sup: &Ty, env: Assoc) -> Result, TyErr> { @@ -432,7 +535,8 @@ pub fn must_subtype(sub: &Ty, sup: &Ty, env: Assoc) -> Result) -> Result<(), TyErr> { let lwr_env = &LazyWalkReses::new_wrapper(env); if walk::(&lhs.concrete(), lwr_env) - == walk::(&rhs.concrete(), lwr_env) { + == walk::(&rhs.concrete(), lwr_env) + { Ok(()) } else { Err(TyErr::Mismatch(lhs.clone(), rhs.clone())) @@ -441,19 +545,20 @@ pub fn must_equal(lhs: &Ty, rhs: &Ty, env: Assoc) -> Result<(), TyErr> #[test] fn basic_subtyping() { - use ::ty::TyErr::*; - use ::util::assoc::Assoc; + use ty::TyErr::*; + use util::assoc::Assoc; let mt_ty_env = Assoc::new(); let int_ty = ty!({ "Type" "Int" : }); let nat_ty = ty!({ "Type" "Nat" : }); let float_ty = ty!({ "Type" "Float" : }); - assert_m!(must_subtype(&int_ty, &int_ty, mt_ty_env.clone()), Ok(_)); - assert_eq!(must_subtype(&float_ty, &int_ty, mt_ty_env.clone()), - Err(Mismatch(float_ty.clone(), int_ty.clone()))); + assert_eq!( + must_subtype(&float_ty, &int_ty, mt_ty_env.clone()), + Err(Mismatch(float_ty.clone(), int_ty.clone())) + ); let id_fn_ty = ty!({ "Type" "forall_type" : "param" => ["t"], @@ -464,21 +569,24 @@ fn basic_subtyping() { "param" => [(, int_ty.concrete())], "ret" => (, int_ty.concrete())}); - assert_m!(must_subtype(&int_to_int_fn_ty, &int_to_int_fn_ty, mt_ty_env.clone()), - Ok(_)); - - + assert_m!( + must_subtype(&int_to_int_fn_ty, &int_to_int_fn_ty, mt_ty_env.clone()), + Ok(_) + ); - assert_m!(must_subtype(&id_fn_ty, &id_fn_ty, mt_ty_env.clone()), - Ok(_)); + assert_m!(must_subtype(&id_fn_ty, &id_fn_ty, mt_ty_env.clone()), Ok(_)); // actually subtype interestingly! - assert_m!(must_subtype(&int_to_int_fn_ty, &id_fn_ty, mt_ty_env.clone()), - Ok(_)); + assert_m!( + must_subtype(&int_to_int_fn_ty, &id_fn_ty, mt_ty_env.clone()), + Ok(_) + ); // TODO: this error spits out generated names to the user without context ) : - assert_m!(must_subtype(&id_fn_ty, &int_to_int_fn_ty, mt_ty_env.clone()), - Err(Mismatch(_,_))); + assert_m!( + must_subtype(&id_fn_ty, &int_to_int_fn_ty, mt_ty_env.clone()), + Err(Mismatch(_, _)) + ); let parametric_ty_env = assoc_n!( "some_int" => ty!( { "Type" "Int" : }), @@ -491,41 +599,56 @@ fn basic_subtyping() { "identity" => id_fn_ty.clone(), "int_to_int" => int_to_int_fn_ty.clone()); + assert_m!( + must_subtype( + &ty!((vr "int_to_int")), + &ty!((vr "identity")), + parametric_ty_env.clone() + ), + Ok(_) + ); - assert_m!(must_subtype(&ty!((vr "int_to_int")), &ty!((vr "identity")), - parametric_ty_env.clone()), - Ok(_)); - - assert_m!(must_subtype(&ty!((vr "identity")), &ty!((vr "int_to_int")), - parametric_ty_env.clone()), - Err(Mismatch(_,_))); + assert_m!( + must_subtype( + &ty!((vr "identity")), + &ty!((vr "int_to_int")), + parametric_ty_env.clone() + ), + Err(Mismatch(_, _)) + ); - fn incomplete_fn_ty() -> Ty { // A function, so we get a fresh underspecified type each time. + fn incomplete_fn_ty() -> Ty { + // A function, so we get a fresh underspecified type each time. ty!({ "Type" "fn" : "param" => [ { "Type" "Int" : } ], "ret" => (, Subtype::underspecified(n("")).concrete() )}) } + assert_m!( + must_subtype(&incomplete_fn_ty(), &int_to_int_fn_ty, mt_ty_env.clone()), + Ok(_) + ); - assert_m!(must_subtype(&incomplete_fn_ty(), &int_to_int_fn_ty, mt_ty_env.clone()), - Ok(_)); - - assert_m!(must_subtype(&incomplete_fn_ty(), &id_fn_ty, mt_ty_env.clone()), - Ok(_)); + assert_m!( + must_subtype(&incomplete_fn_ty(), &id_fn_ty, mt_ty_env.clone()), + Ok(_) + ); - assert_eq!(::ty::synth_type(&ast!({"Expr" "apply" : "rator" => (vr "identity"), + assert_eq!( + ::ty::synth_type( + &ast!({"Expr" "apply" : "rator" => (vr "identity"), "rand" => [(vr "some_int")]}), - parametric_ty_env.clone()), - Ok(ty!({"Type" "Int" : }))); + parametric_ty_env.clone() + ), + Ok(ty!({"Type" "Int" : })) + ); // TODO: write a test that relies on the capture-the-environment behavior of `pre_match` } - #[test] fn misc_subtyping_problems() { - let list_ty = - ty!( { "Type" "forall_type" : + let list_ty = ty!( { "Type" "forall_type" : "param" => ["Datum"], "body" => (import [* [forall "param"]] { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "List"))], @@ -536,14 +659,12 @@ fn misc_subtyping_problems() { "type_rator" => (vr "List"), "arg" => [(vr "Datum")]} ]]})})}); - let int_list_ty = - ty!( { "Type" "mu_type" : + let int_list_ty = ty!( { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "IntList"))], "body" => (import [* [prot "param"]] { "Type" "enum" : "name" => [@"c" "Nil", "Cons"], "component" => [@"c" [], [{"Type" "Int" :}, (vr "IntList") ]]})}); - let bool_list_ty = - ty!( { "Type" "mu_type" : + let bool_list_ty = ty!( { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "FloatList"))], "body" => (import [* [prot "param"]] { "Type" "enum" : "name" => [@"c" "Nil", "Cons"], @@ -556,25 +677,31 @@ fn misc_subtyping_problems() { ); // μ also has binding: - assert_m!(must_subtype(&int_list_ty, &int_list_ty, ty_env.clone()), Ok(_)); - assert_m!(must_subtype(&int_list_ty, &bool_list_ty, ty_env.clone()), Err(_)); + assert_m!( + must_subtype(&int_list_ty, &int_list_ty, ty_env.clone()), + Ok(_) + ); + assert_m!( + must_subtype(&int_list_ty, &bool_list_ty, ty_env.clone()), + Err(_) + ); // Don't walk `Atom`s! let basic_enum = ty!({"Type" "enum" : "name" => [@"arm" "Aa", "Bb"], "component" => [@"arm" [{"Type" "Int" :}], []]}); - assert_m!(must_subtype(&basic_enum, &basic_enum, ::util::assoc::Assoc::new()), - Ok(_)); + assert_m!( + must_subtype(&basic_enum, &basic_enum, ::util::assoc::Assoc::new()), + Ok(_) + ); let basic_mu = ty!({"Type" "mu_type" : "param" => [(import [prot "param"] (vr "X"))], "body" => (import [* [prot "param"]] (vr "X"))}); let mu_env = assoc_n!("X" => basic_mu.clone()); - // Don't diverge on `μ`! - assert_m!(must_subtype(&basic_mu, &basic_mu, mu_env), - Ok(_)); + assert_m!(must_subtype(&basic_mu, &basic_mu, mu_env), Ok(_)); let id_fn_ty = ty!({ "Type" "forall_type" : "param" => ["t"], @@ -586,12 +713,10 @@ fn misc_subtyping_problems() { let int_ty = ty!({ "Type" "Int" : }); let nat_ty = ty!({ "Type" "Nat" : }); - let int_to_int_fn_ty = ty!({ "Type" "fn" : "param" => [(, int_ty.concrete())], "ret" => (, int_ty.concrete())}); - let parametric_ty_env = assoc_n!( "some_int" => ty!( { "Type" "Int" : }), "convert_to_nat" => ty!({ "Type" "forall_type" : @@ -603,114 +728,141 @@ fn misc_subtyping_problems() { "identity" => id_fn_ty.clone(), "int_to_int" => int_to_int_fn_ty.clone()); - assert_m!(must_subtype( - &ty!({"Type" "type_apply" : "type_rator" => (vr "identity"), "arg" => [{"Type" "Int" :}]}), - &ty!({"Type" "type_apply" : "type_rator" => (vr "identity"), "arg" => [{"Type" "Int" :}]}), - parametric_ty_env.clone()), - Ok(_)); - + assert_m!( + must_subtype( + &ty!({"Type" "type_apply" : "type_rator" => (vr "identity"), "arg" => [{"Type" "Int" :}]}), + &ty!({"Type" "type_apply" : "type_rator" => (vr "identity"), "arg" => [{"Type" "Int" :}]}), + parametric_ty_env.clone() + ), + Ok(_) + ); - assert_m!(must_subtype( - &ty!({"Type" "type_apply" : "type_rator" => (vr "identity"), "arg" => [{"Type" "Int" :}]}), - &ty!((vr "identity")), - parametric_ty_env.clone()), - Ok(_)); + assert_m!( + must_subtype( + &ty!({"Type" "type_apply" : "type_rator" => (vr "identity"), "arg" => [{"Type" "Int" :}]}), + &ty!((vr "identity")), + parametric_ty_env.clone() + ), + Ok(_) + ); // Some things that involve mu - assert_m!(must_subtype( - &ty!((vr "List")), &ty!((vr "List")), ty_env.clone()), - Ok(_)); - + assert_m!( + must_subtype(&ty!((vr "List")), &ty!((vr "List")), ty_env.clone()), + Ok(_) + ); - assert_m!(must_subtype( - &ty!({"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]}), - &ty!({"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]}), - ty_env.clone()), - Ok(_)); + assert_m!( + must_subtype( + &ty!({"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]}), + &ty!({"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]}), + ty_env.clone() + ), + Ok(_) + ); - assert_m!(must_subtype( - &ty!({"Type" "type_apply" : "type_rator" => (, ty_env.find_or_panic(&n("List")).0.clone()), + assert_m!( + must_subtype( + &ty!({"Type" "type_apply" : "type_rator" => (, ty_env.find_or_panic(&n("List")).0.clone()), "arg" => [{"Type" "Int" :}]}), - &ty!({"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]}), - ty_env.clone()), - Ok(_)); - + &ty!({"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]}), + ty_env.clone() + ), + Ok(_) + ); - assert_m!(must_subtype( - &ty!({"Type" "mu_type" : + assert_m!( + must_subtype( + &ty!({"Type" "mu_type" : "param" => [(import [prot "param"] (vr "List"))], "body" => (import [* [prot "param"]] {"Type" "type_apply": "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]})}), - &ty!({"Type" "mu_type" : + &ty!({"Type" "mu_type" : "param" => [(import [prot "param"] (vr "List"))], "body" => (import [* [prot "param"]] {"Type" "type_apply": "type_rator" => (vr "List"), "arg" => [{"Type" "Int" :}]})}), - ty_env.clone()), - Ok(_)); + ty_env.clone() + ), + Ok(_) + ); - assert_m!(must_subtype( // Reparameterize - &ty!((vr "List")), - &ty!( { "Type" "forall_type" : + assert_m!( + must_subtype( + // Reparameterize + &ty!((vr "List")), + &ty!( { "Type" "forall_type" : "param" => ["Datum2"], "body" => (import [* [forall "param"]] {"Type" "type_apply" : "type_rator" => (vr "List"), "arg" => [(vr "Datum2")]})}), - ty_env.clone()), - Ok(_)); - + ty_env.clone() + ), + Ok(_) + ); } #[test] fn struct_subtyping() { // Trivial struct subtying: - assert_m!(must_subtype( - &ty!( { "Type" "struct" : + assert_m!( + must_subtype( + &ty!( { "Type" "struct" : "component_name" => [@"c" "a", "b"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}]}), - &ty!( { "Type" "struct" : + &ty!( { "Type" "struct" : "component_name" => [@"c" "a", "b"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}]}), - Assoc::new()), - Ok(_)); + Assoc::new() + ), + Ok(_) + ); // Add a component: - assert_m!(must_subtype( - &ty!( { "Type" "struct" : + assert_m!( + must_subtype( + &ty!( { "Type" "struct" : "component_name" => [@"c" "a", "b"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}]}), - &ty!( { "Type" "struct" : + &ty!( { "Type" "struct" : "component_name" => [@"c" "a", "b", "c"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}, {"Type" "Float" :}]}), - Assoc::new()), - Ok(_)); + Assoc::new() + ), + Ok(_) + ); // Reorder components: - assert_m!(must_subtype( - &ty!( { "Type" "struct" : + assert_m!( + must_subtype( + &ty!( { "Type" "struct" : "component_name" => [@"c" "a", "b"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}]}), - &ty!( { "Type" "struct" : + &ty!( { "Type" "struct" : "component_name" => [@"c" "b", "a"], "component" => [@"c" {"Type" "Nat" :}, {"Type" "Int" :}]}), - Assoc::new()), - Ok(_)); + Assoc::new() + ), + Ok(_) + ); // Scramble: - assert_m!(must_subtype( - &ty!( { "Type" "struct" : + assert_m!( + must_subtype( + &ty!( { "Type" "struct" : "component_name" => [@"c" "a", "b"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}]}), - &ty!( { "Type" "struct" : + &ty!( { "Type" "struct" : "component_name" => [@"c" "b", "a"], "component" => [@"c" {"Type" "Int" :}, {"Type" "Nat" :}]}), - Assoc::new()), - Err(_)); - - + Assoc::new() + ), + Err(_) + ); } #[test] -fn subtype_different_mus() { // testing the Amber rule: +fn subtype_different_mus() { + // testing the Amber rule: // These types are non-contractive, but it doesn't matter for subtyping purposes. let jane_author = ty!({"Type" "mu_type" : "param" => [(import [prot "param"] (vr "CharlotteBrontë"))], @@ -728,14 +880,20 @@ fn subtype_different_mus() { // testing the Amber rule: "CharlotteBrontë" => jane_author.clone(), "CurrerBell" => jane_psuedonym.clone(), "EmilyBrontë" => wuthering_author.clone()); - assert_m!(must_subtype(&jane_author, &jane_author, mu_env.clone()), - Ok(_)); + assert_m!( + must_subtype(&jane_author, &jane_author, mu_env.clone()), + Ok(_) + ); - assert_m!(must_subtype(&jane_author, &jane_psuedonym, mu_env.clone()), - Ok(_)); + assert_m!( + must_subtype(&jane_author, &jane_psuedonym, mu_env.clone()), + Ok(_) + ); - assert_m!(must_subtype(&jane_author, &wuthering_author, mu_env.clone()), - Err(_)); + assert_m!( + must_subtype(&jane_author, &wuthering_author, mu_env.clone()), + Err(_) + ); } #[test] @@ -747,44 +905,72 @@ fn subtype_dotdotdot() { "component" => [{"Type" "dotdotdot" : "driver" => [(vr "T")], "body" => (vr "T")}] }); - assert_m!(must_subtype(&dddple, &threeple, assoc_n!("T" => Subtype::underspecified(n("-")))), - Ok(_)); + assert_m!( + must_subtype( + &dddple, + &threeple, + assoc_n!("T" => Subtype::underspecified(n("-"))) + ), + Ok(_) + ); // TODO #15: this panics in mbe.rs; it ought to error instead // assert_m!(must_subtype(&threeple, &dddple, assoc_n!("T" => Subtype::underspecified(n("-")))), // Err(_)); - assert_m!(must_subtype(&dddple, &dddple, assoc_n!("T" => Subtype::underspecified(n("-")))), - Ok(_)); + assert_m!( + must_subtype( + &dddple, + &dddple, + assoc_n!("T" => Subtype::underspecified(n("-"))) + ), + Ok(_) + ); let intdddple = ty!({"Type" "tuple" : "component" => [{"Type" "Int" :}, {"Type" "dotdotdot" : "driver" => [(vr "T")], "body" => (vr "T")}] }); - assert_m!(must_subtype(&intdddple, &threeple, assoc_n!("T" => Subtype::underspecified(n("-")))), - Ok(_)); - - assert_m!(must_subtype(&intdddple, &intdddple, assoc_n!("T" => Subtype::underspecified(n("-")))), - Ok(_)); + assert_m!( + must_subtype( + &intdddple, + &threeple, + assoc_n!("T" => Subtype::underspecified(n("-"))) + ), + Ok(_) + ); + assert_m!( + must_subtype( + &intdddple, + &intdddple, + assoc_n!("T" => Subtype::underspecified(n("-"))) + ), + Ok(_) + ); let floatdddple = ty!({"Type" "tuple" : "component" => [{"Type" "Float" :}, {"Type" "dotdotdot" : "driver" => [(vr "T")], "body" => (vr "T")}] }); - assert_m!(must_subtype(&floatdddple, &threeple, assoc_n!("T" => Subtype::underspecified(n("-")))), - Err(_)); + assert_m!( + must_subtype( + &floatdddple, + &threeple, + assoc_n!("T" => Subtype::underspecified(n("-"))) + ), + Err(_) + ); } #[test] fn basic_resolve() { - let u_f = underdetermined_form.with(|u_f| { u_f.clone() }); + let u_f = underdetermined_form.with(|u_f| u_f.clone()); let ud0 = ast!({ u_f.clone() ; "id" => "a⚁99" }); - let list_ty = - ty!( { "Type" "forall_type" : + let list_ty = ty!( { "Type" "forall_type" : "param" => ["Datum"], "body" => (import [* [forall "param"]] { "Type" "mu_type" : "param" => [(import [prot "param"] (vr "List"))], @@ -796,14 +982,30 @@ fn basic_resolve() { "type_rator" => (vr "List"), "arg" => [(,ud0.clone())]}]]})})}); let t_env = assoc_n!("List" => list_ty.clone()); - let unif = HashMap::>::new(); - assert_eq!(resolve(Clo{ it: ty!({"Type" "Int" :}), env: t_env.clone()}, &unif).it, - ty!({"Type" "Int" :})); + assert_eq!( + resolve( + Clo { + it: ty!({"Type" "Int" :}), + env: t_env.clone() + }, + &unif + ) + .it, + ty!({"Type" "Int" :}) + ); - assert_eq!(resolve(Clo{ it: ty!({"Type" "type_apply" : - "type_rator" => (vr "List"), "arg" => [(,ud0.clone())] }), env: t_env.clone()}, &unif).it, + assert_eq!( + resolve( + Clo { + it: ty!({"Type" "type_apply" : + "type_rator" => (vr "List"), "arg" => [(,ud0.clone())] }), + env: t_env.clone() + }, + &unif + ) + .it, ty!({ "Type" "mu_type" : "param" => [(import [prot "param"] (vr "List"))], "body" => (import [* [prot "param"]] { "Type" "enum" : @@ -811,5 +1013,6 @@ fn basic_resolve() { "component" => [@"c" [], [(,ud0.clone()), {"Type" "type_apply" : - "type_rator" => (vr "List"), "arg" => [(,ud0.clone())]} ]]})})); + "type_rator" => (vr "List"), "arg" => [(,ud0.clone())]} ]]})}) + ); } diff --git a/src/unparse.rs b/src/unparse.rs index c776b94..c788d60 100644 --- a/src/unparse.rs +++ b/src/unparse.rs @@ -1,8 +1,8 @@ -use name::*; -use grammar::{FormPat, SynEnv}; -use grammar::FormPat::*; use ast::Ast; use ast::Ast::*; +use grammar::FormPat::*; +use grammar::{FormPat, SynEnv}; +use name::*; use util::mbe::EnvMBE; fn node_names_mentioned(pat: &FormPat) -> Vec { @@ -12,14 +12,19 @@ fn node_names_mentioned(pat: &FormPat) -> Vec { res.push(n); res } - Scope(_,_) => { vec![] } - Delimited(_,_, ref body) | Star(ref body) | Plus(ref body) | ComputeSyntax(_, ref body) - | NameImport(ref body, _) | QuoteDeepen(ref body, _) | QuoteEscape(ref body, _)=> { - node_names_mentioned(&*body) - } + Scope(_, _) => vec![], + Delimited(_, _, ref body) + | Star(ref body) + | Plus(ref body) + | ComputeSyntax(_, ref body) + | NameImport(ref body, _) + | QuoteDeepen(ref body, _) + | QuoteEscape(ref body, _) => node_names_mentioned(&*body), Seq(ref sub_pats) | Alt(ref sub_pats) => { let mut res = vec![]; - for pat in sub_pats { res.append(&mut node_names_mentioned(pat)); } + for pat in sub_pats { + res.append(&mut node_names_mentioned(pat)); + } res } Biased(ref lhs, ref rhs) => { @@ -27,13 +32,18 @@ fn node_names_mentioned(pat: &FormPat) -> Vec { res.append(&mut node_names_mentioned(&*rhs)); res } - Anyways(_) | Impossible | Literal(_) | AnyToken | AnyAtomicToken | VarRef | Call(_) - | SynImport(_,_,_) => { vec![] } + Anyways(_) + | Impossible + | Literal(_) + | AnyToken + | AnyAtomicToken + | VarRef + | Call(_) + | SynImport(_, _, _) => vec![], } } pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) -> String { - //HACK: handle underdetermined forms let undet = ::ty_compare::underdetermined_form.with(|u| u.clone()); match *actl { @@ -43,8 +53,10 @@ pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) let looked_up = unif.borrow().get(&var).cloned(); match looked_up { // Apparently the environment is recursive; `{}`ing it stack-overflows - Some(ref clo) => format!("{} in some environment", clo.it /*, {:#?} clo.env*/), - None => format!("¿{}?", var) + Some(ref clo) => { + format!("{} in some environment", clo.it /*, {:#?} clo.env*/) + } + None => format!("¿{}?", var), } }); } @@ -53,10 +65,13 @@ pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) // TODO: this really ought to notice when `actl` is ill-formed for `pat`. match (pat, actl) { - (&Named(name, ref body), _) => { - unparse_mbe(&*body, context.get_leaf(name).unwrap_or(&Atom(n("<->"))), context, s) - } - //=> unparse_mbe(&*body, context.get_leaf(name).unwrap_or(&Atom(n(""))), context, s), + (&Named(name, ref body), _) => unparse_mbe( + &*body, + context.get_leaf(name).unwrap_or(&Atom(n("<->"))), + context, + s, + ), + //=> unparse_mbe(&*body, context.get_leaf(name).unwrap_or(&Atom(n(""))), context, s), (&Call(sub_form), _) => unparse_mbe(s.find_or_panic(&sub_form), actl, context, s), (&Anyways(_), _) | (&Impossible, _) => "".to_string(), (&Literal(n), _) => n.print(), @@ -69,15 +84,22 @@ pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) (&Delimited(opener, delim, ref body), _) => { let mut closer = opener.print(); closer.pop(); - format!("{}{}{}{}", - opener.print(), unparse_mbe(&*body, actl, context, s), delim.close(), closer) + format!( + "{}{}{}{}", + opener.print(), + unparse_mbe(&*body, actl, context, s), + delim.close(), + closer + ) } (&Seq(ref sub_pats), _) => { let mut prev_empty = true; let mut res = String::new(); for sub_pat in sub_pats { let sub_res = unparse_mbe(&*sub_pat, actl, context, s); - if !prev_empty && sub_res != "" { res.push(' '); } + if !prev_empty && sub_res != "" { + res.push(' '); + } prev_empty = sub_res == ""; res.push_str(&sub_res); } @@ -86,10 +108,15 @@ pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) (&Alt(ref sub_pats), _) => { let mut any_scopes = false; for sub_pat in sub_pats { - if let Scope(_, _) = &**sub_pat { any_scopes = true; continue; } + if let Scope(_, _) = &**sub_pat { + any_scopes = true; + continue; + } let sub_res = unparse_mbe(&*sub_pat, actl, context, s); - if sub_res != "" { return sub_res } // HACK: should use `Option` + if sub_res != "" { + return sub_res; + } // HACK: should use `Option` } // HACK: certain forms don't live in the syntax environment, // but "belong" under an `Alt`, so just assume forms know their grammar: @@ -98,20 +125,24 @@ pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) &Node(ref form_actual, ref body, _) => { return unparse_mbe(&*form_actual.grammar, actl, body, s); } - _ => { } + _ => {} } } return "".to_string(); // Not sure if it's an error, or really just empty } - (&Biased(ref lhs, ref rhs), _) => { - format!("{}{}", unparse_mbe(lhs, actl, context, s), unparse_mbe(rhs, actl, context, s)) - } + (&Biased(ref lhs, ref rhs), _) => format!( + "{}{}", + unparse_mbe(lhs, actl, context, s), + unparse_mbe(rhs, actl, context, s) + ), (&Star(ref sub_pat), _) | (&Plus(ref sub_pat), _) => { let mut first = true; let mut res = String::new(); for marched_ctxt in context.march_all(&node_names_mentioned(&*sub_pat)) { - if !first { res.push(' '); } + if !first { + res.push(' '); + } first = false; res.push_str(&unparse_mbe(&*sub_pat, actl, &marched_ctxt, s)); } @@ -125,28 +156,28 @@ pub fn unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv) "".to_string() // HACK for `Alt` } } - (&Scope(_,_), _) => "".to_string(), // Non-match + (&Scope(_, _), _) => "".to_string(), // Non-match (&NameImport(ref body, _), &ExtendEnv(ref actl_body, _)) => { unparse_mbe(&*body, &*actl_body, context, s) } - (&NameImport(_, _), _) => { format!("[Missing import]→{:#?}←", actl) } + (&NameImport(_, _), _) => format!("[Missing import]→{:#?}←", actl), (&QuoteDeepen(ref body, _), &QuoteMore(ref actl_body, _)) => { unparse_mbe(&*body, &*actl_body, context, s) } - (&QuoteDeepen(_, _), _) => { format!("[Missing qm]{:#?}", actl)} + (&QuoteDeepen(_, _), _) => format!("[Missing qm]{:#?}", actl), (&QuoteEscape(ref body, _), &QuoteLess(ref actl_body, _)) => { unparse_mbe(&*body, &*actl_body, context, s) } - (&QuoteEscape(_, _), _) => { format!("[Missing ql]{:#?}", actl)} + (&QuoteEscape(_, _), _) => format!("[Missing ql]{:#?}", actl), (&SynImport(ref _fp, ref _n, ref _se), &Node(_, ref _body, _)) => { // TODO: I think we need to store the LHS in the AST somehow for this to work. -/* (*se.0)(se, ) + /* (*se.0)(se, ) format!("{} {}", unparse_mbe(fp, ????, context, s)) unparse_mbe(pat: &FormPat, actl: &Ast, context: &EnvMBE, s: &SynEnv)*/ - format!("?synax import? {:#?} ?si?", actl) + format!("?synax import? {:#?} ?si?", actl) } - (&SynImport(_,_,_), _) => { "".to_string() } + (&SynImport(_, _, _), _) => "".to_string(), } } diff --git a/src/util/assoc.rs b/src/util/assoc.rs index 6bfe1ac..9627822 100644 --- a/src/util/assoc.rs +++ b/src/util/assoc.rs @@ -1,7 +1,7 @@ -use std::rc::Rc; -use std::clone::Clone; use runtime::reify::Reifiable; +use std::clone::Clone; use std::fmt; +use std::rc::Rc; // Potential optimization: replace a run of ten nodes with a `HashMap`. // Recursively replace runs of those, too... @@ -16,34 +16,39 @@ custom_derive! { } } -impl Clone for Assoc { +impl Clone for Assoc { fn clone(&self) -> Assoc { - Assoc { - n: self.n.clone() - } + Assoc { n: self.n.clone() } } } -impl PartialEq for Assoc { +impl PartialEq for Assoc { fn eq(&self, other: &Assoc) -> bool { for (k, v) in self.iter_pairs() { if let Some(other_v) = other.find(k) { - if !(v == other_v) { return false; } - } else { return false; } + if !(v == other_v) { + return false; + } + } else { + return false; + } } for (other_k, other_v) in other.iter_pairs() { if let Some(v) = self.find(other_k) { - if !(v == other_v) { return false; } - } else { return false; } + if !(v == other_v) { + return false; + } + } else { + return false; + } } true } } -impl Eq for Assoc {} - +impl Eq for Assoc {} custom_derive! { #[derive(Reifiable)] @@ -56,21 +61,24 @@ custom_derive! { // This would rather be `#[derive(Clone)]`, but that would require `K: PartialEq` impl Clone for AssocNode { - fn clone(&self) -> AssocNode { - AssocNode:: { k: self.k.clone(), v: self.v.clone(), next: self.next.clone() } + fn clone(&self) -> AssocNode { + AssocNode:: { + k: self.k.clone(), + v: self.v.clone(), + next: self.next.clone(), + } } } -impl Assoc { - +impl Assoc { /// Possibly unintuitively, all empty assocs are identical. pub fn almost_ptr_eq(&self, other: &Assoc) -> bool { match (&self.n, &other.n) { (&None, &None) => true, (&Some(ref l_rc), &Some(ref r_rc)) => { - &**l_rc as *const AssocNode == &**r_rc as *const AssocNode + &**l_rc as *const AssocNode == &**r_rc as *const AssocNode } - _ => false + _ => false, } } @@ -87,17 +95,22 @@ impl Assoc { } } - pub fn empty(&self) -> bool { self.n.is_none() } + pub fn empty(&self) -> bool { + self.n.is_none() + } pub fn set(&self, k: K, v: V) -> Assoc { - Assoc{ + Assoc { n: Some(Rc::new(AssocNode { - k: k, v: v, next: Assoc { n: self.n.clone() } - }))} + k: k, + v: v, + next: Assoc { n: self.n.clone() }, + })), + } } pub fn new() -> Assoc { - Assoc{ n: None } + Assoc { n: None } } pub fn single(k: K, v: V) -> Assoc { @@ -105,100 +118,113 @@ impl Assoc { } pub fn iter_pairs(&self) -> PairIter { - PairIter{ seen: Assoc::new(), cur: self } + PairIter { + seen: Assoc::new(), + cur: self, + } } pub fn reduce(&self, red: &dyn Fn(&K, &V, Out) -> Out, base: Out) -> Out { match self.n { None => base, - Some(ref node) => { - red(&node.k, &node.v, node.next.reduce(red, base)) - } + Some(ref node) => red(&node.k, &node.v, node.next.reduce(red, base)), } } } -impl Assoc { - pub fn iter_keys<'assoc>(&'assoc self) -> Box +'assoc> { +impl Assoc { + pub fn iter_keys<'assoc>(&'assoc self) -> Box + 'assoc> { Box::new(self.iter_pairs().map(|p| (*p.0).clone())) } } -impl Assoc { - pub fn iter_values<'assoc>(&'assoc self) -> Box + 'assoc> { +impl Assoc { + pub fn iter_values<'assoc>(&'assoc self) -> Box + 'assoc> { Box::new(self.iter_pairs().map(|p| (*p.1).clone())) } - pub fn map(&self, mut f: F) -> Assoc where F: FnMut(&V) -> NewV { + pub fn map(&self, mut f: F) -> Assoc + where + F: FnMut(&V) -> NewV, + { self.map_borrow_f(&mut f) } - pub fn map_borrow_f(&self, f: &mut F) -> Assoc where F: FnMut(&V) -> NewV { + pub fn map_borrow_f(&self, f: &mut F) -> Assoc + where + F: FnMut(&V) -> NewV, + { match self.n { - None => Assoc{ n: None }, - Some(ref node) => { - Assoc { - n: Some(Rc::new(AssocNode { - k: node.k.clone(), v: f(&node.v), - next: node.next.map_borrow_f(f) - })) - } - } + None => Assoc { n: None }, + Some(ref node) => Assoc { + n: Some(Rc::new(AssocNode { + k: node.k.clone(), + v: f(&node.v), + next: node.next.map_borrow_f(f), + })), + }, } } pub fn keyed_map_borrow_f(&self, f: &mut F) -> Assoc - where F: FnMut(&K, &V) -> NewV { + where + F: FnMut(&K, &V) -> NewV, + { match self.n { - None => Assoc{ n: None }, - Some(ref node) => { - Assoc { - n: Some(Rc::new(AssocNode { - k: node.k.clone(), v: f(&node.k, &node.v), - next: node.next.keyed_map_borrow_f(f) - })) - } - } + None => Assoc { n: None }, + Some(ref node) => Assoc { + n: Some(Rc::new(AssocNode { + k: node.k.clone(), + v: f(&node.k, &node.v), + next: node.next.keyed_map_borrow_f(f), + })), + }, } } // TODO: this should handle missing keys symmetrically - pub fn map_with(&self, other: &Assoc, f: &dyn Fn(&V, &V) -> NewV) - -> Assoc { + pub fn map_with( + &self, + other: &Assoc, + f: &dyn Fn(&V, &V) -> NewV, + ) -> Assoc { match self.n { - None => Assoc{ n: None }, + None => Assoc { n: None }, Some(ref node) => { Assoc { n: Some(Rc::new(AssocNode { k: node.k.clone(), // Should we require `K` and `V` to be `Debug` to use `find_or_panic`? v: f(&node.v, other.find(&node.k).unwrap()), - next: node.next.map_with(other, f) - })) + next: node.next.map_with(other, f), + })), } } } } - pub fn keyed_map_with(&self, other: &Assoc, f: &dyn Fn(&K, &V, &V) -> NewV) - -> Assoc { + pub fn keyed_map_with( + &self, + other: &Assoc, + f: &dyn Fn(&K, &V, &V) -> NewV, + ) -> Assoc { match self.n { - None => Assoc{ n: None }, + None => Assoc { n: None }, Some(ref node) => { Assoc { n: Some(Rc::new(AssocNode { k: node.k.clone(), // Should we require `K` and `V` to be `Debug` to use `find_or_panic`? v: f(&node.k, &node.v, other.find(&node.k).unwrap()), - next: node.next.keyed_map_with(other, f) - })) + next: node.next.keyed_map_with(other, f), + })), } } } } } -impl Assoc { +impl Assoc { pub fn find_value<'assoc, 'f>(&'assoc self, target: &'f V) -> Option<&'assoc K> { match self.n { None => None, @@ -216,20 +242,20 @@ impl Assoc { impl Assoc { pub fn find_or_panic<'assoc, 'f>(&'assoc self, target: &'f K) -> &'assoc V { match self.find(target) { - None => { - panic!("{:#?} not found in {:#?}", target, self.map(|_| "…")) - }, - Some(v) => v + None => panic!("{:#?} not found in {:#?}", target, self.map(|_| "…")), + Some(v) => v, } } } -impl fmt::Debug for Assoc { +impl fmt::Debug for Assoc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "⟦")); let mut first = true; - for (k,v) in self.iter_pairs() { - if !first { try!(write!(f, ", ")); } + for (k, v) in self.iter_pairs() { + if !first { + try!(write!(f, ", ")); + } try!(write!(f, "{:#?} ⇒ {:#?}", k, v)); first = false; } @@ -237,12 +263,14 @@ impl fmt::Debug for Assoc fmt::Display for Assoc { +impl fmt::Display for Assoc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(write!(f, "⟦")); let mut first = true; - for (k,v) in self.iter_pairs() { - if !first { try!(write!(f, ", ")); } + for (k, v) in self.iter_pairs() { + if !first { + try!(write!(f, ", ")); + } try!(write!(f, "{} ⇒ {}", k, v)); first = false; } @@ -250,14 +278,13 @@ impl fmt::Display for As } } -impl Assoc { - +impl Assoc { pub fn set_assoc(&self, other: &Assoc) -> Assoc { match other.n { None => (*self).clone(), - Some(ref node) => { - self.set_assoc(&node.next).set(node.k.clone(), node.v.clone()) - } + Some(ref node) => self + .set_assoc(&node.next) + .set(node.k.clone(), node.v.clone()), } } @@ -271,23 +298,25 @@ impl Assoc { return Assoc::new(); // we found the common suffix } } - node.next.cut_common(other).set(node.k.clone(), node.v.clone()) + node.next + .cut_common(other) + .set(node.k.clone(), node.v.clone()) } } } - pub fn unset(&self, k: &K) -> Assoc { match self.n { - None => Assoc{ n: None }, + None => Assoc { n: None }, Some(ref node) => { let v = node.v.clone(); if &node.k != k { - Assoc{ + Assoc { n: Some(Rc::new(AssocNode { - k: node.k.clone(), v: v, - next: node.next.unset(k) - })) + k: node.k.clone(), + v: v, + next: node.next.unset(k), + })), } } else { node.next.unset(k) @@ -316,9 +345,8 @@ impl Assoc { }*/ } - pub struct KeyIter<'assoc, K: PartialEq + 'assoc, V: 'assoc> { - cur: &'assoc Assoc + cur: &'assoc Assoc, } impl<'assoc, K: PartialEq, V> Iterator for KeyIter<'assoc, K, V> { @@ -335,7 +363,7 @@ impl<'assoc, K: PartialEq, V> Iterator for KeyIter<'assoc, K, V> { } pub struct ValueIter<'assoc, K: PartialEq + 'assoc, V: 'assoc> { - cur: &'assoc Assoc + cur: &'assoc Assoc, } impl<'assoc, K: PartialEq, V> Iterator for ValueIter<'assoc, K, V> { @@ -353,7 +381,7 @@ impl<'assoc, K: PartialEq, V> Iterator for ValueIter<'assoc, K, V> { pub struct PairIter<'assoc, K: PartialEq + 'assoc, V: 'assoc> { seen: Assoc, // TODO: this should probably be a HashMap to avoid quadratic behavior. - cur: &'assoc Assoc + cur: &'assoc Assoc, } impl<'assoc, K: PartialEq + Clone, V> Iterator for PairIter<'assoc, K, V> { @@ -363,7 +391,8 @@ impl<'assoc, K: PartialEq + Clone, V> Iterator for PairIter<'assoc, K, V> { None => None, Some(ref node) => { self.cur = &(*node).next; - if self.seen.find(&(*node).k).is_none() { // have we done this key already? + if self.seen.find(&(*node).k).is_none() { + // have we done this key already? self.seen = self.seen.set((*node).k.clone(), ()); Some((&(*node).k, &(*node).v)) } else { @@ -374,13 +403,12 @@ impl<'assoc, K: PartialEq + Clone, V> Iterator for PairIter<'assoc, K, V> { } } - #[test] fn basic_assoc() { - let mt : Assoc = Assoc::new(); - let a1 = mt.set(5,6); - let a2 = a1.set(6,7); - let a_override = a2.set(5,500); + let mt: Assoc = Assoc::new(); + let a1 = mt.set(5, 6); + let a2 = a1.set(6, 7); + let a_override = a2.set(5, 500); assert_eq!(mt.find(&5), None); assert_eq!(a1.find(&6), None); @@ -400,19 +428,17 @@ fn basic_assoc() { assert_eq!(a_override.unset(&5).find(&6), Some(&7)); assert_eq!(a_override.unset(&-111).find(&5), Some(&500)); - } - #[test] fn assoc_equality() { - let mt : Assoc = Assoc::new(); - let a1 = mt.set(5,6); - let a2 = a1.set(6,7); - let a_override = a2.set(5,500); + let mt: Assoc = Assoc::new(); + let a1 = mt.set(5, 6); + let a2 = a1.set(6, 7); + let a_override = a2.set(5, 500); - let a2_opposite = mt.set(6,7).set(5,6); - let a_override_direct = mt.set(5,500).set(6,7); + let a2_opposite = mt.set(6, 7).set(5, 6); + let a_override_direct = mt.set(5, 500).set(6, 7); assert_eq!(mt, Assoc::new()); assert_eq!(a1, a1); @@ -423,7 +449,7 @@ fn assoc_equality() { assert_eq!(a_override, a_override_direct); assert!(a2 != a_override); - let a1_again = mt.set(5,6); + let a1_again = mt.set(5, 6); // Nothing shared: no-op assert_eq!(mt.cut_common(&mt), mt); @@ -433,22 +459,21 @@ fn assoc_equality() { assert_eq!(a_override_direct.cut_common(&a_override), a_override_direct); assert_eq!(a_override.cut_common(&a_override_direct), a_override); - // Everything shared: empty result assert_eq!(a1.cut_common(&a1), mt); assert_eq!(a2.cut_common(&a2), mt); // Partial share: - assert_eq!(a2.cut_common(&a1), mt.set(6,7)); - assert_eq!(a_override.cut_common(&a2), mt.set(5,500)); + assert_eq!(a2.cut_common(&a1), mt.set(6, 7)); + assert_eq!(a_override.cut_common(&a2), mt.set(5, 500)); } #[test] fn assoc_r_and_r_roundtrip() { use num::BigInt; - let mt : Assoc = Assoc::new(); - let a1 = mt.set(BigInt::from(5),BigInt::from(6)); - let a2 = a1.set(BigInt::from(6),BigInt::from(7)); + let mt: Assoc = Assoc::new(); + let a1 = mt.set(BigInt::from(5), BigInt::from(6)); + let a2 = a1.set(BigInt::from(6), BigInt::from(7)); assert_eq!(mt, Assoc::::reflect(&mt.reify())); assert_eq!(a2, Assoc::::reflect(&a2.reify())); @@ -457,18 +482,23 @@ fn assoc_r_and_r_roundtrip() { #[test] fn assoc_map() { let a1 = assoc_n!("x" => 1, "y" => 2, "z" => 3); - assert_eq!(a1.map(|a| a+1), assoc_n!("x" => 2, "y" => 3, "z" => 4)); + assert_eq!(a1.map(|a| a + 1), assoc_n!("x" => 2, "y" => 3, "z" => 4)); let a2 = assoc_n!("y" => -2, "z" => -3, "x" => -1); - assert_eq!(a1.map_with(&a2, &|a, b| a+b), - assoc_n!("x" => 0, "y" => 0, "z" => 0)); + assert_eq!( + a1.map_with(&a2, &|a, b| a + b), + assoc_n!("x" => 0, "y" => 0, "z" => 0) + ); } #[test] fn assoc_reduce() { let a1 = assoc_n!("x" => 1, "y" => 2, "z" => 3); - assert_eq!(a1.reduce(&|_key, a, b| a+b, 0), 6); + assert_eq!(a1.reduce(&|_key, a, b| a + b, 0), 6); let a1 = assoc_n!("x" => 1, "y" => 2, "z" => 3); - assert_eq!(a1.reduce(&|key, a, b| if key.is("y") { b } else { a+b }, 0), 4); + assert_eq!( + a1.reduce(&|key, a, b| if key.is("y") { b } else { a + b }, 0), + 4 + ); } diff --git a/src/util/err.rs b/src/util/err.rs index fb256fa..b2af04d 100644 --- a/src/util/err.rs +++ b/src/util/err.rs @@ -1,4 +1,4 @@ -use std::fmt::{Display, Debug, Result, Formatter}; +use std::fmt::{Debug, Display, Formatter, Result}; custom_derive! { #[derive(Reifiable, Clone, PartialEq)] diff --git a/src/util/mbe.rs b/src/util/mbe.rs index 4357a2b..13383e7 100644 --- a/src/util/mbe.rs +++ b/src/util/mbe.rs @@ -71,12 +71,10 @@ So, if you set a particular index is `ddd`, that will be repeated 0 or more time in order to match the length of whatever is on the other side. */ - -use util::assoc::Assoc; use name::*; -use std::rc::Rc; use std::fmt; - +use std::rc::Rc; +use util::assoc::Assoc; // How on earth can one data structure need so many variations on `map`? // There's got to be a better way! @@ -122,40 +120,49 @@ custom_derive! { } } -impl PartialEq for EnvMBE { - fn eq(&self, other: &EnvMBE) -> bool { - fn assoc_eq_modulo_none - (lhs: &Assoc>, rhs: &Assoc>) - -> bool { - for (k, v_maybe) in lhs.iter_pairs() { - if let Some(ref v) = *v_maybe { - if let Some(&Some(ref other_v)) = rhs.find(k) { - if !(v == other_v) { return false; } - } else { return false; } - } - } - - for (other_k, other_v_maybe) in rhs.iter_pairs() { - if let Some(ref other_v) = *other_v_maybe { - if let Some(&Some(ref v)) = rhs.find(other_k) { - if !(v == other_v) { return false; } - } else { return false; } - } - } - - true - } - - // This ought to handle permutations of `repeats` - // (matched with permutations of the indices in the assocs) - // but that's hard. - - self.leaves == other.leaves - && self.repeats == other.repeats - && self.ddd_rep_idxes == other.ddd_rep_idxes - && assoc_eq_modulo_none(&self.leaf_locations, &other.leaf_locations) - && assoc_eq_modulo_none(&self.named_repeats, &other.named_repeats) - } +impl PartialEq for EnvMBE { + fn eq(&self, other: &EnvMBE) -> bool { + fn assoc_eq_modulo_none( + lhs: &Assoc>, + rhs: &Assoc>, + ) -> bool { + for (k, v_maybe) in lhs.iter_pairs() { + if let Some(ref v) = *v_maybe { + if let Some(&Some(ref other_v)) = rhs.find(k) { + if !(v == other_v) { + return false; + } + } else { + return false; + } + } + } + + for (other_k, other_v_maybe) in rhs.iter_pairs() { + if let Some(ref other_v) = *other_v_maybe { + if let Some(&Some(ref v)) = rhs.find(other_k) { + if !(v == other_v) { + return false; + } + } else { + return false; + } + } + } + + true + } + + // This ought to handle permutations of `repeats` + // (matched with permutations of the indices in the assocs) + // but that's hard. + + self.leaves == other.leaves + && self.repeats == other.repeats + && self.ddd_rep_idxes == other.ddd_rep_idxes + && assoc_eq_modulo_none(&self.leaf_locations, &other.leaf_locations) + && assoc_eq_modulo_none(&self.named_repeats, &other.named_repeats) + } } impl fmt::Debug for EnvMBE { @@ -166,13 +173,17 @@ impl fmt::Debug for EnvMBE { try!(write!(f, "{{ 🍂 {:#?}, ✶", self.leaves)); let mut first = true; for (i, rep) in self.repeats.iter().enumerate() { - if !first { try!(write!(f, ", ")); } + if !first { + try!(write!(f, ", ")); + } first = false; // is it a named repeat? for (name, idx_maybe) in self.named_repeats.iter_pairs() { if let Some(idx) = *idx_maybe { - if idx == i { try!(write!(f, "⋯({:#?})⋯ ", name)); } + if idx == i { + try!(write!(f, "⋯({:#?})⋯ ", name)); + } } } try!(write!(f, "{:#?}", rep)); @@ -188,7 +199,7 @@ struct DddIter<'a, S: 'a> { cur_idx: usize, rep_idx: usize, repeated: Option<&'a S>, - extra_needed: usize + extra_needed: usize, } impl<'a, S: Clone> DddIter<'a, S> { @@ -198,7 +209,7 @@ impl<'a, S: Clone> DddIter<'a, S> { cur_idx: 0, rep_idx: rep_idx, repeated: None, - extra_needed: extra + extra_needed: extra, } } } @@ -228,7 +239,7 @@ impl EnvMBE { repeats: vec![], ddd_rep_idxes: vec![], leaf_locations: Assoc::new(), - named_repeats: Assoc::new() + named_repeats: Assoc::new(), } } @@ -239,7 +250,7 @@ impl EnvMBE { repeats: vec![], ddd_rep_idxes: vec![], leaf_locations: Assoc::new(), - named_repeats: Assoc::new() + named_repeats: Assoc::new(), } } @@ -280,9 +291,13 @@ impl EnvMBE { repeats: new_repeats, ddd_rep_idxes: new__ddd_rep_idxes, leaf_locations: self.leaf_locations.set_assoc( - &rhs.leaf_locations.map(|idx_opt| idx_opt.map(|idx| idx+adjust_rhs_by))), + &rhs.leaf_locations + .map(|idx_opt| idx_opt.map(|idx| idx + adjust_rhs_by)), + ), named_repeats: self.named_repeats.set_assoc( - &rhs.named_repeats.map(|idx_opt| idx_opt.map(|idx| idx+adjust_rhs_by))) + &rhs.named_repeats + .map(|idx_opt| idx_opt.map(|idx| idx + adjust_rhs_by)), + ), } } @@ -292,7 +307,7 @@ impl EnvMBE { pub fn merge(&self, rhs: &EnvMBE) -> EnvMBE { let mut res = self.clone(); - let mut rhs_idx_is_named : Vec = rhs.repeats.iter().map(|_| false).collect(); + let mut rhs_idx_is_named: Vec = rhs.repeats.iter().map(|_| false).collect(); // This could be made more efficient by just reusing the `Rc`s instead of cloning the // arrays, but that would require reworking the interface. @@ -300,14 +315,17 @@ impl EnvMBE { for (n, rep_idx) in rhs.named_repeats.iter_pairs() { if let Some(rep_idx) = *rep_idx { res.add_named_repeat( - *n, (*rhs.repeats[rep_idx]).clone(), rhs.ddd_rep_idxes[rep_idx]); + *n, + (*rhs.repeats[rep_idx]).clone(), + rhs.ddd_rep_idxes[rep_idx], + ); rhs_idx_is_named[rep_idx] = true; } } for (idx, (rep, ddd_rep_idx)) in - rhs.repeats.iter().zip(rhs.ddd_rep_idxes.iter()).enumerate() { - + rhs.repeats.iter().zip(rhs.ddd_rep_idxes.iter()).enumerate() + { if !rhs_idx_is_named[idx] { res.add_anon_repeat((**rep).clone(), *ddd_rep_idx); } @@ -325,38 +343,39 @@ impl EnvMBE { /// This takes a `Vec` of `Name` instead of just one because a particular name might /// not be transcribed at all here, and thus can't tell us how to repeat. pub fn march_all(&self, driving_names: &[Name]) -> Vec> { - let mut march_loc : Option<(usize, Name)> = None; + let mut march_loc: Option<(usize, Name)> = None; for &n in driving_names { match (march_loc, self.leaf_locations.find(&n).unwrap_or(&None)) { - (_, &None) => {} - (None, &Some(loc)) => { march_loc = Some((loc, n)) } - (Some((old_loc, old_name)), &Some(new_loc)) => { - if old_loc != new_loc { - panic!("{:#?} and {:#?} cannot march together; they weren't matched to have the same number of repeats", + (_, &None) => {} + (None, &Some(loc)) => march_loc = Some((loc, n)), + (Some((old_loc, old_name)), &Some(new_loc)) => { + if old_loc != new_loc { + panic!("{:#?} and {:#?} cannot march together; they weren't matched to have the same number of repeats", old_name, n); - } - } + } + } } } let march_loc = match march_loc { - None => { return vec![]; } // FOOTGUN: assume that it is repeated zero times - Some((loc, _)) => loc + None => { + return vec![]; + } // FOOTGUN: assume that it is repeated zero times + Some((loc, _)) => loc, }; let mut result = vec![]; for marched_out in self.repeats[march_loc].iter() { // TODO: should we allow cross-product marching by keeping around unused repeats? // Don't lose the current leaves: - result.push(EnvMBE::new_from_leaves(self.leaves.clone()) - .combine_overriding(marched_out)); + result + .push(EnvMBE::new_from_leaves(self.leaves.clone()).combine_overriding(marched_out)); } result } - /// Get a non-repeated thing in the enviornment pub fn get_leaf(&self, n: Name) -> Option<&T> { self.leaves.find(&n) @@ -369,13 +388,17 @@ impl EnvMBE { let mut res = vec![]; let leaf_loc = match self.leaf_locations.find(&n) { Some(&Some(ll)) => ll, - _ => { return Some(vec![]); } + _ => { + return Some(vec![]); + } }; for r in &*self.repeats[leaf_loc] { match r.get_leaf(n) { - Some(leaf) => { res.push(leaf) } - None => { return Some(vec![]); } // `march` can leave us with dead leaf_locations + Some(leaf) => res.push(leaf), + None => { + return Some(vec![]); + } // `march` can leave us with dead leaf_locations } } Some(res) @@ -387,7 +410,9 @@ impl EnvMBE { } pub fn add_named_repeat(&mut self, n: Name, sub: Vec>, sub_ddd_idx: Option) { - if sub.is_empty() { return; } // no-op-ish, but keep the repeats clean (good for `eq`) + if sub.is_empty() { + return; + } // no-op-ish, but keep the repeats clean (good for `eq`) match *self.named_repeats.find(&n).unwrap_or(&None) { None => { @@ -413,15 +438,19 @@ impl EnvMBE { self.repeats[idx] = Rc::new(new_repeats_at_idx); if self.ddd_rep_idxes[idx] != sub_ddd_idx { // Maybe we should support this usecase! - panic!("Named repetition {:#?} has mismatched ddd rep indices {:#?} and {:#?}.", - n, self.ddd_rep_idxes[idx], sub_ddd_idx); + panic!( + "Named repetition {:#?} has mismatched ddd rep indices {:#?} and {:#?}.", + n, self.ddd_rep_idxes[idx], sub_ddd_idx + ); } } } } pub fn add_anon_repeat(&mut self, sub: Vec>, sub_ddd_idx: Option) { - if sub.is_empty() { return; } // no-op-ish, but keep the repeats clean (good for `eq`) + if sub.is_empty() { + return; + } // no-op-ish, but keep the repeats clean (good for `eq`) let new_index = self.repeats.len(); self.update_leaf_locs(new_index, &sub); @@ -435,108 +464,167 @@ impl EnvMBE { self.named_repeats = self.named_repeats.set(n, None); } - - pub fn map(&self, f: &mut F) -> EnvMBE where F: FnMut(&T) -> NewT { + pub fn map(&self, f: &mut F) -> EnvMBE + where + F: FnMut(&T) -> NewT, + { self.named_map(&mut |_n, elt| f(elt)) } /// Map, but march the `ctxt` along with the structure of `self` pub fn map_marched_against( - &self, f: &mut F, ctxt: &::ast_walk::LazyWalkReses) -> EnvMBE - where F: FnMut(&T, &::ast_walk::LazyWalkReses) -> NewT { + &self, + f: &mut F, + ctxt: &::ast_walk::LazyWalkReses, + ) -> EnvMBE + where + F: FnMut(&T, &::ast_walk::LazyWalkReses) -> NewT, + { EnvMBE { leaves: self.leaves.map_borrow_f(&mut |t: &T| f(t, ctxt)), - repeats: self.repeats.iter().enumerate().map( - |(rpt_idx, rc_vec_mbe) : (usize, &Rc>>)| { + repeats: self + .repeats + .iter() + .enumerate() + .map(|(rpt_idx, rc_vec_mbe): (usize, &Rc>>)| { let this_rpt_name = self.leaf_locations.find_value(&Some(rpt_idx)).unwrap(); let marched_ctxts = ctxt.march_all(&[*this_rpt_name]); - Rc::new(rc_vec_mbe.iter().zip(marched_ctxts).map( - |(mbe, marched_ctxt) : (&EnvMBE, ::ast_walk::LazyWalkReses)| { - mbe.map_marched_against(f, &marched_ctxt) }).collect()) - }).collect(), + Rc::new( + rc_vec_mbe + .iter() + .zip(marched_ctxts) + .map( + |(mbe, marched_ctxt): ( + &EnvMBE, + ::ast_walk::LazyWalkReses, + )| { + mbe.map_marched_against(f, &marched_ctxt) + }, + ) + .collect(), + ) + }) + .collect(), ddd_rep_idxes: self.ddd_rep_idxes.clone(), leaf_locations: self.leaf_locations.clone(), - named_repeats: self.named_repeats.clone() + named_repeats: self.named_repeats.clone(), } - } - pub fn named_map(&self, f: &mut F) -> EnvMBE where F: FnMut(&Name, &T) -> NewT { + pub fn named_map(&self, f: &mut F) -> EnvMBE + where + F: FnMut(&Name, &T) -> NewT, + { EnvMBE { leaves: self.leaves.keyed_map_borrow_f(f), - repeats: self.repeats.iter().map( - |rc_vec_mbe : &Rc>>| Rc::new(rc_vec_mbe.iter().map( - |mbe : &EnvMBE| mbe.named_map(f) - ).collect())).collect(), + repeats: self + .repeats + .iter() + .map(|rc_vec_mbe: &Rc>>| { + Rc::new( + rc_vec_mbe + .iter() + .map(|mbe: &EnvMBE| mbe.named_map(f)) + .collect(), + ) + }) + .collect(), ddd_rep_idxes: self.ddd_rep_idxes.clone(), leaf_locations: self.leaf_locations.clone(), - named_repeats: self.named_repeats.clone() + named_repeats: self.named_repeats.clone(), } } - pub fn map_reduce(&self, f: &dyn Fn(&T) -> NewT, - red: &dyn Fn(&NewT, &NewT) -> NewT, base: NewT) - -> NewT { - let reduced : NewT = self.leaves.map(f).reduce(&|_k, v, res| red(v, &res), base); - - self.repeats.iter().fold( - reduced, - |base: NewT, rc_vec_mbe : &Rc>>| rc_vec_mbe.iter().fold( - base, - |base: NewT, mbe : &EnvMBE| mbe.map_reduce(f, red, base))) + pub fn map_reduce( + &self, + f: &dyn Fn(&T) -> NewT, + red: &dyn Fn(&NewT, &NewT) -> NewT, + base: NewT, + ) -> NewT { + let reduced: NewT = self.leaves.map(f).reduce(&|_k, v, res| red(v, &res), base); + + self.repeats + .iter() + .fold(reduced, |base: NewT, rc_vec_mbe: &Rc>>| { + rc_vec_mbe.iter().fold(base, |base: NewT, mbe: &EnvMBE| { + mbe.map_reduce(f, red, base) + }) + }) } - /// Provide the map map function with the name of the current leaf, /// and the appropriately-marched context element pub fn marched_map(&self, f: &mut F) -> EnvMBE - where F: FnMut(Name, &EnvMBE, &T) -> NewT { + where + F: FnMut(Name, &EnvMBE, &T) -> NewT, + { self.marched_map_rec(self, f) } fn marched_map_rec(&self, outer: &EnvMBE, f: &mut F) -> EnvMBE - where F: FnMut(Name, &EnvMBE, &T) -> NewT { + where + F: FnMut(Name, &EnvMBE, &T) -> NewT, + { let local_mbe = outer.combine_overriding(self); - let new_leaves - = self.leaves.keyed_map_borrow_f(&mut |n: &Name, elt: &T| f(*n, &local_mbe, elt)); + let new_leaves = self + .leaves + .keyed_map_borrow_f(&mut |n: &Name, elt: &T| f(*n, &local_mbe, elt)); EnvMBE { leaves: new_leaves, - repeats: self.repeats.iter().map( - |rc_vec_mbe : &Rc>>| Rc::new(rc_vec_mbe.iter().map( - |marched_out : &EnvMBE| marched_out.marched_map_rec(outer, f) - ).collect())).collect(), + repeats: self + .repeats + .iter() + .map(|rc_vec_mbe: &Rc>>| { + Rc::new( + rc_vec_mbe + .iter() + .map(|marched_out: &EnvMBE| marched_out.marched_map_rec(outer, f)) + .collect(), + ) + }) + .collect(), ddd_rep_idxes: self.ddd_rep_idxes.clone(), leaf_locations: self.leaf_locations.clone(), - named_repeats: self.named_repeats.clone() + named_repeats: self.named_repeats.clone(), } } /// Duplicate contents of the side with a DDD to line it up with the other // TODO: this needs to return `Option` (and so does everything `_with`) // TODO: for efficiency, this ought to return iterators - fn resolve_ddd<'a>(lhs: &'a Rc>>, lhs_ddd: &'a Option, - rhs: &'a Rc>>, rhs_ddd: &'a Option) - -> Vec<(&'a EnvMBE, &'a EnvMBE)> { - + fn resolve_ddd<'a>( + lhs: &'a Rc>>, + lhs_ddd: &'a Option, + rhs: &'a Rc>>, + rhs_ddd: &'a Option, + ) -> Vec<(&'a EnvMBE, &'a EnvMBE)> { let len_diff = lhs.len() as i32 - (rhs.len() as i32); let matched: Vec<(&EnvMBE, &EnvMBE)> = match (lhs_ddd, rhs_ddd) { (&None, &None) => { - if len_diff != 0 { panic!("mismatched MBE lengths") } + if len_diff != 0 { + panic!("mismatched MBE lengths") + } lhs.iter().zip(rhs.iter()).collect() } (&Some(ddd_idx), &None) => { - if len_diff - 1 > 0 { panic!("abstract MBE LHS too long") } + if len_diff - 1 > 0 { + panic!("abstract MBE LHS too long") + } DddIter::new(lhs.iter(), ddd_idx, -(len_diff - 1) as usize) - .zip(rhs.iter()).collect() + .zip(rhs.iter()) + .collect() } (&None, &Some(ddd_idx)) => { - if len_diff + 1 < 0 { panic!("abstract MBE RHS too long") } - lhs.iter().zip( - DddIter::new(rhs.iter(), ddd_idx, (len_diff + 1) as usize)).collect() + if len_diff + 1 < 0 { + panic!("abstract MBE RHS too long") + } + lhs.iter() + .zip(DddIter::new(rhs.iter(), ddd_idx, (len_diff + 1) as usize)) + .collect() } - (&Some(_), &Some(_)) => panic!("repetition on both sides") + (&Some(_), &Some(_)) => panic!("repetition on both sides"), }; matched @@ -546,11 +634,15 @@ impl EnvMBE { // TODO: try just using `reduced` instead of `base.clone()` // TODO #15: `Result` instead of panicing fn match_collapse_ddd<'a, NewT: Clone>( - lhs: &'a Rc>>, lhs_ddd: &'a Option, - rhs: &'a Rc>>, rhs_ddd: &'a Option, f: &dyn Fn(&T, &T) -> NewT, - col: &dyn Fn(Vec) -> NewT, red: &dyn Fn(NewT, NewT) -> NewT, base: NewT) - -> NewT { - + lhs: &'a Rc>>, + lhs_ddd: &'a Option, + rhs: &'a Rc>>, + rhs_ddd: &'a Option, + f: &dyn Fn(&T, &T) -> NewT, + col: &dyn Fn(Vec) -> NewT, + red: &dyn Fn(NewT, NewT) -> NewT, + base: NewT, + ) -> NewT { let len_diff = lhs.len() as i32 - (rhs.len() as i32); // The RHS can have a DDD if it's exactly the same (subtyping only goes the one way) @@ -564,32 +656,46 @@ impl EnvMBE { let mut reduced = base.clone(); // Unpack the DDD ... unless the RHS is a DDD also. if let (&Some(ddd_idx), false) = (lhs_ddd, rhs_ddd.is_some()) { - if len_diff - 1 > 0 { panic!("abstract MBE LHS too long") } + if len_diff - 1 > 0 { + panic!("abstract MBE LHS too long") + } for i in 0..ddd_idx { - reduced = red(reduced, - lhs[i].map_collapse_reduce_with(&rhs[i], f, col, red, base.clone())); + reduced = red( + reduced, + lhs[i].map_collapse_reduce_with(&rhs[i], f, col, red, base.clone()), + ); } let mut ddd_rep = vec![]; - for rep in 0..((1-len_diff) as usize) { - ddd_rep.push( - lhs[ddd_idx].map_collapse_reduce_with(&rhs[ddd_idx+rep], f, col, red, base.clone())); + for rep in 0..((1 - len_diff) as usize) { + ddd_rep.push(lhs[ddd_idx].map_collapse_reduce_with( + &rhs[ddd_idx + rep], + f, + col, + red, + base.clone(), + )); } reduced = red(reduced, col(ddd_rep)); - for i in (ddd_idx+1)..lhs.len() { - let rhs_i = (i as i32-len_diff) as usize; + for i in (ddd_idx + 1)..lhs.len() { + let rhs_i = (i as i32 - len_diff) as usize; reduced = red( reduced, - lhs[i].map_collapse_reduce_with(&rhs[rhs_i], f, col, red, base.clone())); + lhs[i].map_collapse_reduce_with(&rhs[rhs_i], f, col, red, base.clone()), + ); } } else { - if len_diff != 0 { panic!("mismatched MBE lengths {} vs. {}", lhs.len(), rhs.len()) } + if len_diff != 0 { + panic!("mismatched MBE lengths {} vs. {}", lhs.len(), rhs.len()) + } for i in 0..lhs.len() { - reduced = red(reduced, - lhs[i].map_collapse_reduce_with(&rhs[i], f, col, red, base.clone())); + reduced = red( + reduced, + lhs[i].map_collapse_reduce_with(&rhs[i], f, col, red, base.clone()), + ); } } reduced @@ -598,24 +704,41 @@ impl EnvMBE { // TODO: we should just have the relevant functions return None... pub fn can_map_with(&self, o: &EnvMBE) -> bool { let mut lhs_keys = ::std::collections::HashSet::::new(); - for (k,_) in self.leaves.iter_pairs() { lhs_keys.insert(*k); } + for (k, _) in self.leaves.iter_pairs() { + lhs_keys.insert(*k); + } let mut rhs_keys = ::std::collections::HashSet::::new(); - for (k,_) in o.leaves.iter_pairs() { rhs_keys.insert(*k); } + for (k, _) in o.leaves.iter_pairs() { + rhs_keys.insert(*k); + } - if lhs_keys != rhs_keys { return false; } + if lhs_keys != rhs_keys { + return false; + } - for ((subs, subs_ddd), (o_subs, o_subs_ddd)) in - self.repeats.iter().zip(self.ddd_rep_idxes.iter()) - .zip(o.repeats.iter().zip(o.ddd_rep_idxes.iter())) { - if subs_ddd != &None && o_subs_ddd != &None { panic!("Ill-formed; can't walk two DDDs") } + for ((subs, subs_ddd), (o_subs, o_subs_ddd)) in self + .repeats + .iter() + .zip(self.ddd_rep_idxes.iter()) + .zip(o.repeats.iter().zip(o.ddd_rep_idxes.iter())) + { + if subs_ddd != &None && o_subs_ddd != &None { + panic!("Ill-formed; can't walk two DDDs") + } if subs_ddd == &None && o_subs_ddd == &None && subs.len() != o_subs.len() { return false; } - if subs_ddd != &None && o_subs.len() < subs.len() - 1 { return false; } - if o_subs_ddd != &None && subs.len() < o_subs.len() - 1 { return false; } + if subs_ddd != &None && o_subs.len() < subs.len() - 1 { + return false; + } + if o_subs_ddd != &None && subs.len() < o_subs.len() - 1 { + return false; + } for (mbe, o_mbe) in Self::resolve_ddd(subs, subs_ddd, o_subs, o_subs_ddd) { - if !mbe.can_map_with(o_mbe) { return false; } + if !mbe.can_map_with(o_mbe) { + return false; + } } } @@ -623,56 +746,79 @@ impl EnvMBE { } pub fn map_with(&self, o: &EnvMBE, f: &dyn Fn(&T, &T) -> NewT) -> EnvMBE { - self.named_map_with(o, &|_name, l, r| f(l,r)) + self.named_map_with(o, &|_name, l, r| f(l, r)) } - pub fn named_map_with(&self, o: &EnvMBE, f: &dyn Fn(&Name, &T, &T) -> NewT) - -> EnvMBE { + pub fn named_map_with( + &self, + o: &EnvMBE, + f: &dyn Fn(&Name, &T, &T) -> NewT, + ) -> EnvMBE { EnvMBE { leaves: self.leaves.keyed_map_with(&o.leaves, f), // This assumes that "equivalent" repeats have the same indices... ) : - repeats: - self.repeats.iter().zip(self.ddd_rep_idxes.iter()) - .zip(o.repeats.iter().zip(o.ddd_rep_idxes.iter())).map( - - &|((rc_vec_mbe, ddd_idx), (o_rc_vec_mbe, o_ddd_idx)) : - ((&Rc>>, &Option), (&Rc>>, &Option))| { - - let mapped : Vec<_> - = Self::resolve_ddd(rc_vec_mbe, ddd_idx, o_rc_vec_mbe, o_ddd_idx).iter() - .map(|&(mbe, o_mbe) : &(&EnvMBE, &EnvMBE)| - mbe.named_map_with(o_mbe, f)) - .collect(); - - Rc::new(mapped)}).collect(), + repeats: self + .repeats + .iter() + .zip(self.ddd_rep_idxes.iter()) + .zip(o.repeats.iter().zip(o.ddd_rep_idxes.iter())) + .map(&|((rc_vec_mbe, ddd_idx), (o_rc_vec_mbe, o_ddd_idx)): ( + (&Rc>>, &Option), + (&Rc>>, &Option), + )| { + let mapped: Vec<_> = + Self::resolve_ddd(rc_vec_mbe, ddd_idx, o_rc_vec_mbe, o_ddd_idx) + .iter() + .map(|&(mbe, o_mbe): &(&EnvMBE, &EnvMBE)| { + mbe.named_map_with(o_mbe, f) + }) + .collect(); + + Rc::new(mapped) + }) + .collect(), ddd_rep_idxes: self.repeats.iter().map(|_| None).collect(), // remove all dotdotdots leaf_locations: self.leaf_locations.clone(), - named_repeats: self.named_repeats.clone() + named_repeats: self.named_repeats.clone(), } } - - pub fn map_reduce_with(&self, other: &EnvMBE, - f: &dyn Fn(&T, &T) -> NewT, red: &dyn Fn(&NewT, &NewT) -> NewT, base: NewT) -> NewT { + pub fn map_reduce_with( + &self, + other: &EnvMBE, + f: &dyn Fn(&T, &T) -> NewT, + red: &dyn Fn(&NewT, &NewT) -> NewT, + base: NewT, + ) -> NewT { // TODO #15: this panics all over the place if anything goes wrong - let mut reduced : NewT = self.leaves.map_with(&other.leaves, f) + let mut reduced: NewT = self + .leaves + .map_with(&other.leaves, f) .reduce(&|_k, v, res| red(v, &res), base); - let mut already_processed : Vec = self.repeats.iter().map(|_| false).collect(); + let mut already_processed: Vec = self.repeats.iter().map(|_| false).collect(); for (leaf_name, self_idx) in self.leaf_locations.iter_pairs() { let self_idx = match *self_idx { - Some(si) => si, None => { continue; } + Some(si) => si, + None => { + continue; + } }; - if already_processed[self_idx] { continue; } + if already_processed[self_idx] { + continue; + } already_processed[self_idx] = true; let other_idx = other.leaf_locations.find_or_panic(leaf_name).unwrap(); let matched = Self::resolve_ddd( - &self.repeats[self_idx], &self.ddd_rep_idxes[self_idx], - &other.repeats[other_idx], &other.ddd_rep_idxes[other_idx]); + &self.repeats[self_idx], + &self.ddd_rep_idxes[self_idx], + &other.repeats[other_idx], + &other.ddd_rep_idxes[other_idx], + ); for (self_elt, other_elt) in matched { reduced = self_elt.map_reduce_with(other_elt, f, &red, reduced); @@ -684,26 +830,45 @@ impl EnvMBE { // TODO: test this more. pub fn map_collapse_reduce_with( - &self, other: &EnvMBE, f: &dyn Fn(&T, &T) -> NewT, col: &dyn Fn(Vec) -> NewT, - red: &dyn Fn(NewT, NewT) -> NewT, base: NewT) -> NewT { + &self, + other: &EnvMBE, + f: &dyn Fn(&T, &T) -> NewT, + col: &dyn Fn(Vec) -> NewT, + red: &dyn Fn(NewT, NewT) -> NewT, + base: NewT, + ) -> NewT { // TODO #15: this panics all over the place if anything goes wrong - let mut reduced : NewT = self.leaves.map_with(&other.leaves, f) + let mut reduced: NewT = self + .leaves + .map_with(&other.leaves, f) .reduce(&|_k, v, res| red(v.clone(), res), base); - let mut already_processed : Vec = self.repeats.iter().map(|_| false).collect(); + let mut already_processed: Vec = self.repeats.iter().map(|_| false).collect(); for (leaf_name, self_idx) in self.leaf_locations.iter_pairs() { let self_idx = match *self_idx { - Some(si) => si, None => { continue; } + Some(si) => si, + None => { + continue; + } }; - if already_processed[self_idx] { continue; } + if already_processed[self_idx] { + continue; + } already_processed[self_idx] = true; let other_idx = other.leaf_locations.find_or_panic(leaf_name).unwrap(); reduced = Self::match_collapse_ddd( - &self.repeats[self_idx], &self.ddd_rep_idxes[self_idx], - &other.repeats[other_idx], &other.ddd_rep_idxes[other_idx], f, col, red, reduced); + &self.repeats[self_idx], + &self.ddd_rep_idxes[self_idx], + &other.repeats[other_idx], + &other.ddd_rep_idxes[other_idx], + f, + col, + red, + reduced, + ); } reduced @@ -714,8 +879,11 @@ impl EnvMBE { let mut already_placed_repeats = ::std::collections::HashSet::::new(); for sub_mbe in sub { - for leaf_name in sub_mbe.leaf_locations.iter_keys() - .chain(sub_mbe.leaves.iter_keys()) { + for leaf_name in sub_mbe + .leaf_locations + .iter_keys() + .chain(sub_mbe.leaves.iter_keys()) + { if !already_placed_leaves.contains(&leaf_name) { self.leaf_locations = self.leaf_locations.set(leaf_name, Some(idx)); already_placed_leaves.insert(leaf_name); @@ -731,9 +899,9 @@ impl EnvMBE { } // If `f` turns a leaf into a `Vec`, splice those results in - pub fn heal_splices(&mut self, f: &dyn Fn(&T) -> Option>) { + pub fn heal_splices(&mut self, f: &dyn Fn(&T) -> Option>) { for repeat in &mut self.repeats { - let mut cur_repeat : Vec> = (**repeat).clone(); + let mut cur_repeat: Vec> = (**repeat).clone(); let mut i = 0; while i < cur_repeat.len() { cur_repeat[i].heal_splices(f); @@ -741,9 +909,9 @@ impl EnvMBE { let mut splices = vec![]; { let n_and_vals = cur_repeat[i].leaves.iter_pairs(); - for (n, val) in n_and_vals { + for (n, val) in n_and_vals { if let Some(splice) = f(val) { - splices.push((*n,splice)); + splices.push((*n, splice)); } } } @@ -757,10 +925,9 @@ impl EnvMBE { for splice in &splices { template.add_leaf(splice.0, splice.1[rep].clone()); } - cur_repeat.insert(i+rep, template.clone()) + cur_repeat.insert(i + rep, template.clone()) } i += splices[0].1.len(); - } else { i += 1; } @@ -770,27 +937,34 @@ impl EnvMBE { } // TODO: this should return a usable error - pub fn heal_splices__with(&mut self, other: &EnvMBE, - f: &dyn Fn(&T, &dyn Fn() -> Vec) -> Option>) - -> Result<(), ()> - where T: std::fmt::Debug { - + pub fn heal_splices__with( + &mut self, + other: &EnvMBE, + f: &dyn Fn(&T, &dyn Fn() -> Vec) -> Option>, + ) -> Result<(), ()> + where + T: std::fmt::Debug, + { for repeat in &mut self.repeats { // Find the same repeat in `other`: let mut names_needed = vec![]; for (name, _) in self.leaf_locations.iter_pairs() { names_needed.push(name); } - let other__rep_loc = other.leaf_locations.find(names_needed[0]) - .unwrap_or(&None).ok_or(())?; + let other__rep_loc = other + .leaf_locations + .find(names_needed[0]) + .unwrap_or(&None) + .ok_or(())?; // TODO: error out if we don't get the same result for all `names_needed[n]` - let other__cur_repeat : &Vec> = &*other.repeats[other__rep_loc]; - let mut cur_repeat : Vec> = (**repeat).clone(); + let other__cur_repeat: &Vec> = &*other.repeats[other__rep_loc]; + let mut cur_repeat: Vec> = (**repeat).clone(); // If an item splices, how wide does the other side need to be // in order to make everything line up? - let splice_length = (other__cur_repeat.len()+1).checked_sub(cur_repeat.len()) + let splice_length = (other__cur_repeat.len() + 1) + .checked_sub(cur_repeat.len()) .ok_or(())?; let mut i = 0; @@ -802,18 +976,18 @@ impl EnvMBE { { let n_and_vals = cur_repeat[i].leaves.iter_pairs(); for (n, val) in n_and_vals { - let concrete_splice__thunk = || { let mut result = vec![]; - for spliced_i in i .. i+splice_length { + for spliced_i in i..i + splice_length { result.push( - other__cur_repeat[spliced_i].leaves.find_or_panic(n).clone()) + other__cur_repeat[spliced_i].leaves.find_or_panic(n).clone(), + ) } result }; if let Some(splice) = f(val, &concrete_splice__thunk) { - splices.push((*n,splice)); + splices.push((*n, splice)); } } } @@ -827,7 +1001,7 @@ impl EnvMBE { for splice in &splices { template.add_leaf(splice.0, splice.1[rep].clone()); } - cur_repeat.insert(i+rep, template.clone()) + cur_repeat.insert(i + rep, template.clone()) } i += splice_length; other_i += splice_length; @@ -843,16 +1017,15 @@ impl EnvMBE { } Ok(()) } - } impl EnvMBE> { // Is `lift` the right term? pub fn lift_result(&self) -> Result, E> { // There's probably a nice and elegant way to do this with Result::from_iter, but not today - let mut leaves : Assoc = Assoc::new(); - for (k,v) in self.leaves.iter_pairs() { - leaves = leaves.set(*k,try!((*v).clone())); + let mut leaves: Assoc = Assoc::new(); + for (k, v) in self.leaves.iter_pairs() { + leaves = leaves.set(*k, try!((*v).clone())); } let mut repeats = vec![]; @@ -864,25 +1037,25 @@ impl EnvMBE> { repeats.push(Rc::new(items)); } - Ok(EnvMBE { leaves: leaves, repeats: repeats, ddd_rep_idxes: self.ddd_rep_idxes.clone(), leaf_locations: self.leaf_locations.clone(), - named_repeats: self.named_repeats.clone() + named_repeats: self.named_repeats.clone(), }) } - } - impl EnvMBE { // TODO: this should just take `Name`, not `&Name` pub fn get_leaf_or_panic(&self, n: &Name) -> &T { match self.leaves.find(n) { - Some(v) => { v } - None => { panic!(" {:#?} not found in {:#?} (perhaps it is still repeated?)", n, self) } + Some(v) => v, + None => panic!( + " {:#?} not found in {:#?} (perhaps it is still repeated?)", + n, self + ), } } @@ -890,18 +1063,25 @@ impl EnvMBE { self.get_rep_leaf(n).unwrap() } - pub fn map_flatten_rep_leaf_or_panic(&self, n: Name, depth: u8, m: &dyn Fn(&T) -> S, - f: &dyn Fn(Vec) -> S) -> S { + pub fn map_flatten_rep_leaf_or_panic( + &self, + n: Name, + depth: u8, + m: &dyn Fn(&T) -> S, + f: &dyn Fn(Vec) -> S, + ) -> S { if depth == 0 { return m(self.get_leaf_or_panic(&n)); } let leaf_loc = match self.leaf_locations.find(&n) { Some(&Some(ll)) => ll, - _ => { panic!("Leaf {} not found", n) } + _ => panic!("Leaf {} not found", n), }; - f(self.repeats[leaf_loc].iter().map( - |mbe| mbe.map_flatten_rep_leaf_or_panic(n, depth-1, m, f)).collect()) + f(self.repeats[leaf_loc] + .iter() + .map(|mbe| mbe.map_flatten_rep_leaf_or_panic(n, depth - 1, m, f)) + .collect()) } } @@ -917,26 +1097,33 @@ fn basic_mbe() { let teens_mbe = vec![ EnvMBE::new_from_leaves(assoc_n!("t" => 11)), EnvMBE::new_from_leaves(assoc_n!("t" => 12)), - EnvMBE::new_from_leaves(assoc_n!("t" => 13)) + EnvMBE::new_from_leaves(assoc_n!("t" => 13)), ]; mbe.add_named_repeat(n("low_two_digits"), teens_mbe.clone(), None); let big_mbe = vec![ EnvMBE::new_from_leaves(assoc_n!("y" => 9001)), - EnvMBE::new_from_leaves(assoc_n!("y" => 9002)) + EnvMBE::new_from_leaves(assoc_n!("y" => 9002)), ]; mbe.add_anon_repeat(big_mbe, None); - - for (sub_mbe, teen) in mbe.march_all(&vec![n("t"), n("eight")]).iter().zip(vec![11,12,13]) { + for (sub_mbe, teen) in mbe + .march_all(&vec![n("t"), n("eight")]) + .iter() + .zip(vec![11, 12, 13]) + { assert_eq!(sub_mbe.get_leaf(n("eight")), Some(&8)); assert_eq!(sub_mbe.get_leaf(n("nine")), Some(&9)); assert_eq!(sub_mbe.get_leaf(n("t")), Some(&teen)); assert_eq!(sub_mbe.get_leaf(n("y")), None); - for (sub_sub_mbe, big) in sub_mbe.march_all(&vec![n("y"), n("eight")]).iter().zip(vec![9001, 9002]) { + for (sub_sub_mbe, big) in sub_mbe + .march_all(&vec![n("y"), n("eight")]) + .iter() + .zip(vec![9001, 9002]) + { assert_eq!(sub_sub_mbe.get_leaf(n("eight")), Some(&8)); assert_eq!(sub_sub_mbe.get_leaf(n("nine")), Some(&9)); assert_eq!(sub_sub_mbe.get_leaf(n("t")), Some(&teen)); @@ -947,18 +1134,26 @@ fn basic_mbe() { let neg_teens_mbe = vec![ EnvMBE::new_from_leaves(assoc_n!("nt" => -11)), EnvMBE::new_from_leaves(assoc_n!("nt" => -12)), - EnvMBE::new_from_leaves(assoc_n!("nt" => -13)) + EnvMBE::new_from_leaves(assoc_n!("nt" => -13)), ]; mbe.add_named_repeat(n("low_two_digits"), neg_teens_mbe, None); - for (sub_mbe, teen) in mbe.march_all(&vec![n("t"), n("nt"), n("eight")]).iter().zip(vec![11,12,13]) { + for (sub_mbe, teen) in mbe + .march_all(&vec![n("t"), n("nt"), n("eight")]) + .iter() + .zip(vec![11, 12, 13]) + { assert_eq!(sub_mbe.get_leaf(n("eight")), Some(&8)); assert_eq!(sub_mbe.get_leaf(n("nine")), Some(&9)); assert_eq!(sub_mbe.get_leaf(n("t")), Some(&teen)); assert_eq!(sub_mbe.get_leaf(n("nt")), Some(&-teen)); - for (sub_sub_mbe, big) in sub_mbe.march_all(&vec![n("y"), n("eight")]).iter().zip(vec![9001, 9002]) { + for (sub_sub_mbe, big) in sub_mbe + .march_all(&vec![n("y"), n("eight")]) + .iter() + .zip(vec![9001, 9002]) + { assert_eq!(sub_sub_mbe.get_leaf(n("eight")), Some(&8)); assert_eq!(sub_sub_mbe.get_leaf(n("nine")), Some(&9)); assert_eq!(sub_sub_mbe.get_leaf(n("t")), Some(&teen)); @@ -974,8 +1169,11 @@ fn basic_mbe() { assert_eq!(sub_mbe.get_leaf(n("t")), Some(&0)); assert_eq!(sub_mbe.get_leaf(n("nt")), Some(&0)); - for (sub_sub_mbe, _) in sub_mbe.march_all(&vec![n("y"), n("eight")]).iter() - .zip(vec![9001, 9002]) { + for (sub_sub_mbe, _) in sub_mbe + .march_all(&vec![n("y"), n("eight")]) + .iter() + .zip(vec![9001, 9002]) + { assert_eq!(sub_sub_mbe.get_leaf(n("eight")), Some(&0)); assert_eq!(sub_sub_mbe.get_leaf(n("nine")), Some(&0)); assert_eq!(sub_sub_mbe.get_leaf(n("t")), Some(&0)); @@ -990,22 +1188,33 @@ fn basic_mbe() { assert!(mbe != EnvMBE::new()); assert!(EnvMBE::new() != mbe); - assert_eq!(mbe, mbe.map_with(&all_zeroes, &|a,b| a+b)); - assert_eq!(mbe, all_zeroes.map_with(&mbe, &|a,b| a+b)); + assert_eq!(mbe, mbe.map_with(&all_zeroes, &|a, b| a + b)); + assert_eq!(mbe, all_zeroes.map_with(&mbe, &|a, b| a + b)); assert_eq!( - mbe.map_reduce_with(&all_zeroes, &|a,b| if *a<*b { *a } else { *b }, &|a, b| (*a+*b), 0), - -11 + -12 + -13); + mbe.map_reduce_with( + &all_zeroes, + &|a, b| if *a < *b { *a } else { *b }, + &|a, b| (*a + *b), + 0 + ), + -11 + -12 + -13 + ); assert_eq!( Err(()), - mbe.clone().map(&mut |x: &i32| if *x == 12 { Err(()) } else { Ok(*x)} ).lift_result()); + mbe.clone() + .map(&mut |x: &i32| if *x == 12 { Err(()) } else { Ok(*x) }) + .lift_result() + ); assert_eq!( Ok(mbe.clone()), - mbe.clone().map(&mut |x: &i32| if *x == 121212 { Err(()) } else { Ok(*x)} ).lift_result()); - + mbe.clone() + .map(&mut |x: &i32| if *x == 121212 { Err(()) } else { Ok(*x) }) + .lift_result() + ); - let mapped_mbe = mbe.map(&mut |x : &i32| (*x, *x - 9000)); + let mapped_mbe = mbe.map(&mut |x: &i32| (*x, *x - 9000)); let first_sub_mbe = &mapped_mbe.march_all(&vec![n("y")])[0]; @@ -1020,19 +1229,26 @@ fn basic_mbe() { let mut nothing_with_other = EnvMBE::new_from_anon_repeat(vec![]); nothing_with_other.add_leaf(n("outer"), 1); - let teens_and_nothing - = EnvMBE::new_from_anon_repeat(vec![teens_with_outer, nothing_with_other]); + let teens_and_nothing = + EnvMBE::new_from_anon_repeat(vec![teens_with_outer, nothing_with_other]); let mut output = vec![]; for outer in teens_and_nothing.march_all(&vec![n("outer")]) { for inner in outer.march_all(&vec![n("t")]) { - output.push( (inner.get_leaf(n("outer")).map(|x| x.clone()), - inner.get_leaf(n("t")).map(|x| x.clone()) )); + output.push(( + inner.get_leaf(n("outer")).map(|x| x.clone()), + inner.get_leaf(n("t")).map(|x| x.clone()), + )); } } - assert_eq!(output, vec![(Some(0), Some(11)), (Some(0), Some(12)), (Some(0), Some(13))]); - - + assert_eq!( + output, + vec![ + (Some(0), Some(11)), + (Some(0), Some(12)), + (Some(0), Some(13)) + ] + ); } #[test] @@ -1045,14 +1261,23 @@ fn splice_healing() { assert_eq!(noop, orig); let mut b_to_xxx = orig.clone(); - b_to_xxx.heal_splices( - &|a| if a == &ast!((vr "b")) { Some(vec![ast!((vr "x")), ast!((vr "xx"))]) } else { None }); - assert_eq!(b_to_xxx, mbe!( - "rator" => (vr "add"), "rand" => [(vr "a"), (vr "x"), (vr "xx"), (vr "c"), (vr "d")] - )); - - let steal_from_other = |a: &::ast::Ast, other__a_vec__thunk: &dyn Fn() -> Vec<::ast::Ast>| - -> Option> { + b_to_xxx.heal_splices(&|a| { + if a == &ast!((vr "b")) { + Some(vec![ast!((vr "x")), ast!((vr "xx"))]) + } else { + None + } + }); + assert_eq!( + b_to_xxx, + mbe!( + "rator" => (vr "add"), "rand" => [(vr "a"), (vr "x"), (vr "xx"), (vr "c"), (vr "d")] + ) + ); + + let steal_from_other = |a: &::ast::Ast, + other__a_vec__thunk: &dyn Fn() -> Vec<::ast::Ast>| + -> Option> { if a == &ast!((vr "c")) { Some(other__a_vec__thunk()) } else { @@ -1064,38 +1289,66 @@ fn splice_healing() { "rator" => (vr "---"), "rand" => [(vr "1"), (vr "2"), (vr "3")]); let mut with_short = orig.clone(); - assert_eq!(with_short.heal_splices__with(&other_short, &steal_from_other), Ok(())); - assert_eq!(with_short, - mbe!("rator" => (vr "add"), "rand" => [(vr "a"), (vr "b"), (vr "d")])); + assert_eq!( + with_short.heal_splices__with(&other_short, &steal_from_other), + Ok(()) + ); + assert_eq!( + with_short, + mbe!("rator" => (vr "add"), "rand" => [(vr "a"), (vr "b"), (vr "d")]) + ); let other_long = mbe!( "rator" => (vr "---"), "rand" => [(vr "1"), (vr "2"), (vr "3"), (vr "4"), (vr "5"), (vr "6")]); let mut with_long = orig.clone(); - assert_eq!(with_long.heal_splices__with(&other_long, &steal_from_other), Ok(())); - assert_eq!(with_long, mbe!("rator" => (vr "add"), - "rand" => [(vr "a"), (vr "b"), (vr "3"), (vr "4"), (vr "5"), (vr "d")])); + assert_eq!( + with_long.heal_splices__with(&other_long, &steal_from_other), + Ok(()) + ); + assert_eq!( + with_long, + mbe!("rator" => (vr "add"), + "rand" => [(vr "a"), (vr "b"), (vr "3"), (vr "4"), (vr "5"), (vr "d")]) + ); let other__too_short = mbe!( "rator" => (vr "---"), "rand" => [(vr "1"), (vr "2")]); let mut with__too_short = orig.clone(); - assert_eq!(with__too_short.heal_splices__with(&other__too_short, &steal_from_other), Err(())); - + assert_eq!( + with__too_short.heal_splices__with(&other__too_short, &steal_from_other), + Err(()) + ); // TODO: test this more! } #[test] fn ddd_iter() { - assert_eq!(DddIter::new([0,1,2].iter(), 0, 0).collect::>(), [&1,&2]); - assert_eq!(DddIter::new([0,1,2].iter(), 1, 0).collect::>(), [&0,&2]); - assert_eq!(DddIter::new([0,1,2].iter(), 2, 0).collect::>(), [&0,&1]); + assert_eq!( + DddIter::new([0, 1, 2].iter(), 0, 0).collect::>(), + [&1, &2] + ); + assert_eq!( + DddIter::new([0, 1, 2].iter(), 1, 0).collect::>(), + [&0, &2] + ); + assert_eq!( + DddIter::new([0, 1, 2].iter(), 2, 0).collect::>(), + [&0, &1] + ); - assert_eq!(DddIter::new([0,1,2].iter(), 1, 1).collect::>(), [&0,&1,&2]); - assert_eq!(DddIter::new([0,1,2].iter(), 1, 3).collect::>(), [&0,&1,&1,&1,&2]); + assert_eq!( + DddIter::new([0, 1, 2].iter(), 1, 1).collect::>(), + [&0, &1, &2] + ); + assert_eq!( + DddIter::new([0, 1, 2].iter(), 1, 3).collect::>(), + [&0, &1, &1, &1, &2] + ); } #[test] @@ -1107,38 +1360,53 @@ fn mbe_ddd_map_with() { fn concat(l: &Ast, r: &Ast) -> Ast { match (l, r) { - (&Atom(ln), &Atom(rn)) => Atom(n( format!("{}{}", ln, rn).as_str() )), - _ => panic!() + (&Atom(ln), &Atom(rn)) => Atom(n(format!("{}{}", ln, rn).as_str())), + _ => panic!(), } } - assert_eq!(lhs.map_with(&rhs, &concat), - mbe!( "a" => ["00", "11", "21", "31", "44"] )); - assert_eq!(rhs.map_with(&lhs, &concat), - mbe!( "a" => ["00", "11", "12", "13", "44"] )); + assert_eq!( + lhs.map_with(&rhs, &concat), + mbe!( "a" => ["00", "11", "21", "31", "44"] ) + ); + assert_eq!( + rhs.map_with(&lhs, &concat), + mbe!( "a" => ["00", "11", "12", "13", "44"] ) + ); - assert_eq!(lhs.map_reduce_with(&rhs, &concat, &concat, ast!("")), - ast!("4431211100")); // N.B. order is arbitrary + assert_eq!( + lhs.map_reduce_with(&rhs, &concat, &concat, ast!("")), + ast!("4431211100") + ); // N.B. order is arbitrary { let lhs = mbe!( "a" => [["a", "b"], ["c", "d"], ["c", "d"]]); let rhs = mbe!( "a" => [["a", "b"] ...(["c", "d"])...]); - assert_eq!(lhs.map_with(&rhs, &concat), - mbe!( "a" => [["aa", "bb"], ["cc", "dd"], ["cc", "dd"]])); + assert_eq!( + lhs.map_with(&rhs, &concat), + mbe!( "a" => [["aa", "bb"], ["cc", "dd"], ["cc", "dd"]]) + ); } { let lhs = mbe!("a" => [...(["-0", "-1" ...("-")..., "-9"])...]); let rhs = mbe!("a" => [["p0", "p1", "p4", "p5", "p6", "p9"], ["p0", "p1", "p9"]]); - assert_eq!(lhs.map_with(&rhs, &concat), - mbe!("a" => [["-0p0", "-1p1", "-p4", "-p5", "-p6", "-9p9"], ["-0p0", "-1p1", "-9p9"]])); + assert_eq!( + lhs.map_with(&rhs, &concat), + mbe!("a" => [["-0p0", "-1p1", "-p4", "-p5", "-p6", "-9p9"], ["-0p0", "-1p1", "-9p9"]]) + ); // Drop all but the first of each repetition: - assert_eq!(lhs.map_collapse_reduce_with( - &rhs, &concat, &|v| if v.len() > 0 {v[0].clone()} else { ast!("") }, - &|l,r| concat(&l, &r), ast!("")), - ast!("-0p0-1p1-p4-9p9")); + assert_eq!( + lhs.map_collapse_reduce_with( + &rhs, + &concat, + &|v| if v.len() > 0 { v[0].clone() } else { ast!("") }, + &|l, r| concat(&l, &r), + ast!("") + ), + ast!("-0p0-1p1-p4-9p9") + ); } - } diff --git a/src/util/mod.rs b/src/util/mod.rs index b6f4f4d..4006705 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,3 @@ pub mod assoc; +pub mod err; pub mod mbe; -pub mod err; \ No newline at end of file diff --git a/src/walk_mode.rs b/src/walk_mode.rs index 7cb0d8e..e766be0 100644 --- a/src/walk_mode.rs +++ b/src/walk_mode.rs @@ -1,13 +1,13 @@ -use name::*; -use form::Form; -use util::assoc::Assoc; +use alpha::{freshen, freshen_with}; use ast::Ast; use ast::Ast::*; -use util::mbe::EnvMBE; -use std::fmt::{Debug, Display}; -use ast_walk::{Clo, WalkRule, LazyWalkReses, OutEnvHandle, walk}; +use ast_walk::{walk, Clo, LazyWalkReses, OutEnvHandle, WalkRule}; +use form::Form; +use name::*; use runtime::reify::Reifiable; -use alpha::{freshen, freshen_with}; +use std::fmt::{Debug, Display}; +use util::assoc::Assoc; +use util::mbe::EnvMBE; /** * This trait makes a type producable by positive and negative walks. @@ -17,7 +17,9 @@ pub trait WalkElt: Clone + Debug + Display + Reifiable { fn to_ast(&self) -> Ast; // Is this a hack? - fn core_env() -> Assoc { Assoc::::new() } + fn core_env() -> Assoc { + Assoc::::new() + } } // Abbreviation for Result<…::Out, …> @@ -38,55 +40,67 @@ type Res = Result<<::D as Dir>::Out, : * -- the special value they traverse is stored in the environment with a special name -- * but they conceptually are mostly relying on the special value. */ -pub trait WalkMode : Debug + Copy + Reifiable { +pub trait WalkMode: Debug + Copy + Reifiable { /// The object type for the environment to walk in. - type Elt : Clone + Debug + Reifiable + WalkElt; + type Elt: Clone + Debug + Reifiable + WalkElt; - type Err : Debug /*Display*/ + Reifiable + Clone; + type Err: Debug + Reifiable + Clone; - type D : Dir; + type D: Dir; /// The negated version of this walk - type Negated : WalkMode; + type Negated: WalkMode< + Elt = Self::Elt, + Err = Self::Err, + ExtraInfo = Self::ExtraInfo, + Negated = Self, + >; /// Any extra information the walk needs - type ExtraInfo : ::std::default::Default + Reifiable + Clone + Debug; + type ExtraInfo: ::std::default::Default + Reifiable + Clone + Debug; - fn get_walk_rule(&Form) -> WalkRule where Self: Sized ; + fn get_walk_rule(&Form) -> WalkRule + where + Self: Sized; /** - Should the walker extend the environment based on imports? - - While `Beta`s are great for typechecking, - evaluation sometimes extends environments - in ways that they can't handle. - (In particular, λ causes the `Ast` containing the `ExtendEnv` - to be moved to a context where its `Beta`s are meaningless! - If `!automatically_extend_env()`, the `Custom` implementation - must extend the environment properly to be safe. - */ + Should the walker extend the environment based on imports? + + While `Beta`s are great for typechecking, + evaluation sometimes extends environments + in ways that they can't handle. + (In particular, λ causes the `Ast` containing the `ExtendEnv` + to be moved to a context where its `Beta`s are meaningless! + If `!automatically_extend_env()`, the `Custom` implementation + must extend the environment properly to be safe. + */ fn automatically_extend_env() -> bool; /** - Walk over the structure of a node, not its meaning. - This could be because we're inside a syntax-quote, - or it could be that we are a form (like written-out types or a literal) - that acts like a literal. - Children are not necessarily walked quasiliterally - -- maybe they're an interpolation of some kind -- - instead, the mode (=quotation depth) and form together decide what to do. - If the walk is negative, the result might be MatchFailure - */ - fn walk_quasi_literally(expected: Ast, cnc: &LazyWalkReses) - -> Res { + Walk over the structure of a node, not its meaning. + This could be because we're inside a syntax-quote, + or it could be that we are a form (like written-out types or a literal) + that acts like a literal. + Children are not necessarily walked quasiliterally + -- maybe they're an interpolation of some kind -- + instead, the mode (=quotation depth) and form together decide what to do. + If the walk is negative, the result might be MatchFailure + */ + fn walk_quasi_literally(expected: Ast, cnc: &LazyWalkReses) -> Res { Self::D::walk_quasi_literally(expected, cnc) } // TODO: these seem like a hack... // We need to dynamically do these if it's possible, for `env_from_beta` - fn out_as_elt(o: ::Out) -> Self::Elt { Self::D::out_as_elt(o) } - fn out_as_env(o: ::Out) -> Assoc { Self::D::out_as_env(o) } - fn env_as_out(e: Assoc) -> ::Out { Self::D::env_as_out(e) } + fn out_as_elt(o: ::Out) -> Self::Elt { + Self::D::out_as_elt(o) + } + fn out_as_env(o: ::Out) -> Assoc { + Self::D::out_as_env(o) + } + fn env_as_out(e: Assoc) -> ::Out { + Self::D::env_as_out(e) + } fn walk_var(n: Name, cnc: &LazyWalkReses) -> Res { Self::D::walk_var(n, cnc) @@ -98,30 +112,35 @@ pub trait WalkMode : Debug + Copy + Reifiable { /// When a DDDed subterm is matched, it matches against multiple `Elt`s. /// How should we represent that? - fn collapse_repetition(_: Vec>) -> Res { panic!("ICE: unexpected repetition") } + fn collapse_repetition(_: Vec>) -> Res { + panic!("ICE: unexpected repetition") + } /** - Make up a special `Elt` that is currently "underspecified", - but which can be "unified" with some other `Elt`. - If that happens, all copies of this `Elt` will act like that other one. - - Side-effects under the covers make this work. - */ - fn underspecified(Name) -> Self::Elt { panic!("ICE: no underspecified_elt") } + Make up a special `Elt` that is currently "underspecified", + but which can be "unified" with some other `Elt`. + If that happens, all copies of this `Elt` will act like that other one. + + Side-effects under the covers make this work. + */ + fn underspecified(Name) -> Self::Elt { + panic!("ICE: no underspecified_elt") + } fn name() -> &'static str; - } -pub trait Dir : Debug + Copy + Clone - where Self: ::std::marker::Sized /* I don't know why Rust wants this!*/ { - type Mode : WalkMode; +pub trait Dir: Debug + Copy + Clone +where + Self: ::std::marker::Sized, +{ + type Mode: WalkMode; /** The output of the walking process. * * Negated walks produce an environment of Self::Elt, positive walks produce Self::Elt. */ - type Out : Clone + Debug + Reifiable; + type Out: Clone + Debug + Reifiable; /// Get ready to destructure a node. /// Includes freshening (including the context_elt, if necessary) @@ -147,14 +166,16 @@ pub trait Dir : Debug + Copy + Clone } #[derive(Copy, Clone, Debug)] -pub struct Positive { md: Mode } - +pub struct Positive { + md: Mode, +} #[derive(Copy, Clone, Debug)] -pub struct Negative { md: Mode } - +pub struct Negative { + md: Mode, +} -impl> Dir for Positive { +impl> Dir for Positive { type Out = ::Elt; type Mode = Mode; @@ -165,20 +186,32 @@ impl> Dir for Positive { fn walk_quasi_literally(a: Ast, cnc: &LazyWalkReses) -> Res { match a { Node(f, parts, exports) => { - let mut walked : EnvMBE = - parts.map_marched_against( - &mut |p: &Ast, cnc_m: &LazyWalkReses| match *p { - // Yes, `walk`, not `w_q_l`; the mode is in charge of figuring things out. - Node(_,_,_) | VariableReference(_) | ExtendEnv(_,_) => walk(p, cnc_m), - _ => Ok(::Elt::from_ast(&p.clone())) - }.map(|e| ::Elt::to_ast(&e)), - cnc).lift_result()?; + let mut walked: EnvMBE = parts + .map_marched_against( + &mut |p: &Ast, cnc_m: &LazyWalkReses| { + match *p { + // Yes, `walk`, not `w_q_l`; the mode is in charge of figuring things out. + Node(_, _, _) | VariableReference(_) | ExtendEnv(_, _) => { + walk(p, cnc_m) + } + _ => Ok(::Elt::from_ast(&p.clone())), + } + .map(|e| ::Elt::to_ast(&e)) + }, + cnc, + ) + .lift_result()?; // HACK: recognize `Shape` as the output of `core_qq_forms::dotdotdot`: - walked.heal_splices(&|a| match a { Shape(ref v) => Some(v.clone()), _ => None}); - - Ok(::Elt::from_ast(&Node(f, walked, exports))) - }, + walked.heal_splices(&|a| match a { + Shape(ref v) => Some(v.clone()), + _ => None, + }); + + Ok(::Elt::from_ast(&Node( + f, walked, exports, + ))) + } orig => { // All this mess is to push `Shape` down past a wrapper (i.e. `ExtendEnv`), // duplicating the wrapper around each element of `Shape`. @@ -186,7 +219,7 @@ impl> Dir for Positive { let body = match orig { ExtendEnv(ref b, _) | QuoteMore(ref b, _) | QuoteLess(ref b, _) => b, - _ => panic!("ICE") + _ => panic!("ICE"), }; let sub_result = Mode::Elt::to_ast(&walk(&**body, cnc)?); @@ -197,18 +230,18 @@ impl> Dir for Positive { ExtendEnv(_, beta) => ExtendEnv(boxed, beta.clone()), QuoteMore(_, pos) => QuoteMore(boxed, *pos), QuoteLess(_, depth) => QuoteLess(boxed, *depth), - _ => panic!("ICE") + _ => panic!("ICE"), } } let res: Ast = match sub_result { - Shape(sub_results) => { - Shape(sub_results.into_iter().map( - |sub| handle_wrapper::(&orig, sub)).collect()) - } - sub_result => { - handle_wrapper::(&orig, sub_result) - } + Shape(sub_results) => Shape( + sub_results + .into_iter() + .map(|sub| handle_wrapper::(&orig, sub)) + .collect(), + ), + sub_result => handle_wrapper::(&orig, sub_result), }; Ok(Mode::Elt::from_ast(&res)) @@ -216,17 +249,17 @@ impl> Dir for Positive { } } - fn walk_var(n: Name, cnc: &LazyWalkReses) - -> Res { + fn walk_var(n: Name, cnc: &LazyWalkReses) -> Res { Ok(cnc.env.find_or_panic(&n).clone()) } - fn walk_atom(_: Name, _: &LazyWalkReses) - -> Res { + fn walk_atom(_: Name, _: &LazyWalkReses) -> Res { panic!("ICE: Atoms are positively unwalkable"); } - fn out_as_elt(o: Self::Out) -> ::Elt { o } + fn out_as_elt(o: Self::Out) -> ::Elt { + o + } fn out_as_env(_: Self::Out) -> Assoc::Elt> { panic!("ICE: out_as_env") } @@ -234,17 +267,21 @@ impl> Dir for Positive { panic!("ICE: env_as_out") } - fn oeh_if_negative() -> Option> { None } - fn is_positive() -> bool { true } + fn oeh_if_negative() -> Option> { + None + } + fn is_positive() -> bool { + true + } } -impl + NegativeWalkMode> Dir for Negative { +impl + NegativeWalkMode> Dir for Negative { type Out = Assoc::Elt>; type Mode = Mode; fn pre_walk(node: Ast, cnc: LazyWalkReses) -> (Ast, LazyWalkReses) { if !::needs_pre_match() { - return (freshen(&node), cnc) + return (freshen(&node), cnc); } let node_ast = ::Elt::from_ast(&node); // `pre_match` brings things together, which we want to do before attempting to co-freshen. @@ -253,43 +290,51 @@ impl + NegativeWalkMode> Dir for Negative { // Closures; we need to unify their environments: let (l, r, new_env) = l_clo.env_merge(&r_clo); - let (l_fresh, r_fresh) = freshen_with(&::Elt::to_ast(&l), - &::Elt::to_ast(&r)); - (l_fresh, - cnc.with_environment(new_env) - .with_context(::Elt::from_ast(&r_fresh))) + let (l_fresh, r_fresh) = freshen_with( + &::Elt::to_ast(&l), + &::Elt::to_ast(&r), + ); + ( + l_fresh, + cnc.with_environment(new_env) + .with_context(::Elt::from_ast(&r_fresh)), + ) } // HACK: force walking to automatically succeed, avoiding return type muckery - None => (Atom(negative_ret_val()), - cnc.with_context(::Elt::from_ast(&Trivial))) + None => ( + Atom(negative_ret_val()), + cnc.with_context(::Elt::from_ast(&Trivial)), + ), } } fn walk_quasi_literally(expected: Ast, cnc: &LazyWalkReses) -> Res { - let got = ::to_ast(&cnc.context_elt().clone()); let parts_actual = try!(Mode::context_match(&expected, &got, cnc.env.clone())); let its_a_trivial_ast = EnvMBE::new(); // No more walking to do - let expd_parts = match expected { Node(_, ref p, _) => p, _ => &its_a_trivial_ast }; + let expd_parts = match expected { + Node(_, ref p, _) => p, + _ => &its_a_trivial_ast, + }; // Continue the walk on subterms. (`context_match` does the freshening) // TODO: I fear that we need `map_collapse_reduce_with_marched_against` // so that matching DDDed syntax won't go horribly wrong - expd_parts.map_collapse_reduce_with(&parts_actual, - &|model: &Ast, actual: &Ast| { - match *model { - Node(_,_,_) | VariableReference(_) | ExtendEnv(_,_) => { - walk(model, - &cnc.with_context(::Elt::from_ast(actual))) - } - _ => { Ok(Assoc::new()) } - } + expd_parts.map_collapse_reduce_with( + &parts_actual, + &|model: &Ast, actual: &Ast| match *model { + Node(_, _, _) | VariableReference(_) | ExtendEnv(_, _) => walk( + model, + &cnc.with_context(::Elt::from_ast(actual)), + ), + _ => Ok(Assoc::new()), }, &Mode::collapse_repetition, - &|result, next| { Ok(try!(result.clone()).set_assoc(&try!(next.clone()))) }, - Ok(Assoc::new())) + &|result, next| Ok(try!(result.clone()).set_assoc(&try!(next.clone()))), + Ok(Assoc::new()), + ) } fn walk_var(n: Name, _: &LazyWalkReses) -> Res { @@ -301,18 +346,29 @@ impl + NegativeWalkMode> Dir for Negative { Ok(Assoc::new().set(n, cnc.context_elt().clone())) } - fn out_as_elt(_: Self::Out) -> ::Elt { panic!("ICE: out_as_elt") } - fn out_as_env(o: Self::Out) -> Assoc::Elt> { o } - fn env_as_out(e: Assoc::Elt>) -> Self::Out { e } + fn out_as_elt(_: Self::Out) -> ::Elt { + panic!("ICE: out_as_elt") + } + fn out_as_env(o: Self::Out) -> Assoc::Elt> { + o + } + fn env_as_out(e: Assoc::Elt>) -> Self::Out { + e + } fn oeh_if_negative() -> Option> { - Some(::std::rc::Rc::new(::std::cell::RefCell::new( - Assoc::::Elt>::new()))) + Some(::std::rc::Rc::new(::std::cell::RefCell::new(Assoc::< + Name, + ::Elt, + >::new( + )))) + } + fn is_positive() -> bool { + false } - fn is_positive() -> bool { false } } -pub trait NegativeWalkMode : WalkMode { +pub trait NegativeWalkMode: WalkMode { /// What happens if destructuring fails? fn qlit_mismatch_error(l: Self::Elt, r: Self::Elt) -> Self::Err { panic!("match failure unimplemented: {} vs. {}", l, r) @@ -327,19 +383,35 @@ pub trait NegativeWalkMode : WalkMode { /// Before matching, possibly adjust the two `Elt`s to match better. (`None` is auto-match.) /// By default, a no-op. - fn pre_match(expected: Self::Elt, got: Self::Elt, env: &Assoc) - -> Option<(Clo, Clo)> { - Some((Clo{it: expected, env: env.clone()}, Clo{it: got, env: env.clone()})) + fn pre_match( + expected: Self::Elt, + got: Self::Elt, + env: &Assoc, + ) -> Option<(Clo, Clo)> { + Some(( + Clo { + it: expected, + env: env.clone(), + }, + Clo { + it: got, + env: env.clone(), + }, + )) } /// Match the context element against the current node. /// Note that this should come after `pre_match`, /// so any remaining variables will be not be resolved. /// TODO: I should think about whether to use `Ast` or `Elt` during matches in `ast_walk` - fn context_match(expected: &Ast, got: &Ast, _env: Assoc) - -> Result, ::Err> { + fn context_match( + expected: &Ast, + got: &Ast, + _env: Assoc, + ) -> Result, ::Err> { // break apart the node, and walk it element-wise - match (expected, got) { // `pre_walk` has already freshened for us + match (expected, got) { + // `pre_walk` has already freshened for us (&Node(ref f, _, _), &Node(ref f_actual, ref parts_actual, _)) if *f == *f_actual => { Ok(parts_actual.clone()) } @@ -352,23 +424,27 @@ pub trait NegativeWalkMode : WalkMode { // contained useful `diff`-like information for debugging, // when a match was expected to succeed? // (I really like using pattern-matching in unit tests!) - Err(Self::qlit_mismatch_error(Self::Elt::from_ast(got), - Self::Elt::from_ast(expected))) + Err(Self::qlit_mismatch_error( + Self::Elt::from_ast(got), + Self::Elt::from_ast(expected), + )) } } } } - /** `var_to_out`, for positive walks where `Out` == `Elt` */ -pub fn var_lookup(n: Name, env: &Assoc) - -> Result { - Ok((*env.find(&n).unwrap_or_else( - || panic!(format!("Name {:#?} unbound in {:#?}", n, env)))).clone()) +pub fn var_lookup(n: Name, env: &Assoc) -> Result { + Ok((*env + .find(&n) + .unwrap_or_else(|| panic!(format!("Name {:#?} unbound in {:#?}", n, env)))) + .clone()) } /** `var_to_out`, for negative walks where `Out` == `Assoc`` */ -pub fn var_bind(n: Name, env: &Assoc) - -> Result, ()> { +pub fn var_bind( + n: Name, + env: &Assoc, +) -> Result, ()> { Ok(Assoc::new().set(n, env.find(&negative_ret_val()).unwrap().clone())) }