extern crate proc_macro;
use self::proc_macro::{Delimiter,Group,Ident,Punct,
Spacing,Span,TokenStream,TokenTree};
use std::iter::once;
enum Expect { Obj, Tilde, Method, MacroOrFunc, Macro }
struct State {
expect : Expect,
obj : Option<TokenStream>,
method : Option<TokenStream>,
acc : TokenStream,
}
fn opt_ts_extend( opt_ts: &mut Option<TokenStream>, tt: TokenTree ) {
if let Some( ref mut ts ) = opt_ts.as_mut() {
ts.extend( once( tt ));
} else {
*opt_ts = Some( TokenStream::from( tt ));
}
}
impl State {
fn new() -> Self {
State {
expect : Expect::Obj,
obj : None,
method : None,
acc : TokenStream::new(),
}
}
fn reset_obj( &mut self ) {
self.expect = Expect::Obj;
if let Some( obj ) = self.obj.take() {
self.acc.extend( obj );
}
}
fn acc_extend( &mut self, tt: TokenTree ) {
self.acc.extend( once( tt ));
}
fn obj_extend( &mut self, tt: TokenTree ) {
opt_ts_extend( &mut self.obj, tt );
}
fn method_extend( &mut self, tt: TokenTree ) {
opt_ts_extend( &mut self.method, tt );
}
fn next( &mut self ) {
self.expect = match self.expect {
Expect::Obj => Expect::Tilde,
Expect::Tilde => Expect::Method,
Expect::Method => Expect::MacroOrFunc,
Expect::Macro => Expect::Obj,
Expect::MacroOrFunc =>
panic!( "unexpected state in define_tilde" ),
};
}
}
trait TT {
fn tt( self ) -> TokenTree;
}
impl TT for char {
fn tt( self ) -> TokenTree {
TokenTree::Punct( Punct::new( self, Spacing::Alone ))
}
}
impl TT for &'static str {
fn tt( self ) -> TokenTree {
TokenTree::Ident( Ident::new( self, Span::call_site() ))
}
}
fn punct( s: &'static str ) -> impl Iterator<Item=TokenTree> {
s.chars().map( |ch|
TokenTree::Punct( Punct::new( ch, Spacing::Joint ))
)
}
fn define_tilde( input: TokenStream ) -> TokenStream {
let mut state = State::new();
for tt in input {
match state.expect {
Expect::Obj => match tt {
TokenTree::Punct( ref punct ) => match punct.as_char() {
'.' => state.next(),
':' => state.obj_extend( tt ),
_ => {
state.reset_obj();
state.acc_extend( tt );
},
},
TokenTree::Group( group ) => {
state.obj_extend( TokenTree::Group( Group::new(
group.delimiter(),
define_tilde( group.stream() ))));
},
_ => state.obj_extend( tt ),
},
Expect::Tilde => match tt {
TokenTree::Punct( ref punct ) if punct.as_char() == '~' => {
state.next();
},
TokenTree::Ident(_) => {
state.obj_extend( '.'.tt() );
state.obj_extend( tt );
state.expect = Expect::Obj;
},
_ => {
state.reset_obj();
state.acc_extend( '.'.tt() );
state.acc_extend( tt );
}
},
Expect::Method => match tt {
TokenTree::Ident(_) => {
state.method = Some( TokenStream::from( tt ));
state.next();
},
TokenTree::Group( group ) => { state.method_extend( TokenTree::Group( Group::new(
group.delimiter(),
define_tilde( group.stream() ))));
},
_ => {
state.reset_obj();
state.acc_extend( '.'.tt() );
state.acc_extend( '~'.tt() );
state.acc_extend( tt );
}
},
Expect::MacroOrFunc => match tt {
TokenTree::Punct( ref punct ) if punct.as_char() == '!' => {
state.expect = Expect::Macro;
},
TokenTree::Group( ref group )
if group.delimiter() == Delimiter::Parenthesis =>
{
define_func( &mut state, group );
state.expect = Expect::Obj;
},
_ => {
state.reset_obj();
let method = state.method.take()
.expect("The last tt should be some identity");
state.acc_extend( '.'.tt() );
state.acc_extend( '~'.tt() );
state.acc.extend( method );
state.acc_extend( tt );
},
},
Expect::Macro => {
define_macro( &mut state, tt );
state.next();
}
}
}
state.reset_obj();
state.acc
}
fn define_macro( state: &mut State, tt: TokenTree ) {
match tt {
TokenTree::Group( group ) => {
let mut prefixed = state.method.take()
.expect("The state should have got some method");
prefixed.extend( once( '!'.tt() ));
let self_ = "__tilde_postfix_macro_self__".tt();
let mut inner = TokenStream::from( self_.clone() );
let delimiter = group.delimiter();
if !group.stream().is_empty() {
let args = TokenTree::Group( Group::new(
delimiter,
define_tilde( group.stream() )
));
inner.extend( once( ','.tt() ));
inner.extend( once( args ));
}
let group = Group::new( delimiter, inner );
prefixed.extend( once( TokenTree::Group( group )));
let mut match_body = TokenStream::from( self_ );
match_body.extend( punct( "=>" ));
match_body.extend( prefixed );
let mut match_ = TokenStream::from( "match".tt() );
match_.extend( state.obj.take()
.expect("The state should have got some obj"));
match_.extend( once( TokenTree::Group(
Group::new( Delimiter::Brace, match_body ))));
state.obj = Some( match_ );
},
_ => {
state.reset_obj();
let method = state.method.take()
.expect("The state should have got some method!");
state.acc_extend( '.'.tt() );
state.acc_extend( '~'.tt() );
state.acc.extend( method );
state.acc_extend( tt );
}
}
}
fn define_func( state: &mut State, group: &Group ) {
let mut inner = state.obj.take()
.expect("The state should have got some obj");
let delimiter = group.delimiter();
if !group.stream().is_empty() {
let args = TokenTree::Group( Group::new(
delimiter,
define_tilde( group.stream() )
));
inner.extend( once( ','.tt() ));
inner.extend( once( args ));
}
let group = Group::new( delimiter, inner );
let mut call = state.method.take()
.expect("The state should have got some method");
call.extend( once( TokenTree::Group( group )));
state.obj = Some( call );
}
#[proc_macro]
pub fn tilde( input: TokenStream ) -> TokenStream {
define_tilde( input )
}