syntax: Permit splicing anything into a quote that implements ToTokens.

This commit is contained in:
Graydon Hoare 2012-12-06 16:18:41 -08:00
parent 7e9872058e
commit 70e0c6e27d
2 changed files with 192 additions and 10 deletions

View File

@ -19,10 +19,10 @@ use token::*;
*
* Quasiquoting works via token trees.
*
* This is registered as a set of expression syntax extension called quote! that lifts
* its argument token-tree to an AST representing the construction of the same
* token tree, with ast::tt_nonterminal nodes interpreted as antiquotes
* (splices).
* This is registered as a set of expression syntax extension called quote!
* that lifts its argument token-tree to an AST representing the
* construction of the same token tree, with ast::tt_nonterminal nodes
* interpreted as antiquotes (splices).
*
*/
@ -32,6 +32,176 @@ pub mod rt {
pub use parse::new_parser_from_tts;
pub use codemap::BytePos;
pub use codemap::span;
use print::pprust;
use pprust::{item_to_str, ty_to_str};
trait ToTokens {
pub fn to_tokens(_cx: ext_ctxt) -> ~[token_tree];
}
impl ~[token_tree]: ToTokens {
pub fn to_tokens(_cx: ext_ctxt) -> ~[token_tree] {
copy self
}
}
/* Should be (when bugs in default methods are fixed):
trait ToSource : ToTokens {
// Takes a thing and generates a string containing rust code for it.
pub fn to_source(cx: ext_ctxt) -> ~str;
// If you can make source, you can definitely make tokens.
pub fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
*/
trait ToSource {
// Takes a thing and generates a string containing rust code for it.
pub fn to_source(cx: ext_ctxt) -> ~str;
}
impl ast::ident: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
copy *cx.parse_sess().interner.get(self)
}
}
impl @ast::item: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
item_to_str(self, cx.parse_sess().interner)
}
}
impl ~[@ast::item]: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
str::connect(self.map(|i| i.to_source(cx)), ~"\n\n")
}
}
impl @ast::Ty: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
ty_to_str(self, cx.parse_sess().interner)
}
}
impl ~[@ast::Ty]: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
str::connect(self.map(|i| i.to_source(cx)), ~", ")
}
}
impl ~[ast::ty_param]: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
pprust::typarams_to_str(self, cx.parse_sess().interner)
}
}
impl @ast::expr: ToSource {
fn to_source(cx: ext_ctxt) -> ~str {
pprust::expr_to_str(self, cx.parse_sess().interner)
}
}
// Alas ... we write these out instead. All redundant.
impl ast::ident: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
impl @ast::item: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
impl ~[@ast::item]: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
impl @ast::Ty: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
impl ~[@ast::Ty]: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
impl ~[ast::ty_param]: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
impl @ast::expr: ToTokens {
fn to_tokens(cx: ext_ctxt) -> ~[token_tree] {
cx.parse_tts(self.to_source(cx))
}
}
trait ExtParseUtils {
fn parse_item(s: ~str) -> @ast::item;
fn parse_expr(s: ~str) -> @ast::expr;
fn parse_stmt(s: ~str) -> @ast::stmt;
fn parse_tts(s: ~str) -> ~[ast::token_tree];
}
impl ext_ctxt: ExtParseUtils {
fn parse_item(s: ~str) -> @ast::item {
let res = parse::parse_item_from_source_str(
~"<quote expansion>",
@(copy s),
self.cfg(),
~[],
self.parse_sess());
match res {
Some(ast) => ast,
None => {
error!("Parse error with ```\n%s\n```", s);
fail
}
}
}
fn parse_stmt(s: ~str) -> @ast::stmt {
parse::parse_stmt_from_source_str(
~"<quote expansion>",
@(copy s),
self.cfg(),
~[],
self.parse_sess())
}
fn parse_expr(s: ~str) -> @ast::expr {
parse::parse_expr_from_source_str(
~"<quote expansion>",
@(copy s),
self.cfg(),
self.parse_sess())
}
fn parse_tts(s: ~str) -> ~[ast::token_tree] {
parse::parse_tts_from_source_str(
~"<quote expansion>",
@(copy s),
self.cfg(),
self.parse_sess())
}
}
}
pub fn expand_quote_tokens(cx: ext_ctxt,
@ -288,14 +458,27 @@ fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree)
ast::tt_seq(*) => fail ~"tt_seq in quote!",
ast::tt_nonterminal(sp, ident) => {
// tt.push_all_move($ident.to_tokens(ext_cx))
let e_to_toks =
build::mk_call_(cx, sp,
build::mk_access
(cx, sp,
~[ident],
id_ext(cx, ~"to_tokens")),
~[build::mk_path(cx, sp,
ids_ext(cx, ~[~"ext_cx"]))]);
let e_push =
build::mk_call_(cx, sp,
build::mk_access
(cx, sp,
ids_ext(cx, ~[~"tt"]),
id_ext(cx, ~"push_all_move")),
~[build::mk_path(cx, sp, ~[ident])]);
~[build::mk_stmt(cx, sp, e_push)]
~[e_to_toks]);
~[build::mk_stmt(cx, sp, e_push)]
}
}
}
@ -367,7 +550,7 @@ fn expand_tts(cx: ext_ctxt,
let stmt_let_sp = build::mk_local(cx, sp, false,
id_ext(cx, ~"sp"),
e_sp);
let stmt_let_tt = build::mk_local(cx, sp, true,
id_ext(cx, ~"tt"),
build::mk_uniq_vec_e(cx, sp, ~[]));

View File

@ -8,14 +8,13 @@ fn syntax_extension(ext_cx: @ext_ctxt) {
let e_toks : ~[syntax::ast::token_tree] = quote_tokens!(1 + 2);
let p_toks : ~[syntax::ast::token_tree] = quote_tokens!((x, 1 .. 4, *));
let _a: @syntax::ast::expr = quote_expr!(1 + 2);
let a: @syntax::ast::expr = quote_expr!(1 + 2);
let _b: Option<@syntax::ast::item> = quote_item!( const foo : int = $e_toks; );
let _c: @syntax::ast::pat = quote_pat!( (x, 1 .. 4, *) );
let _d: @syntax::ast::stmt = quote_stmt!( let x = $e_toks; );
let _d: @syntax::ast::stmt = quote_stmt!( let x = $a; );
let _e: @syntax::ast::expr = quote_expr!( match foo { $p_toks => 10 } );
}
fn main() {
let _x: ~[syntax::ast::token_tree] = quote_tokens!(a::Foo::foo());
}