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"]
|
#![crate_type = "dylib"]
|
||||||
#![feature(plugin_registrar)]
|
#![feature(globs, plugin_registrar, quote, macro_rules)]
|
||||||
|
|
||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
extern crate rustc;
|
extern crate rustc;
|
||||||
|
|
||||||
|
use syntax::ast::{Ident, TokenTree};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
|
use syntax::ext::base::{DummyResult, ExtCtxt, IdentTT, MacItems, MacResult};
|
||||||
use syntax::parse::token;
|
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;
|
use rustc::plugin::Registry;
|
||||||
|
|
||||||
fn expand_html(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult + 'static> {
|
mod parse;
|
||||||
let s = match args {
|
mod render;
|
||||||
[TtToken(_, token::Ident(s, _))] => token::get_ident(s),
|
|
||||||
_ => {
|
|
||||||
cx.span_err(sp, "argument should be a single identifier");
|
|
||||||
return DummyResult::any(sp);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
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]
|
#[plugin_registrar]
|
||||||
pub fn plugin_registrar(reg: &mut Registry) {
|
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]
|
#[test]
|
||||||
fn it_works() {
|
fn it_works() {
|
||||||
let s = html!(ducks);
|
let mut buf = vec![];
|
||||||
assert_eq!(s, "ducks");
|
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