diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index d183fae3ac6ee..c8a1b96aefdd5 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -266,6 +266,7 @@ macro_rules! try( ) /// Create a `std::vec::Vec` containing the arguments. +#[cfg(stage0)] #[macro_export] macro_rules! vec( ($($e:expr),*) => ({ @@ -276,6 +277,17 @@ macro_rules! vec( }) ) +/// Create a `std::vec::Vec` containing the arguments. +#[cfg(not(stage0))] +#[macro_export] +macro_rules! vec( + ($($e:expr),*) => ({ + // leading _ to allow empty construction without a warning. + let mut _temp = ::std::vec::Vec::with_capacity(#($e)); + $(_temp.push($e);)* + _temp + }) +) /// A macro to select an event from a number of ports. /// diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 24b8a34577638..1d26a4f340c25 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -574,7 +574,10 @@ pub enum TokenTree { TTSeq(Span, @Vec , Option<::parse::token::Token>, bool), // a syntactic variable that will be filled in by macro expansion. - TTNonterminal(Span, Ident) + TTNonterminal(Span, Ident), + + // #($v), expanding to the number of occurrences of a syntactic variable + TTMatchCount(Span, Ident) } // diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 14847aee8cf61..6bb0c9fe7137f 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -560,7 +560,9 @@ fn mk_tt(cx: &ExtCtxt, sp: Span, tt: &ast::TokenTree) -> Vec<@ast::Stmt> { vec!(e_to_toks)); vec!(cx.stmt_expr(e_push)) - } + }, + + ast::TTMatchCount(_, _) => fail!("TTMatchCount in quote!") } } diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index 933fbe3d56624..1a03234b64f93 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -9,11 +9,11 @@ // except according to those terms. use ast; -use ast::{TokenTree, TTDelim, TTTok, TTSeq, TTNonterminal, Ident}; +use ast::{TokenTree, TTDelim, TTTok, TTSeq, TTNonterminal, TTMatchCount, Ident, TyU}; use codemap::{Span, DUMMY_SP}; use diagnostic::SpanHandler; use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal}; -use parse::token::{EOF, INTERPOLATED, IDENT, Token, NtIdent}; +use parse::token::{EOF, INTERPOLATED, IDENT, LIT_UINT, Token, NtIdent}; use parse::token; use parse::lexer::TokenAndSpan; @@ -97,7 +97,6 @@ pub fn dup_tt_reader<'a>(r: &TtReader<'a>) -> TtReader<'a> { } } - fn lookup_cur_matched_by_matched(r: &TtReader, start: @NamedMatch) -> @NamedMatch { fn red(ad: @NamedMatch, idx: &uint) -> @NamedMatch { @@ -112,6 +111,13 @@ fn lookup_cur_matched_by_matched(r: &TtReader, start: @NamedMatch) r.repeat_idx.borrow().iter().fold(start, red) } +fn count_matches(start: @NamedMatch) -> uint { + match *start { + MatchedNonterminal(_) => 1, + MatchedSeq(ref ads, _) => ads.iter().fold(0, |s, &m| s + count_matches(m)) + } +} + fn lookup_cur_matched(r: &TtReader, name: Ident) -> @NamedMatch { let matched_opt = r.interpolations.borrow().find_copy(&name); match matched_opt { @@ -161,7 +167,8 @@ fn lockstep_iter_size(t: &TokenTree, r: &TtReader) -> LockstepIterSize { TTNonterminal(_, name) => match *lookup_cur_matched(r, name) { MatchedNonterminal(_) => LisUnconstrained, MatchedSeq(ref ads, _) => LisConstraint(ads.len(), name) - } + }, + TTMatchCount(_, _) => LisUnconstrained } } @@ -303,6 +310,13 @@ pub fn tt_next_token(r: &TtReader) -> TokenAndSpan { } } } + TTMatchCount(sp, ident) => { + let count = count_matches(lookup_cur_matched(r, ident)); + r.cur_span.set(sp); + r.cur_tok.set(LIT_UINT(count as u64, TyU)); + r.stack.get().idx.set(r.stack.get().idx.get() + 1u); + return ret_val; + } } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 0afde5be9a076..05ab9bb3b9561 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -382,7 +382,9 @@ pub fn fold_tts(tts: &[TokenTree], fld: &mut T) -> Vec { sep.as_ref().map(|tok|maybe_fold_ident(tok,fld)), is_optional), TTNonterminal(sp,ref ident) => - TTNonterminal(sp,fld.fold_ident(*ident)) + TTNonterminal(sp,fld.fold_ident(*ident)), + TTMatchCount(sp, ref ident) => + TTMatchCount(sp, fld.fold_ident(*ident)) } }).collect() } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index adf3ac0e17d1a..f12e99b6ab3a4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -48,7 +48,7 @@ use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField}; use ast::{StructVariantKind, BiSub}; use ast::StrStyle; use ast::{SelfRegion, SelfStatic, SelfUniq, SelfValue}; -use ast::{TokenTree, TraitMethod, TraitRef, TTDelim, TTSeq, TTTok}; +use ast::{TokenTree, TraitMethod, TraitRef, TTDelim, TTSeq, TTTok, TTMatchCount}; use ast::{TTNonterminal, TupleVariantKind, Ty, Ty_, TyBot, TyBox}; use ast::{TypeField, TyFixedLengthVec, TyClosure, TyBareFn, TyTypeof}; use ast::{TyInfer, TypeMethod}; @@ -2122,6 +2122,15 @@ impl<'a> Parser<'a> { p.fatal(format!("incorrect close delimiter: `{}`", token_str)) }, + token::POUND if p.quote_depth > 0u && p.look_ahead(1, |t| *t == token::LPAREN) => { + p.bump(); + let sp = p.span; + p.expect(&token::LPAREN); + p.expect(&token::DOLLAR); + let ident = p.parse_ident(); + p.expect(&token::RPAREN); + TTMatchCount(mk_sp(sp.lo, p.span.hi), ident) + }, /* we ought to allow different depths of unquotation */ token::DOLLAR if p.quote_depth > 0u => { p.bump(); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index a9ab596b300d5..b8a58bcf29769 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -811,6 +811,11 @@ impl<'a> State<'a> { try!(word(&mut self.s, "$")); self.print_ident(name) } + ast::TTMatchCount(_, name) => { + try!(word(&mut self.s, "#($")); + try!(self.print_ident(name)); + word(&mut self.s, ")") + } } } diff --git a/src/test/compile-fail/macro-match-count.rs b/src/test/compile-fail/macro-match-count.rs new file mode 100644 index 0000000000000..9af398b15d4be --- /dev/null +++ b/src/test/compile-fail/macro-match-count.rs @@ -0,0 +1,23 @@ +// Copyright 2014 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. + +#[feature(macro_rules)]; + +pub fn main() { + macro_rules! count_arguments( + (constant) => ( #(1) ); //~ ERROR: expected ident, found `1` + (sequence $($e:expr)*) => ( #($($e)*) ); //~ ERROR: expected ident, found `(` + (delimited_sequence $($e:expr),*) => ( #($($e)*) ); //~ ERROR: expected ident, found `(` + ) + + count_arguments!(constant); + count_arguments!(sequence 1 2 3 4 5); + count_arguments!(delimited_sequence 1, 2, 3, 4, 5); +} diff --git a/src/test/run-pass/macro-match-count.rs b/src/test/run-pass/macro-match-count.rs new file mode 100644 index 0000000000000..52c8d87dc2dd9 --- /dev/null +++ b/src/test/run-pass/macro-match-count.rs @@ -0,0 +1,35 @@ +// Copyright 2014 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. + +#[feature(macro_rules)]; + +pub fn main() { + macro_rules! count_arguments( + (single $e:expr) => ( #($e) ); + (sequence $($e:expr)*) => ( #($e) ); + (sequence_with_delimiter $($e:expr),*) => ( #($e) ); + (nested_sequence_total_count { $($tk:expr => { $($k:expr => $v:expr),* }),* }) => + ( (#($tk), #($k), #($v)) ); + ) + + assert_eq!(count_arguments!(single 42), 1); + assert_eq!(count_arguments!(sequence 1 2 3 4 5), 5); + assert_eq!(count_arguments!(sequence_with_delimiter 1, 2, 3, 4), 4); + assert_eq!(count_arguments!(nested_sequence_total_count { + "key" => { + 0.1 => 0.3, + 0.2 => 0.4 + }, + "value" => { + 5 => true, + 42 => false + } + }), (2, 4, 4)); +}