diff --git a/doc/rust.md b/doc/rust.md index 40a3bc12798..0023e65cd83 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -568,6 +568,7 @@ This requirement most often affects name-designator pairs when they occur at the * `log_syntax!` : print out the arguments at compile time * `trace_macros!` : supply `true` or `false` to enable or disable macro expansion logging * `stringify!` : turn the identifier argument into a string literal +* `concat!` : concatenates a comma-separated list of literals * `concat_idents!` : create a new identifier by concatenating the arguments # Crates and source files diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1f9fe28a46d..7ce73a4afef 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,6 +14,7 @@ use codemap::{CodeMap, Span, ExpnInfo}; use diagnostic::span_handler; use ext; +use ext::expand; use parse; use parse::token; use parse::token::{ident_to_str, intern, str_to_ident}; @@ -246,6 +247,9 @@ fn builtin_item_tt_no_ctxt(f: SyntaxExpanderTTItemFunNoCtxt) -> @Transformer { syntax_expanders.insert(intern("concat_idents"), builtin_normal_tt_no_ctxt( ext::concat_idents::expand_syntax_ext)); + syntax_expanders.insert(intern("concat"), + builtin_normal_tt_no_ctxt( + ext::concat::expand_syntax_ext)); syntax_expanders.insert(intern(&"log_syntax"), builtin_normal_tt_no_ctxt( ext::log_syntax::expand_syntax_ext)); @@ -338,6 +342,22 @@ pub fn new(parse_sess: @mut parse::ParseSess, cfg: ast::CrateConfig) } } + pub fn expand_expr(@self, mut e: @ast::Expr) -> @ast::Expr { + loop { + match e.node { + ast::ExprMac(*) => { + let extsbox = @mut syntax_expander_table(); + let expander = expand::MacroExpander { + extsbox: extsbox, + cx: self, + }; + e = expand::expand_expr(extsbox, self, e, &expander); + } + _ => return e + } + } + } + pub fn codemap(&self) -> @CodeMap { self.parse_sess.cm } pub fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess } pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() } diff --git a/src/libsyntax/ext/concat.rs b/src/libsyntax/ext/concat.rs new file mode 100644 index 00000000000..a89e8d261fe --- /dev/null +++ b/src/libsyntax/ext/concat.rs @@ -0,0 +1,58 @@ +// 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::char; + +use ast; +use codemap; +use ext::base; +use ext::build::AstBuilder; + +pub fn expand_syntax_ext(cx: @base::ExtCtxt, + sp: codemap::Span, + tts: &[ast::token_tree]) -> base::MacResult { + let es = base::get_exprs_from_tts(cx, sp, tts); + let mut accumulator = ~""; + for e in es.move_iter() { + let e = cx.expand_expr(e); + match e.node { + ast::ExprLit(lit) => { + match lit.node { + ast::lit_str(s, _) | + ast::lit_float(s, _) | + ast::lit_float_unsuffixed(s) => { + accumulator.push_str(s); + } + ast::lit_char(c) => { + accumulator.push_char(char::from_u32(c).unwrap()); + } + ast::lit_int(i, _) | + ast::lit_int_unsuffixed(i) => { + accumulator.push_str(format!("{}", i)); + } + ast::lit_uint(u, _) => { + accumulator.push_str(format!("{}", u)); + } + ast::lit_nil => {} + ast::lit_bool(b) => { + accumulator.push_str(format!("{}", b)); + } + ast::lit_binary(*) => { + cx.span_err(e.span, "cannot concatenate a binary literal"); + } + } + } + _ => { + cx.span_err(e.span, "expected a literal"); + } + } + } + return base::MRExpr(cx.expr_str(sp, accumulator.to_managed())); +} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 052b177d4d8..ba946d5fb1f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1083,7 +1083,7 @@ struct NoOpFolder { impl ast_fold for NoOpFolder {} -struct MacroExpander { +pub struct MacroExpander { extsbox: @mut SyntaxEnv, cx: @ExtCtxt, } diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 31befed6c0c..943279d2dc6 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -735,8 +735,10 @@ pub fn expand_args(ecx: @ExtCtxt, sp: Span, (_, None) => { return MRExpr(ecx.expr_uint(sp, 2)); } }; cx.fmtsp = efmt.span; - let (fmt, _fmt_str_style) = expr_to_str(ecx, efmt, - "format argument must be a string literal."); + // Be sure to recursively expand macros just in case the format string uses + // a macro to build the format expression. + let (fmt, _) = expr_to_str(ecx, ecx.expand_expr(efmt), + "format argument must be a string literal."); let mut err = false; do parse::parse_error::cond.trap(|m| { diff --git a/src/libsyntax/syntax.rs b/src/libsyntax/syntax.rs index 00fc676f595..cf2c01e92b4 100644 --- a/src/libsyntax/syntax.rs +++ b/src/libsyntax/syntax.rs @@ -77,6 +77,7 @@ pub mod tt { pub mod format; pub mod env; pub mod bytes; + pub mod concat; pub mod concat_idents; pub mod log_syntax; pub mod auto_encode; diff --git a/src/test/compile-fail/concat.rs b/src/test/compile-fail/concat.rs new file mode 100644 index 00000000000..c34e402c90b --- /dev/null +++ b/src/test/compile-fail/concat.rs @@ -0,0 +1,14 @@ +// 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. + +fn main() { + concat!(foo); //~ ERROR: expected a literal + concat!(foo()); //~ ERROR: expected a literal +} diff --git a/src/test/run-pass/concat.rs b/src/test/run-pass/concat.rs new file mode 100644 index 00000000000..bcf1c4c63f5 --- /dev/null +++ b/src/test/run-pass/concat.rs @@ -0,0 +1,19 @@ +// 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. + +pub fn main() { + assert_eq!(format!(concat!("foo", "bar", "{}"), "baz"), ~"foobarbaz"); + assert_eq!(format!(concat!()), ~""); + + assert_eq!( + concat!(1, 2i, 3u, 4f32, 4.0, 'a', true, ()), + "12344.0atrue" + ); +}