From efadf8b4889b9a5e7bfd776f41775d5151ceb7a8 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sat, 17 May 2014 21:00:18 -0700 Subject: [PATCH] Add syntax extension ary![expr, ..N] ary![expr, ..N] expands to an array by repeating `expr` N times. This is in contrast to [expr, ..N] which Copies `expr` N times. Sample usage: let x = ary![None::>, ..10]; The one limitation is the count needs to be an integral literal, it cannot be a static or a compile-time expression. --- src/libsyntax/ext/ary.rs | 50 ++++++++++++++++ src/libsyntax/ext/base.rs | 3 + src/libsyntax/lib.rs | 1 + .../syntax-extension-ary-non-literal.rs | 14 +++++ ...tax-extension-ary-non-positive-integral.rs | 15 +++++ src/test/run-pass/syntax-extension-ary.rs | 58 +++++++++++++++++++ 6 files changed, 141 insertions(+) create mode 100644 src/libsyntax/ext/ary.rs create mode 100644 src/test/compile-fail/syntax-extension-ary-non-literal.rs create mode 100644 src/test/compile-fail/syntax-extension-ary-non-positive-integral.rs create mode 100644 src/test/run-pass/syntax-extension-ary.rs diff --git a/src/libsyntax/ext/ary.rs b/src/libsyntax/ext/ary.rs new file mode 100644 index 0000000000000..00db0bae4135e --- /dev/null +++ b/src/libsyntax/ext/ary.rs @@ -0,0 +1,50 @@ +// 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. + +//! Implementation of the ary![] macro + +use ast; +use codemap::Span; +use ext::base; +use ext::base::{ExtCtxt, MacExpr, MacResult, DummyResult}; +use ext::build::AstBuilder; +use parse; +use parse::token; + +pub fn expand_ary(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box { + let mut p = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), Vec::from_slice(tts)); + let val_expr = p.parse_expr(); + p.expect(&token::COMMA); + p.expect(&token::DOTDOT); + // negative literals should not be a fatal error + if p.eat(&token::BINOP(token::MINUS)) { + p.span_err(p.last_span, "expected positive integral literal"); + return DummyResult::expr(sp); + } + let lit = p.parse_lit(); + let count = match lit.node { + ast::LitInt(i, _) | ast::LitIntUnsuffixed(i) if i > 0 => i as u64, + ast::LitUint(u, _) if u > 0 => u, + _ => { + p.span_err(lit.span, "expected positive integral literal"); + return DummyResult::expr(sp); + } + }; + let count = match count.to_uint() { + None => { + p.span_err(lit.span, "integral literal out of range"); + return DummyResult::expr(sp); + } + Some(x) => x + }; + let exprs = Vec::from_fn(count, |_| val_expr.clone()); + MacExpr::new(cx.expr_vec(sp, exprs)) +} diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 06b56bbe472a2..063e8aa6bf03e 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -359,6 +359,9 @@ pub fn syntax_expander_table() -> SyntaxEnv { syntax_expanders.insert(intern("trace_macros"), builtin_normal_expander( ext::trace_macros::expand_trace_macros)); + syntax_expanders.insert(intern("ary"), + builtin_normal_expander( + ext::ary::expand_ary)); syntax_expanders } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 1086295d427c9..42e5063df4273 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -98,6 +98,7 @@ pub mod ext { pub mod concat_idents; pub mod log_syntax; pub mod source_util; + pub mod ary; pub mod trace_macros; } diff --git a/src/test/compile-fail/syntax-extension-ary-non-literal.rs b/src/test/compile-fail/syntax-extension-ary-non-literal.rs new file mode 100644 index 0000000000000..712a3b4d8045f --- /dev/null +++ b/src/test/compile-fail/syntax-extension-ary-non-literal.rs @@ -0,0 +1,14 @@ +// 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. + +fn main() { + let count = 4u; + let v = ary![3i, ..count]; //~ ERROR unexpected token: `count` +} diff --git a/src/test/compile-fail/syntax-extension-ary-non-positive-integral.rs b/src/test/compile-fail/syntax-extension-ary-non-positive-integral.rs new file mode 100644 index 0000000000000..3b990c4f0c339 --- /dev/null +++ b/src/test/compile-fail/syntax-extension-ary-non-positive-integral.rs @@ -0,0 +1,15 @@ +// 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. + +fn main() { + let a = ary![42i, ..-5]; //~ ERROR expected positive integral literal + let b = ary![42i, ..0i32]; //~ ERROR expected positive integral literal + let c = ary![42i, ..0u]; //~ ERROR expected positive integral literal +} diff --git a/src/test/run-pass/syntax-extension-ary.rs b/src/test/run-pass/syntax-extension-ary.rs new file mode 100644 index 0000000000000..02fd43911821d --- /dev/null +++ b/src/test/run-pass/syntax-extension-ary.rs @@ -0,0 +1,58 @@ +// 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. + +fn main() { + let a = ary![42i, ..1]; + assert_eq!(a.as_slice(), [42i].as_slice()); + assert_eq!(a.as_slice(), [42i, ..1].as_slice()); + + let b = ary![42i, ..10]; + assert_eq!(b.as_slice(), [42i, ..10].as_slice()); + + let c = ary![None::>, ..10]; + assert_eq!(c.len(), 10); + assert_eq!(c.as_slice(), Vec::from_elem(10, None::>).as_slice()); + + #[deriving(Eq)] + // non-Copy, non-Clone + struct Foo { + x: uint, + nocopy: ::std::kinds::marker::NoCopy + } + + impl Foo { + fn new(x: uint) -> Foo { + Foo { + x: x, + nocopy: ::std::kinds::marker::NoCopy + } + } + } + + let d = ary![Foo::new(42), ..10]; + assert_eq!(d.len(), 10); + assert!(d.as_slice() == Vec::from_fn(10, |_| Foo::new(42)).as_slice()); + + let mut it = range(0, 4); + let e = ary![it.next(), ..8]; + assert_eq!(e.as_slice(), &[Some(0), Some(1), Some(2), Some(3), None, None, None, None]); + + let f = ary![format!("{}", 42i), ..4]; + assert_eq!(f.len(), 4); + for s in f.iter() { + assert_eq!(s.as_slice(), "42"); + } + + let g = ary![vec![1u,2,3], ..4]; + assert_eq!(g.len(), 4); + for v in g.iter() { + assert_eq!(v.as_slice(), &[1u,2,3]); + } +}