diff options
| author | Huon Wilson <dbau.pp+github@gmail.com> | 2014-02-06 08:50:36 +1100 |
|---|---|---|
| committer | Huon Wilson <dbau.pp+github@gmail.com> | 2014-02-08 13:53:21 +1100 |
| commit | 5d63910f90afcb601a765bba6bd4bb8d52ebef81 (patch) | |
| tree | ae122ee4d9d84e58698104f2f41778ede07cb98a /src/libsyntax | |
| parent | fa191a559171094d89a598884ea18aba6cbda858 (diff) | |
| download | rust-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.rs | 166 |
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) } |
