2014-10-26 3 views
6

Я хочу использовать парсер Rust (libsyntax), чтобы разобрать файл Rust и извлечь из него информацию, такую ​​как имена функций. Я начал копать в документах и ​​коде, поэтому моя первая цель - это программа, которая печатает все имена функций автономных функций в файле .rs.Как использовать парсер Rust (libsyntax) самостоятельно?

Программа должна расширять все макросы, прежде чем печатает имена функций, поэтому функции, объявленные с помощью макроса, не пропущены. Вот почему я не могу написать какой-то дрянной маленький парсер сам, чтобы выполнить эту работу.

Должен признать, что я еще не совсем хорошо разбираюсь в Rust, поэтому заранее извиняюсь за любые глупые заявления в этом вопросе.

Как я понял, что нужно сделать следующие шаги:

  1. Разбирает файл Развернуть макросы с MacroExpander
  2. через Parser struct
  3. ???
  4. Используйте Visitor ходить AST и извлекать информацию, мне нужно

Так вот мои вопросы (например, с помощью visit_fn.):

  1. Как использовать MacroExpander?
  2. Как мне пройти расширенный АСТ с помощью посетителя?

У меня возникла идея использовать custom lint check вместо полноценного парсера. Я изучаю этот вариант.

Если это имеет значение, я использую rustc 0.13.0-nightly (f168c12c5 2014-10-25 20:57:10 +0000)

+1

Extraction интересных фактов, как правило, требует много больше, чем «просто парсер» См мою статью «Жизнь после Парсинга "(google или через биографию). –

ответ

5

Я боюсь, что я не могу ответить на ваш вопрос напрямую; но я могу представить альтернативу, которая может помочь.

Если вам нужен только AST, вы можете получить его в формате JSON, используя rustc -Z ast-json. Затем используйте свой любимый язык (Python отлично) для обработки вывода.

Вы также можете получить печатный источник с использованием rustc --pretty=(expanded|normal|typed).

Например, с учетом этого hello.rs:

fn main() { 
    println!("hello world"); 
} 

Получаем:

$ rustc -Z ast-json hello.rs 
{"module":{"inner":null,"view_items":[{"node":{"va... (etc.) 
$ rustc --pretty=normal hello.rs 
#![no_std] 
#[macro_use] 
extern crate "std" as std; 
#[prelude_import] 
use std::prelude::v1::*; 
fn main() { println!("hello world"); } 
$ rustc --pretty=expanded hello.rs 
#![no_std] 
#[macro_use] 
extern crate "std" as std; 
#[prelude_import] 
use std::prelude::v1::*; 
fn main() { 
    ::std::io::stdio::println_args(::std::fmt::Arguments::new({ 
                    #[inline] 
                    #[allow(dead_code)] 
                    static __STATIC_FMTSTR: 
                     &'static [&'static str] 
                     = 
                     &["hello world"]; 
                    __STATIC_FMTSTR 
                   }, 
                   &match() { 
                    () => [], 
                   })); 
} 

Если вам нужно больше, чем хотя, пуха плагин будет быть лучшим вариантом. Правильная обработка макрообмена, config flags, модульная система и все остальное, что подходит, довольно нетривиально. С плагином для линтов вы сразу можете получить AST-тест без суеты. Cargo также поддерживает плагины компилятора, поэтому ваш инструмент будет хорошо вписываться в проекты других людей.

+0

спасибо! Я тоже чувствую, что не использовать нестабильные внутренние компоненты компилятора - это путь сюда! –

5

Вы можете использовать syntex для разбора ржавчины, поэтому вам не нужно использовать неустойчивую ржавчину.

Вот простой пример:

// Tested against syntex_syntax v0.33 
extern crate syntex_syntax as syntax; 

use std::rc::Rc; 
use syntax::codemap::{CodeMap}; 
use syntax::errors::{Handler}; 
use syntax::errors::emitter::{ColorConfig}; 
use syntax::parse::{self, ParseSess}; 

fn main() { 
    let codemap = Rc::new(CodeMap::new()); 
    let tty_handler = 
     Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, codemap.clone()); 
    let parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone()); 

    let src = "fn foo(x: i64) { let y = x + 1; return y; }".to_owned(); 

    let result = parse::parse_crate_from_source_str(String::new(), src, Vec::new(), &parse_session); 
    println!("parse result: {:?}", result); 
} 

Печатает весь АСТ:

parse result: Ok(Crate { module: Mod { inner: Span { lo: BytePos(0), hi: BytePos(43), expn_id: ExpnId(4294967295) }, 
items: [Item { ident: foo#0, attrs: [], id: 4294967295, node: Fn(FnDecl { inputs: [Arg { ty: type(i64), pat: 
pat(4294967295: x), id: 4294967295 }], output: Default(Span { lo: BytePos(15), hi: BytePos(15), expn_id: ExpnId(4294967295) }), 
variadic: false }, Normal, NotConst, Rust, Generics { lifetimes: [], ty_params: [], where_clause: WhereClause { id: 
4294967295, predicates: [] } }, Block { stmts: [stmt(4294967295: let y = x + 1;), stmt(4294967295: return y;)], expr: 
None, id: 4294967295, rules: Default, span: Span { lo: BytePos(15), hi: BytePos(43), expn_id: ExpnId(4294967295) } }), 
vis: Inherited, span: Span { lo: BytePos(0), hi: BytePos(43), expn_id: ExpnId(4294967295) } }] }, attrs: [], config: [], 
span: Span { lo: BytePos(0), hi: BytePos(42), expn_id: ExpnId(4294967295) }, exported_macros: [] }) 
+0

Я думаю, что syntex не будет продолжен. [Вопрос] (https://github.com/serde-rs/syntex/issues/114) –

Смежные вопросы