use syntax::ast::{Expr, Lit, TokenTree, TtToken}; use syntax::ext::base::ExtCtxt; use syntax::parse; use syntax::parse::token; use syntax::ptr::P; #[derive(Show)] pub enum Markup { Element(Vec<(String, Value)>, Vec<Markup>), Value(Value), } #[derive(Show)] pub struct Value { pub value: Value_, pub escape: Escape, } impl Value { pub fn escape(value: Value_) -> Value { Value { value: value, escape: Escape::Escape, } } pub fn no_escape(value: Value_) -> Value { Value { value: value, escape: Escape::NoEscape, } } } #[derive(Show)] pub enum Value_ { Literal(String), Splice(P<Expr>), } #[derive(Copy, PartialEq, Show)] pub enum Escape { NoEscape, Escape, } pub fn parse(cx: &mut ExtCtxt, mut args: &[TokenTree]) -> Option<Vec<Markup>> { macro_rules! semi { () => (TtToken(_, token::Semi)) } macro_rules! minus { () => (TtToken(_, token::BinOp(token::Minus))) } macro_rules! literal { () => (TtToken(_, token::Literal(..))) } let mut result = vec![]; loop { match match args { [semi!(), ..] => { args.shift(1); continue }, [minus!(), ref tt @ literal!(), ..] => { args.shift(2); parse_literal(cx, tt, true) }, [ref tt @ literal!(), ..] => { args.shift(1); parse_literal(cx, tt, false) }, _ => None, } { Some(x) => result.push(x), None => break, } } match args { [] => Some(result), [ref tt, ..] => { cx.span_err(tt.get_span(), "invalid syntax"); None } } } fn parse_literal(cx: &mut ExtCtxt, tt: &TokenTree, minus: bool) -> Option<Markup> { let mut parser = parse::tts_to_parser(cx.parse_sess, vec![tt.clone()], cx.cfg.clone()); let lit = parser.parse_lit(); lit_to_string(cx, lit, minus) .map(|s| Markup::Value(Value::escape(Value_::Literal(s)))) } fn lit_to_string(cx: &mut ExtCtxt, lit: Lit, minus: bool) -> Option<String> { use syntax::ast::Lit_::*; let mut result = String::new(); if minus { result.push('-'); } match lit.node { LitStr(s, _) => result.push_str(s.get()), LitBinary(..) | LitByte(..) => { cx.span_err(lit.span, "cannot splice binary data"); return None; }, LitChar(c) => result.push(c), LitInt(x, _) => result.push_str(&*x.to_string()), LitFloat(s, _) | LitFloatUnsuffixed(s) => result.push_str(s.get()), LitBool(b) => result.push_str(if b { "true" } else { "false" }), }; Some(result) } trait Shift { fn shift(&mut self, n: uint); } impl<'a, T> Shift for &'a [T] { fn shift(&mut self, n: uint) { *self = self.slice_from(n); } }