glsl_lang_lexer/
context.rs

1use std::cell::RefCell;
2use std::collections::{BTreeMap, HashSet};
3use std::rc::Rc;
4
5use lang_util::{FileId, SmolStr};
6
7use glsl_lang_types::ast;
8
9/// Parsing options
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct ParseOptions {
12    /// Default GLSL version number to parse source as
13    pub default_version: u16,
14    /// `true` if the GLSL target should be Vulkan instead of OpenGL
15    pub target_vulkan: bool,
16    /// Unique source identifier for token positions
17    pub source_id: FileId,
18    /// Allow Rust quoting identifiers (`#(ident)`) in the source
19    pub allow_rs_ident: bool,
20}
21
22impl Default for ParseOptions {
23    fn default() -> Self {
24        Self {
25            default_version: 460,
26            target_vulkan: false,
27            source_id: FileId::new(0),
28            allow_rs_ident: false,
29        }
30    }
31}
32
33impl ParseOptions {
34    /// Create new parsing options using default values
35    pub fn new() -> Self {
36        Self::default()
37    }
38}
39
40/// Parsing context
41#[derive(Default, Debug, Clone, PartialEq)]
42pub struct ParseContext {
43    /// Parsing data
44    data: Rc<RefCell<ParseContextData>>,
45}
46
47impl ParseContext {
48    /// Create a new parsing context from this options object
49    pub fn new() -> ParseContext {
50        Default::default()
51    }
52
53    /// Create a new parsing context from this options object, with comment parsing enabled
54    pub fn new_with_comments() -> Self {
55        Self {
56            data: Rc::new(RefCell::new(ParseContextData::with_comments())),
57        }
58    }
59
60    /// Create a new parsing context from this options object, with a custom type table policy
61    pub fn new_with_policy(policy: impl TypeTablePolicy + 'static) -> Self {
62        Self {
63            data: Rc::new(RefCell::new(ParseContextData::with_policy(policy))),
64        }
65    }
66
67    /// Create a new parsing context from this options object, with a custom type table policy and
68    /// comment parsing enabled
69    pub fn new_with_comments_and_policy(policy: impl TypeTablePolicy + 'static) -> Self {
70        Self {
71            data: Rc::new(RefCell::new(ParseContextData::with_comments_and_policy(
72                policy,
73            ))),
74        }
75    }
76
77    /// Create a new parsing context from this options object and pre-existing context data
78    pub fn new_with_context(context: ParseContextData) -> Self {
79        Self {
80            data: Rc::new(RefCell::new(context)),
81        }
82    }
83
84    /// Clone the parsing data and return the cloned context
85    pub fn clone_inner(&self) -> Self {
86        Self {
87            data: Rc::new(RefCell::new(self.data.borrow().clone())),
88        }
89    }
90
91    /// Consume this [ParseContext] and return its data. Will fail if there are multiple references
92    /// to this context's data.
93    pub fn into_data(self) -> Option<ParseContextData> {
94        Rc::try_unwrap(self.data).ok().map(RefCell::into_inner)
95    }
96
97    /// Obtain a reference to the context's data
98    pub fn data(&self) -> std::cell::Ref<'_, ParseContextData> {
99        self.data.borrow()
100    }
101
102    /// Obtain an exclusive reference to the context's data
103    pub fn data_mut(&self) -> std::cell::RefMut<'_, ParseContextData> {
104        self.data.borrow_mut()
105    }
106
107    /// Create a new parse context cloning the given one's data, but applies the given policy
108    pub fn with_policy(&self, policy: impl TypeTablePolicy + 'static) -> ParseContext {
109        Self {
110            data: {
111                let mut data = self.data().clone();
112                data.policy = Rc::new(policy);
113                Rc::new(RefCell::new(data))
114            },
115        }
116    }
117}
118
119impl From<ParseContextData> for ParseContext {
120    fn from(data: ParseContextData) -> Self {
121        Self {
122            data: Rc::new(RefCell::new(data)),
123        }
124    }
125}
126
127/// Parsing context data
128#[derive(Debug, Clone)]
129pub struct ParseContextData {
130    /// List of known type names
131    names: Vec<HashSet<SmolStr>>,
132    /// List of parsed comments (or `None` to disable comment parsing)
133    comments: Option<CommentList>,
134
135    policy: Rc<dyn TypeTablePolicy>,
136}
137
138impl ParseContextData {
139    /// Create a new [ParseContextData] object
140    pub fn new() -> Self {
141        Self::default()
142    }
143
144    /// Create a new [ParseContextData] object with the given type table policy
145    ///
146    /// # Parameters
147    ///
148    /// * `policy`: policy object managing the type table
149    pub fn with_policy(policy: impl TypeTablePolicy + 'static) -> Self {
150        Self {
151            policy: Rc::new(policy),
152            ..Default::default()
153        }
154    }
155
156    /// Create a new [ParseContextData] object with comments parsing enabled
157    pub fn with_comments() -> Self {
158        Self {
159            comments: Some(Default::default()),
160            ..Default::default()
161        }
162    }
163
164    /// Create a new [ParseContextData] object with comments parsing enabled and the given type
165    /// table policy
166    ///
167    /// # Parameters
168    ///
169    /// * `policy`: policy object managing the type table
170    pub fn with_comments_and_policy(policy: impl TypeTablePolicy + 'static) -> Self {
171        Self {
172            comments: Some(Default::default()),
173            policy: Rc::new(policy),
174            ..Default::default()
175        }
176    }
177
178    /// Get the list of comments stored in this parse context
179    pub fn comments(&self) -> Option<&CommentList> {
180        self.comments.as_ref()
181    }
182}
183
184impl Default for ParseContextData {
185    fn default() -> Self {
186        Self {
187            names: vec![HashSet::new()],
188            comments: Default::default(),
189            policy: Rc::new(GlslTypeTablePolicy),
190        }
191    }
192}
193
194impl PartialEq for ParseContextData {
195    fn eq(&self, other: &Self) -> bool {
196        self.names.eq(&other.names)
197    }
198}
199
200// Begin type name stuff
201
202/// Context in which an identifier is seen for the first time
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum IdentifierContext {
205    /// The identifier is a function declaration name
206    FunctionPrototype,
207}
208
209/// A policy to dictate which identifiers should be seen as type names or not
210pub trait TypeTablePolicy: std::fmt::Debug {
211    /// Return `true` if the given identifier (in its context) should now be considered a type name
212    /// or not.
213    fn promote_to_type_name(&self, name: &ast::Identifier, ctx: IdentifierContext) -> bool;
214}
215
216/// Default GLSL type table policy: only struct declarations create new type names
217#[derive(Debug, Clone, Copy)]
218pub struct GlslTypeTablePolicy;
219
220impl TypeTablePolicy for GlslTypeTablePolicy {
221    fn promote_to_type_name(&self, _: &ast::Identifier, _: IdentifierContext) -> bool {
222        false
223    }
224}
225
226impl ParseContext {
227    /// Return `true` if the given name is a type name
228    pub fn is_type_name(&self, name: &str) -> bool {
229        self.data.borrow().is_type_name(name)
230    }
231
232    /// Register `name` as a new type name
233    pub fn add_type_name(&self, name: ast::Identifier) -> ast::TypeName {
234        self.data.borrow_mut().add_type_name(name)
235    }
236
237    /// Enter a new nesting level for declarations
238    pub fn push_scope(&self) {
239        self.data.borrow_mut().push_scope();
240    }
241
242    /// Leave the current nesting level
243    pub fn pop_scope(&self) {
244        self.data.borrow_mut().pop_scope();
245    }
246
247    /// Update the context data with a new identifier in a given context
248    pub fn new_identifier(&self, name: &ast::Identifier, ctx: IdentifierContext) {
249        self.data.borrow_mut().new_identifier(name, ctx)
250    }
251}
252
253impl ParseContextData {
254    /// Return `true` if the given name is a type name
255    pub fn is_type_name(&self, name: &str) -> bool {
256        self.names.iter().any(|level| level.contains(name))
257    }
258
259    /// Register `name` as a new type name
260    pub fn add_type_name(&mut self, name: ast::Identifier) -> ast::TypeName {
261        let name_string = name.0.as_str();
262        self.names.last_mut().unwrap().insert(name_string.into());
263        name.map(ast::TypeNameData::from)
264    }
265
266    /// Enter a new nesting level for declarations
267    pub fn push_scope(&mut self) {
268        self.names.push(HashSet::new());
269    }
270
271    /// Leave the current nesting level
272    pub fn pop_scope(&mut self) {
273        if self.names.len() > 1 {
274            self.names.pop();
275        }
276    }
277
278    /// Update the context data with a new identifier in a given context
279    pub fn new_identifier(&mut self, name: &ast::Identifier, ctx: IdentifierContext) {
280        if self.policy.promote_to_type_name(name, ctx) {
281            self.add_type_name(name.clone());
282        }
283    }
284}
285
286// End type name stuff
287
288// Begin comment stuff
289
290/// A list of comments indexed by their position
291pub type CommentList = BTreeMap<lang_util::position::NodeSpan, ast::Comment>;
292
293impl ParseContext {
294    /// Return `true` if this parsing context supports comments
295    pub fn has_comments(&self) -> bool {
296        self.data.borrow().comments.is_some()
297    }
298
299    /// Add a new comment to the parsed comments list
300    pub fn add_comment(&self, comment: ast::Comment) {
301        self.data.borrow_mut().add_comment(comment)
302    }
303}
304
305impl ParseContextData {
306    /// Add a new comment to the parsed comments list
307    pub fn add_comment(&mut self, comment: ast::Comment) {
308        if let Some(comments) = self.comments.as_mut() {
309            let span = comment.span;
310            comments.insert(span.unwrap(), comment);
311        }
312    }
313}