about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorKevin Cantu <me@kevincantu.org>2012-05-29 21:35:12 -0700
committerBrian Anderson <banderson@mozilla.com>2012-05-31 11:15:00 -0700
commit7dcbaedd329295e1f2692bb2d9eae860a820d0a8 (patch)
treebe0272a0ec2e259da231f9147aaf1dc09b5758c5 /src/libsyntax/ext
parentff6cde788229484e128849d67e4f32b178b18c84 (diff)
downloadrust-7dcbaedd329295e1f2692bb2d9eae860a820d0a8.tar.gz
rust-7dcbaedd329295e1f2692bb2d9eae860a820d0a8.zip
Rename librustsyntax to libsyntax
Per issue #2418.
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/auto_serialize.rs864
-rw-r--r--src/libsyntax/ext/base.rs234
-rw-r--r--src/libsyntax/ext/build.rs78
-rw-r--r--src/libsyntax/ext/concat_idents.rs15
-rw-r--r--src/libsyntax/ext/env.rs35
-rw-r--r--src/libsyntax/ext/expand.rs157
-rw-r--r--src/libsyntax/ext/fmt.rs283
-rw-r--r--src/libsyntax/ext/ident_to_str.rs11
-rw-r--r--src/libsyntax/ext/log_syntax.rs15
-rw-r--r--src/libsyntax/ext/qquote.rs337
-rw-r--r--src/libsyntax/ext/simplext.rs778
-rw-r--r--src/libsyntax/ext/source_util.rs115
12 files changed, 2922 insertions, 0 deletions
diff --git a/src/libsyntax/ext/auto_serialize.rs b/src/libsyntax/ext/auto_serialize.rs
new file mode 100644
index 00000000000..dc632d6b6ac
--- /dev/null
+++ b/src/libsyntax/ext/auto_serialize.rs
@@ -0,0 +1,864 @@
+/*
+
+The compiler code necessary to implement the #[auto_serialize]
+extension.  The idea here is that type-defining items may be tagged
+with #[auto_serialize], which will cause us to generate a little
+companion module with the same name as the item.
+
+For example, a type like:
+
+    type node_id = uint;
+
+would generate two functions like:
+
+    fn serialize_node_id<S: serializer>(s: S, v: node_id) {
+        s.emit_uint(v);
+    }
+    fn deserialize_node_id<D: deserializer>(d: D) -> node_id {
+        d.read_uint()
+    }
+
+Other interesting scenarios are whe the item has type parameters or
+references other non-built-in types.  A type definition like:
+
+    type spanned<T> = {node: T, span: span};
+
+would yield functions like:
+
+    fn serialize_spanned<S: serializer,T>(s: S, v: spanned<T>, t: fn(T)) {
+         s.emit_rec(2u) {||
+             s.emit_rec_field("node", 0u) {||
+                 t(s.node);
+             };
+             s.emit_rec_field("span", 1u) {||
+                 serialize_span(s, s.span);
+             };
+         }
+    }
+    fn deserialize_spanned<D: deserializer>(d: D, t: fn() -> T) -> node_id {
+         d.read_rec(2u) {||
+             {node: d.read_rec_field("node", 0u, t),
+              span: d.read_rec_field("span", 1u) {||deserialize_span(d)}}
+         }
+    }
+
+In general, the code to serialize an instance `v` of a non-built-in
+type a::b::c<T0,...,Tn> looks like:
+
+    a::b::serialize_c(s, {|v| c_T0}, ..., {|v| c_Tn}, v)
+
+where `c_Ti` is the code to serialize an instance `v` of the type
+`Ti`.
+
+Similarly, the code to deserialize an instance of a non-built-in type
+`a::b::c<T0,...,Tn>` using the deserializer `d` looks like:
+
+    a::b::deserialize_c(d, {|| c_T0}, ..., {|| c_Tn})
+
+where `c_Ti` is the code to deserialize an instance of `Ti` using the
+deserializer `d`.
+
+TODO--Hygiene. Search for "__" strings.  We also assume "std" is the
+standard library.
+
+Misc notes:
+-----------
+
+I use move mode arguments for ast nodes that will get inserted as is
+into the tree.  This is intended to prevent us from inserting the same
+node twice.
+
+*/
+import base::*;
+import codemap::span;
+import std::map;
+import std::map::hashmap;
+
+export expand;
+
+// Transitional reexports so qquote can find the paths it is looking for
+mod syntax {
+    import ext;
+    export ext;
+    import parse;
+    export parse;
+}
+
+type ser_tps_map = map::hashmap<str, fn@(@ast::expr) -> [@ast::stmt]>;
+type deser_tps_map = map::hashmap<str, fn@() -> @ast::expr>;
+
+fn expand(cx: ext_ctxt,
+          span: span,
+          _mitem: ast::meta_item,
+          in_items: [@ast::item]) -> [@ast::item] {
+    fn not_auto_serialize(a: ast::attribute) -> bool {
+        attr::get_attr_name(a) != "auto_serialize"
+    }
+
+    fn filter_attrs(item: @ast::item) -> @ast::item {
+        @{attrs: vec::filter(item.attrs, not_auto_serialize)
+          with *item}
+    }
+
+    vec::flat_map(in_items) {|in_item|
+        alt in_item.node {
+          ast::item_ty(ty, tps, _) {
+            [filter_attrs(in_item)] + ty_fns(cx, in_item.ident, ty, tps)
+          }
+
+          ast::item_enum(variants, tps, _) {
+            [filter_attrs(in_item)] + enum_fns(cx, in_item.ident,
+                                               in_item.span, variants, tps)
+          }
+
+          _ {
+            cx.span_err(span, "#[auto_serialize] can only be \
+                               applied to type and enum \
+                               definitions");
+            [in_item]
+          }
+        }
+    }
+}
+
+impl helpers for ext_ctxt {
+    fn helper_path(base_path: @ast::path,
+                   helper_name: str) -> @ast::path {
+        let head = vec::init(base_path.idents);
+        let tail = vec::last(base_path.idents);
+        self.path(base_path.span, head + [helper_name + "_" + tail])
+    }
+
+    fn path(span: span, strs: [str]) -> @ast::path {
+        @{span: span, global: false, idents: strs, rp: none, types: []}
+    }
+
+    fn path_tps(span: span, strs: [str], tps: [@ast::ty]) -> @ast::path {
+        @{span: span, global: false, idents: strs, rp: none, types: tps}
+    }
+
+    fn ty_path(span: span, strs: [str], tps: [@ast::ty]) -> @ast::ty {
+        @{id: self.next_id(),
+          node: ast::ty_path(self.path_tps(span, strs, tps), self.next_id()),
+          span: span}
+    }
+
+    fn ty_fn(span: span,
+             -input_tys: [@ast::ty],
+             -output: @ast::ty) -> @ast::ty {
+        let args = vec::map(input_tys) {|ty|
+            {mode: ast::expl(ast::by_ref),
+             ty: ty,
+             ident: "",
+             id: self.next_id()}
+        };
+
+        @{id: self.next_id(),
+          node: ast::ty_fn(ast::proto_any, {inputs: args,
+                                            output: output,
+                                            purity: ast::impure_fn,
+                                            cf: ast::return_val,
+                                            constraints: []}),
+          span: span}
+    }
+
+    fn ty_nil(span: span) -> @ast::ty {
+        @{id: self.next_id(), node: ast::ty_nil, span: span}
+    }
+
+    fn expr(span: span, node: ast::expr_) -> @ast::expr {
+        @{id: self.next_id(), node: node, span: span}
+    }
+
+    fn var_ref(span: span, name: str) -> @ast::expr {
+        self.expr(span, ast::expr_path(self.path(span, [name])))
+    }
+
+    fn blk(span: span, stmts: [@ast::stmt]) -> ast::blk {
+        {node: {view_items: [],
+                stmts: stmts,
+                expr: none,
+                id: self.next_id(),
+                rules: ast::default_blk},
+         span: span}
+    }
+
+    fn expr_blk(expr: @ast::expr) -> ast::blk {
+        {node: {view_items: [],
+                stmts: [],
+                expr: some(expr),
+                id: self.next_id(),
+                rules: ast::default_blk},
+         span: expr.span}
+    }
+
+    fn binder_pat(span: span, nm: str) -> @ast::pat {
+        let path = @{span: span, global: false, idents: [nm],
+                     rp: none, types: []};
+        @{id: self.next_id(),
+          node: ast::pat_ident(path, none),
+          span: span}
+    }
+
+    fn stmt(expr: @ast::expr) -> @ast::stmt {
+        @{node: ast::stmt_semi(expr, self.next_id()),
+          span: expr.span}
+    }
+
+    fn alt_stmt(arms: [ast::arm], span: span, -v: @ast::expr) -> @ast::stmt {
+        self.stmt(
+            self.expr(
+                span,
+                ast::expr_alt(v, arms, ast::alt_exhaustive)))
+    }
+
+    fn lit_str(span: span, s: str) -> @ast::expr {
+        self.expr(
+            span,
+            ast::expr_lit(
+                @{node: ast::lit_str(s),
+                  span: span}))
+    }
+
+    fn lit_uint(span: span, i: uint) -> @ast::expr {
+        self.expr(
+            span,
+            ast::expr_lit(
+                @{node: ast::lit_uint(i as u64, ast::ty_u),
+                  span: span}))
+    }
+
+    fn lambda(blk: ast::blk) -> @ast::expr {
+        let ext_cx = self;
+        let blk_e = self.expr(blk.span, ast::expr_block(blk));
+        #ast{ {|| $(blk_e) } }
+    }
+
+    fn clone_folder() -> fold::ast_fold {
+        fold::make_fold(@{
+            new_id: {|_id| self.next_id()}
+            with *fold::default_ast_fold()
+        })
+    }
+
+    fn clone(v: @ast::expr) -> @ast::expr {
+        let fld = self.clone_folder();
+        fld.fold_expr(v)
+    }
+
+    fn clone_ty(v: @ast::ty) -> @ast::ty {
+        let fld = self.clone_folder();
+        fld.fold_ty(v)
+    }
+
+    fn clone_ty_param(v: ast::ty_param) -> ast::ty_param {
+        let fld = self.clone_folder();
+        fold::fold_ty_param(v, fld)
+    }
+
+    fn at(span: span, expr: @ast::expr) -> @ast::expr {
+        fn repl_sp(old_span: span, repl_span: span, with_span: span) -> span {
+            if old_span == repl_span {
+                with_span
+            } else {
+                old_span
+            }
+        }
+
+        let fld = fold::make_fold(@{
+            new_span: repl_sp(_, ast_util::dummy_sp(), span)
+            with *fold::default_ast_fold()
+        });
+
+        fld.fold_expr(expr)
+    }
+}
+
+fn ser_path(cx: ext_ctxt, tps: ser_tps_map, path: @ast::path,
+                  -s: @ast::expr, -v: @ast::expr)
+    -> [@ast::stmt] {
+    let ext_cx = cx; // required for #ast{}
+
+    // We want to take a path like a::b::c<...> and generate a call
+    // like a::b::c::serialize(s, ...), as described above.
+
+    let callee =
+        cx.expr(
+            path.span,
+            ast::expr_path(
+                cx.helper_path(path, "serialize")));
+
+    let ty_args = vec::map(path.types) {|ty|
+        let sv_stmts = ser_ty(cx, tps, ty, cx.clone(s), #ast{ __v });
+        let sv = cx.expr(path.span,
+                         ast::expr_block(cx.blk(path.span, sv_stmts)));
+        cx.at(ty.span, #ast{ {|__v| $(sv)} })
+    };
+
+    [cx.stmt(
+        cx.expr(
+            path.span,
+            ast::expr_call(callee, [s, v] + ty_args, false)))]
+}
+
+fn ser_variant(cx: ext_ctxt,
+               tps: ser_tps_map,
+               tys: [@ast::ty],
+               span: span,
+               -s: @ast::expr,
+               pfn: fn([@ast::pat]) -> ast::pat_,
+               bodyfn: fn(-@ast::expr, ast::blk) -> @ast::expr,
+               argfn: fn(-@ast::expr, uint, ast::blk) -> @ast::expr)
+    -> ast::arm {
+    let vnames = vec::from_fn(vec::len(tys)) {|i| #fmt["__v%u", i]};
+    let pats = vec::from_fn(vec::len(tys)) {|i|
+        cx.binder_pat(tys[i].span, vnames[i])
+    };
+    let pat: @ast::pat = @{id: cx.next_id(), node: pfn(pats), span: span};
+    let stmts = vec::from_fn(vec::len(tys)) {|i|
+        let v = cx.var_ref(span, vnames[i]);
+        let arg_blk =
+            cx.blk(
+                span,
+                ser_ty(cx, tps, tys[i], cx.clone(s), v));
+        cx.stmt(argfn(cx.clone(s), i, arg_blk))
+    };
+
+    let body_blk = cx.blk(span, stmts);
+    let body = cx.blk(span, [cx.stmt(bodyfn(s, body_blk))]);
+
+    {pats: [pat], guard: none, body: body}
+}
+
+fn ser_lambda(cx: ext_ctxt, tps: ser_tps_map, ty: @ast::ty,
+              -s: @ast::expr, -v: @ast::expr) -> @ast::expr {
+    cx.lambda(cx.blk(ty.span, ser_ty(cx, tps, ty, s, v)))
+}
+
+fn ser_ty(cx: ext_ctxt, tps: ser_tps_map,
+                ty: @ast::ty, -s: @ast::expr, -v: @ast::expr)
+    -> [@ast::stmt] {
+
+    let ext_cx = cx; // required for #ast{}
+
+    alt ty.node {
+      ast::ty_nil {
+        [#ast[stmt]{$(s).emit_nil()}]
+      }
+
+      ast::ty_bot {
+        cx.span_err(
+            ty.span, #fmt["Cannot serialize bottom type"]);
+        []
+      }
+
+      ast::ty_box(mt) {
+        let l = ser_lambda(cx, tps, mt.ty, cx.clone(s), #ast{ *$(v) });
+        [#ast(stmt){$(s).emit_box($(l));}]
+      }
+
+      ast::ty_uniq(mt) {
+        let l = ser_lambda(cx, tps, mt.ty, cx.clone(s), #ast{ *$(v) });
+        [#ast(stmt){$(s).emit_uniq($(l));}]
+      }
+
+      ast::ty_ptr(_) | ast::ty_rptr(_, _) {
+        cx.span_err(ty.span, "cannot serialize pointer types");
+        []
+      }
+
+      ast::ty_rec(flds) {
+        let fld_stmts = vec::from_fn(vec::len(flds)) {|fidx|
+            let fld = flds[fidx];
+            let vf = cx.expr(fld.span,
+                             ast::expr_field(cx.clone(v),
+                                             fld.node.ident,
+                                             []));
+            let s = cx.clone(s);
+            let f = cx.lit_str(fld.span, fld.node.ident);
+            let i = cx.lit_uint(fld.span, fidx);
+            let l = ser_lambda(cx, tps, fld.node.mt.ty, cx.clone(s), vf);
+            #ast(stmt){$(s).emit_rec_field($(f), $(i), $(l));}
+        };
+        let fld_lambda = cx.lambda(cx.blk(ty.span, fld_stmts));
+        [#ast(stmt){$(s).emit_rec($(fld_lambda));}]
+      }
+
+      ast::ty_fn(_, _) {
+        cx.span_err(ty.span, "cannot serialize function types");
+        []
+      }
+
+      ast::ty_tup(tys) {
+        // Generate code like
+        //
+        // alt v {
+        //    (v1, v2, v3) {
+        //       .. serialize v1, v2, v3 ..
+        //    }
+        // };
+
+        let arms = [
+            ser_variant(
+
+                cx, tps, tys, ty.span, s,
+
+                // Generate pattern (v1, v2, v3)
+                {|pats| ast::pat_tup(pats)},
+
+                // Generate body s.emit_tup(3, {|| blk })
+                {|-s, blk|
+                    let sz = cx.lit_uint(ty.span, vec::len(tys));
+                    let body = cx.lambda(blk);
+                    #ast{ $(s).emit_tup($(sz), $(body)) }
+                },
+
+                // Generate s.emit_tup_elt(i, {|| blk })
+                {|-s, i, blk|
+                    let idx = cx.lit_uint(ty.span, i);
+                    let body = cx.lambda(blk);
+                    #ast{ $(s).emit_tup_elt($(idx), $(body)) }
+                })
+        ];
+        [cx.alt_stmt(arms, ty.span, v)]
+      }
+
+      ast::ty_path(path, _) {
+        if vec::len(path.idents) == 1u &&
+            vec::is_empty(path.types) {
+            let ident = path.idents[0];
+
+            alt tps.find(ident) {
+              some(f) { f(v) }
+              none { ser_path(cx, tps, path, s, v) }
+            }
+        } else {
+            ser_path(cx, tps, path, s, v)
+        }
+      }
+
+      ast::ty_constr(ty, _) {
+        ser_ty(cx, tps, ty, s, v)
+      }
+
+      ast::ty_mac(_) {
+        cx.span_err(ty.span, "cannot serialize macro types");
+        []
+      }
+
+      ast::ty_infer {
+        cx.span_err(ty.span, "cannot serialize inferred types");
+        []
+      }
+
+      ast::ty_vstore(_, _) {
+        cx.span_unimpl(ty.span, "serialization for vstore types");
+      }
+
+      ast::ty_vec(mt) {
+        let ser_e =
+            cx.expr(
+                ty.span,
+                ast::expr_block(
+                    cx.blk(
+                        ty.span,
+                        ser_ty(
+                            cx, tps, mt.ty,
+                            cx.clone(s),
+                            cx.at(ty.span, #ast{ __e })))));
+
+        [#ast(stmt){
+            std::serialization::emit_from_vec($(s), $(v), {|__e| $(ser_e) })
+        }]
+      }
+    }
+}
+
+fn mk_ser_fn(cx: ext_ctxt, span: span, name: str, tps: [ast::ty_param],
+             f: fn(ext_ctxt, ser_tps_map,
+                   -@ast::expr, -@ast::expr) -> [@ast::stmt])
+    -> @ast::item {
+    let ext_cx = cx; // required for #ast
+
+    let tp_types = vec::map(tps, {|tp| cx.ty_path(span, [tp.ident], [])});
+    let v_ty = cx.ty_path(span, [name], tp_types);
+
+    let tp_inputs =
+        vec::map(tps, {|tp|
+            {mode: ast::expl(ast::by_ref),
+             ty: cx.ty_fn(span,
+                          [cx.ty_path(span, [tp.ident], [])],
+                          cx.ty_nil(span)),
+             ident: "__s" + tp.ident,
+             id: cx.next_id()}});
+
+    #debug["tp_inputs = %?", tp_inputs];
+
+
+    let ser_inputs: [ast::arg] =
+        [{mode: ast::expl(ast::by_ref),
+          ty: cx.ty_path(span, ["__S"], []),
+          ident: "__s",
+          id: cx.next_id()},
+         {mode: ast::expl(ast::by_ref),
+          ty: v_ty,
+          ident: "__v",
+          id: cx.next_id()}]
+        + tp_inputs;
+
+    let tps_map = map::str_hash();
+    vec::iter2(tps, tp_inputs) {|tp, arg|
+        let arg_ident = arg.ident;
+        tps_map.insert(
+            tp.ident,
+            fn@(v: @ast::expr) -> [@ast::stmt] {
+                let f = cx.var_ref(span, arg_ident);
+                #debug["serializing type arg %s", arg_ident];
+                [#ast(stmt){$(f)($(v));}]
+            });
+    }
+
+    let ser_bnds = @[
+        ast::bound_iface(cx.ty_path(span,
+                                    ["std", "serialization", "serializer"],
+                                    []))];
+
+    let ser_tps: [ast::ty_param] =
+        [{ident: "__S",
+          id: cx.next_id(),
+          bounds: ser_bnds}] +
+        vec::map(tps) {|tp| cx.clone_ty_param(tp) };
+
+    let ser_output: @ast::ty = @{id: cx.next_id(),
+                                 node: ast::ty_nil,
+                                 span: span};
+
+    let ser_blk = cx.blk(span,
+                         f(cx, tps_map, #ast{ __s }, #ast{ __v }));
+
+    @{ident: "serialize_" + name,
+      attrs: [],
+      id: cx.next_id(),
+      node: ast::item_fn({inputs: ser_inputs,
+                          output: ser_output,
+                          purity: ast::impure_fn,
+                          cf: ast::return_val,
+                          constraints: []},
+                         ser_tps,
+                         ser_blk),
+      vis: ast::public,
+      span: span}
+}
+
+// ______________________________________________________________________
+
+fn deser_path(cx: ext_ctxt, tps: deser_tps_map, path: @ast::path,
+                    -d: @ast::expr) -> @ast::expr {
+    // We want to take a path like a::b::c<...> and generate a call
+    // like a::b::c::deserialize(d, ...), as described above.
+
+    let callee =
+        cx.expr(
+            path.span,
+            ast::expr_path(
+                cx.helper_path(path, "deserialize")));
+
+    let ty_args = vec::map(path.types) {|ty|
+        let dv_expr = deser_ty(cx, tps, ty, cx.clone(d));
+        cx.lambda(cx.expr_blk(dv_expr))
+    };
+
+    cx.expr(path.span, ast::expr_call(callee, [d] + ty_args, false))
+}
+
+fn deser_lambda(cx: ext_ctxt, tps: deser_tps_map, ty: @ast::ty,
+                -d: @ast::expr) -> @ast::expr {
+    cx.lambda(cx.expr_blk(deser_ty(cx, tps, ty, d)))
+}
+
+fn deser_ty(cx: ext_ctxt, tps: deser_tps_map,
+                  ty: @ast::ty, -d: @ast::expr) -> @ast::expr {
+
+    let ext_cx = cx; // required for #ast{}
+
+    alt ty.node {
+      ast::ty_nil {
+        #ast{ $(d).read_nil() }
+      }
+
+      ast::ty_bot {
+        #ast{ fail }
+      }
+
+      ast::ty_box(mt) {
+        let l = deser_lambda(cx, tps, mt.ty, cx.clone(d));
+        #ast{ @$(d).read_box($(l)) }
+      }
+
+      ast::ty_uniq(mt) {
+        let l = deser_lambda(cx, tps, mt.ty, cx.clone(d));
+        #ast{ ~$(d).read_uniq($(l)) }
+      }
+
+      ast::ty_ptr(_) | ast::ty_rptr(_, _) {
+        #ast{ fail }
+      }
+
+      ast::ty_rec(flds) {
+        let fields = vec::from_fn(vec::len(flds)) {|fidx|
+            let fld = flds[fidx];
+            let d = cx.clone(d);
+            let f = cx.lit_str(fld.span, fld.node.ident);
+            let i = cx.lit_uint(fld.span, fidx);
+            let l = deser_lambda(cx, tps, fld.node.mt.ty, cx.clone(d));
+            {node: {mutbl: fld.node.mt.mutbl,
+                    ident: fld.node.ident,
+                    expr: #ast{ $(d).read_rec_field($(f), $(i), $(l))} },
+             span: fld.span}
+        };
+        let fld_expr = cx.expr(ty.span, ast::expr_rec(fields, none));
+        let fld_lambda = cx.lambda(cx.expr_blk(fld_expr));
+        #ast{ $(d).read_rec($(fld_lambda)) }
+      }
+
+      ast::ty_fn(_, _) {
+        #ast{ fail }
+      }
+
+      ast::ty_tup(tys) {
+        // Generate code like
+        //
+        // d.read_tup(3u) {||
+        //   (d.read_tup_elt(0u, {||...}),
+        //    d.read_tup_elt(1u, {||...}),
+        //    d.read_tup_elt(2u, {||...}))
+        // }
+
+        let arg_exprs = vec::from_fn(vec::len(tys)) {|i|
+            let idx = cx.lit_uint(ty.span, i);
+            let body = deser_lambda(cx, tps, tys[i], cx.clone(d));
+            #ast{ $(d).read_tup_elt($(idx), $(body)) }
+        };
+        let body =
+            cx.lambda(cx.expr_blk(
+                cx.expr(ty.span, ast::expr_tup(arg_exprs))));
+        let sz = cx.lit_uint(ty.span, vec::len(tys));
+        #ast{ $(d).read_tup($(sz), $(body)) }
+      }
+
+      ast::ty_path(path, _) {
+        if vec::len(path.idents) == 1u &&
+            vec::is_empty(path.types) {
+            let ident = path.idents[0];
+
+            alt tps.find(ident) {
+              some(f) { f() }
+              none { deser_path(cx, tps, path, d) }
+            }
+        } else {
+            deser_path(cx, tps, path, d)
+        }
+      }
+
+      ast::ty_constr(ty, constrs) {
+        deser_ty(cx, tps, ty, d)
+      }
+
+      ast::ty_mac(_) {
+        #ast{ fail }
+      }
+
+      ast::ty_infer {
+        #ast{ fail }
+      }
+
+      ast::ty_vstore(_, _) {
+        cx.span_unimpl(ty.span, "deserialization for vstore types");
+      }
+
+      ast::ty_vec(mt) {
+        let l = deser_lambda(cx, tps, mt.ty, cx.clone(d));
+        #ast{ std::serialization::read_to_vec($(d), $(l)) }
+      }
+    }
+}
+
+fn mk_deser_fn(cx: ext_ctxt, span: span, name: str, tps: [ast::ty_param],
+               f: fn(ext_ctxt, deser_tps_map, -@ast::expr) -> @ast::expr)
+    -> @ast::item {
+    let ext_cx = cx; // required for #ast
+
+    let tp_types = vec::map(tps, {|tp| cx.ty_path(span, [tp.ident], [])});
+    let v_ty = cx.ty_path(span, [name], tp_types);
+
+    let tp_inputs =
+        vec::map(tps, {|tp|
+            {mode: ast::expl(ast::by_ref),
+             ty: cx.ty_fn(span,
+                          [],
+                          cx.ty_path(span, [tp.ident], [])),
+             ident: "__d" + tp.ident,
+             id: cx.next_id()}});
+
+    #debug["tp_inputs = %?", tp_inputs];
+
+    let deser_inputs: [ast::arg] =
+        [{mode: ast::expl(ast::by_ref),
+          ty: cx.ty_path(span, ["__D"], []),
+          ident: "__d",
+          id: cx.next_id()}]
+        + tp_inputs;
+
+    let tps_map = map::str_hash();
+    vec::iter2(tps, tp_inputs) {|tp, arg|
+        let arg_ident = arg.ident;
+        tps_map.insert(
+            tp.ident,
+            fn@() -> @ast::expr {
+                let f = cx.var_ref(span, arg_ident);
+                #ast{ $(f)() }
+            });
+    }
+
+    let deser_bnds = @[
+        ast::bound_iface(cx.ty_path(span,
+                                    ["std", "serialization", "deserializer"],
+                                    []))];
+
+    let deser_tps: [ast::ty_param] =
+        [{ident: "__D",
+          id: cx.next_id(),
+          bounds: deser_bnds}] + vec::map(tps) {|tp|
+        let cloned = cx.clone_ty_param(tp);
+        {bounds: @(*cloned.bounds + [ast::bound_copy]) with cloned}
+    };
+
+    let deser_blk = cx.expr_blk(f(cx, tps_map, #ast(expr){__d}));
+
+    @{ident: "deserialize_" + name,
+      attrs: [],
+      id: cx.next_id(),
+      node: ast::item_fn({inputs: deser_inputs,
+                          output: v_ty,
+                          purity: ast::impure_fn,
+                          cf: ast::return_val,
+                          constraints: []},
+                         deser_tps,
+                         deser_blk),
+      vis: ast::public,
+      span: span}
+}
+
+fn ty_fns(cx: ext_ctxt, name: str, ty: @ast::ty, tps: [ast::ty_param])
+    -> [@ast::item] {
+
+    let span = ty.span;
+    [
+        mk_ser_fn(cx, span, name, tps, ser_ty(_, _, ty, _, _)),
+        mk_deser_fn(cx, span, name, tps, deser_ty(_, _, ty, _))
+    ]
+}
+
+fn ser_enum(cx: ext_ctxt, tps: ser_tps_map, e_name: str,
+            e_span: span, variants: [ast::variant],
+            -s: @ast::expr, -v: @ast::expr) -> [@ast::stmt] {
+    let ext_cx = cx;
+    let arms = vec::from_fn(vec::len(variants)) {|vidx|
+        let variant = variants[vidx];
+        let v_span = variant.span;
+        let v_name = variant.node.name;
+        let variant_tys = vec::map(variant.node.args) {|a| a.ty };
+
+        ser_variant(
+            cx, tps, variant_tys, v_span, cx.clone(s),
+
+            // Generate pattern var(v1, v2, v3)
+            {|pats|
+                if vec::is_empty(pats) {
+                    ast::pat_ident(cx.path(v_span, [v_name]), none)
+                } else {
+                    ast::pat_enum(cx.path(v_span, [v_name]), some(pats))
+                }
+            },
+
+            // Generate body s.emit_enum_variant("foo", 0u,
+            //                                   3u, {|| blk })
+            {|-s, blk|
+                let v_name = cx.lit_str(v_span, v_name);
+                let v_id = cx.lit_uint(v_span, vidx);
+                let sz = cx.lit_uint(v_span, vec::len(variant_tys));
+                let body = cx.lambda(blk);
+                #ast[expr]{
+                    $(s).emit_enum_variant($(v_name), $(v_id),
+                                           $(sz), $(body))
+                }
+            },
+
+            // Generate s.emit_enum_variant_arg(i, {|| blk })
+            {|-s, i, blk|
+                let idx = cx.lit_uint(v_span, i);
+                let body = cx.lambda(blk);
+                #ast[expr]{
+                    $(s).emit_enum_variant_arg($(idx), $(body))
+                }
+            })
+    };
+    let lam = cx.lambda(cx.blk(e_span, [cx.alt_stmt(arms, e_span, v)]));
+    let e_name = cx.lit_str(e_span, e_name);
+    [#ast(stmt){ $(s).emit_enum($(e_name), $(lam)) }]
+}
+
+fn deser_enum(cx: ext_ctxt, tps: deser_tps_map, e_name: str,
+              e_span: span, variants: [ast::variant],
+              -d: @ast::expr) -> @ast::expr {
+    let ext_cx = cx;
+    let arms: [ast::arm] = vec::from_fn(vec::len(variants)) {|vidx|
+        let variant = variants[vidx];
+        let v_span = variant.span;
+        let v_name = variant.node.name;
+        let tys = vec::map(variant.node.args) {|a| a.ty };
+
+        let arg_exprs = vec::from_fn(vec::len(tys)) {|i|
+            let idx = cx.lit_uint(v_span, i);
+            let body = deser_lambda(cx, tps, tys[i], cx.clone(d));
+            #ast{ $(d).read_enum_variant_arg($(idx), $(body)) }
+        };
+
+        let body = {
+            if vec::is_empty(tys) {
+                // for a nullary variant v, do "v"
+                cx.var_ref(v_span, v_name)
+            } else {
+                // for an n-ary variant v, do "v(a_1, ..., a_n)"
+                cx.expr(v_span, ast::expr_call(
+                    cx.var_ref(v_span, v_name), arg_exprs, false))
+            }
+        };
+
+        {pats: [@{id: cx.next_id(),
+                  node: ast::pat_lit(cx.lit_uint(v_span, vidx)),
+                  span: v_span}],
+         guard: none,
+         body: cx.expr_blk(body)}
+    };
+
+    // Generate code like:
+    let e_name = cx.lit_str(e_span, e_name);
+    let alt_expr = cx.expr(e_span,
+                           ast::expr_alt(#ast{__i}, arms, ast::alt_check));
+    let var_lambda = #ast{ {|__i| $(alt_expr)} };
+    let read_var = #ast{ $(cx.clone(d)).read_enum_variant($(var_lambda)) };
+    let read_lambda = cx.lambda(cx.expr_blk(read_var));
+    #ast{ $(d).read_enum($(e_name), $(read_lambda)) }
+}
+
+fn enum_fns(cx: ext_ctxt, e_name: str, e_span: span,
+               variants: [ast::variant], tps: [ast::ty_param])
+    -> [@ast::item] {
+    [
+        mk_ser_fn(cx, e_span, e_name, tps,
+                  ser_enum(_, _, e_name, e_span, variants, _, _)),
+        mk_deser_fn(cx, e_span, e_name, tps,
+                    deser_enum(_, _, e_name, e_span, variants, _))
+    ]
+}
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
new file mode 100644
index 00000000000..29e20212d66
--- /dev/null
+++ b/src/libsyntax/ext/base.rs
@@ -0,0 +1,234 @@
+import std::map::hashmap;
+import parse::parser;
+import diagnostic::span_handler;
+import codemap::{codemap, span, expn_info, expanded_from};
+import std::map::str_hash;
+
+type syntax_expander_ =
+    fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> @ast::expr;
+type syntax_expander = {
+    expander: syntax_expander_,
+    span: option<span>};
+type macro_def = {ident: str, ext: syntax_extension};
+type macro_definer =
+    fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> macro_def;
+type item_decorator =
+    fn@(ext_ctxt, span, ast::meta_item, [@ast::item]) -> [@ast::item];
+
+enum syntax_extension {
+    normal(syntax_expander),
+    macro_defining(macro_definer),
+    item_decorator(item_decorator),
+}
+
+// A temporary hard-coded map of methods for expanding syntax extension
+// AST nodes into full ASTs
+fn syntax_expander_table() -> hashmap<str, syntax_extension> {
+    fn builtin(f: syntax_expander_) -> syntax_extension
+        {normal({expander: f, span: none})}
+    let syntax_expanders = str_hash::<syntax_extension>();
+    syntax_expanders.insert("fmt", builtin(ext::fmt::expand_syntax_ext));
+    syntax_expanders.insert("auto_serialize",
+                            item_decorator(ext::auto_serialize::expand));
+    syntax_expanders.insert("env", builtin(ext::env::expand_syntax_ext));
+    syntax_expanders.insert("macro",
+                            macro_defining(ext::simplext::add_new_extension));
+    syntax_expanders.insert("concat_idents",
+                            builtin(ext::concat_idents::expand_syntax_ext));
+    syntax_expanders.insert("ident_to_str",
+                            builtin(ext::ident_to_str::expand_syntax_ext));
+    syntax_expanders.insert("log_syntax",
+                            builtin(ext::log_syntax::expand_syntax_ext));
+    syntax_expanders.insert("ast",
+                            builtin(ext::qquote::expand_ast));
+    syntax_expanders.insert("line",
+                            builtin(ext::source_util::expand_line));
+    syntax_expanders.insert("col",
+                            builtin(ext::source_util::expand_col));
+    syntax_expanders.insert("file",
+                            builtin(ext::source_util::expand_file));
+    syntax_expanders.insert("stringify",
+                            builtin(ext::source_util::expand_stringify));
+    syntax_expanders.insert("include",
+                            builtin(ext::source_util::expand_include));
+    syntax_expanders.insert("include_str",
+                            builtin(ext::source_util::expand_include_str));
+    syntax_expanders.insert("include_bin",
+                            builtin(ext::source_util::expand_include_bin));
+    syntax_expanders.insert("mod",
+                            builtin(ext::source_util::expand_mod));
+    ret syntax_expanders;
+}
+
+iface ext_ctxt {
+    fn codemap() -> codemap;
+    fn parse_sess() -> parse::parse_sess;
+    fn cfg() -> ast::crate_cfg;
+    fn print_backtrace();
+    fn backtrace() -> expn_info;
+    fn mod_push(mod_name: ast::ident);
+    fn mod_pop();
+    fn mod_path() -> [ast::ident];
+    fn bt_push(ei: codemap::expn_info_);
+    fn bt_pop();
+    fn span_fatal(sp: span, msg: str) -> !;
+    fn span_err(sp: span, msg: str);
+    fn span_unimpl(sp: span, msg: str) -> !;
+    fn span_bug(sp: span, msg: str) -> !;
+    fn bug(msg: str) -> !;
+    fn next_id() -> ast::node_id;
+}
+
+fn mk_ctxt(parse_sess: parse::parse_sess,
+           cfg: ast::crate_cfg) -> ext_ctxt {
+    type ctxt_repr = {parse_sess: parse::parse_sess,
+                      cfg: ast::crate_cfg,
+                      mut backtrace: expn_info,
+                      mut mod_path: [ast::ident]};
+    impl of ext_ctxt for ctxt_repr {
+        fn codemap() -> codemap { self.parse_sess.cm }
+        fn parse_sess() -> parse::parse_sess { self.parse_sess }
+        fn cfg() -> ast::crate_cfg { self.cfg }
+        fn print_backtrace() { }
+        fn backtrace() -> expn_info { self.backtrace }
+        fn mod_push(i: ast::ident) { vec::push(self.mod_path, i); }
+        fn mod_pop() { vec::pop(self.mod_path); }
+        fn mod_path() -> [ast::ident] { ret self.mod_path; }
+        fn bt_push(ei: codemap::expn_info_) {
+            alt ei {
+              expanded_from({call_site: cs, callie: callie}) {
+                self.backtrace =
+                    some(@expanded_from({
+                        call_site: {lo: cs.lo, hi: cs.hi,
+                                    expn_info: self.backtrace},
+                        callie: callie}));
+              }
+            }
+        }
+        fn bt_pop() {
+            alt self.backtrace {
+              some(@expanded_from({call_site: {expn_info: prev, _}, _})) {
+                self.backtrace = prev
+              }
+              _ { self.bug("tried to pop without a push"); }
+            }
+        }
+        fn span_fatal(sp: span, msg: str) -> ! {
+            self.print_backtrace();
+            self.parse_sess.span_diagnostic.span_fatal(sp, msg);
+        }
+        fn span_err(sp: span, msg: str) {
+            self.print_backtrace();
+            self.parse_sess.span_diagnostic.span_err(sp, msg);
+        }
+        fn span_unimpl(sp: span, msg: str) -> ! {
+            self.print_backtrace();
+            self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
+        }
+        fn span_bug(sp: span, msg: str) -> ! {
+            self.print_backtrace();
+            self.parse_sess.span_diagnostic.span_bug(sp, msg);
+        }
+        fn bug(msg: str) -> ! {
+            self.print_backtrace();
+            self.parse_sess.span_diagnostic.handler().bug(msg);
+        }
+        fn next_id() -> ast::node_id {
+            ret parse::next_node_id(self.parse_sess);
+        }
+    }
+    let imp : ctxt_repr = {
+        parse_sess: parse_sess,
+        cfg: cfg,
+        mut backtrace: none,
+        mut mod_path: []
+    };
+    ret imp as ext_ctxt
+}
+
+fn expr_to_str(cx: ext_ctxt, expr: @ast::expr, error: str) -> str {
+    alt expr.node {
+      ast::expr_lit(l) {
+        alt l.node {
+          ast::lit_str(s) { ret s; }
+          _ { cx.span_fatal(l.span, error); }
+        }
+      }
+      _ { cx.span_fatal(expr.span, error); }
+    }
+}
+
+fn expr_to_ident(cx: ext_ctxt, expr: @ast::expr, error: str) -> ast::ident {
+    alt expr.node {
+      ast::expr_path(p) {
+        if vec::len(p.types) > 0u || vec::len(p.idents) != 1u {
+            cx.span_fatal(expr.span, error);
+        } else { ret p.idents[0]; }
+      }
+      _ { cx.span_fatal(expr.span, error); }
+    }
+}
+
+fn make_new_lit(cx: ext_ctxt, sp: codemap::span, lit: ast::lit_) ->
+   @ast::expr {
+    let sp_lit = @{node: lit, span: sp};
+    ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp};
+}
+
+fn make_new_expr(cx: ext_ctxt, sp: codemap::span, expr: ast::expr_) ->
+    @ast::expr {
+    ret @{id: cx.next_id(), node: expr, span: sp};
+}
+
+fn get_mac_args_no_max(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+                       min: uint, name: str) -> [@ast::expr] {
+    ret get_mac_args(cx, sp, arg, min, none, name);
+}
+
+fn get_mac_args(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+                min: uint, max: option<uint>, name: str) -> [@ast::expr] {
+    alt arg {
+      some(expr) {
+        alt expr.node {
+          ast::expr_vec(elts, _) {
+            let elts_len = vec::len(elts);
+            alt max {
+              some(max) if ! (min <= elts_len && elts_len <= max) {
+                cx.span_fatal(sp,
+                              #fmt["#%s takes between %u and %u arguments.",
+                                   name, min, max]);
+              }
+              none if ! (min <= elts_len) {
+                cx.span_fatal(sp, #fmt["#%s needs at least %u arguments.",
+                                       name, min]);
+              }
+              _ { ret elts; /* we're good */}
+            }
+          }
+          _ {
+            cx.span_fatal(sp, #fmt["#%s: malformed invocation", name])
+          }
+        }
+      }
+      none {cx.span_fatal(sp, #fmt["#%s: missing arguments", name])}
+    }
+}
+
+fn get_mac_body(cx: ext_ctxt, sp: span, args: ast::mac_body)
+    -> ast::mac_body_
+{
+    alt (args) {
+      some(body) {body}
+      none {cx.span_fatal(sp, "missing macro body")}
+    }
+}
+
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
+//
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
new file mode 100644
index 00000000000..516deb1e793
--- /dev/null
+++ b/src/libsyntax/ext/build.rs
@@ -0,0 +1,78 @@
+import codemap::span;
+import base::ext_ctxt;
+
+fn mk_lit(cx: ext_ctxt, sp: span, lit: ast::lit_) -> @ast::expr {
+    let sp_lit = @{node: lit, span: sp};
+    ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp};
+}
+fn mk_str(cx: ext_ctxt, sp: span, s: str) -> @ast::expr {
+    let lit = ast::lit_str(s);
+    ret mk_lit(cx, sp, lit);
+}
+fn mk_int(cx: ext_ctxt, sp: span, i: int) -> @ast::expr {
+    let lit = ast::lit_int(i as i64, ast::ty_i);
+    ret mk_lit(cx, sp, lit);
+}
+fn mk_uint(cx: ext_ctxt, sp: span, u: uint) -> @ast::expr {
+    let lit = ast::lit_uint(u as u64, ast::ty_u);
+    ret mk_lit(cx, sp, lit);
+}
+fn mk_binary(cx: ext_ctxt, sp: span, op: ast::binop,
+             lhs: @ast::expr, rhs: @ast::expr)
+   -> @ast::expr {
+    let binexpr = ast::expr_binary(op, lhs, rhs);
+    ret @{id: cx.next_id(), node: binexpr, span: sp};
+}
+fn mk_unary(cx: ext_ctxt, sp: span, op: ast::unop, e: @ast::expr)
+    -> @ast::expr {
+    let expr = ast::expr_unary(op, e);
+    ret @{id: cx.next_id(), node: expr, span: sp};
+}
+fn mk_path(cx: ext_ctxt, sp: span, idents: [ast::ident]) ->
+    @ast::expr {
+    let path = @{span: sp, global: false, idents: idents,
+                 rp: none, types: []};
+    let pathexpr = ast::expr_path(path);
+    ret @{id: cx.next_id(), node: pathexpr, span: sp};
+}
+fn mk_access_(cx: ext_ctxt, sp: span, p: @ast::expr, m: ast::ident)
+    -> @ast::expr {
+    let expr = ast::expr_field(p, m, []);
+    ret @{id: cx.next_id(), node: expr, span: sp};
+}
+fn mk_access(cx: ext_ctxt, sp: span, p: [ast::ident], m: ast::ident)
+    -> @ast::expr {
+    let pathexpr = mk_path(cx, sp, p);
+    ret mk_access_(cx, sp, pathexpr, m);
+}
+fn mk_call_(cx: ext_ctxt, sp: span, fn_expr: @ast::expr,
+            args: [@ast::expr]) -> @ast::expr {
+    let callexpr = ast::expr_call(fn_expr, args, false);
+    ret @{id: cx.next_id(), node: callexpr, span: sp};
+}
+fn mk_call(cx: ext_ctxt, sp: span, fn_path: [ast::ident],
+             args: [@ast::expr]) -> @ast::expr {
+    let pathexpr = mk_path(cx, sp, fn_path);
+    ret mk_call_(cx, sp, pathexpr, args);
+}
+// e = expr, t = type
+fn mk_vec_e(cx: ext_ctxt, sp: span, exprs: [@ast::expr]) ->
+   @ast::expr {
+    let vecexpr = ast::expr_vec(exprs, ast::m_imm);
+    ret @{id: cx.next_id(), node: vecexpr, span: sp};
+}
+fn mk_rec_e(cx: ext_ctxt, sp: span,
+            fields: [{ident: ast::ident, ex: @ast::expr}]) ->
+    @ast::expr {
+    let mut astfields: [ast::field] = [];
+    for fields.each {|field|
+        let ident = field.ident;
+        let val = field.ex;
+        let astfield =
+            {node: {mutbl: ast::m_imm, ident: ident, expr: val}, span: sp};
+        astfields += [astfield];
+    }
+    let recexpr = ast::expr_rec(astfields, option::none::<@ast::expr>);
+    ret @{id: cx.next_id(), node: recexpr, span: sp};
+}
+
diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs
new file mode 100644
index 00000000000..278321ec8bc
--- /dev/null
+++ b/src/libsyntax/ext/concat_idents.rs
@@ -0,0 +1,15 @@
+import base::*;
+
+fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg,
+                     _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args_no_max(cx,sp,arg,1u,"concat_idents");
+    let mut res: ast::ident = "";
+    for args.each {|e|
+        res += expr_to_ident(cx, e, "expected an ident");
+    }
+
+    ret @{id: cx.next_id(),
+          node: ast::expr_path(@{span: sp, global: false, idents: [res],
+                                 rp: none, types: []}),
+          span: sp};
+}
diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs
new file mode 100644
index 00000000000..6a4d937f083
--- /dev/null
+++ b/src/libsyntax/ext/env.rs
@@ -0,0 +1,35 @@
+
+/*
+ * The compiler code necessary to support the #env extension.  Eventually this
+ * should all get sucked into either the compiler syntax extension plugin
+ * interface.
+ */
+import base::*;
+export expand_syntax_ext;
+
+fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg,
+                     _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args(cx, sp, arg, 1u, option::some(1u), "env");
+
+    // FIXME: if this was more thorough it would manufacture an
+    // option<str> rather than just an maybe-empty string. (Issue #2248)
+
+    let var = expr_to_str(cx, args[0], "#env requires a string");
+    alt os::getenv(var) {
+      option::none { ret make_new_str(cx, sp, ""); }
+      option::some(s) { ret make_new_str(cx, sp, s); }
+    }
+}
+
+fn make_new_str(cx: ext_ctxt, sp: codemap::span, s: str) -> @ast::expr {
+    ret make_new_lit(cx, sp, ast::lit_str(s));
+}
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
+//
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
new file mode 100644
index 00000000000..90487e27956
--- /dev/null
+++ b/src/libsyntax/ext/expand.rs
@@ -0,0 +1,157 @@
+import std::map::hashmap;
+
+import ast::{crate, expr_, expr_mac, mac_invoc};
+import fold::*;
+import ext::base::*;
+import ext::qquote::{qq_helper};
+import parse::parser;
+import parse::parse_expr_from_source_str;
+
+
+import codemap::{span, expanded_from};
+
+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))
+    -> (expr_, span)
+{
+    ret alt e {
+          expr_mac(mac) {
+            alt mac.node {
+              mac_invoc(pth, args, body) {
+                assert (vec::len(pth.idents) > 0u);
+                let extname = pth.idents[0];
+                alt exts.find(extname) {
+                  none {
+                    cx.span_fatal(pth.span,
+                                  #fmt["macro undefined: '%s'", extname])
+                  }
+                  some(item_decorator(_)) {
+                    cx.span_fatal(
+                        pth.span,
+                        #fmt["%s can only be used as a decorator", extname]);
+                  }
+                  some(normal({expander: exp, span: exp_sp})) {
+                    let expanded = exp(cx, pth.span, args, body);
+
+                    cx.bt_push(expanded_from({call_site: s,
+                                callie: {name: extname, span: exp_sp}}));
+                    //keep going, outside-in
+                    let fully_expanded = fld.fold_expr(expanded).node;
+                    cx.bt_pop();
+
+                    (fully_expanded, s)
+                  }
+                  some(macro_defining(ext)) {
+                    let named_extension = ext(cx, pth.span, args, body);
+                    exts.insert(named_extension.ident, named_extension.ext);
+                    (ast::expr_rec([], none), s)
+                  }
+                }
+              }
+              _ { cx.span_bug(mac.span, "naked syntactic bit") }
+            }
+          }
+          _ { orig(e, s, fld) }
+        };
+}
+
+fn expand_mod_items(exts: hashmap<str, syntax_extension>, cx: ext_ctxt,
+                    module: ast::_mod, fld: ast_fold,
+                    orig: fn@(ast::_mod, ast_fold) -> ast::_mod)
+    -> ast::_mod
+{
+    // Fold the contents first:
+    let module = orig(module, fld);
+
+    // For each item, look through the attributes.  If any of them are
+    // decorated with "item decorators", then use that function to transform
+    // the item into a new set of items.
+    let new_items = vec::flat_map(module.items) {|item|
+        vec::foldr(item.attrs, [item]) {|attr, items|
+            let mname = alt attr.node.value.node {
+              ast::meta_word(n) { n }
+              ast::meta_name_value(n, _) { n }
+              ast::meta_list(n, _) { n }
+            };
+            alt exts.find(mname) {
+              none | some(normal(_)) | some(macro_defining(_)) {
+                items
+              }
+
+              some(item_decorator(dec_fn)) {
+                dec_fn(cx, attr.span, attr.node.value, items)
+              }
+            }
+        }
+    };
+
+    ret {items: new_items with module};
+}
+
+/* record module we enter for `#mod` */
+fn expand_item(cx: ext_ctxt, &&it: @ast::item, fld: ast_fold,
+               orig: fn@(&&@ast::item, ast_fold) -> @ast::item)
+    -> @ast::item
+{
+    let is_mod = alt it.node {
+      ast::item_mod(_) | ast::item_native_mod(_) {true}
+      _ {false}
+    };
+    if is_mod { cx.mod_push(it.ident); }
+    let ret_val = orig(it, fld);
+    if is_mod { cx.mod_pop(); }
+    ret ret_val;
+}
+
+fn new_span(cx: ext_ctxt, sp: span) -> span {
+    /* this discards information in the case of macro-defining macros */
+    ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
+}
+
+// FIXME: this is a terrible kludge to inject some macros into the default
+// compilation environment. When the macro-definition system is substantially
+// more mature, these should move from here, into a compiled part of libcore
+// at very least. (Issue #2247)
+
+fn core_macros() -> str {
+    ret
+"{
+    #macro([#error[f, ...], log(core::error, #fmt[f, ...])]);
+    #macro([#warn[f, ...], log(core::warn, #fmt[f, ...])]);
+    #macro([#info[f, ...], log(core::info, #fmt[f, ...])]);
+    #macro([#debug[f, ...], log(core::debug, #fmt[f, ...])]);
+}";
+}
+
+fn expand_crate(parse_sess: parse::parse_sess,
+                cfg: ast::crate_cfg, c: @crate) -> @crate {
+    let exts = syntax_expander_table();
+    let afp = default_ast_fold();
+    let cx: ext_ctxt = mk_ctxt(parse_sess, cfg);
+    let f_pre =
+        @{fold_expr: bind expand_expr(exts, cx, _, _, _, afp.fold_expr),
+          fold_mod: bind expand_mod_items(exts, cx, _, _, afp.fold_mod),
+          fold_item: bind expand_item(cx, _, _, afp.fold_item),
+          new_span: bind new_span(cx, _)
+          with *afp};
+    let f = make_fold(f_pre);
+    let cm = parse_expr_from_source_str("<core-macros>",
+                                        @core_macros(),
+                                        cfg,
+                                        parse_sess);
+
+    // This is run for its side-effects on the expander env,
+    // as it registers all the core macros as expanders.
+    f.fold_expr(cm);
+
+    let res = @f.fold_crate(*c);
+    ret res;
+}
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs
new file mode 100644
index 00000000000..aceeed4b9e8
--- /dev/null
+++ b/src/libsyntax/ext/fmt.rs
@@ -0,0 +1,283 @@
+
+
+/*
+ * The compiler code necessary to support the #fmt extension. Eventually this
+ * should all get sucked into either the standard library extfmt module or the
+ * compiler syntax extension plugin interface.
+ */
+import extfmt::ct::*;
+import base::*;
+import codemap::span;
+import ext::build::*;
+export expand_syntax_ext;
+
+fn expand_syntax_ext(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+                     _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args_no_max(cx, sp, arg, 1u, "fmt");
+    let fmt =
+        expr_to_str(cx, args[0],
+                    "first argument to #fmt must be a string literal.");
+    let fmtspan = args[0].span;
+    #debug("Format string:");
+    log(debug, fmt);
+    fn parse_fmt_err_(cx: ext_ctxt, sp: span, msg: str) -> ! {
+        cx.span_fatal(sp, msg);
+    }
+    let parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _);
+    let pieces = parse_fmt_string(fmt, parse_fmt_err);
+    ret pieces_to_expr(cx, sp, pieces, args);
+}
+
+// FIXME: A lot of these functions for producing expressions can probably
+// be factored out in common with other code that builds expressions.
+// FIXME: Cleanup the naming of these functions
+// NOTE: Moved many of the common ones to build.rs --kevina
+// See Issue #2249
+fn pieces_to_expr(cx: ext_ctxt, sp: span, pieces: [piece], args: [@ast::expr])
+   -> @ast::expr {
+    fn make_path_vec(_cx: ext_ctxt, ident: ast::ident) -> [ast::ident] {
+        ret ["extfmt", "rt", ident];
+    }
+    fn make_rt_path_expr(cx: ext_ctxt, sp: span, ident: str) -> @ast::expr {
+        let path = make_path_vec(cx, ident);
+        ret mk_path(cx, sp, path);
+    }
+    // Produces an AST expression that represents a RT::conv record,
+    // which tells the RT::conv* functions how to perform the conversion
+
+    fn make_rt_conv_expr(cx: ext_ctxt, sp: span, cnv: conv) -> @ast::expr {
+        fn make_flags(cx: ext_ctxt, sp: span, flags: [flag]) -> @ast::expr {
+            let mut flagexprs: [@ast::expr] = [];
+            for flags.each {|f|
+                let mut fstr;
+                alt f {
+                  flag_left_justify { fstr = "flag_left_justify"; }
+                  flag_left_zero_pad { fstr = "flag_left_zero_pad"; }
+                  flag_space_for_sign { fstr = "flag_space_for_sign"; }
+                  flag_sign_always { fstr = "flag_sign_always"; }
+                  flag_alternate { fstr = "flag_alternate"; }
+                }
+                flagexprs += [make_rt_path_expr(cx, sp, fstr)];
+            }
+            ret mk_vec_e(cx, sp, flagexprs);
+        }
+        fn make_count(cx: ext_ctxt, sp: span, cnt: count) -> @ast::expr {
+            alt cnt {
+              count_implied {
+                ret make_rt_path_expr(cx, sp, "count_implied");
+              }
+              count_is(c) {
+                let count_lit = mk_int(cx, sp, c);
+                let count_is_path = make_path_vec(cx, "count_is");
+                let count_is_args = [count_lit];
+                ret mk_call(cx, sp, count_is_path, count_is_args);
+              }
+              _ { cx.span_unimpl(sp, "unimplemented #fmt conversion"); }
+            }
+        }
+        fn make_ty(cx: ext_ctxt, sp: span, t: ty) -> @ast::expr {
+            let mut rt_type;
+            alt t {
+              ty_hex(c) {
+                alt c {
+                  case_upper { rt_type = "ty_hex_upper"; }
+                  case_lower { rt_type = "ty_hex_lower"; }
+                }
+              }
+              ty_bits { rt_type = "ty_bits"; }
+              ty_octal { rt_type = "ty_octal"; }
+              _ { rt_type = "ty_default"; }
+            }
+            ret make_rt_path_expr(cx, sp, rt_type);
+        }
+        fn make_conv_rec(cx: ext_ctxt, sp: span, flags_expr: @ast::expr,
+                         width_expr: @ast::expr, precision_expr: @ast::expr,
+                         ty_expr: @ast::expr) -> @ast::expr {
+            ret mk_rec_e(cx, sp,
+                         [{ident: "flags", ex: flags_expr},
+                          {ident: "width", ex: width_expr},
+                          {ident: "precision", ex: precision_expr},
+                          {ident: "ty", ex: ty_expr}]);
+        }
+        let rt_conv_flags = make_flags(cx, sp, cnv.flags);
+        let rt_conv_width = make_count(cx, sp, cnv.width);
+        let rt_conv_precision = make_count(cx, sp, cnv.precision);
+        let rt_conv_ty = make_ty(cx, sp, cnv.ty);
+        ret make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width,
+                          rt_conv_precision, rt_conv_ty);
+    }
+    fn make_conv_call(cx: ext_ctxt, sp: span, conv_type: str, cnv: conv,
+                      arg: @ast::expr) -> @ast::expr {
+        let fname = "conv_" + conv_type;
+        let path = make_path_vec(cx, fname);
+        let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
+        let args = [cnv_expr, arg];
+        ret mk_call(cx, arg.span, path, args);
+    }
+    fn make_new_conv(cx: ext_ctxt, sp: span, cnv: conv, arg: @ast::expr) ->
+       @ast::expr {
+        // FIXME: Move validation code into core::extfmt (Issue #2249)
+
+        fn is_signed_type(cnv: conv) -> bool {
+            alt cnv.ty {
+              ty_int(s) {
+                alt s { signed { ret true; } unsigned { ret false; } }
+              }
+              ty_float { ret true; }
+              _ { ret false; }
+            }
+        }
+        let unsupported = "conversion not supported in #fmt string";
+        alt cnv.param {
+          option::none { }
+          _ { cx.span_unimpl(sp, unsupported); }
+        }
+        for cnv.flags.each {|f|
+            alt f {
+              flag_left_justify { }
+              flag_sign_always {
+                if !is_signed_type(cnv) {
+                    cx.span_fatal(sp,
+                                  "+ flag only valid in " +
+                                      "signed #fmt conversion");
+                }
+              }
+              flag_space_for_sign {
+                if !is_signed_type(cnv) {
+                    cx.span_fatal(sp,
+                                  "space flag only valid in " +
+                                      "signed #fmt conversions");
+                }
+              }
+              flag_left_zero_pad { }
+              _ { cx.span_unimpl(sp, unsupported); }
+            }
+        }
+        alt cnv.width {
+          count_implied { }
+          count_is(_) { }
+          _ { cx.span_unimpl(sp, unsupported); }
+        }
+        alt cnv.precision {
+          count_implied { }
+          count_is(_) { }
+          _ { cx.span_unimpl(sp, unsupported); }
+        }
+        alt cnv.ty {
+          ty_str { ret make_conv_call(cx, arg.span, "str", cnv, arg); }
+          ty_int(sign) {
+            alt sign {
+              signed { ret make_conv_call(cx, arg.span, "int", cnv, arg); }
+              unsigned {
+                ret make_conv_call(cx, arg.span, "uint", cnv, arg);
+              }
+            }
+          }
+          ty_bool { ret make_conv_call(cx, arg.span, "bool", cnv, arg); }
+          ty_char { ret make_conv_call(cx, arg.span, "char", cnv, arg); }
+          ty_hex(_) { ret make_conv_call(cx, arg.span, "uint", cnv, arg); }
+          ty_bits { ret make_conv_call(cx, arg.span, "uint", cnv, arg); }
+          ty_octal { ret make_conv_call(cx, arg.span, "uint", cnv, arg); }
+          ty_float { ret make_conv_call(cx, arg.span, "float", cnv, arg); }
+          ty_poly { ret make_conv_call(cx, arg.span, "poly", cnv, arg); }
+        }
+    }
+    fn log_conv(c: conv) {
+        alt c.param {
+          some(p) { log(debug, "param: " + int::to_str(p, 10u)); }
+          _ { #debug("param: none"); }
+        }
+        for c.flags.each {|f|
+            alt f {
+              flag_left_justify { #debug("flag: left justify"); }
+              flag_left_zero_pad { #debug("flag: left zero pad"); }
+              flag_space_for_sign { #debug("flag: left space pad"); }
+              flag_sign_always { #debug("flag: sign always"); }
+              flag_alternate { #debug("flag: alternate"); }
+            }
+        }
+        alt c.width {
+          count_is(i) { log(debug,
+                                 "width: count is " + int::to_str(i, 10u)); }
+          count_is_param(i) {
+            log(debug,
+                     "width: count is param " + int::to_str(i, 10u));
+          }
+          count_is_next_param { #debug("width: count is next param"); }
+          count_implied { #debug("width: count is implied"); }
+        }
+        alt c.precision {
+          count_is(i) { log(debug,
+                                 "prec: count is " + int::to_str(i, 10u)); }
+          count_is_param(i) {
+            log(debug,
+                     "prec: count is param " + int::to_str(i, 10u));
+          }
+          count_is_next_param { #debug("prec: count is next param"); }
+          count_implied { #debug("prec: count is implied"); }
+        }
+        alt c.ty {
+          ty_bool { #debug("type: bool"); }
+          ty_str { #debug("type: str"); }
+          ty_char { #debug("type: char"); }
+          ty_int(s) {
+            alt s {
+              signed { #debug("type: signed"); }
+              unsigned { #debug("type: unsigned"); }
+            }
+          }
+          ty_bits { #debug("type: bits"); }
+          ty_hex(cs) {
+            alt cs {
+              case_upper { #debug("type: uhex"); }
+              case_lower { #debug("type: lhex"); }
+            }
+          }
+          ty_octal { #debug("type: octal"); }
+          ty_float { #debug("type: float"); }
+          ty_poly { #debug("type: poly"); }
+        }
+    }
+    let fmt_sp = args[0].span;
+    let mut n = 0u;
+    let mut tmp_expr = mk_str(cx, sp, "");
+    let nargs = vec::len::<@ast::expr>(args);
+    for pieces.each {|pc|
+        alt pc {
+          piece_string(s) {
+            let s_expr = mk_str(cx, fmt_sp, s);
+            tmp_expr = mk_binary(cx, fmt_sp, ast::add, tmp_expr, s_expr);
+          }
+          piece_conv(conv) {
+            n += 1u;
+            if n >= nargs {
+                cx.span_fatal(sp,
+                              "not enough arguments to #fmt " +
+                                  "for the given format string");
+            }
+            #debug("Building conversion:");
+            log_conv(conv);
+            let arg_expr = args[n];
+            let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr);
+            tmp_expr = mk_binary(cx, fmt_sp, ast::add, tmp_expr, c_expr);
+          }
+        }
+    }
+    let expected_nargs = n + 1u; // n conversions + the fmt string
+
+    if expected_nargs < nargs {
+        cx.span_fatal
+            (sp, #fmt["too many arguments to #fmt. found %u, expected %u",
+                           nargs, expected_nargs]);
+    }
+    ret tmp_expr;
+}
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
+//
diff --git a/src/libsyntax/ext/ident_to_str.rs b/src/libsyntax/ext/ident_to_str.rs
new file mode 100644
index 00000000000..7dfb70f1520
--- /dev/null
+++ b/src/libsyntax/ext/ident_to_str.rs
@@ -0,0 +1,11 @@
+import base::*;
+import option;
+
+fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg,
+                     _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args(cx,sp,arg,1u,option::some(1u),"ident_to_str");
+
+    ret make_new_lit(cx, sp,
+                     ast::lit_str(expr_to_ident(cx, args[0u],
+                                                "expected an ident")));
+}
diff --git a/src/libsyntax/ext/log_syntax.rs b/src/libsyntax/ext/log_syntax.rs
new file mode 100644
index 00000000000..5ccbb143b97
--- /dev/null
+++ b/src/libsyntax/ext/log_syntax.rs
@@ -0,0 +1,15 @@
+import base::*;
+import io::writer_util;
+
+fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg,
+                     _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args_no_max(cx,sp,arg,0u,"log_syntax");
+    cx.print_backtrace();
+    io::stdout().write_line(
+        str::connect(vec::map(args,
+                              {|&&ex| print::pprust::expr_to_str(ex)}), ", ")
+    );
+
+    //trivial expression
+    ret @{id: cx.next_id(), node: ast::expr_rec([], option::none), span: sp};
+}
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:
diff --git a/src/libsyntax/ext/simplext.rs b/src/libsyntax/ext/simplext.rs
new file mode 100644
index 00000000000..76b78cb2b86
--- /dev/null
+++ b/src/libsyntax/ext/simplext.rs
@@ -0,0 +1,778 @@
+import codemap::span;
+import std::map::{hashmap, str_hash};
+
+import base::*;
+
+import fold::*;
+import ast_util::respan;
+import ast::{ident, path, ty, blk_, expr, expr_path,
+             expr_vec, expr_mac, mac_invoc, node_id};
+
+export add_new_extension;
+
+fn path_to_ident(pth: @path) -> option<ident> {
+    if vec::len(pth.idents) == 1u && vec::len(pth.types) == 0u {
+        ret some(pth.idents[0u]);
+    }
+    ret 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) -> ! {
+    alt 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|
+        alt elt.node {
+          expr_mac(m) {
+            alt m.node {
+              ast::mac_ellipsis {
+                if res != none {
+                    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;
+    }
+    ret alt 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|
+        alt f(elem) { none { ret none; } some(fv) { res += [fv]; } }
+    }
+    ret some(res);
+}
+
+fn a_d_map(ad: arb_depth<matchable>, f: selector) -> match_result {
+    alt ad {
+      leaf(x) { ret f(x); }
+      seq(ads, span) {
+        alt option_flatten_map(bind a_d_map(_, f), *ads) {
+          none { ret none; }
+          some(ts) { ret some(seq(@ts, span)); }
+        }
+      }
+    }
+}
+
+fn compose_sels(s1: selector, s2: selector) -> selector {
+    fn scomp(s1: selector, s2: selector, m: matchable) -> match_result {
+        ret alt s1(m) {
+              none { none }
+              some(matches) { a_d_map(matches, s2) }
+            }
+    }
+    ret bind scomp(s1, s2, _);
+}
+
+
+
+type binders =
+    {real_binders: hashmap<ident, selector>,
+     mut literal_ast_matchers: [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: str_hash::<selector>(),
+         mut literal_ast_matchers: []};
+    //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 { ret some(leaf(m)); }
+    p_t_s_rec(cx, match_expr(e), trivial_selector, res);
+    ret 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 = str_hash::<arb_depth<matchable>>();
+    //need to do this first, to check vec lengths.
+    for b.literal_ast_matchers.each {|sel|
+        alt sel(match_expr(e)) { none { ret none; } _ { } }
+    }
+    let mut never_mind: bool = false;
+    for b.real_binders.each {|key, val|
+        alt 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 { ret none; }
+    ret 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 { ret cx.next_id(); }
+    fn new_span(cx: ext_ctxt, sp: span) -> span {
+        /* this discards information in the case of macro-defining macros */
+        ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
+    }
+    let afp = default_ast_fold();
+    let f_pre =
+        @{fold_ident: bind transcribe_ident(cx, b, idx_path, _, _),
+          fold_path: bind transcribe_path(cx, b, idx_path, _, _),
+          fold_expr:
+              bind transcribe_expr(cx, b, idx_path, _, _, _, afp.fold_expr),
+          fold_ty: bind transcribe_type(cx, b, idx_path,
+                                        _, _, _, afp.fold_ty),
+          fold_block:
+              bind transcribe_block(cx, b, idx_path, _, _, _, afp.fold_block),
+          map_exprs: bind transcribe_exprs(cx, b, idx_path, _, _),
+          new_id: bind new_id(_, cx)
+          with *afp};
+    let f = make_fold(f_pre);
+    let result = f.fold_expr(body);
+    ret result;
+}
+
+
+/* helper: descend into a matcher */
+fn follow(m: arb_depth<matchable>, idx_path: @mut [uint]) ->
+   arb_depth<matchable> {
+    let mut res: arb_depth<matchable> = m;
+    for vec::each(*idx_path) {|idx|
+        res = alt res {
+          leaf(_) { ret res;/* end of the line */ }
+          seq(new_ms, _) { new_ms[idx] }
+        }
+    }
+    ret res;
+}
+
+fn follow_for_trans(cx: ext_ctxt, mmaybe: option<arb_depth<matchable>>,
+                    idx_path: @mut [uint]) -> option<matchable> {
+    alt mmaybe {
+      none { ret none }
+      some(m) {
+        ret alt follow(m, idx_path) {
+              seq(_, sp) {
+                cx.span_fatal(sp,
+                              "syntax matched under ... but not " +
+                                  "used that way.")
+              }
+              leaf(m) { ret 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<ident, ()> = str_hash::<()>();
+    fn mark_ident(&&i: ident, _fld: ast_fold, b: bindings,
+                  idents: hashmap<ident, ()>) -> ident {
+        if b.contains_key(i) { idents.insert(i, ()); }
+        ret i;
+    }
+    // using fold is a hack: we want visit, but it doesn't hit idents ) :
+    // solve this with macros
+    let f_pre =
+        @{fold_ident: bind mark_ident(_, _, b, idents)
+          with *default_ast_fold()};
+    let f = make_fold(f_pre);
+    f.fold_expr(e); // ignore result
+    for idents.each_key {|x| it(x); };
+}
+
+
+/* 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@(&&@expr) -> @expr, exprs: [@expr]) -> [@expr] {
+    alt elts_to_ell(cx, exprs) {
+      {pre: pre, rep: repeat_me_maybe, post: post} {
+        let mut res = vec::map(pre, recur);
+        alt 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 */
+            free_vars(b, repeat_me) {|fv|
+                let cur_pos = follow(b.get(fv), idx_path);
+                alt cur_pos {
+                  leaf(_) { }
+                  seq(ms, _) {
+                    alt 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 =
+                                #fmt["'%s' occurs %u times, but ", fv, len] +
+                                    #fmt["'%s' occurs %u times", old_name,
+                                         old_len];
+                            cx.span_fatal(repeat_me.span, msg);
+                        }
+                      }
+                    }
+                  }
+                }
+            };
+            alt 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 += [idx];
+                    res += [recur(repeat_me)]; // whew!
+                    vec::pop(*idx_path);
+                    idx += 1u;
+                }
+              }
+            }
+          }
+        }
+        res += vec::map(post, recur);
+        ret 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 {
+    ret alt 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 { ret p; }
+    alt 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)
+{
+    ret alt e {
+          expr_path(p) {
+            // Don't substitute into qualified names.
+            if vec::len(p.types) > 0u || vec::len(p.idents) != 1u {
+                (e, s);
+            }
+            alt 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)
+{
+    ret alt t {
+          ast::ty_path(pth, _) {
+            alt path_to_ident(pth) {
+              some(id) {
+                alt 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)
+{
+    ret alt block_to_ident(blk) {
+          some(id) {
+            alt 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
+    alt m {
+      match_expr(e) {
+        alt e.node {
+          expr_path(p_pth) { p_t_s_r_path(cx, p_pth, s, b); }
+          expr_vec(p_elts, _) {
+            alt 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 != [] {
+                    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: handle embedded types and blocks, at least
+             (Issue #2251) */
+          expr_mac(mac) {
+            p_t_s_r_mac(cx, mac, s, b);
+          }
+          _ {
+            fn select(cx: ext_ctxt, m: matchable, pat: @expr) ->
+               match_result {
+                ret alt m {
+                      match_expr(e) {
+                        if e == pat { some(leaf(match_exact)) } else { none }
+                      }
+                      _ { cx.bug("broken traversal in p_t_s_r") }
+                    }
+            }
+            b.literal_ast_matchers += [bind select(cx, _, e)];
+          }
+        }
+      }
+      _ {
+          cx.bug("undocumented invariant in p_t_s_rec");
+      }
+    }
+}
+
+
+/* make a match more precise */
+fn specialize_match(m: matchable) -> matchable {
+    ret alt m {
+          match_expr(e) {
+            alt e.node {
+              expr_path(pth) {
+                alt 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) {
+    alt path_to_ident(p) {
+      some(p_id) {
+        fn select(cx: ext_ctxt, m: matchable) -> match_result {
+            ret alt m {
+                  match_expr(e) { 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, bind select(cx, _)));
+      }
+      none { }
+    }
+}
+
+fn block_to_ident(blk: blk_) -> option<ident> {
+    if vec::len(blk.stmts) != 0u { ret none; }
+    ret alt blk.expr {
+          some(expr) {
+            alt 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 {
+        ret alt m {
+              match_expr(e) {
+                alt 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");
+    }
+    alt mac.node {
+      ast::mac_ellipsis { cx.span_fatal(mac.span, "misused `...`"); }
+      ast::mac_invoc(_, _, _) { no_des(cx, mac.span, "macro calls"); }
+      ast::mac_embed_type(ty) {
+        alt ty.node {
+          ast::ty_path(pth, _) {
+            alt path_to_ident(pth) {
+              some(id) {
+                /* look for an embedded type */
+                fn select_pt_2(m: ast::mac) -> match_result {
+                    ret alt m.node {
+                          ast::mac_embed_type(t) { some(leaf(match_ty(t))) }
+                          _ { none }
+                        }
+                }
+                let final_step = bind select_pt_1(cx, _, select_pt_2);
+                b.real_binders.insert(id, compose_sels(s, final_step));
+              }
+              none { no_des(cx, pth.span, "under `#<>`"); }
+            }
+          }
+          _ { no_des(cx, ty.span, "under `#<>`"); }
+        }
+      }
+      ast::mac_embed_block(blk) {
+        alt block_to_ident(blk.node) {
+          some(id) {
+            fn select_pt_2(m: ast::mac) -> match_result {
+                ret alt m.node {
+                      ast::mac_embed_block(blk) {
+                        some(leaf(match_block(blk)))
+                      }
+                      _ { none }
+                    }
+            }
+            let final_step = bind select_pt_1(cx, _, select_pt_2);
+            b.real_binders.insert(id, compose_sels(s, final_step));
+          }
+          none { no_des(cx, blk.span, "under `#{}`"); }
+        }
+      }
+      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 {
+        ret alt m {
+              match_expr(e) {
+                alt e.node {
+                  expr_vec(arg_elts, _) {
+                    let mut elts = [];
+                    let mut idx = offset;
+                    while idx < vec::len(arg_elts) {
+                        elts += [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, bind select(cx, repeat_me, offset, _)), 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 {
+        ret alt m {
+              match_expr(e) {
+                alt 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 +=
+        [compose_sels(s, bind len_select(cx, _, 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 {
+            ret alt m {
+                  match_expr(e) {
+                    alt 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, bind select(cx, _, 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|
+        alt arg.node {
+          expr_vec(elts, mutbl) {
+            if vec::len(elts) != 2u {
+                cx.span_fatal((*arg).span,
+                              "extension clause must consist of [" +
+                                  "macro invocation, expansion body]");
+            }
+
+
+            alt elts[0u].node {
+              expr_mac(mac) {
+                alt mac.node {
+                  mac_invoc(pth, invoc_arg, body) {
+                    alt path_to_ident(pth) {
+                      some(id) {
+                        alt macro_name {
+                          none { macro_name = some(id); }
+                          some(other_id) {
+                            if id != 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 = alt invoc_arg {
+                      some(arg) { arg }
+                      none { cx.span_fatal(mac.span,
+                                           "macro must have arguments")}
+                    };
+                    clauses +=
+                        [@{params: pattern_to_selectors(cx, arg),
+                           body: elts[1u]}];
+
+                    // FIXME: check duplicates (or just simplify
+                    // the macro arg situation) (Issue #2251)
+                  }
+                  _ {
+                      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 = bind generic_extension(_, _, _, _, clauses);
+
+    ret {ident:
+             alt macro_name {
+               some(id) { id }
+               none {
+                 cx.span_fatal(sp, "macro definition must have " +
+                               "at least one clause")
+               }
+             },
+         ext: normal({expander: ext, span: some(option::get(arg).span)})};
+
+    fn generic_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+                         _body: ast::mac_body, clauses: [@clause]) -> @expr {
+        let arg = alt arg {
+          some(arg) { arg }
+          none { cx.span_fatal(sp, "macro must have arguments")}
+        };
+        for clauses.each {|c|
+            alt use_selectors_to_bind(c.params, arg) {
+              some(bindings) { ret transcribe(cx, bindings, c.body); }
+              none { cont; }
+            }
+        }
+        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:
+//
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
new file mode 100644
index 00000000000..99b928cfb9c
--- /dev/null
+++ b/src/libsyntax/ext/source_util.rs
@@ -0,0 +1,115 @@
+import base::*;
+import ast;
+import codemap::span;
+import print::pprust;
+
+export expand_line;
+export expand_col;
+export expand_file;
+export expand_stringify;
+export expand_mod;
+export expand_include;
+export expand_include_str;
+export expand_include_bin;
+
+/* #line(): expands to the current line number */
+fn expand_line(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+               _body: ast::mac_body) -> @ast::expr {
+    get_mac_args(cx, sp, arg, 0u, option::some(0u), "line");
+    let loc = codemap::lookup_char_pos(cx.codemap(), sp.lo);
+    ret make_new_lit(cx, sp, ast::lit_uint(loc.line as u64, ast::ty_u));
+}
+
+/* #col(): expands to the current column number */
+fn expand_col(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+              _body: ast::mac_body) -> @ast::expr {
+    get_mac_args(cx, sp, arg, 0u, option::some(0u), "col");
+    let loc = codemap::lookup_char_pos(cx.codemap(), sp.lo);
+    ret make_new_lit(cx, sp, ast::lit_uint(loc.col as u64, ast::ty_u));
+}
+
+/* #file(): expands to the current filename */
+/* The filemap (`loc.file`) contains a bunch more information we could spit
+ * out if we wanted. */
+fn expand_file(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+               _body: ast::mac_body) -> @ast::expr {
+    get_mac_args(cx, sp, arg, 0u, option::some(0u), "file");
+    let loc = codemap::lookup_char_pos(cx.codemap(), sp.lo);
+    ret make_new_lit(cx, sp, ast::lit_str(loc.file.name));
+}
+
+fn expand_stringify(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+                    _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args(cx, sp, arg, 1u, option::some(1u), "stringify");
+    ret make_new_lit(cx, sp, ast::lit_str(pprust::expr_to_str(args[0])));
+}
+
+fn expand_mod(cx: ext_ctxt, sp: span, arg: ast::mac_arg, _body: ast::mac_body)
+    -> @ast::expr {
+    get_mac_args(cx, sp, arg, 0u, option::some(0u), "file");
+    ret make_new_lit(cx, sp, ast::lit_str(str::connect(cx.mod_path(), "::")));
+}
+
+fn expand_include(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
+                  _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args(cx, sp, arg, 1u, option::some(1u), "include");
+    let file = expr_to_str(cx, args[0], "#include_str requires a string");
+    let p = parse::new_parser_from_file(cx.parse_sess(), cx.cfg(),
+                                        res_rel_file(cx, sp, file),
+                                        parse::parser::SOURCE_FILE);
+    ret parse::parser::parse_expr(p)
+}
+
+fn expand_include_str(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg,
+                      _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args(cx,sp,arg,1u,option::some(1u),"include_str");
+
+    let file = expr_to_str(cx, args[0], "#include_str requires a string");
+
+    alt io::read_whole_file_str(res_rel_file(cx, sp, file)) {
+      result::ok(src) { ret make_new_lit(cx, sp, ast::lit_str(src)); }
+      result::err(e) {
+        cx.parse_sess().span_diagnostic.handler().fatal(e)
+      }
+    }
+}
+
+fn expand_include_bin(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg,
+                      _body: ast::mac_body) -> @ast::expr {
+    let args = get_mac_args(cx,sp,arg,1u,option::some(1u),"include_bin");
+
+    let file = expr_to_str(cx, args[0], "#include_bin requires a string");
+
+    alt io::read_whole_file(res_rel_file(cx, sp, file)) {
+      result::ok(src) {
+        let u8_exprs = vec::map(src) { |char: u8|
+            make_new_lit(cx, sp, ast::lit_uint(char as u64, ast::ty_u8))
+        };
+        ret make_new_expr(cx, sp, ast::expr_vec(u8_exprs, ast::m_imm));
+      }
+      result::err(e) {
+        cx.parse_sess().span_diagnostic.handler().fatal(e)
+      }
+    }
+}
+
+fn res_rel_file(cx: ext_ctxt, sp: codemap::span, arg: path) -> path {
+    // NB: relative paths are resolved relative to the compilation unit
+    if !path::path_is_absolute(arg) {
+        let cu = codemap::span_to_filename(sp, cx.codemap());
+        let dir = path::dirname(cu);
+        ret path::connect(dir, arg);
+    } else {
+        ret arg;
+    }
+}
+
+//
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
+//