rust/src/comp/syntax/ext/simplext.rs

781 lines
25 KiB
Rust
Raw Normal View History

use std;
import codemap::span;
import std::ivec;
import std::option;
2011-07-21 16:47:47 -07:00
import std::map::hashmap;
import std::map::new_str_hash;
import option::some;
import option::none;
import base::syntax_extension;
import base::ext_ctxt;
import base::normal;
import base::expr_to_str;
import base::expr_to_ident;
import fold::*;
import ast::node_id;
import ast::respan;
import ast::ident;
import ast::path;
2011-07-21 16:47:47 -07:00
import ast::ty;
import ast::blk;
import ast::blk_;
2011-07-21 16:47:47 -07:00
import ast::expr;
import ast::expr_;
import ast::path_;
import ast::expr_path;
import ast::expr_vec;
import ast::expr_mac;
import ast::mac_invoc;
export add_new_extension;
2011-07-27 14:19:39 +02:00
fn path_to_ident(pth: &path) -> option::t[ident] {
if ivec::len(pth.node.idents) == 1u && ivec::len(pth.node.types) == 0u {
2011-07-21 16:47:47 -07:00
ret some(pth.node.idents.(0u));
}
ret none;
}
//an ivec of binders might be a little big.
type clause = {params: binders, body: @expr};
2011-07-21 16:47:47 -07:00
/* logically, an arb_depth should contain only one kind of matchable */
2011-08-11 15:27:18 -07:00
tag arb_depth[T] { leaf(T); seq(@[arb_depth[T]], span); }
2011-07-21 16:47:47 -07:00
tag matchable {
match_expr(@expr);
match_path(path);
match_ident(ast::spanned[ident]);
match_ty(@ty);
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 */
2011-07-27 14:19:39 +02:00
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);
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
match_path(x) {
cx.span_fatal(x.span,
"this argument is a path, expected " + expected);
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
match_ident(x) {
cx.span_fatal(x.span,
"this argument is an ident, expected " + expected);
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
match_ty(x) {
cx.span_fatal(x.span,
"this argument is a type, expected " + expected);
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
match_block(x) {
cx.span_fatal(x.span,
"this argument is a block, expected " + expected);
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
match_exact. { cx.bug("what is a match_exact doing in a bindings?"); }
2011-07-21 16:47:47 -07:00
}
}
// 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]];
2011-07-27 14:19:39 +02:00
type selector = fn(&matchable) -> match_result ;
fn elts_to_ell(cx: &ext_ctxt, elts: &[@expr])
-> {pre: [@expr], rep: option::t[@expr], post: [@expr]} {
2011-07-27 14:19:39 +02:00
let idx: uint = 0u;
let res = none;
for elt: @expr in elts {
2011-07-27 14:19:39 +02:00
alt elt.node {
expr_mac(m) {
alt m.node {
ast::mac_ellipsis. {
if res != none {
cx.span_fatal(m.span, "only one ellipsis allowed");
2011-07-21 16:47:47 -07:00
}
res = some({pre: ivec::slice(elts, 0u, idx - 1u),
rep: some(elts.(idx - 1u)),
post: ivec::slice(elts, idx + 1u,
ivec::len(elts))});
2011-07-21 16:47:47 -07:00
}
_ { }
2011-07-21 16:47:47 -07:00
}
}
2011-07-27 14:19:39 +02:00
_ { }
}
2011-07-21 16:47:47 -07:00
idx += 1u;
}
ret alt res {
some(val) { val }
none. { {pre: elts, rep: none, post: ~[]} }
}
}
2011-08-11 15:27:18 -07:00
fn option_flatten_map[T, U](f: &fn(&T) -> option::t[U] , v: &[T]) ->
option::t[[U]] {
let res = ~[];
for elem: T in v {
2011-08-11 15:27:18 -07:00
alt f(elem) { none. { ret none; } some(fv) { res += ~[fv]; } }
2011-07-21 16:47:47 -07:00
}
ret some(res);
}
2011-07-27 14:19:39 +02:00
fn a_d_map(ad: &arb_depth[matchable], f: &selector) -> match_result {
alt ad {
leaf(x) { ret f(x); }
seq(ads, span) {
2011-08-11 15:27:18 -07:00
alt option_flatten_map(bind a_d_map(_, f), *ads) {
2011-07-27 14:19:39 +02:00
none. { ret none; }
2011-08-11 15:27:18 -07:00
some(ts) { ret some(seq(@ts, span)); }
}
2011-07-21 16:47:47 -07:00
}
}
}
2011-07-27 14:19:39 +02:00
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) }
}
}
2011-07-21 16:47:47 -07:00
ret bind scomp(s1, s2, _);
}
2011-07-21 16:47:47 -07:00
2011-07-27 14:19:39 +02:00
type binders =
{real_binders: hashmap[ident, selector],
mutable literal_ast_matchers: [selector]};
2011-07-21 16:47:47 -07:00
type bindings = hashmap[ident, arb_depth[matchable]];
2011-07-27 14:19:39 +02:00
fn acumm_bindings(cx: &ext_ctxt, b_dest: &bindings, b_src: &bindings) { }
2011-07-21 16:47:47 -07:00
/* these three functions are the big moving parts */
/* create the selectors needed to bind and verify the pattern */
2011-07-27 14:19:39 +02:00
fn pattern_to_selectors(cx: &ext_ctxt, e: @expr) -> binders {
let res: binders =
{real_binders: new_str_hash[selector](),
mutable literal_ast_matchers: ~[]};
2011-07-21 16:47:47 -07:00
//this oughta return binders instead, but macro args are a sequence of
//expressions, rather than a single expression
2011-07-27 14:19:39 +02:00
fn trivial_selector(m: &matchable) -> match_result { ret some(leaf(m)); }
2011-07-21 16:47:47 -07:00
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. */
2011-07-27 14:19:39 +02:00
fn use_selectors_to_bind(b: &binders, e: @expr) -> option::t[bindings] {
let res = new_str_hash[arb_depth[matchable]]();
//need to do this first, to check vec lengths.
for sel: selector in b.literal_ast_matchers {
alt sel(match_expr(e)) { none. { ret none; } _ { } }
}
2011-07-27 14:19:39 +02:00
let never_mind: bool = false;
for each pair: @{key: ident, val: selector} in b.real_binders.items() {
2011-07-27 14:19:39 +02:00
alt pair.val(match_expr(e)) {
none. { never_mind = true; }
some(mtc) { res.insert(pair.key, mtc); }
2011-07-21 16:47:47 -07:00
}
}
//HACK: `ret` doesn't work in `for each`
if never_mind { ret none; }
2011-07-21 16:47:47 -07:00
ret some(res);
}
/* use the bindings on the body to generate the expanded code */
2011-07-27 14:19:39 +02:00
fn transcribe(cx: &ext_ctxt, b: &bindings, body: @expr) -> @expr {
2011-08-11 15:27:18 -07:00
let idx_path: @mutable [uint] = @mutable ~[];
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, expanded_from: cx.backtrace()};
}
2011-07-27 14:19:39 +02:00
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),
new_span: bind new_span(cx, _) with *afp};
2011-07-27 14:19:39 +02:00
let f = make_fold(f_pre);
let result = f.fold_expr(body);
dummy_out(f); //temporary: kill circular reference
2011-07-21 16:47:47 -07:00
ret result;
}
/* helper: descend into a matcher */
2011-08-11 15:27:18 -07:00
fn follow(m: &arb_depth[matchable], idx_path: @mutable [uint]) ->
2011-07-27 14:19:39 +02:00
arb_depth[matchable] {
let res: arb_depth[matchable] = m;
for idx: uint in *idx_path {
2011-07-27 14:19:39 +02:00
alt res {
leaf(_) { ret res;/* end of the line */ }
seq(new_ms, _) { res = new_ms.(idx); }
2011-07-21 16:47:47 -07:00
}
}
ret res;
}
2011-07-27 14:19:39 +02:00
fn follow_for_trans(cx: &ext_ctxt, mmaybe: &option::t[arb_depth[matchable]],
2011-08-11 15:27:18 -07:00
idx_path: @mutable [uint]) -> option::t[matchable] {
2011-07-27 14:19:39 +02:00
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) }
}
2011-07-21 16:47:47 -07:00
}
}
}
/* helper for transcribe_exprs: what vars from `b` occur in `e`? */
2011-07-27 14:19:39 +02:00
iter free_vars(b: &bindings, e: @expr) -> ident {
let idents: hashmap[ident, ()] = new_str_hash[()]();
fn mark_ident(i: &ident, fld: ast_fold, b: &bindings,
idents: &hashmap[ident, ()]) -> ident {
if b.contains_key(i) { idents.insert(i, ()); }
2011-07-21 16:47:47 -07:00
ret i;
}
// using fold is a hack: we want visit, but it doesn't hit idents ) :
// solve this with macros
2011-07-27 14:19:39 +02:00
let f_pre =
{fold_ident: bind mark_ident(_, _, b, idents)
with *default_ast_fold()};
let f = make_fold(f_pre);
2011-07-21 16:47:47 -07:00
f.fold_expr(e); // ignore result
dummy_out(f);
for each id: ident 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 */
2011-08-11 15:27:18 -07:00
fn transcribe_exprs(cx: &ext_ctxt, b: &bindings, idx_path: @mutable [uint],
recur: fn(&@expr) -> @expr , exprs: [@expr])
-> [@expr] {
2011-07-27 14:19:39 +02:00
alt elts_to_ell(cx, exprs) {
{pre: pre, rep: repeat_me_maybe, post: post} {
let res = ivec::map(recur, pre);
alt repeat_me_maybe {
none. {}
some(repeat_me) {
let repeat: option::t[{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 */
for each fv: ident in free_vars(b, repeat_me) {
let cur_pos = follow(b.get(fv), idx_path);
alt cur_pos {
leaf(_) { }
seq(ms, _) {
alt repeat {
none. {
2011-08-11 15:27:18 -07:00
repeat = some({rep_count: ivec::len(*ms), name: fv});
}
some({rep_count: old_len, name: old_name}) {
2011-08-11 15:27:18 -07:00
let len = ivec::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);
}
}
2011-07-21 16:47:47 -07:00
}
}
}
}
alt repeat {
none. {
cx.span_fatal(repeat_me.span,
"'...' surrounds an expression without any" +
2011-07-27 14:19:39 +02:00
" repeating syntax variables");
}
some({rep_count: rc, _}) {
/* Whew, we now know how how many times to repeat */
let idx: uint = 0u;
while idx < rc {
2011-08-11 15:27:18 -07:00
*idx_path += ~[idx];
res += ~[recur(repeat_me)]; // whew!
2011-08-11 15:27:18 -07:00
ivec::pop(*idx_path);
idx += 1u;
}
}
2011-07-21 16:47:47 -07:00
}
}
}
res += ivec::map(recur, post);
2011-07-21 16:47:47 -07:00
ret res;
}
}
}
2011-07-21 16:47:47 -07:00
// substitute, in a position that's required to be an ident
2011-08-11 15:27:18 -07:00
fn transcribe_ident(cx: &ext_ctxt, b: &bindings, idx_path: @mutable [uint],
2011-07-27 14:19:39 +02:00
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 }
}
2011-07-21 16:47:47 -07:00
}
2011-08-11 15:27:18 -07:00
fn transcribe_path(cx: &ext_ctxt, b: &bindings, idx_path: @mutable [uint],
2011-07-27 14:19:39 +02:00
p: &path_, fld: ast_fold) -> path_ {
2011-07-21 16:47:47 -07:00
// Don't substitute into qualified names.
2011-07-27 14:19:39 +02:00
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) {
some(match_ident(id)) {
{global: false, idents: ~[id.node], types: ~[]}
}
some(match_path(a_pth)) { a_pth.node }
some(m) { match_error(cx, m, "a path") }
none. { p }
}
2011-07-21 16:47:47 -07:00
}
2011-08-11 15:27:18 -07:00
fn transcribe_expr(cx: &ext_ctxt, b: &bindings, idx_path: @mutable [uint],
2011-07-27 14:19:39 +02:00
e: &ast::expr_, fld: ast_fold,
orig: fn(&ast::expr_, ast_fold) -> ast::expr_ ) ->
ast::expr_ {
ret alt e {
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) {
some(match_ident(id)) {
expr_path(respan(id.span,
{global: false,
idents: ~[id.node],
types: ~[]}))
}
some(match_path(a_pth)) { expr_path(a_pth) }
some(match_expr(a_exp)) { a_exp.node }
some(m) { match_error(cx, m, "an expression") }
none. { orig(e, fld) }
}
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
_ { orig(e, fld) }
2011-07-21 16:47:47 -07:00
}
}
2011-08-11 15:27:18 -07:00
fn transcribe_type(cx: &ext_ctxt, b: &bindings, idx_path: @mutable [uint],
2011-07-27 14:19:39 +02:00
t: &ast::ty_, fld: ast_fold,
orig: fn(&ast::ty_, ast_fold) -> ast::ty_ ) -> ast::ty_ {
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 }
some(m) { match_error(cx, m, "a type") }
none. { orig(t, fld) }
}
}
none. { orig(t, fld) }
}
}
2011-07-27 14:19:39 +02:00
_ { orig(t, fld) }
}
}
2011-07-21 16:47:47 -07:00
/* for parsing reasons, syntax variables bound to blocks must be used like
`{v}` */
2011-08-11 15:27:18 -07:00
fn transcribe_block(cx: &ext_ctxt, b: &bindings, idx_path: @mutable [uint],
2011-07-27 14:19:39 +02:00
blk: &blk_, fld: ast_fold,
orig: fn(&blk_, ast_fold) -> blk_ ) -> blk_ {
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 }
// possibly allow promotion of ident/path/expr to blocks?
some(m) {
match_error(cx, m, "a block")
}
none. { orig(blk, fld) }
}
}
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.*/
2011-07-27 14:19:39 +02:00
fn p_t_s_rec(cx: &ext_ctxt, m: &matchable, s: &selector, b: &binders) {
2011-07-21 16:47:47 -07:00
//it might be possible to traverse only exprs, not matchables
2011-07-27 14:19:39 +02:00
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, ivec::len(pre) + ivec::len(post),
true, s, b);
if(ivec::len(pre) > 0u) {
p_t_s_r_actual_vector(cx, pre, true, s, b);
}
p_t_s_r_ellipses(cx, repeat_me, ivec::len(pre), s, b);
if(ivec::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, ivec::len(pre), false, s, b);
p_t_s_r_actual_vector(cx, pre, false, s, b);
}
2011-07-21 16:47:47 -07:00
}
}
2011-07-27 14:19:39 +02:00
2011-07-21 16:47:47 -07:00
/* TODO: handle embedded types and blocks, at least */
2011-07-27 14:19:39 +02:00
expr_mac(mac) {
p_t_s_r_mac(cx, mac, s, b);
}
2011-07-27 14:19:39 +02:00
_ {
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 }
}
2011-07-29 20:51:18 +02:00
_ { cx.bug("broken traversal in p_t_s_r") }
2011-07-27 14:19:39 +02:00
}
}
2011-07-27 14:19:39 +02:00
b.literal_ast_matchers += ~[bind select(cx, _, e)];
2011-07-21 16:47:47 -07:00
}
}
2011-07-21 16:47:47 -07:00
}
}
}
2011-07-21 16:47:47 -07:00
/* make a match more precise */
2011-07-27 14:19:39 +02:00
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 }
2011-07-21 16:47:47 -07:00
}
}
2011-07-27 14:19:39 +02:00
_ { m }
2011-07-21 16:47:47 -07:00
}
}
/* pattern_to_selectors helper functions */
2011-07-27 14:19:39 +02:00
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))) }
2011-07-29 20:51:18 +02:00
_ { cx.bug("broken traversal in p_t_s_r") }
2011-07-27 14:19:39 +02:00
}
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
if b.real_binders.contains_key(p_id) {
2011-07-21 16:47:47 -07:00
cx.span_fatal(p.span, "duplicate binding identifier");
}
2011-07-27 14:19:39 +02:00
b.real_binders.insert(p_id, compose_sels(s, bind select(cx, _)));
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
none. { }
}
}
2011-07-27 14:19:39 +02:00
fn block_to_ident(blk: &blk_) -> option::t[ident] {
if ivec::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 }
}
}
2011-07-27 14:19:39 +02:00
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 } }
}
2011-07-29 20:51:18 +02:00
_ { cx.bug("broken traversal in p_t_s_r") }
}
}
2011-07-27 14:19:39 +02:00
fn no_des(cx: &ext_ctxt, sp: &span, syn: &str) -> ! {
cx.span_fatal(sp, "destructuring " + syn + " is not yet supported");
}
2011-07-27 14:19:39 +02:00
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 */
2011-07-27 14:19:39 +02:00
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));
}
2011-07-27 14:19:39 +02:00
none. { no_des(cx, pth.span, "under `#<>`"); }
}
}
2011-07-27 14:19:39 +02:00
_ { no_des(cx, ty.span, "under `#<>`"); }
}
}
2011-07-27 14:19:39 +02:00
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));
}
2011-07-27 14:19:39 +02:00
none. { no_des(cx, blk.span, "under `#{}`"); }
}
}
}
}
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 {
2011-07-27 14:19:39 +02:00
ret alt m {
match_expr(e) {
alt e.node {
expr_vec(arg_elts, _, _) {
let elts = ~[];
let idx = offset;
while idx < ivec::len(arg_elts) {
elts += ~[leaf(match_expr(arg_elts.(idx)))];
idx += 1u;
}
2011-07-27 14:19:39 +02:00
// 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))
2011-07-27 14:19:39 +02:00
}
_ { none }
}
2011-07-21 16:47:47 -07:00
}
2011-07-29 20:51:18 +02:00
_ { cx.bug("broken traversal in p_t_s_r") }
2011-07-21 16:47:47 -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, 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 {
2011-07-27 14:19:39 +02:00
ret alt m {
match_expr(e) {
alt e.node {
expr_vec(arg_elts, _, _) {
let actual_len = ivec::len(arg_elts);
if (at_least && actual_len >= len) || actual_len == len {
2011-07-27 14:19:39 +02:00
some(leaf(match_exact))
} else { none }
}
_ { none }
}
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
_ { none }
2011-07-21 16:47:47 -07:00
}
}
b.literal_ast_matchers +=
~[compose_sels(s, bind len_select(cx, _, at_least, len))];
}
2011-07-21 16:47:47 -07:00
fn p_t_s_r_actual_vector(cx: &ext_ctxt, elts: [@expr], repeat_after: bool,
s: &selector, b: &binders) {
2011-07-27 14:19:39 +02:00
let idx: uint = 0u;
while idx < ivec::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 }
}
2011-07-21 16:47:47 -07:00
}
2011-07-29 20:51:18 +02:00
_ { cx.bug("broken traversal in p_t_s_r") }
2011-07-21 16:47:47 -07:00
}
}
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: @expr,
2011-07-27 14:19:39 +02:00
body: option::t[str]) -> base::macro_def {
let args: [@ast::expr] = alt arg.node {
ast::expr_vec(elts, _, _) { elts }
_ {
cx.span_fatal(sp, "#macro requires arguments of the form `[...]`.")
}
};
2011-07-27 14:19:39 +02:00
let macro_name: option::t[str] = none;
let clauses: [@clause] = ~[];
for arg: @expr in args {
2011-07-27 14:19:39 +02:00
alt arg.node {
expr_vec(elts, mut, seq_kind) {
if ivec::len(elts) != 2u {
2011-07-21 16:47:47 -07:00
cx.span_fatal((*arg).span,
"extension clause must consist of [" +
2011-07-27 14:19:39 +02:00
"macro invocation, expansion body]");
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
alt elts.(0u).node {
expr_mac(mac) {
alt mac.node {
mac_invoc(pth, invoc_arg, body) {
2011-07-27 14:19:39 +02:00
alt path_to_ident(pth) {
some(id) {
2011-08-04 11:58:09 -07:00
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");
}
}
}
}
2011-07-27 14:19:39 +02:00
none. {
cx.span_fatal(pth.span,
"macro name must not be a path");
2011-07-21 16:47:47 -07:00
}
}
clauses +=
~[@{params: pattern_to_selectors(cx, invoc_arg),
body: elts.(1u)}];
2011-07-21 16:47:47 -07:00
// FIXME: check duplicates (or just simplify
// the macro arg situation)
}
}
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
_ {
cx.span_fatal(elts.(0u).span,
"extension clause must" +
" start with a macro invocation.");
2011-07-21 16:47:47 -07:00
}
}
2011-07-21 16:47:47 -07:00
}
2011-07-27 14:19:39 +02:00
_ {
cx.span_fatal((*arg).span,
"extension must be [clause, " + " ...]");
2011-07-21 16:47:47 -07:00
}
}
}
2011-07-27 14:19:39 +02:00
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(ext)};
fn generic_extension(cx: &ext_ctxt, sp: span, arg: @expr,
body: option::t[str], clauses: [@clause]) -> @expr {
for c: @clause in clauses {
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
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//