maud/maud_macros/src/parse.rs

118 lines
2.7 KiB
Rust
Raw Normal View History

2014-12-18 18:57:55 +13:00
use syntax::ast::{Expr, Lit, TokenTree, TtToken};
use syntax::ext::base::ExtCtxt;
use syntax::parse;
use syntax::parse::token;
use syntax::ptr::P;
#[deriving(Show)]
pub enum Markup {
Element(Vec<(String, Value)>, Vec<Markup>),
Value(Value),
}
#[deriving(Show)]
2014-12-19 11:53:40 +13:00
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,
}
}
}
#[deriving(Show)]
pub enum Value_ {
2014-12-18 18:57:55 +13:00
Literal(String),
Splice(P<Expr>),
}
2014-12-19 11:53:40 +13:00
#[deriving(Copy, PartialEq, Show)]
pub enum Escape {
NoEscape,
Escape,
}
macro_rules! minus(
() => (TtToken(_, token::BinOp(token::Minus)))
)
macro_rules! literal(
() => (TtToken(_, token::Literal(..)))
)
2014-12-18 18:57:55 +13:00
pub fn parse(cx: &mut ExtCtxt, mut args: &[TokenTree]) -> Option<Vec<Markup>> {
let mut result = vec![];
loop {
match match args {
[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,
2014-12-18 18:57:55 +13:00
}
}
match args {
[] => Some(result),
[ref tt, ..] => {
cx.span_err(tt.get_span(), "invalid syntax");
None
2014-12-18 18:57:55 +13:00
}
}
}
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))))
2014-12-18 18:57:55 +13:00
}
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);
}
}