about summary refs log tree commit diff
path: root/src/comp/syntax/ext
diff options
context:
space:
mode:
authorPaul Stansifer <paul.stansifer@gmail.com>2011-07-06 15:22:23 -0700
committerPaul Stansifer <paul.stansifer@gmail.com>2011-07-11 18:52:50 -0700
commit425732311af6d7a4f972dff04e1abb7db6a22f3b (patch)
tree5b3d5493c7fc3a32a28b166f5ea393d640537c23 /src/comp/syntax/ext
parent80cf4ecd3bfe602004145e57cf7fdfb067b76ce1 (diff)
Move macro expansion to a separate phase, change macro syntax, and add parse_sess to session.
Diffstat (limited to 'src/comp/syntax/ext')
-rw-r--r--src/comp/syntax/ext/base.rs23
-rw-r--r--src/comp/syntax/ext/expand.rs65
-rw-r--r--src/comp/syntax/ext/simplext.rs272
3 files changed, 276 insertions, 84 deletions
diff --git a/src/comp/syntax/ext/base.rs b/src/comp/syntax/ext/base.rs
index 0990b3bdacb..84319cf5f38 100644
--- a/src/comp/syntax/ext/base.rs
+++ b/src/comp/syntax/ext/base.rs
@@ -2,7 +2,7 @@ import std::ivec;
 import std::vec;
 import std::option;
 import std::map::hashmap;
-import parse::parser::parse_sess;
+import driver::session::session;
 import codemap::span;
 import std::map::new_str_hash;
 import codemap;
@@ -41,14 +41,14 @@ type ext_ctxt =
         span_msg_fn span_unimpl,
         next_id_fn next_id);
 
-fn mk_ctxt(&parse_sess sess) -> ext_ctxt {
-    fn ext_span_fatal_(&codemap::codemap cm, span sp, str msg) -> ! {
-        codemap::emit_error(option::some(sp), msg, cm);
+fn mk_ctxt(&session sess) -> ext_ctxt {
+    fn ext_span_fatal_(&session sess, span sp, str msg) -> ! {
+        sess.span_err(sp, msg);
         fail;
     }
-    auto ext_span_fatal = bind ext_span_fatal_(sess.cm, _, _);
-    fn ext_span_unimpl_(&codemap::codemap cm, span sp, str msg) -> ! {
-        codemap::emit_error(option::some(sp), "unimplemented " + msg, cm);
+    auto ext_span_fatal = bind ext_span_fatal_(sess, _, _);
+    fn ext_span_unimpl_(&session sess, span sp, str msg) -> ! {
+        sess.span_err(sp, "unimplemented " + msg);
         fail;
     }
 
@@ -59,9 +59,12 @@ fn mk_ctxt(&parse_sess sess) -> ext_ctxt {
     // the extensions the file name of the crate being compiled so they can
     // use it to guess whether paths should be prepended with "std::". This is
     // super-ugly and needs a better solution.
-    auto crate_file_name_hack = sess.cm.files.(0).name;
-    auto ext_span_unimpl = bind ext_span_unimpl_(sess.cm, _, _);
-    auto ext_next_id = bind parse::parser::next_node_id(sess);
+    auto crate_file_name_hack = sess.get_codemap().files.(0).name;
+    auto ext_span_unimpl = bind ext_span_unimpl_(sess, _, _);
+    fn ext_next_id_(&session sess) -> ast::node_id {
+        ret sess.next_node_id(); // temporary, until bind works better
+    }
+    auto ext_next_id = bind ext_next_id_(sess);
     ret rec(crate_file_name_hack=crate_file_name_hack,
             span_fatal=ext_span_fatal,
             span_unimpl=ext_span_unimpl,
diff --git a/src/comp/syntax/ext/expand.rs b/src/comp/syntax/ext/expand.rs
new file mode 100644
index 00000000000..3af9fdc5070
--- /dev/null
+++ b/src/comp/syntax/ext/expand.rs
@@ -0,0 +1,65 @@
+
+import codemap::emit_error;
+import driver::session;
+import syntax::ast::crate;
+import syntax::ast::expr_;
+import syntax::ast::expr_ext;
+import syntax::fold::*;
+
+import std::option::none;
+import std::option::some;
+
+import std::map::hashmap;
+import std::ivec;
+
+fn expand_expr(&hashmap[str, base::syntax_extension] exts, 
+               &session::session sess, &expr_ e, ast_fold fld, 
+               &fn(&ast::expr_, ast_fold) -> expr_ orig) -> expr_ {
+    ret alt(e) {
+        case (expr_ext(?pth, ?args, ?body)) {
+            assert(ivec::len(pth.node.idents) > 0u);
+            auto extname = pth.node.idents.(0);
+            auto ext_cx = base::mk_ctxt(sess);
+            alt (exts.find(extname)) {
+                case (none) {
+                    emit_error(some(pth.span), "unknown syntax expander: '"
+                               + extname + "'", sess.get_codemap());
+                    fail
+                }
+                case (some(base::normal(?ext))) {
+                    //keep going, outside-in
+                    fld.fold_expr(ext(ext_cx, pth.span, args, body)).node
+                }
+                case (some(base::macro_defining(?ext))) {
+                    auto named_extension = ext(ext_cx, pth.span, args, body);
+                    exts.insert(named_extension._0, named_extension._1);
+                    ast::expr_tup([])
+                }
+            }
+
+        }
+        case (_) { orig(e, fld) }
+    };
+}
+
+fn expand_crate(&session::session sess, &@crate c) -> @crate {
+    auto exts = ext::base::syntax_expander_table();
+    auto afp = default_ast_fold();
+    auto f_pre =
+        rec(fold_expr = bind expand_expr(exts, sess, _, _, afp.fold_expr)
+            with *afp);
+    auto f = make_fold(f_pre);
+    auto res = @f.fold_crate(*c);
+    dummy_out(f); //temporary: kill circular reference
+    ret res;
+    
+}
+
+// Local Variables:
+// mode: rust
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+// End:
diff --git a/src/comp/syntax/ext/simplext.rs b/src/comp/syntax/ext/simplext.rs
index 46b1fd636ce..59505a5a89a 100644
--- a/src/comp/syntax/ext/simplext.rs
+++ b/src/comp/syntax/ext/simplext.rs
@@ -16,121 +16,245 @@ import base::expr_to_str;
 import base::expr_to_ident;
 
 import fold::*;
+import ast::respan;
 import ast::ident;
+import ast::path;
 import ast::path_;
 import ast::expr_path;
+import ast::expr_vec;
+import ast::expr_ext;
 
 export add_new_extension;
 
-
-//temporary, until 'position' shows up in the snapshot
-fn position[T](&T x, &vec[T] v) -> option::t[uint] {
-    let uint i = 0u;
-    while (i < len(v)) {
-        if (x == v.(i)) { ret some[uint](i); }
-        i += 1u;
+fn lookup(&vec[invk_binding] ibs, ident i) -> option::t[invk_binding] {
+    for (invk_binding ib in ibs) {
+        alt (ib) {
+            case (ident_binding(?p_id, _)) { if (i == p_id) { ret some(ib); }}
+            case (path_binding(?p_id, _)) { if (i == p_id) { ret some(ib); }}
+            case (expr_binding(?p_id, _)) { if (i == p_id) { ret some(ib); }}
+        }
     }
-    ret none[uint];
+    ret none;
 }
 
 // substitute, in a position that's required to be an ident
-fn subst_ident(&ext_ctxt cx, &(@ast::expr)[] args, 
-               @vec[ident] param_names, &ident i, ast_fold fld) -> ident {
-    alt (position(i, *param_names)) {
-        case (some[uint](?idx)) {
-            ret expr_to_ident(cx, args.(idx), 
-                              "This argument is expanded as an "
-                              + "identifier; it must be one.");
+fn subst_ident(&ext_ctxt cx, &(invk_binding)[] ibs, &ident i, ast_fold fld)
+    -> ident {
+    ret alt (lookup(ibs, i)) {
+        case (some(ident_binding(_, ?a_id))) { a_id.node }
+        case (some(path_binding(_, ?pth))) {
+            cx.span_fatal(pth.span, "This argument is expanded as an "
+                          + "identifier; it must be one.")
         }
-        case (none[uint]) {
-            ret i;
+        case (some(expr_binding(_, ?expr))) {
+            cx.span_fatal(expr.span, "This argument is expanded as an "
+                          + "identifier; it must be one.")
         }
+        case (none) { i }
     }
 }
 
-fn subst_path(&ext_ctxt cx, &(@ast::expr)[] args, 
-              @vec[ident] param_names, &path_ p, ast_fold fld) -> path_ {
+
+fn subst_path(&ext_ctxt cx, &(invk_binding)[] ibs, &path_ p, ast_fold fld)
+    -> path_ {
     // Don't substitute into qualified names.
     if (ivec::len(p.types) > 0u || ivec::len(p.idents) != 1u) { ret p; }
-    alt (position(p.idents.(0), *param_names)) {
-        case (some[uint](?idx)) {
-            alt (args.(idx).node) {
-                case (expr_path(?new_path)) {
-                    ret new_path.node;
-                }
-                case (_) {
-                    cx.span_fatal(args.(idx).span,
-                                "This argument is expanded as a path; "
-                                + "it must be one.");
-                }
-            }
+    ret alt (lookup(ibs, p.idents.(0))) {
+        case (some(ident_binding(_, ?id))) { 
+            rec(global=false, idents=~[id.node], types=~[]) 
         }
-        case (none[uint]) { ret p; }
+        case (some(path_binding(_, ?a_pth))) { a_pth.node }
+        case (some(expr_binding(_, ?expr))) {
+            cx.span_fatal(expr.span, "This argument is expanded as an "
+                          + "path; it must be one.")
+        }
+        case (none) { p }
     }
 }
 
 
-fn subst_expr(&ext_ctxt cx, &(@ast::expr)[] args, @vec[ident] param_names, 
-              &ast::expr_ e, ast_fold fld, 
-              fn(&ast::expr_, ast_fold) -> ast::expr_ orig) -> ast::expr_ {
+fn subst_expr(&ext_ctxt cx, &(invk_binding)[] ibs, &ast::expr_ e, 
+              ast_fold fld, fn(&ast::expr_, ast_fold) -> ast::expr_ orig) 
+    -> ast::expr_ {
     ret alt(e) {
         case (expr_path(?p)){
             // Don't substitute into qualified names.
-            if (ivec::len(p.node.types) > 0u ||
-                    ivec::len(p.node.idents) != 1u) { e }
-            alt (position(p.node.idents.(0), *param_names)) {
-                case (some[uint](?idx)) {
-                    args.(idx).node
+            if (ivec::len(p.node.types) > 0u || 
+                ivec::len(p.node.idents) != 1u) { e }
+            alt (lookup(ibs, p.node.idents.(0))) {
+                case (some(ident_binding(_, ?id))) { 
+                    expr_path(respan(id.span, 
+                                     rec(global=false, 
+                                         idents=~[id.node],types=~[])))
                 }
-                case (none[uint]) { e }
+                case (some(path_binding(_, ?a_pth))) { expr_path(*a_pth) }
+                case (some(expr_binding(_, ?a_exp))) { a_exp.node }
+                case (none) { orig(e,fld) }
             }
         }
         case (_) { orig(e,fld) }
     }
 }
 
+type pat_ext = rec(vec[@ast::expr] invk, @ast::expr body);
+
+// maybe box?
+tag invk_binding {
+    expr_binding(ident, @ast::expr);
+    path_binding(ident, @ast::path);
+    ident_binding(ident, ast::spanned[ident]);
+}
+
+fn path_to_ident(&path pth) -> option::t[ident] {
+    if (ivec::len(pth.node.idents) == 1u 
+        && ivec::len(pth.node.types) == 0u) {
+        ret some(pth.node.idents.(0u));
+    }
+    ret none;
+}
 
 fn add_new_extension(&ext_ctxt cx, span sp, &(@ast::expr)[] args,
                      option::t[str] body) -> tup(str, syntax_extension) {
-    if (ivec::len(args) < 2u) {
-        cx.span_fatal(sp, "malformed extension description");
+    let option::t[str] macro_name = none;
+    let vec[pat_ext] pat_exts = [];
+    for (@ast::expr arg in args) {
+        alt(arg.node) {
+            case(expr_vec(?elts, ?mut, ?seq_kind)) {
+                
+                if (len(elts) != 2u) {
+                    cx.span_fatal((*arg).span, 
+                                  "extension clause must consist of [" + 
+                                  "macro invocation, expansion body]");
+                }
+                alt(elts.(0u).node) {
+                    case(expr_ext(?pth, ?invk_args, ?body)) {
+                        let str clause_name = alt(path_to_ident(pth)) {
+                            case (some(?id)) { id }
+                            case (none) {
+                                cx.span_fatal
+                                (elts.(0u).span,
+                                 "macro name must not be a path")
+                            }
+                        };
+                        if (macro_name == none) {
+                            macro_name = some(clause_name);
+                        } else if (macro_name != some(clause_name)) {
+                            cx.span_fatal(elts.(0u).span, "macros must have"
+                                          + " only one name");
+                        }
+                        pat_exts += [rec(invk=invk_args, body=elts.(1u))];
+                    }
+                    case(_) {
+                        cx.span_fatal(elts.(0u).span, "extension clause must"
+                                      + " start with a macro invocation.");
+                    }
+                }
+            }
+            case(_) {
+                    cx.span_fatal((*arg).span, "extension must be [clause, "
+                                  + " ...]");
+            }
+        }
     }
 
-    fn generic_extension(&ext_ctxt cx, span sp, &(@ast::expr)[] args,
-                         option::t[str] body, @vec[ident] param_names,
-                         @ast::expr dest_form) -> @ast::expr {
-        if (ivec::len(args) != len(*param_names)) {
-            cx.span_fatal(sp, #fmt("extension expects %u arguments, got %u",
-                                 len(*param_names), ivec::len(args)));
+    auto ext = bind generic_extension(_,_,_,_,@pat_exts);
+    
+    ret tup(alt (macro_name) {
+                case (some(?id)) { id }
+                case (none) { 
+                    cx.span_fatal(sp, "macro definition must have "
+                                  + "at least one clause")
+                }
+            },
+            normal(ext));
+
+
+    fn generic_extension(&ext_ctxt cx, span sp, &vec[@ast::expr] args,
+                         option::t[str] body, @vec[pat_ext] clauses)
+        -> @ast::expr {
+
+        /* returns a list of bindings, or none if the match fails. */
+        fn match_invk(@ast::expr pattern, @ast::expr argument)
+            -> option::t[vec[invk_binding]] {
+            auto pat = pattern.node;
+            auto arg = argument.node;
+            ret alt (pat) {
+                case (expr_vec(?p_elts, _, _)) {
+                    alt (arg) {
+                        case (expr_vec(?a_elts, _, _)) {
+                            if (vec::len(p_elts) != vec::len(a_elts)) { 
+                                none[vec[invk_binding]]
+                            }
+                            let uint i = 0u;
+                            let vec[invk_binding] res = [];
+                            while (i < vec::len(p_elts)) {
+                                alt (match_invk(p_elts.(i), a_elts.(i))) {
+                                    case (some(?v)) { res += v; }
+                                    case (none) { ret none; }
+                                }
+                                i += 1u;
+                            }
+                            some(res)
+                        }
+                        case (_) { none }
+                    }
+                }
+                case (expr_path(?p_pth)) {
+                    alt (path_to_ident(p_pth)) {
+                        case (some(?p_id)) {
+                            /* let's bind! */
+                            alt (arg) {
+                                case (expr_path(?a_pth)) {
+                                    alt (path_to_ident(a_pth)) {
+                                        case (some(?a_id)) {
+                                            some([ident_binding
+                                                  (p_id, respan(argument.span,
+                                                                 a_id))])
+                                        }
+                                        case (none) {
+                                            some([path_binding(p_id, @a_pth)])
+                                        }
+                                    }
+                                }
+                                case (_) {
+                                    some([expr_binding(p_id, argument)])
+                                }
+                            }
+                        }
+                        // FIXME this still compares on internal spans
+                        case (_) { if(pat == arg) { some([]) } else { none } }
+                    }
+                }
+                // FIXME this still compares on internal spans
+                case (_) { if (pat == arg) { some([]) } else { none } }
+            }
         }
 
-        // FIXME: This binds to alias arguments.
-        auto afp = default_ast_fold();
-        auto f_pre = 
-            rec(fold_ident = bind subst_ident(cx, args, param_names, _, _),
-                fold_path = bind subst_path(cx, args, param_names, _, _),
-                fold_expr = bind subst_expr(cx, args, param_names, _, _,
-                                            afp.fold_expr)
+        for (pat_ext pe in *clauses) {
+            if (vec::len(args) != vec::len(pe.invk)) { cont; }
+            let uint i = 0u;
+            let vec[invk_binding] bindings = [];
+            while (i < vec::len(args)) {
+                alt (match_invk(pe.invk.(i), args.(i))) {
+                    case (some(?v)) { bindings += v; }
+                    case (none) { cont }
+                }
+                i += 1u;
+            }
+            auto afp = default_ast_fold();
+            auto f_pre =
+                rec(fold_ident = bind subst_ident(cx, bindings, _, _),
+                    fold_path = bind subst_path(cx, bindings, _, _),
+                    fold_expr = bind subst_expr(cx, bindings, _, _,
+                                                afp.fold_expr)
                 with *afp);
-        auto f = make_fold(f_pre);
-        auto result = f.fold_expr(dest_form);
-        dummy_out(f); //temporary: kill circular reference
-        ret result;
-        
-    }
-
-    let vec[ident] param_names = vec::empty[ident]();
-    let uint idx = 1u;
-    while(1u+idx < ivec::len(args)) {
-        param_names +=
-            [expr_to_ident(cx, args.(idx),
-                           "this parameter name must be an identifier.")];
-        idx += 1u;
+            auto f = make_fold(f_pre);
+            auto result = f.fold_expr(pe.body);
+            dummy_out(f); //temporary: kill circular reference
+            ret result;
+        }
+        cx.span_fatal(sp, "no clauses match macro invocation");
     }
-
-    ret tup(expr_to_str(cx, args.(0), "first arg must be a literal string."),
-            normal(bind generic_extension(_,_,_,_,@param_names,
-                                          args.(ivec::len(args)-1u))));
 }