我已经调用了自定义属性:
#[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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过以下方式调用
#![feature(phase)]
#[phase(plugin)]
extern crate dummy_ext;
#[test]
#[dummy]
fn hello() {
println!("Put something above this...");
}
Run Code Online (Sandbox Code Playgroud)
......我已经看到了一些quote_expr!( ... )用来做这件事的例子,但我并不真正理解它们.
假设我想将此语句(或表达式?)添加到任何标记的函数的顶部#[dummy]:
println!("dummy");
Run Code Online (Sandbox Code Playgroud)
我如何实现这一目标?
huo*_*uon 27
这里有两个任务:
笔记:
fn,struct,impl.rustc --pretty expanded foo.rs就是你最好的朋友(最适用于最小的例子,例如避免#[deriving]和println!,除非你要特别调试的).从头开始创建AST块有三种基本方法:
AstBuilder,和在这种情况下,我们可以使用引用,所以我不会浪费时间在其他人身上.所述quote宏采取ExtCtxt("扩展上下文")和一个表达式或项目等,并创建代表该项目AST值,例如
let x: Gc<ast::Expr> = quote_expr!(cx, 1 + 2);
Run Code Online (Sandbox Code Playgroud)
创建一个Expr_带有值的值ExprBinary,它包含两个ExprLits(用于1和2文字).
因此,要创建所需的表达式,quote_expr!(cx, println!("dummy"))应该可行.报价比这更强大:您可以使用$它将存储AST的变量拼接成表达式,例如,如果我们有x如上所述,那么
let y = quote_expr!(cx, if $x > 0 { println!("dummy") });
Run Code Online (Sandbox Code Playgroud)
将创建一个AST reprsenting if 1 + 2 > 0 { println!("dummy") }.
这一切都非常不稳定,并且宏是特征门控的.一个完整的"工作"示例:
#![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 })
}
Run Code Online (Sandbox Code Playgroud)
截至2014年8月29日,引用宏的列表是:quote_tokens,quote_expr,quote_ty,quote_method,quote_item,quote_pat,quote_arm,quote_stmt.(每个都基本上创建了类似命名的类型syntax::ast.)
(请注意:它们目前以一种非常黑客的方式实现,通过对其参数进行字符串化和重新分析,因此遇到令人困惑的行为相对容易.)
我们现在知道如何制作单独的AST块,但是我们如何将它们反馈到主代码中呢?
嗯,确切的方法取决于你想要做什么.有各种不同类型的语法扩展.
println!),NormalTT是正确的,ItemDecorator(例如,根据附加的项目和项目#[deriving]创建一些impl块)structenumItemModifier因此,在这种情况下,我们需要的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>
Run Code Online (Sandbox Code Playgroud)
这是注册的
reg.register_syntax_extension(intern("dummy"), base::ItemModifier(dummy_expand));
Run Code Online (Sandbox Code Playgroud)
我们已经设置了样板,我们只需要编写实现.有两种方法.我们可以只添加println!到的函数的内容开始,或者我们可以将内容从改变foo(); bar(); ...到println!("dummy"); { foo(); bar(); ... }的只是创造了两个新的表达式.
正如您所发现的,ItemFn可以与之匹配
ast::ItemFn(decl, ref style, ref abi, ref generics, block)
Run Code Online (Sandbox Code Playgroud)
block实际内容在哪里.我上面提到的第二种方法是最简单的
let new_contents = quote_expr!(cx,
println!("dummy");
$block
);
Run Code Online (Sandbox Code Playgroud)
然后保存旧的信息,我们将建立一个新的ItemFn,将其套回了正确的方法上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
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4070 次 |
| 最近记录: |