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;
|
|
|
|
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::*;
|
|
|
|
import ast::ident;
|
|
|
|
import ast::path_;
|
|
|
|
import ast::expr_path;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
ret none[uint];
|
|
|
|
}
|
|
|
|
|
|
|
|
// substitute, in a position that's required to be an ident
|
2011-07-06 19:00:00 -07:00
|
|
|
fn subst_ident(&ext_ctxt cx, &(@ast::expr)[] args,
|
2011-06-20 17:26:17 -07:00
|
|
|
@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.");
|
|
|
|
}
|
|
|
|
case (none[uint]) {
|
|
|
|
ret i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-06 19:00:00 -07:00
|
|
|
fn subst_path(&ext_ctxt cx, &(@ast::expr)[] args,
|
2011-06-20 17:26:17 -07:00
|
|
|
@vec[ident] param_names, &path_ p, ast_fold fld) -> path_ {
|
|
|
|
// Don't substitute into qualified names.
|
2011-07-05 16:23:07 -07:00
|
|
|
if (ivec::len(p.types) > 0u || ivec::len(p.idents) != 1u) { ret p; }
|
2011-06-20 17:26:17 -07:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (none[uint]) { ret p; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-06 19:00:00 -07:00
|
|
|
fn subst_expr(&ext_ctxt cx, &(@ast::expr)[] args, @vec[ident] param_names,
|
2011-06-20 17:26:17 -07:00
|
|
|
&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.
|
2011-07-05 16:23:07 -07:00
|
|
|
if (ivec::len(p.node.types) > 0u ||
|
|
|
|
ivec::len(p.node.idents) != 1u) { e }
|
2011-06-20 17:26:17 -07:00
|
|
|
alt (position(p.node.idents.(0), *param_names)) {
|
|
|
|
case (some[uint](?idx)) {
|
|
|
|
args.(idx).node
|
|
|
|
}
|
|
|
|
case (none[uint]) { e }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { orig(e,fld) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-06 19:00:00 -07:00
|
|
|
fn add_new_extension(&ext_ctxt cx, span sp, &(@ast::expr)[] args,
|
2011-06-20 17:26:17 -07:00
|
|
|
option::t[str] body) -> tup(str, syntax_extension) {
|
2011-07-06 19:00:00 -07:00
|
|
|
if (ivec::len(args) < 2u) {
|
2011-06-20 17:26:17 -07:00
|
|
|
cx.span_fatal(sp, "malformed extension description");
|
|
|
|
}
|
|
|
|
|
2011-07-06 19:00:00 -07:00
|
|
|
fn generic_extension(&ext_ctxt cx, span sp, &(@ast::expr)[] args,
|
2011-06-20 17:26:17 -07:00
|
|
|
option::t[str] body, @vec[ident] param_names,
|
|
|
|
@ast::expr dest_form) -> @ast::expr {
|
2011-07-06 19:00:00 -07:00
|
|
|
if (ivec::len(args) != len(*param_names)) {
|
2011-06-20 17:26:17 -07:00
|
|
|
cx.span_fatal(sp, #fmt("extension expects %u arguments, got %u",
|
2011-07-06 19:00:00 -07:00
|
|
|
len(*param_names), ivec::len(args)));
|
2011-06-20 17:26:17 -07:00
|
|
|
}
|
|
|
|
|
2011-07-06 19:00:00 -07:00
|
|
|
// FIXME: This binds to alias arguments.
|
2011-06-20 17:26:17 -07:00
|
|
|
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)
|
|
|
|
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;
|
2011-07-06 19:00:00 -07:00
|
|
|
while(1u+idx < ivec::len(args)) {
|
2011-06-20 17:26:17 -07:00
|
|
|
param_names +=
|
|
|
|
[expr_to_ident(cx, args.(idx),
|
|
|
|
"this parameter name must be an identifier.")];
|
|
|
|
idx += 1u;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret tup(expr_to_str(cx, args.(0), "first arg must be a literal string."),
|
|
|
|
normal(bind generic_extension(_,_,_,_,@param_names,
|
2011-07-06 19:00:00 -07:00
|
|
|
args.(ivec::len(args)-1u))));
|
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:
|
|
|
|
//
|