From 1ace7f0d49f9e482efe680d691a9d1a8f7e53f60 Mon Sep 17 00:00:00 2001 From: Wang Xuerui Date: Sun, 5 Jun 2016 20:39:05 +0800 Subject: [PATCH] syntax_ext: format: resolve named arguments early Converts named argument references into indices, right after verification as suggested by @alexcrichton. This drastically simplifies the whole process! --- src/libsyntax_ext/format.rs | 147 +++++++++++++++--------------------- 1 file changed, 62 insertions(+), 85 deletions(-) diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 8a329126ca8..2635213f58e 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -46,15 +46,12 @@ struct Context<'a, 'b:'a> { /// Parsed argument expressions and the types that we've found so far for /// them. + /// Named expressions are resolved early, and are appended to the end of + /// argument expressions. args: Vec>, arg_types: Vec>, - /// Parsed named expressions and the types that we've found for them so far. - /// Note that we keep a side-array of the ordering of the named arguments - /// found to be sure that we can translate them in the same order that they - /// were declared in. - names: HashMap>, - name_types: HashMap, - name_ordering: Vec, + /// Map from named arguments to their resolved indices. + names: HashMap, /// The latest consecutive literal strings, or empty if there weren't any. literal: String, @@ -66,8 +63,6 @@ struct Context<'a, 'b:'a> { /// Stays `true` if all formatting parameters are default (as in "{}{}"). all_pieces_simple: bool, - name_positions: HashMap, - /// Current position of the implicit positional arg pointer, as if it /// still existed in this phase of processing. /// Used only for `all_pieces_simple` tracking in `trans_piece`. @@ -80,15 +75,12 @@ struct Context<'a, 'b:'a> { /// /// If parsing succeeds, the return value is: /// ```ignore -/// Some((fmtstr, unnamed arguments, ordering of named arguments, -/// named arguments)) +/// Some((fmtstr, parsed arguments, index map for named arguments)) /// ``` fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) - -> Option<(P, Vec>, Vec, - HashMap>)> { - let mut args = Vec::new(); - let mut names = HashMap::>::new(); - let mut order = Vec::new(); + -> Option<(P, Vec>, HashMap)> { + let mut args = Vec::>::new(); + let mut names = HashMap::::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -132,20 +124,45 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", name)) - .span_note(prev.span, "previously here") + .span_note(args[*prev].span, "previously here") .emit(); continue; } - order.push(name.to_string()); - names.insert(name.to_string(), e); + + // Resolve names into slots early. + // Since all the positional args are already seen at this point + // if the input is valid, we can simply append to the positional + // args. And remember the names. + let slot = args.len(); + names.insert(name.to_string(), slot); + args.push(e); } else { args.push(panictry!(p.parse_expr())); } } - Some((fmtstr, args, order, names)) + Some((fmtstr, args, names)) } impl<'a, 'b> Context<'a, 'b> { + fn resolve_name_inplace(&self, p: &mut parse::Piece) { + let lookup = |s| *self.names.get(s).unwrap_or(&0); + + match *p { + parse::String(_) => {} + parse::NextArgument(ref mut arg) => { + if let parse::ArgumentNamed(s) = arg.position { + arg.position = parse::ArgumentIs(lookup(s)); + } + if let parse::CountIsName(s) = arg.format.width { + arg.format.width = parse::CountIsParam(lookup(s)); + } + if let parse::CountIsName(s) = arg.format.precision { + arg.format.precision = parse::CountIsParam(lookup(s)); + } + } + } + } + /// 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. @@ -214,24 +231,16 @@ impl<'a, 'b> Context<'a, 'b> { } Named(name) => { - let span = match self.names.get(&name) { - Some(e) => e.span, + let idx = match self.names.get(&name) { + Some(e) => *e, None => { let msg = format!("there is no argument named `{}`", name); self.ecx.span_err(self.fmtsp, &msg[..]); return; } }; - self.verify_same(span, &ty, self.name_types.get(&name)); - if !self.name_types.contains_key(&name) { - self.name_types.insert(name.clone(), ty); - } - // Assign this named argument a slot in the arguments array if - // it hasn't already been assigned a slot. - if !self.name_positions.contains_key(&name) { - let slot = self.name_positions.len(); - self.name_positions.insert(name, slot); - } + // Treat as positional arg. + self.verify_arg_type(Exact(idx), ty) } } } @@ -301,14 +310,8 @@ impl<'a, 'b> Context<'a, 'b> { count("Param", Some(self.ecx.expr_usize(sp, i))) } parse::CountImplied => count("Implied", None), - parse::CountIsName(n) => { - let i = match self.name_positions.get(n) { - Some(&i) => i, - None => 0, // error already emitted elsewhere - }; - let i = i + self.args.len(); - count("Param", Some(self.ecx.expr_usize(sp, i))) - } + // should never be the case, names are already resolved + parse::CountIsName(_) => panic!("should never happen"), } } @@ -348,16 +351,9 @@ impl<'a, 'b> Context<'a, 'b> { match arg.position { parse::ArgumentIs(i) => pos("At", Some(i)), - // Named arguments are converted to positional arguments - // at the end of the list of arguments - parse::ArgumentNamed(n) => { - let i = match self.name_positions.get(n) { - Some(&i) => i, - None => 0, // error already emitted elsewhere - }; - let i = i + self.args.len(); - pos("At", Some(i)) - } + // should never be the case, because names are already + // resolved. + parse::ArgumentNamed(_) => panic!("should never happen"), } }; @@ -448,7 +444,6 @@ impl<'a, 'b> Context<'a, 'b> { /// to fn into_expr(mut self) -> P { let mut locals = Vec::new(); - let mut names = vec![None; self.name_positions.len()]; let mut pats = Vec::new(); let mut heads = Vec::new(); @@ -485,29 +480,11 @@ impl<'a, 'b> Context<'a, 'b> { self.ecx.expr_ident(e.span, name))); heads.push(self.ecx.expr_addr_of(e.span, e)); } - for name in &self.name_ordering { - let e = match self.names.remove(name) { - Some(e) => e, - None => continue - }; - let arg_ty = match self.name_types.get(name) { - Some(ty) => ty, - None => continue - }; - - let lname = self.ecx.ident_of(&format!("__arg{}", - *name)); - pats.push(self.ecx.pat_ident(DUMMY_SP, lname)); - names[*self.name_positions.get(name).unwrap()] = - Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, - self.ecx.expr_ident(e.span, lname))); - heads.push(self.ecx.expr_addr_of(e.span, e)); - } // Now create a vector containing all the arguments - let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap())); + let args = locals.into_iter().collect(); - let args_array = self.ecx.expr_vec(self.fmtsp, args.collect()); + let args_array = self.ecx.expr_vec(self.fmtsp, args); // Constructs an AST equivalent to: // @@ -605,9 +582,9 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span, -> Box { match parse_args(ecx, sp, tts) { - Some((efmt, args, order, names)) => { + Some((efmt, args, names)) => { MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, - args, order, names)) + args, names)) } None => DummyResult::expr(sp) } @@ -618,8 +595,7 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span, pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, efmt: P, args: Vec>, - name_ordering: Vec, - names: HashMap>) + names: HashMap) -> P { let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect(); let macsp = ecx.call_site(); @@ -632,9 +608,6 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, args: args, arg_types: arg_types, names: names, - name_positions: HashMap::new(), - name_types: HashMap::new(), - name_ordering: name_ordering, curarg: 0, literal: String::new(), pieces: Vec::new(), @@ -655,9 +628,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, loop { match parser.next() { - Some(piece) => { + Some(mut piece) => { if !parser.errors.is_empty() { break } cx.verify_piece(&piece); + cx.resolve_name_inplace(&mut piece); pieces.push(piece); } None => break @@ -683,14 +657,17 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, } // Make sure that all arguments were used and all arguments have types. + let num_pos_args = cx.args.len() - cx.names.len(); for (i, ty) in cx.arg_types.iter().enumerate() { if ty.is_none() { - cx.ecx.span_err(cx.args[i].span, "argument never used"); - } - } - for (name, e) in &cx.names { - if !cx.name_types.contains_key(name) { - cx.ecx.span_err(e.span, "named argument never used"); + let msg = if i >= num_pos_args { + // named argument + "named argument never used" + } else { + // positional argument + "argument never used" + }; + cx.ecx.span_err(cx.args[i].span, msg); } }