Move everything syntax-related to syntax/, break deps on rest of compiler

src/comp/syntax is currently just a sub-module of rustc, but it will,
in the near future, be its own crate. This includes:

 - The AST data structure
 - The parser
 - The pretty-printer
 - Visit, walk, and fold
 - The syntax extension system
 - Some utility stuff that should be in the stdlib*

*) Stdlib extensions currently require a snapshot before they can be
   used, and the win build is very broken right now. This is temporary
   and will be cleaned up when one of those problems goes away.

A lot of code was moved by this patch, mostly towards a more organized
layout. Some package paths did get longer, and I guess the new layout
will take some getting used to. Sorry about that!

Please try not to re-introduce any dependencies in syntax/ on any of
the other src/comp/ subdirs.
This commit is contained in:
Marijn Haverbeke 2011-07-05 11:48:19 +02:00
parent c59ebf0f01
commit 6fd6fdea93
54 changed files with 1254 additions and 1203 deletions

29
src/comp/syntax/_std.rs Normal file
View file

@ -0,0 +1,29 @@
// FIXME all this stuff should be in the standard lib, and in fact is,
// but due to the way our snapshots currently work, rustc can't use it
// until after the next snapshot.
fn new_str_hash[V]() -> std::map::hashmap[str, V] {
let std::map::hashfn[str] hasher = std::str::hash;
let std::map::eqfn[str] eqer = std::str::eq;
ret std::map::mk_hashmap[str, V](hasher, eqer);
}
fn new_int_hash[V]() -> std::map::hashmap[int, V] {
fn hash_int(&int x) -> uint { ret x as uint; }
fn eq_int(&int a, &int b) -> bool { ret a == b; }
auto hasher = hash_int;
auto eqer = eq_int;
ret std::map::mk_hashmap[int, V](hasher, eqer);
}
fn new_uint_hash[V]() -> std::map::hashmap[uint, V] {
fn hash_uint(&uint x) -> uint { ret x; }
fn eq_uint(&uint a, &uint b) -> bool { ret a == b; }
auto hasher = hash_uint;
auto eqer = eq_uint;
ret std::map::mk_hashmap[uint, V](hasher, eqer);
}
fn istr(int i) -> str { ret std::int::to_str(i, 10u); }
fn uistr(uint i) -> str { ret std::uint::to_str(i, 10u); }

654
src/comp/syntax/ast.rs Normal file
View file

@ -0,0 +1,654 @@
import std::option;
import std::str;
import std::vec;
import codemap::span;
import codemap::filename;
type spanned[T] = rec(T node, span span);
fn respan[T](&span sp, &T t) -> spanned[T] { ret rec(node=t, span=sp); }
type ident = str;
// Functions may or may not have names.
type fn_ident = option::t[ident];
// FIXME: with typestate constraint, could say
// idents and types are the same length, and are
// non-empty
type path_ = rec(vec[ident] idents, vec[@ty] types);
type path = spanned[path_];
fn path_name(&path p) -> str { ret str::connect(p.node.idents, "::"); }
type crate_num = int;
type node_id = int;
type def_id = tup(crate_num, node_id);
const crate_num local_crate = 0;
fn local_def(node_id id) -> def_id {
ret tup(local_crate, id);
}
type ty_param = ident;
tag def {
def_fn(def_id, purity);
def_obj_field(def_id);
def_mod(def_id);
def_native_mod(def_id);
def_const(def_id);
def_arg(def_id);
def_local(def_id);
def_variant(def_id, /* tag */def_id);
/* variant */
def_ty(def_id);
def_ty_arg(uint);
def_binding(def_id);
def_use(def_id);
def_native_ty(def_id);
def_native_fn(def_id);
}
fn variant_def_ids(&def d) -> tup(def_id, def_id) {
alt (d) {
case (def_variant(?tag_id, ?var_id)) { ret tup(tag_id, var_id); }
}
}
fn def_id_of_def(def d) -> def_id {
alt (d) {
case (def_fn(?id,_)) { ret id; }
case (def_obj_field(?id)) { ret id; }
case (def_mod(?id)) { ret id; }
case (def_native_mod(?id)) { ret id; }
case (def_const(?id)) { ret id; }
case (def_arg(?id)) { ret id; }
case (def_local(?id)) { ret id; }
case (def_variant(_, ?id)) { ret id; }
case (def_ty(?id)) { ret id; }
case (def_ty_arg(_)) { fail; }
case (def_binding(?id)) { ret id; }
case (def_use(?id)) { ret id; }
case (def_native_ty(?id)) { ret id; }
case (def_native_fn(?id)) { ret id; }
}
fail;
}
// The set of meta_items that define the compilation environment of the crate,
// used to drive conditional compilation
type crate_cfg = vec[@meta_item];
type crate = spanned[crate_];
type crate_ = rec(vec[@crate_directive] directives,
_mod module,
vec[attribute] attrs,
crate_cfg config);
tag crate_directive_ {
cdir_src_mod(ident, option::t[filename], vec[attribute]);
cdir_dir_mod(ident, option::t[filename],
vec[@crate_directive], vec[attribute]);
cdir_view_item(@view_item);
cdir_syntax(path);
cdir_auth(path, _auth);
}
type crate_directive = spanned[crate_directive_];
type meta_item = spanned[meta_item_];
tag meta_item_ {
meta_word(ident);
meta_list(ident, vec[@meta_item]);
meta_name_value(ident, str);
}
type block = spanned[block_];
type block_ = rec(vec[@stmt] stmts, option::t[@expr] expr, node_id id);
type pat = rec(node_id id,
pat_ node,
span span);
tag pat_ {
pat_wild;
pat_bind(ident);
pat_lit(@lit);
pat_tag(path, vec[@pat]);
}
tag mutability { mut; imm; maybe_mut; }
tag layer { layer_value; layer_state; layer_gc; }
tag _auth { auth_unsafe; }
tag proto { proto_iter; proto_fn; }
tag binop {
add;
sub;
mul;
div;
rem;
and;
or;
bitxor;
bitand;
bitor;
lsl;
lsr;
asr;
eq;
lt;
le;
ne;
ge;
gt;
}
fn binop_to_str(binop op) -> str {
alt (op) {
case (add) { ret "+"; }
case (sub) { ret "-"; }
case (mul) { ret "*"; }
case (div) { ret "/"; }
case (rem) { ret "%"; }
case (and) { ret "&&"; }
case (or) { ret "||"; }
case (bitxor) { ret "^"; }
case (bitand) { ret "&"; }
case (bitor) { ret "|"; }
case (lsl) { ret "<<"; }
case (lsr) { ret ">>"; }
case (asr) { ret ">>>"; }
case (eq) { ret "=="; }
case (lt) { ret "<"; }
case (le) { ret "<="; }
case (ne) { ret "!="; }
case (ge) { ret ">="; }
case (gt) { ret ">"; }
}
}
pred lazy_binop(binop b) -> bool {
alt (b) {
case (and) { true }
case (or) { true }
case (_) { false }
}
}
tag unop { box(mutability); deref; not; neg; }
fn unop_to_str(unop op) -> str {
alt (op) {
case (box(?mt)) { if (mt == mut) { ret "@mutable "; } ret "@"; }
case (deref) { ret "*"; }
case (not) { ret "!"; }
case (neg) { ret "-"; }
}
}
tag mode { val; alias(bool); }
type stmt = spanned[stmt_];
tag stmt_ {
stmt_decl(@decl, node_id);
stmt_expr(@expr, node_id);
// These only exist in crate-level blocks.
stmt_crate_directive(@crate_directive);
}
tag init_op { init_assign; init_recv; init_move; }
type initializer = rec(init_op op, @expr expr);
type local_ =
rec(option::t[@ty] ty,
bool infer,
ident ident,
option::t[initializer] init,
node_id id);
type local = spanned[local_];
type decl = spanned[decl_];
tag decl_ { decl_local(@local); decl_item(@item); }
type arm = rec(@pat pat, block block);
type elt = rec(mutability mut, @expr expr);
type field_ = rec(mutability mut, ident ident, @expr expr);
type field = spanned[field_];
tag spawn_dom { dom_implicit; dom_thread; }
tag check_mode { checked; unchecked; }
// FIXME: temporary
tag seq_kind { sk_unique; sk_rc; }
type expr = rec(node_id id,
expr_ node,
span span);
tag expr_ {
expr_vec(vec[@expr], mutability, seq_kind);
expr_tup(vec[elt]);
expr_rec(vec[field], option::t[@expr]);
expr_call(@expr, vec[@expr]);
expr_self_method(ident);
expr_bind(@expr, vec[option::t[@expr]]);
expr_spawn(spawn_dom, option::t[str], @expr, vec[@expr]);
expr_binary(binop, @expr, @expr);
expr_unary(unop, @expr);
expr_lit(@lit);
expr_cast(@expr, @ty);
expr_if(@expr, block, option::t[@expr]);
expr_ternary(@expr, @expr, @expr);
expr_while(@expr, block);
expr_for(@local, @expr, block);
expr_for_each(@local, @expr, block);
expr_do_while(block, @expr);
expr_alt(@expr, vec[arm]);
expr_fn(_fn);
expr_block(block);
/*
* FIXME: many of these @exprs should be constrained with
* is_lval once we have constrained types working.
*/
expr_move(@expr, @expr);
expr_assign(@expr,@expr);
expr_swap(@expr, @expr);
expr_assign_op(binop, @expr, @expr);
expr_send(@expr, @expr);
expr_recv(@expr, @expr);
expr_field(@expr, ident);
expr_index(@expr, @expr);
expr_path(path);
expr_ext(path, vec[@expr], option::t[str], @expr);
expr_fail(option::t[@expr]);
expr_break;
expr_cont;
expr_ret(option::t[@expr]);
expr_put(option::t[@expr]);
expr_be(@expr);
expr_log(int, @expr);
/* just an assert, no significance to typestate */
expr_assert(@expr);
/* preds that typestate is aware of */
expr_check(check_mode, @expr);
/* FIXME Would be nice if expr_check desugared
to expr_if_check. */
expr_if_check(@expr, block, option::t[@expr]);
expr_port(option::t[@ty]);
expr_chan(@expr);
expr_anon_obj(anon_obj, vec[ty_param], obj_def_ids);
}
type lit = spanned[lit_];
tag lit_ {
lit_str(str, seq_kind);
lit_char(char);
lit_int(int);
lit_uint(uint);
lit_mach_int(ty_mach, int);
lit_float(str);
lit_mach_float(ty_mach, str);
lit_nil;
lit_bool(bool);
}
fn is_path(&@expr e) -> bool {
ret alt (e.node) {
case (expr_path(_)) { true }
case (_) { false }
};
}
// NB: If you change this, you'll probably want to change the corresponding
// type structure in middle/ty.rs as well.
type mt = rec(@ty ty, mutability mut);
type ty_field_ = rec(ident ident, mt mt);
type ty_arg_ = rec(mode mode, @ty ty);
type ty_method_ =
rec(proto proto,
ident ident,
vec[ty_arg] inputs,
@ty output,
controlflow cf,
vec[@constr] constrs);
type ty_field = spanned[ty_field_];
type ty_arg = spanned[ty_arg_];
type ty_method = spanned[ty_method_];
tag ty_mach {
ty_i8;
ty_i16;
ty_i32;
ty_i64;
ty_u8;
ty_u16;
ty_u32;
ty_u64;
ty_f32;
ty_f64;
}
fn ty_mach_to_str(ty_mach tm) -> str {
alt (tm) {
case (ty_u8) { ret "u8"; }
case (ty_u16) { ret "u16"; }
case (ty_u32) { ret "u32"; }
case (ty_u64) { ret "u64"; }
case (ty_i8) { ret "i8"; }
case (ty_i16) { ret "i16"; }
case (ty_i32) { ret "i32"; }
case (ty_i64) { ret "i64"; }
case (ty_f32) { ret "f32"; }
case (ty_f64) { ret "f64"; }
}
}
type ty = spanned[ty_];
tag ty_ {
ty_nil;
ty_bot; /* return type of ! functions and type of
ret/fail/break/cont. there is no syntax
for this type. */
/* bot represents the value of functions that don't return a value
locally to their context. in contrast, things like log that do
return, but don't return a meaningful value, have result type nil. */
ty_bool;
ty_int;
ty_uint;
ty_float;
ty_machine(ty_mach);
ty_char;
ty_str;
ty_istr; // interior string
ty_box(mt);
ty_vec(mt);
ty_ivec(mt); // interior vector
ty_ptr(mt);
ty_task;
ty_port(@ty);
ty_chan(@ty);
ty_tup(vec[mt]);
ty_rec(vec[ty_field]);
ty_fn(proto, vec[ty_arg], @ty, controlflow, vec[@constr]);
ty_obj(vec[ty_method]);
ty_path(path, node_id);
ty_type;
ty_constr(@ty, vec[@constr]);
}
/*
A constraint arg that's a function argument is referred to by its position
rather than name. This is so we could have higher-order functions that have
constraints (potentially -- right now there's no way to write that), and also
so that the typestate pass doesn't have to map a function name onto its decl.
So, the constr_arg type is parameterized: it's instantiated with uint for
declarations, and ident for uses.
*/
tag constr_arg_general_[T] { carg_base; carg_ident(T); carg_lit(@lit); }
type constr_arg = constr_arg_general[uint];
type constr_arg_general[T] = spanned[constr_arg_general_[T]];
type constr_ = rec(path path,
vec[@constr_arg_general[uint]] args,
node_id id);
type constr = spanned[constr_];
/* The parser generates ast::constrs; resolve generates
a mapping from each function to a list of ty::constr_defs,
corresponding to these. */
type arg = rec(mode mode, @ty ty, ident ident, node_id id);
type fn_decl =
rec(vec[arg] inputs,
@ty output,
purity purity,
controlflow cf,
vec[@constr] constraints);
tag purity {
pure_fn; // declared with "pred"
impure_fn; // declared with "fn"
}
tag controlflow {
noreturn; // functions with return type _|_ that always
// raise an error or exit (i.e. never return to the caller)
return; // everything else
}
type _fn = rec(fn_decl decl, proto proto, block body);
type method_ = rec(ident ident, _fn meth, node_id id);
type method = spanned[method_];
type obj_field = rec(mutability mut, @ty ty, ident ident, node_id id);
type anon_obj_field = rec(mutability mut, @ty ty, @expr expr, ident ident,
node_id id);
type _obj =
rec(vec[obj_field] fields, vec[@method] methods, option::t[@method] dtor);
type anon_obj =
rec(
// New fields and methods, if they exist.
option::t[vec[anon_obj_field]] fields,
vec[@method] methods,
// with_obj: the original object being extended, if it exists.
option::t[@expr] with_obj);
type _mod = rec(vec[@view_item] view_items, vec[@item] items);
tag native_abi {
native_abi_rust;
native_abi_cdecl;
native_abi_llvm;
native_abi_rust_intrinsic;
}
type native_mod =
rec(str native_name,
native_abi abi,
vec[@view_item] view_items,
vec[@native_item] items);
type variant_arg = rec(@ty ty, node_id id);
type variant_ = rec(str name, vec[variant_arg] args, node_id id);
type variant = spanned[variant_];
type view_item = spanned[view_item_];
tag view_item_ {
view_item_use(ident, vec[@meta_item], node_id);
view_item_import(ident, vec[ident], node_id);
view_item_import_glob(vec[ident], node_id);
view_item_export(ident, node_id);
}
type obj_def_ids = rec(node_id ty, node_id ctor);
// Meta-data associated with an item
type attribute = spanned[attribute_];
// Distinguishes between attributes that decorate items and attributes that
// are contained as statements within items. These two cases need to be
// distinguished for pretty-printing.
tag attr_style { attr_outer; attr_inner; }
type attribute_ = rec(attr_style style, meta_item value);
type item = rec(ident ident,
vec[attribute] attrs,
node_id id, // For objs and resources, this is the type def_id
item_ node,
span span);
tag item_ {
item_const(@ty, @expr);
item_fn(_fn, vec[ty_param]);
item_mod(_mod);
item_native_mod(native_mod);
item_ty(@ty, vec[ty_param]);
item_tag(vec[variant], vec[ty_param]);
item_obj(_obj, vec[ty_param], node_id /* constructor id */);
item_res(_fn /* dtor */, node_id /* dtor id */,
vec[ty_param], node_id /* ctor id */);
}
type native_item = rec(ident ident,
native_item_ node,
node_id id,
span span);
tag native_item_ {
native_item_ty;
native_item_fn(option::t[str], fn_decl, vec[ty_param]);
}
fn is_exported(ident i, _mod m) -> bool {
auto nonlocal = true;
for (@ast::item it in m.items) {
if (it.ident == i) { nonlocal = false; }
alt (it.node) {
case (item_tag(?variants, _)) {
for (variant v in variants) {
if (v.node.name == i) { nonlocal = false; }
}
}
case (_) { }
}
if (!nonlocal) { break; }
}
auto count = 0u;
for (@ast::view_item vi in m.view_items) {
alt (vi.node) {
case (ast::view_item_export(?id, _)) {
if (str::eq(i, id)) {
// even if it's nonlocal (since it's explicit)
ret true;
}
count += 1u;
}
case (_) {/* fall through */ }
}
}
// If there are no declared exports then
// everything not imported is exported
ret count == 0u && !nonlocal;
}
fn is_call_expr(@expr e) -> bool {
alt (e.node) {
case (expr_call(_, _)) { ret true; }
case (_) { ret false; }
}
}
fn is_constraint_arg(@expr e) -> bool {
alt (e.node) {
case (expr_lit(_)) { ret true; }
case (expr_path(_)) { ret true; }
case (_) { ret false; }
}
}
fn eq_ty(&@ty a, &@ty b) -> bool { ret std::box::ptr_eq(a, b); }
fn hash_ty(&@ty t) -> uint { ret t.span.lo << 16u + t.span.hi; }
fn block_from_expr(@expr e) -> block {
let block_ blk_ =
rec(stmts=[],
expr=option::some[@expr](e),
id=e.id);
ret rec(node=blk_, span=e.span);
}
// This is a convenience function to transfor ternary expressions to if
// expressions so that they can be treated the same
fn ternary_to_if(&@expr e) -> @ast::expr {
alt (e.node) {
case (expr_ternary(?cond, ?then, ?els)) {
auto then_blk = block_from_expr(then);
auto els_blk = block_from_expr(els);
auto els_expr = @rec(id=els.id, node=expr_block(els_blk),
span=els.span);
ret @rec(id=e.id,
node=expr_if(cond, then_blk, option::some(els_expr)),
span=e.span);
}
case (_) { fail; }
}
}
// Path stringification
fn path_to_str(&ast::path pth) -> str {
auto result = str::connect(pth.node.idents, "::");
if (vec::len[@ast::ty](pth.node.types) > 0u) {
fn f(&@ast::ty t) -> str { ret print::pprust::ty_to_str(*t); }
result += "[";
result += str::connect(vec::map(f, pth.node.types), ",");
result += "]";
}
ret result;
}
//
// 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:
//

View file

@ -0,0 +1,96 @@
import std::vec;
import std::term;
import std::io;
import std::option;
import std::option::some;
import std::option::none;
type filename = str;
/* A codemap is a thing that maps uints to file/line/column positions
* in a crate. This to make it possible to represent the positions
* with single-word things, rather than passing records all over the
* compiler.
*/
type filemap = @rec(filename name, uint start_pos, mutable vec[uint] lines);
type codemap = @rec(mutable vec[filemap] files);
type loc = rec(filename filename, uint line, uint col);
fn new_codemap() -> codemap {
let vec[filemap] files = [];
ret @rec(mutable files=files);
}
fn new_filemap(filename filename, uint start_pos) -> filemap {
ret @rec(name=filename, start_pos=start_pos, mutable lines=[0u]);
}
fn next_line(filemap file, uint pos) { vec::push[uint](file.lines, pos); }
fn lookup_pos(codemap map, uint pos) -> loc {
auto a = 0u;
auto b = vec::len[filemap](map.files);
while (b - a > 1u) {
auto m = (a + b) / 2u;
if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
}
auto f = map.files.(a);
a = 0u;
b = vec::len[uint](f.lines);
while (b - a > 1u) {
auto m = (a + b) / 2u;
if (f.lines.(m) > pos) { b = m; } else { a = m; }
}
ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}
type span = rec(uint lo, uint hi);
fn span_to_str(&span sp, &codemap cm) -> str {
auto lo = lookup_pos(cm, sp.lo);
auto hi = lookup_pos(cm, sp.hi);
ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}
fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
&codemap cm) {
auto ss = "<input>:0:0:0:0";
alt (sp) {
case (some(?ssp)) { ss = span_to_str(ssp, cm); }
case (none) { }
}
io::stdout().write_str(ss + ": ");
if (term::color_supported()) {
term::fg(io::stdout().get_buf_writer(), color);
}
io::stdout().write_str(#fmt("%s:", kind));
if (term::color_supported()) {
term::reset(io::stdout().get_buf_writer());
}
io::stdout().write_str(#fmt(" %s\n", msg));
}
fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
emit_diagnostic(sp, msg, "note", 10u8, cm);
}
//
// 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:
//

View file

@ -0,0 +1,98 @@
import std::vec;
import std::option;
import std::map::hashmap;
import parse::parser::parse_sess;
import codemap::span;
import syntax::_std::new_str_hash;
import codemap;
type syntax_expander =
fn(&ext_ctxt, span, &vec[@ast::expr], option::t[str]) -> @ast::expr;
type macro_definer = fn(&ext_ctxt, span, &vec[@ast::expr],
option::t[str]) -> tup(str, syntax_extension);
tag syntax_extension {
normal(syntax_expander);
macro_defining(macro_definer);
}
// A temporary hard-coded map of methods for expanding syntax extension
// AST nodes into full ASTs
fn syntax_expander_table() -> hashmap[str, syntax_extension] {
auto syntax_expanders = new_str_hash[syntax_extension]();
syntax_expanders.insert("fmt", normal(ext::fmt::expand_syntax_ext));
syntax_expanders.insert("env", normal(ext::env::expand_syntax_ext));
syntax_expanders.insert("macro",
macro_defining(ext::simplext::add_new_extension));
ret syntax_expanders;
}
type span_msg_fn = fn(span, str) -> ! ;
type next_id_fn = fn() -> ast::node_id ;
// Provides a limited set of services necessary for syntax extensions
// to do their thing
type ext_ctxt =
rec(span_msg_fn span_fatal,
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);
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);
fail;
}
auto ext_span_unimpl = bind ext_span_unimpl_(sess.cm, _, _);
auto ext_next_id = bind parse::parser::next_node_id(sess);
ret rec(span_fatal=ext_span_fatal,
span_unimpl=ext_span_unimpl,
next_id=ext_next_id);
}
fn expr_to_str(&ext_ctxt cx, @ast::expr expr, str error) -> str {
alt (expr.node) {
case (ast::expr_lit(?l)) {
alt (l.node) {
case (ast::lit_str(?s, _)) { ret s; }
case (_) { cx.span_fatal(l.span, error); }
}
}
case (_) { cx.span_fatal(expr.span, error); }
}
}
fn expr_to_ident(&ext_ctxt cx, @ast::expr expr, str error) -> ast::ident {
alt(expr.node) {
case (ast::expr_path(?p)) {
if (vec::len(p.node.types) > 0u
|| vec::len(p.node.idents) != 1u) {
cx.span_fatal(expr.span, error);
} else {
ret p.node.idents.(0);
}
}
case (_) {
cx.span_fatal(expr.span, error);
}
}
}
//
// 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:
//

View file

@ -0,0 +1,47 @@
/*
* The compiler code necessary to support the #env extension. Eventually this
* should all get sucked into either the compiler syntax extension plugin
* interface.
*/
import std::str;
import std::vec;
import std::option;
import std::generic_os;
import base::*;
export expand_syntax_ext;
fn expand_syntax_ext(&ext_ctxt cx, codemap::span sp, &vec[@ast::expr] args,
option::t[str] body) -> @ast::expr {
if (vec::len[@ast::expr](args) != 1u) {
cx.span_fatal(sp, "malformed #env call");
}
// FIXME: if this was more thorough it would manufacture an
// option::t[str] rather than just an maybe-empty string.
auto var = expr_to_str(cx, args.(0), "#env requires a string");
alt (generic_os::getenv(var)) {
case (option::none) { ret make_new_str(cx, sp, ""); }
case (option::some(?s)) { ret make_new_str(cx, sp, s); }
}
}
fn make_new_lit(&ext_ctxt cx, codemap::span sp, ast::lit_ lit) -> @ast::expr {
auto sp_lit = @rec(node=lit, span=sp);
ret @rec(id=cx.next_id(), node=ast::expr_lit(sp_lit), span=sp);
}
fn make_new_str(&ext_ctxt cx, codemap::span sp, str s) -> @ast::expr {
ret make_new_lit(cx, sp, ast::lit_str(s, ast::sk_rc));
}
//
// 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:
//

372
src/comp/syntax/ext/fmt.rs Normal file
View file

@ -0,0 +1,372 @@
/*
* 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 std::str;
import std::vec;
import std::option;
import std::option::none;
import std::option::some;
import std::extfmt::ct::*;
import base::*;
import codemap::span;
export expand_syntax_ext;
fn expand_syntax_ext(&ext_ctxt cx, span sp, &vec[@ast::expr] args,
option::t[str] body) -> @ast::expr {
if (vec::len[@ast::expr](args) == 0u) {
cx.span_fatal(sp, "#fmt requires a format string");
}
auto fmt = expr_to_str(cx, args.(0), "first argument to #fmt must be a "
+ "string literal.");
auto fmtspan = args.(0).span;
log "Format string:";
log fmt;
fn parse_fmt_err_(&ext_ctxt cx, span sp, str msg) -> ! {
cx.span_fatal(sp, msg);
}
auto parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _);
auto 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
fn pieces_to_expr(&ext_ctxt cx, span sp, vec[piece] pieces,
vec[@ast::expr] args) -> @ast::expr {
fn make_new_lit(&ext_ctxt cx, span sp, ast::lit_ lit) ->
@ast::expr {
auto sp_lit = @rec(node=lit, span=sp);
ret @rec(id=cx.next_id(), node=ast::expr_lit(sp_lit), span=sp);
}
fn make_new_str(&ext_ctxt cx, span sp, str s) -> @ast::expr {
auto lit = ast::lit_str(s, ast::sk_rc);
ret make_new_lit(cx, sp, lit);
}
fn make_new_int(&ext_ctxt cx, span sp, int i) -> @ast::expr {
auto lit = ast::lit_int(i);
ret make_new_lit(cx, sp, lit);
}
fn make_new_uint(&ext_ctxt cx, span sp, uint u) -> @ast::expr {
auto lit = ast::lit_uint(u);
ret make_new_lit(cx, sp, lit);
}
fn make_add_expr(&ext_ctxt cx, span sp, @ast::expr lhs,
@ast::expr rhs) -> @ast::expr {
auto binexpr = ast::expr_binary(ast::add, lhs, rhs);
ret @rec(id=cx.next_id(), node=binexpr, span=sp);
}
fn make_path_expr(&ext_ctxt cx, span sp, vec[ast::ident] idents)
-> @ast::expr {
let vec[@ast::ty] types = [];
auto path = rec(idents=idents, types=types);
auto sp_path = rec(node=path, span=sp);
auto pathexpr = ast::expr_path(sp_path);
ret @rec(id=cx.next_id(), node=pathexpr, span=sp);
}
fn make_vec_expr(&ext_ctxt cx, span sp, vec[@ast::expr] exprs) ->
@ast::expr {
auto vecexpr = ast::expr_vec(exprs, ast::imm, ast::sk_rc);
ret @rec(id=cx.next_id(), node=vecexpr, span=sp);
}
fn make_call(&ext_ctxt cx, span sp, vec[ast::ident] fn_path,
vec[@ast::expr] args) -> @ast::expr {
auto pathexpr = make_path_expr(cx, sp, fn_path);
auto callexpr = ast::expr_call(pathexpr, args);
ret @rec(id=cx.next_id(), node=callexpr, span=sp);
}
fn make_rec_expr(&ext_ctxt cx, span sp,
vec[tup(ast::ident, @ast::expr)] fields) -> @ast::expr {
let vec[ast::field] astfields = [];
for (tup(ast::ident, @ast::expr) field in fields) {
auto ident = field._0;
auto val = field._1;
auto astfield =
rec(node=rec(mut=ast::imm, ident=ident, expr=val), span=sp);
astfields += [astfield];
}
auto recexpr = ast::expr_rec(astfields, option::none[@ast::expr]);
ret @rec(id=cx.next_id(), node=recexpr, span=sp);
}
fn make_path_vec(str ident) -> vec[str] {
// FIXME: #fmt can't currently be used from within std
// because we're explicitly referencing the 'std' crate here
ret ["std", "extfmt", "rt", ident];
}
fn make_rt_path_expr(&ext_ctxt cx, span sp, str ident) ->
@ast::expr {
auto path = make_path_vec(ident);
ret make_path_expr(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(&ext_ctxt cx, span sp, &conv cnv) ->
@ast::expr {
fn make_flags(&ext_ctxt cx, span sp, vec[flag] flags) ->
@ast::expr {
let vec[@ast::expr] flagexprs = [];
for (flag f in flags) {
auto fstr;
alt (f) {
case (flag_left_justify) { fstr = "flag_left_justify"; }
case (flag_left_zero_pad) { fstr = "flag_left_zero_pad"; }
case (flag_space_for_sign) {
fstr = "flag_space_for_sign";
}
case (flag_sign_always) { fstr = "flag_sign_always"; }
case (flag_alternate) { fstr = "flag_alternate"; }
}
flagexprs += [make_rt_path_expr(cx, sp, fstr)];
}
// FIXME: 0-length vectors can't have their type inferred
// through the rec that these flags are a member of, so
// this is a hack placeholder flag
if (vec::len[@ast::expr](flagexprs) == 0u) {
flagexprs += [make_rt_path_expr(cx, sp, "flag_none")];
}
ret make_vec_expr(cx, sp, flagexprs);
}
fn make_count(&ext_ctxt cx, span sp, &count cnt) ->
@ast::expr {
alt (cnt) {
case (count_implied) {
ret make_rt_path_expr(cx, sp, "count_implied");
}
case (count_is(?c)) {
auto count_lit = make_new_int(cx, sp, c);
auto count_is_path = make_path_vec("count_is");
auto count_is_args = [count_lit];
ret make_call(cx, sp, count_is_path, count_is_args);
}
case (_) {
cx.span_unimpl(sp, "unimplemented #fmt conversion");
}
}
}
fn make_ty(&ext_ctxt cx, span sp, &ty t) -> @ast::expr {
auto rt_type;
alt (t) {
case (ty_hex(?c)) {
alt (c) {
case (case_upper) { rt_type = "ty_hex_upper"; }
case (case_lower) { rt_type = "ty_hex_lower"; }
}
}
case (ty_bits) { rt_type = "ty_bits"; }
case (ty_octal) { rt_type = "ty_octal"; }
case (_) { rt_type = "ty_default"; }
}
ret make_rt_path_expr(cx, sp, rt_type);
}
fn make_conv_rec(&ext_ctxt cx, span sp, @ast::expr flags_expr,
@ast::expr width_expr, @ast::expr precision_expr,
@ast::expr ty_expr) -> @ast::expr {
ret make_rec_expr(cx, sp,
[tup("flags", flags_expr),
tup("width", width_expr),
tup("precision", precision_expr),
tup("ty", ty_expr)]);
}
auto rt_conv_flags = make_flags(cx, sp, cnv.flags);
auto rt_conv_width = make_count(cx, sp, cnv.width);
auto rt_conv_precision = make_count(cx, sp, cnv.precision);
auto 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(&ext_ctxt cx, span sp, str conv_type, &conv cnv,
@ast::expr arg) -> @ast::expr {
auto fname = "conv_" + conv_type;
auto path = make_path_vec(fname);
auto cnv_expr = make_rt_conv_expr(cx, sp, cnv);
auto args = [cnv_expr, arg];
ret make_call(cx, arg.span, path, args);
}
fn make_new_conv(&ext_ctxt cx, span sp, conv cnv, @ast::expr arg)
-> @ast::expr {
// FIXME: Extract all this validation into extfmt::ct
fn is_signed_type(conv cnv) -> bool {
alt (cnv.ty) {
case (ty_int(?s)) {
alt (s) {
case (signed) { ret true; }
case (unsigned) { ret false; }
}
}
case (_) { ret false; }
}
}
auto unsupported = "conversion not supported in #fmt string";
alt (cnv.param) {
case (option::none) { }
case (_) { cx.span_unimpl(sp, unsupported); }
}
for (flag f in cnv.flags) {
alt (f) {
case (flag_left_justify) { }
case (flag_sign_always) {
if (!is_signed_type(cnv)) {
cx.span_fatal(sp,
"+ flag only valid in " +
"signed #fmt conversion");
}
}
case (flag_space_for_sign) {
if (!is_signed_type(cnv)) {
cx.span_fatal(sp,
"space flag only valid in " +
"signed #fmt conversions");
}
}
case (flag_left_zero_pad) { }
case (_) { cx.span_unimpl(sp, unsupported); }
}
}
alt (cnv.width) {
case (count_implied) { }
case (count_is(_)) { }
case (_) { cx.span_unimpl(sp, unsupported); }
}
alt (cnv.precision) {
case (count_implied) { }
case (count_is(_)) { }
case (_) { cx.span_unimpl(sp, unsupported); }
}
alt (cnv.ty) {
case (ty_str) {
ret make_conv_call(cx, arg.span, "str", cnv, arg);
}
case (ty_int(?sign)) {
alt (sign) {
case (signed) {
ret make_conv_call(cx, arg.span, "int", cnv, arg);
}
case (unsigned) {
ret make_conv_call(cx, arg.span, "uint", cnv, arg);
}
}
}
case (ty_bool) {
ret make_conv_call(cx, arg.span, "bool", cnv, arg);
}
case (ty_char) {
ret make_conv_call(cx, arg.span, "char", cnv, arg);
}
case (ty_hex(_)) {
ret make_conv_call(cx, arg.span, "uint", cnv, arg);
}
case (ty_bits) {
ret make_conv_call(cx, arg.span, "uint", cnv, arg);
}
case (ty_octal) {
ret make_conv_call(cx, arg.span, "uint", cnv, arg);
}
case (_) { cx.span_unimpl(sp, unsupported); }
}
}
fn log_conv(conv c) {
alt (c.param) {
case (some(?p)) { log "param: " + std::int::to_str(p, 10u); }
case (_) { log "param: none"; }
}
for (flag f in c.flags) {
alt (f) {
case (flag_left_justify) { log "flag: left justify"; }
case (flag_left_zero_pad) { log "flag: left zero pad"; }
case (flag_space_for_sign) { log "flag: left space pad"; }
case (flag_sign_always) { log "flag: sign always"; }
case (flag_alternate) { log "flag: alternate"; }
}
}
alt (c.width) {
case (count_is(?i)) {
log "width: count is " + std::int::to_str(i, 10u);
}
case (count_is_param(?i)) {
log "width: count is param " + std::int::to_str(i, 10u);
}
case (count_is_next_param) { log "width: count is next param"; }
case (count_implied) { log "width: count is implied"; }
}
alt (c.precision) {
case (count_is(?i)) {
log "prec: count is " + std::int::to_str(i, 10u);
}
case (count_is_param(?i)) {
log "prec: count is param " + std::int::to_str(i, 10u);
}
case (count_is_next_param) { log "prec: count is next param"; }
case (count_implied) { log "prec: count is implied"; }
}
alt (c.ty) {
case (ty_bool) { log "type: bool"; }
case (ty_str) { log "type: str"; }
case (ty_char) { log "type: char"; }
case (ty_int(?s)) {
alt (s) {
case (signed) { log "type: signed"; }
case (unsigned) { log "type: unsigned"; }
}
}
case (ty_bits) { log "type: bits"; }
case (ty_hex(?cs)) {
alt (cs) {
case (case_upper) { log "type: uhex"; }
case (case_lower) { log "type: lhex"; }
}
}
case (ty_octal) { log "type: octal"; }
}
}
auto fmt_sp = args.(0).span;
auto n = 0u;
auto tmp_expr = make_new_str(cx, sp, "");
auto nargs = vec::len[@ast::expr](args);
for (piece pc in pieces) {
alt (pc) {
case (piece_string(?s)) {
auto s_expr = make_new_str(cx, fmt_sp, s);
tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, s_expr);
}
case (piece_conv(?conv)) {
n += 1u;
if (n >= nargs) {
cx.span_fatal(sp,
"not enough arguments to #fmt " +
"for the given format string");
}
log "Building conversion:";
log_conv(conv);
auto arg_expr = args.(n);
auto c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr);
tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, c_expr);
}
}
}
auto 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
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//

View file

@ -0,0 +1,144 @@
use std;
import codemap::span;
import std::vec;
import std::option;
import vec::map;
import vec::len;
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::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
fn subst_ident(&ext_ctxt cx, &vec[@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.");
}
case (none[uint]) {
ret i;
}
}
}
fn subst_path(&ext_ctxt cx, &vec[@ast::expr] args,
@vec[ident] param_names, &path_ p, ast_fold fld) -> path_ {
// Don't substitute into qualified names.
if (len(p.types) > 0u || 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.");
}
}
}
case (none[uint]) { ret p; }
}
}
fn subst_expr(&ext_ctxt cx, &vec[@ast::expr] args, @vec[ident] param_names,
&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 (len(p.node.types) > 0u || len(p.node.idents) != 1u) { e }
alt (position(p.node.idents.(0), *param_names)) {
case (some[uint](?idx)) {
args.(idx).node
}
case (none[uint]) { e }
}
}
case (_) { orig(e,fld) }
}
}
fn add_new_extension(&ext_ctxt cx, span sp, &vec[@ast::expr] args,
option::t[str] body) -> tup(str, syntax_extension) {
if (len(args) < 2u) {
cx.span_fatal(sp, "malformed extension description");
}
fn generic_extension(&ext_ctxt cx, span sp, &vec[@ast::expr] args,
option::t[str] body, @vec[ident] param_names,
@ast::expr dest_form) -> @ast::expr {
if (len(args) != len(*param_names)) {
cx.span_fatal(sp, #fmt("extension expects %u arguments, got %u",
len(*param_names), len(args)));
}
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;
while(1u+idx < len(args)) {
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,
args.(len(args)-1u))));
}
//
// 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:
//

708
src/comp/syntax/fold.rs Normal file
View file

@ -0,0 +1,708 @@
import syntax::codemap::span;
import ast::*;
import std::vec;
import std::option;
import vec::map;
export ast_fold_precursor;
export ast_fold;
export default_ast_fold;
export make_fold;
export dummy_out;
type ast_fold = @mutable a_f;
// We may eventually want to be able to fold over type parameters, too
type ast_fold_precursor =
rec(fn (&crate_ c, ast_fold) -> crate_ fold_crate,
fn (&crate_directive_ cd, ast_fold) -> crate_directive_
fold_crate_directive,
fn (&view_item_ i, ast_fold) -> view_item_ fold_view_item,
fn (&@native_item i, ast_fold) -> @native_item fold_native_item,
fn (&@item i, ast_fold) -> @item fold_item,
//unlike the others, item_ is non-trivial
fn (&item_ i, ast_fold) -> item_ fold_item_underscore,
fn (&method_ m, ast_fold) -> method_ fold_method,
fn (&block_ b, ast_fold) -> block_ fold_block,
fn (&stmt_ s, ast_fold) -> stmt_ fold_stmt,
fn (&arm a, ast_fold) -> arm fold_arm,
fn (&pat_ p, ast_fold) -> pat_ fold_pat,
fn (&decl_ d, ast_fold) -> decl_ fold_decl,
fn (&expr_ e, ast_fold) -> expr_ fold_expr,
fn (&ty_ t, ast_fold) -> ty_ fold_ty,
fn (&constr_ c, ast_fold) -> constr_ fold_constr,
fn (&_fn f, ast_fold) -> _fn fold_fn,
fn (&_mod m, ast_fold) -> _mod fold_mod,
fn (&native_mod, ast_fold) -> native_mod fold_native_mod,
fn (&variant_, ast_fold) -> variant_ fold_variant,
fn (&ident, ast_fold) -> ident fold_ident,
fn (&path_, ast_fold) -> path_ fold_path,
fn (&local_, ast_fold) -> local_ fold_local
);
type a_f =
rec(fn (&crate c) -> crate fold_crate,
fn (&@crate_directive cd) -> @crate_directive fold_crate_directive,
fn (&@view_item i) -> @view_item fold_view_item,
fn (&@native_item i) -> @native_item fold_native_item,
fn (&@item i) -> @item fold_item,
fn (&item_ i) -> item_ fold_item_underscore,
fn (&@method m) -> @method fold_method,
fn (&block b) -> block fold_block,
fn (&@stmt s) -> @stmt fold_stmt,
fn (&arm a) -> arm fold_arm,
fn (&@pat p) -> @pat fold_pat,
fn (&@decl d) -> @decl fold_decl,
fn (&@expr e) -> @expr fold_expr,
fn (&@ty t) -> @ty fold_ty,
fn (&@constr c) -> @constr fold_constr,
fn (&_fn f) -> _fn fold_fn,
fn (&_mod m) -> _mod fold_mod,
fn (&native_mod) -> native_mod fold_native_mod,
fn (&variant) -> variant fold_variant,
fn (&ident) -> ident fold_ident,
fn (&path) -> path fold_path,
fn (&@local) -> @local fold_local
);
//fn nf_dummy[T](&T node) -> T { fail; }
fn nf_crate_dummy(&crate c) -> crate { fail; }
fn nf_crate_directive_dummy(&@crate_directive c)
-> @crate_directive { fail; }
fn nf_view_item_dummy(&@view_item v) -> @view_item { fail; }
fn nf_native_item_dummy(&@native_item n) -> @native_item { fail; }
fn nf_item_dummy(&@item i) -> @item { fail; }
fn nf_item_underscore_dummy(&item_ i) -> item_ { fail; }
fn nf_method_dummy(&@method m) -> @method { fail; }
fn nf_block_dummy(&block b) -> block { fail; }
fn nf_stmt_dummy(&@stmt s) -> @stmt { fail; }
fn nf_arm_dummy(&arm a) -> arm { fail; }
fn nf_pat_dummy(&@pat p) -> @pat { fail; }
fn nf_decl_dummy(&@decl d) -> @decl { fail; }
fn nf_expr_dummy(&@expr e) -> @expr { fail; }
fn nf_ty_dummy(&@ty t) -> @ty { fail; }
fn nf_constr_dummy(&@constr c) -> @constr { fail; }
fn nf_fn_dummy(&_fn f) -> _fn { fail; }
fn nf_mod_dummy(&_mod m) -> _mod { fail; }
fn nf_native_mod_dummy(&native_mod n) -> native_mod { fail; }
fn nf_variant_dummy(&variant v) -> variant { fail; }
fn nf_ident_dummy(&ident i) -> ident { fail; }
fn nf_path_dummy(&path p) -> path { fail; }
fn nf_obj_field_dummy(&obj_field o) -> obj_field { fail; }
fn nf_local_dummy(&@local o) -> @local { fail; }
/* some little folds that probably aren't useful to have in ast_fold itself*/
//used in noop_fold_item and noop_fold_crate and noop_fold_crate_directive
fn fold_meta_item_(&@meta_item mi, ast_fold fld) -> @meta_item {
ret @rec(node=
alt (mi.node) {
case (meta_word(?id)) { meta_word(fld.fold_ident(id)) }
case (meta_list(?id, ?mis)) {
auto fold_meta_item = bind fold_meta_item_(_,fld);
meta_list(id, map(fold_meta_item, mis))
}
case (meta_name_value(?id,?s)) {
meta_name_value(fld.fold_ident(id),s)
}
},
span=mi.span);
}
//used in noop_fold_item and noop_fold_crate
fn fold_attribute_(&attribute at, fn(&@meta_item) -> @meta_item fmi)
-> attribute {
ret rec(node=rec(style=at.node.style,
value=*fmi(@at.node.value)),
span=at.span);
}
//used in noop_fold_native_item and noop_fold_fn
fn fold_arg_(&arg a, ast_fold fld) -> arg {
ret rec(mode=a.mode, ty=fld.fold_ty(a.ty),
ident=fld.fold_ident(a.ident), id=a.id);
}
fn noop_fold_crate(&crate_ c, ast_fold fld) -> crate_ {
auto fold_meta_item = bind fold_meta_item_(_,fld);
auto fold_attribute = bind fold_attribute_(_,fold_meta_item);
ret rec(directives=map(fld.fold_crate_directive, c.directives),
module=fld.fold_mod(c.module),
attrs=map(fold_attribute, c.attrs),
config=map(fold_meta_item, c.config));
}
fn noop_fold_crate_directive(&crate_directive_ cd, ast_fold fld)
-> crate_directive_ {
ret alt(cd) {
case(cdir_src_mod(?id,?fname,?attrs)) {
cdir_src_mod(fld.fold_ident(id), fname, attrs)
}
case(cdir_dir_mod(?id,?fname,?cds,?attrs)) {
cdir_dir_mod(fld.fold_ident(id),fname,
map(fld.fold_crate_directive, cds), attrs)
}
case(cdir_view_item(?vi)) {
cdir_view_item(fld.fold_view_item(vi))
}
case(cdir_syntax(_)) { cd }
case(cdir_auth(_,_)) { cd }
}
}
fn noop_fold_view_item(&view_item_ vi, ast_fold fld) -> view_item_ {
ret vi;
}
fn noop_fold_native_item(&@native_item ni, ast_fold fld) -> @native_item {
auto fold_arg = bind fold_arg_(_, fld);
ret @rec(ident=fld.fold_ident(ni.ident),
node=alt (ni.node) {
case (native_item_ty) { native_item_ty }
case (native_item_fn(?st, ?fdec, ?typms)) {
native_item_fn(st,
rec(inputs=map(fold_arg, fdec.inputs),
output=fld.fold_ty(fdec.output),
purity=fdec.purity, cf=fdec.cf,
constraints=map(fld.fold_constr,
fdec.constraints)),
typms)
}
},
id=ni.id,
span=ni.span);
}
fn noop_fold_item(&@item i, ast_fold fld) -> @item {
auto fold_meta_item = bind fold_meta_item_(_,fld);
auto fold_attribute = bind fold_attribute_(_,fold_meta_item);
ret @rec(ident=fld.fold_ident(i.ident),
attrs=map(fold_attribute,i.attrs),
id=i.id, node=fld.fold_item_underscore(i.node),
span=i.span);
}
fn noop_fold_item_underscore(&item_ i, ast_fold fld) -> item_ {
fn fold_obj_field_(&obj_field of, ast_fold fld) -> obj_field {
ret rec(mut=of.mut, ty=fld.fold_ty(of.ty),
ident=fld.fold_ident(of.ident), id=of.id);
}
auto fold_obj_field = bind fold_obj_field_(_,fld);
ret alt(i) {
case (item_const(?t, ?e)) {
item_const(fld.fold_ty(t), fld.fold_expr(e))
}
case (item_fn(?f, ?typms)) {
item_fn(fld.fold_fn(f), typms)
}
case (item_mod(?m)) { item_mod(fld.fold_mod(m)) }
case (item_native_mod(?nm)) {
item_native_mod(fld.fold_native_mod(nm))
}
case (item_ty(?t, ?typms)) {
item_ty(fld.fold_ty(t), typms)
}
case (item_tag(?variants, ?typms)) {
item_tag(map(fld.fold_variant, variants), typms)
}
case (item_obj(?o, ?typms, ?d)) {
item_obj(rec(fields=map(fold_obj_field,o.fields),
methods=map(fld.fold_method,o.methods),
dtor=option::map(fld.fold_method,o.dtor)),
typms, d)
}
case (item_res(?dtor, ?did, ?typms, ?cid)) {
item_res(fld.fold_fn(dtor), did, typms, cid)
}
};
}
fn noop_fold_method(&method_ m, ast_fold fld) -> method_ {
ret rec(ident=fld.fold_ident(m.ident),
meth=fld.fold_fn(m.meth), id=m.id);
}
fn noop_fold_block(&block_ b, ast_fold fld) -> block_ {
ret rec(stmts=map(fld.fold_stmt, b.stmts),
expr=option::map(fld.fold_expr, b.expr), id=b.id);
}
fn noop_fold_stmt(&stmt_ s, ast_fold fld) -> stmt_ {
ret alt(s) {
case (stmt_decl(?d, ?nid)) { stmt_decl(fld.fold_decl(d), nid) }
case (stmt_expr(?e, ?nid)) { stmt_expr(fld.fold_expr(e), nid) }
case (stmt_crate_directive(?cd)) {
stmt_crate_directive(fld.fold_crate_directive(cd))
}
};
}
fn noop_fold_arm(&arm a, ast_fold fld) -> arm {
ret rec(pat=fld.fold_pat(a.pat), block=fld.fold_block(a.block));
}
fn noop_fold_pat(&pat_ p, ast_fold fld) -> pat_ {
ret alt (p) {
case (pat_wild) { p }
case (pat_bind(?ident)) { pat_bind(fld.fold_ident(ident))}
case (pat_lit(_)) { p }
case (pat_tag(?pth, ?pats)) {
pat_tag(fld.fold_path(pth), map(fld.fold_pat, pats))
}
};
}
fn noop_fold_decl(&decl_ d, ast_fold fld) -> decl_ {
ret alt (d) {
// local really doesn't need its own fold...
case (decl_local(?l)) {
decl_local(fld.fold_local(l))
}
case (decl_item(?it)) { decl_item(fld.fold_item(it)) }
}
}
fn noop_fold_expr(&expr_ e, ast_fold fld) -> expr_ {
fn fold_elt_(&elt elt, ast_fold fld) -> elt {
ret rec(mut=elt.mut, expr=fld.fold_expr(elt.expr));
}
auto fold_elt = bind fold_elt_(_,fld);
fn fold_field_(&field field, ast_fold fld) -> field {
ret rec(node=rec(mut=field.node.mut,
ident=fld.fold_ident(field.node.ident),
expr=fld.fold_expr(field.node.expr)),
span=field.span);
}
auto fold_field = bind fold_field_(_,fld);
fn fold_anon_obj_(&anon_obj ao, ast_fold fld) -> anon_obj {
fn fold_anon_obj_field_(&anon_obj_field aof, ast_fold fld)
-> anon_obj_field {
ret rec(mut=aof.mut, ty=fld.fold_ty(aof.ty),
expr=fld.fold_expr(aof.expr),
ident=fld.fold_ident(aof.ident), id=aof.id);
}
auto fold_anon_obj_field = bind fold_anon_obj_field_(_,fld);
ret rec(fields=alt(ao.fields) {
case (option::none[vec[anon_obj_field]]) { ao.fields }
case (option::some[vec[anon_obj_field]](?v)) {
option::some[vec[anon_obj_field]]
(map(fold_anon_obj_field, v))
}},
methods=map(fld.fold_method, ao.methods),
with_obj=option::map(fld.fold_expr, ao.with_obj))
}
auto fold_anon_obj = bind fold_anon_obj_(_,fld);
ret alt (e) {
case (expr_vec(?exprs, ?mut, ?seq_kind)) {
expr_vec(map(fld.fold_expr, exprs), mut, seq_kind)
}
case (expr_tup(?elts)) {
expr_tup(map(fold_elt, elts))
}
case (expr_rec(?fields, ?maybe_expr)) {
expr_rec(map(fold_field, fields),
option::map(fld.fold_expr, maybe_expr))
}
case (expr_call(?f, ?args)) {
expr_call(fld.fold_expr(f), map(fld.fold_expr, args))
}
case (expr_self_method(?id)) {
expr_self_method(fld.fold_ident(id))
}
case (expr_bind(?f, ?args)) {
auto opt_map_se = bind option::map(fld.fold_expr,_);
expr_bind(fld.fold_expr(f), map(opt_map_se, args))
}
case (expr_spawn(?spawn_dom, ?name, ?f, ?args)) {
expr_spawn(spawn_dom, name, fld.fold_expr(f),
map(fld.fold_expr, args))
}
case (expr_binary(?binop, ?lhs, ?rhs)) {
expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs))
}
case (expr_unary(?binop, ?ohs)) {
expr_unary(binop, fld.fold_expr(ohs))
}
case (expr_lit(_)) { e }
case (expr_cast(?expr, ?ty)) {
expr_cast(fld.fold_expr(expr), ty)
}
case (expr_if(?cond, ?tr, ?fl)) {
expr_if(fld.fold_expr(cond), fld.fold_block(tr),
option::map(fld.fold_expr, fl))
}
case (expr_ternary(?cond, ?tr, ?fl)) {
expr_ternary(fld.fold_expr(cond),
fld.fold_expr(tr),
fld.fold_expr(fl))
}
case (expr_while(?cond, ?body)) {
expr_while(fld.fold_expr(cond), fld.fold_block(body))
}
case (expr_for(?decl, ?expr, ?block)) {
expr_for(fld.fold_local(decl), fld.fold_expr(expr),
fld.fold_block(block))
}
case (expr_for_each(?decl, ?expr, ?block)) {
expr_for_each(fld.fold_local(decl), fld.fold_expr(expr),
fld.fold_block(block))
}
case (expr_do_while(?block, ?expr)) {
expr_do_while(fld.fold_block(block), fld.fold_expr(expr))
}
case (expr_alt(?expr, ?arms)) {
expr_alt(fld.fold_expr(expr), map(fld.fold_arm, arms))
}
case (expr_fn(?f)) {
expr_fn(fld.fold_fn(f))
}
case (expr_block(?block)) {
expr_block(fld.fold_block(block))
}
case (expr_move(?el, ?er)) {
expr_move(fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_assign(?el, ?er)) {
expr_assign(fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_swap(?el, ?er)) {
expr_swap(fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_assign_op(?op, ?el, ?er)) {
expr_assign_op(op, fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_send(?el, ?er)) {
expr_send(fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_recv(?el, ?er)) {
expr_recv(fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_field(?el, ?id)) {
expr_field(fld.fold_expr(el), fld.fold_ident(id))
}
case (expr_index(?el, ?er)) {
expr_index(fld.fold_expr(el), fld.fold_expr(er))
}
case (expr_path(?pth)) {
expr_path(fld.fold_path(pth))
}
case (expr_ext(?pth, ?args, ?body, ?expanded)) {
expr_ext(fld.fold_path(pth), map(fld.fold_expr, args),
body, fld.fold_expr(expanded))
}
case (expr_fail(_)) { e }
case (expr_break()) { e }
case (expr_cont()) { e }
case (expr_ret(?e)) {
expr_ret(option::map(fld.fold_expr, e))
}
case (expr_put(?e)) {
expr_put(option::map(fld.fold_expr, e))
}
case (expr_be(?e)) { expr_be(fld.fold_expr(e)) }
case (expr_log(?lv, ?e)) { expr_log(lv, fld.fold_expr(e)) }
case (expr_assert(?e)) { expr_assert(fld.fold_expr(e)) }
case (expr_check(?m, ?e)) { expr_check(m, fld.fold_expr(e)) }
case (expr_if_check(?cond, ?tr, ?fl)) {
expr_if_check(fld.fold_expr(cond), fld.fold_block(tr),
option::map(fld.fold_expr, fl))
}
case (expr_port(?ot)) {
expr_port(alt(ot) {
case (option::some(?t)) { option::some(fld.fold_ty(t)) }
case (option::none) { option::none }
})
}
case (expr_chan(?e)) { expr_chan(fld.fold_expr(e)) }
case (expr_anon_obj(?ao, ?typms, ?odis)) {
expr_anon_obj(fold_anon_obj(ao), typms, odis)
}
}
}
fn noop_fold_ty(&ty_ t, ast_fold fld) -> ty_ {
//drop in ty::fold_ty here if necessary
ret t;
}
fn noop_fold_constr(&constr_ c, ast_fold fld) -> constr_ {
ret rec(path=fld.fold_path(c.path), args=c.args, id=c.id);
}
// functions just don't get spans, for some reason
fn noop_fold_fn(&_fn f, ast_fold fld) -> _fn {
auto fold_arg = bind fold_arg_(_, fld);
ret rec(decl= rec(inputs=map(fold_arg, f.decl.inputs),
output=fld.fold_ty(f.decl.output),
purity=f.decl.purity,
cf=f.decl.cf,
constraints=map(fld.fold_constr, f.decl.constraints)),
proto = f.proto,
body = fld.fold_block(f.body));
}
// ...nor do modules
fn noop_fold_mod(&_mod m, ast_fold fld) -> _mod {
ret rec(view_items=map(fld.fold_view_item, m.view_items),
items=map(fld.fold_item, m.items));
}
fn noop_fold_native_mod(&native_mod nm, ast_fold fld) -> native_mod {
ret rec(native_name=nm.native_name,
abi=nm.abi,
view_items=map(fld.fold_view_item, nm.view_items),
items=map(fld.fold_native_item, nm.items))
}
fn noop_fold_variant(&variant_ v, ast_fold fld) -> variant_ {
fn fold_variant_arg_(&variant_arg va, ast_fold fld) -> variant_arg {
ret rec(ty=fld.fold_ty(va.ty), id=va.id);
}
auto fold_variant_arg = bind fold_variant_arg_(_,fld);
ret rec(name=v.name,
args=map(fold_variant_arg, v.args),
id=v.id);
}
fn noop_fold_ident(&ident i, ast_fold fld) -> ident {
ret i;
}
fn noop_fold_path(&path_ p, ast_fold fld) -> path_ {
ret rec(idents=map(fld.fold_ident, p.idents),
types=map(fld.fold_ty, p.types));
}
fn noop_fold_local(&local_ l, ast_fold fld) -> local_ {
ret rec(ty=option::map(fld.fold_ty,l.ty),
infer=l.infer,
ident=fld.fold_ident(l.ident),
init=alt (l.init) {
case (option::none[initializer]) { l.init }
case (option::some[initializer](?init)) {
option::some[initializer]
(rec(op=init.op,
expr=fld.fold_expr(init.expr)))
}
},
id=l.id);
}
fn default_ast_fold() -> @ast_fold_precursor {
ret @rec(fold_crate = noop_fold_crate,
fold_crate_directive = noop_fold_crate_directive,
fold_view_item = noop_fold_view_item,
fold_native_item = noop_fold_native_item,
fold_item = noop_fold_item,
fold_item_underscore = noop_fold_item_underscore,
fold_method = noop_fold_method,
fold_block = noop_fold_block,
fold_stmt = noop_fold_stmt,
fold_arm = noop_fold_arm,
fold_pat = noop_fold_pat,
fold_decl = noop_fold_decl,
fold_expr = noop_fold_expr,
fold_ty = noop_fold_ty,
fold_constr = noop_fold_constr,
fold_fn = noop_fold_fn,
fold_mod = noop_fold_mod,
fold_native_mod = noop_fold_native_mod,
fold_variant = noop_fold_variant,
fold_ident = noop_fold_ident,
fold_path = noop_fold_path,
fold_local = noop_fold_local);
}
fn dummy_out(ast_fold a) {
*a = rec(fold_crate = nf_crate_dummy,
fold_crate_directive = nf_crate_directive_dummy,
fold_view_item = nf_view_item_dummy,
fold_native_item = nf_native_item_dummy,
fold_item = nf_item_dummy,
fold_item_underscore = nf_item_underscore_dummy,
fold_method = nf_method_dummy,
fold_block = nf_block_dummy,
fold_stmt = nf_stmt_dummy,
fold_arm = nf_arm_dummy,
fold_pat = nf_pat_dummy,
fold_decl = nf_decl_dummy,
fold_expr = nf_expr_dummy,
fold_ty = nf_ty_dummy,
fold_constr = nf_constr_dummy,
fold_fn = nf_fn_dummy,
fold_mod = nf_mod_dummy,
fold_native_mod = nf_native_mod_dummy,
fold_variant = nf_variant_dummy,
fold_ident = nf_ident_dummy,
fold_path = nf_path_dummy,
fold_local = nf_local_dummy);
}
fn make_fold(&ast_fold_precursor afp) -> ast_fold {
let ast_fold result =
@mutable rec(fold_crate = nf_crate_dummy,
fold_crate_directive = nf_crate_directive_dummy,
fold_view_item = nf_view_item_dummy,
fold_native_item = nf_native_item_dummy,
fold_item = nf_item_dummy,
fold_item_underscore = nf_item_underscore_dummy,
fold_method = nf_method_dummy,
fold_block = nf_block_dummy,
fold_stmt = nf_stmt_dummy,
fold_arm = nf_arm_dummy,
fold_pat = nf_pat_dummy,
fold_decl = nf_decl_dummy,
fold_expr = nf_expr_dummy,
fold_ty = nf_ty_dummy,
fold_constr = nf_constr_dummy,
fold_fn = nf_fn_dummy,
fold_mod = nf_mod_dummy,
fold_native_mod = nf_native_mod_dummy,
fold_variant = nf_variant_dummy,
fold_ident = nf_ident_dummy,
fold_path = nf_path_dummy,
fold_local = nf_local_dummy);
/* naturally, a macro to write these would be nice */
fn f_crate(&ast_fold_precursor afp, ast_fold f, &crate c) -> crate {
ret rec(node=afp.fold_crate(c.node, f),
span=c.span);
}
fn f_crate_directive(&ast_fold_precursor afp, ast_fold f,
&@crate_directive c) -> @crate_directive {
ret @rec(node=afp.fold_crate_directive(c.node, f),
span=c.span);
}
fn f_view_item(&ast_fold_precursor afp, ast_fold f, &@view_item x)
-> @view_item {
ret @rec(node=afp.fold_view_item(x.node, f), span=x.span);
}
fn f_native_item(&ast_fold_precursor afp, ast_fold f, &@native_item x)
-> @native_item {
ret afp.fold_native_item(x, f);
}
fn f_item(&ast_fold_precursor afp, ast_fold f, &@item i) -> @item {
ret afp.fold_item(i, f);
}
fn f_item_underscore(&ast_fold_precursor afp, ast_fold f, &item_ i) ->
item_ {
ret afp.fold_item_underscore(i, f);
}
fn f_method(&ast_fold_precursor afp, ast_fold f, &@method x) -> @method {
ret @rec(node=afp.fold_method(x.node, f), span=x.span);
}
fn f_block(&ast_fold_precursor afp, ast_fold f, &block x) -> block {
ret rec(node=afp.fold_block(x.node, f), span=x.span);
}
fn f_stmt(&ast_fold_precursor afp, ast_fold f, &@stmt x) -> @stmt {
ret @rec(node=afp.fold_stmt(x.node, f), span=x.span);
}
fn f_arm(&ast_fold_precursor afp, ast_fold f, &arm x) -> arm {
ret afp.fold_arm(x, f);
}
fn f_pat(&ast_fold_precursor afp, ast_fold f, &@pat x) -> @pat {
ret @rec(id=x.id, node=afp.fold_pat(x.node, f), span=x.span);
}
fn f_decl(&ast_fold_precursor afp, ast_fold f, &@decl x) -> @decl {
ret @rec(node=afp.fold_decl(x.node, f), span=x.span);
}
fn f_expr(&ast_fold_precursor afp, ast_fold f, &@expr x) -> @expr {
ret @rec(id=x.id, node=afp.fold_expr(x.node, f), span=x.span);
}
fn f_ty(&ast_fold_precursor afp, ast_fold f, &@ty x) -> @ty {
ret @rec(node=afp.fold_ty(x.node, f), span=x.span);
}
fn f_constr(&ast_fold_precursor afp, ast_fold f, &@constr x) -> @constr {
ret @rec(node=afp.fold_constr(x.node, f), span=x.span);
}
fn f_fn(&ast_fold_precursor afp, ast_fold f, &_fn x) -> _fn {
ret afp.fold_fn(x, f);
}
fn f_mod(&ast_fold_precursor afp, ast_fold f, &_mod x) -> _mod {
ret afp.fold_mod(x, f);
}
fn f_native_mod(&ast_fold_precursor afp, ast_fold f, &native_mod x) ->
native_mod {
ret afp.fold_native_mod(x, f);
}
fn f_variant(&ast_fold_precursor afp, ast_fold f, &variant x)
-> variant {
ret rec(node=afp.fold_variant(x.node, f), span=x.span);
}
fn f_ident(&ast_fold_precursor afp, ast_fold f, &ident x) -> ident {
ret afp.fold_ident(x, f);
}
fn f_path(&ast_fold_precursor afp, ast_fold f, &path x) -> path {
ret rec(node=afp.fold_path(x.node, f), span=x.span);
}
fn f_local(&ast_fold_precursor afp, ast_fold f, &@local x) -> @local {
ret @rec(node=afp.fold_local(x.node, f), span=x.span);
}
*result = rec(fold_crate = bind f_crate(afp,result,_),
fold_crate_directive = bind f_crate_directive(afp,result,_),
fold_view_item = bind f_view_item(afp,result,_),
fold_native_item = bind f_native_item(afp,result,_),
fold_item = bind f_item(afp,result,_),
fold_item_underscore = bind f_item_underscore(afp,result,_),
fold_method = bind f_method(afp,result,_),
fold_block = bind f_block(afp,result,_),
fold_stmt = bind f_stmt(afp,result,_),
fold_arm = bind f_arm(afp, result, _),
fold_pat = bind f_pat(afp,result,_),
fold_decl = bind f_decl(afp,result,_),
fold_expr = bind f_expr(afp,result,_),
fold_ty = bind f_ty(afp,result,_),
fold_constr = bind f_constr(afp,result,_),
fold_fn = bind f_fn(afp,result,_),
fold_mod = bind f_mod(afp,result,_),
fold_native_mod = bind f_native_mod(afp,result,_),
fold_variant = bind f_variant(afp,result,_),
fold_ident = bind f_ident(afp,result,_),
fold_path = bind f_path(afp,result,_),
fold_local = bind f_local(afp,result,_));
ret result;
/*
ret rec(fold_crate = noop_fold_crate,
fold_crate_directive = noop_fold_crate_drective,
fold_view_item = noop_fold_view_item,
fold_native_item = noop_fold_native_item,
fold_item = noop_fold_item,
fold_method = noop_fold_method,
fold_block = noop_fold_block,
fold_stmt = noop_fold_stmt,
fold_arm = noop_fold_arm,
fold_pat = noop_fold_pat,
fold_decl = noop_fold_decl,
fold_expr = noop_fold_expr,
fold_ty = noop_fold_ty,
fold_constr = noop_fold_constr,
fold_fn = noop_fold_fn);*/
}
//
// 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:
//

View file

@ -0,0 +1,124 @@
import std::vec;
import std::str;
import std::option;
import std::option::some;
import std::option::none;
import syntax::ast;
import syntax::parse::token;
import syntax::parse::parser::parser;
import syntax::parse::parser::new_parser;
import syntax::parse::parser::parse_inner_attrs_and_next;
import syntax::parse::parser::parse_mod_items;
export eval_crate_directives_to_mod;
export mode_parse;
tag eval_mode { mode_depend; mode_parse; }
type ctx =
@rec(parser p,
eval_mode mode,
mutable vec[str] deps,
parser::parse_sess sess,
mutable uint chpos,
ast::crate_cfg cfg);
fn eval_crate_directives(ctx cx, vec[@ast::crate_directive] cdirs,
str prefix, &mutable vec[@ast::view_item] view_items,
&mutable vec[@ast::item] items) {
for (@ast::crate_directive sub_cdir in cdirs) {
eval_crate_directive(cx, sub_cdir, prefix, view_items, items);
}
}
fn eval_crate_directives_to_mod(ctx cx,
vec[@ast::crate_directive] cdirs, str prefix)
-> ast::_mod {
let vec[@ast::view_item] view_items = [];
let vec[@ast::item] items = [];
eval_crate_directives(cx, cdirs, prefix, view_items, items);
ret rec(view_items=view_items, items=items);
}
fn eval_crate_directive_block(ctx cx, &ast::block blk, str prefix,
&mutable vec[@ast::view_item] view_items,
&mutable vec[@ast::item] items) {
for (@ast::stmt s in blk.node.stmts) {
alt (s.node) {
case (ast::stmt_crate_directive(?cdir)) {
eval_crate_directive(cx, cdir, prefix, view_items, items);
}
case (_) {
codemap::emit_warning
(some(s.span), "unsupported stmt in crate-directive block",
cx.sess.cm);
}
}
}
}
fn eval_crate_directive(ctx cx, @ast::crate_directive cdir, str prefix,
&mutable vec[@ast::view_item] view_items,
&mutable vec[@ast::item] items) {
alt (cdir.node) {
case (ast::cdir_src_mod(?id, ?file_opt, ?attrs)) {
auto file_path = id + ".rs";
alt (file_opt) {
case (some(?f)) { file_path = f; }
case (none) { }
}
auto full_path = if (std::fs::path_is_absolute(file_path)) {
file_path
} else {
prefix + std::fs::path_sep() + file_path
};
if (cx.mode == mode_depend) { cx.deps += [full_path]; ret; }
auto p0 =
new_parser(cx.sess, cx.cfg, full_path, cx.chpos);
auto inner_attrs = parse_inner_attrs_and_next(p0);
auto mod_attrs = attrs + inner_attrs._0;
auto first_item_outer_attrs = inner_attrs._1;
auto m0 = parse_mod_items(p0, token::EOF, first_item_outer_attrs);
auto i = syntax::parse::parser::mk_item
(p0, cdir.span.lo, cdir.span.hi, id, ast::item_mod(m0),
mod_attrs);
// Thread defids and chpos through the parsers
cx.chpos = p0.get_chpos();
vec::push[@ast::item](items, i);
}
case (ast::cdir_dir_mod(?id, ?dir_opt, ?cdirs, ?attrs)) {
auto path = id;
alt (dir_opt) { case (some(?d)) { path = d; } case (none) { } }
auto full_path = if (std::fs::path_is_absolute(path)) {
path
} else {
prefix + std::fs::path_sep() + path
};
auto m0 = eval_crate_directives_to_mod(cx, cdirs, full_path);
auto i = @rec(ident=id,
attrs=attrs,
id=cx.sess.next_id,
node=ast::item_mod(m0),
span=cdir.span);
cx.sess.next_id += 1;
vec::push[@ast::item](items, i);
}
case (ast::cdir_view_item(?vi)) {
vec::push[@ast::view_item](view_items, vi);
}
case (ast::cdir_syntax(?pth)) { }
case (ast::cdir_auth(?pth, ?eff)) { }
}
}
//
// 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:
//

View file

@ -0,0 +1,758 @@
import std::io;
import std::str;
import std::vec;
import std::int;
import std::map;
import std::map::hashmap;
import std::option;
import std::option::some;
import std::option::none;
import util::interner;
import util::interner::intern;
import codemap;
type reader =
obj {
fn is_eof() -> bool ;
fn curr() -> char ;
fn next() -> char ;
fn init() ;
fn bump() ;
fn mark() ;
fn get_mark_chpos() -> uint ;
fn get_mark_str() -> str ;
fn get_interner() -> @interner::interner[str] ;
fn get_chpos() -> uint ;
fn get_col() -> uint ;
fn get_filemap() -> codemap::filemap ;
fn err(str) ;
};
fn new_reader(&codemap::codemap cm, io::reader rdr, codemap::filemap filemap,
@interner::interner[str] itr) -> reader {
obj reader(codemap::codemap cm,
str file,
uint len,
mutable uint col,
mutable uint pos,
mutable char ch,
mutable uint mark_chpos,
mutable uint chpos,
mutable vec[str] strs,
codemap::filemap fm,
@interner::interner[str] itr) {
fn is_eof() -> bool { ret ch == -1 as char; }
fn mark() { mark_chpos = chpos; }
fn get_mark_str() -> str { ret str::slice(file, mark_chpos, chpos); }
fn get_mark_chpos() -> uint { ret mark_chpos; }
fn get_chpos() -> uint { ret chpos; }
fn curr() -> char { ret ch; }
fn next() -> char {
if (pos < len) {
ret str::char_at(file, pos);
} else { ret -1 as char; }
}
fn init() {
if (pos < len) {
auto next = str::char_range_at(file, pos);
pos = next._1;
ch = next._0;
}
}
fn bump() {
if (pos < len) {
col += 1u;
chpos += 1u;
if (ch == '\n') { codemap::next_line(fm, chpos); col = 0u; }
auto next = str::char_range_at(file, pos);
pos = next._1;
ch = next._0;
} else { ch = -1 as char; }
}
fn get_interner() -> @interner::interner[str] { ret itr; }
fn get_col() -> uint { ret col; }
fn get_filemap() -> codemap::filemap { ret fm; }
fn err(str m) {
codemap::emit_error(some(rec(lo=chpos, hi=chpos)), m, cm);
}
}
auto file = str::unsafe_from_bytes(rdr.read_whole_stream());
let vec[str] strs = [];
auto rd =
reader(cm, file, str::byte_len(file), 0u, 0u, -1 as char,
filemap.start_pos, filemap.start_pos, strs, filemap, itr);
rd.init();
ret rd;
}
fn dec_digit_val(char c) -> int { ret (c as int) - ('0' as int); }
fn hex_digit_val(char c) -> int {
if (in_range(c, '0', '9')) { ret (c as int) - ('0' as int); }
if (in_range(c, 'a', 'f')) { ret (c as int) - ('a' as int) + 10; }
if (in_range(c, 'A', 'F')) { ret (c as int) - ('A' as int) + 10; }
fail;
}
fn bin_digit_value(char c) -> int { if (c == '0') { ret 0; } ret 1; }
fn is_whitespace(char c) -> bool {
ret c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
fn may_begin_ident(char c) -> bool { ret is_alpha(c) || c == '_'; }
fn in_range(char c, char lo, char hi) -> bool { ret lo <= c && c <= hi; }
fn is_alpha(char c) -> bool {
ret in_range(c, 'a', 'z') || in_range(c, 'A', 'Z');
}
fn is_dec_digit(char c) -> bool { ret in_range(c, '0', '9'); }
fn is_alnum(char c) -> bool { ret is_alpha(c) || is_dec_digit(c); }
fn is_hex_digit(char c) -> bool {
ret in_range(c, '0', '9') || in_range(c, 'a', 'f') ||
in_range(c, 'A', 'F');
}
fn is_bin_digit(char c) -> bool { ret c == '0' || c == '1'; }
fn consume_whitespace_and_comments(&reader rdr) {
while (is_whitespace(rdr.curr())) { rdr.bump(); }
be consume_any_line_comment(rdr);
}
fn consume_any_line_comment(&reader rdr) {
if (rdr.curr() == '/') {
alt (rdr.next()) {
case ('/') {
while (rdr.curr() != '\n' && !rdr.is_eof()) { rdr.bump(); }
// Restart whitespace munch.
be consume_whitespace_and_comments(rdr);
}
case ('*') {
rdr.bump();
rdr.bump();
be consume_block_comment(rdr);
}
case (_) { ret; }
}
}
}
fn consume_block_comment(&reader rdr) {
let int level = 1;
while (level > 0) {
if (rdr.is_eof()) { rdr.err("unterminated block comment"); fail; }
if (rdr.curr() == '/' && rdr.next() == '*') {
rdr.bump();
rdr.bump();
level += 1;
} else {
if (rdr.curr() == '*' && rdr.next() == '/') {
rdr.bump();
rdr.bump();
level -= 1;
} else { rdr.bump(); }
}
}
// restart whitespace munch.
be consume_whitespace_and_comments(rdr);
}
fn digits_to_string(str s) -> int {
let int accum_int = 0;
for (u8 c in s) {
accum_int *= 10;
accum_int += dec_digit_val(c as char);
}
ret accum_int;
}
fn scan_exponent(&reader rdr) -> option::t[str] {
auto c = rdr.curr();
auto rslt = "";
if (c == 'e' || c == 'E') {
rslt += str::from_bytes([c as u8]);
rdr.bump();
c = rdr.curr();
if (c == '-' || c == '+') {
rslt += str::from_bytes([c as u8]);
rdr.bump();
}
auto exponent = scan_dec_digits(rdr);
if (str::byte_len(exponent) > 0u) {
ret some(rslt + exponent);
} else { rdr.err("scan_exponent: bad fp literal"); fail; }
} else { ret none[str]; }
}
fn scan_dec_digits(&reader rdr) -> str {
auto c = rdr.curr();
let str rslt = "";
while (is_dec_digit(c) || c == '_') {
if (c != '_') { rslt += str::from_bytes([c as u8]); }
rdr.bump();
c = rdr.curr();
}
ret rslt;
}
fn scan_number(char c, &reader rdr) -> token::token {
auto accum_int = 0;
let str dec_str = "";
let bool is_dec_integer = false;
auto n = rdr.next();
if (c == '0' && n == 'x') {
rdr.bump();
rdr.bump();
c = rdr.curr();
while (is_hex_digit(c) || c == '_') {
if (c != '_') { accum_int *= 16; accum_int += hex_digit_val(c); }
rdr.bump();
c = rdr.curr();
}
} else if (c == '0' && n == 'b') {
rdr.bump();
rdr.bump();
c = rdr.curr();
while (is_bin_digit(c) || c == '_') {
if (c != '_') { accum_int *= 2; accum_int += bin_digit_value(c); }
rdr.bump();
c = rdr.curr();
}
} else { dec_str = scan_dec_digits(rdr); is_dec_integer = true; }
if (is_dec_integer) { accum_int = digits_to_string(dec_str); }
c = rdr.curr();
n = rdr.next();
if (c == 'u' || c == 'i') {
let bool signed = c == 'i';
rdr.bump();
c = rdr.curr();
if (c == '8') {
rdr.bump();
if (signed) {
ret token::LIT_MACH_INT(ast::ty_i8, accum_int);
} else { ret token::LIT_MACH_INT(ast::ty_u8, accum_int); }
}
n = rdr.next();
if (c == '1' && n == '6') {
rdr.bump();
rdr.bump();
if (signed) {
ret token::LIT_MACH_INT(ast::ty_i16, accum_int);
} else { ret token::LIT_MACH_INT(ast::ty_u16, accum_int); }
}
if (c == '3' && n == '2') {
rdr.bump();
rdr.bump();
if (signed) {
ret token::LIT_MACH_INT(ast::ty_i32, accum_int);
} else { ret token::LIT_MACH_INT(ast::ty_u32, accum_int); }
}
if (c == '6' && n == '4') {
rdr.bump();
rdr.bump();
if (signed) {
ret token::LIT_MACH_INT(ast::ty_i64, accum_int);
} else { ret token::LIT_MACH_INT(ast::ty_u64, accum_int); }
}
if (signed) {
ret token::LIT_INT(accum_int);
} else {
// FIXME: should cast in the target bit-width.
ret token::LIT_UINT(accum_int as uint);
}
}
c = rdr.curr();
if (c == '.') {
// Parse a floating-point number.
rdr.bump();
auto dec_part = scan_dec_digits(rdr);
auto float_str = dec_str + "." + dec_part;
c = rdr.curr();
auto exponent_str = scan_exponent(rdr);
alt (exponent_str) {
case (some(?s)) { float_str += s; }
case (none) { }
}
c = rdr.curr();
if (c == 'f') {
rdr.bump();
c = rdr.curr();
n = rdr.next();
if (c == '3' && n == '2') {
rdr.bump();
rdr.bump();
ret token::LIT_MACH_FLOAT(ast::ty_f32,
intern(*rdr.get_interner(),
float_str));
} else if (c == '6' && n == '4') {
rdr.bump();
rdr.bump();
ret token::LIT_MACH_FLOAT(ast::ty_f64,
intern(*rdr.get_interner(),
float_str));
/* FIXME: if this is out of range for either a 32-bit or
64-bit float, it won't be noticed till the back-end */
}
} else {
ret token::LIT_FLOAT(interner::intern[str](*rdr.get_interner(),
float_str));
}
}
auto maybe_exponent = scan_exponent(rdr);
alt (maybe_exponent) {
case (some(?s)) {
ret token::LIT_FLOAT(interner::intern[str](*rdr.get_interner(),
dec_str + s));
}
case (none) { ret token::LIT_INT(accum_int); }
}
}
fn scan_numeric_escape(&reader rdr, uint n_hex_digits) -> char {
auto accum_int = 0;
while (n_hex_digits != 0u) {
auto n = rdr.curr();
rdr.bump();
if (!is_hex_digit(n)) {
rdr.err(#fmt("illegal numeric character escape: %d", n as int));
fail;
}
accum_int *= 16;
accum_int += hex_digit_val(n);
n_hex_digits -= 1u;
}
ret accum_int as char;
}
fn next_token(&reader rdr) -> token::token {
auto accum_str = "";
consume_whitespace_and_comments(rdr);
if (rdr.is_eof()) { ret token::EOF; }
rdr.mark();
auto c = rdr.curr();
if (is_alpha(c) || c == '_') {
while (is_alnum(c) || c == '_') {
str::push_char(accum_str, c);
rdr.bump();
c = rdr.curr();
}
if (str::eq(accum_str, "_")) { ret token::UNDERSCORE; }
auto is_mod_name = c == ':' && rdr.next() == ':';
ret token::IDENT(interner::intern[str](*rdr.get_interner(),
accum_str), is_mod_name);
}
if (is_dec_digit(c)) { ret scan_number(c, rdr); }
fn binop(&reader rdr, token::binop op) -> token::token {
rdr.bump();
if (rdr.curr() == '=') {
rdr.bump();
ret token::BINOPEQ(op);
} else { ret token::BINOP(op); }
}
alt (c) {
case (
// One-byte tokens.
'?') {
rdr.bump();
ret token::QUES;
}
case (';') { rdr.bump(); ret token::SEMI; }
case (',') { rdr.bump(); ret token::COMMA; }
case ('.') { rdr.bump(); ret token::DOT; }
case ('(') { rdr.bump(); ret token::LPAREN; }
case (')') { rdr.bump(); ret token::RPAREN; }
case ('{') { rdr.bump(); ret token::LBRACE; }
case ('}') { rdr.bump(); ret token::RBRACE; }
case ('[') { rdr.bump(); ret token::LBRACKET; }
case (']') { rdr.bump(); ret token::RBRACKET; }
case ('@') { rdr.bump(); ret token::AT; }
case ('#') { rdr.bump(); ret token::POUND; }
case ('~') { rdr.bump(); ret token::TILDE; }
case (':') {
rdr.bump();
if (rdr.curr() == ':') {
rdr.bump();
ret token::MOD_SEP;
} else { ret token::COLON; }
}
case (
// Multi-byte tokens.
'=') {
rdr.bump();
if (rdr.curr() == '=') {
rdr.bump();
ret token::EQEQ;
} else { ret token::EQ; }
}
case ('!') {
rdr.bump();
if (rdr.curr() == '=') {
rdr.bump();
ret token::NE;
} else { ret token::NOT; }
}
case ('<') {
rdr.bump();
alt (rdr.curr()) {
case ('=') { rdr.bump(); ret token::LE; }
case ('<') { ret binop(rdr, token::LSL); }
case ('|') { rdr.bump(); ret token::SEND; }
case ('-') {
rdr.bump();
alt (rdr.curr()) {
case ('>') { rdr.bump(); ret token::DARROW; }
case (_) { ret token::LARROW; }
}
}
case (_) { ret token::LT; }
}
}
case ('>') {
rdr.bump();
alt (rdr.curr()) {
case ('=') { rdr.bump(); ret token::GE; }
case ('>') {
if (rdr.next() == '>') {
rdr.bump();
ret binop(rdr, token::ASR);
} else { ret binop(rdr, token::LSR); }
}
case (_) { ret token::GT; }
}
}
case ('\'') {
rdr.bump();
auto c2 = rdr.curr();
rdr.bump();
if (c2 == '\\') {
auto escaped = rdr.curr();
rdr.bump();
alt (escaped) {
case ('n') { c2 = '\n'; }
case ('r') { c2 = '\r'; }
case ('t') { c2 = '\t'; }
case ('\\') { c2 = '\\'; }
case ('\'') { c2 = '\''; }
case ('x') { c2 = scan_numeric_escape(rdr, 2u); }
case ('u') { c2 = scan_numeric_escape(rdr, 4u); }
case ('U') { c2 = scan_numeric_escape(rdr, 8u); }
case (?c2) {
rdr.err(#fmt("unknown character escape: %d",
c2 as int));
fail;
}
}
}
if (rdr.curr() != '\'') {
rdr.err("unterminated character constant");
fail;
}
rdr.bump(); // advance curr past token
ret token::LIT_CHAR(c2);
}
case ('"') {
rdr.bump();
while (rdr.curr() != '"') {
auto ch = rdr.curr();
rdr.bump();
alt (ch) {
case ('\\') {
auto escaped = rdr.curr();
rdr.bump();
alt (escaped) {
case ('n') {
str::push_byte(accum_str, '\n' as u8);
}
case ('r') {
str::push_byte(accum_str, '\r' as u8);
}
case ('t') {
str::push_byte(accum_str, '\t' as u8);
}
case ('\\') {
str::push_byte(accum_str, '\\' as u8);
}
case ('"') {
str::push_byte(accum_str, '"' as u8);
}
case ('\n') { consume_whitespace(rdr); }
case ('x') {
str::push_char(accum_str,
scan_numeric_escape(rdr, 2u));
}
case ('u') {
str::push_char(accum_str,
scan_numeric_escape(rdr, 4u));
}
case ('U') {
str::push_char(accum_str,
scan_numeric_escape(rdr, 8u));
}
case (?c2) {
rdr.err(#fmt("unknown string escape: %d",
c2 as int));
fail;
}
}
}
case (_) { str::push_char(accum_str, ch); }
}
}
rdr.bump();
ret token::LIT_STR(interner::intern[str](*rdr.get_interner(),
accum_str));
}
case ('-') {
if (rdr.next() == '>') {
rdr.bump();
rdr.bump();
ret token::RARROW;
} else { ret binop(rdr, token::MINUS); }
}
case ('&') {
if (rdr.next() == '&') {
rdr.bump();
rdr.bump();
ret token::ANDAND;
} else { ret binop(rdr, token::AND); }
}
case ('|') {
alt (rdr.next()) {
case ('|') { rdr.bump(); rdr.bump(); ret token::OROR; }
case ('>') { rdr.bump(); rdr.bump(); ret token::RECV; }
case (_) { ret binop(rdr, token::OR); }
}
}
case ('+') { ret binop(rdr, token::PLUS); }
case ('*') { ret binop(rdr, token::STAR); }
case ('/') { ret binop(rdr, token::SLASH); }
case ('^') { ret binop(rdr, token::CARET); }
case ('%') { ret binop(rdr, token::PERCENT); }
case (?c) {
rdr.err(#fmt("unkown start of token: %d", c as int));
fail;
}
}
fail;
}
tag cmnt_style {
isolated; // No code on either side of each line of the comment
trailing; // Code exists to the left of the comment
mixed; // Code before /* foo */ and after the comment
blank_line; // Just a manual blank linke "\n\n", for layout
}
type cmnt = rec(cmnt_style style, vec[str] lines, uint pos);
fn read_to_eol(&reader rdr) -> str {
auto val = "";
while (rdr.curr() != '\n' && !rdr.is_eof()) {
str::push_char(val, rdr.curr());
rdr.bump();
}
ret val;
}
fn read_one_line_comment(&reader rdr) -> str {
auto val = read_to_eol(rdr);
assert (val.(0) == '/' as u8 && val.(1) == '/' as u8);
ret val;
}
fn consume_whitespace(&reader rdr) {
while (is_whitespace(rdr.curr()) && !rdr.is_eof()) { rdr.bump(); }
}
fn consume_non_eol_whitespace(&reader rdr) {
while (is_whitespace(rdr.curr()) && rdr.curr() != '\n' && !rdr.is_eof()) {
rdr.bump();
}
}
fn consume_whitespace_counting_blank_lines(&reader rdr,
&mutable vec[cmnt] comments) {
while (is_whitespace(rdr.curr()) && !rdr.is_eof()) {
if (rdr.curr() == '\n' && rdr.next() == '\n') {
log ">>> blank-line comment";
let vec[str] v = [];
comments += [rec(style=blank_line, lines=v,
pos=rdr.get_chpos())];
}
rdr.bump();
}
}
fn read_line_comments(&reader rdr, bool code_to_the_left) -> cmnt {
log ">>> line comments";
auto p = rdr.get_chpos();
let vec[str] lines = [];
while (rdr.curr() == '/' && rdr.next() == '/') {
auto line = read_one_line_comment(rdr);
log line;
lines += [line];
consume_non_eol_whitespace(rdr);
}
log "<<< line comments";
ret rec(style=if (code_to_the_left) { trailing } else { isolated },
lines=lines,
pos=p);
}
fn all_whitespace(&str s, uint begin, uint end) -> bool {
let uint i = begin;
while (i != end) {
if (!is_whitespace(s.(i) as char)) { ret false; }
i += 1u;
}
ret true;
}
fn trim_whitespace_prefix_and_push_line(&mutable vec[str] lines, &str s,
uint col) {
auto s1;
if (all_whitespace(s, 0u, col)) {
if (col < str::byte_len(s)) {
s1 = str::slice(s, col, str::byte_len(s));
} else { s1 = ""; }
} else { s1 = s; }
log "pushing line: " + s1;
lines += [s1];
}
fn read_block_comment(&reader rdr, bool code_to_the_left) -> cmnt {
log ">>> block comment";
auto p = rdr.get_chpos();
let vec[str] lines = [];
let uint col = rdr.get_col();
rdr.bump();
rdr.bump();
auto curr_line = "/*";
let int level = 1;
while (level > 0) {
log #fmt("=== block comment level %d", level);
if (rdr.is_eof()) { rdr.err("unterminated block comment"); fail; }
if (rdr.curr() == '\n') {
trim_whitespace_prefix_and_push_line(lines, curr_line, col);
curr_line = "";
rdr.bump();
} else {
str::push_char(curr_line, rdr.curr());
if (rdr.curr() == '/' && rdr.next() == '*') {
rdr.bump();
rdr.bump();
curr_line += "*";
level += 1;
} else {
if (rdr.curr() == '*' && rdr.next() == '/') {
rdr.bump();
rdr.bump();
curr_line += "/";
level -= 1;
} else { rdr.bump(); }
}
}
}
if (str::byte_len(curr_line) != 0u) {
trim_whitespace_prefix_and_push_line(lines, curr_line, col);
}
auto style = if (code_to_the_left) { trailing } else { isolated };
consume_non_eol_whitespace(rdr);
if (!rdr.is_eof() && rdr.curr() != '\n' && vec::len(lines) == 1u) {
style = mixed;
}
log "<<< block comment";
ret rec(style=style, lines=lines, pos=p);
}
fn peeking_at_comment(&reader rdr) -> bool {
ret rdr.curr() == '/' && rdr.next() == '/' ||
rdr.curr() == '/' && rdr.next() == '*';
}
fn consume_comment(&reader rdr, bool code_to_the_left,
&mutable vec[cmnt] comments) {
log ">>> consume comment";
if (rdr.curr() == '/' && rdr.next() == '/') {
vec::push[cmnt](comments, read_line_comments(rdr, code_to_the_left));
} else if (rdr.curr() == '/' && rdr.next() == '*') {
vec::push[cmnt](comments, read_block_comment(rdr, code_to_the_left));
} else { fail; }
log "<<< consume comment";
}
fn is_lit(&token::token t) -> bool {
ret alt (t) {
case (token::LIT_INT(_)) { true }
case (token::LIT_UINT(_)) { true }
case (token::LIT_MACH_INT(_, _)) { true }
case (token::LIT_FLOAT(_)) { true }
case (token::LIT_MACH_FLOAT(_, _)) { true }
case (token::LIT_STR(_)) { true }
case (token::LIT_CHAR(_)) { true }
case (token::LIT_BOOL(_)) { true }
case (_) { false }
}
}
type lit = rec(str lit, uint pos);
fn gather_comments_and_literals(&codemap::codemap cm, str path) ->
rec(vec[cmnt] cmnts, vec[lit] lits) {
auto srdr = io::file_reader(path);
auto itr = @interner::mk[str](str::hash, str::eq);
auto rdr = new_reader(cm, srdr, codemap::new_filemap(path, 0u), itr);
let vec[cmnt] comments = [];
let vec[lit] literals = [];
let bool first_read = true;
while (!rdr.is_eof()) {
while (true) {
auto code_to_the_left = !first_read;
consume_non_eol_whitespace(rdr);
if (rdr.curr() == '\n') {
code_to_the_left = false;
consume_whitespace_counting_blank_lines(rdr, comments);
}
while (peeking_at_comment(rdr)) {
consume_comment(rdr, code_to_the_left, comments);
consume_whitespace_counting_blank_lines(rdr, comments);
}
break;
}
auto tok = next_token(rdr);
if (is_lit(tok)) {
vec::push[lit](literals,
rec(lit=rdr.get_mark_str(),
pos=rdr.get_mark_chpos()));
}
log "tok: " + token::to_str(rdr, tok);
first_read = false;
}
ret rec(cmnts=comments, lits=literals);
}
//
// 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:
//

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,202 @@
import ast::ty_mach;
import ast::ty_mach_to_str;
import _std::new_str_hash;
import util::interner;
import std::int;
import std::uint;
import std::str;
type str_num = uint;
tag binop {
PLUS;
MINUS;
STAR;
SLASH;
PERCENT;
CARET;
AND;
OR;
LSL;
LSR;
ASR;
}
tag token {
/* Expression-operator symbols. */
EQ;
LT;
LE;
EQEQ;
NE;
GE;
GT;
ANDAND;
OROR;
NOT;
TILDE;
BINOP(binop);
BINOPEQ(binop);
/* Structural symbols */
AT;
DOT;
COMMA;
SEMI;
COLON;
MOD_SEP;
QUES;
RARROW;
SEND;
RECV;
LARROW;
DARROW;
LPAREN;
RPAREN;
LBRACKET;
RBRACKET;
LBRACE;
RBRACE;
POUND;
/* Literals */
LIT_INT(int);
LIT_UINT(uint);
LIT_MACH_INT(ty_mach, int);
LIT_FLOAT(str_num);
LIT_MACH_FLOAT(ty_mach, str_num);
LIT_STR(str_num);
LIT_CHAR(char);
LIT_BOOL(bool);
/* Name components */
IDENT(str_num, bool);
IDX(int);
UNDERSCORE;
BRACEQUOTE(str_num);
EOF;
}
fn binop_to_str(binop o) -> str {
alt (o) {
case (PLUS) { ret "+"; }
case (MINUS) { ret "-"; }
case (STAR) { ret "*"; }
case (SLASH) { ret "/"; }
case (PERCENT) { ret "%"; }
case (CARET) { ret "^"; }
case (AND) { ret "&"; }
case (OR) { ret "|"; }
case (LSL) { ret "<<"; }
case (LSR) { ret ">>"; }
case (ASR) { ret ">>>"; }
}
}
fn to_str(lexer::reader r, token t) -> str {
alt (t) {
case (EQ) { ret "="; }
case (LT) { ret "<"; }
case (LE) { ret "<="; }
case (EQEQ) { ret "=="; }
case (NE) { ret "!="; }
case (GE) { ret ">="; }
case (GT) { ret ">"; }
case (NOT) { ret "!"; }
case (TILDE) { ret "~"; }
case (OROR) { ret "||"; }
case (ANDAND) { ret "&&"; }
case (BINOP(?op)) { ret binop_to_str(op); }
case (BINOPEQ(?op)) { ret binop_to_str(op) + "="; }
case (
/* Structural symbols */
AT) {
ret "@";
}
case (DOT) { ret "."; }
case (COMMA) { ret ","; }
case (SEMI) { ret ";"; }
case (COLON) { ret ":"; }
case (MOD_SEP) { ret "::"; }
case (QUES) { ret "?"; }
case (RARROW) { ret "->"; }
case (SEND) { ret "<|"; }
case (RECV) { ret "|>"; }
case (LARROW) { ret "<-"; }
case (DARROW) { ret "<->"; }
case (LPAREN) { ret "("; }
case (RPAREN) { ret ")"; }
case (LBRACKET) { ret "["; }
case (RBRACKET) { ret "]"; }
case (LBRACE) { ret "{"; }
case (RBRACE) { ret "}"; }
case (POUND) { ret "#"; }
case (
/* Literals */
LIT_INT(?i)) {
ret int::to_str(i, 10u);
}
case (LIT_UINT(?u)) { ret uint::to_str(u, 10u); }
case (LIT_MACH_INT(?tm, ?i)) {
ret int::to_str(i, 10u) + "_" + ty_mach_to_str(tm);
}
case (LIT_MACH_FLOAT(?tm, ?s)) {
ret interner::get[str](*r.get_interner(), s) + "_" +
ty_mach_to_str(tm);
}
case (LIT_FLOAT(?s)) { ret interner::get[str](*r.get_interner(), s); }
case (LIT_STR(?s)) {
// FIXME: escape.
ret "\"" + interner::get[str](*r.get_interner(), s) + "\"";
}
case (LIT_CHAR(?c)) {
// FIXME: escape.
auto tmp = "'";
str::push_char(tmp, c);
str::push_byte(tmp, '\'' as u8);
ret tmp;
}
case (LIT_BOOL(?b)) { if (b) { ret "true"; } else { ret "false"; } }
case (
/* Name components */
IDENT(?s, _)) {
ret interner::get[str](*r.get_interner(), s);
}
case (IDX(?i)) { ret "_" + int::to_str(i, 10u); }
case (UNDERSCORE) { ret "_"; }
case (BRACEQUOTE(_)) { ret "<bracequote>"; }
case (EOF) { ret "<eof>"; }
}
}
pred can_begin_expr(token t) -> bool {
alt (t) {
case (LPAREN) { true }
case (LBRACE) { true }
case (IDENT(_,_)) { true }
case (UNDERSCORE) { true }
case (TILDE) { true }
case (LIT_INT(_)) { true }
case (LIT_UINT(_)) { true }
case (LIT_MACH_INT(_,_)) { true }
case (LIT_FLOAT(_)) { true }
case (LIT_MACH_FLOAT(_,_)) { true }
case (LIT_STR(_)) { true }
case (LIT_CHAR(_)) { true }
case (POUND) { true }
case (AT) { true }
case (_) { false }
}
}
// Local Variables:
// 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:

532
src/comp/syntax/print/pp.rs Normal file
View file

@ -0,0 +1,532 @@
import std::io;
import std::vec;
import std::str;
/*
* This pretty-printer is a direct reimplementation of Philip Karlton's
* Mesa pretty-printer, as described in appendix A of
*
* STAN-CS-79-770: "Pretty Printing", by Derek C. Oppen.
* Stanford Department of Computer Science, 1979.
*
* The algorithm's aim is to break a stream into as few lines as possible
* while respecting the indentation-consistency requirements of the enclosing
* block, and avoiding breaking at silly places on block boundaries, for
* example, between "x" and ")" in "x)".
*
* I am implementing this algorithm because it comes with 20 pages of
* documentation explaining its theory, and because it addresses the set of
* concerns I've seen other pretty-printers fall down on. Weirdly. Even though
* it's 32 years old and not written in Haskell. What can I say?
*
* Despite some redundancies and quirks in the way it's implemented in that
* paper, I've opted to keep the implementation here as similar as I can,
* changing only what was blatantly wrong, a typo, or sufficiently
* non-idiomatic rust that it really stuck out.
*
* In particular you'll see a certain amount of churn related to INTEGER vs.
* CARDINAL in the Mesa implementation. Mesa apparently interconverts the two
* somewhat readily? In any case, I've used uint for indices-in-buffers and
* ints for character-sizes-and-indentation-offsets. This respects the need
* for ints to "go negative" while carrying a pending-calculation balance, and
* helps differentiate all the numbers flying around internally (slightly).
*
* I also inverted the indentation arithmetic used in the print stack, since
* the Mesa implementation (somewhat randomly) stores the offset on the print
* stack in terms of margin-col rather than col itself. I store col.
*
* I also implemented a small change in the STRING token, in that I store an
* explicit length for the string. For most tokens this is just the length of
* the accompanying string. But it's necessary to permit it to differ, for
* encoding things that are supposed to "go on their own line" -- certain
* classes of comment and blank-line -- where relying on adjacent
* hardbreak-like BREAK tokens with long blankness indication doesn't actually
* work. To see why, consider when there is a "thing that should be on its own
* line" between two long blocks, say functions. If you put a hardbreak after
* each function (or before each) and the breaking algorithm decides to break
* there anyways (because the functions themselves are long) you wind up with
* extra blank lines. If you don't put hardbreaks you can wind up with the
* "thing which should be on its own line" not getting its own line in the
* rare case of "really small functions" or such. This re-occurs with comments
* and explicit blank lines. So in those cases we use a string with a payload
* we want isolated to a line and an explicit length that's huge, surrounded
* by two zero-length breaks. The algorithm will try its best to fit it on a
* line (which it can't) and so naturally place the content on its own line to
* avoid combining it with other lines and making matters even worse.
*/
tag breaks { consistent; inconsistent; }
type break_t = rec(int offset, int blank_space);
type begin_t = rec(int offset, breaks breaks);
tag token { STRING(str, int); BREAK(break_t); BEGIN(begin_t); END; EOF; }
fn tok_str(token t) -> str {
alt (t) {
case (STRING(?s, ?len)) { ret #fmt("STR(%s,%d)", s, len); }
case (BREAK(_)) { ret "BREAK"; }
case (BEGIN(_)) { ret "BEGIN"; }
case (END) { ret "END"; }
case (EOF) { ret "EOF"; }
}
}
fn buf_str(vec[mutable token] toks, vec[mutable int] szs, uint left,
uint right, uint lim) -> str {
auto n = vec::len(toks);
assert (n == vec::len(szs));
auto i = left;
auto L = lim;
auto s = "[";
while (i != right && L != 0u) {
L -= 1u;
if (i != left) { s += ", "; }
s += #fmt("%d=%s", szs.(i), tok_str(toks.(i)));
i += 1u;
i %= n;
}
s += "]";
ret s;
}
tag print_stack_break { fits; broken(breaks); }
type print_stack_elt = rec(int offset, print_stack_break pbreak);
const int size_infinity = 0xffff;
fn mk_printer(io::writer out, uint linewidth) -> printer {
// Yes 3, it makes the ring buffers big enough to never
// fall behind.
let uint n = 3u * linewidth;
log #fmt("mk_printer %u", linewidth);
let vec[mutable token] token = vec::init_elt_mut(EOF, n);
let vec[mutable int] size = vec::init_elt_mut(0, n);
let vec[mutable uint] scan_stack = vec::init_elt_mut(0u, n);
let vec[print_stack_elt] print_stack = [];
ret printer(out, n, linewidth as int, // margin
linewidth as int, // space
0u, // left
0u, // right
token, size, 0, // left_total
0, // right_total
scan_stack, true, // scan_stack_empty
0u, // top
0u, // bottom
print_stack, 0);
}
/*
* In case you do not have the paper, here is an explanation of what's going
* on.
*
* There is a stream of input tokens flowing through this printer.
*
* The printer buffers up to 3N tokens inside itself, where N is linewidth.
* Yes, linewidth is chars and tokens are multi-char, but in the worst
* case every token worth buffering is 1 char long, so it's ok.
*
* Tokens are STRING, BREAK, and BEGIN/END to delimit blocks.
*
* BEGIN tokens can carry an offset, saying "how far to indent when you break
* inside here", as well as a flag indicating "consistent" or "inconsistent"
* breaking. Consistent breaking means that after the first break, no attempt
* will be made to flow subsequent breaks together onto lines. Inconsistent
* is the opposite. Inconsistent breaking example would be, say:
*
* foo(hello, there, good, friends)
*
* breaking inconsistently to become
*
* foo(hello, there
* good, friends);
*
* whereas a consistent breaking would yield:
*
* foo(hello,
* there
* good,
* friends);
*
* That is, in the consistent-break blocks we value vertical alignment
* more than the ability to cram stuff onto a line. But in all cases if it
* can make a block a one-liner, it'll do so.
*
* Carrying on with high-level logic:
*
* The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and
* 'right' indices denote the active portion of the ring buffer as well as
* describing hypothetical points-in-the-infinite-stream at most 3N tokens
* apart (i.e. "not wrapped to ring-buffer boundaries"). The paper will switch
* between using 'left' and 'right' terms to denote the wrapepd-to-ring-buffer
* and point-in-infinite-stream senses freely.
*
* There is a parallel ring buffer, 'size', that holds the calculated size of
* each token. Why calculated? Because for BEGIN/END pairs, the "size"
* includes everything betwen the pair. That is, the "size" of BEGIN is
* actually the sum of the sizes of everything between BEGIN and the paired
* END that follows. Since that is arbitrarily far in the future, 'size' is
* being rewritten regularly while the printer runs; in fact most of the
* machinery is here to work out 'size' entries on the fly (and give up when
* they're so obviously over-long that "infinity" is a good enough
* approximation for purposes of line breaking).
*
* The "input side" of the printer is managed as an abstract process called
* SCAN, which uses 'scan_stack', 'scan_stack_empty', 'top' and 'bottom', to
* manage calculating 'size'. SCAN is, in other words, the process of
* calculating 'size' entries.
*
* The "output side" of the printer is managed by an abstract process called
* PRINT, which uses 'print_stack', 'margin' and 'space' to figure out what to
* do with each token/size pair it consumes as it goes. It's trying to consume
* the entire buffered window, but can't output anything until the size is >=
* 0 (sizes are set to negative while they're pending calculation).
*
* So SCAN takeks input and buffers tokens and pending calculations, while
* PRINT gobbles up completed calculations and tokens from the buffer. The
* theory is that the two can never get more than 3N tokens apart, because
* once there's "obviously" too much data to fit on a line, in a size
* calculation, SCAN will write "infinity" to the size and let PRINT consume
* it.
*
* In this implementation (following the paper, again) the SCAN process is
* the method called 'pretty_print', and the 'PRINT' process is the method
* called 'print'.
*/
obj printer(io::writer out,
uint buf_len,
mutable int margin, // width of lines we're constrained to
mutable int space, // number of spaces left on line
mutable uint left, // index of left side of input stream
mutable uint right, // index of right side of input stream
mutable vec[mutable token] token,
// ring-buffr stream goes through
mutable vec[mutable int] size, // ring-buffer of calculated sizes
mutable int left_total, // running size of stream "...left"
mutable int right_total, // running size of stream "...right"
// pseudo-stack, really a ring too. Holds the
// primary-ring-buffers index of the BEGIN that started the
// current block, possibly with the most recent BREAK after that
// BEGIN (if there is any) on top of it. Stuff is flushed off the
// bottom as it becomes irrelevant due to the primary ring-buffer
// advancing.
mutable vec[mutable uint] scan_stack,
mutable bool scan_stack_empty, // top==bottom disambiguator
mutable uint top, // index of top of scan_stack
mutable uint bottom, // index of bottom of scan_stack
// stack of blocks-in-progress being flushed by print
mutable vec[print_stack_elt] print_stack,
// buffered indentation to avoid writing trailing whitespace
mutable int pending_indentation) {
fn last_token() -> token {
ret token.(right);
}
fn pretty_print(token t) {
log #fmt("pp [%u,%u]", left, right);
alt (t) {
case (EOF) {
if (!scan_stack_empty) {
self.check_stack(0);
self.advance_left(token.(left), size.(left));
}
self.indent(0);
}
case (BEGIN(?b)) {
if (scan_stack_empty) {
left_total = 1;
right_total = 1;
left = 0u;
right = 0u;
} else { self.advance_right(); }
log #fmt("pp BEGIN/buffer [%u,%u]", left, right);
token.(right) = t;
size.(right) = -right_total;
self.scan_push(right);
}
case (END) {
if (scan_stack_empty) {
log #fmt("pp END/print [%u,%u]", left, right);
self.print(t, 0);
} else {
log #fmt("pp END/buffer [%u,%u]", left, right);
self.advance_right();
token.(right) = t;
size.(right) = -1;
self.scan_push(right);
}
}
case (BREAK(?b)) {
if (scan_stack_empty) {
left_total = 1;
right_total = 1;
left = 0u;
right = 0u;
} else { self.advance_right(); }
log #fmt("pp BREAK/buffer [%u,%u]", left, right);
self.check_stack(0);
self.scan_push(right);
token.(right) = t;
size.(right) = -right_total;
right_total += b.blank_space;
}
case (STRING(?s, ?len)) {
if (scan_stack_empty) {
log #fmt("pp STRING/print [%u,%u]", left, right);
self.print(t, len);
} else {
log #fmt("pp STRING/buffer [%u,%u]", left, right);
self.advance_right();
token.(right) = t;
size.(right) = len;
right_total += len;
self.check_stream();
}
}
}
}
fn check_stream() {
log #fmt("check_stream [%u, %u] with left_total=%d, right_total=%d",
left, right, left_total, right_total);
if (right_total - left_total > space) {
log #fmt("scan window is %d, longer than space on line (%d)",
right_total - left_total, space);
if (!scan_stack_empty) {
if (left == scan_stack.(bottom)) {
log #fmt("setting %u to infinity and popping", left);
size.(self.scan_pop_bottom()) = size_infinity;
}
}
self.advance_left(token.(left), size.(left));
if (left != right) { self.check_stream(); }
}
}
fn scan_push(uint x) {
log #fmt("scan_push %u", x);
if (scan_stack_empty) {
scan_stack_empty = false;
} else { top += 1u; top %= buf_len; assert (top != bottom); }
scan_stack.(top) = x;
}
fn scan_pop() -> uint {
assert (!scan_stack_empty);
auto x = scan_stack.(top);
if (top == bottom) {
scan_stack_empty = true;
} else { top += buf_len - 1u; top %= buf_len; }
ret x;
}
fn scan_top() -> uint {
assert (!scan_stack_empty);
ret scan_stack.(top);
}
fn scan_pop_bottom() -> uint {
assert (!scan_stack_empty);
auto x = scan_stack.(bottom);
if (top == bottom) {
scan_stack_empty = true;
} else { bottom += 1u; bottom %= buf_len; }
ret x;
}
fn advance_right() {
right += 1u;
right %= buf_len;
assert (right != left);
}
fn advance_left(token x, int L) {
log #fmt("advnce_left [%u,%u], sizeof(%u)=%d", left, right, left, L);
if (L >= 0) {
self.print(x, L);
alt (x) {
case (BREAK(?b)) { left_total += b.blank_space; }
case (STRING(_, ?len)) {
assert (len == L);
left_total += len;
}
case (_) { }
}
if (left != right) {
left += 1u;
left %= buf_len;
self.advance_left(token.(left), size.(left));
}
}
}
fn check_stack(int k) {
if (!scan_stack_empty) {
auto x = self.scan_top();
alt (token.(x)) {
case (BEGIN(?b)) {
if (k > 0) {
size.(self.scan_pop()) = size.(x) + right_total;
self.check_stack(k - 1);
}
}
case (END) {
// paper says + not =, but that makes no sense.
size.(self.scan_pop()) = 1;
self.check_stack(k + 1);
}
case (_) {
size.(self.scan_pop()) = size.(x) + right_total;
if (k > 0) { self.check_stack(k); }
}
}
}
}
fn print_newline(int amount) {
log #fmt("NEWLINE %d", amount);
out.write_str("\n");
pending_indentation = 0;
self.indent(amount);
}
fn indent(int amount) {
log #fmt("INDENT %d", amount);
pending_indentation += amount;
}
fn top() -> print_stack_elt {
auto n = vec::len(print_stack);
let print_stack_elt top = rec(offset=0, pbreak=broken(inconsistent));
if (n != 0u) { top = print_stack.(n - 1u); }
ret top;
}
fn write_str(str s) {
while (pending_indentation > 0) {
out.write_str(" ");
pending_indentation -= 1;
}
out.write_str(s);
}
fn print(token x, int L) {
log #fmt("print %s %d (remaining line space=%d)", tok_str(x), L,
space);
log buf_str(token, size, left, right, 6u);
alt (x) {
case (BEGIN(?b)) {
if (L > space) {
auto col = margin - space + b.offset;
log #fmt("print BEGIN -> push broken block at col %d",
col);
vec::push(print_stack,
rec(offset=col, pbreak=broken(b.breaks)));
} else {
log "print BEGIN -> push fitting block";
vec::push(print_stack, rec(offset=0, pbreak=fits));
}
}
case (END) {
log "print END -> pop END";
assert (vec::len(print_stack) != 0u);
vec::pop(print_stack);
}
case (BREAK(?b)) {
auto top = self.top();
alt (top.pbreak) {
case (fits) {
log "print BREAK in fitting block";
space -= b.blank_space;
self.indent(b.blank_space);
}
case (broken(consistent)) {
log "print BREAK in consistent block";
self.print_newline(top.offset + b.offset);
space = margin - (top.offset + b.offset);
}
case (broken(inconsistent)) {
if (L > space) {
log "print BREAK w/ newline in inconsistent";
self.print_newline(top.offset + b.offset);
space = margin - (top.offset + b.offset);
} else {
log "print BREAK w/o newline in inconsistent";
self.indent(b.blank_space);
space -= b.blank_space;
}
}
}
}
case (STRING(?s, ?len)) {
log "print STRING";
assert (L == len);
// assert L <= space;
space -= len;
self.write_str(s);
}
case (EOF) {
// EOF should never get here.
fail;
}
}
}
}
// Convenience functions to talk to the printer.
fn box(printer p, uint indent, breaks b) {
p.pretty_print(BEGIN(rec(offset=indent as int, breaks=b)));
}
fn ibox(printer p, uint indent) { box(p, indent, inconsistent); }
fn cbox(printer p, uint indent) { box(p, indent, consistent); }
fn break_offset(printer p, uint n, int off) {
p.pretty_print(BREAK(rec(offset=off, blank_space=n as int)));
}
fn end(printer p) { p.pretty_print(END); }
fn eof(printer p) { p.pretty_print(EOF); }
fn word(printer p, str wrd) {
p.pretty_print(STRING(wrd, str::char_len(wrd) as int));
}
fn huge_word(printer p, str wrd) {
p.pretty_print(STRING(wrd, size_infinity));
}
fn zero_word(printer p, str wrd) { p.pretty_print(STRING(wrd, 0)); }
fn spaces(printer p, uint n) { break_offset(p, n, 0); }
fn zerobreak(printer p) { spaces(p, 0u); }
fn space(printer p) { spaces(p, 1u); }
fn hardbreak(printer p) { spaces(p, size_infinity as uint); }
fn hardbreak_tok() -> token {
ret BREAK(rec(offset=0, blank_space=size_infinity));
}
//
// 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:
//

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
// An "interner" is a data structure that associates values with uint tags and
// allows bidirectional lookup; i.e. given a value, one can easily find the
// type, and vice versa.
import std::vec;
import std::map;
import std::map::hashmap;
import std::map::hashfn;
import std::map::eqfn;
import std::option;
import std::option::none;
import std::option::some;
type interner[T] =
rec(hashmap[T, uint] map,
mutable vec[T] vect,
hashfn[T] hasher,
eqfn[T] eqer);
fn mk[T](hashfn[T] hasher, eqfn[T] eqer) -> interner[T] {
auto m = map::mk_hashmap[T, uint](hasher, eqer);
let vec[T] vect = [];
ret rec(map=m, mutable vect=vect, hasher=hasher, eqer=eqer);
}
fn intern[T](&interner[T] itr, &T val) -> uint {
alt (itr.map.find(val)) {
case (some(?idx)) { ret idx; }
case (none) {
auto new_idx = vec::len[T](itr.vect);
itr.map.insert(val, new_idx);
itr.vect += [val];
ret new_idx;
}
}
}
fn get[T](&interner[T] itr, uint idx) -> T { ret itr.vect.(idx); }

407
src/comp/syntax/visit.rs Normal file
View file

@ -0,0 +1,407 @@
import ast::*;
import std::option;
import std::option::some;
import std::option::none;
import codemap::span;
// Context-passing AST walker. Each overridden visit method has full control
// over what happens with its node, it can do its own traversal of the node's
// children (potentially passing in different contexts to each), call
// visit::visit_* to apply the default traversal algorithm (again, it can
// override the context), or prevent deeper traversal by doing nothing.
// Lots of redundant indirection and refcounting. Our typesystem doesn't do
// circular types, so the visitor record can not hold functions that take
// visitors. A vt tag is used to break the cycle.
tag vt[E] { vtor(visitor[E]); }
fn vt[E](&vt[E] x) -> visitor[E] { alt (x) { case (vtor(?v)) { ret v; } } }
type visitor[E] =
@rec(fn(&_mod, &span, &E, &vt[E]) visit_mod,
fn(&@view_item, &E, &vt[E]) visit_view_item,
fn(&@native_item, &E, &vt[E]) visit_native_item,
fn(&@item, &E, &vt[E]) visit_item,
fn(&@local, &E, &vt[E]) visit_local,
fn(&block, &E, &vt[E]) visit_block,
fn(&@stmt, &E, &vt[E]) visit_stmt,
fn(&arm, &E, &vt[E]) visit_arm,
fn(&@pat, &E, &vt[E]) visit_pat,
fn(&@decl, &E, &vt[E]) visit_decl,
fn(&@expr, &E, &vt[E]) visit_expr,
fn(&@ty, &E, &vt[E]) visit_ty,
fn(&@constr, &E, &vt[E]) visit_constr,
fn(&_fn, &vec[ty_param], &span, &fn_ident, node_id, &E, &vt[E])
visit_fn);
fn default_visitor[E]() -> visitor[E] {
ret @rec(visit_mod=bind visit_mod[E](_, _, _, _),
visit_view_item=bind visit_view_item[E](_, _, _),
visit_native_item=bind visit_native_item[E](_, _, _),
visit_item=bind visit_item[E](_, _, _),
visit_local=bind visit_local[E](_, _, _),
visit_block=bind visit_block[E](_, _, _),
visit_stmt=bind visit_stmt[E](_, _, _),
visit_arm=bind visit_arm[E](_, _, _),
visit_pat=bind visit_pat[E](_, _, _),
visit_decl=bind visit_decl[E](_, _, _),
visit_expr=bind visit_expr[E](_, _, _),
visit_ty=bind visit_ty[E](_, _, _),
visit_constr=bind visit_constr[E](_, _, _),
visit_fn=bind visit_fn[E](_, _, _, _, _, _, _));
}
fn visit_crate[E](&crate c, &E e, &vt[E] v) {
vt(v).visit_mod(c.node.module, c.span, e, v);
}
fn visit_crate_directive[E](&@crate_directive cd, &E e, &vt[E] v) {
alt (cd.node) {
case (cdir_src_mod(_, _, _)) { }
case (cdir_dir_mod(_, _, ?cdirs, _)) {
for (@crate_directive cdir in cdirs) {
visit_crate_directive(cdir, e, v);
}
}
case (cdir_view_item(?vi)) { vt(v).visit_view_item(vi, e, v); }
case (cdir_syntax(_)) { }
case (cdir_auth(_, _)) { }
}
}
fn visit_mod[E](&_mod m, &span sp, &E e, &vt[E] v) {
for (@view_item vi in m.view_items) { vt(v).visit_view_item(vi, e, v); }
for (@item i in m.items) { vt(v).visit_item(i, e, v); }
}
fn visit_view_item[E](&@view_item vi, &E e, &vt[E] v) { }
fn visit_local[E](&@local loc, &E e, &vt[E] v) {
alt (loc.node.ty) {
case (none) { }
case (some(?t)) { vt(v).visit_ty(t, e, v); }
}
alt (loc.node.init) {
case (none) { }
case (some(?i)) { vt(v).visit_expr(i.expr, e, v); }
}
}
fn visit_item[E](&@item i, &E e, &vt[E] v) {
alt (i.node) {
case (item_const(?t, ?ex)) {
vt(v).visit_ty(t, e, v);
vt(v).visit_expr(ex, e, v);
}
case (item_fn(?f, ?tp)) {
vt(v).visit_fn(f, tp, i.span, some(i.ident), i.id, e, v);
}
case (item_mod(?m)) { vt(v).visit_mod(m, i.span, e, v); }
case (item_native_mod(?nm)) {
for (@view_item vi in nm.view_items) {
vt(v).visit_view_item(vi, e, v);
}
for (@native_item ni in nm.items) {
vt(v).visit_native_item(ni, e, v);
}
}
case (item_ty(?t, _)) { vt(v).visit_ty(t, e, v); }
case (item_res(?f, ?dtor_id, ?tps, _)) {
vt(v).visit_fn(f, tps, i.span, some(i.ident), dtor_id, e, v);
}
case (item_tag(?variants, _)) {
for (variant vr in variants) {
for (variant_arg va in vr.node.args) {
vt(v).visit_ty(va.ty, e, v);
}
}
}
case (item_obj(?ob, _, _)) {
for (obj_field f in ob.fields) { vt(v).visit_ty(f.ty, e, v); }
for (@method m in ob.methods) {
vt(v).visit_fn(m.node.meth, [], m.span, some(m.node.ident),
m.node.id, e, v);
}
alt (ob.dtor) {
case (none) { }
case (some(?m)) {
vt(v).visit_fn(m.node.meth, [], m.span,
some(m.node.ident),
m.node.id, e, v);
}
}
}
}
}
fn visit_ty[E](&@ty t, &E e, &vt[E] v) {
alt (t.node) {
case (ty_nil) { /* no-op */ }
case (ty_bot) { /* no-op */ }
case (ty_bool) { /* no-op */ }
case (ty_int) { /* no-op */ }
case (ty_float) { /* no-op */ }
case (ty_uint) { /* no-op */ }
case (ty_machine(_)) { /* no-op */ }
case (ty_char) { /* no-op */ }
case (ty_str) { /* no-op */ }
case (ty_istr) { /* no-op */ }
case (ty_box(?mt)) { vt(v).visit_ty(mt.ty, e, v); }
case (ty_vec(?mt)) { vt(v).visit_ty(mt.ty, e, v); }
case (ty_ivec(?mt)) { vt(v).visit_ty(mt.ty, e, v); }
case (ty_ptr(?mt)) { vt(v).visit_ty(mt.ty, e, v); }
case (ty_port(?t)) { vt(v).visit_ty(t, e, v); }
case (ty_chan(?t)) { vt(v).visit_ty(t, e, v); }
case (ty_task) { /* no-op */ }
case (ty_tup(?mts)) {
for (mt mt in mts) { vt(v).visit_ty(mt.ty, e, v); }
}
case (ty_rec(?flds)) {
for (ty_field f in flds) { vt(v).visit_ty(f.node.mt.ty, e, v); }
}
case (ty_fn(_, ?args, ?out, _, ?constrs)) {
for (ty_arg a in args) { vt(v).visit_ty(a.node.ty, e, v); }
for (@constr c in constrs) { vt(v).visit_constr(c, e, v); }
vt(v).visit_ty(out, e, v);
}
case (ty_obj(?tmeths)) {
for (ty_method m in tmeths) {
for (ty_arg a in m.node.inputs) {
vt(v).visit_ty(a.node.ty, e, v);
}
vt(v).visit_ty(m.node.output, e, v);
}
}
case (ty_path(?p, _)) {
for (@ty tp in p.node.types) { vt(v).visit_ty(tp, e, v); }
}
case (ty_type) { /* no-op */ }
case (ty_constr(?t, _)) { vt(v).visit_ty(t, e, v); }
}
}
fn visit_constr[E](&@constr c, &E e, &vt[E] v) {
// default
}
fn visit_pat[E](&@pat p, &E e, &vt[E] v) {
alt (p.node) {
case (pat_tag(?path, ?children)) {
for (@ty tp in path.node.types) { vt(v).visit_ty(tp, e, v); }
for (@pat child in children) { vt(v).visit_pat(child, e, v); }
}
case (_) { }
}
}
fn visit_native_item[E](&@native_item ni, &E e, &vt[E] v) {
alt (ni.node) {
case (native_item_fn(_, ?fd, _)) { visit_fn_decl(fd, e, v); }
case (native_item_ty) { }
}
}
fn visit_fn_decl[E](&fn_decl fd, &E e, &vt[E] v) {
for (arg a in fd.inputs) { vt(v).visit_ty(a.ty, e, v); }
for (@constr c in fd.constraints) { vt(v).visit_constr(c, e, v); }
vt(v).visit_ty(fd.output, e, v);
}
fn visit_fn[E](&_fn f, &vec[ty_param] tp, &span sp, &fn_ident i,
node_id id, &E e, &vt[E] v) {
visit_fn_decl(f.decl, e, v);
vt(v).visit_block(f.body, e, v);
}
fn visit_block[E](&block b, &E e, &vt[E] v) {
for (@stmt s in b.node.stmts) { vt(v).visit_stmt(s, e, v); }
visit_expr_opt(b.node.expr, e, v);
}
fn visit_stmt[E](&@stmt s, &E e, &vt[E] v) {
alt (s.node) {
case (stmt_decl(?d, _)) { vt(v).visit_decl(d, e, v); }
case (stmt_expr(?ex, _)) { vt(v).visit_expr(ex, e, v); }
case (stmt_crate_directive(?cd)) { visit_crate_directive(cd, e, v); }
}
}
fn visit_decl[E](&@decl d, &E e, &vt[E] v) {
alt (d.node) {
case (decl_local(?loc)) {
vt(v).visit_local(loc, e, v);
}
case (decl_item(?it)) { vt(v).visit_item(it, e, v); }
}
}
fn visit_expr_opt[E](option::t[@expr] eo, &E e, &vt[E] v) {
alt (eo) {
case (none) { }
case (some(?ex)) { vt(v).visit_expr(ex, e, v); }
}
}
fn visit_exprs[E](vec[@expr] exprs, &E e, &vt[E] v) {
for (@expr ex in exprs) { vt(v).visit_expr(ex, e, v); }
}
fn visit_expr[E](&@expr ex, &E e, &vt[E] v) {
alt (ex.node) {
case (expr_vec(?es, _, _)) { visit_exprs(es, e, v); }
case (expr_tup(?elts)) {
for (elt el in elts) { vt(v).visit_expr(el.expr, e, v); }
}
case (expr_rec(?flds, ?base)) {
for (field f in flds) { vt(v).visit_expr(f.node.expr, e, v); }
visit_expr_opt(base, e, v);
}
case (expr_call(?callee, ?args)) {
vt(v).visit_expr(callee, e, v);
visit_exprs(args, e, v);
}
case (expr_self_method(_)) { }
case (expr_bind(?callee, ?args)) {
vt(v).visit_expr(callee, e, v);
for (option::t[@expr] eo in args) { visit_expr_opt(eo, e, v); }
}
case (expr_spawn(_, _, ?callee, ?args)) {
vt(v).visit_expr(callee, e, v);
visit_exprs(args, e, v);
}
case (expr_binary(_, ?a, ?b)) {
vt(v).visit_expr(a, e, v);
vt(v).visit_expr(b, e, v);
}
case (expr_unary(_, ?a)) { vt(v).visit_expr(a, e, v); }
case (expr_lit(_)) { }
case (expr_cast(?x, ?t)) {
vt(v).visit_expr(x, e, v);
vt(v).visit_ty(t, e, v);
}
case (expr_if(?x, ?b, ?eo)) {
vt(v).visit_expr(x, e, v);
vt(v).visit_block(b, e, v);
visit_expr_opt(eo, e, v);
}
case (expr_if_check(?x, ?b, ?eo)) {
vt(v).visit_expr(x, e, v);
vt(v).visit_block(b, e, v);
visit_expr_opt(eo, e, v);
}
case (expr_ternary(?c, ?t, ?el)) {
vt(v).visit_expr(c, e, v);
vt(v).visit_expr(t, e, v);
vt(v).visit_expr(el, e, v);
}
case (expr_while(?x, ?b)) {
vt(v).visit_expr(x, e, v);
vt(v).visit_block(b, e, v);
}
case (expr_for(?dcl, ?x, ?b)) {
vt(v).visit_local(dcl, e, v);
vt(v).visit_expr(x, e, v);
vt(v).visit_block(b, e, v);
}
case (expr_for_each(?dcl, ?x, ?b)) {
vt(v).visit_local(dcl, e, v);
vt(v).visit_expr(x, e, v);
vt(v).visit_block(b, e, v);
}
case (expr_do_while(?b, ?x)) {
vt(v).visit_block(b, e, v);
vt(v).visit_expr(x, e, v);
}
case (expr_alt(?x, ?arms)) {
vt(v).visit_expr(x, e, v);
for (arm a in arms) { vt(v).visit_arm(a, e, v); }
}
case (expr_fn(?f)) {
vt(v).visit_fn(f, [], ex.span, none, ex.id, e, v);
}
case (expr_block(?b)) { vt(v).visit_block(b, e, v); }
case (expr_assign(?a, ?b)) {
vt(v).visit_expr(b, e, v);
vt(v).visit_expr(a, e, v);
}
case (expr_move(?a, ?b)) {
vt(v).visit_expr(b, e, v);
vt(v).visit_expr(a, e, v);
}
case (expr_swap(?a, ?b)) {
vt(v).visit_expr(a, e, v);
vt(v).visit_expr(b, e, v);
}
case (expr_assign_op(_, ?a, ?b)) {
vt(v).visit_expr(b, e, v);
vt(v).visit_expr(a, e, v);
}
case (expr_send(?a, ?b)) {
vt(v).visit_expr(a, e, v);
vt(v).visit_expr(b, e, v);
}
case (expr_recv(?a, ?b)) {
vt(v).visit_expr(a, e, v);
vt(v).visit_expr(b, e, v);
}
case (expr_field(?x, _)) { vt(v).visit_expr(x, e, v); }
case (expr_index(?a, ?b)) {
vt(v).visit_expr(a, e, v);
vt(v).visit_expr(b, e, v);
}
case (expr_path(?p)) {
for (@ty tp in p.node.types) { vt(v).visit_ty(tp, e, v); }
}
case (expr_ext(_, _, _, ?expansion)) {
vt(v).visit_expr(expansion, e, v);
}
case (expr_fail(?eo)) {
visit_expr_opt(eo, e, v);
}
case (expr_break) { }
case (expr_cont) { }
case (expr_ret(?eo)) { visit_expr_opt(eo, e, v); }
case (expr_put(?eo)) { visit_expr_opt(eo, e, v); }
case (expr_be(?x)) { vt(v).visit_expr(x, e, v); }
case (expr_log(_, ?x)) { vt(v).visit_expr(x, e, v); }
case (expr_check(_, ?x)) { vt(v).visit_expr(x, e, v); }
case (expr_assert(?x)) { vt(v).visit_expr(x, e, v); }
case (expr_port(_)) { }
case (expr_chan(?x)) { vt(v).visit_expr(x, e, v); }
case (expr_anon_obj(?anon_obj, _, _)) {
alt (anon_obj.fields) {
case (none) { }
case (some(?fields)) {
for (anon_obj_field f in fields) {
vt(v).visit_ty(f.ty, e, v);
vt(v).visit_expr(f.expr, e, v);
}
}
}
alt (anon_obj.with_obj) {
case (none) { }
case (some(?ex)) { vt(v).visit_expr(ex, e, v); }
}
for (@method m in anon_obj.methods) {
vt(v).visit_fn(m.node.meth, [], m.span, some(m.node.ident),
m.node.id, e, v);
}
}
}
}
fn visit_arm[E](&arm a, &E e, &vt[E] v) {
vt(v).visit_pat(a.pat, e, v);
vt(v).visit_block(a.block, e, v);
}
// 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:

495
src/comp/syntax/walk.rs Normal file
View file

@ -0,0 +1,495 @@
import ast;
import ast::ty_param;
import ast::respan;
import std::option;
import std::option::some;
import std::option::none;
import codemap::span;
type ast_visitor =
rec(fn() -> bool keep_going,
fn() -> bool want_crate_directives,
fn(&ast::crate) visit_crate_pre,
fn(&ast::crate) visit_crate_post,
fn(&@ast::crate_directive) visit_crate_directive_pre,
fn(&@ast::crate_directive) visit_crate_directive_post,
fn(&@ast::view_item) visit_view_item_pre,
fn(&@ast::view_item) visit_view_item_post,
fn(&@ast::native_item) visit_native_item_pre,
fn(&@ast::native_item) visit_native_item_post,
fn(&@ast::item) visit_item_pre,
fn(&@ast::item) visit_item_post,
fn(&@ast::method) visit_method_pre,
fn(&@ast::method) visit_method_post,
fn(&ast::block) visit_block_pre,
fn(&ast::block) visit_block_post,
fn(&@ast::stmt) visit_stmt_pre,
fn(&@ast::stmt) visit_stmt_post,
fn(&ast::arm) visit_arm_pre,
fn(&ast::arm) visit_arm_post,
fn(&@ast::pat) visit_pat_pre,
fn(&@ast::pat) visit_pat_post,
fn(&@ast::decl) visit_decl_pre,
fn(&@ast::decl) visit_decl_post,
fn(&@ast::local) visit_local_pre,
fn(&@ast::local) visit_local_post,
fn(&@ast::expr) visit_expr_pre,
fn(&@ast::expr) visit_expr_post,
fn(&@ast::ty) visit_ty_pre,
fn(&@ast::ty) visit_ty_post,
fn(&@ast::constr) visit_constr,
fn(&ast::_fn, &vec[ast::ty_param], &span, &ast::fn_ident,
ast::node_id) visit_fn_pre,
fn(&ast::_fn, &vec[ast::ty_param], &span, &ast::fn_ident,
ast::node_id) visit_fn_post);
fn walk_crate(&ast_visitor v, &ast::crate c) {
if (!v.keep_going()) { ret; }
v.visit_crate_pre(c);
walk_mod(v, c.node.module);
v.visit_crate_post(c);
}
fn walk_crate_directive(&ast_visitor v, @ast::crate_directive cd) {
if (!v.keep_going()) { ret; }
if (!v.want_crate_directives()) { ret; }
v.visit_crate_directive_pre(cd);
alt (cd.node) {
case (ast::cdir_src_mod(_, _, _)) { }
case (ast::cdir_dir_mod(_, _, ?cdirs, _)) {
for (@ast::crate_directive cdir in cdirs) {
walk_crate_directive(v, cdir);
}
}
case (ast::cdir_view_item(?vi)) { walk_view_item(v, vi); }
case (ast::cdir_syntax(_)) { }
case (ast::cdir_auth(_, _)) { }
}
v.visit_crate_directive_post(cd);
}
fn walk_mod(&ast_visitor v, &ast::_mod m) {
if (!v.keep_going()) { ret; }
for (@ast::view_item vi in m.view_items) { walk_view_item(v, vi); }
for (@ast::item i in m.items) { walk_item(v, i); }
}
fn walk_view_item(&ast_visitor v, @ast::view_item vi) {
if (!v.keep_going()) { ret; }
v.visit_view_item_pre(vi);
v.visit_view_item_post(vi);
}
fn walk_local(&ast_visitor v, @ast::local loc) {
v.visit_local_pre(loc);
alt (loc.node.ty) { case (none) { } case (some(?t)) { walk_ty(v, t); } }
alt (loc.node.init) {
case (none) { }
case (some(?i)) { walk_expr(v, i.expr); }
}
v.visit_local_post(loc);
}
fn walk_item(&ast_visitor v, @ast::item i) {
if (!v.keep_going()) { ret; }
v.visit_item_pre(i);
alt (i.node) {
case (ast::item_const(?t, ?e)) { walk_ty(v, t); walk_expr(v, e); }
case (ast::item_fn(?f, ?tps)) {
walk_fn(v, f, tps, i.span, some(i.ident), i.id);
}
case (ast::item_mod(?m)) { walk_mod(v, m); }
case (ast::item_native_mod(?nm)) { walk_native_mod(v, nm); }
case (ast::item_ty(?t, _)) { walk_ty(v, t); }
case (ast::item_res(?f, ?dtor_id, ?tps, _)) {
walk_fn(v, f, tps, i.span, some(i.ident), dtor_id);
}
case (ast::item_tag(?variants, _)) {
for (ast::variant vr in variants) {
for (ast::variant_arg va in vr.node.args) {
walk_ty(v, va.ty);
}
}
}
case (ast::item_obj(?ob, _, _)) {
for (ast::obj_field f in ob.fields) { walk_ty(v, f.ty); }
for (@ast::method m in ob.methods) {
v.visit_method_pre(m);
// Methods don't have ty params?
walk_fn(v, m.node.meth, [], m.span,
some(m.node.ident), m.node.id);
v.visit_method_post(m);
}
alt (ob.dtor) {
case (none) { }
case (some(?m)) {
walk_fn(v, m.node.meth, [], m.span,
some(m.node.ident), m.node.id);
}
}
}
}
v.visit_item_post(i);
}
fn walk_ty(&ast_visitor v, @ast::ty t) {
if (!v.keep_going()) { ret; }
v.visit_ty_pre(t);
alt (t.node) {
case (ast::ty_nil) { }
case (ast::ty_bot) { }
case (ast::ty_bool) { }
case (ast::ty_int) { }
case (ast::ty_uint) { }
case (ast::ty_float) { }
case (ast::ty_machine(_)) { }
case (ast::ty_char) { }
case (ast::ty_str) { }
case (ast::ty_istr) { }
case (ast::ty_box(?mt)) { walk_ty(v, mt.ty); }
case (ast::ty_vec(?mt)) { walk_ty(v, mt.ty); }
case (ast::ty_ivec(?mt)) { walk_ty(v, mt.ty); }
case (ast::ty_ptr(?mt)) { walk_ty(v, mt.ty); }
case (ast::ty_task) { }
case (ast::ty_port(?t)) { walk_ty(v, t); }
case (ast::ty_chan(?t)) { walk_ty(v, t); }
case (ast::ty_tup(?mts)) {
for (ast::mt mt in mts) { walk_ty(v, mt.ty); }
}
case (ast::ty_rec(?flds)) {
for (ast::ty_field f in flds) { walk_ty(v, f.node.mt.ty); }
}
case (ast::ty_fn(_, ?args, ?out, _, ?constrs)) {
for (ast::ty_arg a in args) { walk_ty(v, a.node.ty); }
for (@ast::constr c in constrs) { v.visit_constr(c); }
walk_ty(v, out);
}
case (ast::ty_obj(?tmeths)) {
for (ast::ty_method m in tmeths) {
for (ast::ty_arg a in m.node.inputs) {
walk_ty(v, a.node.ty);
}
walk_ty(v, m.node.output);
}
}
case (ast::ty_path(?p, _)) {
for (@ast::ty tp in p.node.types) { walk_ty(v, tp); }
}
case (ast::ty_type) { }
case (ast::ty_constr(?t, _)) { walk_ty(v, t); }
}
v.visit_ty_post(t);
}
fn walk_pat(&ast_visitor v, &@ast::pat p) {
v.visit_pat_pre(p);
alt (p.node) {
case (ast::pat_tag(?path, ?children)) {
for (@ast::ty tp in path.node.types) { walk_ty(v, tp); }
for (@ast::pat child in children) { walk_pat(v, child); }
}
case (_) { }
}
v.visit_pat_post(p);
}
fn walk_native_mod(&ast_visitor v, &ast::native_mod nm) {
if (!v.keep_going()) { ret; }
for (@ast::view_item vi in nm.view_items) { walk_view_item(v, vi); }
for (@ast::native_item ni in nm.items) { walk_native_item(v, ni); }
}
fn walk_native_item(&ast_visitor v, @ast::native_item ni) {
if (!v.keep_going()) { ret; }
v.visit_native_item_pre(ni);
alt (ni.node) {
case (ast::native_item_fn(_, ?fd, _)) {
walk_fn_decl(v, fd);
}
case (ast::native_item_ty) { }
}
v.visit_native_item_post(ni);
}
fn walk_fn_decl(&ast_visitor v, &ast::fn_decl fd) {
for (ast::arg a in fd.inputs) { walk_ty(v, a.ty); }
for (@ast::constr c in fd.constraints) { v.visit_constr(c); }
walk_ty(v, fd.output);
}
fn walk_fn(&ast_visitor v, &ast::_fn f, &vec[ast::ty_param] tps,
&span sp, &ast::fn_ident i, ast::node_id d) {
if (!v.keep_going()) { ret; }
v.visit_fn_pre(f, tps, sp, i, d);
walk_fn_decl(v, f.decl);
walk_block(v, f.body);
v.visit_fn_post(f, tps, sp, i, d);
}
fn walk_block(&ast_visitor v, &ast::block b) {
if (!v.keep_going()) { ret; }
v.visit_block_pre(b);
for (@ast::stmt s in b.node.stmts) { walk_stmt(v, s); }
walk_expr_opt(v, b.node.expr);
v.visit_block_post(b);
}
fn walk_stmt(&ast_visitor v, @ast::stmt s) {
if (!v.keep_going()) { ret; }
v.visit_stmt_pre(s);
alt (s.node) {
case (ast::stmt_decl(?d, _)) { walk_decl(v, d); }
case (ast::stmt_expr(?e, _)) { walk_expr(v, e); }
case (ast::stmt_crate_directive(?cdir)) {
walk_crate_directive(v, cdir);
}
}
v.visit_stmt_post(s);
}
fn walk_decl(&ast_visitor v, @ast::decl d) {
if (!v.keep_going()) { ret; }
v.visit_decl_pre(d);
alt (d.node) {
case (ast::decl_local(?loc)) { walk_local(v, loc); }
case (ast::decl_item(?it)) { walk_item(v, it); }
}
v.visit_decl_post(d);
}
fn walk_expr_opt(&ast_visitor v, option::t[@ast::expr] eo) {
alt (eo) { case (none) { } case (some(?e)) { walk_expr(v, e); } }
}
fn walk_exprs(&ast_visitor v, vec[@ast::expr] exprs) {
for (@ast::expr e in exprs) { walk_expr(v, e); }
}
fn walk_expr(&ast_visitor v, @ast::expr e) {
if (!v.keep_going()) { ret; }
v.visit_expr_pre(e);
alt (e.node) {
case (ast::expr_vec(?es, _, _)) { walk_exprs(v, es); }
case (ast::expr_tup(?elts)) {
for (ast::elt e in elts) { walk_expr(v, e.expr); }
}
case (ast::expr_rec(?flds, ?base)) {
for (ast::field f in flds) { walk_expr(v, f.node.expr); }
walk_expr_opt(v, base);
}
case (ast::expr_call(?callee, ?args)) {
walk_expr(v, callee);
walk_exprs(v, args);
}
case (ast::expr_self_method(_)) { }
case (ast::expr_bind(?callee, ?args)) {
walk_expr(v, callee);
for (option::t[@ast::expr] eo in args) { walk_expr_opt(v, eo); }
}
case (ast::expr_spawn(_, _, ?callee, ?args)) {
walk_expr(v, callee);
walk_exprs(v, args);
}
case (ast::expr_binary(_, ?a, ?b)) {
walk_expr(v, a);
walk_expr(v, b);
}
case (ast::expr_unary(_, ?a)) { walk_expr(v, a); }
case (ast::expr_lit(_)) { }
case (ast::expr_cast(?x, ?t)) { walk_expr(v, x); walk_ty(v, t); }
case (ast::expr_if(?x, ?b, ?eo)) {
walk_expr(v, x);
walk_block(v, b);
walk_expr_opt(v, eo);
}
case (ast::expr_if_check(?x, ?b, ?eo)) {
walk_expr(v, x);
walk_block(v, b);
walk_expr_opt(v, eo);
}
case (ast::expr_ternary(?c, ?t, ?e)) {
walk_expr(v, c);
walk_expr(v, t);
walk_expr(v, e);
}
case (ast::expr_while(?x, ?b)) {
walk_expr(v, x);
walk_block(v, b);
}
case (ast::expr_for(?dcl, ?x, ?b)) {
walk_local(v, dcl);
walk_expr(v, x);
walk_block(v, b);
}
case (ast::expr_for_each(?dcl, ?x, ?b)) {
walk_local(v, dcl);
walk_expr(v, x);
walk_block(v, b);
}
case (ast::expr_do_while(?b, ?x)) {
walk_block(v, b);
walk_expr(v, x);
}
case (ast::expr_alt(?x, ?arms)) {
walk_expr(v, x);
for (ast::arm a in arms) {
walk_pat(v, a.pat);
v.visit_arm_pre(a);
walk_block(v, a.block);
v.visit_arm_post(a);
}
}
case (ast::expr_fn(?f)) {
walk_fn(v, f, [], e.span, none, e.id);
}
case (ast::expr_block(?b)) { walk_block(v, b); }
case (ast::expr_assign(?a, ?b)) {
walk_expr(v, a);
walk_expr(v, b);
}
case (ast::expr_move(?a, ?b)) { walk_expr(v, a); walk_expr(v, b); }
case (ast::expr_swap(?a, ?b)) { walk_expr(v, a); walk_expr(v, b); }
case (ast::expr_assign_op(_, ?a, ?b)) {
walk_expr(v, a);
walk_expr(v, b);
}
case (ast::expr_send(?a, ?b)) { walk_expr(v, a); walk_expr(v, b); }
case (ast::expr_recv(?a, ?b)) { walk_expr(v, a); walk_expr(v, b); }
case (ast::expr_field(?x, _)) { walk_expr(v, x); }
case (ast::expr_index(?a, ?b)) {
walk_expr(v, a);
walk_expr(v, b);
}
case (ast::expr_path(?p)) {
for (@ast::ty tp in p.node.types) { walk_ty(v, tp); }
}
case (ast::expr_ext(_, ?args, ?body, ?expansion)) {
// Only walk expansion, not args/body.
walk_expr(v, expansion);
}
case (ast::expr_fail(?eo)) { walk_expr_opt(v, eo); }
case (ast::expr_break) { }
case (ast::expr_cont) { }
case (ast::expr_ret(?eo)) { walk_expr_opt(v, eo); }
case (ast::expr_put(?eo)) { walk_expr_opt(v, eo); }
case (ast::expr_be(?x)) { walk_expr(v, x); }
case (ast::expr_log(_, ?x)) { walk_expr(v, x); }
case (ast::expr_check(_, ?x)) { walk_expr(v, x); }
case (ast::expr_assert(?x)) { walk_expr(v, x); }
case (ast::expr_port(_)) { }
case (ast::expr_chan(?x)) { walk_expr(v, x); }
case (ast::expr_anon_obj(?anon_obj, _, _)) {
// Fields
alt (anon_obj.fields) {
case (none) { }
case (some(?fields)) {
for (ast::anon_obj_field f in fields) {
walk_ty(v, f.ty);
walk_expr(v, f.expr);
}
}
}
// with_obj
alt (anon_obj.with_obj) {
case (none) { }
case (some(?e)) { walk_expr(v, e); }
}
// Methods
for (@ast::method m in anon_obj.methods) {
v.visit_method_pre(m);
walk_fn(v, m.node.meth, [], m.span, some(m.node.ident),
m.node.id);
v.visit_method_post(m);
}
}
}
v.visit_expr_post(e);
}
fn def_keep_going() -> bool { ret true; }
fn def_want_crate_directives() -> bool { ret false; }
fn def_visit_crate(&ast::crate c) { }
fn def_visit_crate_directive(&@ast::crate_directive c) { }
fn def_visit_view_item(&@ast::view_item vi) { }
fn def_visit_native_item(&@ast::native_item ni) { }
fn def_visit_item(&@ast::item i) { }
fn def_visit_method(&@ast::method m) { }
fn def_visit_block(&ast::block b) { }
fn def_visit_stmt(&@ast::stmt s) { }
fn def_visit_arm(&ast::arm a) { }
fn def_visit_pat(&@ast::pat p) { }
fn def_visit_decl(&@ast::decl d) { }
fn def_visit_local(&@ast::local l) { }
fn def_visit_expr(&@ast::expr e) { }
fn def_visit_ty(&@ast::ty t) { }
fn def_visit_constr(&@ast::constr c) { }
fn def_visit_fn(&ast::_fn f, &vec[ast::ty_param] tps,
&span sp, &ast::fn_ident i, ast::node_id d) { }
fn default_visitor() -> ast_visitor {
ret rec(keep_going=def_keep_going,
want_crate_directives=def_want_crate_directives,
visit_crate_pre=def_visit_crate,
visit_crate_post=def_visit_crate,
visit_crate_directive_pre=def_visit_crate_directive,
visit_crate_directive_post=def_visit_crate_directive,
visit_view_item_pre=def_visit_view_item,
visit_view_item_post=def_visit_view_item,
visit_native_item_pre=def_visit_native_item,
visit_native_item_post=def_visit_native_item,
visit_item_pre=def_visit_item,
visit_item_post=def_visit_item,
visit_method_pre=def_visit_method,
visit_method_post=def_visit_method,
visit_block_pre=def_visit_block,
visit_block_post=def_visit_block,
visit_stmt_pre=def_visit_stmt,
visit_stmt_post=def_visit_stmt,
visit_arm_pre=def_visit_arm,
visit_arm_post=def_visit_arm,
visit_pat_pre=def_visit_pat,
visit_pat_post=def_visit_pat,
visit_decl_pre=def_visit_decl,
visit_decl_post=def_visit_decl,
visit_local_pre=def_visit_local,
visit_local_post=def_visit_local,
visit_expr_pre=def_visit_expr,
visit_expr_post=def_visit_expr,
visit_ty_pre=def_visit_ty,
visit_ty_post=def_visit_ty,
visit_constr=def_visit_constr,
visit_fn_pre=def_visit_fn,
visit_fn_post=def_visit_fn);
}
//
// 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:
//