Skip to content

Commit 7a5679e

Browse files
authored
v2: add syntax (#433)
Another crate to copy over before we can swap to the new implementation.
1 parent 5ce4ceb commit 7a5679e

25 files changed

+6334
-3
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,15 @@ dir-test = "0.4"
3232
drop_bomb = "0.1.5"
3333
camino = "1.1.9"
3434
pg_query = "6.1.0"
35+
rowan = "0.15.15"
36+
smol_str = "0.3.2"
3537

3638
# local
3739
squawk-parser = { version = "0.0.0", path = "./crates/parser" }
3840
squawk-linter = { version = "0.0.0", path = "./crates/linter" }
3941
squawk-github = { version = "0.0.0", path = "./crates/github" }
4042
squawk_lexer = { version = "0.0.0", path = "./crates/squawk_lexer" }
43+
squawk_parser = { version = "0.0.0", path = "./crates/squawk_parser" }
4144

4245
[workspace.lints.clippy]
4346
collapsible_else_if = "allow"
@@ -53,3 +56,6 @@ debug = 0
5356
[profile.dev.package]
5457
insta.opt-level = 3
5558
similar.opt-level = 3
59+
# These speed up local tests.
60+
rowan.opt-level = 3
61+
text-size.opt-level = 3

crates/squawk_parser/src/test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn sqltest(fixture: Fixture<&str>) {
7979

8080
let parent_dir = input_file.parent().and_then(|x| x.file_name()).unwrap();
8181

82-
let (parsed, has_errors) = parse_text(&content);
82+
let (parsed, has_errors) = parse_text(content);
8383

8484
with_settings!({
8585
omit_expression => true,
@@ -97,12 +97,12 @@ fn sqltest(fixture: Fixture<&str>) {
9797
);
9898
// skipping pg17 specific stuff since our parser isn't using the latest parser
9999
if !test_name.ends_with("pg17") {
100-
let pg_result = pg_query::parse(&content);
100+
let pg_result = pg_query::parse(content);
101101
if let Err(e) = &pg_result {
102102
assert!(
103103
&pg_result.is_ok(),
104104
"tests defined in the `ok` can't have Postgres parser errors. Found {}",
105-
e.to_string()
105+
e
106106
);
107107
}
108108
}

crates/squawk_syntax/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "squawk_syntax"
3+
version = "0.0.0"
4+
edition.workspace = true
5+
rust-version.workspace = true
6+
authors.workspace = true
7+
license.workspace = true
8+
9+
[dependencies]
10+
squawk_parser.workspace = true
11+
rowan.workspace = true
12+
smol_str.workspace = true
13+
14+
[dev-dependencies]
15+
insta.workspace = true
16+
dir-test.workspace = true
17+
camino.workspace = true
18+
19+
[lints]
20+
workspace = true

crates/squawk_syntax/src/ast.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast.rs
2+
//
3+
// Permission is hereby granted, free of charge, to any
4+
// person obtaining a copy of this software and associated
5+
// documentation files (the "Software"), to deal in the
6+
// Software without restriction, including without
7+
// limitation the rights to use, copy, modify, merge,
8+
// publish, distribute, sublicense, and/or sell copies of
9+
// the Software, and to permit persons to whom the Software
10+
// is furnished to do so, subject to the following
11+
// conditions:
12+
//
13+
// The above copyright notice and this permission notice
14+
// shall be included in all copies or substantial portions
15+
// of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18+
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19+
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21+
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22+
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24+
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25+
// DEALINGS IN THE SOFTWARE.
26+
27+
mod nodes;
28+
mod support;
29+
mod traits;
30+
31+
mod node_ext;
32+
33+
use std::marker::PhantomData;
34+
35+
use crate::syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken};
36+
use squawk_parser::SyntaxKind;
37+
38+
pub use self::{
39+
nodes::*,
40+
// generated::{nodes::*, tokens::*},
41+
// node_ext::{
42+
// AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
43+
// SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam,
44+
// VisibilityKind,
45+
// },
46+
// operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
47+
// token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
48+
traits::{
49+
// AttrDocCommentIter, DocCommentIter,
50+
HasArgList, // HasAttrs, HasDocComments, HasGenericArgs,
51+
HasIfExists,
52+
HasIfNotExists, // HasTypeBounds,
53+
// HasVisibility,
54+
// HasGenericParams, HasLoopBody,
55+
HasModuleItem,
56+
HasName,
57+
},
58+
};
59+
60+
/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
61+
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
62+
/// the same representation: a pointer to the tree root and a pointer to the
63+
/// node itself.
64+
pub trait AstNode {
65+
fn can_cast(kind: SyntaxKind) -> bool
66+
where
67+
Self: Sized;
68+
69+
fn cast(syntax: SyntaxNode) -> Option<Self>
70+
where
71+
Self: Sized;
72+
73+
fn syntax(&self) -> &SyntaxNode;
74+
fn clone_for_update(&self) -> Self
75+
where
76+
Self: Sized,
77+
{
78+
Self::cast(self.syntax().clone_for_update()).unwrap()
79+
}
80+
fn clone_subtree(&self) -> Self
81+
where
82+
Self: Sized,
83+
{
84+
Self::cast(self.syntax().clone_subtree()).unwrap()
85+
}
86+
}
87+
88+
/// Like `AstNode`, but wraps tokens rather than interior nodes.
89+
pub trait AstToken {
90+
fn can_cast(token: SyntaxKind) -> bool
91+
where
92+
Self: Sized;
93+
94+
fn cast(syntax: SyntaxToken) -> Option<Self>
95+
where
96+
Self: Sized;
97+
98+
fn syntax(&self) -> &SyntaxToken;
99+
100+
fn text(&self) -> &str {
101+
self.syntax().text()
102+
}
103+
}
104+
105+
/// An iterator over `SyntaxNode` children of a particular AST type.
106+
#[derive(Debug, Clone)]
107+
pub struct AstChildren<N> {
108+
inner: SyntaxNodeChildren,
109+
ph: PhantomData<N>,
110+
}
111+
112+
impl<N> AstChildren<N> {
113+
fn new(parent: &SyntaxNode) -> Self {
114+
AstChildren {
115+
inner: parent.children(),
116+
ph: PhantomData,
117+
}
118+
}
119+
}
120+
121+
impl<N: AstNode> Iterator for AstChildren<N> {
122+
type Item = N;
123+
fn next(&mut self) -> Option<N> {
124+
self.inner.find_map(N::cast)
125+
}
126+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast/node_ext.rs
2+
//
3+
// Permission is hereby granted, free of charge, to any
4+
// person obtaining a copy of this software and associated
5+
// documentation files (the "Software"), to deal in the
6+
// Software without restriction, including without
7+
// limitation the rights to use, copy, modify, merge,
8+
// publish, distribute, sublicense, and/or sell copies of
9+
// the Software, and to permit persons to whom the Software
10+
// is furnished to do so, subject to the following
11+
// conditions:
12+
//
13+
// The above copyright notice and this permission notice
14+
// shall be included in all copies or substantial portions
15+
// of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18+
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19+
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20+
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21+
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22+
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24+
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25+
// DEALINGS IN THE SOFTWARE.
26+
27+
use std::borrow::Cow;
28+
29+
use rowan::{GreenNodeData, GreenTokenData, NodeOrToken};
30+
31+
use crate::{SyntaxNode, TokenText};
32+
33+
// impl ast::Name {
34+
// pub fn text(&self) -> TokenText<'_> {
35+
// text_of_first_token(self.syntax())
36+
// }
37+
// }
38+
39+
pub(crate) fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
40+
fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
41+
green_ref
42+
.children()
43+
.next()
44+
.and_then(NodeOrToken::into_token)
45+
.unwrap()
46+
}
47+
48+
match node.green() {
49+
Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
50+
Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
51+
}
52+
}

0 commit comments

Comments
 (0)