• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! Types and helpers to parse the input of the derive macro.
11 
12 use syn::{Attribute, DeriveInput, Expr, ExprLit, Generics, Ident, Lit, Meta};
13 
14 #[derive(Debug, PartialEq)]
15 pub(crate) enum GrammarSource {
16     File(String),
17     Inline(String),
18 }
19 
20 /// Parsed information of the derive and the attributes.
21 pub struct ParsedDerive {
22     /// The identifier of the deriving struct, union, or enum.
23     pub name: Ident,
24     /// The generics of the deriving struct, union, or enum.
25     pub generics: Generics,
26     /// Indicates whether the 'non_exhaustive' attribute is added to the 'Rule' enum.
27     pub non_exhaustive: bool,
28 }
29 
parse_derive(ast: DeriveInput) -> (ParsedDerive, Vec<GrammarSource>)30 pub(crate) fn parse_derive(ast: DeriveInput) -> (ParsedDerive, Vec<GrammarSource>) {
31     let name = ast.ident;
32     let generics = ast.generics;
33 
34     let grammar: Vec<&Attribute> = ast
35         .attrs
36         .iter()
37         .filter(|attr| {
38             let path = attr.meta.path();
39             path.is_ident("grammar") || path.is_ident("grammar_inline")
40         })
41         .collect();
42 
43     if grammar.is_empty() {
44         panic!("a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute");
45     }
46 
47     let mut grammar_sources = Vec::with_capacity(grammar.len());
48     for attr in grammar {
49         grammar_sources.push(get_attribute(attr))
50     }
51 
52     let non_exhaustive = ast
53         .attrs
54         .iter()
55         .any(|attr| attr.meta.path().is_ident("non_exhaustive"));
56 
57     (
58         ParsedDerive {
59             name,
60             generics,
61             non_exhaustive,
62         },
63         grammar_sources,
64     )
65 }
66 
get_attribute(attr: &Attribute) -> GrammarSource67 fn get_attribute(attr: &Attribute) -> GrammarSource {
68     match &attr.meta {
69         Meta::NameValue(name_value) => match &name_value.value {
70             Expr::Lit(ExprLit {
71                 lit: Lit::Str(string),
72                 ..
73             }) => {
74                 if name_value.path.is_ident("grammar") {
75                     GrammarSource::File(string.value())
76                 } else {
77                     GrammarSource::Inline(string.value())
78                 }
79             }
80             _ => panic!("grammar attribute must be a string"),
81         },
82         _ => panic!("grammar attribute must be of the form `grammar = \"...\"`"),
83     }
84 }
85 
86 #[cfg(test)]
87 mod tests {
88     use super::parse_derive;
89     use super::GrammarSource;
90 
91     #[test]
derive_inline_file()92     fn derive_inline_file() {
93         let definition = "
94             #[other_attr]
95             #[grammar_inline = \"GRAMMAR\"]
96             pub struct MyParser<'a, T>;
97         ";
98         let ast = syn::parse_str(definition).unwrap();
99         let (_, filenames) = parse_derive(ast);
100         assert_eq!(filenames, [GrammarSource::Inline("GRAMMAR".to_string())]);
101     }
102 
103     #[test]
derive_ok()104     fn derive_ok() {
105         let definition = "
106             #[other_attr]
107             #[grammar = \"myfile.pest\"]
108             pub struct MyParser<'a, T>;
109         ";
110         let ast = syn::parse_str(definition).unwrap();
111         let (parsed_derive, filenames) = parse_derive(ast);
112         assert_eq!(filenames, [GrammarSource::File("myfile.pest".to_string())]);
113         assert!(!parsed_derive.non_exhaustive);
114     }
115 
116     #[test]
derive_multiple_grammars()117     fn derive_multiple_grammars() {
118         let definition = "
119             #[other_attr]
120             #[grammar = \"myfile1.pest\"]
121             #[grammar = \"myfile2.pest\"]
122             pub struct MyParser<'a, T>;
123         ";
124         let ast = syn::parse_str(definition).unwrap();
125         let (_, filenames) = parse_derive(ast);
126         assert_eq!(
127             filenames,
128             [
129                 GrammarSource::File("myfile1.pest".to_string()),
130                 GrammarSource::File("myfile2.pest".to_string())
131             ]
132         );
133     }
134 
135     #[test]
derive_nonexhaustive()136     fn derive_nonexhaustive() {
137         let definition = "
138             #[non_exhaustive]
139             #[grammar = \"myfile.pest\"]
140             pub struct MyParser<'a, T>;
141         ";
142         let ast = syn::parse_str(definition).unwrap();
143         let (parsed_derive, filenames) = parse_derive(ast);
144         assert_eq!(filenames, [GrammarSource::File("myfile.pest".to_string())]);
145         assert!(parsed_derive.non_exhaustive);
146     }
147 
148     #[test]
149     #[should_panic(expected = "grammar attribute must be a string")]
derive_wrong_arg()150     fn derive_wrong_arg() {
151         let definition = "
152             #[other_attr]
153             #[grammar = 1]
154             pub struct MyParser<'a, T>;
155         ";
156         let ast = syn::parse_str(definition).unwrap();
157         parse_derive(ast);
158     }
159 
160     #[test]
161     #[should_panic(
162         expected = "a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute"
163     )]
derive_no_grammar()164     fn derive_no_grammar() {
165         let definition = "
166             #[other_attr]
167             pub struct MyParser<'a, T>;
168         ";
169         let ast = syn::parse_str(definition).unwrap();
170         parse_derive(ast);
171     }
172 }
173