1use std::cell::RefCell;
2use std::collections::{BTreeMap, HashSet};
3use std::rc::Rc;
45use lang_util::{FileId, SmolStr};
67use glsl_lang_types::ast;
89/// Parsing options
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct ParseOptions {
12/// Default GLSL version number to parse source as
13pub default_version: u16,
14/// `true` if the GLSL target should be Vulkan instead of OpenGL
15pub target_vulkan: bool,
16/// Unique source identifier for token positions
17pub source_id: FileId,
18/// Allow Rust quoting identifiers (`#(ident)`) in the source
19pub allow_rs_ident: bool,
20}
2122impl Default for ParseOptions {
23fn default() -> Self {
24Self {
25 default_version: 460,
26 target_vulkan: false,
27 source_id: FileId::new(0),
28 allow_rs_ident: false,
29 }
30 }
31}
3233impl ParseOptions {
34/// Create new parsing options using default values
35pub fn new() -> Self {
36Self::default()
37 }
38}
3940/// Parsing context
41#[derive(Default, Debug, Clone, PartialEq)]
42pub struct ParseContext {
43/// Parsing data
44data: Rc<RefCell<ParseContextData>>,
45}
4647impl ParseContext {
48/// Create a new parsing context from this options object
49pub fn new() -> ParseContext {
50 Default::default()
51 }
5253/// Create a new parsing context from this options object, with comment parsing enabled
54pub fn new_with_comments() -> Self {
55Self {
56 data: Rc::new(RefCell::new(ParseContextData::with_comments())),
57 }
58 }
5960/// Create a new parsing context from this options object, with a custom type table policy
61pub fn new_with_policy(policy: impl TypeTablePolicy + 'static) -> Self {
62Self {
63 data: Rc::new(RefCell::new(ParseContextData::with_policy(policy))),
64 }
65 }
6667/// Create a new parsing context from this options object, with a custom type table policy and
68 /// comment parsing enabled
69pub fn new_with_comments_and_policy(policy: impl TypeTablePolicy + 'static) -> Self {
70Self {
71 data: Rc::new(RefCell::new(ParseContextData::with_comments_and_policy(
72 policy,
73 ))),
74 }
75 }
7677/// Create a new parsing context from this options object and pre-existing context data
78pub fn new_with_context(context: ParseContextData) -> Self {
79Self {
80 data: Rc::new(RefCell::new(context)),
81 }
82 }
8384/// Clone the parsing data and return the cloned context
85pub fn clone_inner(&self) -> Self {
86Self {
87 data: Rc::new(RefCell::new(self.data.borrow().clone())),
88 }
89 }
9091/// Consume this [ParseContext] and return its data. Will fail if there are multiple references
92 /// to this context's data.
93pub fn into_data(self) -> Option<ParseContextData> {
94 Rc::try_unwrap(self.data).ok().map(RefCell::into_inner)
95 }
9697/// Obtain a reference to the context's data
98pub fn data(&self) -> std::cell::Ref<'_, ParseContextData> {
99self.data.borrow()
100 }
101102/// Obtain an exclusive reference to the context's data
103pub fn data_mut(&self) -> std::cell::RefMut<'_, ParseContextData> {
104self.data.borrow_mut()
105 }
106107/// Create a new parse context cloning the given one's data, but applies the given policy
108pub fn with_policy(&self, policy: impl TypeTablePolicy + 'static) -> ParseContext {
109Self {
110 data: {
111let mut data = self.data().clone();
112 data.policy = Rc::new(policy);
113 Rc::new(RefCell::new(data))
114 },
115 }
116 }
117}
118119impl From<ParseContextData> for ParseContext {
120fn from(data: ParseContextData) -> Self {
121Self {
122 data: Rc::new(RefCell::new(data)),
123 }
124 }
125}
126127/// Parsing context data
128#[derive(Debug, Clone)]
129pub struct ParseContextData {
130/// List of known type names
131names: Vec<HashSet<SmolStr>>,
132/// List of parsed comments (or `None` to disable comment parsing)
133comments: Option<CommentList>,
134135 policy: Rc<dyn TypeTablePolicy>,
136}
137138impl ParseContextData {
139/// Create a new [ParseContextData] object
140pub fn new() -> Self {
141Self::default()
142 }
143144/// Create a new [ParseContextData] object with the given type table policy
145 ///
146 /// # Parameters
147 ///
148 /// * `policy`: policy object managing the type table
149pub fn with_policy(policy: impl TypeTablePolicy + 'static) -> Self {
150Self {
151 policy: Rc::new(policy),
152 ..Default::default()
153 }
154 }
155156/// Create a new [ParseContextData] object with comments parsing enabled
157pub fn with_comments() -> Self {
158Self {
159 comments: Some(Default::default()),
160 ..Default::default()
161 }
162 }
163164/// 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
170pub fn with_comments_and_policy(policy: impl TypeTablePolicy + 'static) -> Self {
171Self {
172 comments: Some(Default::default()),
173 policy: Rc::new(policy),
174 ..Default::default()
175 }
176 }
177178/// Get the list of comments stored in this parse context
179pub fn comments(&self) -> Option<&CommentList> {
180self.comments.as_ref()
181 }
182}
183184impl Default for ParseContextData {
185fn default() -> Self {
186Self {
187 names: vec![HashSet::new()],
188 comments: Default::default(),
189 policy: Rc::new(GlslTypeTablePolicy),
190 }
191 }
192}
193194impl PartialEq for ParseContextData {
195fn eq(&self, other: &Self) -> bool {
196self.names.eq(&other.names)
197 }
198}
199200// Begin type name stuff
201202/// 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
206FunctionPrototype,
207}
208209/// 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.
213fn promote_to_type_name(&self, name: &ast::Identifier, ctx: IdentifierContext) -> bool;
214}
215216/// Default GLSL type table policy: only struct declarations create new type names
217#[derive(Debug, Clone, Copy)]
218pub struct GlslTypeTablePolicy;
219220impl TypeTablePolicy for GlslTypeTablePolicy {
221fn promote_to_type_name(&self, _: &ast::Identifier, _: IdentifierContext) -> bool {
222false
223}
224}
225226impl ParseContext {
227/// Return `true` if the given name is a type name
228pub fn is_type_name(&self, name: &str) -> bool {
229self.data.borrow().is_type_name(name)
230 }
231232/// Register `name` as a new type name
233pub fn add_type_name(&self, name: ast::Identifier) -> ast::TypeName {
234self.data.borrow_mut().add_type_name(name)
235 }
236237/// Enter a new nesting level for declarations
238pub fn push_scope(&self) {
239self.data.borrow_mut().push_scope();
240 }
241242/// Leave the current nesting level
243pub fn pop_scope(&self) {
244self.data.borrow_mut().pop_scope();
245 }
246247/// Update the context data with a new identifier in a given context
248pub fn new_identifier(&self, name: &ast::Identifier, ctx: IdentifierContext) {
249self.data.borrow_mut().new_identifier(name, ctx)
250 }
251}
252253impl ParseContextData {
254/// Return `true` if the given name is a type name
255pub fn is_type_name(&self, name: &str) -> bool {
256self.names.iter().any(|level| level.contains(name))
257 }
258259/// Register `name` as a new type name
260pub fn add_type_name(&mut self, name: ast::Identifier) -> ast::TypeName {
261let name_string = name.0.as_str();
262self.names.last_mut().unwrap().insert(name_string.into());
263 name.map(ast::TypeNameData::from)
264 }
265266/// Enter a new nesting level for declarations
267pub fn push_scope(&mut self) {
268self.names.push(HashSet::new());
269 }
270271/// Leave the current nesting level
272pub fn pop_scope(&mut self) {
273if self.names.len() > 1 {
274self.names.pop();
275 }
276 }
277278/// Update the context data with a new identifier in a given context
279pub fn new_identifier(&mut self, name: &ast::Identifier, ctx: IdentifierContext) {
280if self.policy.promote_to_type_name(name, ctx) {
281self.add_type_name(name.clone());
282 }
283 }
284}
285286// End type name stuff
287288// Begin comment stuff
289290/// A list of comments indexed by their position
291pub type CommentList = BTreeMap<lang_util::position::NodeSpan, ast::Comment>;
292293impl ParseContext {
294/// Return `true` if this parsing context supports comments
295pub fn has_comments(&self) -> bool {
296self.data.borrow().comments.is_some()
297 }
298299/// Add a new comment to the parsed comments list
300pub fn add_comment(&self, comment: ast::Comment) {
301self.data.borrow_mut().add_comment(comment)
302 }
303}
304305impl ParseContextData {
306/// Add a new comment to the parsed comments list
307pub fn add_comment(&mut self, comment: ast::Comment) {
308if let Some(comments) = self.comments.as_mut() {
309let span = comment.span;
310 comments.insert(span.unwrap(), comment);
311 }
312 }
313}