From 68196ccc10c60de52bb771d295879456f73ede95 Mon Sep 17 00:00:00 2001 From: Luca Barbieri Date: Fri, 3 Apr 2020 21:12:08 +0200 Subject: Add AstElement trait, generate tokens, support tokens in enums - Adds a new AstElement trait that is implemented by all generated node, token and enum structs - Overhauls the code generators to code-generate all tokens, and also enhances enums to support including tokens, node, and nested enums --- crates/ra_syntax/src/ast.rs | 114 +++++++++++++++++++++++++++++++++++-- crates/ra_syntax/src/ast/tokens.rs | 62 +------------------- 2 files changed, 111 insertions(+), 65 deletions(-) (limited to 'crates/ra_syntax') diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 26fafb469..1ac0201b8 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -11,7 +11,10 @@ pub mod make; use std::marker::PhantomData; use crate::{ - syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken}, + syntax_node::{ + NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, + SyntaxToken, + }, SmolStr, SyntaxKind, }; @@ -30,16 +33,24 @@ pub use self::{ /// conversion itself has zero runtime cost: ast and syntax nodes have exactly /// the same representation: a pointer to the tree root and a pointer to the /// node itself. -pub trait AstNode: std::fmt::Display { +pub trait AstNode: AstElement { fn can_cast(kind: SyntaxKind) -> bool where Self: Sized; - fn cast(syntax: SyntaxNode) -> Option + fn cast_or_return(syntax: SyntaxNode) -> Result where Self: Sized; + fn cast(syntax: SyntaxNode) -> Option + where + Self: Sized, + { + ::cast_or_return(syntax).ok() + } + fn syntax(&self) -> &SyntaxNode; + fn into_syntax(self) -> SyntaxNode; } #[test] @@ -48,16 +59,51 @@ fn assert_ast_is_object_safe() { } /// Like `AstNode`, but wraps tokens rather than interior nodes. -pub trait AstToken { - fn cast(token: SyntaxToken) -> Option +pub trait AstToken: AstElement { + fn can_cast(token: SyntaxKind) -> bool where Self: Sized; + + fn cast_or_return(syntax: SyntaxToken) -> Result + where + Self: Sized; + + fn cast(syntax: SyntaxToken) -> Option + where + Self: Sized, + { + ::cast_or_return(syntax).ok() + } + fn syntax(&self) -> &SyntaxToken; + fn into_syntax(self) -> SyntaxToken; + fn text(&self) -> &SmolStr { self.syntax().text() } } +/// Like `AstNode`, but wraps either nodes or tokens rather than interior nodes. +pub trait AstElement: std::fmt::Display { + fn can_cast_element(kind: SyntaxKind) -> bool + where + Self: Sized; + + fn cast_or_return_element(syntax: SyntaxElement) -> Result + where + Self: Sized; + + fn cast_element(syntax: SyntaxElement) -> Option + where + Self: Sized, + { + ::cast_or_return_element(syntax).ok() + } + + fn syntax_element(&self) -> NodeOrToken<&SyntaxNode, &SyntaxToken>; + fn into_syntax_element(self) -> SyntaxElement; +} + /// An iterator over `SyntaxNode` children of a particular AST type. #[derive(Debug, Clone)] pub struct AstChildren { @@ -86,6 +132,64 @@ fn children(parent: &P) -> AstChildren { AstChildren::new(parent.syntax()) } +/// An iterator over `SyntaxToken` children of a particular AST type. +#[derive(Debug, Clone)] +pub struct AstChildTokens { + inner: SyntaxElementChildren, + ph: PhantomData, +} + +impl AstChildTokens { + fn new(parent: &SyntaxNode) -> Self { + AstChildTokens { inner: parent.children_with_tokens(), ph: PhantomData } + } +} + +impl Iterator for AstChildTokens { + type Item = N; + fn next(&mut self) -> Option { + self.inner.by_ref().filter_map(|x| x.into_token()).find_map(N::cast) + } +} + +fn child_token_opt(parent: &P) -> Option { + child_tokens(parent).next() +} + +fn child_tokens(parent: &P) -> AstChildTokens { + AstChildTokens::new(parent.syntax()) +} + +/// An iterator over `SyntaxNode` children of a particular AST type. +#[derive(Debug, Clone)] +pub struct AstChildElements { + inner: SyntaxElementChildren, + ph: PhantomData, +} + +impl AstChildElements { + fn new(parent: &SyntaxNode) -> Self { + AstChildElements { inner: parent.children_with_tokens(), ph: PhantomData } + } +} + +impl Iterator for AstChildElements { + type Item = N; + fn next(&mut self) -> Option { + self.inner.by_ref().find_map(N::cast_element) + } +} + +#[allow(dead_code)] +fn child_element_opt(parent: &P) -> Option { + child_elements(parent).next() +} + +#[allow(dead_code)] +fn child_elements(parent: &P) -> AstChildElements { + AstChildElements::new(parent.syntax()) +} + #[test] fn test_doc_comment_none() { let file = SourceFile::parse( diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 1a51b8d3b..e8320b57e 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -1,26 +1,10 @@ //! There are many AstNodes, but only a few tokens, so we hand-write them here. use crate::{ - ast::AstToken, - SyntaxKind::{COMMENT, RAW_STRING, STRING, WHITESPACE}, - SyntaxToken, TextRange, TextUnit, + ast::{AstToken, Comment, RawString, String, Whitespace}, + TextRange, TextUnit, }; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Comment(SyntaxToken); - -impl AstToken for Comment { - fn cast(token: SyntaxToken) -> Option { - match token.kind() { - COMMENT => Some(Comment(token)), - _ => None, - } - } - fn syntax(&self) -> &SyntaxToken { - &self.0 - } -} - impl Comment { pub fn kind(&self) -> CommentKind { kind_by_prefix(self.text()) @@ -89,20 +73,6 @@ fn prefix_by_kind(kind: CommentKind) -> &'static str { unreachable!() } -pub struct Whitespace(SyntaxToken); - -impl AstToken for Whitespace { - fn cast(token: SyntaxToken) -> Option { - match token.kind() { - WHITESPACE => Some(Whitespace(token)), - _ => None, - } - } - fn syntax(&self) -> &SyntaxToken { - &self.0 - } -} - impl Whitespace { pub fn spans_multiple_lines(&self) -> bool { let text = self.text(); @@ -168,20 +138,6 @@ pub trait HasStringValue: HasQuotes { fn value(&self) -> Option; } -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 HasStringValue for String { fn value(&self) -> Option { let text = self.text().as_str(); @@ -201,20 +157,6 @@ impl HasStringValue for String { } } -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 HasStringValue for RawString { fn value(&self) -> Option { let text = self.text().as_str(); -- cgit v1.2.3