我已经调用了自定义属性:
#[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");
我该如何做到这一点?
最佳答案
这里有两个任务:
笔记:
fn
, struct
, impl
. rustc --pretty expanded foo.rs
是你最好的 friend (在最小的例子上效果最好,例如避免 #[deriving]
和 println!
,除非你试图专门调试它们)。 AST 创建
有 3 种创建 AST block 的基本方法从头开始:
AstBuilder
缩写,和 在这种情况下,我们可以使用引用,所以我不会在其他方面浪费时间。
quote
宏采用 ExtCtxt
(“扩展上下文”)和一个表达式或项目等,并创建一个表示该项目的 AST 值,例如let x: Gc<ast::Expr> = quote_expr!(cx, 1 + 2);
创建一个
Expr_
有值 ExprBinary
,其中包含两个 ExprLit
s(对于 1
和 2
文字)。因此,要创建所需的表达式,
quote_expr!(cx, println!("dummy"))
应该管用。报价比这更强大:您可以使用 $
它将存储 AST 的变量拼接到表达式中,例如,如果我们有 x
如上,那么let y = quote_expr!(cx, if $x > 0 { println!("dummy") });
将创建一个代表
if 1 + 2 > 0 { println!("dummy") }
的 AST .这一切都非常不稳定,并且宏是功能门控的。一个完整的“工作”示例:
#![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 年 8 月 29 日,the list of quoting macros是:
quote_tokens
, quote_expr
, quote_ty
, quote_method
, quote_item
, quote_pat
, quote_arm
, quote_stmt
. (每个本质上都在 syntax::ast
中创建了类似名称的类型。)(请注意:它们目前以一种非常老套的方式实现,只是将它们的论点串起来并重新解析,因此相对容易遇到令人困惑的行为。)
AST转换
我们现在知道如何制作独立的 AST block ,但我们如何将它们反馈到主代码中呢?
好吧,确切的方法取决于您要执行的操作。有a variety of different types of syntax extensions .
println!
),NormalTT
是正确的,ItemDecorator
(例如 #[deriving]
根据所附加的 impl
和 struct
项目创建一些 enum
block )ItemModifier
因此,在这种情况下,我们需要一个
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
并用 the right method 将其包裹起来在 AstBuilder
.总共:#![feature(quote, plugin_registrar)]
#![crate_type = "dylib"]
// general boilerplate
extern crate syntax;
extern crate rustc;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, ItemModifier};
// NB. this is important or the method calls don't work
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use std::gc::Gc;
#[plugin_registrar]
pub fn registrar(reg: &mut rustc::plugin::Registry) {
// Register the `#[dummy]` attribute.
reg.register_syntax_extension(token::intern("dummy"),
ItemModifier(dummy_expand));
}
fn dummy_expand(cx: &mut ExtCtxt, sp: Span, _: Gc<ast::MetaItem>,
item: Gc<ast::Item>) -> Gc<ast::Item> {
match item.node {
ast::ItemFn(decl, ref style, ref abi, ref generics, block) => {
let new_contents = quote_expr!(&mut *cx,
println!("dummy");
$block
);
let new_item_ = ast::ItemFn(decl, style.clone(),
abi.clone(), generics.clone(),
// AstBuilder to create block from expr
cx.block_expr(new_contents));
// copying info from old to new
cx.item(item.span, item.ident, item.attrs.clone(), new_item_)
}
_ => {
cx.span_err(sp, "dummy is only permissible on functions");
item
}
}
}
关于rust - 如何编写将代码注入(inject)函数的自定义属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25561137/