wgsl_parser/decl/
mod.rs

1//! A module for the syntax nodes representing top-level WGSL declarations.
2
3use gramatika::{
4	arcstr::literal_substr, Parse, ParseStreamer, Span, Spanned, SpannedError, Token as _,
5};
6use lazy_static::lazy_static;
7
8mod alias;
9mod func;
10pub mod import;
11pub mod legacy;
12mod struc;
13mod var;
14
15use crate::{common::AttributeList, ParseStream, Token, TokenKind};
16
17pub use self::{
18	alias::TypeAliasDecl,
19	func::{FunctionDecl, ParamDecl},
20	import::{
21		ImportDecl, ImportPath, ImportPathBlock, ImportPathDecl, ImportPathLeaf,
22		NamespacedImportPath,
23	},
24	struc::{FieldDecl, StructBody, StructDecl},
25	var::VarDecl,
26};
27
28#[derive(Clone, DebugLisp)]
29pub enum Decl {
30	Var(VarDecl),
31	Const(VarDecl),
32	TypeAlias(TypeAliasDecl),
33	Struct(StructDecl),
34	Field(FieldDecl),
35	Function(FunctionDecl),
36	Param(ParamDecl),
37	Extension(legacy::ExtensionDecl), // TODO
38	ImportPath(ImportPathDecl),
39	Import(ImportDecl),
40	Module(legacy::ModuleDecl), // TODO
41}
42
43lazy_static! {
44	// FIXME: This is gross -- is there a better way to handle this?
45	static ref ERR_TOKEN: Token = Token::Ident(literal_substr!("ERROR"), Span::default());
46}
47
48impl Decl {
49	pub fn name(&self) -> &Token {
50		match self {
51			Decl::Var(decl) | Decl::Const(decl) => &decl.name,
52			Decl::TypeAlias(decl) => &decl.name,
53			Decl::Struct(decl) => &decl.name,
54			Decl::Field(decl) => &decl.name,
55			Decl::Function(decl) => &decl.name,
56			Decl::Param(decl) => &decl.name,
57			Decl::Extension(decl) => &decl.name,
58			Decl::ImportPath(decl) => decl.name(),
59			Decl::Import(_) => &ERR_TOKEN,
60			Decl::Module(decl) => &decl.name,
61		}
62	}
63
64	pub fn attributes(&self) -> Option<&AttributeList> {
65		match self {
66			Decl::Var(decl) => decl.attributes.as_ref(),
67			Decl::Const(decl) => decl.attributes.as_ref(),
68			Decl::TypeAlias(_) => None,
69			Decl::Struct(decl) => decl.attributes.as_ref(),
70			Decl::Field(decl) => decl.attributes.as_ref(),
71			Decl::Function(decl) => decl.attributes.as_ref(),
72			Decl::Param(decl) => decl.attributes.as_ref(),
73			Decl::Extension(_) => None,
74			Decl::ImportPath(_) => None,
75			Decl::Import(_) => None,
76			Decl::Module(_) => None,
77		}
78	}
79}
80
81impl Parse for Decl {
82	type Stream = ParseStream;
83
84	fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
85		use TokenKind::*;
86
87		match input.peek() {
88			Some(token) => match token.as_matchable() {
89				(Punct, "@", _) => {
90					let attributes = input.parse::<AttributeList>()?;
91
92					match input.parse::<Decl>()? {
93						Decl::Var(mut inner) => {
94							inner.attributes = Some(attributes);
95							Ok(Decl::Var(inner))
96						}
97						Decl::Const(mut inner) => {
98							inner.attributes = Some(attributes);
99							Ok(Decl::Const(inner))
100						}
101						Decl::Struct(mut inner) => {
102							inner.attributes = Some(attributes);
103							Ok(Decl::Struct(inner))
104						}
105						Decl::Function(mut inner) => {
106							inner.attributes = Some(attributes);
107							Ok(Decl::Function(inner))
108						}
109						_ => Err(SpannedError {
110							message: "Attributes are not valid in this position".into(),
111							span: Some(attributes.span()),
112							source: input.source(),
113						}),
114					}
115				}
116				(Keyword, "var", _) => Ok(Decl::Var(input.parse()?)),
117				(Keyword, "let" | "const" | "override", _) => Ok(Decl::Const(input.parse()?)),
118				(Keyword, "alias", _) => Ok(Decl::TypeAlias(input.parse()?)),
119				(Keyword, "struct", _) => Ok(Decl::Struct(input.parse()?)),
120				(Keyword, "fn", _) => Ok(Decl::Function(input.parse()?)),
121				(Keyword, "enable", _) => Ok(Decl::Extension(input.parse()?)),
122				(Directive, "#define_import_path", _) => Ok(Decl::ImportPath(input.parse()?)),
123				(Directive, "#import", _) => Ok(Decl::Import(input.parse()?)),
124				(Keyword, "import", _) => Ok(Decl::Module(input.parse()?)),
125				(_, _, span) => Err(SpannedError {
126					message: "Expected `var`, `let`, `const`, `override`, `alias`, \
127						`struct`, `fn`, `enable`, or `import`"
128						.into(),
129					span: Some(span),
130					source: input.source(),
131				}),
132			},
133			None => Err(SpannedError {
134				message: "Unexpected end of input".into(),
135				source: input.source(),
136				span: input.prev().map(|token| token.span()),
137			}),
138		}
139	}
140}
141
142impl Spanned for Decl {
143	fn span(&self) -> Span {
144		use Decl::*;
145
146		match self {
147			Var(inner) => inner.span(),
148			Const(inner) => inner.span(),
149			TypeAlias(inner) => inner.span(),
150			Struct(inner) => inner.span(),
151			Field(inner) => inner.span(),
152			Function(inner) => inner.span(),
153			Param(inner) => inner.span(),
154			Extension(inner) => inner.span(),
155			ImportPath(inner) => inner.span(),
156			Import(inner) => inner.span(),
157			Module(inner) => inner.span(),
158		}
159	}
160}