// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(unused_parens)] #![feature(plugin)] #![feature(plugin_registrar)] #![feature(rustc_private)] #![plugin(proc_macro_plugin)] extern crate rustc_plugin; extern crate proc_macro_tokens; extern crate syntax; use proc_macro_tokens::prelude::*; use rustc_plugin::Registry; use syntax::ast::Ident; use syntax::codemap::{DUMMY_SP, Span}; use syntax::ext::proc_macro_shim::build_block_emitter; use syntax::ext::base::{ExtCtxt, MacResult}; use syntax::parse::token::{self, Token, DelimToken}; use syntax::tokenstream::{TokenTree, TokenStream}; #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { reg.register_macro("cond", cond); } fn cond<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box { let output = cond_rec(TokenStream::from_tts(tts.clone().to_owned())); build_block_emitter(cx, sp, output) } fn cond_rec(input: TokenStream) -> TokenStream { if input.is_empty() { return qquote!(); } let next = input.slice(0..1); let rest = input.slice_from(1..); let clause : TokenStream = match next.maybe_delimited() { Some(ts) => ts, _ => panic!("Invalid input"), }; // clause is ([test]) [rhs] if clause.len() < 2 { panic!("Invalid macro usage in cond: {:?}", clause) } let test: TokenStream = clause.slice(0..1); let rhs: TokenStream = clause.slice_from(1..); if ident_eq(&test[0], Ident::from_str("else")) || rest.is_empty() { qquote!({unquote(rhs)}) } else { qquote!({if unquote(test) { unquote(rhs) } else { cond!(unquote(rest)) } }) } }