From 0faf3ed12680d31cc85a57720a912bab74dfa935 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 7 Nov 2012 23:13:15 -0500 Subject: [PATCH 01/17] Allow `macro_rules!` macros to expand to expressions or items. --- src/libsyntax/ext/base.rs | 1 + src/libsyntax/ext/expand.rs | 3 +++ src/libsyntax/ext/tt/macro_rules.rs | 13 ++++++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 480cbfe0060a5..9a642818a537c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -44,6 +44,7 @@ type syntax_expander_tt_item_ enum mac_result { mr_expr(@ast::expr), mr_item(@ast::item), + mr_expr_or_item(fn@()-> @ast::expr, fn@()-> Option<@ast::item>), mr_def(macro_def) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 443a937d4eb7a..904e68d1a00cf 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -81,6 +81,7 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, Some(expr_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(), _ => cx.span_fatal( pth.span, fmt!("non-expr macro in expr pos: %s", *extname)) @@ -214,6 +215,8 @@ 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) => + option::chain(item_maker(), |i| {fld.fold_item(i)}), mr_def(mdef) => { exts.insert(mdef.name, mdef.ext); None diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 56418989c497a..7957cde8fdc67 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -1,4 +1,4 @@ -use base::{ext_ctxt, mac_result, mr_expr, mr_def, expr_tt}; +use base::{ext_ctxt, mac_result, mr_expr_or_item, mr_def, expr_tt}; use codemap::span; use ast::{ident, matcher_, matcher, match_tok, match_nonterminal, match_seq, tt_delim}; @@ -87,10 +87,13 @@ fn add_new_extension(cx: ext_ctxt, sp: span, name: ident, // rhs has holes ( `$id` and `$(...)` that need filled) let trncbr = new_tt_reader(s_d, itr, Some(named_matches), ~[rhs]); - let p = Parser(cx.parse_sess(), cx.cfg(), - trncbr as reader); - let e = p.parse_expr(); - return mr_expr(e); + let p = @Parser(cx.parse_sess(), cx.cfg(), + trncbr as reader); + + // 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*/])); } failure(sp, msg) => if sp.lo >= best_fail_spot.lo { best_fail_spot = sp; From 345349d409a63706cb95cd13adac764f20bae873 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Thu, 8 Nov 2012 22:29:53 -0500 Subject: [PATCH 02/17] Grammaticalinate an error message. --- src/libsyntax/ext/expand.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 904e68d1a00cf..61d0fff66cfe7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -225,8 +225,8 @@ fn expand_item_mac(exts: HashMap<~str, syntax_extension>, cx.bt_pop(); return maybe_it } - _ => cx.span_fatal(it.span, - fmt!("%s is not a legal here", *extname)) + _ => cx.span_fatal( + it.span, fmt!("%s! is not legal in item position", *extname)) } } _ => cx.span_bug(it.span, ~"invalid item macro invocation") From 7bb81bf6b018a7337b3545482941c68ba3bf105b Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Thu, 8 Nov 2012 23:12:45 -0500 Subject: [PATCH 03/17] Make it possible to invoke item macros without passing identifier arguments. --- src/libsyntax/ext/base.rs | 15 ++++--- src/libsyntax/ext/expand.rs | 63 ++++++++++++++++++----------- src/libsyntax/ext/tt/macro_rules.rs | 4 +- src/libsyntax/parse/parser.rs | 6 ++- 4 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 9a642818a537c..3701614f13798 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -12,7 +12,7 @@ use ast_util::dummy_sp; // new-style macro! tt code: // // syntax_expander_tt, syntax_expander_tt_item, mac_result, -// expr_tt, item_tt +// normal_tt, item_tt // // also note that ast::mac has way too many cases and can probably // be trimmed down substantially. @@ -60,7 +60,10 @@ enum syntax_extension { item_decorator(item_decorator), // Token-tree expanders - expr_tt(syntax_expander_tt), + normal_tt(syntax_expander_tt), + + // perhaps macro_rules! will lose its odd special identifier argument, + // and this can go away also item_tt(syntax_expander_tt_item), } @@ -69,8 +72,8 @@ enum syntax_extension { fn syntax_expander_table() -> HashMap<~str, syntax_extension> { fn builtin(f: syntax_expander_) -> syntax_extension {normal({expander: f, span: None})} - fn builtin_expr_tt(f: syntax_expander_tt_) -> syntax_extension { - expr_tt({expander: f, span: None}) + fn builtin_normal_tt(f: syntax_expander_tt_) -> syntax_extension { + normal_tt({expander: f, span: None}) } fn builtin_item_tt(f: syntax_expander_tt_item_) -> syntax_extension { item_tt({expander: f, span: None}) @@ -94,7 +97,7 @@ fn syntax_expander_table() -> HashMap<~str, syntax_extension> { syntax_expanders.insert(~"ident_to_str", builtin(ext::ident_to_str::expand_syntax_ext)); syntax_expanders.insert(~"log_syntax", - builtin_expr_tt( + builtin_normal_tt( ext::log_syntax::expand_syntax_ext)); syntax_expanders.insert(~"ast", builtin(ext::qquote::expand_ast)); @@ -139,7 +142,7 @@ fn syntax_expander_table() -> HashMap<~str, syntax_extension> { builtin_item_tt(ext::pipes::expand_proto)); syntax_expanders.insert( ~"trace_macros", - builtin_expr_tt(ext::trace_macros::expand_trace_macros)); + builtin_normal_tt(ext::trace_macros::expand_trace_macros)); return syntax_expanders; } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 61d0fff66cfe7..b8bc8a5630540 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -54,7 +54,7 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, exts.insert(named_extension.name, named_extension.ext); (ast::expr_rec(~[], None), s) } - Some(expr_tt(_)) => { + Some(normal_tt(_)) => { cx.span_fatal(pth.span, fmt!("this tt-style macro should be \ invoked '%s!(...)'", *extname)) @@ -78,7 +78,7 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extname)) } - Some(expr_tt({expander: exp, span: exp_sp})) => { + 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(), @@ -153,7 +153,7 @@ fn expand_mod_items(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, }; match exts.find(mname) { None | Some(normal(_)) | Some(macro_defining(_)) - | Some(expr_tt(_)) | Some(item_tt(*)) => items, + | Some(normal_tt(_)) | Some(item_tt(*)) => items, Some(item_decorator(dec_fn)) => { dec_fn(cx, attr.span, attr.node.value, items) } @@ -200,34 +200,49 @@ fn expand_item_mac(exts: HashMap<~str, syntax_extension>, match it.node { item_mac({node: mac_invoc_tt(pth, tts), _}) => { let extname = cx.parse_sess().interner.get(pth.idents[0]); - match exts.find(*extname) { + let (expanded, ex_span) = match exts.find(*extname) { None => { cx.span_fatal(pth.span, - fmt!("macro undefined: '%s'", *extname)) + fmt!("macro undefined: '%s!'", *extname)) + } + Some(normal_tt(expand)) => { + if it.ident != parse::token::special_idents::invalid { + cx.span_fatal(pth.span, + fmt!("macro %s! expects no ident argument, \ + given '%s'", *extname, + *cx.parse_sess().interner.get(it.ident))); + } + (expand.expander(cx, it.span, tts), expand.span) } Some(item_tt(expand)) => { - let expanded = expand.expander(cx, it.span, it.ident, tts); - cx.bt_push(ExpandedFrom({call_site: it.span, - callie: {name: *extname, - span: expand.span}})); - let maybe_it = match expanded { - mr_item(it) => fld.fold_item(it), - mr_expr(_) => cx.span_fatal(pth.span, - ~"expr macro in item position: " + - *extname), - mr_expr_or_item(_, item_maker) => - option::chain(item_maker(), |i| {fld.fold_item(i)}), - mr_def(mdef) => { - exts.insert(mdef.name, mdef.ext); - None - } - }; - cx.bt_pop(); - return maybe_it + if it.ident == parse::token::special_idents::invalid { + cx.span_fatal(pth.span, + fmt!("macro %s! expects an ident argument", + *extname)); + } + (expand.expander(cx, it.span, it.ident, tts), expand.span) } _ => cx.span_fatal( it.span, fmt!("%s! is not legal in item position", *extname)) - } + }; + + cx.bt_push(ExpandedFrom({call_site: it.span, + callie: {name: *extname, + span: ex_span}})); + let maybe_it = match expanded { + mr_item(it) => fld.fold_item(it), + mr_expr(_) => cx.span_fatal(pth.span, + ~"expr macro in item position: " + + *extname), + mr_expr_or_item(_, item_maker) => + option::chain(item_maker(), |i| {fld.fold_item(i)}), + mr_def(mdef) => { + exts.insert(mdef.name, mdef.ext); + None + } + }; + cx.bt_pop(); + return maybe_it; } _ => cx.span_bug(it.span, ~"invalid item macro invocation") } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 7957cde8fdc67..012e421718ad4 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -1,4 +1,4 @@ -use base::{ext_ctxt, mac_result, mr_expr_or_item, mr_def, expr_tt}; +use base::{ext_ctxt, mac_result, mr_expr_or_item, mr_def, normal_tt}; use codemap::span; use ast::{ident, matcher_, matcher, match_tok, match_nonterminal, match_seq, tt_delim}; @@ -113,6 +113,6 @@ fn add_new_extension(cx: ext_ctxt, sp: span, name: ident, return mr_def({ name: *cx.parse_sess().interner.get(name), - ext: expr_tt({expander: exp, span: Some(sp)}) + ext: normal_tt({expander: exp, span: Some(sp)}) }); } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 925934d165f68..afee5dfaf7f04 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3430,7 +3430,11 @@ impl Parser { // item macro. let pth = self.parse_path_without_tps(); self.expect(token::NOT); - let id = self.parse_ident(); + let id = if self.token == token::LPAREN { + token::special_idents::invalid // no special identifier + } else { + self.parse_ident() + }; let tts = match self.token { token::LPAREN | token::LBRACE => { let ket = token::flip_delimiter(copy self.token); From b35992b111dc9826a0d221f756b0d1be9e80dbf2 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 12 Nov 2012 23:06:55 -0500 Subject: [PATCH 04/17] Allow for macros to occur in statement position. --- src/librustc/middle/astencode.rs | 3 ++- src/librustc/middle/liveness.rs | 4 ++++ src/librustc/middle/region.rs | 1 + src/librustc/middle/trans/base.rs | 1 + src/librustc/middle/ty.rs | 1 + src/librustc/middle/typeck/check.rs | 1 + src/librusti/rusti.rs | 2 +- src/libsyntax/ast.rs | 2 ++ src/libsyntax/ast_util.rs | 3 ++- src/libsyntax/fold.rs | 4 +++- src/libsyntax/parse/classify.rs | 21 +++++++++------------ src/libsyntax/print/pprust.rs | 4 ++++ src/libsyntax/visit.rs | 3 ++- 13 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index e10966a6c2292..0c0029ca462a2 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -239,7 +239,8 @@ fn simplify_ast(ii: ast::inlined_item) -> ast::inlined_item { match stmt.node { ast::stmt_expr(_, _) | ast::stmt_semi(_, _) | ast::stmt_decl(@{node: ast::decl_local(_), span: _}, _) => true, - ast::stmt_decl(@{node: ast::decl_item(_), span: _}, _) => false + ast::stmt_decl(@{node: ast::decl_item(_), span: _}, _) => false, + ast::stmt_mac(*) => fail ~"unexpanded macro in astencode" } }; let blk_sans_items = { stmts: stmts_sans_items,.. blk }; diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index f55985aba13c5..a06784ad3b8f5 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1031,6 +1031,10 @@ impl Liveness { stmt_expr(expr, _) | stmt_semi(expr, _) => { return self.propagate_through_expr(expr, succ); } + + stmt_mac(*) => { + self.tcx.sess.span_bug(stmt.span, ~"unexpanded macro"); + } } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index e2bb63da487d1..b149bfef3cb1b 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -245,6 +245,7 @@ fn resolve_stmt(stmt: @ast::stmt, cx: ctxt, visitor: visit::vt) { expr_cx.parent = Some(stmt_id); visit::visit_stmt(stmt, expr_cx, visitor); } + ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro") } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 548018438ca3d..52e2c3b788432 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1026,6 +1026,7 @@ fn trans_stmt(cx: block, s: ast::stmt) -> block { ast::decl_item(i) => trans_item(cx.fcx.ccx, *i) } } + ast::stmt_mac(*) => cx.tcx().sess.bug(~"unexpanded macro") } return bcx; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index cb567028ac99c..f032018cb9281 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3181,6 +3181,7 @@ fn stmt_node_id(s: @ast::stmt) -> ast::node_id { ast::stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) => { return id; } + ast::stmt_mac(*) => fail ~"unexpanded macro in trans" } } diff --git a/src/librustc/middle/typeck/check.rs b/src/librustc/middle/typeck/check.rs index 5a45e13a16f73..8a9efe0a04d72 100644 --- a/src/librustc/middle/typeck/check.rs +++ b/src/librustc/middle/typeck/check.rs @@ -2362,6 +2362,7 @@ fn check_stmt(fcx: @fn_ctxt, stmt: @ast::stmt) -> bool { node_id = id; bot = check_expr(fcx, expr, None); } + ast::stmt_mac(*) => fcx.ccx.tcx.sess.bug(~"unexpanded macro") } fcx.write_nil(node_id); return bot; diff --git a/src/librusti/rusti.rs b/src/librusti/rusti.rs index 7900976146748..0faf214662e4b 100644 --- a/src/librusti/rusti.rs +++ b/src/librusti/rusti.rs @@ -52,7 +52,7 @@ fn record(repl: Repl, blk: @ast::blk, intr: @token::ident_interner) -> Repl { let new_stmts = do with_pp(intr) |pp, writer| { for blk.node.stmts.each |stmt| { match stmt.node { - ast::stmt_decl(*) => { + ast::stmt_decl(*) | ast::stmt_mac(*) => { pprust::print_stmt(pp, **stmt); writer.write_line(~""); } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index da28e349a563d..4e318aa65331f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -896,6 +896,8 @@ enum stmt_ { // expr with trailing semi-colon (may have any type): stmt_semi(@expr, node_id), + + stmt_mac(mac), } // FIXME (pending discussion of #1697, #2178...): local should really be diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 73a1c4b7530a0..982ede6262ff4 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -39,7 +39,8 @@ pure fn stmt_id(s: stmt) -> node_id { match s.node { stmt_decl(_, id) => id, stmt_expr(_, id) => id, - stmt_semi(_, id) => id + stmt_semi(_, id) => id, + stmt_mac(_) => fail ~"attempted to analyze unexpanded stmt", } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9d57b5ae814f6..d9e3054901da3 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -325,10 +325,12 @@ fn noop_fold_block(b: blk_, fld: ast_fold) -> blk_ { } fn noop_fold_stmt(s: stmt_, fld: ast_fold) -> stmt_ { + let fold_mac = |x| fold_mac_(x, fld); return match s { stmt_decl(d, nid) => stmt_decl(fld.fold_decl(d), fld.new_id(nid)), stmt_expr(e, nid) => stmt_expr(fld.fold_expr(e), fld.new_id(nid)), - stmt_semi(e, nid) => stmt_semi(fld.fold_expr(e), fld.new_id(nid)) + stmt_semi(e, nid) => stmt_semi(fld.fold_expr(e), fld.new_id(nid)), + stmt_mac(mac) => stmt_mac(fold_mac(mac)) }; } diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index 2f3e29bd90ff3..0d370525d1789 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -21,18 +21,15 @@ fn expr_is_simple_block(e: @ast::expr) -> bool { } fn stmt_ends_with_semi(stmt: ast::stmt) -> bool { - match stmt.node { - ast::stmt_decl(d, _) => { - return match d.node { - ast::decl_local(_) => true, - ast::decl_item(_) => false + return match stmt.node { + ast::stmt_decl(d, _) => { + match d.node { + ast::decl_local(_) => true, + ast::decl_item(_) => false } - } - ast::stmt_expr(e, _) => { - return expr_requires_semi_to_be_stmt(e); - } - ast::stmt_semi(*) => { - return false; - } + } + ast::stmt_expr(e, _) => { expr_requires_semi_to_be_stmt(e) } + ast::stmt_semi(*) => { false } + ast::stmt_mac(*) => { false } } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index b4a5407c00249..e23e3767c0819 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -881,6 +881,10 @@ fn print_stmt(s: ps, st: ast::stmt) { print_expr(s, expr); word(s.s, ~";"); } + ast::stmt_mac(mac) => { + space_if_not_bol(s); + print_mac(s, mac); + } } if parse::classify::stmt_ends_with_semi(st) { word(s.s, ~";"); } maybe_print_trailing_comment(s, st.span, None); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ae0de2add81b8..2c25064c456ef 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -357,7 +357,8 @@ fn visit_stmt(s: @stmt, e: E, v: vt) { match s.node { stmt_decl(d, _) => v.visit_decl(d, e, v), stmt_expr(ex, _) => v.visit_expr(ex, e, v), - stmt_semi(ex, _) => v.visit_expr(ex, e, v) + stmt_semi(ex, _) => v.visit_expr(ex, e, v), + stmt_mac(mac) => visit_mac(mac, e, v) } } From c6b118eb2ba1d2d1505a8c9f4a9c41ef6cc465ef Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 19 Nov 2012 20:31:22 -0500 Subject: [PATCH 05/17] Make it possible to expand stmt macros. --- src/libsyntax/ext/base.rs | 16 +++---- src/libsyntax/ext/expand.rs | 66 ++++++++++++++++++++++++++--- src/libsyntax/ext/tt/macro_rules.rs | 7 +-- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3701614f13798..01193a0b483f6 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -44,7 +44,7 @@ type syntax_expander_tt_item_ 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 syntax_expander_table() -> HashMap<~str, 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)); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b8bc8a5630540..0acda26abe99b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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()}; @@ -278,7 +331,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(~"", diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 012e421718ad4..1c62c9b2a6da2 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -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 add_new_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; From ae1718fa23dc85a7f562a2119d83dbd52b3cc858 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Sat, 17 Nov 2012 07:41:36 -0500 Subject: [PATCH 06/17] Use `biased_match!` to avoid over-deep indentation in expand.rs. --- src/libsyntax/ext/expand.rs | 170 +++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 79 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0acda26abe99b..bbc20919117b0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -10,6 +10,7 @@ use parse::{parser, parse_expr_from_source_str, new_parser_from_tts}; use codemap::{span, ExpandedFrom}; + fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, e: expr_, s: span, fld: ast_fold, orig: fn@(expr_, span, ast_fold) -> (expr_, span)) @@ -191,21 +192,43 @@ fn expand_item(exts: HashMap<~str, syntax_extension>, } } +// avoid excess indentation when a series of nested `match`es +// has only one "good" outcome +macro_rules! biased_match ( + ( ($e :expr) ~ ($p :pat) else $err :stmt ; + $( ($e_cdr:expr) ~ ($p_cdr:pat) else $err_cdr:stmt ; )* + => $body:expr + ) => ( + match $e { + $p => { + biased_match!($( ($e_cdr) ~ ($p_cdr) else $err_cdr ; )* + => $body) + } + _ => { $err } + } + ); + ( => $body:expr ) => ( $body ) +) + // Support for item-position macro invocations, exactly the same // logic as for expression-position macro invocations. fn expand_item_mac(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, &&it: @ast::item, fld: ast_fold) -> Option<@ast::item> { - match it.node { - item_mac({node: mac_invoc_tt(pth, tts), _}) => { - let extname = cx.parse_sess().interner.get(pth.idents[0]); - let (expanded, ex_span) = match exts.find(*extname) { - None => { - cx.span_fatal(pth.span, - fmt!("macro undefined: '%s!'", *extname)) - } - Some(normal_tt(expand)) => { + let (pth, tts) = biased_match!( + (it.node) ~ (item_mac({node: mac_invoc_tt(pth, tts), _})) else { + cx.span_bug(it.span, ~"invalid item macro invocation") + }; + => (pth, tts) + ); + + let extname = cx.parse_sess().interner.get(pth.idents[0]); + let (expanded, ex_span) = match exts.find(*extname) { + None => cx.span_fatal(pth.span, + fmt!("macro undefined: '%s!'", *extname)), + + Some(normal_tt(expand)) => { if it.ident != parse::token::special_idents::invalid { cx.span_fatal(pth.span, fmt!("macro %s! expects no ident argument, \ @@ -213,39 +236,35 @@ fn expand_item_mac(exts: HashMap<~str, syntax_extension>, *cx.parse_sess().interner.get(it.ident))); } (expand.expander(cx, it.span, tts), expand.span) - } - Some(item_tt(expand)) => { + } + Some(item_tt(expand)) => { if it.ident == parse::token::special_idents::invalid { cx.span_fatal(pth.span, fmt!("macro %s! expects an ident argument", *extname)); } (expand.expander(cx, it.span, it.ident, tts), expand.span) - } - _ => cx.span_fatal( - it.span, fmt!("%s! is not legal in item position", *extname)) - }; + } + _ => cx.span_fatal( + it.span, fmt!("%s! is not legal in item position", *extname)) + }; - cx.bt_push(ExpandedFrom({call_site: it.span, - callie: {name: *extname, - span: ex_span}})); - let maybe_it = match expanded { - mr_item(it) => fld.fold_item(it), - mr_expr(_) => cx.span_fatal(pth.span, - ~"expr macro in item position: " + - *extname), - mr_any(_, item_maker, _) => - option::chain(item_maker(), |i| {fld.fold_item(i)}), - mr_def(mdef) => { - exts.insert(mdef.name, mdef.ext); - None - } - }; - cx.bt_pop(); - return maybe_it; - } - _ => cx.span_bug(it.span, ~"invalid item macro invocation") - } + cx.bt_push(ExpandedFrom({call_site: it.span, + callie: {name: *extname, span: ex_span}})); + let maybe_it = match expanded { + mr_item(it) => fld.fold_item(it), + mr_expr(_) => cx.span_fatal(pth.span, + ~"expr macro in item position: " + + *extname), + mr_any(_, item_maker, _) => + option::chain(item_maker(), |i| {fld.fold_item(i)}), + mr_def(mdef) => { + exts.insert(mdef.name, mdef.ext); + None + } + }; + cx.bt_pop(); + return maybe_it; } fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, @@ -253,51 +272,44 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, 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") - } + let (mac, pth, tts) = biased_match! ( + (s) ~ (stmt_mac(mac)) else return orig(s, sp, fld); + (mac.node) ~ (mac_invoc_tt(pth, tts)) else { + cx.span_bug(mac.span, ~"naked syntactic bit") + }; + => (mac, 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(); + + return (fully_expanded, sp) } - _ => orig(s, sp, fld) - }; + _ => { + cx.span_fatal(pth.span, + fmt!("'%s' is not a tt-style macro", + *extname)) + } + } + } From fab90005978dea3600b64738ed3d8f03a3efb819 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 19 Nov 2012 00:30:48 -0500 Subject: [PATCH 07/17] Allow the expansion of old-style expr macros in stmt position. --- src/libsyntax/ext/expand.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index bbc20919117b0..9c63376e85920 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -303,6 +303,24 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, return (fully_expanded, sp) } + + Some(normal({expander: exp, span: exp_sp})) => { + //convert the new-style invoc for the old-style macro + let arg = base::tt_args_to_original_flavor(cx, pth.span, tts); + let exp_expr = exp(cx, mac.span, arg, None); + let expanded = @{node: ast::stmt_expr(exp_expr, cx.next_id()), + span: exp_expr.span}; + + 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", From a160714fc688bd53234d78c9729e4407c0959d12 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 19 Nov 2012 23:53:52 -0500 Subject: [PATCH 08/17] No longer parse the delimiters of the RHS of a macro as part of the expansion. --- src/libcore/hash.rs | 4 ++-- src/libcore/pipes.rs | 2 +- src/libcore/task/spawn.rs | 2 +- src/librustc/middle/trans/alt.rs | 4 ++-- src/libsyntax/ext/tt/macro_rules.rs | 13 ++++++++++--- src/libsyntax/parse/parser.rs | 24 ++++++++++++------------ 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/libcore/hash.rs b/src/libcore/hash.rs index d938a1d41a321..cdca6852dbb62 100644 --- a/src/libcore/hash.rs +++ b/src/libcore/hash.rs @@ -203,14 +203,14 @@ impl &SipState : io::Writer { macro_rules! compress ( ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => - { + ({ $v0 += $v1; $v1 = rotl!($v1, 13); $v1 ^= $v0; $v0 = rotl!($v0, 32); $v2 += $v3; $v3 = rotl!($v3, 16); $v3 ^= $v2; $v0 += $v3; $v3 = rotl!($v3, 21); $v3 ^= $v0; $v2 += $v1; $v1 = rotl!($v1, 17); $v1 ^= $v2; $v2 = rotl!($v2, 32); - } + }) ); let length = msg.len(); diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index 8b05f63752f5b..78f0883635762 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -86,7 +86,7 @@ use option::unwrap; const SPIN_COUNT: uint = 0; macro_rules! move_it ( - { $x:expr } => { unsafe { let y = move *ptr::addr_of(&($x)); move y } } + { $x:expr } => ( unsafe { let y = move *ptr::addr_of(&($x)); move y } ) ) #[doc(hidden)] diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs index ea66776d22d66..b4bea442e69cc 100644 --- a/src/libcore/task/spawn.rs +++ b/src/libcore/task/spawn.rs @@ -67,7 +67,7 @@ use rt::rust_task; use rt::rust_closure; macro_rules! move_it ( - { $x:expr } => { unsafe { let y = move *ptr::addr_of(&($x)); move y } } + { $x:expr } => ( unsafe { let y = move *ptr::addr_of(&($x)); move y } ) ) type TaskSet = send_map::linear::LinearMap<*rust_task,()>; diff --git a/src/librustc/middle/trans/alt.rs b/src/librustc/middle/trans/alt.rs index 49db8613366ca..e55b6780d0864 100644 --- a/src/librustc/middle/trans/alt.rs +++ b/src/librustc/middle/trans/alt.rs @@ -831,14 +831,14 @@ fn root_pats_as_necessary(bcx: block, m: &[@Match], // matches should fit that sort of pattern or NONE (however, some of the // matches may be wildcards like _ or identifiers). macro_rules! any_pat ( - ($m:expr, $pattern:pat) => { + ($m:expr, $pattern:pat) => ( vec::any($m, |br| { match br.pats[col].node { $pattern => true, _ => false } }) - } + ) ) fn any_box_pat(m: &[@Match], col: uint) -> bool { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 1c62c9b2a6da2..63885d5c2580b 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -81,12 +81,19 @@ fn add_new_extension(cx: ext_ctxt, sp: span, name: ident, success(named_matches) => { let rhs = match rhses[i] { // okay, what's your transcriber? - @matched_nonterminal(nt_tt(@tt)) => tt, - _ => cx.span_bug(sp, ~"bad thing in rhs") + @matched_nonterminal(nt_tt(@tt)) => { + match tt { + // cut off delimiters; don't parse 'em + tt_delim(tts) => tts.slice(1u,tts.len()-1u), + _ => cx.span_fatal( + sp, ~"macro rhs must be delimited") + } + }, + _ => cx.span_bug(sp, ~"bad thing in rhs") }; // rhs has holes ( `$id` and `$(...)` that need filled) let trncbr = new_tt_reader(s_d, itr, Some(named_matches), - ~[rhs]); + rhs); let p = @Parser(cx.parse_sess(), cx.cfg(), trncbr as reader); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index afee5dfaf7f04..ea5be51758e6e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -121,7 +121,7 @@ enum view_item_parse_mode { The important thing is to make sure that lookahead doesn't balk at INTERPOLATED tokens */ macro_rules! maybe_whole_expr ( - ($p:expr) => { match copy $p.token { + ($p:expr) => ( match copy $p.token { INTERPOLATED(token::nt_expr(e)) => { $p.bump(); return e; @@ -132,33 +132,33 @@ macro_rules! maybe_whole_expr ( expr_path(pt)); } _ => () - }} + }) ) macro_rules! maybe_whole ( - ($p:expr, $constructor:ident) => { match copy $p.token { + ($p:expr, $constructor:ident) => ( match copy $p.token { INTERPOLATED(token::$constructor(x)) => { $p.bump(); return x; } _ => () - }} ; - (deref $p:expr, $constructor:ident) => { match copy $p.token { + }) ; + (deref $p:expr, $constructor:ident) => ( match copy $p.token { INTERPOLATED(token::$constructor(x)) => { $p.bump(); return *x; } _ => () - }} ; - (Some $p:expr, $constructor:ident) => { match copy $p.token { + }) ; + (Some $p:expr, $constructor:ident) => ( match copy $p.token { INTERPOLATED(token::$constructor(x)) => { $p.bump(); return Some(x); } _ => () - }} ; - (iovi $p:expr, $constructor:ident) => { match copy $p.token { + }) ; + (iovi $p:expr, $constructor:ident) => ( match copy $p.token { INTERPOLATED(token::$constructor(x)) => { $p.bump(); return iovi_item(x); } _ => () - }} ; - (pair_empty $p:expr, $constructor:ident) => { match copy $p.token { + }) ; + (pair_empty $p:expr, $constructor:ident) => ( match copy $p.token { INTERPOLATED(token::$constructor(x)) => { $p.bump(); return (~[], x); } _ => () - }} + }) ) From 5f95bfb65cbc285fc37ac40e0599cd0abb6290e5 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Tue, 13 Nov 2012 23:45:25 -0500 Subject: [PATCH 09/17] Allow parsing of macros in statement position. --- src/libsyntax/parse/parser.rs | 45 ++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ea5be51758e6e..eb1c23407007a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -53,12 +53,12 @@ use ast::{_mod, add, arg, arm, attribute, pat_tup, pat_uniq, pat_wild, path, private, Proto, ProtoBare, ProtoBorrowed, ProtoBox, ProtoUniq, provided, public, pure_fn, purity, re_static, re_self, re_anon, re_named, region, - rem, required, ret_style, - return_val, self_ty, shl, shr, stmt, stmt_decl, stmt_expr, - stmt_semi, struct_def, struct_field, struct_variant_kind, - subtract, sty_box, sty_by_ref, sty_region, sty_static, sty_uniq, - sty_value, token_tree, trait_method, trait_ref, tt_delim, tt_seq, - tt_tok, tt_nonterminal, tuple_variant_kind, Ty, ty_, ty_bot, + rem, required, ret_style, return_val, self_ty, shl, shr, stmt, + stmt_decl, stmt_expr, stmt_semi, stmt_mac, struct_def, + struct_field, struct_variant_kind, subtract, sty_box, sty_by_ref, + sty_region, sty_static, sty_uniq, sty_value, token_tree, + trait_method, trait_ref, tt_delim, tt_seq, tt_tok, + tt_nonterminal, tuple_variant_kind, Ty, ty_, ty_bot, ty_box, ty_field, ty_fn, ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_param_bound, ty_path, ty_ptr, ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec, ty_fixed_length_vec, @@ -2207,6 +2207,39 @@ impl Parser { self.expect_keyword(~"let"); let decl = self.parse_let(); return @spanned(lo, decl.span.hi, stmt_decl(decl, self.get_id())); + } else if is_ident(self.token) + && !self.is_any_keyword(copy self.token) + && self.look_ahead(1) == token::NOT { + // Potential trouble: if we allow macros with paths instead of + // idents, we'd need to look ahead past the whole path here... + let pth = self.parse_value_path(); + self.bump(); + + let id = if self.token == token::LPAREN { + token::special_idents::invalid // no special identifier + } else { + self.parse_ident() + }; + + let tts = self.parse_unspanned_seq( + token::LPAREN, token::RPAREN, seq_sep_none(), + |p| p.parse_token_tree()); + let hi = self.span.hi; + + if id == token::special_idents::invalid { + return @spanned(lo, hi, stmt_mac( + spanned(lo, hi, mac_invoc_tt(pth, tts)))); + } else { + // if it has a special ident, it's definitely an item + return @spanned(lo, hi, stmt_decl( + @spanned(lo, hi, decl_item( + self.mk_item( + lo, hi, id /*id is good here*/, + item_mac(spanned(lo, hi, mac_invoc_tt(pth, tts))), + inherited, ~[/*no attrs*/]))), + self.get_id())); + } + } else { let mut item_attrs; match self.parse_outer_attrs_or_ext(first_item_attrs) { From c7811c2c663130d7a1d8ad655d4bebc8c7ae4eb7 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Mon, 19 Nov 2012 00:36:26 -0500 Subject: [PATCH 10/17] Make the parser handle stmt macros that might be exprs at the end of blocks. --- src/libsyntax/parse/parser.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index eb1c23407007a..85b6e3d7ccc5d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2380,6 +2380,25 @@ impl Parser { } } + stmt_mac(m) => { + // Statement macro; might be an expr + match self.token { + token::SEMI => { + self.bump(); + stmts.push(stmt); + } + token::RBRACE => { + // if a block ends in `m!(arg)` without + // a `;`, it must be an expr + expr = Some( + self.mk_mac_expr(stmt.span.lo, + stmt.span.hi, + m.node)); + } + _ => { stmts.push(stmt); } + } + } + _ => { // All other kinds of statements: stmts.push(stmt); @@ -3463,6 +3482,10 @@ impl Parser { // item macro. let pth = self.parse_path_without_tps(); self.expect(token::NOT); + + // a 'special' identifier (like what `macro_rules!` uses) + // is optional. We should eventually unify invoc syntax + // and remove this. let id = if self.token == token::LPAREN { token::special_idents::invalid // no special identifier } else { From bbe5d630411de7e40961a1f829ba9bc688afffc6 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 21 Nov 2012 13:37:43 -0500 Subject: [PATCH 11/17] Distinguish `stmt_mac`s that are followed by semicolons and those that aren't. --- src/libsyntax/ast.rs | 3 ++- src/libsyntax/ast_util.rs | 2 +- src/libsyntax/ext/expand.rs | 10 ++++++---- src/libsyntax/fold.rs | 2 +- src/libsyntax/parse/parser.rs | 7 ++++--- src/libsyntax/print/pprust.rs | 3 ++- src/libsyntax/visit.rs | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 4e318aa65331f..c6e6cc935184e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -897,7 +897,8 @@ enum stmt_ { // expr with trailing semi-colon (may have any type): stmt_semi(@expr, node_id), - stmt_mac(mac), + // bool: is there a trailing sem-colon? + stmt_mac(mac, bool), } // FIXME (pending discussion of #1697, #2178...): local should really be diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 982ede6262ff4..68cc7cba1b35e 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -40,7 +40,7 @@ pure fn stmt_id(s: stmt) -> node_id { stmt_decl(_, id) => id, stmt_expr(_, id) => id, stmt_semi(_, id) => id, - stmt_mac(_) => fail ~"attempted to analyze unexpanded stmt", + stmt_mac(*) => fail ~"attempted to analyze unexpanded stmt", } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9c63376e85920..c12318a8fba81 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -272,12 +272,12 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, orig: fn@(&&s: stmt_, span, ast_fold) -> (stmt_, span)) -> (stmt_, span) { - let (mac, pth, tts) = biased_match! ( - (s) ~ (stmt_mac(mac)) else return orig(s, sp, fld); + let (mac, pth, tts, semi) = biased_match! ( + (s) ~ (stmt_mac(mac, semi)) else return orig(s, sp, fld); (mac.node) ~ (mac_invoc_tt(pth, tts)) else { cx.span_bug(mac.span, ~"naked syntactic bit") }; - => (mac, pth, tts)); + => (mac, pth, tts, semi)); assert(vec::len(pth.idents) == 1u); let extname = cx.parse_sess().interner.get(pth.idents[0]); @@ -287,8 +287,10 @@ fn expand_stmt(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) => + mr_expr(e) if !semi => @{node: ast::stmt_expr(e, cx.next_id()), span: e.span}, + mr_expr(e) if semi => + @{node: ast::stmt_semi(e, cx.next_id()), span: e.span}, mr_any(_,_,stmt_mkr) => stmt_mkr(), _ => cx.span_fatal( pth.span, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d9e3054901da3..1eb2fec8bdf1f 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -330,7 +330,7 @@ fn noop_fold_stmt(s: stmt_, fld: ast_fold) -> stmt_ { stmt_decl(d, nid) => stmt_decl(fld.fold_decl(d), fld.new_id(nid)), stmt_expr(e, nid) => stmt_expr(fld.fold_expr(e), fld.new_id(nid)), stmt_semi(e, nid) => stmt_semi(fld.fold_expr(e), fld.new_id(nid)), - stmt_mac(mac) => stmt_mac(fold_mac(mac)) + stmt_mac(mac, semi) => stmt_mac(fold_mac(mac), semi) }; } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 85b6e3d7ccc5d..3228efec6110d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2228,7 +2228,7 @@ impl Parser { if id == token::special_idents::invalid { return @spanned(lo, hi, stmt_mac( - spanned(lo, hi, mac_invoc_tt(pth, tts)))); + spanned(lo, hi, mac_invoc_tt(pth, tts)), false)); } else { // if it has a special ident, it's definitely an item return @spanned(lo, hi, stmt_decl( @@ -2380,12 +2380,13 @@ impl Parser { } } - stmt_mac(m) => { + stmt_mac(m, false) => { // Statement macro; might be an expr match self.token { token::SEMI => { self.bump(); - stmts.push(stmt); + stmts.push(@{node: stmt_mac(m, true), + ..*stmt}); } token::RBRACE => { // if a block ends in `m!(arg)` without diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e23e3767c0819..adb23b948e578 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -881,9 +881,10 @@ fn print_stmt(s: ps, st: ast::stmt) { print_expr(s, expr); word(s.s, ~";"); } - ast::stmt_mac(mac) => { + ast::stmt_mac(mac, semi) => { space_if_not_bol(s); print_mac(s, mac); + if semi { word(s.s, ~";"); } } } if parse::classify::stmt_ends_with_semi(st) { word(s.s, ~";"); } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 2c25064c456ef..241c6f1ac4bb0 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -358,7 +358,7 @@ fn visit_stmt(s: @stmt, e: E, v: vt) { stmt_decl(d, _) => v.visit_decl(d, e, v), stmt_expr(ex, _) => v.visit_expr(ex, e, v), stmt_semi(ex, _) => v.visit_expr(ex, e, v), - stmt_mac(mac) => visit_mac(mac, e, v) + stmt_mac(mac, _) => visit_mac(mac, e, v) } } From d63f3d595e5805d1fea9f55deea3349720d181aa Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 21 Nov 2012 14:49:19 -0500 Subject: [PATCH 12/17] Forbid item macros from occuring where statement macros might occur, and allow item macros to look like statement/expr macros. --- src/libsyntax/parse/parser.rs | 36 +++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3228efec6110d..f35e5584b6902 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2253,7 +2253,8 @@ impl Parser { let item_attrs = vec::append(first_item_attrs, item_attrs); - match self.parse_item_or_view_item(item_attrs, true, false) { + match self.parse_item_or_view_item(item_attrs, + true, false, false) { iovi_item(i) => { let mut hi = i.span.hi; let decl = @spanned(lo, hi, decl_item(i)); @@ -2333,7 +2334,7 @@ impl Parser { let {attrs_remaining, view_items, items: items, _} = self.parse_items_and_view_items(first_item_attrs, - IMPORTS_AND_ITEMS_ALLOWED); + IMPORTS_AND_ITEMS_ALLOWED, false); for items.each |item| { let decl = @spanned(item.span.lo, item.span.hi, decl_item(*item)); @@ -2968,7 +2969,8 @@ impl Parser { // Shouldn't be any view items since we've already parsed an item attr let {attrs_remaining, view_items, items: starting_items, _} = self.parse_items_and_view_items(first_item_attrs, - VIEW_ITEMS_AND_ITEMS_ALLOWED); + VIEW_ITEMS_AND_ITEMS_ALLOWED, + true); let mut items: ~[@item] = move starting_items; let mut first = true; @@ -2980,7 +2982,7 @@ impl Parser { } debug!("parse_mod_items: parse_item_or_view_item(attrs=%?)", attrs); - match self.parse_item_or_view_item(attrs, true, false) { + match self.parse_item_or_view_item(attrs, true, false, true) { iovi_item(item) => items.push(item), iovi_view_item(view_item) => { self.span_fatal(view_item.span, ~"view items must be \ @@ -3097,7 +3099,8 @@ impl Parser { // Shouldn't be any view items since we've already parsed an item attr let {attrs_remaining, view_items, items: _, foreign_items} = self.parse_items_and_view_items(first_item_attrs, - VIEW_ITEMS_AND_FOREIGN_ITEMS_ALLOWED); + VIEW_ITEMS_AND_FOREIGN_ITEMS_ALLOWED, + true); let mut items: ~[@foreign_item] = move foreign_items; let mut initial_attrs = attrs_remaining; @@ -3368,7 +3371,8 @@ impl Parser { } fn parse_item_or_view_item(+attrs: ~[attribute], items_allowed: bool, - foreign_items_allowed: bool) + foreign_items_allowed: bool, + macros_allowed: bool) -> item_or_view_item { assert items_allowed != foreign_items_allowed; @@ -3477,9 +3481,11 @@ impl Parser { vis: visibility, span: mk_sp(lo, self.last_span.hi) }); - } else if items_allowed && (!self.is_any_keyword(copy self.token) + } else if macros_allowed && !self.is_any_keyword(copy self.token) && self.look_ahead(1) == token::NOT - && is_plain_ident(self.look_ahead(2))) { + && (is_plain_ident(self.look_ahead(2)) + || self.look_ahead(2) == token::LPAREN + || self.look_ahead(2) == token::LBRACE) { // item macro. let pth = self.parse_path_without_tps(); self.expect(token::NOT); @@ -3487,10 +3493,10 @@ impl Parser { // a 'special' identifier (like what `macro_rules!` uses) // is optional. We should eventually unify invoc syntax // and remove this. - let id = if self.token == token::LPAREN { - token::special_idents::invalid // no special identifier - } else { + let id = if is_plain_ident(self.token) { self.parse_ident() + } else { + token::special_idents::invalid // no special identifier }; let tts = match self.token { token::LPAREN | token::LBRACE => { @@ -3520,7 +3526,7 @@ impl Parser { } fn parse_item(+attrs: ~[attribute]) -> Option<@ast::item> { - match self.parse_item_or_view_item(attrs, true, false) { + match self.parse_item_or_view_item(attrs, true, false, true) { iovi_none => None, iovi_view_item(_) => @@ -3658,7 +3664,8 @@ impl Parser { } fn parse_items_and_view_items(+first_item_attrs: ~[attribute], - mode: view_item_parse_mode) + mode: view_item_parse_mode, + macros_allowed: bool) -> {attrs_remaining: ~[attribute], view_items: ~[@view_item], items: ~[@item], @@ -3685,7 +3692,8 @@ impl Parser { let (view_items, items, foreign_items) = (DVec(), DVec(), DVec()); loop { match self.parse_item_or_view_item(attrs, items_allowed, - foreign_items_allowed) { + foreign_items_allowed, + macros_allowed) { iovi_none => break, iovi_view_item(view_item) => { From 887cf280c613299d6ef03314c77cd4f532d78e97 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 21 Nov 2012 15:53:49 -0500 Subject: [PATCH 13/17] Update tests to respect the way that macros work now. --- src/test/run-pass/macro-2.rs | 4 ++-- src/test/run-pass/macro-interpolation.rs | 4 ++-- src/test/run-pass/macro-path.rs | 12 +++++------- src/test/run-pass/pipe-presentation-examples.rs | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/test/run-pass/macro-2.rs b/src/test/run-pass/macro-2.rs index 595583d2cefeb..f342f5db140ba 100644 --- a/src/test/run-pass/macro-2.rs +++ b/src/test/run-pass/macro-2.rs @@ -3,10 +3,10 @@ fn main() { macro_rules! mylambda_tt( - ($x:ident, $body:expr) => { + ($x:ident, $body:expr) => ({ fn f($x: int) -> int { return $body; }; f - } + }) ) assert(mylambda_tt!(y, y * 2)(8) == 16) diff --git a/src/test/run-pass/macro-interpolation.rs b/src/test/run-pass/macro-interpolation.rs index 6ee4ac8780779..d5e5fe1b489b9 100644 --- a/src/test/run-pass/macro-interpolation.rs +++ b/src/test/run-pass/macro-interpolation.rs @@ -1,7 +1,7 @@ macro_rules! overly_complicated ( ($fnname:ident, $arg:ident, $ty:ty, $body:block, $val:expr, $pat:pat, $res:path) => - { + ({ fn $fnname($arg: $ty) -> Option<$ty> $body match $fnname($val) { Some($pat) => { @@ -9,7 +9,7 @@ macro_rules! overly_complicated ( } _ => { fail; } } - } + }) ) fn main() { diff --git a/src/test/run-pass/macro-path.rs b/src/test/run-pass/macro-path.rs index 6c5a944ee2917..d54fef5c260de 100644 --- a/src/test/run-pass/macro-path.rs +++ b/src/test/run-pass/macro-path.rs @@ -2,13 +2,11 @@ mod m { pub type t = int; } -fn macros() { - macro_rules! foo { - ($p:path) => { - fn f() -> $p { 10 } - f() - } - } +macro_rules! foo { + ($p:path) => ({ + fn f() -> $p { 10 }; + f() + }) } fn main() { diff --git a/src/test/run-pass/pipe-presentation-examples.rs b/src/test/run-pass/pipe-presentation-examples.rs index 06c1734a28f73..50b22725e9847 100644 --- a/src/test/run-pass/pipe-presentation-examples.rs +++ b/src/test/run-pass/pipe-presentation-examples.rs @@ -54,12 +54,12 @@ macro_rules! select ( $($message:path$(($($x: ident),+))dont_type_this* -> $next:ident $e:expr),+ } )+ - } => { + } => ({ let index = pipes::selecti([$(($port).header()),+]); select_if!(index, 0, $( $port => [ $($message$(($($x),+))dont_type_this* -> $next $e),+ ], )+) - } + }) ) // Types and protocols From 9b5995e3a38e4fea0c14b8fb490b1335727cf777 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 21 Nov 2012 18:49:42 -0500 Subject: [PATCH 14/17] Respect semicolons always when expanding statments. --- src/libsyntax/ext/expand.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index c12318a8fba81..901f1775db8eb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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, stmt_, stmt_mac}; + tt_delim, tt_tok, item_mac, stmt_, stmt_mac, stmt_expr, stmt_semi}; use fold::*; use ext::base::*; use ext::qquote::{qq_helper}; @@ -281,16 +281,14 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, assert(vec::len(pth.idents) == 1u); let extname = cx.parse_sess().interner.get(pth.idents[0]); - match exts.find(*extname) { + let (fully_expanded, sp) = 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) if !semi => - @{node: ast::stmt_expr(e, cx.next_id()), span: e.span}, - mr_expr(e) if semi => - @{node: ast::stmt_semi(e, cx.next_id()), span: e.span}, + mr_expr(e) => + @{node: stmt_expr(e, cx.next_id()), span: e.span}, mr_any(_,_,stmt_mkr) => stmt_mkr(), _ => cx.span_fatal( pth.span, @@ -303,14 +301,14 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, let fully_expanded = fld.fold_stmt(expanded).node; cx.bt_pop(); - return (fully_expanded, sp) + (fully_expanded, sp) } Some(normal({expander: exp, span: exp_sp})) => { //convert the new-style invoc for the old-style macro let arg = base::tt_args_to_original_flavor(cx, pth.span, tts); let exp_expr = exp(cx, mac.span, arg, None); - let expanded = @{node: ast::stmt_expr(exp_expr, cx.next_id()), + let expanded = @{node: stmt_expr(exp_expr, cx.next_id()), span: exp_expr.span}; cx.bt_push(ExpandedFrom({call_site: sp, @@ -325,10 +323,14 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, _ => { cx.span_fatal(pth.span, - fmt!("'%s' is not a tt-style macro", - *extname)) + fmt!("'%s' is not a tt-style macro", *extname)) } - } + }; + + return (match fully_expanded { + stmt_expr(e, stmt_id) if semi => stmt_semi(e, stmt_id), + _ => { fully_expanded } /* might already have a semi */ + }, sp) } From 2db3b7926da48a40a6f0ddf723456b583bee94b4 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Wed, 21 Nov 2012 22:38:27 -0500 Subject: [PATCH 15/17] Forbid attrs on macros, since we don't handle them properly yet. --- src/libsyntax/parse/parser.rs | 11 +++++++++-- src/libsyntax/parse/token.rs | 2 +- src/test/compile-fail/attr-before-ext.rs | 2 +- src/test/compile-fail/ext-after-attrib.rs | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f35e5584b6902..7ffc694020233 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2197,7 +2197,7 @@ impl Parser { fn check_expected_item(p: Parser, current_attrs: ~[attribute]) { // If we have attributes then we should have an item if vec::is_not_empty(current_attrs) { - p.fatal(~"expected item"); + p.fatal(~"expected item after attrs"); } } @@ -2210,6 +2210,9 @@ impl Parser { } else if is_ident(self.token) && !self.is_any_keyword(copy self.token) && self.look_ahead(1) == token::NOT { + + check_expected_item(self, first_item_attrs); + // Potential trouble: if we allow macros with paths instead of // idents, we'd need to look ahead past the whole path here... let pth = self.parse_value_path(); @@ -2381,7 +2384,7 @@ impl Parser { } } - stmt_mac(m, false) => { + stmt_mac(m, _) => { // Statement macro; might be an expr match self.token { token::SEMI => { @@ -3486,6 +3489,10 @@ impl Parser { && (is_plain_ident(self.look_ahead(2)) || self.look_ahead(2) == token::LPAREN || self.look_ahead(2) == token::LBRACE) { + if attrs.len() > 0 { + self.fatal(~"attrs on macros are not yet supported"); + } + // item macro. let pth = self.parse_path_without_tps(); self.expect(token::NOT); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 2e59d2fa45fcc..05c0016d33b4d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -343,7 +343,7 @@ impl ident_interner { } } -/** Key for thread-local data for sneaking interner information to the +/* Key for thread-local data for sneaking interner information to the * serializer/deserializer. It sounds like a hack because it is one. * Bonus ultra-hack: functions as keys don't work across crates, * so we have to use a unique number. See taskgroup_key! in task.rs diff --git a/src/test/compile-fail/attr-before-ext.rs b/src/test/compile-fail/attr-before-ext.rs index e6bbb50044bd3..e474b63be857a 100644 --- a/src/test/compile-fail/attr-before-ext.rs +++ b/src/test/compile-fail/attr-before-ext.rs @@ -1,4 +1,4 @@ fn main() { #[attr] - debug!("hi"); //~ ERROR expected item + debug!("hi"); //~ ERROR expected item after attrs } \ No newline at end of file diff --git a/src/test/compile-fail/ext-after-attrib.rs b/src/test/compile-fail/ext-after-attrib.rs index 5c3ca527068fe..948a7bf17925d 100644 --- a/src/test/compile-fail/ext-after-attrib.rs +++ b/src/test/compile-fail/ext-after-attrib.rs @@ -1,4 +1,4 @@ -// error-pattern:expected item but found `fmt` +// error-pattern:attrs on macros are not yet supported // Don't know how to deal with a syntax extension appearing after an // item attribute. Probably could use a better error message. From 539fe0e2e3f338392091ff6c583b43cd71e5b7c4 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Thu, 22 Nov 2012 10:29:56 -0500 Subject: [PATCH 16/17] Test statement macros. --- src/test/run-pass/macro-stmt.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/run-pass/macro-stmt.rs diff --git a/src/test/run-pass/macro-stmt.rs b/src/test/run-pass/macro-stmt.rs new file mode 100644 index 0000000000000..e0b88d1c892bc --- /dev/null +++ b/src/test/run-pass/macro-stmt.rs @@ -0,0 +1,32 @@ +// xfail-pretty - token trees can't pretty print + +macro_rules! myfn( + ( $f:ident, ( $( $x:ident ),* ), $body:block ) => ( + fn $f( $( $x : int),* ) -> int $body + ) +) + +myfn!(add, (a,b), { return a+b; } ) + +fn main() { + + macro_rules! mylet( + ($x:ident, $val:expr) => ( + let $x = $val; + ) + ); + + mylet!(y, 8*2); + assert(y == 16); + + myfn!(mult, (a,b), { a*b } ); + + assert (mult(2, add(4,4)) == 16); + + macro_rules! actually_an_expr_macro ( + () => ( 16 ) + ) + + assert { actually_an_expr_macro!() } == 16; + +} From 99626896ab2c23322be683448dbb92b3c8ce39c1 Mon Sep 17 00:00:00 2001 From: Paul Stansifer Date: Thu, 22 Nov 2012 11:54:52 -0500 Subject: [PATCH 17/17] Macros can now also be stmts and items. --- doc/rust.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/rust.md b/doc/rust.md index d4e8a9e469144..0c8d1c5a1409b 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -494,7 +494,7 @@ transcriber : '(' transcriber * ')' | '[' transcriber * ']' User-defined syntax extensions are called "macros", and they can be defined with the `macro_rules!` syntax extension. User-defined macros can currently -only be invoked in expression position. +be invoked as expressions, statements, or items. (A `sep_token` is any token other than `*` and `+`. A `non_special_token` is any token other than a delimiter or `$`.) @@ -510,7 +510,7 @@ For parsing reasons, delimiters must be balanced, but they are otherwise not spe In the matcher, `$` _name_ `:` _designator_ matches the nonterminal in the Rust syntax named by _designator_. Valid designators are `item`, `block`, -`stmt`, `pat`, `expr`, `ty` (type), `ident`, `path`, `matchers` (lhs of the `=>` in macro rules), +`stmt`, `pat`, `expr`, `ty` (type), `ident`, `path`, `matchers` (lhs of the `=>` in macro rules), `tt` (rhs of the `=>` in macro rules). In the transcriber, the designator is already known, and so only the name of a matched nonterminal comes after the dollar sign. @@ -2196,7 +2196,7 @@ Records and structures can also be pattern-matched and their fields bound to var When matching fields of a record, the fields being matched are specified first, then a placeholder (`_`) represents the remaining fields. - + ~~~~ # type options = {choose: bool, size: ~str}; # type player = {player: ~str, stats: (), options: options}; @@ -2726,7 +2726,7 @@ The kinds are: structural types containing only other sendable types. `Owned` : Types of this kind do not contain any borrowed pointers; - this can be a useful guarantee for code that breaks borrowing assumptions using [`unsafe` operations](#unsafe-functions). + this can be a useful guarantee for code that breaks borrowing assumptions using [`unsafe` operations](#unsafe-functions). `Copy` : This kind includes all types that can be copied. All types with sendable kind are copyable, as are managed boxes, managed closures,