about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorHuon Wilson <dbau.pp+github@gmail.com>2014-02-06 08:50:36 +1100
committerHuon Wilson <dbau.pp+github@gmail.com>2014-02-08 13:53:21 +1100
commit5d63910f90afcb601a765bba6bd4bb8d52ebef81 (patch)
treeae122ee4d9d84e58698104f2f41778ede07cb98a /src/libsyntax
parentfa191a559171094d89a598884ea18aba6cbda858 (diff)
downloadrust-5d63910f90afcb601a765bba6bd4bb8d52ebef81.tar.gz
rust-5d63910f90afcb601a765bba6bd4bb8d52ebef81.zip
syntax: split out the parsing and the formatting part of format_args!().
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/format.rs166
1 files changed, 92 insertions, 74 deletions
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index 9d2a891bf6b..4bc3b804c7f 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -56,78 +56,83 @@ struct Context<'a> {
     next_arg: uint,
 }
 
-impl<'a> Context<'a> {
-    /// Parses the arguments from the given list of tokens, returning None if
-    /// there's a parse error so we can continue parsing other format! expressions.
-    fn parse_args(&mut self, sp: Span, tts: &[ast::TokenTree])
-                  -> (@ast::Expr, Option<@ast::Expr>) {
-        let mut p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
-                                                 self.ecx.cfg(),
-                                                 tts.to_owned());
-        // Parse the leading function expression (maybe a block, maybe a path)
-        let extra = p.parse_expr();
-        if !p.eat(&token::COMMA) {
-            self.ecx.span_err(sp, "expected token: `,`");
-            return (extra, None);
-        }
+/// Parses the arguments from the given list of tokens, returning None
+/// if there's a parse error so we can continue parsing other format!
+/// expressions.
+///
+/// If parsing succeeds, the second return value is:
+///
+///     Some((fmtstr, unnamed arguments, named arguments))
+fn parse_args(ecx: &mut ExtCtxt, sp: Span,
+              tts: &[ast::TokenTree]) -> (@ast::Expr, Option<(@ast::Expr, ~[@ast::Expr],
+                                                              HashMap<~str, @ast::Expr>)>) {
+    let mut args = ~[];
+    let mut names = HashMap::<~str, @ast::Expr>::new();
+
+    let mut p = rsparse::new_parser_from_tts(ecx.parse_sess(),
+                                             ecx.cfg(),
+                                             tts.to_owned());
+    // Parse the leading function expression (maybe a block, maybe a path)
+    let extra = p.parse_expr();
+    if !p.eat(&token::COMMA) {
+        ecx.span_err(sp, "expected token: `,`");
+        return (extra, None);
+    }
 
-        if p.token == token::EOF {
-            self.ecx.span_err(sp, "requires at least a format string argument");
+    if p.token == token::EOF {
+        ecx.span_err(sp, "requires at least a format string argument");
+        return (extra, None);
+    }
+    let fmtstr = p.parse_expr();
+    let mut named = false;
+    while p.token != token::EOF {
+        if !p.eat(&token::COMMA) {
+            ecx.span_err(sp, "expected token: `,`");
             return (extra, None);
         }
-        let fmtstr = p.parse_expr();
-        let mut named = false;
-        while p.token != token::EOF {
-            if !p.eat(&token::COMMA) {
-                self.ecx.span_err(sp, "expected token: `,`");
-                return (extra, None);
-            }
-            if p.token == token::EOF { break } // accept trailing commas
-            if named || (token::is_ident(&p.token) &&
-                         p.look_ahead(1, |t| *t == token::EQ)) {
-                named = true;
-                let ident = match p.token {
-                    token::IDENT(i, _) => {
-                        p.bump();
-                        i
-                    }
-                    _ if named => {
-                        self.ecx.span_err(p.span,
-                                          "expected ident, positional arguments \
-                                           cannot follow named arguments");
-                        return (extra, None);
-                    }
-                    _ => {
-                        self.ecx.span_err(p.span,
-                                          format!("expected ident for named \
-                                                argument, but found `{}`",
-                                               p.this_token_to_str()));
-                        return (extra, None);
-                    }
-                };
-                let interned_name = token::get_ident(ident.name);
-                let name = interned_name.get();
-                p.expect(&token::EQ);
-                let e = p.parse_expr();
-                match self.names.find_equiv(&name) {
-                    None => {}
-                    Some(prev) => {
-                        self.ecx.span_err(e.span, format!("duplicate argument \
-                                                        named `{}`", name));
-                        self.ecx.parse_sess.span_diagnostic.span_note(
-                            prev.span, "previously here");
-                        continue
-                    }
+        if p.token == token::EOF { break } // accept trailing commas
+        if named || (token::is_ident(&p.token) &&
+                     p.look_ahead(1, |t| *t == token::EQ)) {
+            named = true;
+            let ident = match p.token {
+                token::IDENT(i, _) => {
+                    p.bump();
+                    i
+                }
+                _ if named => {
+                    ecx.span_err(p.span,
+                                 "expected ident, positional arguments \
+                                 cannot follow named arguments");
+                    return (extra, None);
+                }
+                _ => {
+                    ecx.span_err(p.span,
+                                 format!("expected ident for named argument, but found `{}`",
+                                         p.this_token_to_str()));
+                    return (extra, None);
+                }
+            };
+            let interned_name = token::get_ident(ident.name);
+            let name = interned_name.get();
+            p.expect(&token::EQ);
+            let e = p.parse_expr();
+            match names.find_equiv(&name) {
+                None => {}
+                Some(prev) => {
+                    ecx.span_err(e.span, format!("duplicate argument named `{}`", name));
+                    ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
+                    continue
                 }
-                self.names.insert(name.to_str(), e);
-            } else {
-                self.args.push(p.parse_expr());
-                self.arg_types.push(None);
             }
+            names.insert(name.to_str(), e);
+        } else {
+            args.push(p.parse_expr());
         }
-        return (extra, Some(fmtstr));
     }
+    return (extra, Some((fmtstr, args, names)));
+}
 
+impl<'a> Context<'a> {
     /// Verifies one piece of a parse string. All errors are not emitted as
     /// fatal so we can continue giving errors about this and possibly other
     /// format strings.
@@ -758,11 +763,28 @@ impl<'a> Context<'a> {
 
 pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
                    tts: &[ast::TokenTree]) -> base::MacResult {
+
+    match parse_args(ecx, sp, tts) {
+        (extra, Some((efmt, args, names))) => {
+            MRExpr(expand_preparsed_format_args(ecx, sp, extra, efmt, args, names))
+        }
+        (_, None) => MRExpr(ecx.expr_uint(sp, 2))
+    }
+}
+
+/// Take the various parts of `format_args!(extra, efmt, args...,
+/// name=names...)` and construct the appropriate formatting
+/// expression.
+pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
+                                    extra: @ast::Expr,
+                                    efmt: @ast::Expr, args: ~[@ast::Expr],
+                                    names: HashMap<~str, @ast::Expr>) -> @ast::Expr {
+    let arg_types = vec::from_fn(args.len(), |_| None);
     let mut cx = Context {
         ecx: ecx,
-        args: ~[],
-        arg_types: ~[],
-        names: HashMap::new(),
+        args: args,
+        arg_types: arg_types,
+        names: names,
         name_positions: HashMap::new(),
         name_types: HashMap::new(),
         nest_level: 0,
@@ -771,10 +793,6 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
         method_statics: ~[],
         fmtsp: sp,
     };
-    let (extra, efmt) = match cx.parse_args(sp, tts) {
-        (extra, Some(e)) => (extra, e),
-        (_, None) => { return MRExpr(cx.ecx.expr_uint(sp, 2)); }
-    };
     cx.fmtsp = efmt.span;
     // Be sure to recursively expand macros just in case the format string uses
     // a macro to build the format expression.
@@ -783,7 +801,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
                                 expr,
                                 "format argument must be a string literal.") {
         Some((fmt, _)) => fmt,
-        None => return MacResult::dummy_expr()
+        None => return efmt
     };
 
     let mut parser = parse::Parser::new(fmt.get());
@@ -801,7 +819,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
     match parser.errors.shift() {
         Some(error) => {
             cx.ecx.span_err(efmt.span, "invalid format string: " + error);
-            return MRExpr(efmt);
+            return efmt;
         }
         None => {}
     }
@@ -818,5 +836,5 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
         }
     }
 
-    MRExpr(cx.to_expr(extra))
+    cx.to_expr(extra)
 }