1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#![feature(plugin_registrar, quote, box_syntax, collections, rustc_private, box_patterns, str_char)]
extern crate rustc;
extern crate syntax;
use syntax::ast;
use syntax::codemap;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::parse;
use syntax::parse::token;
use syntax::fold::Folder;
use syntax::util::small_vector::SmallVector;
use rustc::plugin::Registry;
use std::io::Read;
use std::fs::File;
use std::path::Path;
use rustast::{AstBuilder, DUMMY_SP};
mod translate;
mod grammar;
mod rustast;
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(
token::intern("peg"),
syntax::ext::base::IdentTT(box expand_peg_str, None, false));
reg.register_syntax_extension(
token::intern("peg_file"),
syntax::ext::base::IdentTT(box expand_peg_file, None, false));
}
fn expand_peg_str<'s>(cx: &'s mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult + 's> {
let source = match parse_arg(cx, &tts) {
Some(source) => source,
None => return DummyResult::any(sp),
};
expand_peg(cx, sp, ident, &source)
}
fn expand_peg_file<'s>(cx: &'s mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult + 's> {
let fname = match parse_arg(cx, &tts) {
Some(fname) => fname,
None => return DummyResult::any(sp),
};
let path = Path::new(&cx.codemap().span_to_filename(sp)).parent().unwrap().join(&fname);
let mut source = String::new();
if let Err(e) = File::open(&path).map(|mut f| f.read_to_string(&mut source)) {
cx.span_err(sp, &e.to_string());
return DummyResult::any(sp);
}
cx.codemap().new_filemap(format!("{}", path.display()), "".to_string());
expand_peg(cx, sp, ident, &source)
}
fn expand_peg(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, source: &str) -> Box<MacResult + 'static> {
let grammar_def = grammar::grammar(source);
let grammar_def = match grammar_def {
Ok(grammar_def) => grammar_def,
Err(msg) => {
cx.span_err(sp, &format!("{}", msg));
return DummyResult::any(sp)
}
};
let ast = translate::compile_grammar(cx, &grammar_def);
let allow = cx.attribute(DUMMY_SP, cx.meta_list(DUMMY_SP, token::InternedString::new("allow"), vec![
cx.meta_word(DUMMY_SP, token::InternedString::new("non_snake_case")),
cx.meta_word(DUMMY_SP, token::InternedString::new("unused")),
]));
MacEager::items(SmallVector::one(cx.item_mod(sp, sp, ident, vec![allow], ast.items.clone())))
}
fn parse_arg(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
use syntax::print::pprust;
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
tts.to_vec());
let arg = cx.expander().fold_expr(parser.parse_expr());
match arg.node {
ast::ExprLit(ref spanned) => {
match spanned.node {
ast::LitStr(ref n, _) => {
if !parser.eat(&token::Eof).unwrap_or_else(|e| panic!(e)) {
cx.span_err(parser.span,
"expected only one string literal");
return None
}
return Some(n.to_string())
}
_ => {}
}
}
_ => {}
}
let err = format!("expected string literal but got `{}`",
pprust::expr_to_string(&*arg));
cx.span_err(parser.span, &err);
None
}