Skip to content

Commit 5d63910

Browse files
committed
syntax: split out the parsing and the formatting part of format_args!().
1 parent fa191a5 commit 5d63910

File tree

1 file changed

+92
-74
lines changed

1 file changed

+92
-74
lines changed

src/libsyntax/ext/format.rs

Lines changed: 92 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -56,78 +56,83 @@ struct Context<'a> {
5656
next_arg: uint,
5757
}
5858

59-
impl<'a> Context<'a> {
60-
/// Parses the arguments from the given list of tokens, returning None if
61-
/// there's a parse error so we can continue parsing other format! expressions.
62-
fn parse_args(&mut self, sp: Span, tts: &[ast::TokenTree])
63-
-> (@ast::Expr, Option<@ast::Expr>) {
64-
let mut p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
65-
self.ecx.cfg(),
66-
tts.to_owned());
67-
// Parse the leading function expression (maybe a block, maybe a path)
68-
let extra = p.parse_expr();
69-
if !p.eat(&token::COMMA) {
70-
self.ecx.span_err(sp, "expected token: `,`");
71-
return (extra, None);
72-
}
59+
/// Parses the arguments from the given list of tokens, returning None
60+
/// if there's a parse error so we can continue parsing other format!
61+
/// expressions.
62+
///
63+
/// If parsing succeeds, the second return value is:
64+
///
65+
/// Some((fmtstr, unnamed arguments, named arguments))
66+
fn parse_args(ecx: &mut ExtCtxt, sp: Span,
67+
tts: &[ast::TokenTree]) -> (@ast::Expr, Option<(@ast::Expr, ~[@ast::Expr],
68+
HashMap<~str, @ast::Expr>)>) {
69+
let mut args = ~[];
70+
let mut names = HashMap::<~str, @ast::Expr>::new();
71+
72+
let mut p = rsparse::new_parser_from_tts(ecx.parse_sess(),
73+
ecx.cfg(),
74+
tts.to_owned());
75+
// Parse the leading function expression (maybe a block, maybe a path)
76+
let extra = p.parse_expr();
77+
if !p.eat(&token::COMMA) {
78+
ecx.span_err(sp, "expected token: `,`");
79+
return (extra, None);
80+
}
7381

74-
if p.token == token::EOF {
75-
self.ecx.span_err(sp, "requires at least a format string argument");
82+
if p.token == token::EOF {
83+
ecx.span_err(sp, "requires at least a format string argument");
84+
return (extra, None);
85+
}
86+
let fmtstr = p.parse_expr();
87+
let mut named = false;
88+
while p.token != token::EOF {
89+
if !p.eat(&token::COMMA) {
90+
ecx.span_err(sp, "expected token: `,`");
7691
return (extra, None);
7792
}
78-
let fmtstr = p.parse_expr();
79-
let mut named = false;
80-
while p.token != token::EOF {
81-
if !p.eat(&token::COMMA) {
82-
self.ecx.span_err(sp, "expected token: `,`");
83-
return (extra, None);
84-
}
85-
if p.token == token::EOF { break } // accept trailing commas
86-
if named || (token::is_ident(&p.token) &&
87-
p.look_ahead(1, |t| *t == token::EQ)) {
88-
named = true;
89-
let ident = match p.token {
90-
token::IDENT(i, _) => {
91-
p.bump();
92-
i
93-
}
94-
_ if named => {
95-
self.ecx.span_err(p.span,
96-
"expected ident, positional arguments \
97-
cannot follow named arguments");
98-
return (extra, None);
99-
}
100-
_ => {
101-
self.ecx.span_err(p.span,
102-
format!("expected ident for named \
103-
argument, but found `{}`",
104-
p.this_token_to_str()));
105-
return (extra, None);
106-
}
107-
};
108-
let interned_name = token::get_ident(ident.name);
109-
let name = interned_name.get();
110-
p.expect(&token::EQ);
111-
let e = p.parse_expr();
112-
match self.names.find_equiv(&name) {
113-
None => {}
114-
Some(prev) => {
115-
self.ecx.span_err(e.span, format!("duplicate argument \
116-
named `{}`", name));
117-
self.ecx.parse_sess.span_diagnostic.span_note(
118-
prev.span, "previously here");
119-
continue
120-
}
93+
if p.token == token::EOF { break } // accept trailing commas
94+
if named || (token::is_ident(&p.token) &&
95+
p.look_ahead(1, |t| *t == token::EQ)) {
96+
named = true;
97+
let ident = match p.token {
98+
token::IDENT(i, _) => {
99+
p.bump();
100+
i
101+
}
102+
_ if named => {
103+
ecx.span_err(p.span,
104+
"expected ident, positional arguments \
105+
cannot follow named arguments");
106+
return (extra, None);
107+
}
108+
_ => {
109+
ecx.span_err(p.span,
110+
format!("expected ident for named argument, but found `{}`",
111+
p.this_token_to_str()));
112+
return (extra, None);
113+
}
114+
};
115+
let interned_name = token::get_ident(ident.name);
116+
let name = interned_name.get();
117+
p.expect(&token::EQ);
118+
let e = p.parse_expr();
119+
match names.find_equiv(&name) {
120+
None => {}
121+
Some(prev) => {
122+
ecx.span_err(e.span, format!("duplicate argument named `{}`", name));
123+
ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
124+
continue
121125
}
122-
self.names.insert(name.to_str(), e);
123-
} else {
124-
self.args.push(p.parse_expr());
125-
self.arg_types.push(None);
126126
}
127+
names.insert(name.to_str(), e);
128+
} else {
129+
args.push(p.parse_expr());
127130
}
128-
return (extra, Some(fmtstr));
129131
}
132+
return (extra, Some((fmtstr, args, names)));
133+
}
130134

135+
impl<'a> Context<'a> {
131136
/// Verifies one piece of a parse string. All errors are not emitted as
132137
/// fatal so we can continue giving errors about this and possibly other
133138
/// format strings.
@@ -758,11 +763,28 @@ impl<'a> Context<'a> {
758763

759764
pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
760765
tts: &[ast::TokenTree]) -> base::MacResult {
766+
767+
match parse_args(ecx, sp, tts) {
768+
(extra, Some((efmt, args, names))) => {
769+
MRExpr(expand_preparsed_format_args(ecx, sp, extra, efmt, args, names))
770+
}
771+
(_, None) => MRExpr(ecx.expr_uint(sp, 2))
772+
}
773+
}
774+
775+
/// Take the various parts of `format_args!(extra, efmt, args...,
776+
/// name=names...)` and construct the appropriate formatting
777+
/// expression.
778+
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
779+
extra: @ast::Expr,
780+
efmt: @ast::Expr, args: ~[@ast::Expr],
781+
names: HashMap<~str, @ast::Expr>) -> @ast::Expr {
782+
let arg_types = vec::from_fn(args.len(), |_| None);
761783
let mut cx = Context {
762784
ecx: ecx,
763-
args: ~[],
764-
arg_types: ~[],
765-
names: HashMap::new(),
785+
args: args,
786+
arg_types: arg_types,
787+
names: names,
766788
name_positions: HashMap::new(),
767789
name_types: HashMap::new(),
768790
nest_level: 0,
@@ -771,10 +793,6 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
771793
method_statics: ~[],
772794
fmtsp: sp,
773795
};
774-
let (extra, efmt) = match cx.parse_args(sp, tts) {
775-
(extra, Some(e)) => (extra, e),
776-
(_, None) => { return MRExpr(cx.ecx.expr_uint(sp, 2)); }
777-
};
778796
cx.fmtsp = efmt.span;
779797
// Be sure to recursively expand macros just in case the format string uses
780798
// a macro to build the format expression.
@@ -783,7 +801,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
783801
expr,
784802
"format argument must be a string literal.") {
785803
Some((fmt, _)) => fmt,
786-
None => return MacResult::dummy_expr()
804+
None => return efmt
787805
};
788806

789807
let mut parser = parse::Parser::new(fmt.get());
@@ -801,7 +819,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
801819
match parser.errors.shift() {
802820
Some(error) => {
803821
cx.ecx.span_err(efmt.span, "invalid format string: " + error);
804-
return MRExpr(efmt);
822+
return efmt;
805823
}
806824
None => {}
807825
}
@@ -818,5 +836,5 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span,
818836
}
819837
}
820838

821-
MRExpr(cx.to_expr(extra))
839+
cx.to_expr(extra)
822840
}

0 commit comments

Comments
 (0)