diff options
Diffstat (limited to 'src/libsyntax/ext/simplext.rs')
| -rw-r--r-- | src/libsyntax/ext/simplext.rs | 750 |
1 files changed, 0 insertions, 750 deletions
diff --git a/src/libsyntax/ext/simplext.rs b/src/libsyntax/ext/simplext.rs deleted file mode 100644 index f13c5c9aff9..00000000000 --- a/src/libsyntax/ext/simplext.rs +++ /dev/null @@ -1,750 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -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(ref 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(ref 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(ref fv) => res.push((*fv)) - } - } - return Some(res); -} - -fn a_d_map(ad: arb_depth<matchable>, f: selector) -> match_result { - match ad { - leaf(ref 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(ref 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(ref 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 span {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(ref m) => { - return match follow((*m), *idx_path) { - seq(_, sp) => { - cx.span_fatal(sp, - ~"syntax matched under ... but not " + - ~"used that way.") - } - leaf(ref 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(ref 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(ref 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(ref 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(ref 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(ref new_blk)) => { - ((*new_blk).node, (*new_blk).span) - } - - // possibly allow promotion of ident/path/expr to blocks? - Some(ref 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(ref 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 managed::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(ref 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(ref 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(ref 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(ref 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: -// |
