about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/ext/base.rs20
-rw-r--r--src/libsyntax/ext/concat.rs58
-rw-r--r--src/libsyntax/ext/expand.rs2
-rw-r--r--src/libsyntax/ext/format.rs6
-rw-r--r--src/libsyntax/syntax.rs1
-rw-r--r--src/test/compile-fail/concat.rs14
-rw-r--r--src/test/run-pass/concat.rs19
7 files changed, 117 insertions, 3 deletions
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;
 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 @@ pub fn syntax_expander_table() -> SyntaxEnv {
     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 @@ impl ExtCtxt {
         }
     }
 
+    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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 ext {
     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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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"
+    );
+}