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 
derive_parser(input: TokenStream, include_grammar: bool) -> TokenStream37 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 
read_file<P: AsRef<Path>>(path: P) -> io::Result<String>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 
parse_derive(ast: DeriveInput) -> (Ident, Generics, GrammarSource)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.parse_meta() {
122             Ok(Meta::NameValue(name_value)) => {
123                 name_value.path.is_ident("grammar") || name_value.path.is_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 
get_attribute(attr: &Attribute) -> GrammarSource138 fn get_attribute(attr: &Attribute) -> GrammarSource {
139     match attr.parse_meta() {
140         Ok(Meta::NameValue(name_value)) => match name_value.lit {
141             Lit::Str(string) => {
142                 if name_value.path.is_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]
derive_inline_file()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]
derive_ok()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")]
derive_multiple_grammars()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")]
derive_wrong_arg()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 }
209