use ast::{crate, expr_, mac_invoc, mac_aq, mac_var}; use parse::parser; use parse::parser::parse_from_source_str; use dvec::DVec; use parse::token::ident_interner; use fold::*; use visit::*; use ext::base::*; use ext::build::*; use print::*; use io::*; use codemap::span; struct gather_item { lo: uint, hi: uint, e: @ast::expr, constr: ~str } type aq_ctxt = @{lo: uint, gather: DVec}; enum fragment { from_expr(@ast::expr), from_ty(@ast::ty) } fn ids_ext(cx: ext_ctxt, strs: ~[~str]) -> ~[ast::ident] { strs.map(|str| cx.parse_sess().interner.intern(@*str)) } fn id_ext(cx: ext_ctxt, str: ~str) -> ast::ident { cx.parse_sess().interner.intern(@str) } trait qq_helper { fn span() -> span; fn visit(aq_ctxt, vt); fn extract_mac() -> Option; fn mk_parse_fn(ext_ctxt,span) -> @ast::expr; fn get_fold_fn() -> ~str; } impl @ast::crate: qq_helper { fn span() -> span {self.span} fn visit(cx: aq_ctxt, v: vt) {visit_crate(*self, cx, v);} fn extract_mac() -> Option {fail} fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { mk_path(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"parse_crate"])) } fn get_fold_fn() -> ~str {~"fold_crate"} } impl @ast::expr: qq_helper { fn span() -> span {self.span} fn visit(cx: aq_ctxt, v: vt) {visit_expr(self, cx, v);} fn extract_mac() -> Option { match (self.node) { ast::expr_mac({node: mac, _}) => Some(mac), _ => None } } fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { mk_path(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"parse_expr"])) } fn get_fold_fn() -> ~str {~"fold_expr"} } impl @ast::ty: qq_helper { fn span() -> span {self.span} fn visit(cx: aq_ctxt, v: vt) {visit_ty(self, cx, v);} fn extract_mac() -> Option { match (self.node) { ast::ty_mac({node: mac, _}) => Some(mac), _ => None } } fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { mk_path(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"parse_ty"])) } fn get_fold_fn() -> ~str {~"fold_ty"} } impl @ast::item: qq_helper { fn span() -> span {self.span} fn visit(cx: aq_ctxt, v: vt) {visit_item(self, cx, v);} fn extract_mac() -> Option {fail} fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { mk_path(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"parse_item"])) } fn get_fold_fn() -> ~str {~"fold_item"} } impl @ast::stmt: qq_helper { fn span() -> span {self.span} fn visit(cx: aq_ctxt, v: vt) {visit_stmt(self, cx, v);} fn extract_mac() -> Option {fail} fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { mk_path(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"parse_stmt"])) } fn get_fold_fn() -> ~str {~"fold_stmt"} } impl @ast::pat: qq_helper { fn span() -> span {self.span} fn visit(cx: aq_ctxt, v: vt) {visit_pat(self, cx, v);} fn extract_mac() -> Option {fail} fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { mk_path(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"parse_pat"])) } fn get_fold_fn() -> ~str {~"fold_pat"} } fn gather_anti_quotes(lo: uint, node: N) -> aq_ctxt { let v = @{visit_expr: |node, &&cx, v| visit_aq(node, ~"from_expr", cx, v), visit_ty: |node, &&cx, v| visit_aq(node, ~"from_ty", cx, v), .. *default_visitor()}; let cx = @{lo:lo, gather: DVec()}; node.visit(cx, mk_vt(v)); // FIXME (#2250): Maybe this is an overkill (merge_sort), it might // be better to just keep the gather array in sorted order. do cx.gather.swap |v| { pure fn by_lo(a: &gather_item, b: &gather_item) -> bool { a.lo < b.lo } std::sort::merge_sort(by_lo, v) }; return cx; } fn visit_aq(node: T, constr: ~str, &&cx: aq_ctxt, v: vt) { match (node.extract_mac()) { Some(mac_aq(sp, e)) => { cx.gather.push(gather_item { lo: sp.lo - cx.lo, hi: sp.hi - cx.lo, e: e, constr: constr}); } _ => node.visit(cx, v) } } fn is_space(c: char) -> bool { parse::lexer::is_whitespace(c) } fn expand_ast(ecx: ext_ctxt, _sp: span, arg: ast::mac_arg, body: ast::mac_body) -> @ast::expr { let mut what = ~"expr"; do arg.iter |arg| { let args: ~[@ast::expr] = match arg.node { ast::expr_vec(elts, _) => elts, _ => { ecx.span_fatal (_sp, ~"#ast requires arguments of the form `~[...]`.") } }; if vec::len::<@ast::expr>(args) != 1u { ecx.span_fatal(_sp, ~"#ast requires exactly one arg"); } match (args[0].node) { ast::expr_path(@{idents: id, _}) if vec::len(id) == 1u => what = *ecx.parse_sess().interner.get(id[0]), _ => ecx.span_fatal(args[0].span, ~"expected an identifier") } } let body = get_mac_body(ecx,_sp,body); return match what { ~"crate" => finish(ecx, body, parse_crate), ~"expr" => finish(ecx, body, parse_expr), ~"ty" => finish(ecx, body, parse_ty), ~"item" => finish(ecx, body, parse_item), ~"stmt" => finish(ecx, body, parse_stmt), ~"pat" => finish(ecx, body, parse_pat), _ => ecx.span_fatal(_sp, ~"unsupported ast type") }; } fn parse_crate(p: parser) -> @ast::crate { p.parse_crate_mod(~[]) } fn parse_ty(p: parser) -> @ast::ty { p.parse_ty(false) } fn parse_stmt(p: parser) -> @ast::stmt { p.parse_stmt(~[]) } fn parse_expr(p: parser) -> @ast::expr { p.parse_expr() } fn parse_pat(p: parser) -> @ast::pat { p.parse_pat(true) } fn parse_item(p: parser) -> @ast::item { match p.parse_item(~[]) { Some(item) => item, None => fail ~"parse_item: parsing an item failed" } } fn finish (ecx: ext_ctxt, body: ast::mac_body_, f: fn (p: parser) -> T) -> @ast::expr { let cm = ecx.codemap(); let str = @codemap::span_to_snippet(body.span, cm); debug!("qquote--str==%?", str); let fname = codemap::mk_substr_filename(cm, body.span); let node = parse_from_source_str (f, fname, codemap::fss_internal(body.span), str, ecx.cfg(), ecx.parse_sess()); let loc = codemap::lookup_char_pos(cm, body.span.lo); let sp = node.span(); let qcx = gather_anti_quotes(sp.lo, node); let cx = qcx; for uint::range(1u, cx.gather.len()) |i| { assert cx.gather[i-1u].lo < cx.gather[i].lo; // ^^ check that the vector is sorted assert cx.gather[i-1u].hi <= cx.gather[i].lo; // ^^ check that the spans are non-overlapping } let mut str2 = ~""; enum state {active, skip(uint), blank}; let mut state = active; let mut i = 0u, j = 0u; let g_len = cx.gather.len(); for str::chars_each(*str) |ch| { if (j < g_len && i == cx.gather[j].lo) { assert ch == '$'; let repl = fmt!("$%u ", j); state = skip(str::char_len(repl)); str2 += repl; } match copy state { active => str::push_char(&mut str2, ch), skip(1u) => state = blank, skip(sk) => state = skip (sk-1u), blank if is_space(ch) => str::push_char(&mut str2, ch), blank => str::push_char(&mut str2, ' ') } i += 1u; if (j < g_len && i == cx.gather[j].hi) { assert ch == ')'; state = active; j += 1u; } } let cx = ecx; let cfg_call = || mk_call_( cx, sp, mk_access(cx, sp, ids_ext(cx, ~[~"ext_cx"]), id_ext(cx, ~"cfg")), ~[]); let parse_sess_call = || mk_call_( cx, sp, mk_access(cx, sp, ids_ext(cx, ~[~"ext_cx"]), id_ext(cx, ~"parse_sess")), ~[]); let pcall = mk_call(cx,sp, ids_ext(cx, ~[~"syntax", ~"parse", ~"parser", ~"parse_from_source_str"]), ~[node.mk_parse_fn(cx,sp), mk_uniq_str(cx,sp, fname), mk_call(cx,sp, ids_ext(cx, ~[~"syntax",~"ext", ~"qquote", ~"mk_file_substr"]), ~[mk_uniq_str(cx,sp, loc.file.name), mk_uint(cx,sp, loc.line), mk_uint(cx,sp, loc.col)]), mk_unary(cx,sp, ast::box(ast::m_imm), mk_uniq_str(cx,sp, str2)), cfg_call(), parse_sess_call()] ); let mut rcall = pcall; if (g_len > 0u) { rcall = mk_call(cx,sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", ~"replace"]), ~[pcall, mk_uniq_vec_e(cx,sp, qcx.gather.map_to_vec(|g| { mk_call(cx,sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", g.constr]), ~[g.e])})), mk_path(cx,sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"qquote", node.get_fold_fn()]))]); } return rcall; } fn replace(node: T, repls: ~[fragment], ff: fn (ast_fold, T) -> T) -> T { let aft = default_ast_fold(); let f_pre = @{fold_expr: |a,b,c|replace_expr(repls, a, b, c, aft.fold_expr), fold_ty: |a,b,c|replace_ty(repls, a, b, c, aft.fold_ty), .. *aft}; return ff(make_fold(f_pre), node); } fn fold_crate(f: ast_fold, &&n: @ast::crate) -> @ast::crate { @f.fold_crate(*n) } fn fold_expr(f: ast_fold, &&n: @ast::expr) -> @ast::expr {f.fold_expr(n)} fn fold_ty(f: ast_fold, &&n: @ast::ty) -> @ast::ty {f.fold_ty(n)} fn fold_item(f: ast_fold, &&n: @ast::item) -> @ast::item { f.fold_item(n).get() //HACK: we know we don't drop items } fn fold_stmt(f: ast_fold, &&n: @ast::stmt) -> @ast::stmt {f.fold_stmt(n)} fn fold_pat(f: ast_fold, &&n: @ast::pat) -> @ast::pat {f.fold_pat(n)} fn replace_expr(repls: ~[fragment], e: ast::expr_, s: span, fld: ast_fold, orig: fn@(ast::expr_, span, ast_fold)->(ast::expr_, span)) -> (ast::expr_, span) { match e { ast::expr_mac({node: mac_var(i), _}) => match (repls[i]) { from_expr(r) => (r.node, r.span), _ => fail /* fixme error message */ }, _ => orig(e,s,fld) } } fn replace_ty(repls: ~[fragment], e: ast::ty_, s: span, fld: ast_fold, orig: fn@(ast::ty_, span, ast_fold)->(ast::ty_, span)) -> (ast::ty_, span) { match e { ast::ty_mac({node: mac_var(i), _}) => match (repls[i]) { from_ty(r) => (r.node, r.span), _ => fail /* fixme error message */ }, _ => orig(e,s,fld) } } fn mk_file_substr(fname: ~str, line: uint, col: uint) -> codemap::file_substr { codemap::fss_external({filename: fname, line: line, col: col}) } // Local Variables: // mode: rust // fill-column: 78; // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // End: