]> git.proxmox.com Git - rustc.git/blob - src/libsyntax/ast_util.rs
Merge tag 'debian/0.7-0_exp1'
[rustc.git] / src / libsyntax / ast_util.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use ast::*;
12 use ast;
13 use ast_util;
14 use codemap::{span, spanned};
15 use opt_vec;
16 use parse::token;
17 use visit;
18
19 use std::hashmap::HashMap;
20 use std::int;
21 use std::option;
22 use std::cast;
23 use std::local_data;
24
25 pub fn path_name_i(idents: &[ident]) -> ~str {
26 // FIXME: Bad copies (#2543 -- same for everything else that says "bad")
27 idents.map(|i| token::interner_get(i.name)).connect("::")
28 }
29
30 pub fn path_to_ident(p: @Path) -> ident { copy *p.idents.last() }
31
32 pub fn local_def(id: node_id) -> def_id {
33 ast::def_id { crate: local_crate, node: id }
34 }
35
36 pub fn is_local(did: ast::def_id) -> bool { did.crate == local_crate }
37
38 pub fn stmt_id(s: &stmt) -> node_id {
39 match s.node {
40 stmt_decl(_, id) => id,
41 stmt_expr(_, id) => id,
42 stmt_semi(_, id) => id,
43 stmt_mac(*) => fail!("attempted to analyze unexpanded stmt")
44 }
45 }
46
47 pub fn variant_def_ids(d: def) -> Option<(def_id, def_id)> {
48 match d {
49 def_variant(enum_id, var_id) => {
50 Some((enum_id, var_id))
51 }
52 _ => None
53 }
54 }
55
56 pub fn def_id_of_def(d: def) -> def_id {
57 match d {
58 def_fn(id, _) | def_static_method(id, _, _) | def_mod(id) |
59 def_foreign_mod(id) | def_static(id, _) |
60 def_variant(_, id) | def_ty(id) | def_ty_param(id, _) |
61 def_use(id) | def_struct(id) | def_trait(id) | def_method(id, _) => {
62 id
63 }
64 def_arg(id, _) | def_local(id, _) | def_self(id, _) | def_self_ty(id)
65 | def_upvar(id, _, _, _) | def_binding(id, _) | def_region(id)
66 | def_typaram_binder(id) | def_label(id) => {
67 local_def(id)
68 }
69
70 def_prim_ty(_) => fail!()
71 }
72 }
73
74 pub fn binop_to_str(op: binop) -> ~str {
75 match op {
76 add => return ~"+",
77 subtract => return ~"-",
78 mul => return ~"*",
79 div => return ~"/",
80 rem => return ~"%",
81 and => return ~"&&",
82 or => return ~"||",
83 bitxor => return ~"^",
84 bitand => return ~"&",
85 bitor => return ~"|",
86 shl => return ~"<<",
87 shr => return ~">>",
88 eq => return ~"==",
89 lt => return ~"<",
90 le => return ~"<=",
91 ne => return ~"!=",
92 ge => return ~">=",
93 gt => return ~">"
94 }
95 }
96
97 pub fn binop_to_method_name(op: binop) -> Option<~str> {
98 match op {
99 add => return Some(~"add"),
100 subtract => return Some(~"sub"),
101 mul => return Some(~"mul"),
102 div => return Some(~"div"),
103 rem => return Some(~"rem"),
104 bitxor => return Some(~"bitxor"),
105 bitand => return Some(~"bitand"),
106 bitor => return Some(~"bitor"),
107 shl => return Some(~"shl"),
108 shr => return Some(~"shr"),
109 lt => return Some(~"lt"),
110 le => return Some(~"le"),
111 ge => return Some(~"ge"),
112 gt => return Some(~"gt"),
113 eq => return Some(~"eq"),
114 ne => return Some(~"ne"),
115 and | or => return None
116 }
117 }
118
119 pub fn lazy_binop(b: binop) -> bool {
120 match b {
121 and => true,
122 or => true,
123 _ => false
124 }
125 }
126
127 pub fn is_shift_binop(b: binop) -> bool {
128 match b {
129 shl => true,
130 shr => true,
131 _ => false
132 }
133 }
134
135 pub fn unop_to_str(op: unop) -> ~str {
136 match op {
137 box(mt) => if mt == m_mutbl { ~"@mut " } else { ~"@" },
138 uniq => ~"~",
139 deref => ~"*",
140 not => ~"!",
141 neg => ~"-"
142 }
143 }
144
145 pub fn is_path(e: @expr) -> bool {
146 return match e.node { expr_path(_) => true, _ => false };
147 }
148
149 pub fn int_ty_to_str(t: int_ty) -> ~str {
150 match t {
151 ty_char => ~"u8", // ???
152 ty_i => ~"",
153 ty_i8 => ~"i8",
154 ty_i16 => ~"i16",
155 ty_i32 => ~"i32",
156 ty_i64 => ~"i64"
157 }
158 }
159
160 pub fn int_ty_max(t: int_ty) -> u64 {
161 match t {
162 ty_i8 => 0x80u64,
163 ty_i16 => 0x8000u64,
164 ty_i | ty_char | ty_i32 => 0x80000000u64, // actually ni about ty_i
165 ty_i64 => 0x8000000000000000u64
166 }
167 }
168
169 pub fn uint_ty_to_str(t: uint_ty) -> ~str {
170 match t {
171 ty_u => ~"u",
172 ty_u8 => ~"u8",
173 ty_u16 => ~"u16",
174 ty_u32 => ~"u32",
175 ty_u64 => ~"u64"
176 }
177 }
178
179 pub fn uint_ty_max(t: uint_ty) -> u64 {
180 match t {
181 ty_u8 => 0xffu64,
182 ty_u16 => 0xffffu64,
183 ty_u | ty_u32 => 0xffffffffu64, // actually ni about ty_u
184 ty_u64 => 0xffffffffffffffffu64
185 }
186 }
187
188 pub fn float_ty_to_str(t: float_ty) -> ~str {
189 match t { ty_f => ~"f", ty_f32 => ~"f32", ty_f64 => ~"f64" }
190 }
191
192 pub fn is_call_expr(e: @expr) -> bool {
193 match e.node { expr_call(*) => true, _ => false }
194 }
195
196 pub fn block_from_expr(e: @expr) -> blk {
197 let blk_ = default_block(~[], option::Some::<@expr>(e), e.id);
198 return spanned {node: blk_, span: e.span};
199 }
200
201 pub fn default_block(
202 stmts1: ~[@stmt],
203 expr1: Option<@expr>,
204 id1: node_id
205 ) -> blk_ {
206 ast::blk_ {
207 view_items: ~[],
208 stmts: stmts1,
209 expr: expr1,
210 id: id1,
211 rules: default_blk,
212 }
213 }
214
215 pub fn ident_to_path(s: span, i: ident) -> @Path {
216 @ast::Path { span: s,
217 global: false,
218 idents: ~[i],
219 rp: None,
220 types: ~[] }
221 }
222
223 pub fn ident_to_pat(id: node_id, s: span, i: ident) -> @pat {
224 @ast::pat { id: id,
225 node: pat_ident(bind_infer, ident_to_path(s, i), None),
226 span: s }
227 }
228
229 pub fn is_unguarded(a: &arm) -> bool {
230 match a.guard {
231 None => true,
232 _ => false
233 }
234 }
235
236 pub fn unguarded_pat(a: &arm) -> Option<~[@pat]> {
237 if is_unguarded(a) { Some(/* FIXME (#2543) */ copy a.pats) } else { None }
238 }
239
240 pub fn public_methods(ms: ~[@method]) -> ~[@method] {
241 do ms.filtered |m| {
242 match m.vis {
243 public => true,
244 _ => false
245 }
246 }
247 }
248
249 // extract a ty_method from a trait_method. if the trait_method is
250 // a default, pull out the useful fields to make a ty_method
251 pub fn trait_method_to_ty_method(method: &trait_method) -> ty_method {
252 match *method {
253 required(ref m) => copy *m,
254 provided(ref m) => {
255 ty_method {
256 ident: m.ident,
257 attrs: copy m.attrs,
258 purity: m.purity,
259 decl: copy m.decl,
260 generics: copy m.generics,
261 explicit_self: m.explicit_self,
262 id: m.id,
263 span: m.span,
264 }
265 }
266 }
267 }
268
269 pub fn split_trait_methods(trait_methods: &[trait_method])
270 -> (~[ty_method], ~[@method]) {
271 let mut reqd = ~[];
272 let mut provd = ~[];
273 for trait_methods.iter().advance |trt_method| {
274 match *trt_method {
275 required(ref tm) => reqd.push(copy *tm),
276 provided(m) => provd.push(m)
277 }
278 };
279 (reqd, provd)
280 }
281
282 pub fn struct_field_visibility(field: ast::struct_field) -> visibility {
283 match field.node.kind {
284 ast::named_field(_, visibility) => visibility,
285 ast::unnamed_field => ast::public
286 }
287 }
288
289 pub trait inlined_item_utils {
290 fn ident(&self) -> ident;
291 fn id(&self) -> ast::node_id;
292 fn accept<E: Copy>(&self, e: E, v: visit::vt<E>);
293 }
294
295 impl inlined_item_utils for inlined_item {
296 fn ident(&self) -> ident {
297 match *self {
298 ii_item(i) => /* FIXME (#2543) */ copy i.ident,
299 ii_foreign(i) => /* FIXME (#2543) */ copy i.ident,
300 ii_method(_, m) => /* FIXME (#2543) */ copy m.ident,
301 }
302 }
303
304 fn id(&self) -> ast::node_id {
305 match *self {
306 ii_item(i) => i.id,
307 ii_foreign(i) => i.id,
308 ii_method(_, m) => m.id,
309 }
310 }
311
312 fn accept<E: Copy>(&self, e: E, v: visit::vt<E>) {
313 match *self {
314 ii_item(i) => (v.visit_item)(i, (e, v)),
315 ii_foreign(i) => (v.visit_foreign_item)(i, (e, v)),
316 ii_method(_, m) => visit::visit_method_helper(m, (e, v)),
317 }
318 }
319 }
320
321 /* True if d is either a def_self, or a chain of def_upvars
322 referring to a def_self */
323 pub fn is_self(d: ast::def) -> bool {
324 match d {
325 def_self(*) => true,
326 def_upvar(_, d, _, _) => is_self(*d),
327 _ => false
328 }
329 }
330
331 /// Maps a binary operator to its precedence
332 pub fn operator_prec(op: ast::binop) -> uint {
333 match op {
334 mul | div | rem => 12u,
335 // 'as' sits between here with 11
336 add | subtract => 10u,
337 shl | shr => 9u,
338 bitand => 8u,
339 bitxor => 7u,
340 bitor => 6u,
341 lt | le | ge | gt => 4u,
342 eq | ne => 3u,
343 and => 2u,
344 or => 1u
345 }
346 }
347
348 /// Precedence of the `as` operator, which is a binary operator
349 /// not appearing in the prior table.
350 pub static as_prec: uint = 11u;
351
352 pub fn empty_generics() -> Generics {
353 Generics {lifetimes: opt_vec::Empty,
354 ty_params: opt_vec::Empty}
355 }
356
357 // ______________________________________________________________________
358 // Enumerating the IDs which appear in an AST
359
360 #[deriving(Encodable, Decodable)]
361 pub struct id_range {
362 min: node_id,
363 max: node_id,
364 }
365
366 impl id_range {
367 pub fn max() -> id_range {
368 id_range {
369 min: int::max_value,
370 max: int::min_value,
371 }
372 }
373
374 pub fn empty(&self) -> bool {
375 self.min >= self.max
376 }
377
378 pub fn add(&mut self, id: node_id) {
379 self.min = int::min(self.min, id);
380 self.max = int::max(self.max, id + 1);
381 }
382 }
383
384 pub fn id_visitor<T: Copy>(vfn: @fn(node_id, T)) -> visit::vt<T> {
385 let visit_generics: @fn(&Generics, T) = |generics, t| {
386 for generics.ty_params.iter().advance |p| {
387 vfn(p.id, copy t);
388 }
389 for generics.lifetimes.iter().advance |p| {
390 vfn(p.id, copy t);
391 }
392 };
393 visit::mk_vt(@visit::Visitor {
394 visit_mod: |m, sp, id, (t, vt)| {
395 vfn(id, copy t);
396 visit::visit_mod(m, sp, id, (t, vt));
397 },
398
399 visit_view_item: |vi, (t, vt)| {
400 match vi.node {
401 view_item_extern_mod(_, _, id) => vfn(id, copy t),
402 view_item_use(ref vps) => {
403 for vps.iter().advance |vp| {
404 match vp.node {
405 view_path_simple(_, _, id) => vfn(id, copy t),
406 view_path_glob(_, id) => vfn(id, copy t),
407 view_path_list(_, ref paths, id) => {
408 vfn(id, copy t);
409 for paths.iter().advance |p| {
410 vfn(p.node.id, copy t);
411 }
412 }
413 }
414 }
415 }
416 }
417 visit::visit_view_item(vi, (t, vt));
418 },
419
420 visit_foreign_item: |ni, (t, vt)| {
421 vfn(ni.id, copy t);
422 visit::visit_foreign_item(ni, (t, vt));
423 },
424
425 visit_item: |i, (t, vt)| {
426 vfn(i.id, copy t);
427 match i.node {
428 item_enum(ref enum_definition, _) =>
429 for (*enum_definition).variants.iter().advance |v| { vfn(v.node.id, copy t); },
430 _ => ()
431 }
432 visit::visit_item(i, (t, vt));
433 },
434
435 visit_local: |l, (t, vt)| {
436 vfn(l.node.id, copy t);
437 visit::visit_local(l, (t, vt));
438 },
439 visit_block: |b, (t, vt)| {
440 vfn(b.node.id, copy t);
441 visit::visit_block(b, (t, vt));
442 },
443 visit_stmt: |s, (t, vt)| {
444 vfn(ast_util::stmt_id(s), copy t);
445 visit::visit_stmt(s, (t, vt));
446 },
447 visit_pat: |p, (t, vt)| {
448 vfn(p.id, copy t);
449 visit::visit_pat(p, (t, vt));
450 },
451
452 visit_expr: |e, (t, vt)| {
453 {
454 let r = e.get_callee_id();
455 for r.iter().advance |callee_id| {
456 vfn(*callee_id, copy t);
457 }
458 }
459 vfn(e.id, copy t);
460 visit::visit_expr(e, (t, vt));
461 },
462
463 visit_ty: |ty, (t, vt)| {
464 match ty.node {
465 ty_path(_, _, id) => vfn(id, copy t),
466 _ => { /* fall through */ }
467 }
468 visit::visit_ty(ty, (t, vt));
469 },
470
471 visit_generics: |generics, (t, vt)| {
472 visit_generics(generics, copy t);
473 visit::visit_generics(generics, (t, vt));
474 },
475
476 visit_fn: |fk, d, a, b, id, (t, vt)| {
477 vfn(id, copy t);
478
479 match *fk {
480 visit::fk_item_fn(_, generics, _, _) => {
481 visit_generics(generics, copy t);
482 }
483 visit::fk_method(_, generics, m) => {
484 vfn(m.self_id, copy t);
485 visit_generics(generics, copy t);
486 }
487 visit::fk_anon(_) |
488 visit::fk_fn_block => {
489 }
490 }
491
492 for d.inputs.iter().advance |arg| {
493 vfn(arg.id, copy t)
494 }
495 visit::visit_fn(fk, d, a, b, id, (copy t, vt));
496 },
497
498 visit_struct_field: |f, (t, vt)| {
499 vfn(f.node.id, copy t);
500 visit::visit_struct_field(f, (t, vt));
501 },
502
503 .. *visit::default_visitor()
504 })
505 }
506
507 pub fn visit_ids_for_inlined_item(item: &inlined_item, vfn: @fn(node_id)) {
508 item.accept((), id_visitor(|id, ()| vfn(id)));
509 }
510
511 pub fn compute_id_range(visit_ids_fn: &fn(@fn(node_id))) -> id_range {
512 let result = @mut id_range::max();
513 do visit_ids_fn |id| {
514 result.add(id);
515 }
516 *result
517 }
518
519 pub fn compute_id_range_for_inlined_item(item: &inlined_item) -> id_range {
520 compute_id_range(|f| visit_ids_for_inlined_item(item, f))
521 }
522
523 pub fn is_item_impl(item: @ast::item) -> bool {
524 match item.node {
525 item_impl(*) => true,
526 _ => false
527 }
528 }
529
530 pub fn walk_pat(pat: @pat, it: &fn(@pat) -> bool) -> bool {
531 if !it(pat) {
532 return false;
533 }
534
535 match pat.node {
536 pat_ident(_, _, Some(p)) => walk_pat(p, it),
537 pat_struct(_, ref fields, _) => {
538 fields.iter().advance(|f| walk_pat(f.pat, |p| it(p)))
539 }
540 pat_enum(_, Some(ref s)) | pat_tup(ref s) => {
541 s.iter().advance(|&p| walk_pat(p, |p| it(p)))
542 }
543 pat_box(s) | pat_uniq(s) | pat_region(s) => {
544 walk_pat(s, it)
545 }
546 pat_vec(ref before, ref slice, ref after) => {
547 before.iter().advance(|&p| walk_pat(p, |p| it(p))) &&
548 slice.iter().advance(|&p| walk_pat(p, |p| it(p))) &&
549 after.iter().advance(|&p| walk_pat(p, |p| it(p)))
550 }
551 pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, _, _) |
552 pat_enum(_, _) => {
553 true
554 }
555 }
556 }
557
558 pub trait EachViewItem {
559 pub fn each_view_item(&self, f: @fn(&ast::view_item) -> bool) -> bool;
560 }
561
562 impl EachViewItem for ast::crate {
563 fn each_view_item(&self, f: @fn(&ast::view_item) -> bool) -> bool {
564 let broke = @mut false;
565 let vtor: visit::vt<()> = visit::mk_simple_visitor(@visit::SimpleVisitor {
566 visit_view_item: |vi| { *broke = f(vi); }, ..*visit::default_simple_visitor()
567 });
568 visit::visit_crate(self, ((), vtor));
569 true
570 }
571 }
572
573 pub fn view_path_id(p: &view_path) -> node_id {
574 match p.node {
575 view_path_simple(_, _, id) |
576 view_path_glob(_, id) |
577 view_path_list(_, _, id) => id
578 }
579 }
580
581 /// Returns true if the given struct def is tuple-like; i.e. that its fields
582 /// are unnamed.
583 pub fn struct_def_is_tuple_like(struct_def: @ast::struct_def) -> bool {
584 struct_def.ctor_id.is_some()
585 }
586
587 pub fn visibility_to_privacy(visibility: visibility) -> Privacy {
588 match visibility {
589 public => Public,
590 inherited | private => Private
591 }
592 }
593
594 pub fn variant_visibility_to_privacy(visibility: visibility,
595 enclosing_is_public: bool)
596 -> Privacy {
597 if enclosing_is_public {
598 match visibility {
599 public | inherited => Public,
600 private => Private
601 }
602 } else {
603 visibility_to_privacy(visibility)
604 }
605 }
606
607 #[deriving(Eq)]
608 pub enum Privacy {
609 Private,
610 Public
611 }
612
613 /// Returns true if the given pattern consists solely of an identifier
614 /// and false otherwise.
615 pub fn pat_is_ident(pat: @ast::pat) -> bool {
616 match pat.node {
617 ast::pat_ident(*) => true,
618 _ => false,
619 }
620 }
621
622 // HYGIENE FUNCTIONS
623
624 /// Construct an identifier with the given name and an empty context:
625 pub fn new_ident(name: Name) -> ident { ident {name: name, ctxt: 0}}
626
627 /// Extend a syntax context with a given mark
628 pub fn new_mark(m:Mrk, tail:SyntaxContext) -> SyntaxContext {
629 new_mark_internal(m,tail,get_sctable())
630 }
631
632 // Extend a syntax context with a given mark and table
633 // FIXME #4536 : currently pub to allow testing
634 pub fn new_mark_internal(m:Mrk, tail:SyntaxContext,table:&mut SCTable)
635 -> SyntaxContext {
636 let key = (tail,m);
637 // FIXME #5074 : can't use more natural style because we're missing
638 // flow-sensitivity. Results in two lookups on a hash table hit.
639 // also applies to new_rename_internal, below.
640 // let try_lookup = table.mark_memo.find(&key);
641 match table.mark_memo.contains_key(&key) {
642 false => {
643 let new_idx = idx_push(&mut table.table,Mark(m,tail));
644 table.mark_memo.insert(key,new_idx);
645 new_idx
646 }
647 true => {
648 match table.mark_memo.find(&key) {
649 None => fail!(~"internal error: key disappeared 2013042901"),
650 Some(idxptr) => {*idxptr}
651 }
652 }
653 }
654 }
655
656 /// Extend a syntax context with a given rename
657 pub fn new_rename(id:ident, to:Name, tail:SyntaxContext) -> SyntaxContext {
658 new_rename_internal(id, to, tail, get_sctable())
659 }
660
661 // Extend a syntax context with a given rename and sctable
662 // FIXME #4536 : currently pub to allow testing
663 pub fn new_rename_internal(id:ident, to:Name, tail:SyntaxContext, table: &mut SCTable)
664 -> SyntaxContext {
665 let key = (tail,id,to);
666 // FIXME #5074
667 //let try_lookup = table.rename_memo.find(&key);
668 match table.rename_memo.contains_key(&key) {
669 false => {
670 let new_idx = idx_push(&mut table.table,Rename(id,to,tail));
671 table.rename_memo.insert(key,new_idx);
672 new_idx
673 }
674 true => {
675 match table.rename_memo.find(&key) {
676 None => fail!(~"internal error: key disappeared 2013042902"),
677 Some(idxptr) => {*idxptr}
678 }
679 }
680 }
681 }
682
683 /// Make a fresh syntax context table with EmptyCtxt in slot zero
684 /// and IllegalCtxt in slot one.
685 // FIXME #4536 : currently pub to allow testing
686 pub fn new_sctable_internal() -> SCTable {
687 SCTable {
688 table: ~[EmptyCtxt,IllegalCtxt],
689 mark_memo: HashMap::new(),
690 rename_memo: HashMap::new()
691 }
692 }
693
694 // fetch the SCTable from TLS, create one if it doesn't yet exist.
695 pub fn get_sctable() -> @mut SCTable {
696 unsafe {
697 let sctable_key = (cast::transmute::<(uint, uint),
698 &fn:Copy(v: @@mut SCTable)>(
699 (-4 as uint, 0u)));
700 match local_data::local_data_get(sctable_key) {
701 None => {
702 let new_table = @@mut new_sctable_internal();
703 local_data::local_data_set(sctable_key,new_table);
704 *new_table
705 },
706 Some(intr) => *intr
707 }
708 }
709 }
710
711 /// Add a value to the end of a vec, return its index
712 fn idx_push<T>(vec: &mut ~[T], val: T) -> uint {
713 vec.push(val);
714 vec.len() - 1
715 }
716
717 /// Resolve a syntax object to a name, per MTWT.
718 pub fn resolve(id : ident) -> Name {
719 resolve_internal(id, get_sctable())
720 }
721
722 // Resolve a syntax object to a name, per MTWT.
723 // FIXME #4536 : currently pub to allow testing
724 pub fn resolve_internal(id : ident, table : &mut SCTable) -> Name {
725 match table.table[id.ctxt] {
726 EmptyCtxt => id.name,
727 // ignore marks here:
728 Mark(_,subctxt) => resolve_internal(ident{name:id.name, ctxt: subctxt},table),
729 // do the rename if necessary:
730 Rename(ident{name,ctxt},toname,subctxt) => {
731 // this could be cached or computed eagerly:
732 let resolvedfrom = resolve_internal(ident{name:name,ctxt:ctxt},table);
733 let resolvedthis = resolve_internal(ident{name:id.name,ctxt:subctxt},table);
734 if ((resolvedthis == resolvedfrom)
735 && (marksof(ctxt,resolvedthis,table)
736 == marksof(subctxt,resolvedthis,table))) {
737 toname
738 } else {
739 resolvedthis
740 }
741 }
742 IllegalCtxt() => fail!(~"expected resolvable context, got IllegalCtxt")
743 }
744 }
745
746 /// Compute the marks associated with a syntax context.
747 // it's not clear to me whether it's better to use a [] mutable
748 // vector or a cons-list for this.
749 pub fn marksof(ctxt: SyntaxContext, stopname: Name, table: &SCTable) -> ~[Mrk] {
750 let mut result = ~[];
751 let mut loopvar = ctxt;
752 loop {
753 match table.table[loopvar] {
754 EmptyCtxt => {return result;},
755 Mark(mark,tl) => {
756 xorPush(&mut result,mark);
757 loopvar = tl;
758 },
759 Rename(_,name,tl) => {
760 // see MTWT for details on the purpose of the stopname.
761 // short version: it prevents duplication of effort.
762 if (name == stopname) {
763 return result;
764 } else {
765 loopvar = tl;
766 }
767 }
768 IllegalCtxt => fail!(~"expected resolvable context, got IllegalCtxt")
769 }
770 }
771 }
772
773 /// Push a name... unless it matches the one on top, in which
774 /// case pop and discard (so two of the same marks cancel)
775 pub fn xorPush(marks: &mut ~[uint], mark: uint) {
776 if ((marks.len() > 0) && (getLast(marks) == mark)) {
777 marks.pop();
778 } else {
779 marks.push(mark);
780 }
781 }
782
783 // get the last element of a mutable array.
784 // FIXME #4903: , must be a separate procedure for now.
785 pub fn getLast(arr: &~[Mrk]) -> uint {
786 *arr.last()
787 }
788
789
790 #[cfg(test)]
791 mod test {
792 use ast::*;
793 use super::*;
794 use std::io;
795
796 #[test] fn xorpush_test () {
797 let mut s = ~[];
798 xorPush(&mut s,14);
799 assert_eq!(copy s,~[14]);
800 xorPush(&mut s,14);
801 assert_eq!(copy s,~[]);
802 xorPush(&mut s,14);
803 assert_eq!(copy s,~[14]);
804 xorPush(&mut s,15);
805 assert_eq!(copy s,~[14,15]);
806 xorPush (&mut s,16);
807 assert_eq!(copy s,~[14,15,16]);
808 xorPush (&mut s,16);
809 assert_eq!(copy s,~[14,15]);
810 xorPush (&mut s,15);
811 assert_eq!(copy s,~[14]);
812 }
813
814 // convert a list of uints to an @[ident]
815 // (ignores the interner completely)
816 fn uints_to_idents (uints: &~[uint]) -> @~[ident] {
817 @uints.map(|u| ident {name:*u, ctxt: empty_ctxt})
818 }
819
820 fn id (u : uint, s: SyntaxContext) -> ident {
821 ident{name:u, ctxt: s}
822 }
823
824 // because of the SCTable, I now need a tidy way of
825 // creating syntax objects. Sigh.
826 #[deriving(Eq)]
827 enum TestSC {
828 M(Mrk),
829 R(ident,Name)
830 }
831
832 // unfold a vector of TestSC values into a SCTable,
833 // returning the resulting index
834 fn unfold_test_sc(tscs : ~[TestSC], tail: SyntaxContext, table : &mut SCTable)
835 -> SyntaxContext {
836 tscs.rev_iter().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
837 {match *tsc {
838 M(mrk) => new_mark_internal(mrk,tail,table),
839 R(ident,name) => new_rename_internal(ident,name,tail,table)}})
840 }
841
842 // gather a SyntaxContext back into a vector of TestSCs
843 fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> ~[TestSC] {
844 let mut result = ~[];
845 loop {
846 match table.table[sc] {
847 EmptyCtxt => {return result;},
848 Mark(mrk,tail) => {
849 result.push(M(mrk));
850 sc = tail;
851 loop;
852 },
853 Rename(id,name,tail) => {
854 result.push(R(id,name));
855 sc = tail;
856 loop;
857 }
858 IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
859 }
860 }
861 }
862
863 #[test] fn test_unfold_refold(){
864 let mut t = new_sctable_internal();
865
866 let test_sc = ~[M(3),R(id(101,0),14),M(9)];
867 assert_eq!(unfold_test_sc(copy test_sc,empty_ctxt,&mut t),4);
868 assert_eq!(t.table[2],Mark(9,0));
869 assert_eq!(t.table[3],Rename(id(101,0),14,2));
870 assert_eq!(t.table[4],Mark(3,3));
871 assert_eq!(refold_test_sc(4,&t),test_sc);
872 }
873
874 // extend a syntax context with a sequence of marks given
875 // in a vector. v[0] will be the outermost mark.
876 fn unfold_marks(mrks:~[Mrk],tail:SyntaxContext,table: &mut SCTable) -> SyntaxContext {
877 mrks.rev_iter().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
878 {new_mark_internal(*mrk,tail,table)})
879 }
880
881 #[test] fn unfold_marks_test() {
882 let mut t = new_sctable_internal();
883
884 assert_eq!(unfold_marks(~[3,7],empty_ctxt,&mut t),3);
885 assert_eq!(t.table[2],Mark(7,0));
886 assert_eq!(t.table[3],Mark(3,2));
887 }
888
889 #[test] fn test_marksof () {
890 let stopname = 242;
891 let name1 = 243;
892 let mut t = new_sctable_internal();
893 assert_eq!(marksof (empty_ctxt,stopname,&t),~[]);
894 // FIXME #5074: ANF'd to dodge nested calls
895 { let ans = unfold_marks(~[4,98],empty_ctxt,&mut t);
896 assert_eq! (marksof (ans,stopname,&t),~[4,98]);}
897 // does xoring work?
898 { let ans = unfold_marks(~[5,5,16],empty_ctxt,&mut t);
899 assert_eq! (marksof (ans,stopname,&t), ~[16]);}
900 // does nested xoring work?
901 { let ans = unfold_marks(~[5,10,10,5,16],empty_ctxt,&mut t);
902 assert_eq! (marksof (ans, stopname,&t), ~[16]);}
903 // rename where stop doesn't match:
904 { let chain = ~[M(9),
905 R(id(name1,
906 new_mark_internal (4, empty_ctxt,&mut t)),
907 100101102),
908 M(14)];
909 let ans = unfold_test_sc(chain,empty_ctxt,&mut t);
910 assert_eq! (marksof (ans, stopname, &t), ~[9,14]);}
911 // rename where stop does match
912 { let name1sc = new_mark_internal(4, empty_ctxt, &mut t);
913 let chain = ~[M(9),
914 R(id(name1, name1sc),
915 stopname),
916 M(14)];
917 let ans = unfold_test_sc(chain,empty_ctxt,&mut t);
918 assert_eq! (marksof (ans, stopname, &t), ~[9]); }
919 }
920
921
922 #[test] fn resolve_tests () {
923 let a = 40;
924 let mut t = new_sctable_internal();
925 // - ctxt is MT
926 assert_eq!(resolve_internal(id(a,empty_ctxt),&mut t),a);
927 // - simple ignored marks
928 { let sc = unfold_marks(~[1,2,3],empty_ctxt,&mut t);
929 assert_eq!(resolve_internal(id(a,sc),&mut t),a);}
930 // - orthogonal rename where names don't match
931 { let sc = unfold_test_sc(~[R(id(50,empty_ctxt),51),M(12)],empty_ctxt,&mut t);
932 assert_eq!(resolve_internal(id(a,sc),&mut t),a);}
933 // - rename where names do match, but marks don't
934 { let sc1 = new_mark_internal(1,empty_ctxt,&mut t);
935 let sc = unfold_test_sc(~[R(id(a,sc1),50),
936 M(1),
937 M(2)],
938 empty_ctxt,&mut t);
939 assert_eq!(resolve_internal(id(a,sc),&mut t), a);}
940 // - rename where names and marks match
941 { let sc1 = unfold_test_sc(~[M(1),M(2)],empty_ctxt,&mut t);
942 let sc = unfold_test_sc(~[R(id(a,sc1),50),M(1),M(2)],empty_ctxt,&mut t);
943 assert_eq!(resolve_internal(id(a,sc),&mut t), 50); }
944 // - rename where names and marks match by literal sharing
945 { let sc1 = unfold_test_sc(~[M(1),M(2)],empty_ctxt,&mut t);
946 let sc = unfold_test_sc(~[R(id(a,sc1),50)],sc1,&mut t);
947 assert_eq!(resolve_internal(id(a,sc),&mut t), 50); }
948 // - two renames of the same var.. can only happen if you use
949 // local-expand to prevent the inner binding from being renamed
950 // during the rename-pass caused by the first:
951 io::println("about to run bad test");
952 { let sc = unfold_test_sc(~[R(id(a,empty_ctxt),50),
953 R(id(a,empty_ctxt),51)],
954 empty_ctxt,&mut t);
955 assert_eq!(resolve_internal(id(a,sc),&mut t), 51); }
956 // the simplest double-rename:
957 { let a_to_a50 = new_rename_internal(id(a,empty_ctxt),50,empty_ctxt,&mut t);
958 let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
959 assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t),51);
960 // mark on the outside doesn't stop rename:
961 let sc = new_mark_internal(9,a50_to_a51,&mut t);
962 assert_eq!(resolve_internal(id(a,sc),&mut t),51);
963 // but mark on the inside does:
964 let a50_to_a51_b = unfold_test_sc(~[R(id(a,a_to_a50),51),
965 M(9)],
966 a_to_a50,
967 &mut t);
968 assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t),50);}
969 }
970
971 #[test] fn hashing_tests () {
972 let mut t = new_sctable_internal();
973 assert_eq!(new_mark_internal(12,empty_ctxt,&mut t),2);
974 assert_eq!(new_mark_internal(13,empty_ctxt,&mut t),3);
975 // using the same one again should result in the same index:
976 assert_eq!(new_mark_internal(12,empty_ctxt,&mut t),2);
977 // I'm assuming that the rename table will behave the same....
978 }
979
980 }