wgsl-parser 0.5.0

A zero-copy recursive-descent parser for WebGPU shading language
Documentation
use std::sync::Arc;

use gramatika::{Parse, ParseStreamer, Span, Spanned};

use crate::{
	common::{AttributeList, TypeDecl},
	parser::ErrorRecoveringParseStream,
	token::{brace, keyword, operator, punct, Token, TokenKind},
	ParseStream,
};

use super::Decl;

#[derive(Clone, DebugLisp)]
pub struct StructDecl {
	pub attributes: Option<AttributeList>,
	pub storage: Token,
	pub storage_modifier: Option<Token>,
	pub name: Token,
	pub body: StructBody,
}

#[derive(Clone, DebugLisp)]
pub struct StructBody {
	pub open_brace: Token,
	pub fields: Arc<[Decl]>,
	pub close_brace: Token,
}

#[derive(Clone, DebugLisp)]
pub struct FieldDecl {
	pub attributes: Option<AttributeList>,
	pub name: Token,
	pub ty: TypeDecl,
	pub separator: Option<Token>,
}

impl Parse for StructDecl {
	type Stream = ParseStream;

	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
		let storage = input.consume(keyword![struct])?;

		let storage_modifier = if input.check(operator![<]) {
			input.consume(operator![<])?;
			let modifier = input.consume_kind(TokenKind::Keyword)?;
			input.consume(operator![>])?;

			Some(modifier)
		} else {
			None
		};

		let name = input.consume_as(TokenKind::Ident, Token::struct_)?;
		let body = input.parse()?;

		Ok(Self {
			attributes: None,
			storage,
			storage_modifier,
			name,
			body,
		})
	}
}

impl Spanned for StructDecl {
	fn span(&self) -> Span {
		self.storage.span().through(self.body.span())
	}
}

impl Parse for StructBody {
	type Stream = ParseStream;

	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
		let open_brace = input.consume(brace!["{"])?;
		let fields = input.parse_seq_with_finisher::<FieldDecl, _>(
			|input| !input.check(brace!["}"]),
			|_, field| Ok(Decl::Field(field)),
		)?;
		let close_brace = input.consume(brace!["}"])?;

		Ok(Self {
			open_brace,
			fields: fields.into(),
			close_brace,
		})
	}
}

impl Spanned for StructBody {
	fn span(&self) -> Span {
		self.open_brace.span().through(self.close_brace.span())
	}
}

impl Parse for FieldDecl {
	type Stream = ParseStream;

	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
		let attributes = if input.check(punct!["@"]) {
			Some(input.parse::<AttributeList>()?)
		} else {
			None
		};
		let name = input.consume_as(TokenKind::Ident, Token::field)?;
		let ty = input.parse()?;

		let separator = if !input.check(brace!("}")) {
			Some(input.consume(punct![,])?)
		} else {
			None
		};

		Ok(Self {
			attributes,
			name,
			ty,
			separator,
		})
	}
}

impl Spanned for FieldDecl {
	fn span(&self) -> Span {
		let span_start = self
			.attributes
			.as_ref()
			.and_then(|attrs| attrs.attributes.first().map(|attr| &attr.at_sign))
			.unwrap_or(&self.name)
			.span();

		let span_end = match &self.separator {
			Some(sep) => sep.span(),
			None => self.ty.span(),
		};

		span_start.through(span_end)
	}
}