Make it possible to expand stmt macros.

This commit is contained in:
Paul Stansifer 2012-11-19 20:31:22 -05:00 committed by Graydon Hoare
parent ee076f63f9
commit cf26a7d7b9
3 changed files with 72 additions and 17 deletions

View File

@ -44,7 +44,7 @@
enum mac_result {
mr_expr(@ast::expr),
mr_item(@ast::item),
mr_expr_or_item(fn@()-> @ast::expr, fn@()-> Option<@ast::item>),
mr_any(fn@()-> @ast::expr, fn@()-> Option<@ast::item>, fn@()->@ast::stmt),
mr_def(macro_def)
}
@ -109,18 +109,18 @@ fn builtin_item_tt(f: syntax_expander_tt_item_) -> syntax_extension {
ext::deriving::expand_deriving_iter_bytes));
// Quasi-quoting expanders
syntax_expanders.insert(~"quote_tokens",
builtin_expr_tt(ext::quote::expand_quote_tokens));
syntax_expanders.insert(
~"quote_tokens", builtin_normal_tt(ext::quote::expand_quote_tokens));
syntax_expanders.insert(~"quote_expr",
builtin_expr_tt(ext::quote::expand_quote_expr));
builtin_normal_tt(ext::quote::expand_quote_expr));
syntax_expanders.insert(~"quote_type",
builtin_expr_tt(ext::quote::expand_quote_type));
builtin_normal_tt(ext::quote::expand_quote_type));
syntax_expanders.insert(~"quote_item",
builtin_expr_tt(ext::quote::expand_quote_item));
builtin_normal_tt(ext::quote::expand_quote_item));
syntax_expanders.insert(~"quote_pat",
builtin_expr_tt(ext::quote::expand_quote_pat));
builtin_normal_tt(ext::quote::expand_quote_pat));
syntax_expanders.insert(~"quote_stmt",
builtin_expr_tt(ext::quote::expand_quote_stmt));
builtin_normal_tt(ext::quote::expand_quote_stmt));
syntax_expanders.insert(~"line",
builtin(ext::source_util::expand_line));

View File

@ -1,7 +1,7 @@
use std::map::HashMap;
use ast::{crate, expr_, expr_mac, mac_invoc, mac_invoc_tt,
tt_delim, tt_tok, item_mac};
tt_delim, tt_tok, item_mac, stmt_, stmt_mac};
use fold::*;
use ext::base::*;
use ext::qquote::{qq_helper};
@ -20,9 +20,9 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
// entry-point for all syntax extensions.
expr_mac(mac) => {
// Old-style macros, for compatibility, will erase this whole
// block once we've transitioned.
match mac.node {
// Old-style macros. For compatibility, will erase this whole
// block once we've transitioned.
mac_invoc(pth, args, body) => {
assert (vec::len(pth.idents) > 0u);
/* using idents and token::special_idents would make the
@ -81,7 +81,7 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
Some(normal_tt({expander: exp, span: exp_sp})) => {
let expanded = match exp(cx, mac.span, tts) {
mr_expr(e) => e,
mr_expr_or_item(expr_maker,_) => expr_maker(),
mr_any(expr_maker,_,_) => expr_maker(),
_ => cx.span_fatal(
pth.span, fmt!("non-expr macro in expr pos: %s",
*extname))
@ -234,7 +234,7 @@ fn expand_item_mac(exts: HashMap<~str, syntax_extension>,
mr_expr(_) => cx.span_fatal(pth.span,
~"expr macro in item position: " +
*extname),
mr_expr_or_item(_, item_maker) =>
mr_any(_, item_maker, _) =>
option::chain(item_maker(), |i| {fld.fold_item(i)}),
mr_def(mdef) => {
exts.insert(mdef.name, mdef.ext);
@ -248,6 +248,59 @@ fn expand_item_mac(exts: HashMap<~str, syntax_extension>,
}
}
fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
&& s: stmt_, sp: span, fld: ast_fold,
orig: fn@(&&s: stmt_, span, ast_fold) -> (stmt_, span))
-> (stmt_, span)
{
return match s {
stmt_mac(mac) => {
match mac.node {
mac_invoc_tt(pth, tts) => {
assert(vec::len(pth.idents) == 1u);
let extname = cx.parse_sess().interner.get(pth.idents[0]);
match exts.find(*extname) {
None => {
cx.span_fatal(
pth.span,
fmt!("macro undefined: '%s'", *extname))
}
Some(normal_tt({expander: exp, span: exp_sp})) => {
let expanded = match exp(cx, mac.span, tts) {
mr_expr(e) =>
@{node: ast::stmt_expr(e, cx.next_id()),
span: e.span},
mr_any(_,_,stmt_mkr) => stmt_mkr(),
_ => cx.span_fatal(
pth.span,
fmt!("non-stmt macro in stmt pos: %s",
*extname))
};
cx.bt_push(ExpandedFrom(
{call_site: sp,
callie: {name: *extname, span: exp_sp}}));
//keep going, outside-in
let fully_expanded = fld.fold_stmt(expanded).node;
cx.bt_pop();
(fully_expanded, sp)
}
_ => {
cx.span_fatal(pth.span,
fmt!("'%s' is not a tt-style macro",
*extname))
}
}
}
_ => cx.span_bug(mac.span, ~"naked syntactic bit")
}
}
_ => orig(s, sp, fld)
};
}
fn new_span(cx: ext_ctxt, sp: span) -> span {
/* this discards information in the case of macro-defining macros */
return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
@ -298,7 +351,8 @@ fn expand_crate(parse_sess: parse::parse_sess,
@{fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr),
fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod),
fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item),
new_span: |a|new_span(cx, a),
fold_stmt: |a,b,c| expand_stmt(exts, cx, a, b, c, afp.fold_stmt),
new_span: |a| new_span(cx, a),
.. *afp};
let f = make_fold(f_pre);
let cm = parse_expr_from_source_str(~"<core-macros>",

View File

@ -1,4 +1,4 @@
use base::{ext_ctxt, mac_result, mr_expr_or_item, mr_def, normal_tt};
use base::{ext_ctxt, mac_result, mr_any, mr_def, normal_tt};
use codemap::span;
use ast::{ident, matcher_, matcher, match_tok,
match_nonterminal, match_seq, tt_delim};
@ -92,8 +92,9 @@ fn generic_extension(cx: ext_ctxt, sp: span, name: ident,
// Let the context choose how to interpret the result.
// Weird, but useful for X-macros.
return mr_expr_or_item(|| p.parse_expr(),
|| p.parse_item(~[/* no attrs*/]));
return mr_any(|| p.parse_expr(),
|| p.parse_item(~[/* no attrs*/]),
|| p.parse_stmt(~[/* no attrs*/]));
}
failure(sp, msg) => if sp.lo >= best_fail_spot.lo {
best_fail_spot = sp;