diff options
author | David Lattimore <[email protected]> | 2020-07-03 03:57:17 +0100 |
---|---|---|
committer | David Lattimore <[email protected]> | 2020-07-24 12:34:00 +0100 |
commit | 1fce8b6ba32bebba36d588d07781e9e578845728 (patch) | |
tree | c027096421e49a7e0279711e0ccd7491ca3d6a9a /crates/ra_ssr/src/parsing.rs | |
parent | 2b53639e381b1f17c829fb33f6e4135a9c930f41 (diff) |
SSR: Change the way rules are stored internally.
Previously we had:
- Multiple rules
- Each rule had its pattern parsed as an expression, path etc
This meant that there were two levels at which there could be multiple
rules.
Now we just have multiple rules. If a pattern can parse as more than one
kind of thing, then they get stored as multiple separate rules.
We also now don't have separate fields for the different kinds of things
that a pattern can parse as. This makes adding new kinds of things
simpler.
Previously, add_search_pattern would construct a rule with a dummy
replacement. Now the replacement is an Option. This is slightly cleaner
and also opens the way for parsing the replacement template as the same
kind of thing as the search pattern.
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r-- | crates/ra_ssr/src/parsing.rs | 106 |
1 files changed, 76 insertions, 30 deletions
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs index 4aee97bb2..682b7011a 100644 --- a/crates/ra_ssr/src/parsing.rs +++ b/crates/ra_ssr/src/parsing.rs | |||
@@ -7,17 +7,24 @@ | |||
7 | 7 | ||
8 | use crate::errors::bail; | 8 | use crate::errors::bail; |
9 | use crate::{SsrError, SsrPattern, SsrRule}; | 9 | use crate::{SsrError, SsrPattern, SsrRule}; |
10 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T}; | 10 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, T}; |
11 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
12 | use std::str::FromStr; | 12 | use std::str::FromStr; |
13 | 13 | ||
14 | #[derive(Debug)] | ||
15 | pub(crate) struct ParsedRule { | ||
16 | pub(crate) placeholders_by_stand_in: FxHashMap<SmolStr, Placeholder>, | ||
17 | pub(crate) pattern: SyntaxNode, | ||
18 | pub(crate) template: Option<SsrTemplate>, | ||
19 | } | ||
20 | |||
14 | #[derive(Clone, Debug)] | 21 | #[derive(Clone, Debug)] |
15 | pub(crate) struct SsrTemplate { | 22 | pub(crate) struct SsrTemplate { |
16 | pub(crate) tokens: Vec<PatternElement>, | 23 | pub(crate) tokens: Vec<PatternElement>, |
17 | } | 24 | } |
18 | 25 | ||
19 | #[derive(Debug)] | 26 | #[derive(Debug)] |
20 | pub(crate) struct RawSearchPattern { | 27 | pub(crate) struct RawPattern { |
21 | tokens: Vec<PatternElement>, | 28 | tokens: Vec<PatternElement>, |
22 | } | 29 | } |
23 | 30 | ||
@@ -54,6 +61,50 @@ pub(crate) struct Token { | |||
54 | pub(crate) text: SmolStr, | 61 | pub(crate) text: SmolStr, |
55 | } | 62 | } |
56 | 63 | ||
64 | impl ParsedRule { | ||
65 | fn new( | ||
66 | pattern: &RawPattern, | ||
67 | template: Option<&SsrTemplate>, | ||
68 | ) -> Result<Vec<ParsedRule>, SsrError> { | ||
69 | let raw_pattern = pattern.as_rust_code(); | ||
70 | let mut builder = RuleBuilder { | ||
71 | placeholders_by_stand_in: pattern.placeholders_by_stand_in(), | ||
72 | rules: Vec::new(), | ||
73 | }; | ||
74 | builder.try_add(ast::Expr::parse(&raw_pattern), template); | ||
75 | builder.try_add(ast::TypeRef::parse(&raw_pattern), template); | ||
76 | builder.try_add(ast::ModuleItem::parse(&raw_pattern), template); | ||
77 | builder.try_add(ast::Path::parse(&raw_pattern), template); | ||
78 | builder.try_add(ast::Pat::parse(&raw_pattern), template); | ||
79 | builder.build() | ||
80 | } | ||
81 | } | ||
82 | |||
83 | struct RuleBuilder { | ||
84 | placeholders_by_stand_in: FxHashMap<SmolStr, Placeholder>, | ||
85 | rules: Vec<ParsedRule>, | ||
86 | } | ||
87 | |||
88 | impl RuleBuilder { | ||
89 | fn try_add<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<&SsrTemplate>) { | ||
90 | match pattern { | ||
91 | Ok(pattern) => self.rules.push(ParsedRule { | ||
92 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), | ||
93 | pattern: pattern.syntax().clone(), | ||
94 | template: template.cloned(), | ||
95 | }), | ||
96 | _ => {} | ||
97 | } | ||
98 | } | ||
99 | |||
100 | fn build(self) -> Result<Vec<ParsedRule>, SsrError> { | ||
101 | if self.rules.is_empty() { | ||
102 | bail!("Pattern is not a valid Rust expression, type, item, path or pattern"); | ||
103 | } | ||
104 | Ok(self.rules) | ||
105 | } | ||
106 | } | ||
107 | |||
57 | impl FromStr for SsrRule { | 108 | impl FromStr for SsrRule { |
58 | type Err = SsrError; | 109 | type Err = SsrError; |
59 | 110 | ||
@@ -68,21 +119,24 @@ impl FromStr for SsrRule { | |||
68 | if it.next().is_some() { | 119 | if it.next().is_some() { |
69 | return Err(SsrError("More than one delimiter found".into())); | 120 | return Err(SsrError("More than one delimiter found".into())); |
70 | } | 121 | } |
71 | let rule = SsrRule { pattern: pattern.parse()?, template: template.parse()? }; | 122 | let raw_pattern = pattern.parse()?; |
123 | let raw_template = template.parse()?; | ||
124 | let parsed_rules = ParsedRule::new(&raw_pattern, Some(&raw_template))?; | ||
125 | let rule = SsrRule { pattern: raw_pattern, template: raw_template, parsed_rules }; | ||
72 | validate_rule(&rule)?; | 126 | validate_rule(&rule)?; |
73 | Ok(rule) | 127 | Ok(rule) |
74 | } | 128 | } |
75 | } | 129 | } |
76 | 130 | ||
77 | impl FromStr for RawSearchPattern { | 131 | impl FromStr for RawPattern { |
78 | type Err = SsrError; | 132 | type Err = SsrError; |
79 | 133 | ||
80 | fn from_str(pattern_str: &str) -> Result<RawSearchPattern, SsrError> { | 134 | fn from_str(pattern_str: &str) -> Result<RawPattern, SsrError> { |
81 | Ok(RawSearchPattern { tokens: parse_pattern(pattern_str)? }) | 135 | Ok(RawPattern { tokens: parse_pattern(pattern_str)? }) |
82 | } | 136 | } |
83 | } | 137 | } |
84 | 138 | ||
85 | impl RawSearchPattern { | 139 | impl RawPattern { |
86 | /// Returns this search pattern as Rust source code that we can feed to the Rust parser. | 140 | /// Returns this search pattern as Rust source code that we can feed to the Rust parser. |
87 | fn as_rust_code(&self) -> String { | 141 | fn as_rust_code(&self) -> String { |
88 | let mut res = String::new(); | 142 | let mut res = String::new(); |
@@ -95,7 +149,7 @@ impl RawSearchPattern { | |||
95 | res | 149 | res |
96 | } | 150 | } |
97 | 151 | ||
98 | fn placeholders_by_stand_in(&self) -> FxHashMap<SmolStr, Placeholder> { | 152 | pub(crate) fn placeholders_by_stand_in(&self) -> FxHashMap<SmolStr, Placeholder> { |
99 | let mut res = FxHashMap::default(); | 153 | let mut res = FxHashMap::default(); |
100 | for t in &self.tokens { | 154 | for t in &self.tokens { |
101 | if let PatternElement::Placeholder(placeholder) = t { | 155 | if let PatternElement::Placeholder(placeholder) = t { |
@@ -106,30 +160,22 @@ impl RawSearchPattern { | |||
106 | } | 160 | } |
107 | } | 161 | } |
108 | 162 | ||
163 | impl ParsedRule { | ||
164 | pub(crate) fn get_placeholder(&self, token: &SyntaxToken) -> Option<&Placeholder> { | ||
165 | if token.kind() != SyntaxKind::IDENT { | ||
166 | return None; | ||
167 | } | ||
168 | self.placeholders_by_stand_in.get(token.text()) | ||
169 | } | ||
170 | } | ||
171 | |||
109 | impl FromStr for SsrPattern { | 172 | impl FromStr for SsrPattern { |
110 | type Err = SsrError; | 173 | type Err = SsrError; |
111 | 174 | ||
112 | fn from_str(pattern_str: &str) -> Result<SsrPattern, SsrError> { | 175 | fn from_str(pattern_str: &str) -> Result<SsrPattern, SsrError> { |
113 | let raw: RawSearchPattern = pattern_str.parse()?; | 176 | let raw_pattern = pattern_str.parse()?; |
114 | let raw_str = raw.as_rust_code(); | 177 | let parsed_rules = ParsedRule::new(&raw_pattern, None)?; |
115 | let res = SsrPattern { | 178 | Ok(SsrPattern { raw: raw_pattern, parsed_rules }) |
116 | expr: ast::Expr::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
117 | type_ref: ast::TypeRef::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
118 | item: ast::ModuleItem::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
119 | path: ast::Path::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
120 | pattern: ast::Pat::parse(&raw_str).ok().map(|n| n.syntax().clone()), | ||
121 | placeholders_by_stand_in: raw.placeholders_by_stand_in(), | ||
122 | raw, | ||
123 | }; | ||
124 | if res.expr.is_none() | ||
125 | && res.type_ref.is_none() | ||
126 | && res.item.is_none() | ||
127 | && res.path.is_none() | ||
128 | && res.pattern.is_none() | ||
129 | { | ||
130 | bail!("Pattern is not a valid Rust expression, type, item, path or pattern"); | ||
131 | } | ||
132 | Ok(res) | ||
133 | } | 179 | } |
134 | } | 180 | } |
135 | 181 | ||
@@ -173,7 +219,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> { | |||
173 | /// pattern didn't define. | 219 | /// pattern didn't define. |
174 | fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { | 220 | fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { |
175 | let mut defined_placeholders = FxHashSet::default(); | 221 | let mut defined_placeholders = FxHashSet::default(); |
176 | for p in &rule.pattern.raw.tokens { | 222 | for p in &rule.pattern.tokens { |
177 | if let PatternElement::Placeholder(placeholder) = p { | 223 | if let PatternElement::Placeholder(placeholder) = p { |
178 | defined_placeholders.insert(&placeholder.ident); | 224 | defined_placeholders.insert(&placeholder.ident); |
179 | } | 225 | } |
@@ -316,7 +362,7 @@ mod tests { | |||
316 | } | 362 | } |
317 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); | 363 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); |
318 | assert_eq!( | 364 | assert_eq!( |
319 | result.pattern.raw.tokens, | 365 | result.pattern.tokens, |
320 | vec![ | 366 | vec![ |
321 | token(SyntaxKind::IDENT, "foo"), | 367 | token(SyntaxKind::IDENT, "foo"), |
322 | token(T!['('], "("), | 368 | token(T!['('], "("), |