99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

編譯器插件

2018-08-12 22:04 更新

編譯器插件

簡介

rustc 可以加載編譯器插件,它是用戶提供的庫,這個(gè)庫使用新語法擴(kuò)展編譯器的行為,lint 檢查等?!   ?/p>

插件是一個(gè)有指定的 registrar 函數(shù)的動(dòng)態(tài)庫,注冊 rustc 擴(kuò)展。其它庫可以使用屬性 #![plugin(...)] 加載這些擴(kuò)展。想了解更多關(guān)于定義機(jī)制和加載插件,查看 rustc::plugin 文檔。

如果存在,像 #![plugin(foo(... args ...))] 傳遞的參數(shù)不被 rustc 本身編譯。他們通過注冊表參數(shù)的方法提供給插件?!   ?/p>

在絕大多數(shù)情況下,一個(gè)插件只能通過 #![plugin] 而不是通過一個(gè) extern crate 項(xiàng)目來被使用。連接一個(gè)插件將會(huì)把所有 libsyntax 和 librustc 作為庫的依賴關(guān)系。這通常是不必要的,除非你正在創(chuàng)建另一個(gè)插件。plugin_as_library lint 檢查這些準(zhǔn)則?!   ?/p>

通常的做法是將編譯器插件放入自己的庫中,獨(dú)立于庫中被客戶端使用的任何 macro_rules ! 宏或普通 Rust 代碼。

語法擴(kuò)展

插件可以用不同的方法擴(kuò)展 Rust 的語法。一種語法擴(kuò)展是程序宏。調(diào)用程序宏就像調(diào)用普通的宏一樣,但擴(kuò)展是由任意在運(yùn)行時(shí)操縱語法樹的 Rust 代碼執(zhí)行?!   ?/p>

讓我們寫一個(gè)插件 roman_numerals.rs 實(shí)現(xiàn)羅馬數(shù)字整數(shù)常量。

#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private)]

extern crate syntax;
extern crate rustc;

use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ast::{TokenTree, TtToken};
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::build::AstBuilder;  // trait for expr_usize
use rustc::plugin::Registry;

fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
-> Box<MacResult + 'static> {

static NUMERALS: &'static [(&'static str, u32)] = &[
("M", 1000), ("CM", 900), ("D", 500), ("CD", 400),
("C",  100), ("XC",  90), ("L",  50), ("XL",  40),
("X",   10), ("IX",   9), ("V",   5), ("IV",   4),
("I",1)];

let text = match args {
[TtToken(_, token::Ident(s, _))] => token::get_ident(s).to_string(),
_ => {
cx.span_err(sp, "argument should be a single identifier");
return DummyResult::any(sp);
}
};

let mut text = &*text;
let mut total = 0;
while !text.is_empty() {
match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) {
Some(&(rn, val)) => {
total += val;
text = &text[rn.len()..];
}
None => {
cx.span_err(sp, "invalid Roman numeral");
return DummyResult::any(sp);
}
}
}

MacEager::expr(cx.expr_u32(sp, total))
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("rn", expand_rn);
}

然后我們可以像使用其他宏一樣使用 rn !():

#![feature(plugin)]
#![plugin(roman_numerals)]

fn main() {
assert_eq!(rn!(MMXV), 2015);
}

一個(gè)簡單的 fn(&str) -> u32 的優(yōu)勢是:

  • (任意復(fù)雜的)轉(zhuǎn)換是在編譯時(shí)完成的?! ?/li>
  • 輸入驗(yàn)證在編譯時(shí)執(zhí)行?! ?/li>
  • 它可以擴(kuò)展到模式中允許使用,它有效地給出了一個(gè)為數(shù)據(jù)類型定義新的文字語法的方法。

除了程序宏,你可以定義新的 derive-like 屬性和其他類型的擴(kuò)展。請看Registry::register_syntax_extension 和 SyntaxExtension enum。更多調(diào)用宏的例子,請見 regex_macros。

提示和技巧

有一些宏的調(diào)試技巧是適用的。    

您可以使用 syntax::parse 將標(biāo)記樹轉(zhuǎn)化為更高級(jí)的語法元素如表達(dá)式:

fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
-> Box<MacResult+'static> {

let mut parser = cx.new_parser_from_tts(args);

let expr: P<Expr> = parser.parse_expr();

通過這些 libsyntax 解析代碼我們可以知道解析基礎(chǔ)結(jié)構(gòu)是如何工作的?!   ?/p>

為得到更準(zhǔn)確的錯(cuò)誤報(bào)告,保持所有你解析的代碼的 span 。你可以將 spaned 封裝到自定義數(shù)據(jù)結(jié)構(gòu)中?!   ?/p>

調(diào)用 ExtCtxt:span_fatal 會(huì)立即中止編譯。最好不要調(diào)用 ExtCtxt:span_err 并返回 DummyResult,編譯器可以繼續(xù)并找到更多的錯(cuò)誤?!   ?/p>

為了打印語法片段進(jìn)行調(diào)試,可以使用 span\_note 加上 syntax::print::pprust::*_to_string。

上面的例子使用 AstBuilder::expr_usize 產(chǎn)生一個(gè)整數(shù)。除了 AstBuilder 特征,libsyntax 提供了一組 quasiquote 宏。他們沒有正式文件并且非常粗糙的。然而,它的實(shí)現(xiàn)可能是一個(gè)改進(jìn)的一個(gè)普通的插件庫 quasiquote 的好的起點(diǎn) 。

Lint 插件

插件可以通過對額外的代碼類型、安全等等的檢查來擴(kuò)展 Rust 的 lint 基礎(chǔ)結(jié)構(gòu)。在 src/test/auxiliary/lint_plugin\_test.rs 中你可以看到一個(gè)完整的例子。這個(gè)例子的核心如下:

declare_lint!(TEST_LINT, Warn,
  "Warn about items named 'lintme'");

struct Pass;

impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(TEST_LINT)
}

fn check_item(&mut self, cx: &Context, it: &ast::Item) {
let name = token::get_ident(it.ident);
if name.get() == "lintme" {
cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'");
}
}
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_lint_pass(box Pass as LintPassObject);
}

然后代碼

#![plugin(lint_plugin_test)]

fn lintme() { }

將會(huì)產(chǎn)生一個(gè)編譯器警告:

foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
foo.rs:4 fn lintme() { }

lint插件的組件如下:

  • 一個(gè)或多個(gè) 定義靜態(tài)的 Lint 結(jié)構(gòu)的 declare_lint ! 調(diào)用;   
  • 一個(gè)控制 int pass (here, none) 所需的任何 state 的 struct;
  • 一個(gè)定義如何檢查每個(gè)語法元素的 LintPass 實(shí)現(xiàn)。一個(gè) LintPass 可能為幾個(gè)不同的 Lint 調(diào)用span_lint,但應(yīng)該通過 get_lints 方法注冊它們?!     ?/li>

Lint 通過遍歷語法,但他們在編譯的后期運(yùn)行,在哪里可以獲得類型信息。rustc 內(nèi)置的 lint 大多使用相同的基礎(chǔ)結(jié)構(gòu)作為 lint 插件,并提供了一些說明如何訪問類型信息的例子。

插件定義的 lint 是由通常的屬性和編譯器標(biāo)志所控制,例如 #[allow(test_lint)]-A test-lint。通過合適的案例和標(biāo)點(diǎn)符號(hào)的轉(zhuǎn)換,這些標(biāo)識(shí)符由第一個(gè)參數(shù)傳遞到 declare_lint !。

您可以運(yùn)行 rustc -W help foo.rs 來看 rustc 知道的 lint 的列表,包括那些由foo.rs 加載的插件提供的列表。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)