]> git.proxmox.com Git - rustc.git/blob - vendor/pest_generator/src/lib.rs
New upstream version 1.34.2+dfsg1
[rustc.git] / vendor / pest_generator / src / lib.rs
1 // pest. The Elegant Parser
2 // Copyright (c) 2018 DragoČ™ Tiselice
3 //
4 // Licensed under the Apache License, Version 2.0
5 // <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6 // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. All files in the project carrying such notice may not be copied,
8 // modified, or distributed except according to those terms.
9
10 #![doc(html_root_url = "https://docs.rs/pest_derive")]
11 #![recursion_limit = "256"]
12
13 extern crate pest;
14 extern crate pest_meta;
15
16 extern crate proc_macro;
17 extern crate proc_macro2;
18 #[macro_use]
19 extern crate quote;
20 extern crate syn;
21
22 use std::env;
23 use std::fs::File;
24 use std::io::{self, Read};
25 use std::path::Path;
26
27 use proc_macro2::TokenStream;
28 use syn::{Attribute, DeriveInput, Generics, Ident, Lit, Meta};
29
30 #[macro_use]
31 mod macros;
32 mod generator;
33
34 use pest_meta::parser::{self, Rule};
35 use pest_meta::{optimizer, unwrap_or_report, validator};
36
37 pub fn derive_parser(input: TokenStream, include_grammar: bool) -> TokenStream {
38 let ast: DeriveInput = syn::parse2(input).unwrap();
39 let (name, generics, content) = parse_derive(ast);
40
41 let (data, path) = match content {
42 GrammarSource::File(ref path) => {
43 let root = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
44 let path = Path::new(&root).join("src/").join(&path);
45 let file_name = match path.file_name() {
46 Some(file_name) => file_name,
47 None => panic!("grammar attribute should point to a file"),
48 };
49
50 let data = match read_file(&path) {
51 Ok(data) => data,
52 Err(error) => panic!("error opening {:?}: {}", file_name, error),
53 };
54 (data, Some(path.clone()))
55 }
56 GrammarSource::Inline(content) => (content, None),
57 };
58
59 let pairs = match parser::parse(Rule::grammar_rules, &data) {
60 Ok(pairs) => pairs,
61 Err(error) => panic!(
62 "error parsing \n{}",
63 error.renamed_rules(|rule| match *rule {
64 Rule::grammar_rule => "rule".to_owned(),
65 Rule::_push => "PUSH".to_owned(),
66 Rule::assignment_operator => "`=`".to_owned(),
67 Rule::silent_modifier => "`_`".to_owned(),
68 Rule::atomic_modifier => "`@`".to_owned(),
69 Rule::compound_atomic_modifier => "`$`".to_owned(),
70 Rule::non_atomic_modifier => "`!`".to_owned(),
71 Rule::opening_brace => "`{`".to_owned(),
72 Rule::closing_brace => "`}`".to_owned(),
73 Rule::opening_brack => "`[`".to_owned(),
74 Rule::closing_brack => "`]`".to_owned(),
75 Rule::opening_paren => "`(`".to_owned(),
76 Rule::positive_predicate_operator => "`&`".to_owned(),
77 Rule::negative_predicate_operator => "`!`".to_owned(),
78 Rule::sequence_operator => "`&`".to_owned(),
79 Rule::choice_operator => "`|`".to_owned(),
80 Rule::optional_operator => "`?`".to_owned(),
81 Rule::repeat_operator => "`*`".to_owned(),
82 Rule::repeat_once_operator => "`+`".to_owned(),
83 Rule::comma => "`,`".to_owned(),
84 Rule::closing_paren => "`)`".to_owned(),
85 Rule::quote => "`\"`".to_owned(),
86 Rule::insensitive_string => "`^`".to_owned(),
87 Rule::range_operator => "`..`".to_owned(),
88 Rule::single_quote => "`'`".to_owned(),
89 other_rule => format!("{:?}", other_rule),
90 })
91 ),
92 };
93
94 let defaults = unwrap_or_report(validator::validate_pairs(pairs.clone()));
95 let ast = unwrap_or_report(parser::consume_rules(pairs));
96 let optimized = optimizer::optimize(ast);
97
98 generator::generate(name, &generics, path, optimized, defaults, include_grammar)
99 }
100
101 fn read_file<P: AsRef<Path>>(path: P) -> io::Result<String> {
102 let mut file = File::open(path.as_ref())?;
103 let mut string = String::new();
104 file.read_to_string(&mut string)?;
105 Ok(string)
106 }
107
108 #[derive(Debug, PartialEq)]
109 enum GrammarSource {
110 File(String),
111 Inline(String),
112 }
113
114 fn parse_derive(ast: DeriveInput) -> (Ident, Generics, GrammarSource) {
115 let name = ast.ident;
116 let generics = ast.generics;
117
118 let grammar: Vec<&Attribute> = ast
119 .attrs
120 .iter()
121 .filter(|attr| match attr.interpret_meta() {
122 Some(Meta::NameValue(name_value)) => {
123 (name_value.ident == "grammar" || name_value.ident == "grammar_inline")
124 }
125 _ => false,
126 })
127 .collect();
128
129 let argument = match grammar.len() {
130 0 => panic!("a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute"),
131 1 => get_attribute(grammar[0]),
132 _ => panic!("only 1 grammar file can be provided"),
133 };
134
135 (name, generics, argument)
136 }
137
138 fn get_attribute(attr: &Attribute) -> GrammarSource {
139 match attr.interpret_meta() {
140 Some(Meta::NameValue(name_value)) => match name_value.lit {
141 Lit::Str(string) => {
142 if name_value.ident == "grammar" {
143 GrammarSource::File(string.value())
144 } else {
145 GrammarSource::Inline(string.value())
146 }
147 }
148 _ => panic!("grammar attribute must be a string"),
149 },
150 _ => panic!("grammar attribute must be of the form `grammar = \"...\"`"),
151 }
152 }
153
154 #[cfg(test)]
155 mod tests {
156 use super::parse_derive;
157 use super::GrammarSource;
158 use syn;
159
160 #[test]
161 fn derive_inline_file() {
162 let definition = "
163 #[other_attr]
164 #[grammar_inline = \"GRAMMAR\"]
165 pub struct MyParser<'a, T>;
166 ";
167 let ast = syn::parse_str(definition).unwrap();
168 let (_, _, filename) = parse_derive(ast);
169 assert_eq!(filename, GrammarSource::Inline("GRAMMAR".to_string()));
170 }
171
172 #[test]
173 fn derive_ok() {
174 let definition = "
175 #[other_attr]
176 #[grammar = \"myfile.pest\"]
177 pub struct MyParser<'a, T>;
178 ";
179 let ast = syn::parse_str(definition).unwrap();
180 let (_, _, filename) = parse_derive(ast);
181 assert_eq!(filename, GrammarSource::File("myfile.pest".to_string()));
182 }
183
184 #[test]
185 #[should_panic(expected = "only 1 grammar file can be provided")]
186 fn derive_multiple_grammars() {
187 let definition = "
188 #[other_attr]
189 #[grammar = \"myfile1.pest\"]
190 #[grammar = \"myfile2.pest\"]
191 pub struct MyParser<'a, T>;
192 ";
193 let ast = syn::parse_str(definition).unwrap();
194 parse_derive(ast);
195 }
196
197 #[test]
198 #[should_panic(expected = "grammar attribute must be a string")]
199 fn derive_wrong_arg() {
200 let definition = "
201 #[other_attr]
202 #[grammar = 1]
203 pub struct MyParser<'a, T>;
204 ";
205 let ast = syn::parse_str(definition).unwrap();
206 parse_derive(ast);
207 }
208 }