diff options
Diffstat (limited to 'src/libsyntax/ext/qquote.rs')
| -rw-r--r-- | src/libsyntax/ext/qquote.rs | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/libsyntax/ext/qquote.rs b/src/libsyntax/ext/qquote.rs new file mode 100644 index 00000000000..a6e08439356 --- /dev/null +++ b/src/libsyntax/ext/qquote.rs @@ -0,0 +1,337 @@ +import ast::{crate, expr_, mac_invoc, + mac_aq, mac_var}; +import fold::*; +import visit::*; +import ext::base::*; +import ext::build::*; +import parse::parser; +import parse::parser::parse_from_source_str; +import dvec::{dvec, extensions}; + +import print::*; +import io::*; + +import codemap::span; + +type aq_ctxt = @{lo: uint, + gather: dvec<{lo: uint, hi: uint, + e: @ast::expr, + constr: str}>}; +enum fragment { + from_expr(@ast::expr), + from_ty(@ast::ty) +} + +iface qq_helper { + fn span() -> span; + fn visit(aq_ctxt, vt<aq_ctxt>); + fn extract_mac() -> option<ast::mac_>; + fn mk_parse_fn(ext_ctxt,span) -> @ast::expr; + fn get_fold_fn() -> str; +} + +impl of qq_helper for @ast::crate { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_crate(*self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_crate"]) + } + fn get_fold_fn() -> str {"fold_crate"} +} +impl of qq_helper for @ast::expr { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_expr(self, cx, v);} + fn extract_mac() -> option<ast::mac_> { + alt (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, ["syntax", "ext", "qquote", "parse_expr"]) + } + fn get_fold_fn() -> str {"fold_expr"} +} +impl of qq_helper for @ast::ty { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_ty(self, cx, v);} + fn extract_mac() -> option<ast::mac_> { + alt (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, ["syntax", "ext", "qquote", "parse_ty"]) + } + fn get_fold_fn() -> str {"fold_ty"} +} +impl of qq_helper for @ast::item { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_item(self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_item"]) + } + fn get_fold_fn() -> str {"fold_item"} +} +impl of qq_helper for @ast::stmt { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_stmt(self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_stmt"]) + } + fn get_fold_fn() -> str {"fold_stmt"} +} +impl of qq_helper for @ast::pat { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_pat(self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_pat"]) + } + fn get_fold_fn() -> str {"fold_pat"} +} + +fn gather_anti_quotes<N: qq_helper>(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)} + with *default_visitor()}; + let cx = @{lo:lo, gather: dvec()}; + node.visit(cx, mk_vt(v)); + // FIXME: Maybe this is an overkill (merge_sort), it might be better + // to just keep the gather array in sorted order ... (Issue #2250) + cx.gather.swap { |v| + vec::to_mut(std::sort::merge_sort({|a,b| a.lo < b.lo}, v)) + }; + ret cx; +} + +fn visit_aq<T:qq_helper>(node: T, constr: str, &&cx: aq_ctxt, v: vt<aq_ctxt>) +{ + alt (node.extract_mac()) { + some(mac_aq(sp, e)) { + cx.gather.push({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"; + option::iter(arg) {|arg| + let args: [@ast::expr] = + alt 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"); + } + alt (args[0].node) { + ast::expr_path(@{idents: id, _}) if vec::len(id) == 1u + {what = id[0]} + _ {ecx.span_fatal(args[0].span, "expected an identifier");} + } + } + let body = get_mac_body(ecx,_sp,body); + + ret alt 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() } + +fn parse_item(p: parser) -> @ast::item { + alt p.parse_item([], ast::public) { + some(item) { item } + none { fail "parse_item: parsing an item failed"; } + } +} + +fn finish<T: qq_helper> + (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(); + str::chars_iter(*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; + } + alt state { + active {str::push_char(str2, ch);} + skip(1u) {state = blank;} + skip(sk) {state = skip (sk-1u);} + blank if is_space(ch) {str::push_char(str2, ch);} + blank {str::push_char(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, ["ext_cx"], "cfg"), []) + }; + + let parse_sess_call = {|| + mk_call_(cx, sp, mk_access(cx, sp, ["ext_cx"], "parse_sess"), []) + }; + + let pcall = mk_call(cx,sp, + ["syntax", "parse", "parser", + "parse_from_source_str"], + [node.mk_parse_fn(cx,sp), + mk_str(cx,sp, fname), + mk_call(cx,sp, + ["syntax","ext","qquote", "mk_file_substr"], + [mk_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_str(cx,sp, str2)), + cfg_call(), + parse_sess_call()] + ); + let mut rcall = pcall; + if (g_len > 0u) { + rcall = mk_call(cx,sp, + ["syntax", "ext", "qquote", "replace"], + [pcall, + mk_vec_e(cx,sp, qcx.gather.map_to_vec {|g| + mk_call(cx,sp, + ["syntax", "ext", "qquote", g.constr], + [g.e])}), + mk_path(cx,sp, + ["syntax", "ext", "qquote", + node.get_fold_fn()])]); + } + ret rcall; +} + +fn replace<T>(node: T, repls: [fragment], ff: fn (ast_fold, T) -> T) + -> T +{ + let aft = default_ast_fold(); + let f_pre = @{fold_expr: bind replace_expr(repls, _, _, _, + aft.fold_expr), + fold_ty: bind replace_ty(repls, _, _, _, + aft.fold_ty) + with *aft}; + ret 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)} +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) +{ + alt e { + ast::expr_mac({node: mac_var(i), _}) { + alt (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) +{ + alt e { + ast::ty_mac({node: mac_var(i), _}) { + alt (repls[i]) { + from_ty(r) {(r.node, r.span)} + _ {fail /* fixme error message */}}} + _ {orig(e,s,fld)} + } +} + +fn print_expr(expr: @ast::expr) { + let stdout = io::stdout(); + let pp = pprust::rust_printer(stdout); + pprust::print_expr(pp, expr); + pp::eof(pp.s); + stdout.write_str("\n"); +} + +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: |
