wgsl_parser/utils/
mod.rs

1//! Miscellaneous helpers for working with WGSL syntax trees.
2
3use crate::{decl::Decl, expr::Expr, stmt::Stmt};
4
5mod parents;
6
7use bitflags::bitflags;
8use gramatika::{ArcStr, Spanned, Substr};
9pub use parents::{find_parent, find_parents};
10
11#[cfg(feature = "lsp")]
12use lsp_types::{Position, Range};
13
14#[derive(DebugLisp, Clone)]
15pub enum SyntaxNode {
16	Decl(Decl),
17	Stmt(Stmt),
18	Expr(Expr),
19}
20
21bitflags! {
22	#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23	pub struct SyntaxKind: u8 {
24		const DECL = 0b001;
25		const STMT = 0b010;
26		const EXPR = 0b100;
27	}
28}
29
30pub trait GetSyntaxKind {
31	const KIND: SyntaxKind;
32}
33impl GetSyntaxKind for Decl {
34	const KIND: SyntaxKind = SyntaxKind::DECL;
35}
36impl GetSyntaxKind for Stmt {
37	const KIND: SyntaxKind = SyntaxKind::STMT;
38}
39impl GetSyntaxKind for Expr {
40	const KIND: SyntaxKind = SyntaxKind::EXPR;
41}
42impl GetSyntaxKind for SyntaxNode {
43	const KIND: SyntaxKind = SyntaxKind::all();
44}
45
46impl Spanned for SyntaxNode {
47	fn span(&self) -> gramatika::Span {
48		match self {
49			SyntaxNode::Decl(decl) => decl.span(),
50			SyntaxNode::Stmt(stmt) => stmt.span(),
51			SyntaxNode::Expr(expr) => expr.span(),
52		}
53	}
54}
55
56impl From<SyntaxNode> for Decl {
57	fn from(value: SyntaxNode) -> Self {
58		match value {
59			SyntaxNode::Decl(inner) => inner,
60			_ => panic!("Expected a `SyntaxNode::Decl(...)`"),
61		}
62	}
63}
64
65impl From<SyntaxNode> for Stmt {
66	fn from(value: SyntaxNode) -> Self {
67		match value {
68			SyntaxNode::Stmt(inner) => inner,
69			_ => panic!("Expected a `SyntaxNode::Stmt(...)`"),
70		}
71	}
72}
73
74impl From<SyntaxNode> for Expr {
75	fn from(value: SyntaxNode) -> Self {
76		match value {
77			SyntaxNode::Expr(inner) => inner,
78			_ => panic!("Expected a `SyntaxNode::Expr(...)`"),
79		}
80	}
81}
82
83#[cfg(feature = "lsp")]
84pub trait ToRange {
85	fn to_range(self) -> Range;
86}
87
88#[cfg(feature = "lsp")]
89impl ToRange for gramatika::Span {
90	fn to_range(self) -> Range {
91		Range {
92			start: Position {
93				line: self.start.line as _,
94				character: self.start.character as _,
95			},
96			end: Position {
97				line: self.end.line as _,
98				character: self.end.character as _,
99			},
100		}
101	}
102}
103
104/// # Panics
105///
106/// Panics if either of the following are true:
107///
108/// - `start` and `end` point to different `ArcStr` allocations
109/// - `end.range().end` is less than or equal to `start.range().start`
110pub(crate) fn join_substrs(start: &Substr, end: &Substr) -> Substr {
111	assert!(ArcStr::ptr_eq(start.parent(), end.parent()));
112	assert!(end.range().end > start.range().start);
113
114	let source = start.parent().clone();
115	let range = start.range().start..end.range().end;
116
117	unsafe { Substr::from_parts_unchecked(source, range) }
118}
119
120#[cfg(test)]
121pub(crate) trait WithComments {
122	type Output: Sized + std::fmt::Debug;
123
124	fn get(&self) -> &[Self::Output];
125}
126
127#[cfg(test)]
128pub(crate) trait WithTokens {
129	type Output: Sized + std::fmt::Debug;
130
131	fn into(self) -> Vec<Self::Output>;
132}
133
134#[cfg(test)]
135pub(crate) trait WithErrors {
136	type Output: Sized + std::fmt::Display;
137
138	fn get(&self) -> &[Self::Output];
139}