From 09f84aa8f4298489828720c048ec7f769338c0e2 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 24 Nov 2013 21:18:21 -0800 Subject: [PATCH 1/3] Add SmallVector to libsyntax --- src/libsyntax/lib.rs | 1 + src/libsyntax/util/small_vector.rs | 213 +++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/libsyntax/util/small_vector.rs diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 29ed87f220283..5b12e8b8eb1fe 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -31,6 +31,7 @@ pub mod util { pub mod interner; #[cfg(test)] pub mod parser_testing; + pub mod small_vector; } pub mod syntax { diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs new file mode 100644 index 0000000000000..c6b223f7c1580 --- /dev/null +++ b/src/libsyntax/util/small_vector.rs @@ -0,0 +1,213 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use std::vec::MoveIterator; +use std::util; + +/// A vector type optimized for cases where the size is almost always 0 or 1 +pub enum SmallVector { + priv Zero, + priv One(T), + priv Many(~[T]), +} + +impl Container for SmallVector { + fn len(&self) -> uint { + match *self { + Zero => 0, + One(*) => 1, + Many(ref vals) => vals.len() + } + } +} + +impl FromIterator for SmallVector { + fn from_iterator>(iter: &mut I) -> SmallVector { + let mut v = Zero; + for val in *iter { + v.push(val); + } + v + } +} + +impl SmallVector { + pub fn zero() -> SmallVector { + Zero + } + + pub fn one(v: T) -> SmallVector { + One(v) + } + + pub fn many(vs: ~[T]) -> SmallVector { + Many(vs) + } + + pub fn push(&mut self, v: T) { + match *self { + Zero => *self = One(v), + One(*) => { + let mut tmp = Many(~[]); + util::swap(self, &mut tmp); + match *self { + Many(ref mut vs) => { + match tmp { + One(v1) => { + vs.push(v1); + vs.push(v); + } + _ => unreachable!() + } + } + _ => unreachable!() + } + } + Many(ref mut vs) => vs.push(v) + } + } + + pub fn get<'a>(&'a self, idx: uint) -> &'a T { + match *self { + One(ref v) if idx == 0 => v, + Many(ref vs) => &vs[idx], + _ => fail!("Out of bounds access") + } + } + + pub fn iter<'a>(&'a self) -> SmallVectorIterator<'a, T> { + SmallVectorIterator { + vec: self, + idx: 0 + } + } + + pub fn move_iter(self) -> SmallVectorMoveIterator { + match self { + Zero => ZeroIterator, + One(v) => OneIterator(v), + Many(vs) => ManyIterator(vs.move_iter()) + } + } +} + +pub struct SmallVectorIterator<'vec, T> { + priv vec: &'vec SmallVector, + priv idx: uint +} + +impl<'vec, T> Iterator<&'vec T> for SmallVectorIterator<'vec, T> { + fn next(&mut self) -> Option<&'vec T> { + if self.idx == self.vec.len() { + return None; + } + + self.idx += 1; + Some(self.vec.get(self.idx - 1)) + } + + fn size_hint(&self) -> (uint, Option) { + let rem = self.vec.len() - self.idx; + (rem, Some(rem)) + } +} + +pub enum SmallVectorMoveIterator { + priv ZeroIterator, + priv OneIterator(T), + priv ManyIterator(MoveIterator), +} + +impl Iterator for SmallVectorMoveIterator { + fn next(&mut self) -> Option { + match *self { + ZeroIterator => None, + OneIterator(*) => { + let mut replacement = ZeroIterator; + util::swap(self, &mut replacement); + match replacement { + OneIterator(v) => Some(v), + _ => unreachable!() + } + } + ManyIterator(ref mut inner) => inner.next() + } + } + + fn size_hint(&self) -> (uint, Option) { + match *self { + ZeroIterator => (0, Some(0)), + OneIterator(*) => (1, Some(1)), + ManyIterator(ref inner) => inner.size_hint() + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_len() { + let v: SmallVector = SmallVector::zero(); + assert_eq!(0, v.len()); + + assert_eq!(1, SmallVector::one(1).len()); + assert_eq!(5, SmallVector::many(~[1, 2, 3, 4, 5]).len()); + } + + #[test] + fn test_push_get() { + let mut v = SmallVector::zero(); + v.push(1); + assert_eq!(1, v.len()); + assert_eq!(&1, v.get(0)); + v.push(2); + assert_eq!(2, v.len()); + assert_eq!(&2, v.get(1)); + v.push(3); + assert_eq!(3, v.len()); + assert_eq!(&3, v.get(2)); + } + + #[test] + fn test_from_iterator() { + let v: SmallVector = (~[1, 2, 3]).move_iter().collect(); + assert_eq!(3, v.len()); + assert_eq!(&1, v.get(0)); + assert_eq!(&2, v.get(1)); + assert_eq!(&3, v.get(2)); + } + + #[test] + fn test_iter() { + let v = SmallVector::zero(); + let v: ~[&int] = v.iter().collect(); + assert_eq!(~[], v); + + let v = SmallVector::one(1); + assert_eq!(~[&1], v.iter().collect()); + + let v = SmallVector::many(~[1, 2, 3]); + assert_eq!(~[&1, &2, &3], v.iter().collect()); + } + + #[test] + fn test_move_iter() { + let v = SmallVector::zero(); + let v: ~[int] = v.move_iter().collect(); + assert_eq!(~[], v); + + let v = SmallVector::one(1); + assert_eq!(~[1], v.move_iter().collect()); + + let v = SmallVector::many(~[1, 2, 3]); + assert_eq!(~[1, 2, 3], v.move_iter().collect()); + } +} From c144752a2de4ffe3a2a22da9a8309ca2ecd85c58 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 24 Nov 2013 23:08:53 -0800 Subject: [PATCH 2/3] Support multiple item macros Closes #4375 --- src/librustc/front/config.rs | 34 ++---- src/librustc/front/std_inject.rs | 5 +- src/librustc/front/test.rs | 5 +- src/librustc/middle/astencode.rs | 12 +- src/librustpkg/util.rs | 5 +- src/libsyntax/ext/base.rs | 3 +- src/libsyntax/ext/expand.rs | 114 +++++++++--------- src/libsyntax/ext/tt/macro_rules.rs | 13 +- src/libsyntax/fold.rs | 53 ++++---- src/libsyntax/util/small_vector.rs | 28 +++++ .../compile-fail/macro-incomplete-parse.rs | 3 +- src/test/run-pass/macro-multiple-items.rs | 29 +++++ 12 files changed, 180 insertions(+), 124 deletions(-) create mode 100644 src/test/run-pass/macro-multiple-items.rs diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index 1602cbb6a9baf..ee1935207b17e 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -48,14 +48,6 @@ pub fn strip_items(crate: ast::Crate, ctxt.fold_crate(crate) } -fn filter_item(cx: &Context, item: @ast::item) -> Option<@ast::item> { - if item_in_cfg(cx, item) { - Some(item) - } else { - None - } -} - fn filter_view_item<'r>(cx: &Context, view_item: &'r ast::view_item) -> Option<&'r ast::view_item> { if view_item_in_cfg(cx, view_item) { @@ -66,9 +58,10 @@ fn filter_view_item<'r>(cx: &Context, view_item: &'r ast::view_item) } fn fold_mod(cx: &Context, m: &ast::_mod) -> ast::_mod { - let filtered_items = m.items.iter().filter_map(|a| { - filter_item(cx, *a).and_then(|x| cx.fold_item(x)) - }).collect(); + let filtered_items = m.items.iter() + .filter(|&a| item_in_cfg(cx, *a)) + .flat_map(|&x| cx.fold_item(x).move_iter()) + .collect(); let filtered_view_items = m.view_items.iter().filter_map(|a| { filter_view_item(cx, a).map(|x| cx.fold_view_item(x)) }).collect(); @@ -122,28 +115,25 @@ fn fold_item_underscore(cx: &Context, item: &ast::item_) -> ast::item_ { fold::noop_fold_item_underscore(&item, cx) } -fn filter_stmt(cx: &Context, stmt: @ast::Stmt) -> Option<@ast::Stmt> { +fn retain_stmt(cx: &Context, stmt: @ast::Stmt) -> bool { match stmt.node { ast::StmtDecl(decl, _) => { match decl.node { ast::DeclItem(item) => { - if item_in_cfg(cx, item) { - Some(stmt) - } else { - None - } + item_in_cfg(cx, item) } - _ => Some(stmt) + _ => true } } - _ => Some(stmt), + _ => true } } fn fold_block(cx: &Context, b: &ast::Block) -> ast::Block { - let resulting_stmts = b.stmts.iter().filter_map(|a| { - filter_stmt(cx, *a).and_then(|stmt| cx.fold_stmt(stmt)) - }).collect(); + let resulting_stmts = b.stmts.iter() + .filter(|&a| retain_stmt(cx, *a)) + .flat_map(|&stmt| cx.fold_stmt(stmt).move_iter()) + .collect(); let filtered_view_items = b.view_items.iter().filter_map(|a| { filter_view_item(cx, a).map(|x| cx.fold_view_item(x)) }).collect(); diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index c2d0251b23cc2..a40f8183e1904 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -19,6 +19,7 @@ use syntax::codemap; use syntax::fold::ast_fold; use syntax::fold; use syntax::opt_vec; +use syntax::util::small_vector::SmallVector; static STD_VERSION: &'static str = "0.9-pre"; @@ -98,14 +99,14 @@ impl fold::ast_fold for StandardLibraryInjector { } } - fn fold_item(&self, item: @ast::item) -> Option<@ast::item> { + fn fold_item(&self, item: @ast::item) -> SmallVector<@ast::item> { if !no_prelude(item.attrs) { // only recur if there wasn't `#[no_implicit_prelude];` // on this item, i.e. this means that the prelude is not // implicitly imported though the whole subtree fold::noop_fold_item(item, self) } else { - Some(item) + SmallVector::one(item) } } diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index 4d1998c8fd722..ca82502f84be1 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -26,6 +26,7 @@ use syntax::fold; use syntax::opt_vec; use syntax::print::pprust; use syntax::{ast, ast_util}; +use syntax::util::small_vector::SmallVector; struct Test { span: Span, @@ -76,7 +77,7 @@ impl fold::ast_fold for TestHarnessGenerator { } } - fn fold_item(&self, i: @ast::item) -> Option<@ast::item> { + fn fold_item(&self, i: @ast::item) -> SmallVector<@ast::item> { self.cx.path.push(i.ident); debug!("current path: {}", ast_util::path_name_i(self.cx.path.clone())); @@ -108,7 +109,7 @@ impl fold::ast_fold for TestHarnessGenerator { let res = fold::noop_fold_item(i, self); self.cx.path.pop(); - return res; + res } fn fold_mod(&self, m: &ast::_mod) -> ast::_mod { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index c0dc904b9a376..2658f7efa1c84 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -33,6 +33,7 @@ use syntax::codemap; use syntax::fold::*; use syntax::fold; use syntax::parse::token; +use syntax::util::small_vector::SmallVector; use syntax; use std::at_vec; @@ -347,13 +348,20 @@ fn simplify_ast(ii: &ast::inlined_item) -> ast::inlined_item { match *ii { //hack: we're not dropping items - ast::ii_item(i) => ast::ii_item(fld.fold_item(i).unwrap()), + ast::ii_item(i) => ast::ii_item(get_only_one(fld.fold_item(i))), ast::ii_method(d, is_provided, m) => ast::ii_method(d, is_provided, fld.fold_method(m)), ast::ii_foreign(i) => ast::ii_foreign(fld.fold_foreign_item(i)) } } +fn get_only_one(mut v: SmallVector) -> T { + if v.len() != 1 { + fail!("Attempting to extract unique member but there isn't one"); + } + v.pop() +} + fn decode_ast(par_doc: ebml::Doc) -> ast::inlined_item { let chi_doc = par_doc.get(c::tag_tree as uint); let mut d = reader::Decoder(chi_doc); @@ -379,7 +387,7 @@ fn renumber_ast(xcx: @ExtendedDecodeContext, ii: ast::inlined_item) xcx: xcx, }; match ii { - ast::ii_item(i) => ast::ii_item(fld.fold_item(i).unwrap()), + ast::ii_item(i) => ast::ii_item(get_only_one(fld.fold_item(i))), ast::ii_method(d, is_provided, m) => ast::ii_method(xcx.tr_def_id(d), is_provided, fld.fold_method(m)), ast::ii_foreign(i) => ast::ii_foreign(fld.fold_foreign_item(i)), diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 9842508a5c320..ec5922cf67a71 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -22,6 +22,7 @@ use syntax::{ast, attr, codemap, diagnostic, fold, visit}; use syntax::attr::AttrMetaMethods; use syntax::fold::ast_fold; use syntax::visit::Visitor; +use syntax::util::small_vector::SmallVector; use rustc::back::link::output_type_exe; use rustc::back::link; use rustc::driver::session::{lib_crate, bin_crate}; @@ -99,7 +100,7 @@ fn fold_mod(_ctx: @mut ReadyCtx, m: &ast::_mod, fold: &CrateSetup) } fn fold_item(ctx: @mut ReadyCtx, item: @ast::item, fold: &CrateSetup) - -> Option<@ast::item> { + -> SmallVector<@ast::item> { ctx.path.push(item.ident); let mut cmds = ~[]; @@ -142,7 +143,7 @@ struct CrateSetup { } impl fold::ast_fold for CrateSetup { - fn fold_item(&self, item: @ast::item) -> Option<@ast::item> { + fn fold_item(&self, item: @ast::item) -> SmallVector<@ast::item> { fold_item(self.ctx, item, self) } fn fold_mod(&self, module: &ast::_mod) -> ast::_mod { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index ccb88e0c9c019..deaa821cd45f2 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -18,6 +18,7 @@ use ext::expand; use parse; use parse::token; use parse::token::{ident_to_str, intern, str_to_ident}; +use util::small_vector::SmallVector; use std::hashmap::HashMap; @@ -131,7 +132,7 @@ pub type SyntaxExpanderTTItemFunNoCtxt = pub trait AnyMacro { fn make_expr(&self) -> @ast::Expr; - fn make_item(&self) -> Option<@ast::item>; + fn make_items(&self) -> SmallVector<@ast::item>; fn make_stmt(&self) -> @ast::Stmt; } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f23e13b89311c..3719362a7dfdb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -28,6 +28,7 @@ use parse::token; use parse::token::{fresh_mark, fresh_name, ident_to_str, intern}; use visit; use visit::Visitor; +use util::small_vector::SmallVector; use std::vec; @@ -310,7 +311,7 @@ pub fn expand_item(extsbox: @mut SyntaxEnv, cx: @ExtCtxt, it: @ast::item, fld: &MacroExpander) - -> Option<@ast::item> { + -> SmallVector<@ast::item> { match it.node { ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld), ast::item_mod(_) | ast::item_foreign_mod(_) => { @@ -337,7 +338,7 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv, cx: @ExtCtxt, it: @ast::item, fld: &MacroExpander) - -> Option<@ast::item> { + -> SmallVector<@ast::item> { let (pth, tts, ctxt) = match it.node { item_mac(codemap::Spanned { node: mac_invoc_tt(ref pth, ref tts, ctxt), @@ -396,28 +397,30 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv, it.span, format!("{}! is not legal in item position", extnamestr)) }; - let maybe_it = match expanded { + let items = match expanded { MRItem(it) => { - mark_item(it,fm) - .and_then(|i| fld.fold_item(i)) + mark_item(it,fm).move_iter() + .flat_map(|i| fld.fold_item(i).move_iter()) + .collect() } MRExpr(_) => { cx.span_fatal(pth.span, format!("expr macro in item position: {}", extnamestr)) } MRAny(any_macro) => { - any_macro.make_item() - .and_then(|i| mark_item(i,fm)) - .and_then(|i| fld.fold_item(i)) + any_macro.make_items().move_iter() + .flat_map(|i| mark_item(i, fm).move_iter()) + .flat_map(|i| fld.fold_item(i).move_iter()) + .collect() } MRDef(ref mdef) => { // yikes... no idea how to apply the mark to this. I'm afraid // we're going to have to wait-and-see on this one. insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext)); - None + SmallVector::zero() } }; cx.bt_pop(); - return maybe_it; + return items; } @@ -442,7 +445,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv, cx: @ExtCtxt, s: &Stmt, fld: &MacroExpander) - -> Option<@Stmt> { + -> SmallVector<@Stmt> { // why the copying here and not in expand_expr? // looks like classic changed-in-only-one-place let (pth, tts, semi, ctxt) = match s.node { @@ -461,7 +464,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv, } let extname = &pth.segments[0].identifier; let extnamestr = ident_to_str(extname); - let fully_expanded: @ast::Stmt = match (*extsbox).find(&extname.name) { + let fully_expanded: SmallVector<@Stmt> = match (*extsbox).find(&extname.name) { None => { cx.span_fatal(pth.span, format!("macro undefined: '{}'", extnamestr)) } @@ -501,22 +504,15 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv, let marked_after = mark_stmt(expanded,fm); // Keep going, outside-in. - let fully_expanded = match fld.fold_stmt(marked_after) { - Some(stmt) => { - let fully_expanded = &stmt.node; - cx.bt_pop(); - @Spanned { - span: stmt.span, - node: (*fully_expanded).clone(), - } - } - None => { - cx.span_fatal(pth.span, - "macro didn't expand to a statement") - } - }; - - fully_expanded + let fully_expanded = fld.fold_stmt(marked_after); + if fully_expanded.is_empty() { + cx.span_fatal(pth.span, + "macro didn't expand to a statement"); + } + cx.bt_pop(); + fully_expanded.move_iter() + .map(|s| @Spanned { span: s.span, node: s.node.clone() }) + .collect() } _ => { @@ -525,21 +521,23 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv, } }; - match fully_expanded.node { - StmtExpr(e, stmt_id) if semi => { - Some(@Spanned { - span: fully_expanded.span, - node: StmtSemi(e, stmt_id), - }) + fully_expanded.move_iter().map(|s| { + match s.node { + StmtExpr(e, stmt_id) if semi => { + @Spanned { + span: s.span, + node: StmtSemi(e, stmt_id) + } + } + _ => s /* might already have a semi */ } - _ => Some(fully_expanded), /* might already have a semi */ - } + }).collect() } // expand a non-macro stmt. this is essentially the fallthrough for // expand_stmt, above. fn expand_non_macro_stmt(exts: SyntaxEnv, s: &Stmt, fld: &MacroExpander) - -> Option<@Stmt> { + -> SmallVector<@Stmt> { // is it a let? match s.node { StmtDecl(@Spanned { @@ -590,7 +588,7 @@ fn expand_non_macro_stmt(exts: SyntaxEnv, s: &Stmt, fld: &MacroExpander) id: id, span: span, }; - Some(@Spanned { + SmallVector::one(@Spanned { node: StmtDecl(@Spanned { node: DeclLocal(rewritten_local), span: stmt_span @@ -679,13 +677,9 @@ pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander) let pending_renames = block_info.pending_renames; let rename_fld = renames_to_fold(pending_renames); let new_view_items = b.view_items.map(|x| fld.fold_view_item(x)); - let mut new_stmts = ~[]; - for x in b.stmts.iter() { - match fld.fold_stmt(mustbesome(rename_fld.fold_stmt(*x))) { - Some(s) => new_stmts.push(s), - None => () - } - } + let new_stmts = b.stmts.iter() + .flat_map(|x| fld.fold_stmt(mustbeone(rename_fld.fold_stmt(*x))).move_iter()) + .collect(); let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(x))); Block{ view_items: new_view_items, @@ -697,13 +691,12 @@ pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander) } } -// rename_fold should never return "None". -// (basically, just .get() with a better message...) -fn mustbesome(val : Option) -> T { - match val { - Some(v) => v, - None => fail!("rename_fold returned None") +// rename_fold should never return anything other than one thing +fn mustbeone(mut val : SmallVector) -> T { + if val.len() != 1 { + fail!("rename_fold didn't return one value"); } + val.pop() } // get the (innermost) BlockInfo from an exts stack @@ -741,10 +734,11 @@ pub fn renames_to_fold(renames: @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold { // perform a bunch of renames fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt { - match folder.fold_stmt(&stmt) { - Some(s) => s, - None => fail!("renaming of stmt produced None") + let mut stmts = folder.fold_stmt(&stmt); + if stmts.len() != 1 { + fail!("renaming of stmt did not produce one stmt"); } + stmts.pop() } @@ -1025,14 +1019,14 @@ impl ast_fold for MacroExpander { self) } - fn fold_item(&self, item: @ast::item) -> Option<@ast::item> { + fn fold_item(&self, item: @ast::item) -> SmallVector<@ast::item> { expand_item(self.extsbox, self.cx, item, self) } - fn fold_stmt(&self, stmt: &ast::Stmt) -> Option<@ast::Stmt> { + fn fold_stmt(&self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> { expand_stmt(self.extsbox, self.cx, stmt, @@ -1191,11 +1185,15 @@ fn mark_expr(expr : @ast::Expr, m : Mrk) -> @ast::Expr { // apply a given mark to the given stmt. Used following the expansion of a macro. fn mark_stmt(expr : &ast::Stmt, m : Mrk) -> @ast::Stmt { - new_mark_folder(m).fold_stmt(expr).unwrap() + let mut stmts = new_mark_folder(m).fold_stmt(expr); + if stmts.len() != 1 { + fail!("marking a stmt didn't return a stmt"); + } + stmts.pop() } // apply a given mark to the given item. Used following the expansion of a macro. -fn mark_item(expr : @ast::item, m : Mrk) -> Option<@ast::item> { +fn mark_item(expr : @ast::item, m : Mrk) -> SmallVector<@ast::item> { new_mark_folder(m).fold_item(expr) } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 343116df64276..3cc00ef819958 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -24,6 +24,7 @@ use parse::attr::parser_attr; use parse::token::{get_ident_interner, special_idents, gensym_ident, ident_to_str}; use parse::token::{FAT_ARROW, SEMI, nt_matchers, nt_tt, EOF}; use print; +use util::small_vector::SmallVector; struct ParserAnyMacro { parser: @Parser, @@ -54,9 +55,15 @@ impl AnyMacro for ParserAnyMacro { self.ensure_complete_parse(true); ret } - fn make_item(&self) -> Option<@ast::item> { - let attrs = self.parser.parse_outer_attributes(); - let ret = self.parser.parse_item(attrs); + fn make_items(&self) -> SmallVector<@ast::item> { + let mut ret = SmallVector::zero(); + loop { + let attrs = self.parser.parse_outer_attributes(); + match self.parser.parse_item(attrs) { + Some(item) => ret.push(item), + None => break + } + } self.ensure_complete_parse(false); ret } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index b268988c3c567..4c0653a3c04f3 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -13,6 +13,7 @@ use ast; use codemap::{respan, Span, Spanned}; use parse::token; use opt_vec::OptVec; +use util::small_vector::SmallVector; // We may eventually want to be able to fold over type parameters, too. pub trait ast_fold { @@ -113,7 +114,7 @@ pub trait ast_fold { } } - fn fold_item(&self, i: @item) -> Option<@item> { + fn fold_item(&self, i: @item) -> SmallVector<@item> { noop_fold_item(i, self) } @@ -159,7 +160,7 @@ pub trait ast_fold { noop_fold_block(b, self) } - fn fold_stmt(&self, s: &Stmt) -> Option<@Stmt> { + fn fold_stmt(&self, s: &Stmt) -> SmallVector<@Stmt> { noop_fold_stmt(s, self) } @@ -216,23 +217,20 @@ pub trait ast_fold { } } - fn fold_decl(&self, d: @Decl) -> Option<@Decl> { + fn fold_decl(&self, d: @Decl) -> SmallVector<@Decl> { let node = match d.node { - DeclLocal(ref l) => Some(DeclLocal(self.fold_local(*l))), + DeclLocal(ref l) => SmallVector::one(DeclLocal(self.fold_local(*l))), DeclItem(it) => { - match self.fold_item(it) { - Some(it_folded) => Some(DeclItem(it_folded)), - None => None, - } + self.fold_item(it).move_iter().map(|i| DeclItem(i)).collect() } }; - node.map(|node| { + node.move_iter().map(|node| { @Spanned { node: node, span: d.span, } - }) + }).collect() } fn fold_expr(&self, e: @Expr) -> @Expr { @@ -618,13 +616,7 @@ fn fold_variant_arg_(va: &variant_arg, folder: &T) pub fn noop_fold_block(b: &Block, folder: &T) -> Block { let view_items = b.view_items.map(|x| folder.fold_view_item(x)); - let mut stmts = ~[]; - for stmt in b.stmts.iter() { - match folder.fold_stmt(*stmt) { - None => {} - Some(stmt) => stmts.push(stmt) - } - } + let stmts = b.stmts.iter().flat_map(|s| folder.fold_stmt(*s).move_iter()).collect(); ast::Block { view_items: view_items, stmts: stmts, @@ -711,7 +703,7 @@ pub fn noop_fold_mod(m: &_mod, folder: &T) -> _mod { view_items: m.view_items .iter() .map(|x| folder.fold_view_item(x)).collect(), - items: m.items.iter().filter_map(|x| folder.fold_item(*x)).collect(), + items: m.items.iter().flat_map(|x| folder.fold_item(*x).move_iter()).collect(), } } @@ -728,10 +720,10 @@ pub fn noop_fold_crate(c: Crate, folder: &T) -> Crate { } pub fn noop_fold_item(i: @ast::item, folder: &T) - -> Option<@ast::item> { + -> SmallVector<@ast::item> { let fold_attribute = |x| fold_attribute_(x, folder); - Some(@ast::item { + SmallVector::one(@ast::item { ident: folder.fold_ident(i.ident), attrs: i.attrs.map(|e| fold_attribute(*e)), id: folder.new_id(i.id), @@ -867,27 +859,26 @@ pub fn noop_fold_expr(e: @ast::Expr, folder: &T) -> @ast::Expr { } } -pub fn noop_fold_stmt(s: &Stmt, folder: &T) -> Option<@Stmt> { - let node = match s.node { +pub fn noop_fold_stmt(s: &Stmt, folder: &T) -> SmallVector<@Stmt> { + let nodes = match s.node { StmtDecl(d, nid) => { - match folder.fold_decl(d) { - Some(d) => Some(StmtDecl(d, folder.new_id(nid))), - None => None, - } + folder.fold_decl(d).move_iter() + .map(|d| StmtDecl(d, folder.new_id(nid))) + .collect() } StmtExpr(e, nid) => { - Some(StmtExpr(folder.fold_expr(e), folder.new_id(nid))) + SmallVector::one(StmtExpr(folder.fold_expr(e), folder.new_id(nid))) } StmtSemi(e, nid) => { - Some(StmtSemi(folder.fold_expr(e), folder.new_id(nid))) + SmallVector::one(StmtSemi(folder.fold_expr(e), folder.new_id(nid))) } - StmtMac(ref mac, semi) => Some(StmtMac(folder.fold_mac(mac), semi)) + StmtMac(ref mac, semi) => SmallVector::one(StmtMac(folder.fold_mac(mac), semi)) }; - node.map(|node| @Spanned { + nodes.move_iter().map(|node| @Spanned { node: node, span: folder.new_span(s.span), - }) + }).collect() } #[cfg(test)] diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index c6b223f7c1580..0196a0212e23d 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -73,6 +73,22 @@ impl SmallVector { } } + pub fn pop(&mut self) -> T { + match *self { + Zero => fail!("attempted to pop from an empty SmallVector"), + One(*) => { + let mut tmp = Zero; + util::swap(self, &mut tmp); + match tmp { + One(v) => v, + _ => unreachable!() + } + } + // Should this reduce to a One if possible? + Many(ref mut vs) => vs.pop() + } + } + pub fn get<'a>(&'a self, idx: uint) -> &'a T { match *self { One(ref v) if idx == 0 => v, @@ -176,6 +192,18 @@ mod test { assert_eq!(&3, v.get(2)); } + #[test] + fn test_pop() { + let mut v = SmallVector::one(1); + assert_eq!(1, v.pop()); + assert_eq!(0, v.len()); + + let mut v= SmallVector::many(~[1, 2]); + assert_eq!(2, v.pop()); + assert_eq!(1, v.pop()); + assert_eq!(0, v.len()); + } + #[test] fn test_from_iterator() { let v: SmallVector = (~[1, 2, 3]).move_iter().collect(); diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs index 598e0706adcca..98e0ecfad8645 100644 --- a/src/test/compile-fail/macro-incomplete-parse.rs +++ b/src/test/compile-fail/macro-incomplete-parse.rs @@ -13,7 +13,8 @@ macro_rules! ignored_item { () => { fn foo() {} - fn bar() {} //~ ERROR macro expansion ignores token `fn` + fn bar() {} + , //~ ERROR macro expansion ignores token `,` } } diff --git a/src/test/run-pass/macro-multiple-items.rs b/src/test/run-pass/macro-multiple-items.rs new file mode 100644 index 0000000000000..c96f8c7b7bbe8 --- /dev/null +++ b/src/test/run-pass/macro-multiple-items.rs @@ -0,0 +1,29 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-pretty - token trees can't pretty print + +#[feature(macro_rules)]; + +macro_rules! make_foo( + () => ( + struct Foo; + + impl Foo { + fn bar(&self) {} + } + ) +) + +make_foo!() + +pub fn main() { + Foo.bar() +} From c403c1f18e987482a050299a647b0fbcdfea09ad Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 25 Nov 2013 20:02:15 -0800 Subject: [PATCH 3/3] Clean up SmallVector use a bit --- src/librustc/middle/astencode.rs | 14 ++-- src/libsyntax/ext/expand.rs | 26 ++----- src/libsyntax/util/small_vector.rs | 106 ++++++++--------------------- 3 files changed, 38 insertions(+), 108 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 2658f7efa1c84..64ce1e7b4e7de 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -33,7 +33,6 @@ use syntax::codemap; use syntax::fold::*; use syntax::fold; use syntax::parse::token; -use syntax::util::small_vector::SmallVector; use syntax; use std::at_vec; @@ -348,20 +347,14 @@ fn simplify_ast(ii: &ast::inlined_item) -> ast::inlined_item { match *ii { //hack: we're not dropping items - ast::ii_item(i) => ast::ii_item(get_only_one(fld.fold_item(i))), + ast::ii_item(i) => ast::ii_item(fld.fold_item(i) + .expect_one("expected one item")), ast::ii_method(d, is_provided, m) => ast::ii_method(d, is_provided, fld.fold_method(m)), ast::ii_foreign(i) => ast::ii_foreign(fld.fold_foreign_item(i)) } } -fn get_only_one(mut v: SmallVector) -> T { - if v.len() != 1 { - fail!("Attempting to extract unique member but there isn't one"); - } - v.pop() -} - fn decode_ast(par_doc: ebml::Doc) -> ast::inlined_item { let chi_doc = par_doc.get(c::tag_tree as uint); let mut d = reader::Decoder(chi_doc); @@ -387,7 +380,8 @@ fn renumber_ast(xcx: @ExtendedDecodeContext, ii: ast::inlined_item) xcx: xcx, }; match ii { - ast::ii_item(i) => ast::ii_item(get_only_one(fld.fold_item(i))), + ast::ii_item(i) => ast::ii_item(fld.fold_item(i) + .expect_one("expected one item")), ast::ii_method(d, is_provided, m) => ast::ii_method(xcx.tr_def_id(d), is_provided, fld.fold_method(m)), ast::ii_foreign(i) => ast::ii_foreign(fld.fold_foreign_item(i)), diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3719362a7dfdb..45f82d9b3a935 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -678,7 +678,9 @@ pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander) let rename_fld = renames_to_fold(pending_renames); let new_view_items = b.view_items.map(|x| fld.fold_view_item(x)); let new_stmts = b.stmts.iter() - .flat_map(|x| fld.fold_stmt(mustbeone(rename_fld.fold_stmt(*x))).move_iter()) + .map(|x| rename_fld.fold_stmt(*x) + .expect_one("rename_fold didn't return one value")) + .flat_map(|x| fld.fold_stmt(x).move_iter()) .collect(); let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(x))); Block{ @@ -691,14 +693,6 @@ pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander) } } -// rename_fold should never return anything other than one thing -fn mustbeone(mut val : SmallVector) -> T { - if val.len() != 1 { - fail!("rename_fold didn't return one value"); - } - val.pop() -} - // get the (innermost) BlockInfo from an exts stack fn get_block_info(exts : SyntaxEnv) -> BlockInfo { match exts.find_in_topmost_frame(&intern(special_block_name)) { @@ -734,11 +728,8 @@ pub fn renames_to_fold(renames: @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold { // perform a bunch of renames fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt { - let mut stmts = folder.fold_stmt(&stmt); - if stmts.len() != 1 { - fail!("renaming of stmt did not produce one stmt"); - } - stmts.pop() + folder.fold_stmt(&stmt) + .expect_one("renaming of stmt did not produce one stmt") } @@ -1185,11 +1176,8 @@ fn mark_expr(expr : @ast::Expr, m : Mrk) -> @ast::Expr { // apply a given mark to the given stmt. Used following the expansion of a macro. fn mark_stmt(expr : &ast::Stmt, m : Mrk) -> @ast::Stmt { - let mut stmts = new_mark_folder(m).fold_stmt(expr); - if stmts.len() != 1 { - fail!("marking a stmt didn't return a stmt"); - } - stmts.pop() + new_mark_folder(m).fold_stmt(expr) + .expect_one("marking a stmt didn't return a stmt") } // apply a given mark to the given item. Used following the expansion of a macro. diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index 0196a0212e23d..71eee8b7ccca0 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -54,42 +54,17 @@ impl SmallVector { match *self { Zero => *self = One(v), One(*) => { - let mut tmp = Many(~[]); - util::swap(self, &mut tmp); - match *self { - Many(ref mut vs) => { - match tmp { - One(v1) => { - vs.push(v1); - vs.push(v); - } - _ => unreachable!() - } - } + let one = util::replace(self, Zero); + match one { + One(v1) => util::replace(self, Many(~[v1, v])), _ => unreachable!() - } + }; } Many(ref mut vs) => vs.push(v) } } - pub fn pop(&mut self) -> T { - match *self { - Zero => fail!("attempted to pop from an empty SmallVector"), - One(*) => { - let mut tmp = Zero; - util::swap(self, &mut tmp); - match tmp { - One(v) => v, - _ => unreachable!() - } - } - // Should this reduce to a One if possible? - Many(ref mut vs) => vs.pop() - } - } - - pub fn get<'a>(&'a self, idx: uint) -> &'a T { + fn get<'a>(&'a self, idx: uint) -> &'a T { match *self { One(ref v) if idx == 0 => v, Many(ref vs) => &vs[idx], @@ -97,10 +72,11 @@ impl SmallVector { } } - pub fn iter<'a>(&'a self) -> SmallVectorIterator<'a, T> { - SmallVectorIterator { - vec: self, - idx: 0 + pub fn expect_one(self, err: &'static str) -> T { + match self { + One(v) => v, + Many([v]) => v, + _ => fail!(err) } } @@ -113,27 +89,6 @@ impl SmallVector { } } -pub struct SmallVectorIterator<'vec, T> { - priv vec: &'vec SmallVector, - priv idx: uint -} - -impl<'vec, T> Iterator<&'vec T> for SmallVectorIterator<'vec, T> { - fn next(&mut self) -> Option<&'vec T> { - if self.idx == self.vec.len() { - return None; - } - - self.idx += 1; - Some(self.vec.get(self.idx - 1)) - } - - fn size_hint(&self) -> (uint, Option) { - let rem = self.vec.len() - self.idx; - (rem, Some(rem)) - } -} - pub enum SmallVectorMoveIterator { priv ZeroIterator, priv OneIterator(T), @@ -192,18 +147,6 @@ mod test { assert_eq!(&3, v.get(2)); } - #[test] - fn test_pop() { - let mut v = SmallVector::one(1); - assert_eq!(1, v.pop()); - assert_eq!(0, v.len()); - - let mut v= SmallVector::many(~[1, 2]); - assert_eq!(2, v.pop()); - assert_eq!(1, v.pop()); - assert_eq!(0, v.len()); - } - #[test] fn test_from_iterator() { let v: SmallVector = (~[1, 2, 3]).move_iter().collect(); @@ -214,28 +157,33 @@ mod test { } #[test] - fn test_iter() { + fn test_move_iter() { let v = SmallVector::zero(); - let v: ~[&int] = v.iter().collect(); + let v: ~[int] = v.move_iter().collect(); assert_eq!(~[], v); let v = SmallVector::one(1); - assert_eq!(~[&1], v.iter().collect()); + assert_eq!(~[1], v.move_iter().collect()); let v = SmallVector::many(~[1, 2, 3]); - assert_eq!(~[&1, &2, &3], v.iter().collect()); + assert_eq!(~[1, 2, 3], v.move_iter().collect()); } #[test] - fn test_move_iter() { - let v = SmallVector::zero(); - let v: ~[int] = v.move_iter().collect(); - assert_eq!(~[], v); + #[should_fail] + fn test_expect_one_zero() { + let _: int = SmallVector::zero().expect_one(""); + } - let v = SmallVector::one(1); - assert_eq!(~[1], v.move_iter().collect()); + #[test] + #[should_fail] + fn test_expect_one_many() { + SmallVector::many(~[1, 2]).expect_one(""); + } - let v = SmallVector::many(~[1, 2, 3]); - assert_eq!(~[1, 2, 3], v.move_iter().collect()); + #[test] + fn test_expect_one_one() { + assert_eq!(1, SmallVector::one(1).expect_one("")); + assert_eq!(1, SmallVector::many(~[1]).expect_one("")); } }