Scaffold syntax extension thing
This commit is contained in:
parent
34031b2f95
commit
6a4709315b
4 changed files with 153 additions and 16 deletions
|
@ -1,29 +1,29 @@
|
|||
#![crate_type = "dylib"]
|
||||
#![feature(plugin_registrar)]
|
||||
#![feature(globs, plugin_registrar, quote, macro_rules)]
|
||||
|
||||
extern crate syntax;
|
||||
extern crate rustc;
|
||||
|
||||
use syntax::ast::{Ident, TokenTree};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ext::base::{DummyResult, ExtCtxt, IdentTT, MacItems, MacResult};
|
||||
use syntax::parse::token;
|
||||
use syntax::ast::{TokenTree, TtToken};
|
||||
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacExpr};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use rustc::plugin::Registry;
|
||||
|
||||
fn expand_html(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult + 'static> {
|
||||
let s = match args {
|
||||
[TtToken(_, token::Ident(s, _))] => token::get_ident(s),
|
||||
_ => {
|
||||
cx.span_err(sp, "argument should be a single identifier");
|
||||
return DummyResult::any(sp);
|
||||
},
|
||||
};
|
||||
mod parse;
|
||||
mod render;
|
||||
|
||||
MacExpr::new(cx.expr_str(sp, s))
|
||||
fn expand_html<'cx>(cx: &'cx mut ExtCtxt, sp: Span, ident: Ident, args: Vec<TokenTree>) -> Box<MacResult + 'cx> {
|
||||
match parse::parse(cx, &*args) {
|
||||
Some(markups) => {
|
||||
let item = render::render(cx, ident, &*markups);
|
||||
MacItems::new(item.into_iter())
|
||||
},
|
||||
None => DummyResult::any(sp),
|
||||
}
|
||||
}
|
||||
|
||||
#[plugin_registrar]
|
||||
pub fn plugin_registrar(reg: &mut Registry) {
|
||||
reg.register_macro("html", expand_html);
|
||||
reg.register_syntax_extension(token::intern("html"), IdentTT(box expand_html, None));
|
||||
}
|
||||
|
|
101
htmlthing_macros/src/parse.rs
Normal file
101
htmlthing_macros/src/parse.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
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);
|
||||
}
|
||||
}
|
33
htmlthing_macros/src/render.rs
Normal file
33
htmlthing_macros/src/render.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use syntax::ast::{Ident, Item, Stmt};
|
||||
use syntax::ext::base::ExtCtxt;
|
||||
use syntax::parse::token;
|
||||
use syntax::ptr::P;
|
||||
|
||||
use super::parse::Markup;
|
||||
|
||||
pub fn render(cx: &mut ExtCtxt, ident: Ident, markups: &[Markup]) -> Option<P<Item>> {
|
||||
let w = Ident::new(token::intern("w"));
|
||||
let mut stmts = vec![];
|
||||
for markup in markups.iter() {
|
||||
render_markup(cx, markup, w, &mut stmts);
|
||||
}
|
||||
quote_item!(cx,
|
||||
fn $ident<W: ::std::io::Writer>($w: &mut W) -> ::std::io::IoResult<()> {
|
||||
$stmts;
|
||||
Ok(())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn render_markup(cx: &mut ExtCtxt, markup: &Markup, w: Ident, out: &mut Vec<P<Stmt>>) {
|
||||
use super::parse::Markup::*;
|
||||
use super::parse::Value::*;
|
||||
match *markup {
|
||||
Empty => {},
|
||||
Element(..) => unimplemented!(),
|
||||
Value(Literal(ref s)) => {
|
||||
out.push(quote_stmt!(cx, try!($w.write_str($s))));
|
||||
},
|
||||
Value(Splice(_)) => unimplemented!(),
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@ extern crate htmlthing;
|
|||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let s = html!(ducks);
|
||||
assert_eq!(s, "ducks");
|
||||
let mut buf = vec![];
|
||||
html! test_template("du\tcks" -23 3.14 '\n' "geese");
|
||||
test_template(&mut buf).unwrap();
|
||||
let s = String::from_utf8(buf).unwrap();
|
||||
assert_eq!(&*s, "du\tcks-233.14\ngeese");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue