wgsl-parser 0.5.0

A zero-copy recursive-descent parser for WebGPU shading language
Documentation
//! Miscellaneous helpers for working with WGSL syntax trees.

use crate::{decl::Decl, expr::Expr, stmt::Stmt};

mod parents;

use bitflags::bitflags;
use gramatika::{ArcStr, Spanned, Substr};
pub use parents::{find_parent, find_parents};

#[cfg(feature = "lsp")]
use lsp_types::{Position, Range};

#[derive(DebugLisp, Clone)]
pub enum SyntaxNode {
	Decl(Decl),
	Stmt(Stmt),
	Expr(Expr),
}

bitflags! {
	#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
	pub struct SyntaxKind: u8 {
		const DECL = 0b001;
		const STMT = 0b010;
		const EXPR = 0b100;
	}
}

pub trait GetSyntaxKind {
	const KIND: SyntaxKind;
}
impl GetSyntaxKind for Decl {
	const KIND: SyntaxKind = SyntaxKind::DECL;
}
impl GetSyntaxKind for Stmt {
	const KIND: SyntaxKind = SyntaxKind::STMT;
}
impl GetSyntaxKind for Expr {
	const KIND: SyntaxKind = SyntaxKind::EXPR;
}
impl GetSyntaxKind for SyntaxNode {
	const KIND: SyntaxKind = SyntaxKind::all();
}

impl Spanned for SyntaxNode {
	fn span(&self) -> gramatika::Span {
		match self {
			SyntaxNode::Decl(decl) => decl.span(),
			SyntaxNode::Stmt(stmt) => stmt.span(),
			SyntaxNode::Expr(expr) => expr.span(),
		}
	}
}

impl From<SyntaxNode> for Decl {
	fn from(value: SyntaxNode) -> Self {
		match value {
			SyntaxNode::Decl(inner) => inner,
			_ => panic!("Expected a `SyntaxNode::Decl(...)`"),
		}
	}
}

impl From<SyntaxNode> for Stmt {
	fn from(value: SyntaxNode) -> Self {
		match value {
			SyntaxNode::Stmt(inner) => inner,
			_ => panic!("Expected a `SyntaxNode::Stmt(...)`"),
		}
	}
}

impl From<SyntaxNode> for Expr {
	fn from(value: SyntaxNode) -> Self {
		match value {
			SyntaxNode::Expr(inner) => inner,
			_ => panic!("Expected a `SyntaxNode::Expr(...)`"),
		}
	}
}

#[cfg(feature = "lsp")]
pub trait ToRange {
	fn to_range(self) -> Range;
}

#[cfg(feature = "lsp")]
impl ToRange for gramatika::Span {
	fn to_range(self) -> Range {
		Range {
			start: Position {
				line: self.start.line as _,
				character: self.start.character as _,
			},
			end: Position {
				line: self.end.line as _,
				character: self.end.character as _,
			},
		}
	}
}

/// # Panics
///
/// Panics if either of the following are true:
///
/// - `start` and `end` point to different `ArcStr` allocations
/// - `end.range().end` is less than or equal to `start.range().start`
pub(crate) fn join_substrs(start: &Substr, end: &Substr) -> Substr {
	assert!(ArcStr::ptr_eq(start.parent(), end.parent()));
	assert!(end.range().end > start.range().start);

	let source = start.parent().clone();
	let range = start.range().start..end.range().end;

	unsafe { Substr::from_parts_unchecked(source, range) }
}

#[cfg(test)]
pub(crate) trait WithComments {
	type Output: Sized + std::fmt::Debug;

	fn get(&self) -> &[Self::Output];
}

#[cfg(test)]
pub(crate) trait WithTokens {
	type Output: Sized + std::fmt::Debug;

	fn into(self) -> Vec<Self::Output>;
}

#[cfg(test)]
pub(crate) trait WithErrors {
	type Output: Sized + std::fmt::Display;

	fn get(&self) -> &[Self::Output];
}