Skip to content

Commit 34385e0

Browse files
committed
Rewrite line_doc extract in pest_generator, avoid change AST.
1 parent 02356b1 commit 34385e0

14 files changed

Lines changed: 187 additions & 145 deletions

File tree

generator/src/generator.rs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,28 @@ use pest::unicode::unicode_property_names;
1717
use pest_meta::ast::*;
1818
use pest_meta::optimizer::*;
1919

20-
pub fn generate(
20+
#[derive(Debug)]
21+
pub(crate) struct DocComment<'a> {
22+
pub(crate) grammar_docs: Vec<&'a str>,
23+
pub(crate) line_docs: Vec<Vec<&'a str>>,
24+
pub(crate) rules: Vec<Rule>,
25+
}
26+
27+
impl DocComment<'_> {
28+
fn line_docs_for_rule(&self, rule_name: &str) -> Option<String> {
29+
let idx = self.rules.iter().position(|r| r.name == rule_name)?;
30+
31+
self.line_docs.get(idx).map(|comments| comments.join("\n"))
32+
}
33+
}
34+
35+
pub(crate) fn generate(
2136
name: Ident,
2237
generics: &Generics,
2338
path: Option<PathBuf>,
2439
rules: Vec<OptimizedRule>,
2540
defaults: Vec<&str>,
26-
grammar_docs: Vec<&str>,
41+
doc_comment: &DocComment<'_>,
2742
include_grammar: bool,
2843
) -> TokenStream {
2944
let uses_eoi = defaults.iter().any(|name| *name == "EOI");
@@ -37,7 +52,7 @@ pub fn generate(
3752
} else {
3853
quote!()
3954
};
40-
let rule_enum = generate_enum(&rules, grammar_docs, uses_eoi);
55+
let rule_enum = generate_enum(&rules, doc_comment, uses_eoi);
4156
let patterns = generate_patterns(&rules, uses_eoi);
4257
let skip = generate_skip(&rules);
4358

@@ -182,23 +197,29 @@ fn generate_include(name: &Ident, path: &str) -> TokenStream {
182197
}
183198
}
184199

185-
fn generate_enum(rules: &[OptimizedRule], grammar_docs: Vec<&str>, uses_eoi: bool) -> TokenStream {
200+
fn generate_enum(
201+
rules: &[OptimizedRule],
202+
doc_comment: &DocComment<'_>,
203+
uses_eoi: bool,
204+
) -> TokenStream {
186205
let rules = rules.iter().map(|rule| {
187206
let rule_name = format_ident!("r#{}", rule.name);
188-
if rule.comments.is_empty() {
207+
208+
let comments = doc_comment.line_docs_for_rule(&rule.name);
209+
let comments = comments.unwrap_or_else(|| "".to_owned());
210+
if comments.is_empty() {
189211
quote! {
190212
#rule_name
191213
}
192214
} else {
193-
let comments = rule.comments.join("\n");
194215
quote! {
195216
#[doc = #comments]
196217
#rule_name
197218
}
198219
}
199220
});
200221

201-
let grammar_docs = grammar_docs.join("\n");
222+
let grammar_docs = doc_comment.grammar_docs.join("\n");
202223
if uses_eoi {
203224
quote! {
204225
#[doc = #grammar_docs]
@@ -686,11 +707,20 @@ mod tests {
686707
name: "f".to_owned(),
687708
ty: RuleType::Normal,
688709
expr: OptimizedExpr::Ident("g".to_owned()),
689-
comments: vec!["This is rule comment".to_owned()],
690710
}];
691711

712+
let doc_comment = &DocComment {
713+
grammar_docs: vec!["Rule doc", "hello"],
714+
line_docs: vec![vec!["This is rule comment"]],
715+
rules: vec![Rule {
716+
name: "f".to_owned(),
717+
ty: RuleType::Normal,
718+
expr: Expr::Ident("g".to_owned()),
719+
}],
720+
};
721+
692722
assert_eq!(
693-
generate_enum(&rules, vec!["Rule doc", "hello"], false).to_string(),
723+
generate_enum(&rules, doc_comment, false).to_string(),
694724
quote! {
695725
#[doc = "Rule doc\nhello"]
696726
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
@@ -988,24 +1018,39 @@ mod tests {
9881018
name: "a".to_owned(),
9891019
ty: RuleType::Silent,
9901020
expr: OptimizedExpr::Str("b".to_owned()),
991-
comments: vec![],
9921021
},
9931022
OptimizedRule {
9941023
name: "if".to_owned(),
9951024
ty: RuleType::Silent,
9961025
expr: OptimizedExpr::Ident("a".to_owned()),
997-
comments: vec!["If statement".to_owned()],
9981026
},
9991027
];
10001028

1029+
let doc_comment = &DocComment {
1030+
line_docs: vec![vec![], vec!["If statement"]],
1031+
grammar_docs: vec!["This is Rule doc", "This is second line"],
1032+
rules: vec![
1033+
Rule {
1034+
name: "a".to_owned(),
1035+
ty: RuleType::Silent,
1036+
expr: Expr::Str("b".to_owned()),
1037+
},
1038+
Rule {
1039+
name: "if".to_owned(),
1040+
ty: RuleType::Silent,
1041+
expr: Expr::Str("b".to_owned()),
1042+
},
1043+
],
1044+
};
1045+
10011046
let defaults = vec!["ANY"];
10021047
let result = result_type();
10031048
let box_ty = box_type();
10041049
let mut current_dir = std::env::current_dir().expect("Unable to get current directory");
10051050
current_dir.push("test.pest");
10061051
let test_path = current_dir.to_str().expect("path contains invalid unicode");
10071052
assert_eq!(
1008-
generate(name, &generics, Some(PathBuf::from("test.pest")), rules, defaults, vec!["This is Rule doc", "This is second line"], true).to_string(),
1053+
generate(name, &generics, Some(PathBuf::from("test.pest")), rules, defaults, doc_comment, true).to_string(),
10091054
quote! {
10101055
#[allow(non_upper_case_globals)]
10111056
const _PEST_GRAMMAR_MyParser: &'static str = include_str!(#test_path);

generator/src/lib.rs

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ mod generator;
3737
use pest_meta::parser::{self, rename_meta_rule, Rule};
3838
use pest_meta::{optimizer, unwrap_or_report, validator};
3939

40+
use generator::DocComment;
41+
4042
/// Processes the derive/proc macro input and generates the corresponding parser based
4143
/// on the parsed grammar. If `include_grammar` is set to true, it'll generate an explicit
4244
/// "include_str" statement (done in pest_derive, but turned off in the local bootstrap).
@@ -92,18 +94,25 @@ pub fn derive_parser(input: TokenStream, include_grammar: bool) -> TokenStream {
9294
};
9395

9496
let grammar_docs = consume_grammar_doc(pairs.clone());
97+
let line_docs = consume_line_docs(pairs.clone());
9598

9699
let defaults = unwrap_or_report(validator::validate_pairs(pairs.clone()));
97100
let ast = unwrap_or_report(parser::consume_rules(pairs));
98-
let optimized = optimizer::optimize(ast);
101+
let optimized = optimizer::optimize(ast.clone());
102+
103+
let doc_comment = DocComment {
104+
grammar_docs,
105+
line_docs,
106+
rules: ast,
107+
};
99108

100109
generator::generate(
101110
name,
102111
&generics,
103112
path,
104113
optimized,
105114
defaults,
106-
grammar_docs,
115+
&doc_comment,
107116
include_grammar,
108117
)
109118
}
@@ -119,6 +128,32 @@ fn consume_grammar_doc(pairs: Pairs<'_, Rule>) -> Vec<&'_ str> {
119128
docs
120129
}
121130

131+
fn consume_line_docs(pairs: Pairs<'_, Rule>) -> Vec<Vec<&'_ str>> {
132+
let mut docs = vec![];
133+
let mut comments = vec![];
134+
135+
for pair in pairs {
136+
if pair.as_rule() == Rule::grammar_rule {
137+
if let Some(inner) = pair.into_inner().next() {
138+
if inner.as_rule() == Rule::line_doc {
139+
comments.push(inner.as_str()[3..inner.as_str().len()].trim());
140+
continue;
141+
} else {
142+
docs.push(comments);
143+
comments = vec![];
144+
}
145+
}
146+
}
147+
148+
if !comments.is_empty() {
149+
docs.push(comments);
150+
comments = vec![];
151+
}
152+
}
153+
154+
docs
155+
}
156+
122157
fn read_file<P: AsRef<Path>>(path: P) -> io::Result<String> {
123158
let mut file = File::open(path.as_ref())?;
124159
let mut string = String::new();
@@ -177,9 +212,12 @@ fn get_attribute(attr: &Attribute) -> GrammarSource {
177212

178213
#[cfg(test)]
179214
mod tests {
215+
use super::consume_line_docs;
180216
use super::parse_derive;
181217
use super::GrammarSource;
182218

219+
use pest_meta::parser::{self, Rule};
220+
183221
#[test]
184222
fn derive_inline_file() {
185223
let definition = "
@@ -247,4 +285,57 @@ mod tests {
247285
let ast = syn::parse_str(definition).unwrap();
248286
parse_derive(ast);
249287
}
288+
289+
#[test]
290+
fn test_consume_line_docs() {
291+
let pairs = match parser::parse(Rule::grammar_rules, include_str!("../tests/test.pest")) {
292+
Ok(pairs) => pairs,
293+
Err(_) => panic!("error parsing tests/test.pest"),
294+
};
295+
296+
let line_docs = consume_line_docs(pairs);
297+
assert_eq!(
298+
vec![
299+
vec!["Matches foo str, e.g.: `foo`"],
300+
vec!["Matches bar str,", "e.g: `bar` or `foobar`"],
301+
vec![],
302+
vec!["Matches dar", "Match dar description"]
303+
],
304+
line_docs
305+
);
306+
}
307+
308+
#[test]
309+
fn test_generate_doc() {
310+
let input = quote! {
311+
#[derive(Parser)]
312+
#[grammar = "../tests/test.pest"]
313+
pub struct TestParser;
314+
};
315+
316+
let token = super::derive_parser(input, true);
317+
318+
let expected = quote! {
319+
#[doc = "A parser for JSON file.\nAnd this is a example for JSON parser."]
320+
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
321+
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
322+
323+
pub enum Rule {
324+
#[doc = "Matches foo str, e.g.: `foo`"]
325+
r#foo,
326+
#[doc = "Matches bar str,\ne.g: `bar` or `foobar`"]
327+
r#bar,
328+
r#bar1,
329+
#[doc = "Matches dar\nMatch dar description"]
330+
r#dar
331+
}
332+
};
333+
334+
assert!(
335+
token.to_string().contains(expected.to_string().as_str()),
336+
"{}\n\nExpected to contains:\n{}",
337+
token,
338+
expected
339+
);
340+
}
250341
}

generator/tests/test.pest

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! A parser for JSON file.
2+
//! And this is a example for JSON parser.
3+
4+
/// Matches foo str, e.g.: `foo`
5+
foo = { "foo" }
6+
7+
/// Matches bar str,
8+
/// e.g: `bar` or `foobar`
9+
10+
bar = { "bar" | "foobar" }
11+
12+
bar1 = { "bar1" }
13+
14+
/// Matches dar
15+
16+
/// Match dar description
17+
18+
dar = { "da" }

grammars/src/grammars/json.pest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
json = { SOI ~ (object | array) ~ EOI }
1313

1414
/// Matches object, e.g.: `{ "foo": "bar" }`
15+
/// Foobar
1516
object = { "{" ~ pair ~ ("," ~ pair)* ~ "}" | "{" ~ "}" }
1617
pair = { string ~ ":" ~ value }
1718

meta/src/ast.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
//! Types for the pest's abstract syntax tree.
1111
1212
/// A grammar rule
13-
#[non_exhaustive]
1413
#[derive(Clone, Debug, Eq, PartialEq)]
1514
pub struct Rule {
1615
/// The name of the rule
@@ -19,8 +18,6 @@ pub struct Rule {
1918
pub ty: RuleType,
2019
/// The rule's expression
2120
pub expr: Expr,
22-
/// Doc comments of the rule
23-
pub(crate) comments: Vec<String>,
2421
}
2522

2623
/// All possible rule types

meta/src/optimizer/concatenator.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@
1010
use crate::ast::*;
1111

1212
pub fn concatenate(rule: Rule) -> Rule {
13-
let Rule {
14-
name,
15-
ty,
16-
expr,
17-
comments,
18-
} = rule;
13+
let Rule { name, ty, expr } = rule;
1914
Rule {
2015
name,
2116
ty,
@@ -34,6 +29,5 @@ pub fn concatenate(rule: Rule) -> Rule {
3429
expr
3530
}
3631
}),
37-
comments,
3832
}
3933
}

meta/src/optimizer/factorizer.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@
1010
use crate::ast::*;
1111

1212
pub fn factor(rule: Rule) -> Rule {
13-
let Rule {
14-
name,
15-
ty,
16-
expr,
17-
comments,
18-
} = rule;
13+
let Rule { name, ty, expr } = rule;
1914
Rule {
2015
name,
2116
ty,
@@ -52,6 +47,5 @@ pub fn factor(rule: Rule) -> Rule {
5247
expr => expr,
5348
}
5449
}),
55-
comments,
5650
}
5751
}

meta/src/optimizer/lister.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@
1010
use crate::ast::*;
1111

1212
pub fn list(rule: Rule) -> Rule {
13-
let Rule {
14-
name,
15-
ty,
16-
expr,
17-
comments,
18-
} = rule;
13+
let Rule { name, ty, expr } = rule;
1914
Rule {
2015
name,
2116
ty,
@@ -43,6 +38,5 @@ pub fn list(rule: Rule) -> Rule {
4338
expr => expr,
4439
}
4540
}),
46-
comments,
4741
}
4842
}

0 commit comments

Comments
 (0)