Created
January 11, 2015 17:53
-
-
Save erickt/d779354e851980d5a6f4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(unboxed_closures, default_type_params)] | |
use std::collections::HashMap; | |
use std::cell::RefCell; | |
use std::thunk::Invoke; | |
#[deriving(Copy, Clone, Show)] | |
struct Ident(uint); | |
#[deriving(Show)] | |
struct Arg { | |
name: Ident, | |
ty: Ty, | |
} | |
#[deriving(Show)] | |
struct FnDecl { | |
inputs: Vec<Arg>, | |
output: FunctionReturnTy, | |
variadic: bool, | |
} | |
#[deriving(Show)] | |
enum FunctionReturnTy { | |
NoReturn, | |
Return(Ty), | |
} | |
#[deriving(Show)] | |
enum Ty { | |
Int, | |
Tuple(Vec<Ty>), | |
} | |
#[deriving(Show)] | |
struct Item { | |
name: Ident, | |
node: Item_, | |
} | |
#[deriving(Show)] | |
enum Item_ { | |
Fn(FnDecl, Block), | |
} | |
#[deriving(Show)] | |
struct Block { | |
stmts: Vec<Stmt>, | |
expr: Option<Expr> | |
} | |
#[deriving(Show)] | |
enum Expr { | |
Path(Ident), | |
Int(int), | |
Binop(Binop, Box<Expr>, Box<Expr>), | |
Tuple(Vec<Expr>), | |
} | |
#[deriving(Show)] | |
enum Binop { | |
Add, | |
} | |
#[deriving(Show)] | |
enum Stmt { | |
Decl(Decl), | |
Expr(Expr), | |
} | |
#[deriving(Show)] | |
enum Decl { | |
Local(Ident, Option<Expr>), | |
Item(Item), | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
trait ToIdent { | |
fn to_ident(&self, ctx: &Ctx) -> Ident; | |
} | |
impl ToIdent for Ident { | |
fn to_ident(&self, _ctx: &Ctx) -> Ident { | |
*self | |
} | |
} | |
impl<'a> ToIdent for &'a str { | |
fn to_ident(&self, ctx: &Ctx) -> Ident { | |
ctx.intern(*self) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct Ctx { | |
ident_map: RefCell<HashMap<String, Ident>>, | |
idents: RefCell<Vec<String>>, | |
} | |
impl Ctx { | |
fn new() -> Ctx { | |
Ctx { | |
ident_map: RefCell::new(HashMap::new()), | |
idents: RefCell::new(Vec::new()), | |
} | |
} | |
fn intern(&self, name: &str) -> Ident { | |
let mut map = self.ident_map.borrow_mut(); | |
match map.get(name) { | |
Some(ident) => { return *ident; } | |
None => {} | |
} | |
let mut idents = self.idents.borrow_mut(); | |
let ident = Ident(idents.len()); | |
idents.push(name.to_string()); | |
map.insert(name.to_string(), ident); | |
ident | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
trait Builder<T> { | |
fn build(self) -> T; | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
impl Arg { | |
fn builder<I: ToIdent>(ctx: &Ctx, name: I) -> ArgBuilder<Arg> { | |
ArgBuilder { | |
ctx: ctx, | |
name: name.to_ident(ctx), | |
callback: box |: arg| arg, | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct ArgBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
name: Ident, | |
callback: Box<Invoke<Arg, T> + 'a>, | |
} | |
impl<'a, T> ArgBuilder<'a, T> { | |
fn ty(self) -> TyBuilder<'a, T> { | |
TyBuilder { | |
ctx: self.ctx, | |
callback: box move |: ty| { | |
let arg = Arg { name: self.name, ty: ty }; | |
self.callback.invoke(arg) | |
} | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct FnDeclBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
inputs: Vec<Arg>, | |
variadic: bool, | |
callback: Box<Invoke<FnDecl, T> + 'a>, | |
} | |
impl FnDecl { | |
fn builder(ctx: &Ctx) -> FnDeclBuilder<FnDecl> { | |
FnDecl::builder_with_callback(ctx, box |: fn_decl| fn_decl) | |
} | |
fn builder_with_callback<'a, T>( | |
ctx: &'a Ctx, | |
callback: Box<Invoke<FnDecl, T> + 'a> | |
) -> FnDeclBuilder<'a, T> { | |
FnDeclBuilder { | |
ctx: ctx, | |
inputs: Vec::new(), | |
variadic: false, | |
callback: callback, | |
} | |
} | |
} | |
impl<'a, T> FnDeclBuilder<'a, T> { | |
pub fn variadic(mut self) -> Self { | |
self.variadic = true; | |
self | |
} | |
pub fn arg<I: ToIdent>(mut self, name: I) -> ArgBuilder<'a, Self> { | |
ArgBuilder { | |
ctx: self.ctx, | |
name: name.to_ident(self.ctx), | |
callback: box move |: arg| { | |
self.inputs.push(arg); | |
self | |
} | |
} | |
} | |
pub fn no_return(self) -> T { | |
self.callback.invoke(FnDecl { | |
inputs: self.inputs, | |
output: FunctionReturnTy::NoReturn, | |
variadic: self.variadic, | |
}) | |
} | |
pub fn output(self) -> TyBuilder<'a, T> { | |
TyBuilder { | |
ctx: self.ctx, | |
callback: box move |: ty| { | |
self.callback.invoke(FnDecl { | |
inputs: self.inputs, | |
output: FunctionReturnTy::Return(ty), | |
variadic: self.variadic, | |
}) | |
} | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct TyBuilder<'a, T> { | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Ty, T> + 'a>, | |
} | |
impl Ty { | |
fn builder(ctx: &Ctx) -> TyBuilder<Ty> { | |
Ty::builder_with_callback(ctx, box |: ty| ty) | |
} | |
fn builder_with_callback<'a, T>( | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Ty, T> + 'a>, | |
) -> TyBuilder<T> { | |
TyBuilder { | |
ctx: ctx, | |
callback: callback, | |
} | |
} | |
} | |
impl<'a, T> TyBuilder<'a, T> { | |
fn int(self) -> T { | |
self.callback.invoke(Ty::Int) | |
} | |
fn tuple(self) -> TyTupleBuilder<'a, T> { | |
TyTupleBuilder { | |
ctx: self.ctx, | |
tys: Vec::new(), | |
callback: self.callback, | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct TyTupleBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
tys: Vec<Ty>, | |
callback: Box<Invoke<Ty, T> + 'a>, | |
} | |
impl<'a, T> TyTupleBuilder<'a, T> { | |
pub fn ty(mut self) -> TyBuilder<'a, Self> { | |
Ty::builder_with_callback(self.ctx, box move |: ty| { | |
self.tys.push(ty); | |
self | |
}) | |
} | |
pub fn build(self) -> T { | |
self.callback.invoke(Ty::Tuple(self.tys)) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct ItemBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
name: Ident, | |
callback: Box<Invoke<Item, T> + 'a>, | |
} | |
impl Item { | |
fn builder<I: ToIdent>(ctx: &Ctx, name: I) -> ItemBuilder<Item> { | |
Item::builder_with_callback(ctx, name, box |: item| item) | |
} | |
fn builder_with_callback<'a, I: ToIdent, T>( | |
ctx: &'a Ctx, | |
name: I, | |
callback: Box<Invoke<Item, T> + 'a>, | |
) -> ItemBuilder<T> { | |
ItemBuilder { | |
ctx: ctx, | |
name: name.to_ident(ctx), | |
callback: callback, | |
} | |
} | |
} | |
impl<'a, T> ItemBuilder<'a, T> { | |
pub fn fn_(self) -> FnDeclBuilder<'a, ItemFnBuilder<'a, T>> { | |
FnDecl::builder_with_callback(self.ctx, box move |: fn_decl| { | |
ItemFnBuilder { | |
ctx: self.ctx, | |
name: self.name, | |
fn_decl: fn_decl, | |
callback: self.callback, | |
} | |
}) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct ItemFnBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
name: Ident, | |
fn_decl: FnDecl, | |
callback: Box<Invoke<Item, T> + 'a>, | |
} | |
impl<'a, T> ItemFnBuilder<'a, T> { | |
fn block(self) -> BlockBuilder<'a, T> { | |
Block::builder_with_callback(self.ctx, box move |: block| { | |
self.callback.invoke(Item { | |
name: self.name, | |
node: Item_::Fn(self.fn_decl, block), | |
}) | |
}) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct ExprBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Expr, T> + 'a>, | |
} | |
impl Expr { | |
fn builder(ctx: &Ctx) -> ExprBuilder<Expr> { | |
Expr::builder_with_callback(ctx, box |: expr| expr) | |
} | |
fn builder_with_callback<'a, T>( | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Expr, T> + 'a>, | |
) -> ExprBuilder<T> { | |
ExprBuilder { | |
ctx: ctx, | |
callback: callback, | |
} | |
} | |
} | |
impl<'a, T> ExprBuilder<'a, T> { | |
fn path<I: ToIdent>(self, name: I) -> T { | |
self.callback.invoke(Expr::Path(name.to_ident(self.ctx))) | |
} | |
fn int(self, value: int) -> T { | |
self.callback.invoke(Expr::Int(value)) | |
} | |
fn add(self) -> ExprBuilder<'a, ExprBuilder<'a, T>> { | |
self.binop(Binop::Add) | |
} | |
fn binop(self, binop: Binop) -> ExprBuilder<'a, ExprBuilder<'a, T>> { | |
Expr::builder_with_callback(self.ctx, box move |: lhs| { | |
Expr::builder_with_callback(self.ctx, box move |: rhs| { | |
self.callback.invoke(Expr::Binop(binop, box lhs, box rhs)) | |
}) | |
}) | |
} | |
fn tuple(self) -> ExprTupleBuilder<'a, T> { | |
ExprTupleBuilder { | |
ctx: self.ctx, | |
exprs: Vec::new(), | |
callback: self.callback, | |
} | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct ExprTupleBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
exprs: Vec<Expr>, | |
callback: Box<Invoke<Expr, T> + 'a>, | |
} | |
impl<'a, T> ExprTupleBuilder<'a, T> { | |
pub fn expr(mut self) -> ExprBuilder<'a, Self> { | |
Expr::builder_with_callback(self.ctx, box move |: expr| { | |
self.exprs.push(expr); | |
self | |
}) | |
} | |
pub fn build(self) -> T { | |
self.callback.invoke(Expr::Tuple(self.exprs)) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct StmtBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Stmt, T> + 'a>, | |
} | |
impl Stmt { | |
fn builder(ctx: &Ctx) -> StmtBuilder<Stmt> { | |
Stmt::builder_with_callback(ctx, box |: expr| expr) | |
} | |
fn builder_with_callback<'a, T>( | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Stmt, T> + 'a>, | |
) -> StmtBuilder<T> { | |
StmtBuilder { | |
ctx: ctx, | |
callback: callback, | |
} | |
} | |
} | |
impl<'a, T> StmtBuilder<'a, T> { | |
fn let_no_expr<I: ToIdent>(self, name: I) -> T { | |
let decl = Decl::Local(name.to_ident(self.ctx), None); | |
self.callback.invoke(Stmt::Decl(decl)) | |
} | |
fn let_<I: ToIdent>(self, name: I) -> ExprBuilder<'a, T> { | |
let name = name.to_ident(self.ctx); | |
Expr::builder_with_callback(self.ctx, box move |: expr| { | |
let decl = Decl::Local(name, Some(expr)); | |
self.callback.invoke(Stmt::Decl(decl)) | |
}) | |
} | |
fn item<I: ToIdent>(self, name: I) -> ItemBuilder<'a, T> { | |
Item::builder_with_callback(self.ctx, name, box move |: item| { | |
let decl = Decl::Item(item); | |
self.callback.invoke(Stmt::Decl(decl)) | |
}) | |
} | |
fn expr(self) -> ExprBuilder<'a, T> { | |
Expr::builder_with_callback(self.ctx, box move |: expr| { | |
self.callback.invoke(Stmt::Expr(expr)) | |
}) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct BlockBuilder<'a, T: 'a> { | |
ctx: &'a Ctx, | |
stmts: Vec<Stmt>, | |
callback: Box<Invoke<Block, T> + 'a>, | |
} | |
impl Block { | |
fn builder(ctx: &Ctx) -> BlockBuilder<Block> { | |
Block::builder_with_callback(ctx, box |: expr| expr) | |
} | |
fn builder_with_callback<'a, T>( | |
ctx: &'a Ctx, | |
callback: Box<Invoke<Block, T> + 'a>, | |
) -> BlockBuilder<T> { | |
BlockBuilder { | |
ctx: ctx, | |
stmts: Vec::new(), | |
callback: callback, | |
} | |
} | |
} | |
impl<'a, T> BlockBuilder<'a, T> { | |
pub fn stmt(mut self) -> StmtBuilder<'a, Self> { | |
Stmt::builder_with_callback(self.ctx, box move |: stmt| { | |
self.stmts.push(stmt); | |
self | |
}) | |
} | |
fn expr(self) -> ExprBuilder<'a, T> { | |
Expr::builder_with_callback(self.ctx, box move |: expr| { | |
self.callback.invoke(Block { | |
stmts: self.stmts, | |
expr: Some(expr), | |
}) | |
}) | |
} | |
fn build(self) -> T { | |
self.callback.invoke(Block { | |
stmts: self.stmts, | |
expr: None, | |
}) | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
struct AstBuilder<'a> { | |
ctx: &'a Ctx, | |
} | |
impl<'a> AstBuilder<'a> { | |
fn new(ctx: &Ctx) -> AstBuilder { | |
AstBuilder { | |
ctx: ctx, | |
} | |
} | |
fn ty(&self) -> TyBuilder<Ty> { | |
Ty::builder(self.ctx) | |
} | |
fn arg<I: ToIdent>(&self, name: I) -> ArgBuilder<Arg> { | |
Arg::builder(self.ctx, name) | |
} | |
fn fn_decl(&self) -> FnDeclBuilder<FnDecl> { | |
FnDecl::builder(self.ctx) | |
} | |
fn item<I: ToIdent>(&self, name: I) -> ItemBuilder<Item> { | |
Item::builder(self.ctx, name) | |
} | |
fn fn_<I: ToIdent>(&self, name: I) -> FnDeclBuilder<ItemFnBuilder<Item>> { | |
Item::builder(self.ctx, name).fn_() | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
fn main() { | |
let ctx = Ctx::new(); | |
let builder = AstBuilder::new(&ctx); | |
let ty = builder.ty().int(); | |
println!("ty: {}", ty); | |
let ty = builder.ty() | |
.tuple() | |
.ty().int() | |
.ty().tuple() | |
.ty().tuple().build() | |
.ty().int() | |
.build() | |
.build(); | |
println!("ty: {}", ty); | |
let arg = builder.arg("an_argument").ty().int(); | |
println!("arg: {}", arg); | |
let fn_decl = builder.fn_decl() | |
.arg("an_int").ty().int() | |
.arg("a_tuple").ty().tuple() | |
.ty().int() | |
.ty().tuple().build() | |
.build() | |
.output().int(); | |
println!("fn_decl: {}", fn_decl); | |
let fn_ = builder.fn_("a_fn") | |
.output().int() | |
.block() | |
.stmt() | |
.expr().int(5) | |
.build(); | |
println!("fn: {}", fn_); | |
let fn_ = builder.fn_("a_fn") | |
.output().int() | |
.block() | |
.stmt().let_("x").int(5) | |
.stmt().let_("y").int(6) | |
.expr().add() | |
.path("x") | |
.path("y"); | |
println!("fn: {}", fn_); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment