| use codemap::span; |
| use std::map::HashMap; |
| use dvec::DVec; |
| |
| use base::*; |
| |
| use fold::*; |
| use ast_util::respan; |
| use ast::{ident, path, ty, blk_, expr, expr_path, |
| expr_vec, expr_mac, mac_invoc, node_id, expr_index}; |
| |
| export add_new_extension; |
| |
| fn path_to_ident(pth: @path) -> Option<ident> { |
| if vec::len(pth.idents) == 1u && vec::len(pth.types) == 0u { |
| return Some(pth.idents[0u]); |
| } |
| return None; |
| } |
| |
| //a vec of binders might be a little big. |
| type clause = {params: binders, body: @expr}; |
| |
| /* logically, an arb_depth should contain only one kind of matchable */ |
| enum arb_depth<T> { leaf(T), seq(@~[arb_depth<T>], span), } |
| |
| |
| enum matchable { |
| match_expr(@expr), |
| match_path(@path), |
| match_ident(ast::spanned<ident>), |
| match_ty(@ty), |
| match_block(ast::blk), |
| match_exact, /* don't bind anything, just verify the AST traversal */ |
| } |
| |
| /* for when given an incompatible bit of AST */ |
| fn match_error(cx: ext_ctxt, m: matchable, expected: ~str) -> ! { |
| match m { |
| match_expr(x) => cx.span_fatal( |
| x.span, ~"this argument is an expr, expected " + expected), |
| match_path(x) => cx.span_fatal( |
| x.span, ~"this argument is a path, expected " + expected), |
| match_ident(x) => cx.span_fatal( |
| x.span, ~"this argument is an ident, expected " + expected), |
| match_ty(x) => cx.span_fatal( |
| x.span, ~"this argument is a type, expected " + expected), |
| match_block(x) => cx.span_fatal( |
| x.span, ~"this argument is a block, expected " + expected), |
| match_exact => cx.bug(~"what is a match_exact doing in a bindings?") |
| } |
| } |
| |
| // We can't make all the matchables in a match_result the same type because |
| // idents can be paths, which can be exprs. |
| |
| // If we want better match failure error messages (like in Fortifying Syntax), |
| // we'll want to return something indicating amount of progress and location |
| // of failure instead of `none`. |
| type match_result = Option<arb_depth<matchable>>; |
| type selector = fn@(matchable) -> match_result; |
| |
| fn elts_to_ell(cx: ext_ctxt, elts: ~[@expr]) -> |
| {pre: ~[@expr], rep: Option<@expr>, post: ~[@expr]} { |
| let mut idx: uint = 0u; |
| let mut res = None; |
| for elts.each |elt| { |
| match elt.node { |
| expr_mac(m) => match m.node { |
| ast::mac_ellipsis => { |
| if res.is_some() { |
| cx.span_fatal(m.span, ~"only one ellipsis allowed"); |
| } |
| res = |
| Some({pre: vec::slice(elts, 0u, idx - 1u), |
| rep: Some(elts[idx - 1u]), |
| post: vec::slice(elts, idx + 1u, vec::len(elts))}); |
| } |
| _ => () |
| }, |
| _ => () |
| } |
| idx += 1u; |
| } |
| return match res { |
| Some(val) => val, |
| None => {pre: elts, rep: None, post: ~[]} |
| } |
| } |
| |
| fn option_flatten_map<T: Copy, U: Copy>(f: fn@(T) -> Option<U>, v: ~[T]) -> |
| Option<~[U]> { |
| let mut res = ~[]; |
| for v.each |elem| { |
| match f(*elem) { |
| None => return None, |
| Some(fv) => res.push(fv) |
| } |
| } |
| return Some(res); |
| } |
| |
| fn a_d_map(ad: arb_depth<matchable>, f: selector) -> match_result { |
| match ad { |
| leaf(x) => return f(x), |
| seq(ads, span) => match option_flatten_map(|x| a_d_map(x, f), *ads) { |
| None => return None, |
| Some(ts) => return Some(seq(@ts, span)) |
| } |
| } |
| } |
| |
| fn compose_sels(s1: selector, s2: selector) -> selector { |
| fn scomp(s1: selector, s2: selector, m: matchable) -> match_result { |
| return match s1(m) { |
| None => None, |
| Some(matches) => a_d_map(matches, s2) |
| } |
| } |
| return { |x| scomp(s1, s2, x) }; |
| } |
| |
| |
| |
| type binders = |
| {real_binders: HashMap<ident, selector>, |
| literal_ast_matchers: DVec<selector>}; |
| type bindings = HashMap<ident, arb_depth<matchable>>; |
| |
| fn acumm_bindings(_cx: ext_ctxt, _b_dest: bindings, _b_src: bindings) { } |
| |
| /* these three functions are the big moving parts */ |
| |
| /* create the selectors needed to bind and verify the pattern */ |
| |
| fn pattern_to_selectors(cx: ext_ctxt, e: @expr) -> binders { |
| let res: binders = |
| {real_binders: HashMap(), |
| literal_ast_matchers: DVec()}; |
| //this oughta return binders instead, but macro args are a sequence of |
| //expressions, rather than a single expression |
| fn trivial_selector(m: matchable) -> match_result { |
| return Some(leaf(m)); |
| } |
| p_t_s_rec(cx, match_expr(e), trivial_selector, res); |
| move res |
| } |
| |
| |
| |
| /* use the selectors on the actual arguments to the macro to extract |
| bindings. Most of the work is done in p_t_s, which generates the |
| selectors. */ |
| |
| fn use_selectors_to_bind(b: binders, e: @expr) -> Option<bindings> { |
| let res = HashMap(); |
| //need to do this first, to check vec lengths. |
| for b.literal_ast_matchers.each |sel| { |
| match (*sel)(match_expr(e)) { None => return None, _ => () } |
| } |
| let mut never_mind: bool = false; |
| for b.real_binders.each |key, val| { |
| match val(match_expr(e)) { |
| None => never_mind = true, |
| Some(mtc) => { res.insert(key, mtc); } |
| } |
| }; |
| //HACK: `ret` doesn't work in `for each` |
| if never_mind { return None; } |
| return Some(res); |
| } |
| |
| /* use the bindings on the body to generate the expanded code */ |
| |
| fn transcribe(cx: ext_ctxt, b: bindings, body: @expr) -> @expr { |
| let idx_path: @mut ~[uint] = @mut ~[]; |
| fn new_id(_old: node_id, cx: ext_ctxt) -> node_id { return cx.next_id(); } |
| fn new_span(cx: ext_ctxt, sp: span) -> span { |
| /* this discards information in the case of macro-defining macros */ |
| return {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; |
| } |
| let afp = default_ast_fold(); |
| let f_pre = |
| @{fold_ident: |x,y|transcribe_ident(cx, b, idx_path, x, y), |
| fold_path: |x,y|transcribe_path(cx, b, idx_path, x, y), |
| fold_expr: |x,y,z| |
| transcribe_expr(cx, b, idx_path, x, y, z, afp.fold_expr) |
| , |
| fold_ty: |x,y,z| |
| transcribe_type(cx, b, idx_path, |
| x, y, z, afp.fold_ty) |
| , |
| fold_block: |x,y,z| |
| transcribe_block(cx, b, idx_path, x, y, z, afp.fold_block) |
| , |
| map_exprs: |x,y| |
| transcribe_exprs(cx, b, idx_path, x, y) |
| , |
| new_id: |x|new_id(x, cx), |
| .. *afp}; |
| let f = make_fold(f_pre); |
| let result = f.fold_expr(body); |
| return result; |
| } |
| |
| |
| /* helper: descend into a matcher */ |
| pure fn follow(m: arb_depth<matchable>, idx_path: &[uint]) -> |
| arb_depth<matchable> { |
| let mut res: arb_depth<matchable> = m; |
| for vec::each(idx_path) |idx| { |
| res = match res { |
| leaf(_) => return res,/* end of the line */ |
| seq(new_ms, _) => new_ms[*idx] |
| } |
| } |
| return res; |
| } |
| |
| fn follow_for_trans(cx: ext_ctxt, mmaybe: Option<arb_depth<matchable>>, |
| idx_path: @mut ~[uint]) -> Option<matchable> { |
| match mmaybe { |
| None => return None, |
| Some(m) => { |
| return match follow(m, *idx_path) { |
| seq(_, sp) => { |
| cx.span_fatal(sp, |
| ~"syntax matched under ... but not " + |
| ~"used that way.") |
| } |
| leaf(m) => return Some(m) |
| } |
| } |
| } |
| |
| } |
| |
| /* helper for transcribe_exprs: what vars from `b` occur in `e`? */ |
| fn free_vars(b: bindings, e: @expr, it: fn(ident)) { |
| let idents = HashMap(); |
| fn mark_ident(&&i: ident, _fld: ast_fold, b: bindings, |
| idents: HashMap<ident, ()>) -> ident { |
| if b.contains_key(i) { idents.insert(i, ()); } |
| return i; |
| } |
| // using fold is a hack: we want visit, but it doesn't hit idents ) : |
| // solve this with macros |
| let f_pre = |
| @{fold_ident: |x,y|mark_ident(x, y, b, idents), |
| .. *default_ast_fold()}; |
| let f = make_fold(f_pre); |
| f.fold_expr(e); // ignore result |
| for idents.each_key |x| { it(x); }; |
| } |
| |
| fn wrong_occurs(cx: ext_ctxt, l: ident, l_c: uint, r: ident, r_c: uint) |
| -> ~str { |
| fmt!("'%s' occurs %u times, but '%s' occurs %u times", |
| *cx.parse_sess().interner.get(l), l_c, |
| *cx.parse_sess().interner.get(r), r_c) |
| } |
| |
| /* handle sequences (anywhere in the AST) of exprs, either real or ...ed */ |
| fn transcribe_exprs(cx: ext_ctxt, b: bindings, idx_path: @mut ~[uint], |
| recur: fn@(&&v: @expr) -> @expr, |
| exprs: ~[@expr]) -> ~[@expr] { |
| match elts_to_ell(cx, exprs) { |
| {pre: pre, rep: repeat_me_maybe, post: post} => { |
| let mut res = vec::map(pre, |x| recur(*x)); |
| match repeat_me_maybe { |
| None => (), |
| Some(repeat_me) => { |
| let mut repeat: Option<{rep_count: uint, name: ident}> = None; |
| /* we need to walk over all the free vars in lockstep, except for |
| the leaves, which are just duplicated */ |
| do free_vars(b, repeat_me) |fv| { |
| let fv_depth = b.get(fv); |
| let cur_pos = follow(fv_depth, *idx_path); |
| match cur_pos { |
| leaf(_) => (), |
| seq(ms, _) => { |
| match repeat { |
| None => { |
| repeat = Some({rep_count: vec::len(*ms), name: fv}); |
| } |
| Some({rep_count: old_len, name: old_name}) => { |
| let len = vec::len(*ms); |
| if old_len != len { |
| let msg = wrong_occurs(cx, fv, len, |
| old_name, old_len); |
| cx.span_fatal(repeat_me.span, msg); |
| } |
| } |
| } |
| } |
| } |
| }; |
| match repeat { |
| None => { |
| cx.span_fatal(repeat_me.span, |
| ~"'...' surrounds an expression without any" + |
| ~" repeating syntax variables"); |
| } |
| Some({rep_count: rc, _}) => { |
| /* Whew, we now know how how many times to repeat */ |
| let mut idx: uint = 0u; |
| while idx < rc { |
| idx_path.push(idx); |
| res.push(recur(repeat_me)); // whew! |
| idx_path.pop(); |
| idx += 1u; |
| } |
| } |
| } |
| } |
| } |
| res = vec::append(res, vec::map(post, |x| recur(*x))); |
| return res; |
| } |
| } |
| } |
| |
| |
| |
| // substitute, in a position that's required to be an ident |
| fn transcribe_ident(cx: ext_ctxt, b: bindings, idx_path: @mut ~[uint], |
| &&i: ident, _fld: ast_fold) -> ident { |
| return match follow_for_trans(cx, b.find(i), idx_path) { |
| Some(match_ident(a_id)) => a_id.node, |
| Some(m) => match_error(cx, m, ~"an identifier"), |
| None => i |
| } |
| } |
| |
| |
| fn transcribe_path(cx: ext_ctxt, b: bindings, idx_path: @mut ~[uint], |
| p: path, _fld: ast_fold) -> path { |
| // Don't substitute into qualified names. |
| if vec::len(p.types) > 0u || vec::len(p.idents) != 1u { return p; } |
| match follow_for_trans(cx, b.find(p.idents[0]), idx_path) { |
| Some(match_ident(id)) => { |
| {span: id.span, global: false, idents: ~[id.node], |
| rp: None, types: ~[]} |
| } |
| Some(match_path(a_pth)) => *a_pth, |
| Some(m) => match_error(cx, m, ~"a path"), |
| None => p |
| } |
| } |
| |
| |
| fn transcribe_expr(cx: ext_ctxt, b: bindings, idx_path: @mut ~[uint], |
| e: ast::expr_, s: span, fld: ast_fold, |
| orig: fn@(ast::expr_, span, ast_fold)->(ast::expr_, span)) |
| -> (ast::expr_, span) |
| { |
| return match e { |
| expr_path(p) => { |
| // Don't substitute into qualified names. |
| if vec::len(p.types) > 0u || vec::len(p.idents) != 1u { |
| (e, s); |
| } |
| match follow_for_trans(cx, b.find(p.idents[0]), idx_path) { |
| Some(match_ident(id)) => { |
| (expr_path(@{span: id.span, |
| global: false, |
| idents: ~[id.node], |
| rp: None, |
| types: ~[]}), id.span) |
| } |
| Some(match_path(a_pth)) => (expr_path(a_pth), s), |
| Some(match_expr(a_exp)) => (a_exp.node, a_exp.span), |
| Some(m) => match_error(cx, m, ~"an expression"), |
| None => orig(e, s, fld) |
| } |
| } |
| _ => orig(e, s, fld) |
| } |
| } |
| |
| fn transcribe_type(cx: ext_ctxt, b: bindings, idx_path: @mut ~[uint], |
| t: ast::ty_, s: span, fld: ast_fold, |
| orig: fn@(ast::ty_, span, ast_fold) -> (ast::ty_, span)) |
| -> (ast::ty_, span) |
| { |
| return match t { |
| ast::ty_path(pth, _) => { |
| match path_to_ident(pth) { |
| Some(id) => { |
| match follow_for_trans(cx, b.find(id), idx_path) { |
| Some(match_ty(ty)) => (ty.node, ty.span), |
| Some(m) => match_error(cx, m, ~"a type"), |
| None => orig(t, s, fld) |
| } |
| } |
| None => orig(t, s, fld) |
| } |
| } |
| _ => orig(t, s, fld) |
| } |
| } |
| |
| |
| /* for parsing reasons, syntax variables bound to blocks must be used like |
| `{v}` */ |
| |
| fn transcribe_block(cx: ext_ctxt, b: bindings, idx_path: @mut ~[uint], |
| blk: blk_, s: span, fld: ast_fold, |
| orig: fn@(blk_, span, ast_fold) -> (blk_, span)) |
| -> (blk_, span) |
| { |
| return match block_to_ident(blk) { |
| Some(id) => { |
| match follow_for_trans(cx, b.find(id), idx_path) { |
| Some(match_block(new_blk)) => (new_blk.node, new_blk.span), |
| |
| // possibly allow promotion of ident/path/expr to blocks? |
| Some(m) => match_error(cx, m, ~"a block"), |
| None => orig(blk, s, fld) |
| } |
| } |
| None => orig(blk, s, fld) |
| } |
| } |
| |
| |
| /* traverse the pattern, building instructions on how to bind the actual |
| argument. ps accumulates instructions on navigating the tree.*/ |
| fn p_t_s_rec(cx: ext_ctxt, m: matchable, s: selector, b: binders) { |
| |
| //it might be possible to traverse only exprs, not matchables |
| match m { |
| match_expr(e) => { |
| match e.node { |
| expr_path(p_pth) => p_t_s_r_path(cx, p_pth, s, b), |
| expr_vec(p_elts, _) => { |
| match elts_to_ell(cx, p_elts) { |
| {pre: pre, rep: Some(repeat_me), post: post} => { |
| p_t_s_r_length(cx, vec::len(pre) + vec::len(post), true, s, |
| b); |
| if vec::len(pre) > 0u { |
| p_t_s_r_actual_vector(cx, pre, true, s, b); |
| } |
| p_t_s_r_ellipses(cx, repeat_me, vec::len(pre), s, b); |
| |
| if vec::len(post) > 0u { |
| cx.span_unimpl(e.span, |
| ~"matching after `...` not yet supported"); |
| } |
| } |
| {pre: pre, rep: None, post: post} => { |
| if post.len() > 0 { |
| cx.bug(~"elts_to_ell provided an invalid result"); |
| } |
| p_t_s_r_length(cx, vec::len(pre), false, s, b); |
| p_t_s_r_actual_vector(cx, pre, false, s, b); |
| } |
| } |
| } |
| /* FIXME (#2251): handle embedded types and blocks, at least */ |
| expr_mac(mac) => { |
| p_t_s_r_mac(cx, mac, s, b); |
| } |
| _ => { |
| fn select(cx: ext_ctxt, m: matchable, pat: @expr) -> |
| match_result { |
| return match m { |
| match_expr(e) => { |
| if box::ptr_eq(e, pat) { |
| // XXX: Is this right? |
| Some(leaf(match_exact)) |
| } else { |
| None |
| } |
| } |
| _ => cx.bug(~"broken traversal in p_t_s_r") |
| } |
| } |
| b.literal_ast_matchers.push(|x| select(cx, x, e)); |
| } |
| } |
| } |
| _ => cx.bug(~"undocumented invariant in p_t_s_rec") |
| } |
| } |
| |
| |
| /* make a match more precise */ |
| fn specialize_match(m: matchable) -> matchable { |
| return match m { |
| match_expr(e) => { |
| match e.node { |
| expr_path(pth) => { |
| match path_to_ident(pth) { |
| Some(id) => match_ident(respan(pth.span, id)), |
| None => match_path(pth) |
| } |
| } |
| _ => m |
| } |
| } |
| _ => m |
| } |
| } |
| |
| /* pattern_to_selectors helper functions */ |
| fn p_t_s_r_path(cx: ext_ctxt, p: @path, s: selector, b: binders) { |
| match path_to_ident(p) { |
| Some(p_id) => { |
| fn select(cx: ext_ctxt, m: matchable) -> match_result { |
| return match m { |
| match_expr(*) => Some(leaf(specialize_match(m))), |
| _ => cx.bug(~"broken traversal in p_t_s_r") |
| } |
| } |
| if b.real_binders.contains_key(p_id) { |
| cx.span_fatal(p.span, ~"duplicate binding identifier"); |
| } |
| b.real_binders.insert(p_id, compose_sels(s, |x| select(cx, x))); |
| } |
| None => () |
| } |
| } |
| |
| fn block_to_ident(blk: blk_) -> Option<ident> { |
| if vec::len(blk.stmts) != 0u { return None; } |
| return match blk.expr { |
| Some(expr) => match expr.node { |
| expr_path(pth) => path_to_ident(pth), |
| _ => None |
| }, |
| None => None |
| } |
| } |
| |
| fn p_t_s_r_mac(cx: ext_ctxt, mac: ast::mac, _s: selector, _b: binders) { |
| fn select_pt_1(cx: ext_ctxt, m: matchable, |
| fn_m: fn(ast::mac) -> match_result) -> match_result { |
| return match m { |
| match_expr(e) => match e.node { |
| expr_mac(mac) => fn_m(mac), |
| _ => None |
| }, |
| _ => cx.bug(~"broken traversal in p_t_s_r") |
| } |
| } |
| fn no_des(cx: ext_ctxt, sp: span, syn: ~str) -> ! { |
| cx.span_fatal(sp, ~"destructuring " + syn + ~" is not yet supported"); |
| } |
| match mac.node { |
| ast::mac_ellipsis => cx.span_fatal(mac.span, ~"misused `...`"), |
| ast::mac_invoc(_, _, _) => no_des(cx, mac.span, ~"macro calls"), |
| ast::mac_invoc_tt(_, _) => no_des(cx, mac.span, ~"macro calls"), |
| ast::mac_aq(_,_) => no_des(cx, mac.span, ~"antiquotes"), |
| ast::mac_var(_) => no_des(cx, mac.span, ~"antiquote variables") |
| } |
| } |
| |
| fn p_t_s_r_ellipses(cx: ext_ctxt, repeat_me: @expr, offset: uint, s: selector, |
| b: binders) { |
| fn select(cx: ext_ctxt, repeat_me: @expr, offset: uint, m: matchable) -> |
| match_result { |
| return match m { |
| match_expr(e) => { |
| match e.node { |
| expr_vec(arg_elts, _) => { |
| let mut elts = ~[]; |
| let mut idx = offset; |
| while idx < vec::len(arg_elts) { |
| elts.push(leaf(match_expr(arg_elts[idx]))); |
| idx += 1u; |
| } |
| |
| // using repeat_me.span is a little wacky, but the |
| // error we want to report is one in the macro def |
| Some(seq(@elts, repeat_me.span)) |
| } |
| _ => None |
| } |
| } |
| _ => cx.bug(~"broken traversal in p_t_s_r") |
| } |
| } |
| p_t_s_rec(cx, match_expr(repeat_me), |
| compose_sels(s, |x| select(cx, repeat_me, offset, x)), b); |
| } |
| |
| |
| fn p_t_s_r_length(cx: ext_ctxt, len: uint, at_least: bool, s: selector, |
| b: binders) { |
| fn len_select(_cx: ext_ctxt, m: matchable, at_least: bool, len: uint) -> |
| match_result { |
| return match m { |
| match_expr(e) => { |
| match e.node { |
| expr_vec(arg_elts, _) => { |
| let actual_len = vec::len(arg_elts); |
| if at_least && actual_len >= len || actual_len == len { |
| Some(leaf(match_exact)) |
| } else { None } |
| } |
| _ => None |
| } |
| } |
| _ => None |
| } |
| } |
| b.literal_ast_matchers.push( |
| compose_sels(s, |x| len_select(cx, x, at_least, len))); |
| } |
| |
| fn p_t_s_r_actual_vector(cx: ext_ctxt, elts: ~[@expr], _repeat_after: bool, |
| s: selector, b: binders) { |
| let mut idx: uint = 0u; |
| while idx < vec::len(elts) { |
| fn select(cx: ext_ctxt, m: matchable, idx: uint) -> match_result { |
| return match m { |
| match_expr(e) => { |
| match e.node { |
| expr_vec(arg_elts, _) => { |
| Some(leaf(match_expr(arg_elts[idx]))) |
| } |
| _ => None |
| } |
| } |
| _ => cx.bug(~"broken traversal in p_t_s_r") |
| } |
| } |
| p_t_s_rec(cx, match_expr(elts[idx]), |
| compose_sels(s, |x, copy idx| select(cx, x, idx)), b); |
| idx += 1u; |
| } |
| } |
| |
| fn add_new_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg, |
| _body: ast::mac_body) -> base::macro_def { |
| let args = get_mac_args_no_max(cx, sp, arg, 0u, ~"macro"); |
| |
| let mut macro_name: Option<~str> = None; |
| let mut clauses: ~[@clause] = ~[]; |
| for args.each |arg| { |
| match arg.node { |
| expr_vec(elts, _) => { |
| if vec::len(elts) != 2u { |
| cx.span_fatal((*arg).span, |
| ~"extension clause must consist of ~[" + |
| ~"macro invocation, expansion body]"); |
| } |
| |
| |
| match elts[0u].node { |
| expr_mac(mac) => { |
| match mac.node { |
| mac_invoc(pth, invoc_arg, _) => { |
| match path_to_ident(pth) { |
| Some(id) => { |
| let id_str = cx.str_of(id); |
| match macro_name { |
| None => macro_name = Some(id_str), |
| Some(other_id) => if id_str != other_id { |
| cx.span_fatal(pth.span, |
| ~"macro name must be " + |
| ~"consistent"); |
| } |
| } |
| }, |
| None => cx.span_fatal(pth.span, |
| ~"macro name must not be a path") |
| } |
| let arg = match invoc_arg { |
| Some(arg) => arg, |
| None => cx.span_fatal(mac.span, |
| ~"macro must have arguments") |
| }; |
| clauses.push(@{params: pattern_to_selectors(cx, arg), |
| body: elts[1u]}); |
| |
| // FIXME (#2251): check duplicates (or just simplify |
| // the macro arg situation) |
| } |
| _ => { |
| cx.span_bug(mac.span, ~"undocumented invariant in \ |
| add_extension"); |
| } |
| } |
| } |
| _ => { |
| cx.span_fatal(elts[0u].span, |
| ~"extension clause must" + |
| ~" start with a macro invocation."); |
| } |
| } |
| } |
| _ => { |
| cx.span_fatal((*arg).span, |
| ~"extension must be ~[clause, " + ~" ...]"); |
| } |
| } |
| } |
| |
| let ext = |a,b,c,d, move clauses| generic_extension(a,b,c,d,clauses); |
| |
| return {name: |
| match macro_name { |
| Some(id) => id, |
| None => cx.span_fatal(sp, ~"macro definition must have " + |
| ~"at least one clause") |
| }, |
| ext: normal({expander: ext, span: Some(arg.get().span)})}; |
| |
| fn generic_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg, |
| _body: ast::mac_body, |
| clauses: ~[@clause]) -> @expr { |
| let arg = match arg { |
| Some(arg) => arg, |
| None => cx.span_fatal(sp, ~"macro must have arguments") |
| }; |
| for clauses.each |c| { |
| match use_selectors_to_bind(c.params, arg) { |
| Some(bindings) => return transcribe(cx, bindings, c.body), |
| None => loop |
| } |
| } |
| cx.span_fatal(sp, ~"no clauses match macro invocation"); |
| } |
| } |
| |
| |
| |
| // |
| // Local Variables: |
| // mode: rust |
| // fill-column: 78; |
| // indent-tabs-mode: nil |
| // c-basic-offset: 4 |
| // buffer-file-coding-system: utf-8-unix |
| // End: |
| // |