From 5645c153e0379874d1f44ab149c3ec9257812692 Mon Sep 17 00:00:00 2001 From: Geoffry Song Date: Fri, 15 Nov 2019 12:05:29 -0800 Subject: Attempt to implement typed accessors --- crates/ra_syntax/src/ast/expr_extensions.rs | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 25dbd0bed..7c53aa934 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs @@ -189,6 +189,52 @@ impl ast::BinExpr { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum RangeOp { + /// `..` + Exclusive, + /// `..=` + Inclusive, +} + +impl ast::RangeExpr { + fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> { + self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| { + let token = child.into_token()?; + let bin_op = match token.kind() { + T![..] => RangeOp::Exclusive, + T![..=] => RangeOp::Inclusive, + _ => return None, + }; + Some((ix, token, bin_op)) + }) + } + + pub fn op_kind(&self) -> Option { + self.op_details().map(|t| t.2) + } + + pub fn op_token(&self) -> Option { + self.op_details().map(|t| t.1) + } + + pub fn start(&self) -> Option { + let op_ix = self.op_details()?.0; + self.syntax() + .children_with_tokens() + .take(op_ix) + .find_map(|it| ast::Expr::cast(it.into_node()?)) + } + + pub fn end(&self) -> Option { + let op_ix = self.op_details()?.0; + self.syntax() + .children_with_tokens() + .skip(op_ix + 1) + .find_map(|it| ast::Expr::cast(it.into_node()?)) + } +} + impl ast::IndexExpr { pub fn base(&self) -> Option { children(self).nth(0) -- cgit v1.2.3 From 5b54a93fe71137606674ff11dc57bc6e7eaa37f9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 16 Nov 2019 22:50:41 +0300 Subject: Add ast for plain and raw string literals --- crates/ra_syntax/src/ast/tokens.rs | 95 ++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 10 deletions(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 87cca325d..ed8661faf 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -2,8 +2,8 @@ use crate::{ ast::AstToken, - SyntaxKind::{COMMENT, WHITESPACE}, - SyntaxToken, + SyntaxKind::{COMMENT, RAW_STRING, STRING, WHITESPACE}, + SyntaxToken, TextRange, TextUnit, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -11,10 +11,9 @@ pub struct Comment(SyntaxToken); impl AstToken for Comment { fn cast(token: SyntaxToken) -> Option { - if token.kind() == COMMENT { - Some(Comment(token)) - } else { - None + match token.kind() { + COMMENT => Some(Comment(token)), + _ => None, } } fn syntax(&self) -> &SyntaxToken { @@ -94,10 +93,9 @@ pub struct Whitespace(SyntaxToken); impl AstToken for Whitespace { fn cast(token: SyntaxToken) -> Option { - if token.kind() == WHITESPACE { - Some(Whitespace(token)) - } else { - None + match token.kind() { + WHITESPACE => Some(Whitespace(token)), + _ => None, } } fn syntax(&self) -> &SyntaxToken { @@ -111,3 +109,80 @@ impl Whitespace { text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) } } + +pub struct String(SyntaxToken); + +impl AstToken for String { + fn cast(token: SyntaxToken) -> Option { + match token.kind() { + STRING => Some(String(token)), + _ => None, + } + } + fn syntax(&self) -> &SyntaxToken { + &self.0 + } +} + +impl String { + pub fn value(&self) -> Option { + let text = self.text().as_str(); + let usual_string_range = find_usual_string_range(text)?; + let start_of_inside = usual_string_range.start().to_usize() + 1; + let end_of_inside = usual_string_range.end().to_usize(); + let inside_str = &text[start_of_inside..end_of_inside]; + + let mut buf = std::string::String::with_capacity(inside_str.len()); + let mut has_error = false; + rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| { + match unescaped_char { + Ok(c) => buf.push(c), + Err(_) => has_error = true, + } + }); + + if has_error { + return None; + } + Some(buf) + } +} + +pub struct RawString(SyntaxToken); + +impl AstToken for RawString { + fn cast(token: SyntaxToken) -> Option { + match token.kind() { + RAW_STRING => Some(RawString(token)), + _ => None, + } + } + fn syntax(&self) -> &SyntaxToken { + &self.0 + } +} + +impl RawString { + pub fn value(&self) -> Option { + let text = self.text().as_str(); + let usual_string_range = find_usual_string_range(text)?; + let start_of_inside = usual_string_range.start().to_usize() + 1; + let end_of_inside = usual_string_range.end().to_usize(); + let inside_str = &text[start_of_inside..end_of_inside]; + Some(inside_str.to_string()) + } +} + +fn find_usual_string_range(s: &str) -> Option { + let left_quote = s.find('"')?; + let right_quote = s.rfind('"')?; + if left_quote == right_quote { + // `s` only contains one quote + None + } else { + Some(TextRange::from_to( + TextUnit::from(left_quote as u32), + TextUnit::from(right_quote as u32), + )) + } +} -- cgit v1.2.3