maud/htmlthing_macros/src/parse.rs
2014-12-18 18:57:55 +13:00

101 lines
2.5 KiB
Rust

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 {
Empty,
Element(Vec<(String, Value)>, Vec<Markup>),
Value(Value),
}
#[deriving(Show)]
pub enum Value {
Literal(String),
Splice(P<Expr>),
}
pub fn parse(cx: &mut ExtCtxt, mut args: &[TokenTree]) -> Option<Vec<Markup>> {
use self::Markup::*;
let mut result = vec![];
loop {
match parse_markup(cx, &mut args) {
Empty => break,
markup => result.push(markup),
}
}
// If not all tokens were consumed, then there must have been an
// error somewhere
match args {
[] => Some(result),
_ => None,
}
}
fn parse_markup(cx: &mut ExtCtxt, args: &mut &[TokenTree]) -> Markup {
use self::Markup::*;
use self::Value::*;
if let Some(s) = parse_literal(cx, args) {
Value(Literal(s))
} else {
match *args {
[] => Empty,
[ref tt, ..] => {
cx.span_err(tt.get_span(), "invalid syntax");
Empty
},
}
}
}
fn parse_literal(cx: &mut ExtCtxt, args: &mut &[TokenTree]) -> Option<String> {
let minus = match *args {
[TtToken(_, token::BinOp(token::Minus)), ..] => {
args.shift(1);
true
},
_ => false,
};
match *args {
[ref tt @ TtToken(_, token::Literal(..)), ..] => {
args.shift(1);
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)
},
_ => None,
}
}
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);
}
}