diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index f4330960aca..06b56bbe472 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -281,7 +281,10 @@ pub fn syntax_expander_table() -> SyntaxEnv { ext::fmt::expand_syntax_ext)); syntax_expanders.insert(intern("format_args"), builtin_normal_expander( - ext::format::expand_args)); + ext::format::expand_format_args)); + syntax_expanders.insert(intern("format_args_method"), + builtin_normal_expander( + ext::format::expand_format_args_method)); syntax_expanders.insert(intern("env"), builtin_normal_expander( ext::env::expand_env)); diff --git a/src/libsyntax/ext/deriving/show.rs b/src/libsyntax/ext/deriving/show.rs index aeaf53a1939..343100d3a8e 100644 --- a/src/libsyntax/ext/deriving/show.rs +++ b/src/libsyntax/ext/deriving/show.rs @@ -120,23 +120,18 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span, // AST construction! // we're basically calling // - // format_arg!(|__args| ::std::fmt::write(fmt.buf, __args), "", exprs...) + // format_arg_method!(fmt, write_fmt, "", exprs...) // // but doing it directly via ext::format. let formatter = substr.nonself_args[0]; - let buf = cx.expr_field_access(span, formatter, cx.ident_of("buf")); - - let std_write = vec!(cx.ident_of("std"), cx.ident_of("fmt"), cx.ident_of("write")); - let args = cx.ident_of("__args"); - let write_call = cx.expr_call_global(span, std_write, vec!(buf, cx.expr_ident(span, args))); - let format_closure = cx.lambda_expr(span, vec!(args), write_call); + let meth = cx.ident_of("write_fmt"); let s = token::intern_and_get_ident(format_string.as_slice()); let format_string = cx.expr_str(span, s); // phew, not our responsibility any more! format::expand_preparsed_format_args(cx, span, - format_closure, + format::MethodCall(formatter, meth), format_string, exprs, Vec::new(), HashMap::new()) } diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index c03d174365e..e92ce139d00 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -59,6 +59,11 @@ struct Context<'a, 'b> { next_arg: uint, } +pub enum Invocation { + Call(@ast::Expr), + MethodCall(@ast::Expr, ast::Ident), +} + /// 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. @@ -67,8 +72,9 @@ struct Context<'a, 'b> { /// /// Some((fmtstr, unnamed arguments, ordering of named arguments, /// named arguments)) -fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> (@ast::Expr, Option<(@ast::Expr, Vec<@ast::Expr>, Vec, +fn parse_args(ecx: &mut ExtCtxt, sp: Span, allow_method: bool, + tts: &[ast::TokenTree]) + -> (Invocation, Option<(@ast::Expr, Vec<@ast::Expr>, Vec, HashMap)>) { let mut args = Vec::new(); let mut names = HashMap::::new(); @@ -80,22 +86,31 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) .map(|x| (*x).clone()) .collect()); // Parse the leading function expression (maybe a block, maybe a path) - let extra = p.parse_expr(); + let invocation = if allow_method { + let e = p.parse_expr(); + if !p.eat(&token::COMMA) { + ecx.span_err(sp, "expected token: `,`"); + return (Call(e), None); + } + MethodCall(e, p.parse_ident()) + } else { + Call(p.parse_expr()) + }; if !p.eat(&token::COMMA) { ecx.span_err(sp, "expected token: `,`"); - return (extra, None); + return (invocation, None); } if p.token == token::EOF { ecx.span_err(sp, "requires at least a format string argument"); - return (extra, None); + return (invocation, 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); + return (invocation, None); } if p.token == token::EOF { break } // accept trailing commas if named || (token::is_ident(&p.token) && @@ -110,13 +125,13 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) ecx.span_err(p.span, "expected ident, positional arguments \ cannot follow named arguments"); - return (extra, None); + return (invocation, None); } _ => { ecx.span_err(p.span, format!("expected ident for named argument, but found `{}`", p.this_token_to_str())); - return (extra, None); + return (invocation, None); } }; let interned_name = token::get_ident(ident); @@ -137,7 +152,7 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) args.push(p.parse_expr()); } } - return (extra, Some((fmtstr, args, order, names))); + return (invocation, Some((fmtstr, args, order, names))); } impl<'a, 'b> Context<'a, 'b> { @@ -595,7 +610,7 @@ impl<'a, 'b> Context<'a, 'b> { /// Actually builds the expression which the iformat! block will be expanded /// to - fn to_expr(&self, extra: @ast::Expr) -> @ast::Expr { + fn to_expr(&self, invocation: Invocation) -> @ast::Expr { let mut lets = Vec::new(); let mut locals = Vec::new(); let mut names = Vec::from_fn(self.name_positions.len(), |_| None); @@ -699,8 +714,16 @@ impl<'a, 'b> Context<'a, 'b> { let resname = self.ecx.ident_of("__args"); lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result)); let res = self.ecx.expr_ident(self.fmtsp, resname); - let result = self.ecx.expr_call(extra.span, extra, vec!( - self.ecx.expr_addr_of(extra.span, res))); + let result = match invocation { + Call(e) => { + self.ecx.expr_call(e.span, e, + vec!(self.ecx.expr_addr_of(e.span, res))) + } + MethodCall(e, m) => { + self.ecx.expr_method_call(e.span, e, m, + vec!(self.ecx.expr_addr_of(e.span, res))) + } + }; let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets, Some(result))); @@ -794,13 +817,25 @@ impl<'a, 'b> Context<'a, 'b> { } } -pub fn expand_args(ecx: &mut ExtCtxt, sp: Span, - tts: &[ast::TokenTree]) -> Box { +pub fn expand_format_args(ecx: &mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) -> Box { - match parse_args(ecx, sp, tts) { - (extra, Some((efmt, args, order, names))) => { - MacExpr::new(expand_preparsed_format_args(ecx, sp, extra, efmt, args, - order, names)) + match parse_args(ecx, sp, false, tts) { + (invocation, Some((efmt, args, order, names))) => { + MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt, + args, order, names)) + } + (_, None) => MacExpr::new(ecx.expr_uint(sp, 2)) + } +} + +pub fn expand_format_args_method(ecx: &mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) -> Box { + + match parse_args(ecx, sp, true, tts) { + (invocation, Some((efmt, args, order, names))) => { + MacExpr::new(expand_preparsed_format_args(ecx, sp, invocation, efmt, + args, order, names)) } (_, None) => MacExpr::new(ecx.expr_uint(sp, 2)) } @@ -810,7 +845,7 @@ pub fn expand_args(ecx: &mut ExtCtxt, sp: Span, /// name=names...)` and construct the appropriate formatting /// expression. pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, - extra: @ast::Expr, + invocation: Invocation, efmt: @ast::Expr, args: Vec<@ast::Expr>, name_ordering: Vec, names: HashMap) -> @ast::Expr { @@ -869,5 +904,5 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, } } - cx.to_expr(extra) + cx.to_expr(invocation) }