我已经调用了自定义属性:
#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
use syntax::parse::token::intern;
use syntax::ext::base;
// Register the `#[dummy]` attribute.
reg.register_syntax_extension(intern("dummy"),
base::ItemDecorator(dummy_expand));
}
// Decorator for `dummy` attribute
pub fn dummy_expand(context: &mut ext::base::ExtCtxt, span: codemap::Span, meta_item: Gc<ast::MetaItem>, item: Gc<ast::Item>, push: |Gc<ast::Item>|) {
match item.node {
ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
trace!("{}", decl);
// ...? Add something here.
}
_ => {
context.span_err(span, "dummy is only permissiable on functions");
}
}
}
通过以下方式调用:
#![feature(phase)]
#[phase(plugin)]
extern crate dummy_ext;
#[test]
#[dummy]
fn hello() {
println!("Put something above this...");
}
...我已经看到了一些使用quote_expr!( ... )
执行此操作的示例,但我并不理解它们。
假设我想将此语句(或表达式?)添加到标记为#[dummy]
的任何函数的顶部:
println!("dummy");
我如何实现这一目标?
答案 0 :(得分:27)
这里有两个任务:
注意:
fn
,struct
,impl
。rustc --pretty expanded foo.rs
是您最好的朋友(最适合最小的示例,例如避免#[deriving]
和println!
,除非您尝试专门调试这些)。从头开始创建AST块的3种基本方法:
AstBuilder
缩写,在这种情况下,我们可以使用引用,所以我不会浪费时间在其他人身上。 quote
宏采用ExtCtxt
(&#34;扩展上下文&#34;)和表达式或项目等,并创建表示该项的AST值,例如
let x: Gc<ast::Expr> = quote_expr!(cx, 1 + 2);
创建一个值为ExprBinary
的{{3}},其中包含两个ExprLit
s(适用于1
和2
文字。)
因此,要创建所需的表达式,quote_expr!(cx, println!("dummy"))
应该有效。报价比这更强大:您可以使用$
将存储AST的变量拼接成表达式,例如,如果我们有x
,那么
let y = quote_expr!(cx, if $x > 0 { println!("dummy") });
将创建AST reprsenting if 1 + 2 > 0 { println!("dummy") }
。
这一切都非常不稳定,并且宏是特征门控的。一个完整的&#34;工作&#34;例如:
#![feature(quote)]
#![crate_type = "dylib"]
extern crate syntax;
use syntax::ext::base::ExtCtxt;
use syntax::ast;
use std::gc::Gc;
fn basic_print(cx: &mut ExtCtxt) -> Gc<ast::Expr> {
quote_expr!(cx, println!("dummy"))
}
fn quoted_print(cx: &mut ExtCtxt) -> Gc<ast::Expr> {
let p = basic_print(cx);
quote_expr!(cx, if true { $p })
}
截至2014-08-29,Expr_
为:quote_tokens
,quote_expr
,quote_ty
,quote_method
,quote_item
,{{ 1}},quote_pat
,quote_arm
。 (每个基本上都在quote_stmt
中创建类似命名的类型。)
(请注意:目前,他们以非常黑客的方式实施,只是将他们的论点和解析进行字符串化,因此相对容易遇到令人困惑的行为。)
我们现在知道如何制作单独的AST块,但是我们如何将它们反馈到主代码中呢?
嗯,确切的方法取决于你想要做什么。那里有the list of quoting macros。
syntax::ast
),println!
是正确的,NormalTT
根据#[deriving]
创建一些impl
块struct
附加到的项目ItemDecorator
因此,在这种情况下,我们需要enum
,以便我们可以将ItemModifier
更改为#[dummy] fn foo() { ... }
。让我们声明一个具有正确签名的函数:
#[dummy] fn foo() { println!("dummy"); .... }
已在
注册fn dummy_expand(cx: &mut ExtCtxt, sp: Span, _: Gc<ast::MetaItem>, item: Gc<ast::Item>) -> Gc<Item>
我们已经设置了样板,我们只需要编写实现。有两种方法。我们只需将reg.register_syntax_extension(intern("dummy"), base::ItemModifier(dummy_expand));
添加到函数内容的开头,或者我们可以通过创建两个新表达式将内容从println!
更改为foo(); bar(); ...
。
如您所见,println!("dummy"); { foo(); bar(); ... }
可以与
ItemFn
其中ast::ItemFn(decl, ref style, ref abi, ref generics, block)
是实际内容。我上面提到的第二种方法最简单,只是
block
然后为了保留旧信息,我们将构建一个新的let new_contents = quote_expr!(cx,
println!("dummy");
$block
);
并在ItemFn
上使用ItemModifier
将其重新包装起来。总计:
AstBuilder