wgsl_parser/scopes/
mod.rs

1//! This module is responsible for building a tree of scopes from a [`SyntaxTree`].
2//!
3//! Scopes are built according to the following rules:
4//!
5//!  * Most declarations spawn a new child scope which is valid through the end of the
6//!    scope which contains it. The starting point for declaration scopes depends on the
7//!    type of declaration:
8//!     - `var`, `let`, and `type` bindings become active after their semicolon
9//!     - `struct` bindings become active after their closing brace
10//!     - `fn` bindings become active after their opening brace
11//!  * Block statements (generally any chunk of code enclosed in curly braces, e.g.
12//!    following an `if` statement's condition expression) spawn a new child scope from
13//!    the end of the opening brace to the start of the closing brace
14//!
15//! There are a couple special cases to be aware of:
16//!
17//!  * Parameter bindings are valid within their function body, but don't spawn new scopes
18//!    of their own
19//!  * Bindings declared in the initializer of a `for` statement are valid within the
20//!    statement's body, and within the condition and increment expressions
21//!
22//! Once the `Scopes` object has been constructed, the declaration for any given
23//! identifier binding can be found by querying the `Scopes` instance for the identifier's
24//! `Token`. If the declaration is not found, either something has gone terribly wrong or
25//! the identifier is invalid.
26
27use std::{
28	collections::HashMap,
29	fmt::{self, Write},
30	sync::{Arc, Weak},
31};
32
33use gramatika::{Span, Spanned, Substr, Token as _};
34use parking_lot::RwLock;
35
36use crate::{decl::Decl, traversal::Walk, SyntaxTree, Token, TokenKind};
37
38mod builder;
39use builder::ScopeBuilder;
40
41/// A mapping of identifier names to their declarations
42pub type Bindings = HashMap<(Substr, TokenKind), Arc<Decl>>;
43
44pub struct Scope {
45	span: Span,
46	parent: Option<Weak<Scope>>,
47	children: RwLock<Vec<Arc<Scope>>>,
48	bindings: Arc<RwLock<Bindings>>,
49}
50
51impl Spanned for Scope {
52	fn span(&self) -> Span {
53		self.span
54	}
55}
56
57trait PrintKeys {
58	fn print_keys(&self, indent: usize) -> Result<String, fmt::Error>;
59}
60
61impl PrintKeys for Bindings {
62	fn print_keys(&self, indent: usize) -> Result<String, fmt::Error> {
63		let spacing = "  ".repeat(indent);
64
65		let mut out = String::new();
66		let f = &mut out;
67
68		for (lexeme, kind) in self.keys() {
69			write!(f, "`{}` ({:?}), ", lexeme, kind)?;
70		}
71		out.pop();
72		out.pop();
73
74		if out.is_empty() {
75			return Ok(out);
76		}
77
78		out = out.replace(", ", &format!("\n  {spacing}"));
79		out = std::format!("\n  {spacing}{out}");
80
81		Ok(out)
82	}
83}
84
85impl fmt::Debug for Scope {
86	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87		write!(f, "{}", self.print(0)?)
88	}
89}
90
91pub fn build(syntax_tree: &SyntaxTree) -> Arc<Scope> {
92	let span = syntax_tree.span();
93	let mut builder = ScopeBuilder::new(span);
94
95	syntax_tree.walk(&mut builder);
96	builder.build()
97}
98
99impl Scope {
100	pub fn new(span: Span) -> Self {
101		Self {
102			span,
103			parent: None,
104			children: RwLock::new(vec![]),
105			bindings: Arc::new(RwLock::new(HashMap::default())),
106		}
107	}
108
109	pub fn with_parent(span: Span, parent: Arc<Scope>) -> Arc<Self> {
110		let mut this = Self::new(span);
111		this.parent = Some(Arc::downgrade(&parent));
112
113		let arc_this = Arc::new(this);
114		parent.children.write().push(Arc::clone(&arc_this));
115
116		arc_this
117	}
118
119	pub fn parent(&self) -> Option<Arc<Scope>> {
120		self.parent.as_ref().and_then(|env| env.upgrade())
121	}
122
123	pub fn find_decl(
124		&self,
125		token: &Token,
126		debug: bool, // TODO
127	) -> Option<Arc<Decl>> {
128		let (lexeme, span) = token.as_inner();
129		let kind = token.kind();
130
131		if !self.span.contains(span) {
132			None
133		} else {
134			if debug {
135				eprintln!(
136					"Looking for `{lexeme}` ({kind:?}) ({span:?}) in scope:\n  ({:?}){}",
137					self.span(),
138					self.bindings.read().print_keys(1).unwrap(),
139				);
140			}
141
142			if self.bindings.read().contains_key(&(lexeme.clone(), kind)) {
143				self.bindings.read().get(&(lexeme, kind)).cloned()
144			} else {
145				self.children
146					.read()
147					.iter()
148					.find_map(|env| env.find_decl(token, debug))
149					.clone()
150			}
151		}
152	}
153
154	pub fn find_field_decl(&self, token: &Token, struct_name: &Token) -> Option<Arc<Decl>> {
155		let (lexeme, span) = token.as_inner();
156		let field_key = &(lexeme, token.kind());
157		let struct_key = &(struct_name.lexeme(), TokenKind::Ident);
158
159		if !self.span.contains(span) {
160			None
161		} else if self.bindings.read().contains_key(field_key)
162			&& self.bindings.read().contains_key(struct_key)
163		{
164			self.bindings.read().get(field_key).cloned()
165		} else {
166			self.children
167				.read()
168				.iter()
169				.find_map(|env| env.find_field_decl(token, struct_name))
170				.clone()
171		}
172	}
173
174	pub fn find(&self, token: &Token) -> Option<Arc<Scope>> {
175		let (lexeme, span) = token.as_inner();
176		let kind = token.kind();
177
178		if !self.span.contains(span) {
179			return None;
180		}
181
182		self.children
183			.read()
184			.iter()
185			.find_map(|scope| {
186				if scope.span.contains(span)
187					&& scope.bindings.read().contains_key(&(lexeme.clone(), kind))
188				{
189					Some(Arc::clone(scope))
190				} else {
191					None
192				}
193			})
194			.or_else(|| {
195				self.children
196					.read()
197					.iter()
198					.find_map(|scope| scope.find(token))
199			})
200	}
201
202	fn define(&self, ident: (Substr, TokenKind), decl: Decl) {
203		self.bindings.write().insert(ident, Arc::new(decl));
204	}
205
206	fn print(&self, indent: usize) -> Result<String, fmt::Error> {
207		let mut out = String::new();
208		let spacing = "  ".repeat(indent);
209
210		writeln!(
211			&mut out,
212			"{spacing}({:?}) {}",
213			self.span,
214			self.bindings.read().print_keys(indent)?
215		)?;
216
217		for child in self.children.read().iter() {
218			write!(&mut out, "{}", child.print(indent + 1)?)?;
219		}
220
221		Ok(out)
222	}
223}