aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/parsing.rs
diff options
context:
space:
mode:
authorDavid Lattimore <[email protected]>2020-06-23 10:07:42 +0100
committerDavid Lattimore <[email protected]>2020-07-01 09:44:11 +0100
commit3d9997889bfe536a96e70535ab208a6e7ff3bc12 (patch)
tree6f280e6af08979d7a5c17d45a15b94a54798dba3 /crates/ra_ssr/src/parsing.rs
parentd34fd372bbcce4600fe7dd1ca61b9b213a7f5ced (diff)
SSR: Add initial support for placeholder constraints
Diffstat (limited to 'crates/ra_ssr/src/parsing.rs')
-rw-r--r--crates/ra_ssr/src/parsing.rs92
1 files changed, 87 insertions, 5 deletions
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs
index 04d46bd32..0f4f88b7c 100644
--- a/crates/ra_ssr/src/parsing.rs
+++ b/crates/ra_ssr/src/parsing.rs
@@ -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)]
46pub(crate) enum Constraint {
47 Kind(NodeKind),
48 Not(Box<Constraint>),
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub(crate) enum NodeKind {
53 Literal,
42} 54}
43 55
44#[derive(Debug, Clone, PartialEq, Eq)] 56#[derive(Debug, Clone, PartialEq, Eq)]
@@ -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
206fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> { 221fn 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 SyntaxKind::L_CURLY => {
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 SyntaxKind::COLON => {
241 constraints.push(parse_constraint(tokens)?);
242 }
243 SyntaxKind::R_CURLY => 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
257fn 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
285fn 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
295impl 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
222impl Placeholder { 304impl 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,7 +323,7 @@ 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!(