diff options
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r-- | crates/ra_ssr/src/parsing.rs | 110 |
1 files changed, 96 insertions, 14 deletions
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs index 1ae166d19..5ea125616 100644 --- a/crates/ra_ssr/src/parsing.rs +++ b/crates/ra_ssr/src/parsing.rs | |||
@@ -6,7 +6,7 @@ | |||
6 | //! e.g. expressions, type references etc. | 6 | //! e.g. expressions, type references etc. |
7 | 7 | ||
8 | use crate::{SsrError, SsrPattern, SsrRule}; | 8 | use crate::{SsrError, SsrPattern, SsrRule}; |
9 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind}; | 9 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T}; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use std::str::FromStr; | 11 | use std::str::FromStr; |
12 | 12 | ||
@@ -39,6 +39,18 @@ pub(crate) struct Placeholder { | |||
39 | pub(crate) ident: SmolStr, | 39 | pub(crate) ident: SmolStr, |
40 | /// A unique name used in place of this placeholder when we parse the pattern as Rust code. | 40 | /// A unique name used in place of this placeholder when we parse the pattern as Rust code. |
41 | stand_in_name: String, | 41 | stand_in_name: String, |
42 | pub(crate) constraints: Vec<Constraint>, | ||
43 | } | ||
44 | |||
45 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
46 | pub(crate) enum Constraint { | ||
47 | Kind(NodeKind), | ||
48 | Not(Box<Constraint>), | ||
49 | } | ||
50 | |||
51 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
52 | pub(crate) enum NodeKind { | ||
53 | Literal, | ||
42 | } | 54 | } |
43 | 55 | ||
44 | #[derive(Debug, Clone, PartialEq, Eq)] | 56 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -55,7 +67,7 @@ impl FromStr for SsrRule { | |||
55 | let pattern = it.next().expect("at least empty string").trim(); | 67 | let pattern = it.next().expect("at least empty string").trim(); |
56 | let template = it | 68 | let template = it |
57 | .next() | 69 | .next() |
58 | .ok_or_else(|| SsrError("Cannot find delemiter `==>>`".into()))? | 70 | .ok_or_else(|| SsrError("Cannot find delimiter `==>>`".into()))? |
59 | .trim() | 71 | .trim() |
60 | .to_string(); | 72 | .to_string(); |
61 | if it.next().is_some() { | 73 | if it.next().is_some() { |
@@ -149,7 +161,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> { | |||
149 | let mut placeholder_names = FxHashSet::default(); | 161 | let mut placeholder_names = FxHashSet::default(); |
150 | let mut tokens = tokenize(pattern_str)?.into_iter(); | 162 | let mut tokens = tokenize(pattern_str)?.into_iter(); |
151 | while let Some(token) = tokens.next() { | 163 | while let Some(token) = tokens.next() { |
152 | if token.kind == SyntaxKind::DOLLAR { | 164 | if token.kind == T![$] { |
153 | let placeholder = parse_placeholder(&mut tokens)?; | 165 | let placeholder = parse_placeholder(&mut tokens)?; |
154 | if !placeholder_names.insert(placeholder.ident.clone()) { | 166 | if !placeholder_names.insert(placeholder.ident.clone()) { |
155 | bail!("Name `{}` repeats more than once", placeholder.ident); | 167 | bail!("Name `{}` repeats more than once", placeholder.ident); |
@@ -177,6 +189,9 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> { | |||
177 | if !defined_placeholders.contains(&placeholder.ident) { | 189 | if !defined_placeholders.contains(&placeholder.ident) { |
178 | undefined.push(format!("${}", placeholder.ident)); | 190 | undefined.push(format!("${}", placeholder.ident)); |
179 | } | 191 | } |
192 | if !placeholder.constraints.is_empty() { | ||
193 | bail!("Replacement placeholders cannot have constraints"); | ||
194 | } | ||
180 | } | 195 | } |
181 | } | 196 | } |
182 | if !undefined.is_empty() { | 197 | if !undefined.is_empty() { |
@@ -205,23 +220,90 @@ fn tokenize(source: &str) -> Result<Vec<Token>, SsrError> { | |||
205 | 220 | ||
206 | fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> { | 221 | fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> { |
207 | let mut name = None; | 222 | let mut name = None; |
223 | let mut constraints = Vec::new(); | ||
208 | if let Some(token) = tokens.next() { | 224 | if let Some(token) = tokens.next() { |
209 | match token.kind { | 225 | match token.kind { |
210 | SyntaxKind::IDENT => { | 226 | SyntaxKind::IDENT => { |
211 | name = Some(token.text); | 227 | name = Some(token.text); |
212 | } | 228 | } |
229 | T!['{'] => { | ||
230 | let token = | ||
231 | tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?; | ||
232 | if token.kind == SyntaxKind::IDENT { | ||
233 | name = Some(token.text); | ||
234 | } | ||
235 | loop { | ||
236 | let token = tokens | ||
237 | .next() | ||
238 | .ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?; | ||
239 | match token.kind { | ||
240 | T![:] => { | ||
241 | constraints.push(parse_constraint(tokens)?); | ||
242 | } | ||
243 | T!['}'] => break, | ||
244 | _ => bail!("Unexpected token while parsing placeholder: '{}'", token.text), | ||
245 | } | ||
246 | } | ||
247 | } | ||
213 | _ => { | 248 | _ => { |
214 | bail!("Placeholders should be $name"); | 249 | bail!("Placeholders should either be $name or ${name:constraints}"); |
215 | } | 250 | } |
216 | } | 251 | } |
217 | } | 252 | } |
218 | let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?; | 253 | let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?; |
219 | Ok(Placeholder::new(name)) | 254 | Ok(Placeholder::new(name, constraints)) |
255 | } | ||
256 | |||
257 | fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> { | ||
258 | let constraint_type = tokens | ||
259 | .next() | ||
260 | .ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))? | ||
261 | .text | ||
262 | .to_string(); | ||
263 | match constraint_type.as_str() { | ||
264 | "kind" => { | ||
265 | expect_token(tokens, "(")?; | ||
266 | let t = tokens.next().ok_or_else(|| { | ||
267 | SsrError::new("Unexpected end of constraint while looking for kind") | ||
268 | })?; | ||
269 | if t.kind != SyntaxKind::IDENT { | ||
270 | bail!("Expected ident, found {:?} while parsing kind constraint", t.kind); | ||
271 | } | ||
272 | expect_token(tokens, ")")?; | ||
273 | Ok(Constraint::Kind(NodeKind::from(&t.text)?)) | ||
274 | } | ||
275 | "not" => { | ||
276 | expect_token(tokens, "(")?; | ||
277 | let sub = parse_constraint(tokens)?; | ||
278 | expect_token(tokens, ")")?; | ||
279 | Ok(Constraint::Not(Box::new(sub))) | ||
280 | } | ||
281 | x => bail!("Unsupported constraint type '{}'", x), | ||
282 | } | ||
283 | } | ||
284 | |||
285 | fn expect_token(tokens: &mut std::vec::IntoIter<Token>, expected: &str) -> Result<(), SsrError> { | ||
286 | if let Some(t) = tokens.next() { | ||
287 | if t.text == expected { | ||
288 | return Ok(()); | ||
289 | } | ||
290 | bail!("Expected {} found {}", expected, t.text); | ||
291 | } | ||
292 | bail!("Expected {} found end of stream"); | ||
293 | } | ||
294 | |||
295 | impl NodeKind { | ||
296 | fn from(name: &SmolStr) -> Result<NodeKind, SsrError> { | ||
297 | Ok(match name.as_str() { | ||
298 | "literal" => NodeKind::Literal, | ||
299 | _ => bail!("Unknown node kind '{}'", name), | ||
300 | }) | ||
301 | } | ||
220 | } | 302 | } |
221 | 303 | ||
222 | impl Placeholder { | 304 | impl Placeholder { |
223 | fn new(name: SmolStr) -> Self { | 305 | fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self { |
224 | Self { stand_in_name: format!("__placeholder_{}", name), ident: name } | 306 | Self { stand_in_name: format!("__placeholder_{}", name), constraints, ident: name } |
225 | } | 307 | } |
226 | } | 308 | } |
227 | 309 | ||
@@ -241,31 +323,31 @@ mod tests { | |||
241 | PatternElement::Token(Token { kind, text: SmolStr::new(text) }) | 323 | PatternElement::Token(Token { kind, text: SmolStr::new(text) }) |
242 | } | 324 | } |
243 | fn placeholder(name: &str) -> PatternElement { | 325 | fn placeholder(name: &str) -> PatternElement { |
244 | PatternElement::Placeholder(Placeholder::new(SmolStr::new(name))) | 326 | PatternElement::Placeholder(Placeholder::new(SmolStr::new(name), Vec::new())) |
245 | } | 327 | } |
246 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); | 328 | let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap(); |
247 | assert_eq!( | 329 | assert_eq!( |
248 | result.pattern.raw.tokens, | 330 | result.pattern.raw.tokens, |
249 | vec![ | 331 | vec![ |
250 | token(SyntaxKind::IDENT, "foo"), | 332 | token(SyntaxKind::IDENT, "foo"), |
251 | token(SyntaxKind::L_PAREN, "("), | 333 | token(T!['('], "("), |
252 | placeholder("a"), | 334 | placeholder("a"), |
253 | token(SyntaxKind::COMMA, ","), | 335 | token(T![,], ","), |
254 | token(SyntaxKind::WHITESPACE, " "), | 336 | token(SyntaxKind::WHITESPACE, " "), |
255 | placeholder("b"), | 337 | placeholder("b"), |
256 | token(SyntaxKind::R_PAREN, ")"), | 338 | token(T![')'], ")"), |
257 | ] | 339 | ] |
258 | ); | 340 | ); |
259 | assert_eq!( | 341 | assert_eq!( |
260 | result.template.tokens, | 342 | result.template.tokens, |
261 | vec![ | 343 | vec![ |
262 | token(SyntaxKind::IDENT, "bar"), | 344 | token(SyntaxKind::IDENT, "bar"), |
263 | token(SyntaxKind::L_PAREN, "("), | 345 | token(T!['('], "("), |
264 | placeholder("b"), | 346 | placeholder("b"), |
265 | token(SyntaxKind::COMMA, ","), | 347 | token(T![,], ","), |
266 | token(SyntaxKind::WHITESPACE, " "), | 348 | token(SyntaxKind::WHITESPACE, " "), |
267 | placeholder("a"), | 349 | placeholder("a"), |
268 | token(SyntaxKind::R_PAREN, ")"), | 350 | token(T![')'], ")"), |
269 | ] | 351 | ] |
270 | ); | 352 | ); |
271 | } | 353 | } |