wgsl_parser/
modules.rs

1use std::sync::Arc;
2
3use gramatika::{Spanned, Substr, Token as _};
4
5use crate::{
6	decl::{
7		Decl, ImportDecl, ImportPath, ImportPathBlock, ImportPathDecl, ImportPathLeaf,
8		NamespacedImportPath,
9	},
10	token::Token,
11	traversal::{FlowControl, Visitor, Walk},
12	utils, SyntaxTree,
13};
14
15#[derive(Clone, DebugLisp, PartialEq, Eq, Hash)]
16pub struct ImportedSymbol {
17	pub qualified_path: Arc<[Token]>,
18	pub local_binding: Token,
19}
20
21impl SyntaxTree {
22	pub fn find_exported_symbol<'a>(&'a self, needle: &Token) -> Option<&'a Decl> {
23		self.inner.iter().find(|decl| {
24			decl.name().lexeme() == needle.lexeme()
25				&& (needle.kind() == decl.name().kind() || !matches!(decl, Decl::ImportPath(_)))
26		})
27	}
28
29	pub fn import_path_decl(&self) -> Option<&Decl> {
30		self.inner
31			.iter()
32			.find(|decl| matches!(decl, Decl::ImportPath(_)))
33	}
34}
35
36impl ImportPathDecl {
37	/// Returns the fully-qualified path defined by this declaration as an array
38	/// of tokens.
39	///
40	/// I.e., for the following import path declaration:
41	/// ```wgsl
42	/// #define_import_path foo::bar::baz
43	/// ```
44	/// This function will return the tokens `[foo, bar, baz]`.
45	pub fn qualified_path(&self) -> Arc<[Token]> {
46		let mut result = vec![];
47		let mut import_path = &self.path;
48		loop {
49			match import_path {
50				ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
51					result.push(namespace.clone());
52					import_path = path.as_ref();
53				}
54				ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
55					result.push(name.clone());
56					break result.into();
57				}
58				ImportPath::Block(_) => unreachable!(),
59			}
60		}
61	}
62
63	/// Returns an ad-hoc `Token::Path` that represents a combination of the
64	/// tokens that make up the path defined by this declaration.
65	///
66	/// I.e., for the following import path declaration:
67	/// ```wgsl
68	/// #define_import_path foo::bar::baz
69	/// ```
70	/// This function will return a `Token::Path("foo::bar::baz", (1:21..1:34))`.
71	///
72	/// The token returned by this function is _created by this function_ by
73	/// "joining" the tokens actually contained in the [`ImportPath`] into a
74	/// single token. This is in contrast to [`qualified_path`](Self::qualified_path),
75	/// which returns the actual sequence of tokens contained in the [`ImportPath`].
76	pub fn qualified_name(&self) -> Token {
77		let mut result = Substr::new();
78		let mut import_path = &self.path;
79		loop {
80			match import_path {
81				ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
82					if result.is_empty() {
83						(result, _) = namespace.as_inner();
84					} else {
85						let (next, _) = namespace.as_inner();
86						result = utils::join_substrs(&result, &next);
87					}
88
89					import_path = path.as_ref();
90				}
91				ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
92					if result.is_empty() {
93						(result, _) = name.as_inner();
94					} else {
95						let (next, _) = name.as_inner();
96						result = utils::join_substrs(&result, &next);
97					}
98
99					break;
100				}
101				ImportPath::Block(_) => unreachable!(),
102			}
103		}
104
105		Token::Path(result, self.path.span())
106	}
107}
108
109impl ImportDecl {
110	pub fn build_imported_symbols(&self) -> Vec<ImportedSymbol> {
111		let mut builder = ImportedSymbolsBuilder::default();
112		self.walk(&mut builder);
113
114		let mut result = builder.build();
115		result.sort_unstable_by_key(|sym| sym.local_binding.span());
116
117		result
118	}
119}
120
121#[derive(Default)]
122struct ImportedSymbolsBuilder {
123	path: Vec<Token>,
124	binding: Option<Token>,
125	forks: Vec<ImportedSymbolsBuilder>,
126}
127
128impl ImportedSymbolsBuilder {
129	fn build(self) -> Vec<ImportedSymbol> {
130		std::iter::once(ImportedSymbol {
131			qualified_path: self.path.into(),
132			local_binding: self.binding.unwrap(),
133		})
134		.chain(self.forks.into_iter().flat_map(|builder| builder.build()))
135		.collect()
136	}
137
138	fn fork(&self) -> Self {
139		Self {
140			path: self.path.clone(),
141			binding: None,
142			forks: vec![],
143		}
144	}
145}
146
147impl Visitor for ImportedSymbolsBuilder {
148	fn visit_import_path(&mut self, path: &ImportPath) -> FlowControl {
149		match path {
150			ImportPath::Leaf(ImportPathLeaf { name, as_binding }) => {
151				self.path.push(name.clone());
152				self.binding = Some(as_binding.as_ref().cloned().unwrap_or(name.clone()));
153
154				FlowControl::Break
155			}
156			ImportPath::Namespaced(NamespacedImportPath { namespace, .. }) => {
157				self.path.push(namespace.clone());
158
159				FlowControl::Continue
160			}
161			ImportPath::Block(ImportPathBlock { paths, .. }) => {
162				for (idx, path) in paths.iter().enumerate().rev() {
163					if idx == 0 {
164						path.walk(self);
165					} else {
166						let mut fork = self.fork();
167						path.walk(&mut fork);
168						self.forks.push(fork);
169					}
170				}
171
172				FlowControl::Break
173			}
174		}
175	}
176}