2011-06-20 17:26:17 -07:00
|
|
|
use std;
|
|
|
|
|
2011-07-05 11:48:19 +02:00
|
|
|
import codemap::span;
|
2011-07-05 16:23:07 -07:00
|
|
|
import std::ivec;
|
2011-06-20 17:26:17 -07:00
|
|
|
import std::vec;
|
|
|
|
import std::option;
|
|
|
|
import vec::map;
|
|
|
|
import vec::len;
|
2011-07-21 16:47:47 -07:00
|
|
|
import std::map::hashmap;
|
|
|
|
import std::map::new_str_hash;
|
2011-06-20 17:26:17 -07:00
|
|
|
import option::some;
|
|
|
|
import option::none;
|
|
|
|
|
2011-07-05 11:48:19 +02:00
|
|
|
import base::syntax_extension;
|
|
|
|
import base::ext_ctxt;
|
|
|
|
import base::normal;
|
|
|
|
import base::expr_to_str;
|
|
|
|
import base::expr_to_ident;
|
2011-06-20 17:26:17 -07:00
|
|
|
|
|
|
|
import fold::*;
|
2011-07-06 15:22:23 -07:00
|
|
|
import ast::respan;
|
2011-06-20 17:26:17 -07:00
|
|
|
import ast::ident;
|
2011-07-06 15:22:23 -07:00
|
|
|
import ast::path;
|
2011-07-21 16:47:47 -07:00
|
|
|
import ast::ty;
|
2011-07-25 14:16:12 -07:00
|
|
|
import ast::blk;
|
2011-07-26 10:53:14 -07:00
|
|
|
import ast::blk_;
|
2011-07-21 16:47:47 -07:00
|
|
|
import ast::expr;
|
|
|
|
import ast::expr_;
|
2011-06-20 17:26:17 -07:00
|
|
|
import ast::path_;
|
|
|
|
import ast::expr_path;
|
2011-07-06 15:22:23 -07:00
|
|
|
import ast::expr_vec;
|
2011-07-08 16:35:09 -07:00
|
|
|
import ast::expr_mac;
|
|
|
|
import ast::mac_invoc;
|
2011-06-20 17:26:17 -07:00
|
|
|
|
|
|
|
export add_new_extension;
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//an ivec of binders might be a little big.
|
|
|
|
type clause = rec((binders)[] params, @expr body);
|
|
|
|
|
|
|
|
/* logically, an arb_depth should contain only one kind of matchable */
|
|
|
|
tag arb_depth[T] {
|
|
|
|
leaf(T);
|
|
|
|
seq(vec[arb_depth[T]], span);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tag matchable {
|
|
|
|
match_expr(@expr);
|
|
|
|
match_path(path);
|
|
|
|
match_ident(ast::spanned[ident]);
|
|
|
|
match_ty(@ty);
|
2011-07-25 14:16:12 -07:00
|
|
|
match_block(ast::blk);
|
2011-07-21 16:47:47 -07:00
|
|
|
match_exact; /* don't bind anything, just verify the AST traversal */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for when given an incompatible bit of AST */
|
|
|
|
fn match_error(&ext_ctxt cx, &matchable m, &str expected) -> ! {
|
|
|
|
alt(m) {
|
|
|
|
case (match_expr(?x)) {
|
|
|
|
cx.span_fatal(x.span, "this argument is an expr, expected "
|
|
|
|
+ expected);
|
|
|
|
}
|
|
|
|
case (match_path(?x)) {
|
|
|
|
cx.span_fatal(x.span, "this argument is a path, expected "
|
|
|
|
+ expected);
|
|
|
|
}
|
|
|
|
case (match_ident(?x)) {
|
|
|
|
cx.span_fatal(x.span, "this argument is an ident, expected "
|
|
|
|
+ expected);
|
|
|
|
}
|
|
|
|
case (match_ty(?x)) {
|
|
|
|
cx.span_fatal(x.span, "this argument is a type, expected "
|
|
|
|
+ expected);
|
|
|
|
}
|
|
|
|
case (match_block(?x)) {
|
|
|
|
cx.span_fatal(x.span, "this argument is a block, expected "
|
|
|
|
+ expected);
|
|
|
|
}
|
|
|
|
case (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::t[arb_depth[matchable]];
|
|
|
|
type selector = fn(&matchable) -> match_result;
|
|
|
|
|
|
|
|
fn elts_to_ell(&ext_ctxt cx, &(@expr)[] elts) -> option::t[@expr] {
|
|
|
|
let uint idx = 0u;
|
|
|
|
for (@expr elt in elts) {
|
|
|
|
alt (elt.node) {
|
|
|
|
case (expr_mac(?m)) {
|
|
|
|
alt (m.node) {
|
|
|
|
case (ast::mac_ellipsis) {
|
|
|
|
if (idx != 1u || ivec::len(elts) != 2u) {
|
|
|
|
cx.span_fatal(m.span,
|
|
|
|
"Ellpisis may only appear"
|
|
|
|
+" after exactly 1 item.");
|
|
|
|
}
|
|
|
|
ret some(elts.(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { }
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
idx += 1u;
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
ret none;
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
fn option_flatten_map[T,U](&fn(&T)->option::t[U] f, &vec[T] v)
|
|
|
|
-> option::t[vec[U]] {
|
|
|
|
auto res = vec::alloc[U](vec::len(v));
|
|
|
|
for (T elem in v) {
|
|
|
|
alt (f(elem)) {
|
|
|
|
case (none) { ret none; }
|
|
|
|
case (some(?fv)) { res += [fv]; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret some(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn a_d_map(&arb_depth[matchable] ad, &selector f)
|
|
|
|
-> match_result {
|
|
|
|
alt (ad) {
|
|
|
|
case (leaf(?x)) { ret f(x); }
|
|
|
|
case (seq(?ads,?span)) {
|
|
|
|
alt (option_flatten_map(bind a_d_map(_, f), ads)) {
|
|
|
|
case (none) { ret none; }
|
|
|
|
case (some(?ts)) { ret some(seq(ts,span)); }
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compose_sels(selector s1, selector s2) -> selector {
|
|
|
|
fn scomp(selector s1, selector s2, &matchable m) ->
|
|
|
|
match_result {
|
|
|
|
ret alt (s1(m)) {
|
|
|
|
case (none) { none }
|
|
|
|
case (some(?matches)) { a_d_map(matches, s2) }
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
ret bind scomp(s1, s2, _);
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
|
2011-07-06 15:22:23 -07:00
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
|
|
|
|
type binders = rec(hashmap[ident,selector] real_binders,
|
|
|
|
mutable (selector)[] literal_ast_matchers);
|
|
|
|
type bindings = hashmap[ident, arb_depth[matchable]];
|
|
|
|
|
|
|
|
fn acumm_bindings(&ext_ctxt cx, &bindings b_dest, &bindings b_src) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/* these three functions are the big moving parts */
|
|
|
|
|
|
|
|
/* create the selectors needed to bind and verify the pattern */
|
|
|
|
|
|
|
|
fn pattern_to_selectors(&ext_ctxt cx, @expr e) -> binders {
|
|
|
|
let binders res = rec(real_binders=new_str_hash[selector](),
|
|
|
|
mutable literal_ast_matchers=~[]);
|
|
|
|
//this oughta return binders instead, but macro args are a sequence of
|
|
|
|
//expressions, rather than a single expression
|
|
|
|
fn trivial_selector(&matchable m) -> 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(&binders b, @expr e) -> option::t[bindings] {
|
|
|
|
auto res = new_str_hash[arb_depth[matchable]]();
|
|
|
|
let bool never_mind = false;
|
2011-07-26 14:06:02 +02:00
|
|
|
for each(@rec(ident key, selector val) pair
|
|
|
|
in b.real_binders.items()) {
|
|
|
|
alt (pair.val(match_expr(e))) {
|
2011-07-21 16:47:47 -07:00
|
|
|
case (none) { never_mind = true; }
|
2011-07-26 14:06:02 +02:00
|
|
|
case (some(?mtc)) { res.insert(pair.key, mtc); }
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (never_mind) { ret none; } //HACK: `ret` doesn't work in `for each`
|
|
|
|
for (selector sel in b.literal_ast_matchers) {
|
|
|
|
alt (sel(match_expr(e))) {
|
|
|
|
case (none) { ret none; }
|
|
|
|
case (_) { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret some(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use the bindings on the body to generate the expanded code */
|
|
|
|
|
|
|
|
fn transcribe(&ext_ctxt cx, &bindings b, @expr body) -> @expr {
|
|
|
|
let @mutable vec[uint] idx_path = @mutable [];
|
|
|
|
auto afp = default_ast_fold();
|
|
|
|
auto f_pre =
|
|
|
|
rec(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),
|
2011-07-26 10:53:14 -07:00
|
|
|
fold_ty = bind transcribe_type(cx, b, idx_path, _, _,
|
|
|
|
afp.fold_ty),
|
2011-07-26 11:11:25 -07:00
|
|
|
fold_block = bind transcribe_block(cx, b, idx_path, _, _,
|
2011-07-26 10:53:14 -07:00
|
|
|
afp.fold_block),
|
2011-07-21 16:47:47 -07:00
|
|
|
map_exprs = bind transcribe_exprs(cx, b, idx_path, _, _)
|
|
|
|
with *afp);
|
|
|
|
auto f = make_fold(f_pre);
|
|
|
|
auto result = f.fold_expr(body);
|
|
|
|
dummy_out(f); //temporary: kill circular reference
|
|
|
|
ret result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* helper: descend into a matcher */
|
|
|
|
fn follow(&arb_depth[matchable] m, @mutable vec[uint] idx_path)
|
|
|
|
-> arb_depth[matchable] {
|
|
|
|
let arb_depth[matchable] res = m;
|
|
|
|
for (uint idx in *idx_path) {
|
|
|
|
alt(res) {
|
|
|
|
case (leaf(_)) { ret res; /* end of the line */ }
|
|
|
|
case (seq(?new_ms,_)) { res = new_ms.(idx); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret res;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn follow_for_trans(&ext_ctxt cx, &option::t[arb_depth[matchable]] mmaybe,
|
|
|
|
@mutable vec[uint] idx_path) -> option::t[matchable] {
|
|
|
|
alt(mmaybe) {
|
|
|
|
case (none) { ret none }
|
|
|
|
case (some(?m)) {
|
|
|
|
ret alt(follow(m, idx_path)) {
|
|
|
|
case (seq(_,?sp)) {
|
|
|
|
cx.span_fatal(sp, "syntax matched under ... but not "
|
|
|
|
+ "used that way.")
|
|
|
|
}
|
|
|
|
case (leaf(?m)) {
|
|
|
|
ret some(m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* helper for transcribe_exprs: what vars from `b` occur in `e`? */
|
|
|
|
iter free_vars(&bindings b, @expr e) -> ident {
|
|
|
|
let hashmap[ident,()] idents = new_str_hash[()]();
|
|
|
|
fn mark_ident(&ident i, ast_fold fld, &bindings b,
|
|
|
|
&hashmap[ident,()] idents) -> 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
|
|
|
|
auto f_pre = rec(fold_ident=bind mark_ident(_, _, b, idents)
|
|
|
|
with *default_ast_fold());
|
|
|
|
auto f = make_fold(f_pre);
|
|
|
|
f.fold_expr(e); // ignore result
|
|
|
|
dummy_out(f);
|
2011-07-26 14:06:02 +02:00
|
|
|
for each(ident id in idents.keys()) { put id; }
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handle sequences (anywhere in the AST) of exprs, either real or ...ed */
|
|
|
|
fn transcribe_exprs(&ext_ctxt cx, &bindings b, @mutable vec[uint] idx_path,
|
|
|
|
fn(&@expr)->@expr recur, (@expr)[] exprs) -> (@expr)[] {
|
|
|
|
alt (elts_to_ell(cx, exprs)) {
|
|
|
|
case (some(?repeat_me)) {
|
|
|
|
let option::t[rec(uint rep_count, ident name)] repeat = none;
|
|
|
|
/* we need to walk over all the free vars in lockstep, except for
|
|
|
|
the leaves, which are just duplicated */
|
|
|
|
for each (ident fv in free_vars(b, repeat_me)) {
|
|
|
|
auto cur_pos = follow(b.get(fv), idx_path);
|
|
|
|
alt (cur_pos) {
|
|
|
|
case (leaf(_)) { }
|
|
|
|
case (seq(?ms,_)) {
|
|
|
|
alt (repeat) {
|
|
|
|
case (none) {
|
|
|
|
repeat = some
|
|
|
|
(rec(rep_count=vec::len(ms), name=fv));
|
|
|
|
}
|
|
|
|
case (some({rep_count: ?old_len,
|
|
|
|
name: ?old_name})) {
|
|
|
|
auto len = vec::len(ms);
|
|
|
|
if (old_len != len) {
|
|
|
|
cx.span_fatal
|
|
|
|
(repeat_me.span,
|
|
|
|
#fmt("'%s' occurs %u times, but ",
|
|
|
|
fv, len)+
|
|
|
|
#fmt("'%s' occurs %u times",
|
|
|
|
old_name, old_len));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
auto res = ~[];
|
|
|
|
alt (repeat) {
|
|
|
|
case (none) {
|
|
|
|
cx.span_fatal(repeat_me.span,
|
|
|
|
"'...' surrounds an expression without any"
|
|
|
|
+ " repeating syntax variables");
|
|
|
|
}
|
|
|
|
case (some({rep_count: ?rc, _})) {
|
|
|
|
/* Whew, we now know how how many times to repeat */
|
|
|
|
let uint idx = 0u;
|
|
|
|
while (idx < rc) {
|
|
|
|
vec::push(*idx_path, idx);
|
|
|
|
res += ~[recur(repeat_me)]; // whew!
|
|
|
|
vec::pop(*idx_path);
|
|
|
|
idx += 1u;
|
|
|
|
}
|
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
ret res;
|
|
|
|
}
|
|
|
|
case (none) { ret ivec::map(recur, exprs); }
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
|
|
|
|
// substitute, in a position that's required to be an ident
|
|
|
|
fn transcribe_ident(&ext_ctxt cx, &bindings b, @mutable vec[uint] idx_path,
|
|
|
|
&ident i, ast_fold fld) -> ident {
|
|
|
|
ret alt (follow_for_trans(cx, b.find(i), idx_path)) {
|
|
|
|
case (some(match_ident(?a_id))) { a_id.node }
|
|
|
|
case (some(?m)) { match_error(cx, m, "an identifier") }
|
|
|
|
case (none) { i }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn transcribe_path(&ext_ctxt cx, &bindings b, @mutable vec[uint] idx_path,
|
|
|
|
&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; }
|
|
|
|
ret alt (follow_for_trans(cx, b.find(p.idents.(0)), idx_path)) {
|
|
|
|
case (some(match_ident(?id))) {
|
|
|
|
rec(global=false, idents=~[id.node], types=~[])
|
|
|
|
}
|
|
|
|
case (some(match_path(?a_pth))) { a_pth.node }
|
|
|
|
case (some(?m)) { match_error(cx, m, "a path") }
|
|
|
|
case (none) { p }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn transcribe_expr(&ext_ctxt cx, &bindings b, @mutable vec[uint] idx_path,
|
|
|
|
&ast::expr_ e, ast_fold fld,
|
|
|
|
fn(&ast::expr_, ast_fold) -> ast::expr_ orig)
|
2011-07-06 15:22:23 -07:00
|
|
|
-> ast::expr_ {
|
2011-06-20 17:26:17 -07:00
|
|
|
ret alt(e) {
|
2011-07-21 16:47:47 -07:00
|
|
|
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 (follow_for_trans(cx, b.find(p.node.idents.(0)), idx_path)) {
|
|
|
|
case (some(match_ident(?id))) {
|
|
|
|
expr_path(respan(id.span,
|
|
|
|
rec(global=false,
|
|
|
|
idents=~[id.node],types=~[])))
|
|
|
|
}
|
|
|
|
case (some(match_path(?a_pth))) { expr_path(a_pth) }
|
|
|
|
case (some(match_expr(?a_exp))) { a_exp.node }
|
|
|
|
case (some(?m)) { match_error(cx, m, "an expression")}
|
|
|
|
case (none) { orig(e,fld) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { orig(e,fld) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-26 10:53:14 -07:00
|
|
|
fn transcribe_type(&ext_ctxt cx, &bindings b, @mutable vec[uint] idx_path,
|
|
|
|
&ast::ty_ t, ast_fold fld,
|
|
|
|
fn(&ast::ty_, ast_fold) -> ast::ty_ orig) -> ast::ty_ {
|
|
|
|
ret alt(t) {
|
|
|
|
case (ast::ty_path(?pth,_)) {
|
|
|
|
alt (path_to_ident(pth)) {
|
|
|
|
case (some(?id)) {
|
|
|
|
alt (follow_for_trans(cx, b.find(id), idx_path)) {
|
|
|
|
case (some(match_ty(?ty))) { ty.node }
|
|
|
|
case (some(?m)) { match_error(cx, m, "a type") }
|
|
|
|
case (none) { orig(t, fld) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (none) { orig(t, fld) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { orig(t, fld) }
|
|
|
|
}
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
|
|
|
|
|
2011-07-26 10:53:14 -07:00
|
|
|
/* for parsing reasons, syntax variables bound to blocks must be used like
|
|
|
|
`{v}` */
|
|
|
|
|
|
|
|
fn transcribe_block(&ext_ctxt cx, &bindings b, @mutable vec[uint] idx_path,
|
|
|
|
&blk_ blk, ast_fold fld,
|
|
|
|
fn(&blk_, ast_fold) -> blk_ orig) -> blk_ {
|
|
|
|
ret alt (block_to_ident(blk)) {
|
|
|
|
case (some(?id)) {
|
|
|
|
alt (follow_for_trans(cx, b.find(id), idx_path)) {
|
|
|
|
case (some(match_block(?new_blk))) { new_blk.node }
|
|
|
|
// possibly allow promotion of ident/path/expr to blocks?
|
|
|
|
case (some(?m)) { match_error(cx, m, "a block")}
|
|
|
|
case (none) { orig(blk, fld) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (none) { orig(blk, fld) }
|
|
|
|
}
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
|
|
|
|
|
|
|
|
/* traverse the pattern, building instructions on how to bind the actual
|
|
|
|
argument. ps accumulates instructions on navigating the tree.*/
|
|
|
|
fn p_t_s_rec(&ext_ctxt cx, &matchable m, &selector s, &binders b) {
|
|
|
|
//it might be possible to traverse only exprs, not matchables
|
|
|
|
alt (m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
alt (e.node) {
|
|
|
|
case (expr_path(?p_pth)) {
|
|
|
|
p_t_s_r_path(cx,p_pth, s, b);
|
|
|
|
}
|
|
|
|
case (expr_vec(?p_elts, _, _)) {
|
|
|
|
alt (elts_to_ell(cx, p_elts)) {
|
|
|
|
case (some(?repeat_me)) {
|
|
|
|
p_t_s_r_ellipses(cx, repeat_me, s, b);
|
|
|
|
}
|
|
|
|
case (none) {
|
|
|
|
p_t_s_r_actual_vector(cx, p_elts, s, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* TODO: handle embedded types and blocks, at least */
|
2011-07-26 10:53:14 -07:00
|
|
|
case (expr_mac(?mac)) {
|
|
|
|
p_t_s_r_mac(cx, mac, s, b);
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
case (_) {
|
|
|
|
fn select(&ext_ctxt cx, &matchable m, @expr pat)
|
|
|
|
-> match_result {
|
|
|
|
ret alt(m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
if (e==pat) { some(leaf(match_exact)) } else { none }
|
|
|
|
}
|
2011-07-26 10:53:14 -07:00
|
|
|
case (_) { cx.bug("broken traversal in p_t_s_r"); fail }
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
b.literal_ast_matchers += ~[bind select(cx,_,e)];
|
|
|
|
}
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-06 15:22:23 -07:00
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
/* make a match more precise */
|
|
|
|
fn specialize_match(&matchable m) -> matchable {
|
|
|
|
ret alt (m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
alt (e.node) {
|
|
|
|
case (expr_path(?pth)) {
|
|
|
|
alt (path_to_ident(pth)) {
|
2011-07-26 10:53:14 -07:00
|
|
|
case (some(?id)) { match_ident(respan(pth.span,id)) }
|
|
|
|
case (none) { match_path(pth) }
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { m }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { m }
|
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
|
|
|
|
2011-07-26 10:53:14 -07:00
|
|
|
/* pattern_to_selectors helper functions */
|
2011-07-21 16:47:47 -07:00
|
|
|
fn p_t_s_r_path(&ext_ctxt cx, &path p, &selector s, &binders b) {
|
|
|
|
alt (path_to_ident(p)) {
|
|
|
|
case (some(?p_id)) {
|
|
|
|
fn select(&ext_ctxt cx, &matchable m) -> match_result {
|
|
|
|
ret alt (m) {
|
|
|
|
case (match_expr(?e)) { some(leaf(specialize_match(m))) }
|
|
|
|
case (_) { cx.bug("broken traversal in p_t_s_r"); fail }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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,_)));
|
|
|
|
}
|
|
|
|
case (none) { }
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
|
|
|
}
|
2011-06-20 17:26:17 -07:00
|
|
|
|
2011-07-26 10:53:14 -07:00
|
|
|
fn block_to_ident(&blk_ blk) -> option::t[ident] {
|
|
|
|
if(ivec::len(blk.stmts) != 0u) { ret none; }
|
|
|
|
ret alt (blk.expr) {
|
|
|
|
case (some(?expr)) {
|
|
|
|
alt (expr.node) {
|
|
|
|
case (expr_path(?pth)) { path_to_ident(pth) }
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case(none) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn p_t_s_r_mac(&ext_ctxt cx, &ast::mac mac, &selector s, &binders b) {
|
|
|
|
fn select_pt_1(&ext_ctxt cx, &matchable m, fn(&ast::mac) ->
|
|
|
|
match_result fn_m) -> match_result {
|
|
|
|
ret alt(m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
alt(e.node) {
|
|
|
|
case (expr_mac(?mac)) { fn_m(mac) }
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { cx.bug("broken traversal in p_t_s_r"); fail }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn no_des(&ext_ctxt cx, &span sp, &str syn) -> ! {
|
|
|
|
cx.span_fatal(sp, "destructuring "+syn+" is not yet supported");
|
|
|
|
}
|
|
|
|
alt (mac.node) {
|
|
|
|
case (ast::mac_ellipsis) { cx.span_fatal(mac.span, "misused `...`"); }
|
|
|
|
case (ast::mac_invoc(_,_, _)) { no_des(cx, mac.span, "macro calls"); }
|
|
|
|
case (ast::mac_embed_type(?ty)) {
|
|
|
|
alt (ty.node) {
|
|
|
|
case ast::ty_path(?pth, _) {
|
|
|
|
alt (path_to_ident(pth)) {
|
|
|
|
case (some(?id)) {
|
|
|
|
/* look for an embedded type */
|
|
|
|
fn select_pt_2(&ast::mac m) -> match_result {
|
|
|
|
ret alt (m.node) {
|
|
|
|
case (ast::mac_embed_type(?t)) {
|
|
|
|
some(leaf(match_ty(t)))
|
|
|
|
}
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.real_binders.insert(id,
|
|
|
|
bind select_pt_1(cx, _, select_pt_2));
|
|
|
|
}
|
|
|
|
case (none) { no_des(cx, pth.span, "under `#<>`"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { no_des(cx, ty.span, "under `#<>`"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (ast::mac_embed_block(?blk)) {
|
|
|
|
alt (block_to_ident(blk.node)) {
|
|
|
|
case (some(?id)) {
|
|
|
|
fn select_pt_2(&ast::mac m) -> match_result {
|
|
|
|
ret alt (m.node) {
|
|
|
|
case (ast::mac_embed_block(?blk)) {
|
|
|
|
some(leaf(match_block(blk)))
|
|
|
|
}
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.real_binders.insert(id, bind select_pt_1(cx, _, select_pt_2));
|
|
|
|
}
|
|
|
|
case (none) { no_des(cx, blk.span, "under `#{}`"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
/* TODO: move this to vec.rs */
|
|
|
|
|
|
|
|
fn ivec_to_vec[T](&(T)[] v) -> vec[T] {
|
|
|
|
let vec[T] rs = vec::alloc[T](ivec::len(v));
|
|
|
|
for (T ve in v) { rs += [ve]; }
|
|
|
|
ret rs;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn p_t_s_r_ellipses(&ext_ctxt cx, @expr repeat_me, &selector s, &binders b) {
|
|
|
|
fn select(&ext_ctxt cx, @expr repeat_me, &matchable m) -> match_result {
|
|
|
|
ret alt (m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
alt (e.node) {
|
|
|
|
case (expr_vec(?arg_elts, _, _)) {
|
|
|
|
auto elts = ivec::map(leaf, ivec::map(match_expr,
|
|
|
|
arg_elts));
|
|
|
|
// using repeat_me.span is a little wacky, but the
|
|
|
|
// error we want to report is one in the macro def
|
|
|
|
some(seq(ivec_to_vec(elts), repeat_me.span))
|
|
|
|
}
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { cx.bug("broken traversal in p_t_s_r"); fail }
|
2011-07-08 16:35:09 -07:00
|
|
|
}
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
p_t_s_rec(cx, match_expr(repeat_me),
|
|
|
|
compose_sels(s, bind select(cx, repeat_me, _)), b);
|
2011-07-08 16:35:09 -07:00
|
|
|
}
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
fn p_t_s_r_actual_vector(&ext_ctxt cx, (@expr)[] elts, &selector s,
|
|
|
|
&binders b) {
|
|
|
|
fn len_select(&ext_ctxt cx, &matchable m, uint len) -> match_result {
|
|
|
|
ret alt (m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
alt (e.node) {
|
|
|
|
case (expr_vec(?arg_elts, _, _)) {
|
|
|
|
if (ivec::len(arg_elts) == len) { some(leaf(match_exact)) }
|
|
|
|
else { none }
|
|
|
|
}
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.literal_ast_matchers +=
|
|
|
|
~[compose_sels(s, bind len_select(cx, _, ivec::len(elts)))];
|
|
|
|
|
|
|
|
|
|
|
|
let uint idx = 0u;
|
|
|
|
while (idx < ivec::len(elts)) {
|
|
|
|
fn select(&ext_ctxt cx, &matchable m, uint idx) -> match_result {
|
|
|
|
ret alt (m) {
|
|
|
|
case (match_expr(?e)) {
|
|
|
|
alt (e.node) {
|
|
|
|
case (expr_vec(?arg_elts, _, _)) {
|
|
|
|
some(leaf(match_expr(arg_elts.(idx))))
|
|
|
|
}
|
|
|
|
case (_) { none }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { cx.bug("broken traversal in p_t_s_r"); fail}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p_t_s_rec(cx, match_expr(elts.(idx)),
|
|
|
|
compose_sels(s, bind select(cx, _, idx)), b);
|
|
|
|
idx += 1u;
|
|
|
|
}
|
|
|
|
}
|
2011-07-08 16:35:09 -07:00
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
fn add_new_extension(&ext_ctxt cx, span sp, &(@expr)[] args,
|
2011-07-26 14:06:02 +02:00
|
|
|
option::t[str] body) -> base::macro_def {
|
2011-07-06 15:22:23 -07:00
|
|
|
let option::t[str] macro_name = none;
|
2011-07-21 16:47:47 -07:00
|
|
|
let (clause)[] clauses = ~[];
|
|
|
|
for (@expr arg in args) {
|
2011-07-06 15:22:23 -07:00
|
|
|
alt(arg.node) {
|
2011-07-21 16:47:47 -07:00
|
|
|
case(expr_vec(?elts, ?mut, ?seq_kind)) {
|
|
|
|
if (ivec::len(elts) != 2u) {
|
|
|
|
cx.span_fatal((*arg).span,
|
|
|
|
"extension clause must consist of [" +
|
|
|
|
"macro invocation, expansion body]");
|
|
|
|
}
|
2011-07-13 15:44:09 -07:00
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
alt(elts.(0u).node) {
|
|
|
|
case(expr_mac(?mac)) {
|
|
|
|
alt (mac.node) {
|
|
|
|
case (mac_invoc(?pth, ?invoc_args, ?body)) {
|
|
|
|
alt (path_to_ident(pth)) {
|
|
|
|
case (some(?id)) { macro_name=some(id); }
|
|
|
|
case (none) {
|
|
|
|
cx.span_fatal(pth.span, "macro name "
|
|
|
|
+ "must not be a path");
|
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
auto bdrses = ~[];
|
|
|
|
for(@expr arg in invoc_args) {
|
|
|
|
bdrses +=
|
|
|
|
~[pattern_to_selectors(cx, arg)];
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
clauses +=
|
|
|
|
~[rec(params=bdrses, body=elts.(1u))];
|
|
|
|
// FIXME: check duplicates (or just simplify
|
|
|
|
// the macro arg situation)
|
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
case(_) {
|
|
|
|
cx.span_fatal(elts.(0u).span, "extension clause must"
|
|
|
|
+ " start with a macro invocation.");
|
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
case(_) {
|
|
|
|
cx.span_fatal((*arg).span, "extension must be [clause, "
|
|
|
|
+ " ...]");
|
|
|
|
}
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
auto ext = bind generic_extension(_,_,_,_,clauses);
|
2011-07-13 15:44:09 -07:00
|
|
|
|
2011-07-26 14:06:02 +02:00
|
|
|
ret rec(ident=alt (macro_name) {
|
2011-07-21 16:47:47 -07:00
|
|
|
case (some(?id)) { id }
|
|
|
|
case (none) {
|
|
|
|
cx.span_fatal(sp, "macro definition must have "
|
|
|
|
+ "at least one clause")
|
|
|
|
}
|
2011-07-26 14:06:02 +02:00
|
|
|
}, ext=normal(ext));
|
2011-07-06 15:22:23 -07:00
|
|
|
|
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
fn generic_extension(&ext_ctxt cx, span sp, &(@expr)[] args,
|
|
|
|
option::t[str] body, (clause)[] clauses)
|
|
|
|
-> @expr {
|
2011-06-20 17:26:17 -07:00
|
|
|
|
2011-07-21 16:47:47 -07:00
|
|
|
|
|
|
|
for (clause c in clauses) {
|
|
|
|
if (ivec::len(args) != ivec::len(c.params)) { cont; }
|
2011-07-06 15:22:23 -07:00
|
|
|
let uint i = 0u;
|
2011-07-21 16:47:47 -07:00
|
|
|
let bindings bdgs = new_str_hash[arb_depth[matchable]]();
|
|
|
|
let bool abort = false;
|
2011-07-08 16:35:09 -07:00
|
|
|
while (i < ivec::len(args)) {
|
2011-07-21 16:47:47 -07:00
|
|
|
alt (use_selectors_to_bind(c.params.(i), args.(i))) {
|
|
|
|
case (some(?new_bindings)) {
|
|
|
|
/* ick; I wish macros just took one expr */
|
2011-07-26 14:06:02 +02:00
|
|
|
for each (@rec(ident key, arb_depth[matchable] val) it
|
2011-07-21 16:47:47 -07:00
|
|
|
in new_bindings.items()) {
|
2011-07-26 14:06:02 +02:00
|
|
|
bdgs.insert(it.key, it.val);
|
2011-07-21 16:47:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case (none) { abort = true; }
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
|
|
|
i += 1u;
|
|
|
|
}
|
2011-07-21 16:47:47 -07:00
|
|
|
if (abort) { cont; }
|
|
|
|
ret transcribe(cx, bdgs, c.body);
|
2011-07-06 15:22:23 -07:00
|
|
|
}
|
|
|
|
cx.span_fatal(sp, "no clauses match macro invocation");
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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:
|
|
|
|
//
|