├── .gitignore ├── README.md ├── rabbot-plugin ├── Cargo.toml └── src │ └── lib.rs ├── rabbot-test ├── Cargo.toml └── src │ ├── lib.rs │ └── lib.rs.old └── rabbot ├── Cargo.toml ├── build.rs └── src ├── abt.rs ├── ast.rs ├── codegen.rs ├── grammar.lalrpop ├── lib.rs ├── token.rs └── var.rs /.gitignore: -------------------------------------------------------------------------------- 1 | rabbot/target 2 | rabbot/Cargo.lock 3 | rabbot/src/grammar.rs 4 | 5 | rabbot-plugin/target 6 | rabbot-plugin/Cargo.lock 7 | 8 | rabbot-test/target 9 | rabbot-test/Cargo.lock 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rabbot 2 | 3 | Rabbot generates interfaces to abstract binding trees (ABTs). It is a port of SML's [Abbot](https://github.com/robsimmons/abbot) to Rust. 4 | 5 | ## What is an ABT? 6 | 7 | An abstract binding tree is a data structure similar to an abstract syntax tree, except that it lets you pleasantly and correctly handle the declaration and binding of symbols. To motivate this, let's consider a syntax tree for a simple pseudo-language. For the expression `2 + 1`, this would produce a syntax tree `Plus(Number(2), Number(1))`. For the expression `let x = 1 in x + 2`, this would produce `Let(Var("x"), Number(1), Plus(Var("x"), Number(2))`. For these examples, using a normal abstract syntax tree works fine. 8 | 9 | However, we run into trouble if we introduce multiple bindings with the same name. For example, in the expression `let x = 1 in (let x = 2 in x) + x`, we now need some notion of scope analysis. Which `x` does each instance refer to? ABTs eliminate this problem by providing constructs to handle the binding and substitution of variables. The prior expression translates roughly to `Let(1, x . (Let(2, x . x) + x))` where the dot `.` denotes a binding site. See Bob Harper's [Practical Foundations for Programming Languages](https://www.cs.cmu.edu/~rwh/pfpl/2nded.pdf), chapter 1 for more information. 10 | 11 | ## What does Rabbot do? 12 | 13 | The usual way to create an abstract syntax is tree is to define an enum with branches for each part of your syntax, e.g. 14 | 15 | ```rust 16 | enum Expression { 17 | Number(i32), 18 | Plus(Box, Box), 19 | ... 20 | } 21 | ``` 22 | 23 | However, ABTs work differently. At its core, an ABT is a fixed tree data structure that is parameterized by a set of operators, where each operator describes functionality like `Number` or `Plus`. See `rabbot/src/abt.rs` for the definition of the data type. This lets you easily re-use the ABT structure for different kinds of programs, but it requires more boilerplate and more code in practice to use. 24 | 25 | To alleviate the verbosity of using ABTs, Rabbot takes a description of an abstract binding tree and generates a more concise interface for the programmer to use. For example, one can implement lambda calculus with Peano numerals and its interpreter in a few lines: 26 | 27 | ```rust 28 | #![feature(plugin, box_syntax)] 29 | #![plugin(rabbot_plugin)] 30 | 31 | #[macro_use] extern crate rabbot; 32 | 33 | rabbot! { 34 | sort Term { 35 | Z, 36 | S(Term), 37 | Lam(Binding . Term), 38 | App((Term, Term)) 39 | } 40 | } 41 | 42 | use rabbot::var::Var; 43 | use term::*; 44 | fn interpret(t: Term) -> Term { 45 | match out(t) { 46 | v @ View::Z | v @ View::Lam(_) => into(v), 47 | View::S(t) => { 48 | bind!(View::S{v} = out(interpret(t))); 49 | into(View::S(v)) 50 | }, 51 | View::App((fun, arg)) => { 52 | bind!(View::Lam{(var, body)} = out(interpret(fun))); 53 | subst(arg, var, body) 54 | }, 55 | View::Var(_) => unreachable!() 56 | } 57 | } 58 | 59 | #[test] 60 | fn test() { 61 | let x = Var::new("x".to_string()); 62 | let term = into(View::App(( 63 | into(View::Lam(( 64 | x.clone(), into(View::S(into(View::Var(x.clone()))))))), 65 | into(View::Z)))); 66 | 67 | println!("{:?}", interpret(term)); // prints S(Z) 68 | } 69 | ``` 70 | 71 | ## Caveats 72 | 73 | This is still in heavy development, so talk to me (email: [wcrichto@stanford.edu](mailto:wcrichto@stanford.edu)) if you want to use it. The code generator works as a compiler plugin, so it only works on nightly, although that's not a hard requirement. -------------------------------------------------------------------------------- /rabbot-plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rabbot-plugin" 3 | version = "0.1.0" 4 | description = "Compiler plugin for Rabbot" 5 | repository = "https://github.com/willcrichton/rabbot" 6 | readme = "../README.md" 7 | keywords = ["abstract", "binding", "tree"] 8 | license = "Apache-2.0/MIT" 9 | authors = ["Will Crichton "] 10 | 11 | [lib] 12 | plugin = true 13 | crate-type = ["dylib"] 14 | 15 | [dependencies] 16 | rabbot = { path = "../rabbot", version = "0.1" } 17 | -------------------------------------------------------------------------------- /rabbot-plugin/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private, plugin_registrar)] 2 | 3 | extern crate rustc_plugin; 4 | extern crate rabbot; 5 | extern crate syntax; 6 | 7 | use rustc_plugin::Registry; 8 | use syntax::codemap::Span; 9 | use syntax::parse::token::{Token as RsToken}; 10 | use syntax::ext::base::{ExtCtxt, MacResult, MacEager}; 11 | use syntax::tokenstream::TokenTree; 12 | use syntax::util::small_vector::SmallVector as Svec; 13 | 14 | use rabbot::token::Token; 15 | use rabbot::grammar::{parse_Decls as parse}; 16 | use rabbot::codegen; 17 | 18 | fn tt_flatten(tt: &TokenTree) -> Vec { 19 | match tt { 20 | &TokenTree::Token(_, ref t) => vec![t.clone()], 21 | &TokenTree::Delimited(_, ref delim) => { 22 | let mut toks: Vec = 23 | delim.tts.iter().flat_map(tt_flatten).collect(); 24 | toks.insert(0, RsToken::OpenDelim(delim.delim)); 25 | toks.push(RsToken::CloseDelim(delim.delim)); 26 | toks 27 | }, 28 | _ => panic!("Unreachable") 29 | } 30 | } 31 | 32 | #[allow(unused_variables)] 33 | pub fn expand_rabbot(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> 34 | Box 35 | { 36 | let tokens: Vec = 37 | args 38 | .into_iter() 39 | .flat_map(tt_flatten) 40 | .map(Token::from_rust_token) 41 | .collect(); 42 | //println!("{:?}", tokens); 43 | 44 | let ast = parse(tokens).unwrap(); 45 | //println!("{:?}", ast); 46 | 47 | MacEager::items(Svec::many(codegen::gen_decls(cx, ast))) 48 | } 49 | 50 | #[plugin_registrar] 51 | pub fn plugin_registrar(reg: &mut Registry) { 52 | reg.register_macro("rabbot", expand_rabbot); 53 | } 54 | -------------------------------------------------------------------------------- /rabbot-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rabbot-test" 3 | version = "0.1.0" 4 | authors = ["Will Crichton "] 5 | 6 | [dependencies] 7 | rabbot = { path = "../rabbot" } 8 | rabbot-plugin = { path = "../rabbot-plugin" } 9 | -------------------------------------------------------------------------------- /rabbot-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, box_syntax)] 2 | #![plugin(rabbot_plugin)] 3 | 4 | #[macro_use] extern crate rabbot; 5 | 6 | rabbot! { 7 | enum Typ { 8 | A 9 | } 10 | 11 | enum Term {mark: i32} { 12 | B(Typ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /rabbot-test/src/lib.rs.old: -------------------------------------------------------------------------------- 1 | #![feature(plugin, box_syntax)] 2 | #![plugin(rabbot_plugin)] 3 | 4 | #[macro_use] extern crate rabbot; 5 | 6 | rabbot! { 7 | sort Term { 8 | Z, 9 | S(Term), 10 | Lam(Binding . Term), 11 | App((Term, Term)) 12 | } 13 | } 14 | 15 | use rabbot::var::Var; 16 | use term::*; 17 | fn interpret(t: Term) -> Term { 18 | match out(t) { 19 | v @ View::Z | v @ View::Lam(_) => into(v), 20 | View::S(t) => { 21 | bind!(View::S{v} = out(interpret(t))); 22 | into(View::S(v)) 23 | }, 24 | View::App((fun, arg)) => { 25 | bind!(View::Lam{(var, body)} = out(interpret(fun))); 26 | subst(arg, var, body) 27 | }, 28 | View::Var(_) => unreachable!() 29 | } 30 | } 31 | 32 | #[test] 33 | fn test() { 34 | let x = Var::new("x".to_string()); 35 | let term = into(View::App(( 36 | into(View::Lam(( 37 | x.clone(), into(View::S(into(View::Var(x.clone()))))))), 38 | into(View::Z)))); 39 | 40 | println!("{:?}", interpret(term)); // prints S(Z) 41 | } 42 | -------------------------------------------------------------------------------- /rabbot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rabbot" 3 | version = "0.2.0" 4 | description = "Abstract binding tree generator" 5 | repository = "https://github.com/willcrichton/rabbot" 6 | readme = "../README.md" 7 | keywords = ["abstract", "binding", "tree"] 8 | license = "Apache-2.0/MIT" 9 | authors = ["Will Crichton "] 10 | build = "build.rs" 11 | 12 | [dependencies] 13 | lalrpop-util = "0.12.0" 14 | 15 | [build-dependencies] 16 | lalrpop = "0.12.0" 17 | -------------------------------------------------------------------------------- /rabbot/build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main () { 4 | lalrpop::process_root().unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /rabbot/src/abt.rs: -------------------------------------------------------------------------------- 1 | use std::default::Default; 2 | use var::Var; 3 | 4 | #[derive(Clone, Debug)] 5 | pub enum Oper { 6 | Int(i32), 7 | Plus, 8 | Let, 9 | Lam, 10 | App 11 | } 12 | 13 | // Oper contains a boxed T to avoid infintely-sized types. 14 | 15 | #[derive(Clone, Debug)] 16 | pub enum Abt { 17 | Fv(Var), 18 | Bv(i32), 19 | Abs(String, Box>), 20 | Oper(Box), 21 | } 22 | 23 | impl Default for Abt where T: Default { 24 | fn default() -> Abt { 25 | let t: T = Default::default(); 26 | Abt::Oper(box t) 27 | } 28 | } 29 | 30 | #[derive(Clone, Debug)] 31 | pub enum View { 32 | Var(Var), 33 | Binding(Var, Abt), 34 | Oper(T), 35 | } 36 | 37 | type BindingModifier = Box T>; 38 | 39 | impl Abt { 40 | pub fn bind(bind_oper: BindingModifier, x: Var, i: i32, t: Abt) -> Abt { 41 | match t { 42 | Abt::Fv(y) => if x == y { Abt::Bv(i) } else { Abt::Fv(y) }, 43 | Abt::Abs(name, box t) => Abt::Abs(name, box Abt::bind(bind_oper, x, i + 1, t)), 44 | Abt::Bv(n) => Abt::Bv(n), 45 | Abt::Oper(f) => Abt::Oper(box bind_oper(x, i, *f)) 46 | } 47 | } 48 | 49 | pub fn unbind(unbind_oper: BindingModifier, x: Var, i: i32, t: Abt) -> Abt { 50 | use self::*; 51 | match t { 52 | Abt::Fv(y) => Abt::Fv(y), 53 | Abt::Abs(name, box t) => Abt::Abs(name, box Abt::unbind(unbind_oper, x, i + 1, t)), 54 | Abt::Bv(n) => if i == n { Abt::Fv(x) } else { Abt::Bv(n) }, 55 | Abt::Oper(f) => Abt::Oper(box unbind_oper(x, i, *f)) 56 | } 57 | } 58 | 59 | pub fn into(bind_oper: BindingModifier, t: View) -> Abt { 60 | match t { 61 | View::Var(x) => Abt::Fv(x), 62 | View::Binding(x, t) => Abt::Abs(x.to_string(), box Abt::bind(bind_oper, x, 0, t)), 63 | View::Oper(f) => Abt::Oper(box f) 64 | } 65 | } 66 | 67 | pub fn out(unbind_oper: BindingModifier, t: Abt) -> View { 68 | match t { 69 | Abt::Bv(_) => panic!("Out on Bv"), 70 | Abt::Fv(x) => View::Var(x), 71 | Abt::Oper(f) => View::Oper(*f), 72 | Abt::Abs(_, box t) => { 73 | let var = Var::new(); 74 | //println!("out: new var {}", var); 75 | View::Binding(var.clone(), Abt::unbind(unbind_oper, var, 0, t)) 76 | } 77 | } 78 | } 79 | 80 | pub fn aequiv(oper_eq: Box bool>, t1: Abt, t2: Abt) -> bool { 81 | match (t1, t2) { 82 | (Abt::Bv(i), Abt::Bv(j)) => i == j, 83 | (Abt::Fv(x), Abt::Fv(y)) => x == y, 84 | (Abt::Abs(_, box t1), Abt::Abs(_, box t2)) => Abt::aequiv(oper_eq, t1, t2), 85 | (Abt::Oper(box f1), Abt::Oper(box f2)) => oper_eq(f1, f2), 86 | _ => false 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rabbot/src/ast.rs: -------------------------------------------------------------------------------- 1 | use syntax::ast::Ident; 2 | use syntax::ast::{Expr, Arm, Pat}; 3 | use syntax::ptr::P; 4 | use syntax::ext::base::ExtCtxt; 5 | use syntax::parse::token::{str_to_ident, Token as RsToken}; 6 | use syntax::tokenstream::TokenTree; 7 | use syntax::ext::quote::rt::ToTokens; 8 | use syntax::parse; 9 | use syntax::print::pprust; 10 | use syntax_pos::DUMMY_SP; 11 | use std::collections::HashSet; 12 | 13 | pub fn token_separate(ecx: &ExtCtxt, things: &[T], 14 | token: RsToken) -> Vec { 15 | let mut output: Vec = vec![]; 16 | for (i, thing) in things.iter().enumerate() { 17 | output.extend(thing.to_tokens(ecx)); 18 | if i < things.len() - 1 { 19 | output.push(TokenTree::Token(DUMMY_SP, token.clone())); 20 | } 21 | } 22 | output 23 | } 24 | 25 | struct IdGenerator { 26 | count: i32 27 | } 28 | 29 | impl IdGenerator { 30 | pub fn new() -> IdGenerator { 31 | IdGenerator { 32 | count: 0 33 | } 34 | } 35 | 36 | pub fn gen(&mut self) -> Ident { 37 | let new_id = str_to_ident(format!("var{}", self.count).as_str()); 38 | self.count += 1; 39 | new_id 40 | } 41 | } 42 | 43 | pub fn ident_to_lower(id: &Ident) -> Ident { 44 | str_to_ident(id.name.as_str().to_lowercase().as_str()) 45 | } 46 | 47 | pub type Metadata = Vec<(Ident, Ident)>; 48 | 49 | pub enum Decl { 50 | Sort(Ident, Vec, Metadata), 51 | Use(String), 52 | } 53 | 54 | pub type Item = (Ident, Option); 55 | 56 | #[derive(Debug, Clone, Hash)] 57 | pub enum Type { 58 | Ident(Ident), 59 | Prod(Vec), 60 | Var(Box, Box), 61 | Bind(Ident), 62 | Vec(Box), 63 | } 64 | 65 | pub struct State<'a> { 66 | pub sess: &'a parse::ParseSess, 67 | pub sorts: HashSet, 68 | pub meta: Metadata, 69 | pub sort_id: Ident, 70 | } 71 | 72 | impl<'a> State<'a> { 73 | pub fn build_node(&mut self, val: P) -> P { 74 | let exprs = self.meta.iter().map(|&(ref key, _)| { 75 | let key = key.name.as_str(); 76 | format!("{}: node.{}", key, key) 77 | }).collect::>().join(","); 78 | let val = pprust::expr_to_string(&val.unwrap()); 79 | let node = format!("Meta {{ val: {}, {} }}", val, exprs); 80 | parse::parse_expr_from_source_str("".to_string(), node, vec![], self.sess) 81 | .unwrap() 82 | } 83 | } 84 | 85 | impl Type { 86 | pub fn to_ops_arm(&self, cx: &mut ExtCtxt, name: &Ident, 87 | st: &mut State, bind: bool) 88 | -> Arm 89 | { 90 | let (pat, e) = self.to_ops_arm_helper(cx, &mut IdGenerator::new(), st, bind); 91 | let node = st.build_node(quote_expr!(cx, Op::$name($e))); 92 | quote_arm!(cx, Op::$name($pat) => { $node }) 93 | } 94 | 95 | fn to_ops_arm_helper(&self, cx: &mut ExtCtxt, generator: &mut IdGenerator, 96 | st: &mut State, bind: bool) 97 | -> (P, P) 98 | { 99 | match self { 100 | &Type::Ident(ref id) => { 101 | let new_id = generator.gen(); 102 | (quote_pat!(cx, $new_id), 103 | if st.sorts.contains(id) { 104 | let lower = ident_to_lower(id); 105 | if bind { 106 | quote_expr!(cx, (Abt::bind(box super::$lower::oper_bind, x.clone(), i, $new_id))) 107 | } else { 108 | quote_expr!(cx, (Abt::unbind(box super::$lower::oper_unbind, x.clone(), i, $new_id))) 109 | } 110 | } else { 111 | quote_expr!(cx, $new_id) 112 | }) 113 | }, 114 | &Type::Prod(ref tys) => { 115 | let (pats, exprs): (Vec>, Vec>) = 116 | tys.iter().map(|ty| ty.to_ops_arm_helper(cx, generator, st, bind)).unzip(); 117 | let pats = token_separate(cx, &pats, RsToken::Comma); 118 | let exprs = token_separate(cx, &exprs, RsToken::Comma); 119 | (quote_pat!(cx, ($pats)), quote_expr!(cx, ($exprs))) 120 | }, 121 | &Type::Vec(ref ty) => { 122 | let (pat, expr) = ty.to_ops_arm_helper(cx, generator, st, bind); 123 | let new_id = generator.gen(); 124 | (quote_pat!(cx, $new_id), 125 | quote_expr!(cx, { 126 | $new_id.into_iter().map(|$pat| { $expr }).collect() 127 | })) 128 | }, 129 | &Type::Var(box ref l, box ref r) => { 130 | Type::Prod(vec![l.clone(), r.clone()]).to_ops_arm_helper(cx, generator, st, bind) 131 | }, 132 | &Type::Bind(_) => { 133 | let new_id = generator.gen(); 134 | (quote_pat!(cx, $new_id), quote_expr!(cx, $new_id)) 135 | }, 136 | } 137 | } 138 | 139 | pub fn to_view_in_arm(&self, cx: &mut ExtCtxt, st: &mut State, name: &Ident) 140 | -> Arm 141 | { 142 | let (pat, e) = self.to_view_in_arm_helper(cx, &mut IdGenerator::new(), st); 143 | let e = quote_expr!(cx, Op::$name({ 144 | let (t, _): (_, Vec) = $e; 145 | t 146 | })); 147 | let node = st.build_node(e); 148 | quote_arm!(cx, View::$name($pat) => { AbtView::Oper($node) }) 149 | } 150 | 151 | fn to_view_in_arm_helper(&self, cx: &mut ExtCtxt, generator: &mut IdGenerator, 152 | st: &mut State) 153 | -> (P, P) 154 | { 155 | match self { 156 | &Type::Ident(ref id) => { 157 | let new_id = generator.gen(); 158 | (quote_pat!(cx, $new_id), 159 | if st.sorts.contains(id) { 160 | let lower = ident_to_lower(id); 161 | quote_expr!(cx, { 162 | let mut abt = $new_id; 163 | for var in vars.iter() { 164 | abt = Abt::into(box super::$lower::oper_bind, AbtView::Binding(var.clone(), abt)); 165 | } 166 | (abt, vec![]) 167 | }) 168 | } else { 169 | quote_expr!(cx, ($new_id, vec![])) 170 | }) 171 | }, 172 | &Type::Prod(ref tys) => { 173 | let (pats, exprs): (Vec>, Vec>) = 174 | tys.iter().map(|ty| ty.to_view_in_arm_helper(cx, generator, st)).unzip(); 175 | let new_vars = generator.gen(); 176 | let exprs: Vec> = exprs.into_iter().map(|expr| { 177 | let t_id = generator.gen(); 178 | let vars_id = generator.gen(); 179 | quote_expr!(cx, { 180 | let ($t_id, mut $vars_id) = $expr; 181 | $new_vars.append(&mut $vars_id); 182 | $t_id 183 | }) 184 | }).collect(); 185 | let pats = token_separate(cx, &pats, RsToken::Comma); 186 | let exprs = token_separate(cx, &exprs, RsToken::Comma); 187 | (quote_pat!(cx, ($pats)), 188 | quote_expr!(cx, { 189 | let mut $new_vars = vec![]; 190 | (($exprs), $new_vars) 191 | })) 192 | }, 193 | &Type::Vec(ref ty) => { 194 | let (pat, expr) = ty.to_view_in_arm_helper(cx, generator, st); 195 | let new_id = generator.gen(); 196 | (quote_pat!(cx, $new_id), 197 | quote_expr!(cx, { 198 | let (ts, vars): (_, Vec>) = 199 | $new_id.into_iter().map(|$pat| { 200 | $expr 201 | }).unzip(); 202 | (ts, vars.into_iter().flat_map(|x| x).collect()) 203 | })) 204 | }, 205 | &Type::Var(box ref l, box ref r) => { 206 | let (lpat, lexpr) = l.to_view_in_arm_helper(cx, generator, st); 207 | let (rpat, rexpr) = r.to_view_in_arm_helper(cx, generator, st); 208 | (quote_pat!(cx, ($lpat, $rpat)), 209 | quote_expr!(cx, { 210 | let (t0, vars) = $lexpr; 211 | let (t1, vars0) = $rexpr; 212 | ((t0, t1), vars0) 213 | })) 214 | }, 215 | &Type::Bind(_) => { 216 | let new_id = generator.gen(); 217 | (quote_pat!(cx, $new_id), 218 | quote_expr!(cx, { 219 | ($new_id.to_string(), vec![$new_id]) 220 | })) 221 | } 222 | } 223 | } 224 | 225 | pub fn to_view_out_arm(&self, cx: &mut ExtCtxt, st: &mut State, name: &Ident) 226 | -> Arm 227 | { 228 | let (pat, e) = self.to_view_out_arm_helper(cx, &mut IdGenerator::new(), st); 229 | let e = quote_expr!(cx, View::$name({ 230 | let (t, _): (_, Vec) = $e; 231 | t 232 | })); 233 | let node = st.build_node(e); 234 | quote_arm!(cx, Op::$name($pat) => { $node }) 235 | } 236 | 237 | fn to_view_out_arm_helper(&self, cx: &mut ExtCtxt, generator: &mut IdGenerator, 238 | st: &mut State) 239 | -> (P, P) 240 | { 241 | match self { 242 | &Type::Ident(ref id) => { 243 | let new_id = generator.gen(); 244 | (quote_pat!(cx, $new_id), 245 | if st.sorts.contains(id) { 246 | let lower = ident_to_lower(id); 247 | quote_expr!(cx, { 248 | let mut abt = $new_id; 249 | for var in vars.iter() { 250 | let unbound = Abt::unbind(box super::$lower::oper_unbind, var.clone(), -1, abt); 251 | abt = match Abt::out(box super::$lower::oper_unbind, unbound) { 252 | AbtView::Binding(_, abt) => abt, 253 | _ => unreachable!() 254 | }; 255 | } 256 | (abt, vec![]) 257 | }) 258 | } else { 259 | quote_expr!(cx, ($new_id, vec![])) 260 | }) 261 | }, 262 | &Type::Prod(ref tys) => { 263 | let (pats, exprs): (Vec>, Vec>) = 264 | tys.iter().map(|ty| ty.to_view_out_arm_helper(cx, generator, st)).unzip(); 265 | let new_vars = generator.gen(); 266 | let exprs: Vec> = exprs.into_iter().map(|expr| { 267 | let t_id = generator.gen(); 268 | let vars_id = generator.gen(); 269 | quote_expr!(cx, { 270 | let ($t_id, mut $vars_id) = $expr; 271 | $new_vars.append(&mut $vars_id); 272 | $t_id 273 | }) 274 | }).collect(); 275 | let pats = token_separate(cx, &pats, RsToken::Comma); 276 | let exprs = token_separate(cx, &exprs, RsToken::Comma); 277 | (quote_pat!(cx, ($pats)), 278 | quote_expr!(cx, { 279 | let mut $new_vars = vec![]; 280 | (($exprs), $new_vars) 281 | })) 282 | }, 283 | &Type::Vec(ref ty) => { 284 | let (pat, expr) = ty.to_view_out_arm_helper(cx, generator, st); 285 | let new_id = generator.gen(); 286 | (quote_pat!(cx, $new_id), 287 | quote_expr!(cx, { 288 | let (ts, vars): (_, Vec>) = 289 | $new_id.into_iter().map(|$pat| { 290 | $expr 291 | }).unzip(); 292 | (ts, vars.into_iter().flat_map(|x| x).collect()) 293 | })) 294 | }, 295 | &Type::Var(box ref l, box ref r) => { 296 | let (lpat, lexpr) = l.to_view_out_arm_helper(cx, generator, st); 297 | let (rpat, rexpr) = r.to_view_out_arm_helper(cx, generator, st); 298 | (quote_pat!(cx, ($lpat, $rpat)), 299 | quote_expr!(cx, { 300 | let (t0, vars) = $lexpr; 301 | let (t1, vars0) = $rexpr; 302 | ((t0, t1), vars0) 303 | })) 304 | }, 305 | &Type::Bind(_) => { 306 | //let new_id = generator.gen(); 307 | (quote_pat!(cx, _), 308 | quote_expr!(cx, { 309 | let x = Var::new(); 310 | (x.clone(), vec![x]) 311 | })) 312 | } 313 | } 314 | } 315 | 316 | pub fn to_subst_arm(&self, cx: &mut ExtCtxt, st: &mut State, name: &Ident) -> Arm 317 | { 318 | let generator = &mut IdGenerator::new(); 319 | let (pat, e) = if name == &str_to_ident("Var") { 320 | let var_id = generator.gen(); 321 | let node = st.build_node(quote_expr!(cx, View::$name($var_id))); 322 | (quote_pat!(cx, $var_id), 323 | quote_expr!(cx, { 324 | let var = extract_var($var_id.clone()); 325 | if var == x { t } 326 | else { into($node) } 327 | })) 328 | } else { 329 | let (pat, e) = self.to_subst_arm_helper(cx, generator, st); 330 | let node = st.build_node(quote_expr!(cx, View::$name($e))); 331 | (pat, quote_expr!(cx, into($node))) 332 | }; 333 | quote_arm!(cx, View::$name($pat) => { $e }) 334 | } 335 | 336 | fn to_subst_arm_helper(&self, cx: &mut ExtCtxt, generator: &mut IdGenerator, 337 | st: &mut State) 338 | -> (P, P) 339 | { 340 | match self { 341 | &Type::Ident(ref id) => { 342 | let new_id = generator.gen(); 343 | (quote_pat!(cx, $new_id), 344 | if id == &st.sort_id { 345 | quote_expr!(cx, subst(t.clone(), x.clone(), $new_id)) 346 | } else { 347 | quote_expr!(cx, $new_id) 348 | }) 349 | }, 350 | &Type::Prod(ref tys) => { 351 | let (pats, exprs): (Vec>, Vec>) = 352 | tys.iter().map(|ty| ty.to_subst_arm_helper(cx, generator, st)).unzip(); 353 | let pats = token_separate(cx, &pats, RsToken::Comma); 354 | let exprs = token_separate(cx, &exprs, RsToken::Comma); 355 | (quote_pat!(cx, ($pats)), quote_expr!(cx, ($exprs))) 356 | }, 357 | &Type::Vec(ref ty) => { 358 | let (pat, expr) = ty.to_subst_arm_helper(cx, generator, st); 359 | let new_id = generator.gen(); 360 | (quote_pat!(cx, $new_id), 361 | quote_expr!(cx, { 362 | $new_id.into_iter().map(|$pat| $expr).collect() 363 | })) 364 | }, 365 | &Type::Var(_, box ref r) => { 366 | let (rpat, rexpr) = r.to_subst_arm_helper(cx, generator, st); 367 | (quote_pat!(cx, (l, $rpat)), 368 | quote_expr!(cx, (l, $rexpr))) 369 | }, 370 | &Type::Bind(_) => { 371 | let new_id = generator.gen(); 372 | (quote_pat!(cx, $new_id), quote_expr!(cx, $new_id)) 373 | } 374 | } 375 | } 376 | 377 | pub fn to_free_vars_arm(&self, cx: &mut ExtCtxt, sorts: &HashSet, 378 | name: &Ident) 379 | -> Arm 380 | { 381 | let generator = &mut IdGenerator::new(); 382 | let (pat, e) = if name == &str_to_ident("Var") { 383 | let var_id = generator.gen(); 384 | (quote_pat!(cx, $var_id), 385 | quote_expr!(cx, { 386 | let var = extract_var($var_id); 387 | let mut hs = HashSet::new(); 388 | hs.insert(var); 389 | hs 390 | })) 391 | } else { 392 | self.to_free_vars_arm_helper(cx, generator, sorts) 393 | }; 394 | 395 | quote_arm!(cx, View::$name($pat) => { $e }) 396 | } 397 | 398 | fn to_free_vars_arm_helper(&self, 399 | cx: &mut ExtCtxt, 400 | generator: &mut IdGenerator, 401 | sorts: &HashSet) 402 | -> (P, P) 403 | { 404 | match self { 405 | &Type::Ident(ref id) => { 406 | let new_id = generator.gen(); 407 | (quote_pat!(cx, $new_id), 408 | if sorts.contains(id) { 409 | let lower = ident_to_lower(id); 410 | quote_expr!(cx, $lower::free_vars($new_id)) 411 | } else { 412 | quote_expr!(cx, HashSet::new()) 413 | }) 414 | }, 415 | &Type::Prod(ref tys) => { 416 | let (pats, exprs): (Vec>, Vec>) = 417 | tys.iter().map(|ty| ty.to_free_vars_arm_helper(cx, generator, sorts)).unzip(); 418 | let new_vars = generator.gen(); 419 | let exprs: Vec> = exprs.into_iter().map(|expr| { 420 | quote_expr!(cx, { 421 | $new_vars.extend(($expr).into_iter()); 422 | }) 423 | }).collect(); 424 | let pats = token_separate(cx, &pats, RsToken::Comma); 425 | let exprs = token_separate(cx, &exprs, RsToken::Comma); 426 | (quote_pat!(cx, ($pats)), 427 | quote_expr!(cx, { 428 | let mut $new_vars = HashSet::new(); 429 | ($exprs); 430 | $new_vars 431 | })) 432 | }, 433 | &Type::Vec(ref ty) => { 434 | let (pat, expr) = ty.to_free_vars_arm_helper(cx, generator, sorts); 435 | let new_id = generator.gen(); 436 | (quote_pat!(cx, $new_id), 437 | quote_expr!(cx, { 438 | let mut hs = HashSet::new(); 439 | for $pat in $new_id.into_iter() { 440 | hs.extend($expr); 441 | } 442 | hs 443 | })) 444 | }, 445 | &Type::Var(_, box ref r) => { 446 | let (rpat, rexpr) = r.to_free_vars_arm_helper(cx, generator, sorts); 447 | (quote_pat!(cx, (_, $rpat)), 448 | quote_expr!(cx, $rexpr)) 449 | }, 450 | &Type::Bind(_) => { 451 | (quote_pat!(cx, _), quote_expr!(cx, HashSet::new())) 452 | } 453 | } 454 | } 455 | 456 | pub fn to_aequiv_arm(&self, cx: &mut ExtCtxt, sorts: &HashSet, 457 | name: &Ident) 458 | -> Arm 459 | { 460 | let generator = &mut IdGenerator::new(); 461 | let ((pat1, pat2), e) = self.to_aequiv_arm_helper(cx, generator, sorts); 462 | 463 | quote_arm!(cx, (Op::$name($pat1), Op::$name($pat2)) => { $e }) 464 | } 465 | 466 | fn to_aequiv_arm_helper(&self, cx: &mut ExtCtxt, generator: &mut IdGenerator, 467 | sorts: &HashSet) 468 | -> ((P, P), P) 469 | { 470 | match self { 471 | &Type::Ident(ref id) => { 472 | let (id1, id2) = (generator.gen(), generator.gen()); 473 | ((quote_pat!(cx, $id1), 474 | quote_pat!(cx, $id2)), 475 | if sorts.contains(id) { 476 | let id = ident_to_lower(id); 477 | quote_expr!(cx, $id::aequiv($id1, $id2)) 478 | } else { 479 | quote_expr!(cx, $id1 == $id2) 480 | }) 481 | }, 482 | &Type::Prod(ref tys) => { 483 | let (pats, exprs): (Vec<(P, P)>, Vec>) = 484 | tys.iter().map(|ty| ty.to_aequiv_arm_helper(cx, generator, sorts)).unzip(); 485 | let (pats1, pats2): (Vec>, Vec>) = pats.into_iter().unzip(); 486 | let (pats1, pats2) = ( 487 | token_separate(cx, &pats1, RsToken::Comma), 488 | token_separate(cx, &pats2, RsToken::Comma)); 489 | let exprs = token_separate(cx, &exprs, RsToken::AndAnd); 490 | ((quote_pat!(cx, ($pats1)), 491 | quote_pat!(cx, ($pats2))), 492 | quote_expr!(cx, $exprs)) 493 | }, 494 | &Type::Vec(ref ty) => { 495 | let ((pat1, pat2), expr) = ty.to_aequiv_arm_helper(cx, generator, sorts); 496 | let (id1, id2) = (generator.gen(), generator.gen()); 497 | ((quote_pat!(cx, $id1), quote_pat!(cx, $id2)), 498 | quote_expr!(cx, { 499 | $id1.into_iter().zip($id2.into_iter()).all(|($pat1, $pat2)| { 500 | $expr 501 | }) 502 | })) 503 | }, 504 | &Type::Var(_, box ref r) => { 505 | let ((pat1, pat2), expr) = r.to_aequiv_arm_helper(cx, generator, sorts); 506 | ((quote_pat!(cx, (_, $pat1)), 507 | quote_pat!(cx, (_, $pat2))), 508 | quote_expr!(cx, $expr)) 509 | }, 510 | &Type::Bind(_) => { 511 | ((quote_pat!(cx, _), quote_pat!(cx, _)), quote_expr!(cx, true)) 512 | } 513 | } 514 | } 515 | 516 | pub fn to_enum_string(&self, sort_id: &Ident) -> String { 517 | match self { 518 | &Type::Ident(ref id) => id.name.as_str().to_string(), 519 | &Type::Prod(ref tys) => { 520 | let strs: Vec = 521 | tys.iter().map(|ty| ty.to_enum_string(sort_id)).collect(); 522 | format!("({})", strs.join(", ")) 523 | }, 524 | &Type::Vec(ref ty) => format!("Vec<{}>", ty.to_enum_string(sort_id)), 525 | &Type::Var(box ref l, box ref r) => 526 | Type::Prod(vec![l.clone(), r.clone()]).to_enum_string(sort_id), 527 | &Type::Bind(_) => "Var".to_string() 528 | } 529 | } 530 | 531 | pub fn to_ops_enum_string(&self, sort_id: &Ident) -> String { 532 | match self { 533 | &Type::Ident(ref id) => id.name.as_str().to_string(), 534 | &Type::Prod(ref tys) => { 535 | let strs: Vec = 536 | tys.iter().map(|ty| ty.to_ops_enum_string(sort_id)).collect(); 537 | format!("({})", strs.join(", ")) 538 | }, 539 | &Type::Vec(ref ty) => format!("Vec<{}>", ty.to_enum_string(sort_id)), 540 | &Type::Var(box ref l, box ref r) => 541 | Type::Prod(vec![l.clone(), r.clone()]).to_ops_enum_string(sort_id), 542 | &Type::Bind(_) => "String".to_string() 543 | } 544 | } 545 | } 546 | -------------------------------------------------------------------------------- /rabbot/src/codegen.rs: -------------------------------------------------------------------------------- 1 | use syntax::ext::base::ExtCtxt; 2 | use syntax::ast::{Item as RsItem, Arm, Ident}; 3 | use syntax::parse::token::str_to_ident; 4 | use syntax::parse; 5 | use syntax::ptr::P; 6 | use std::collections::HashSet; 7 | 8 | use super::ast::*; 9 | 10 | pub fn gen_sort(cx: &mut ExtCtxt, decl: Decl, sorts: &HashSet, global_uses: &Vec) 11 | -> Vec> 12 | { 13 | let (sort_id, mut items, meta) = 14 | if let Decl::Sort(sort_id, items, meta) = decl { (sort_id, items, meta) } 15 | else { unreachable!() }; 16 | 17 | items.insert(0, (str_to_ident("Var"), Some(Type::Ident(sort_id.clone())))); 18 | 19 | let sess = parse::ParseSess::new(); 20 | 21 | let mut uses = vec![quote_item!(cx, use std::collections::HashSet;).unwrap()]; 22 | for id in sorts.iter() { 23 | let module = ident_to_lower(id); 24 | uses.push(quote_item!(cx, use super::$module;).unwrap()); 25 | if id != &sort_id { 26 | uses.push(quote_item!(cx, use super::$module::$id;).unwrap()) 27 | } 28 | } 29 | 30 | for path in global_uses.iter() { 31 | let tt = 32 | parse::parse_tts_from_source_str("".to_string(), path.clone(), vec![], &sess) 33 | .unwrap(); 34 | uses.push(quote_item!(cx, use $tt;).unwrap()); 35 | } 36 | 37 | let ops_variant = { 38 | let variant_arms: Vec = 39 | items.iter().map(|&(ref name, ref item)| { 40 | match item { 41 | &None => 42 | format!("{},", name.name.as_str()), 43 | &Some(ref item) => 44 | format!("{}({}),", name.name.as_str(), item.to_ops_enum_string(&sort_id)) 45 | } 46 | }).collect(); 47 | let ops_variant = format!( 48 | "#[derive(Debug, Clone)] pub enum Op {{ {} }}", 49 | variant_arms.join("\n")); 50 | 51 | parse::parse_item_from_source_str("".to_string(), ops_variant, vec![], &sess) 52 | .unwrap().unwrap() 53 | }; 54 | 55 | let main_variant = { 56 | let mut variant_arms: Vec = 57 | items.iter().map(|&(ref name, ref item)| { 58 | match item { 59 | &None => 60 | format!("{},", name.name.as_str()), 61 | &Some(ref item) => 62 | format!("{}({}),", name.name.as_str(), item.to_enum_string(&sort_id)) 63 | } 64 | }).collect(); 65 | variant_arms.insert(0, "Var_(Var),".to_string()); 66 | let main_variant = format!( 67 | "#[derive(Debug, Clone)] pub enum View {{ {} }}", 68 | variant_arms.join("\n")); 69 | 70 | parse::parse_item_from_source_str("".to_string(), main_variant, vec![], &sess) 71 | .unwrap().unwrap() 72 | }; 73 | 74 | 75 | let st = &mut State { 76 | sess: &sess, 77 | sorts: sorts.clone(), 78 | meta: meta.clone(), 79 | sort_id: sort_id.clone(), 80 | }; 81 | 82 | let oper_bind = { 83 | let arms: Vec = items.iter().map(|&(ref name, ref item)| { 84 | match item { 85 | &Some(ref item) => item.to_ops_arm(cx, name, st, true), 86 | &None => { 87 | let node = st.build_node(quote_expr!(cx, Op::$name)); 88 | quote_arm!(cx, Op::$name => { $node }) 89 | } 90 | } 91 | }).collect(); 92 | 93 | quote_item!( 94 | cx, 95 | pub fn oper_bind(x: Var, i: i32, node: Meta) -> Meta { 96 | match node.val { 97 | $arms 98 | } 99 | }).unwrap() 100 | }; 101 | 102 | let oper_unbind = { 103 | let arms: Vec = items.iter().map(|&(ref name, ref item)| { 104 | match item { 105 | &Some(ref item) => item.to_ops_arm(cx, name, st, false), 106 | &None => { 107 | let node = st.build_node(quote_expr!(cx, Op::$name)); 108 | quote_arm!(cx, Op::$name => { $node }) 109 | } 110 | } 111 | }).collect(); 112 | 113 | quote_item!( 114 | cx, 115 | pub fn oper_unbind(x: Var, i: i32, node: Meta) -> Meta { 116 | match node.val { 117 | $arms 118 | } 119 | }).unwrap() 120 | }; 121 | 122 | let meta_def = { 123 | let arms = meta.iter().map(|&(ref key, ref value)| { 124 | format!("pub {}: {}", key.name.as_str(), value.name.as_str()) 125 | }).collect::>().join(",\n"); 126 | 127 | let node = format!("#[derive(Clone, Default)] pub struct Meta {{ pub val: T, \n {} }}", arms); 128 | 129 | parse::parse_item_from_source_str("".to_string(), node, vec![], &sess) 130 | .unwrap().unwrap() 131 | }; 132 | 133 | let alias = quote_item!( 134 | cx, 135 | pub type $sort_id = Abt>; 136 | ).unwrap(); 137 | 138 | let view_in = { 139 | let mut arms: Vec = items.iter().map(|&(ref name, ref item)| { 140 | match item { 141 | &Some(ref item) => item.to_view_in_arm(cx, st, name), 142 | &None => { 143 | let node = st.build_node(quote_expr!(cx, Op::$name)); 144 | quote_arm!(cx, View::$name => { AbtView::Oper($node) }) 145 | } 146 | } 147 | }).collect(); 148 | 149 | arms.insert(0, { 150 | quote_arm!(cx, View::Var_(x) => { AbtView::Var(x) }) 151 | }); 152 | 153 | quote_item!( 154 | cx, 155 | fn view_in(vars: Vec, node: Meta) -> AbtView> { 156 | match node.val { 157 | $arms 158 | } 159 | }).unwrap() 160 | }; 161 | 162 | let oper_view_out = { 163 | let arms: Vec = items.iter().map(|&(ref name, ref item)| { 164 | match item { 165 | &Some(ref item) => item.to_view_out_arm(cx, st, name), 166 | &None => { 167 | let node = st.build_node(quote_expr!(cx, View::$name)); 168 | quote_arm!(cx, Op::$name => { $node }) 169 | } 170 | } 171 | }).collect(); 172 | 173 | quote_item!( 174 | cx, 175 | fn oper_view_out(vars: Vec, node: Meta) -> Meta { 176 | match node.val { 177 | $arms 178 | } 179 | }).unwrap() 180 | }; 181 | 182 | let out = quote_item!( 183 | cx, 184 | pub fn out(term: $sort_id) -> Meta { 185 | match Abt::out(box oper_unbind, term) { 186 | AbtView::Var(_) => panic!("Out called on a Var"), 187 | AbtView::Oper(t) => oper_view_out(vec![], t), 188 | _ => panic!("Invalid out") 189 | } 190 | }).unwrap(); 191 | 192 | let into = quote_item!( 193 | cx, 194 | pub fn into(v: Meta) -> $sort_id { 195 | Abt::into(box oper_bind, view_in(vec![], v)) 196 | }).unwrap(); 197 | 198 | let subst = { 199 | let mut arms: Vec = items.iter().map(|&(ref name, ref item)| { 200 | match item { 201 | &Some(ref item) => item.to_subst_arm(cx, st, name), 202 | &None => { 203 | let e = st.build_node(quote_expr!(cx, View::$name)); 204 | quote_arm!(cx, View::$name => { into($e) }) 205 | } 206 | } 207 | }).collect(); 208 | 209 | let e = st.build_node(quote_expr!(cx, View::Var_(var))); 210 | arms.insert(0, quote_arm!(cx, View::Var_(var) => { 211 | if var == x { 212 | t 213 | } else { 214 | into($e) 215 | } 216 | })); 217 | 218 | quote_item!( 219 | cx, 220 | pub fn subst(t: $sort_id, x: Var, term: $sort_id) -> $sort_id { 221 | let node = out(term); 222 | match node.val { 223 | $arms 224 | } 225 | }).unwrap() 226 | }; 227 | 228 | let free_vars_helper = { 229 | let mut arms: Vec = items.iter().map(|&(ref name, ref item)| { 230 | match item { 231 | &Some(ref item) => item.to_free_vars_arm(cx, sorts, name), 232 | &None => quote_arm!(cx, View::$name => { HashSet::new() }) 233 | } 234 | }).collect(); 235 | 236 | arms.insert(0, quote_arm!(cx, View::Var_(var) => { unreachable!() })); 237 | 238 | quote_item!( 239 | cx, 240 | fn free_vars_helper(t: $sort_id, bound: HashSet) -> HashSet { 241 | match out(t).val { 242 | $arms 243 | } 244 | }).unwrap() 245 | }; 246 | 247 | let free_vars = quote_item!( 248 | cx, 249 | pub fn free_vars(t: $sort_id) -> HashSet { 250 | free_vars_helper(t, HashSet::new()) 251 | }).unwrap(); 252 | 253 | let oper_aequiv = { 254 | let arms: Vec = items.iter().map(|&(ref name, ref item)| { 255 | match item { 256 | &Some(ref item) => item.to_aequiv_arm(cx, sorts, name), 257 | &None => quote_arm!(cx, (Op::$name, Op::$name) => { true }) 258 | } 259 | }).collect(); 260 | 261 | quote_item!( 262 | cx, 263 | fn oper_aequiv(t1: Meta, t2: Meta) -> bool { 264 | match (t1.val, t2.val) { 265 | $arms 266 | _ => false 267 | } 268 | }) 269 | }; 270 | 271 | let aequiv = quote_item!( 272 | cx, 273 | pub fn aequiv(t1: $sort_id, t2: $sort_id) -> bool { 274 | Abt::aequiv(box oper_aequiv, t1, t2) 275 | }); 276 | 277 | let var = { 278 | let e = st.build_node(quote_expr!(cx, View::Var_(node.val))); 279 | quote_item!( 280 | cx, 281 | pub fn var(node: Meta) -> View { 282 | View::Var(into($e)) 283 | }) 284 | }; 285 | 286 | let (default1, default2) = { 287 | let mut default = None; 288 | for &(ref name, ref item) in items.iter() { 289 | match item { 290 | &None => { 291 | default = Some(name.clone()); 292 | break; 293 | }, 294 | _ => { continue; } 295 | } 296 | } 297 | let default = default.expect("Enum must have one arm with no fields"); 298 | (quote_item!( 299 | cx, 300 | impl Default for Op { 301 | fn default() -> Op { Op::$default } 302 | }), 303 | quote_item!( 304 | cx, 305 | impl Default for View { 306 | fn default() -> View { View::$default } 307 | })) 308 | }; 309 | 310 | let sort_id_lower = ident_to_lower(&sort_id); 311 | let module = quote_item!( 312 | cx, 313 | #[allow(unused_variables, dead_code, unused_imports)] 314 | pub mod $sort_id_lower { 315 | use rabbot::abt::{Abt, View as AbtView}; 316 | use rabbot::var::Var; 317 | use std::fmt; 318 | $uses 319 | $ops_variant 320 | $main_variant 321 | $meta_def 322 | $alias 323 | $oper_bind 324 | $oper_unbind 325 | $view_in 326 | $oper_view_out 327 | $out 328 | $into 329 | $subst 330 | $free_vars_helper 331 | $free_vars 332 | $oper_aequiv 333 | $aequiv 334 | $var 335 | $default1 336 | $default2 337 | pub fn extract_var(t: $sort_id) -> Var { 338 | match Abt::out(box oper_unbind, t) { 339 | AbtView::Var(v) => v, 340 | _ => unreachable!() 341 | } 342 | } 343 | pub fn into_view(v: View) -> $sort_id { 344 | into(Meta { val: v, ..Default::default() }) 345 | } 346 | impl fmt::Debug for Meta { 347 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 348 | self.val.fmt(f) 349 | } 350 | } 351 | }).unwrap(); 352 | 353 | vec![module] 354 | } 355 | 356 | pub fn gen_decls(cx: &mut ExtCtxt, ast: Vec) -> Vec> { 357 | let (uses, sorts): (Vec, Vec) = 358 | ast.into_iter().partition(|node| match node { 359 | &Decl::Use(_) => true, 360 | _ => false 361 | }); 362 | 363 | let uses = uses.into_iter().map(|node| { 364 | if let Decl::Use(path) = node { 365 | path 366 | } else { 367 | unreachable!() 368 | } 369 | }).collect::>(); 370 | 371 | let mut sort_ids = HashSet::new(); 372 | for node in sorts.iter() { 373 | if let &Decl::Sort(ref id, _, _) = node { 374 | sort_ids.insert(id.clone()); 375 | } else { 376 | unreachable!() 377 | } 378 | } 379 | 380 | sorts.into_iter().flat_map(|ast| gen_sort(cx, ast, &sort_ids, &uses)).collect() 381 | } 382 | -------------------------------------------------------------------------------- /rabbot/src/grammar.lalrpop: -------------------------------------------------------------------------------- 1 | use super::ast::*; 2 | use super::token::Token; 3 | use syntax::ast::Ident; 4 | use syntax::parse::token::{Token as RsToken, DelimToken}; 5 | 6 | grammar; 7 | 8 | extern { 9 | enum Token { 10 | Ident => Token::RustToken(RsToken::Ident()), 11 | Lbrace => Token::RustToken(RsToken::OpenDelim(DelimToken::Brace)), 12 | Rbrace => Token::RustToken(RsToken::CloseDelim(DelimToken::Brace)), 13 | Lparen => Token::RustToken(RsToken::OpenDelim(DelimToken::Paren)), 14 | Rparen => Token::RustToken(RsToken::CloseDelim(DelimToken::Paren)), 15 | Gt => Token::RustToken(RsToken::Gt), 16 | Lt => Token::RustToken(RsToken::Lt), 17 | Dot => Token::RustToken(RsToken::Dot), 18 | Colon => Token::RustToken(RsToken::Colon), 19 | Comma => Token::RustToken(RsToken::Comma), 20 | ModSep => Token::RustToken(RsToken::ModSep), 21 | Semi => Token::RustToken(RsToken::Semi), 22 | Enum => Token::Enum, 23 | Binding => Token::Binding, 24 | Use => Token::Use, 25 | Vec => Token::Vec, 26 | } 27 | } 28 | 29 | pub Decls: Vec = { Decl+ }; 30 | 31 | Sep: Vec = { 32 | S)*> => { 33 | let mut ts = ts; 34 | ts.push(t); 35 | ts 36 | } 37 | }; 38 | 39 | MetadataItem: (Ident, Ident) = { 40 | Colon => (l, r) 41 | }; 42 | 43 | Metadata: Metadata = { 44 | Lbrace > Rbrace => items 45 | }; 46 | 47 | Decl: Decl = { 48 | Enum Lbrace > Rbrace => 49 | Decl::Sort(id, items, match meta { Some(v) => v, None => vec![] }), 50 | Use > Semi => 51 | Decl::Use(path.into_iter() 52 | .map(|id| id.name.as_str().to_string()) 53 | .collect::>() 54 | .join("::")) 55 | }; 56 | 57 | Item: Item = { 58 | Rparen)?> => (id, ty) 59 | }; 60 | 61 | Type: Type = { 62 | Dot => Type::Var(box l, box r), 63 | TyAtom 64 | }; 65 | 66 | TyAtom: Type = { 67 | Binding Lt Gt => Type::Bind(id), 68 | Vec Lt Gt => Type::Vec(box ty), 69 | Ident => Type::Ident(<>), 70 | Lparen > Rparen => Type::Prod(tys), 71 | }; -------------------------------------------------------------------------------- /rabbot/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private, plugin_registrar, quote, box_syntax, box_patterns)] 2 | 3 | extern crate rustc; 4 | extern crate rustc_plugin; 5 | extern crate syntax; 6 | extern crate syntax_pos; 7 | 8 | pub mod token; 9 | pub mod grammar; 10 | pub mod ast; 11 | pub mod codegen; 12 | pub mod abt; 13 | pub mod var; 14 | 15 | #[macro_export] 16 | macro_rules! bind { 17 | ($variant:path { $args:tt } = $e:expr) => { 18 | let $args = match $e { 19 | $variant($args) => { $args }, 20 | _ => unreachable!() 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rabbot/src/token.rs: -------------------------------------------------------------------------------- 1 | use syntax::parse::token::{Token as RsToken}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub enum Token { 5 | RustToken(RsToken), 6 | Symbol, 7 | Abt, 8 | Enum, 9 | Binding, 10 | Use, 11 | Vec, 12 | } 13 | 14 | impl Token { 15 | pub fn from_rust_token(t: RsToken) -> Token { 16 | if let RsToken::Ident(ident) = t { 17 | let s = ident.name.as_str(); 18 | 19 | // No better way to go from InternedString -> &str? 20 | match unsafe { s.slice_unchecked(0, s.len()) } { 21 | "symbol" => Token::Symbol, 22 | "abt" => Token::Abt, 23 | "enum" => Token::Enum, 24 | "Binding" => Token::Binding, 25 | "use" => Token::Use, 26 | "Vec" => Token::Vec, 27 | _ => Token::RustToken(t) 28 | } 29 | } else { 30 | Token::RustToken(t) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rabbot/src/var.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fmt; 3 | 4 | #[derive(Eq, PartialEq, Clone, Hash)] 5 | pub struct Var { 6 | name: String 7 | } 8 | 9 | thread_local! { 10 | static COUNT: RefCell = RefCell::new(0); 11 | } 12 | 13 | impl Var { 14 | pub fn from_string(name: String) -> Var { 15 | Var { 16 | name: name 17 | } 18 | } 19 | 20 | pub fn new() -> Var { 21 | let name = 22 | COUNT.with(|c| { 23 | let s = format!("t{}", *c.borrow()); 24 | *c.borrow_mut() += 1; 25 | s 26 | }); 27 | Var::from_string(name) 28 | } 29 | 30 | pub fn to_string(&self) -> String { 31 | self.name.clone() 32 | } 33 | } 34 | 35 | impl fmt::Display for Var { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 | self.name.fmt(f) 38 | } 39 | } 40 | 41 | impl fmt::Debug for Var { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | self.name.fmt(f) 44 | } 45 | } 46 | --------------------------------------------------------------------------------