Skip to content

Instantly share code, notes, and snippets.

@erickt
Created January 11, 2015 17:53
Show Gist options
  • Save erickt/d779354e851980d5a6f4 to your computer and use it in GitHub Desktop.
Save erickt/d779354e851980d5a6f4 to your computer and use it in GitHub Desktop.
#![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