glslt/transform/
global_scope.rs

1use std::collections::HashSet;
2use std::sync::Arc;
3
4use glsl_lang::ast::*;
5
6use indexmap::IndexMap;
7
8use super::template::{TemplateDefinition, TryTemplate};
9use super::{ResolvedArgument, ResolvedArgumentExpr, Scope};
10
11use crate::{Error, Result, TransformConfig};
12
13/// Result of parsing an ExternalDeclaration
14pub enum ParsedDeclaration {
15    /// The declaration was a function template type definition
16    ConsumedAsType,
17    /// The declaration was a function and was merged into the global scope as a template
18    ConsumedAsTemplate(Arc<TemplateDefinition>),
19    /// The declaration was something else and is to be processed by the caller
20    Unparsed(Arc<ExternalDeclaration>),
21}
22
23/// GLSLT template definition global scope
24#[derive(Default, Debug, Clone)]
25pub struct GlobalScope {
26    /// Transform config
27    config: TransformConfig,
28    /// Known pointer types
29    declared_pointer_types: IndexMap<SmolStr, FunctionPrototype>,
30    /// Known GLSLT template functions
31    declared_templates: IndexMap<SmolStr, Arc<TemplateDefinition>>,
32    /// Identifiers of function declarations
33    known_functions: IndexMap<SmolStr, FunctionPrototype>,
34    /// Identifiers of already instantiated templates
35    instantiated_templates: HashSet<SmolStr>,
36    /// Pending external declarations
37    instanced_templates: Vec<FunctionDefinition>,
38}
39
40impl GlobalScope {
41    /// Create a new template definition global scope
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    /// Create a new template definition global scope with the specified config
47    pub fn with_config(config: TransformConfig) -> Self {
48        Self {
49            config,
50            ..Default::default()
51        }
52    }
53
54    fn parse_function_prototype(&mut self, prototype: FunctionPrototype) -> Result<()> {
55        if let Some(previous) = self.declared_pointer_types.get(&prototype.name.0) {
56            // Since function pointer types are raw identifiers, they have to be unique
57            return Err(Error::new_duplicate_pointer_definition(
58                &prototype.name,
59                previous,
60            ));
61        } else {
62            info!("declared pointer: {}", prototype.name.0);
63
64            self.declared_pointer_types
65                .insert(prototype.name.0.clone(), prototype);
66        }
67
68        Ok(())
69    }
70
71    fn parse_declaration(&mut self, decl: Declaration) -> Result<ParsedDeclaration> {
72        match decl.content {
73            DeclarationData::FunctionPrototype(prototype) => {
74                // A function prototype is what we'll call a function pointer type
75                self.parse_function_prototype(prototype)?;
76                Ok(ParsedDeclaration::ConsumedAsType)
77            }
78            other => Ok(ParsedDeclaration::Unparsed(Arc::new(
79                ExternalDeclaration::new(
80                    ExternalDeclarationData::Declaration(Declaration::new(other, decl.span)),
81                    decl.span,
82                ),
83            ))),
84        }
85    }
86
87    fn parse_function_definition(&mut self, def: FunctionDefinition) -> Result<ParsedDeclaration> {
88        let span = def.span;
89
90        // A function definition is a template if any of its arguments is a pointer
91        let name = def.prototype.name.0.clone();
92        let template =
93            super::template::parse_definition_as_template(def, &self.declared_pointer_types)?;
94
95        match template {
96            TryTemplate::Template(template) => {
97                info!("declared template: {}", template.ast().prototype.name.0);
98
99                // We found a template parameter, so it's a template function
100                self.declared_templates
101                    .insert(name.clone(), Arc::new(*template));
102
103                let parsed = self.declared_templates.get(&name).unwrap();
104                Ok(ParsedDeclaration::ConsumedAsTemplate(parsed.clone()))
105            }
106            TryTemplate::Function(def) => Ok(ParsedDeclaration::Unparsed(Arc::new(
107                ExternalDeclaration::new(ExternalDeclarationData::FunctionDefinition(*def), span),
108            ))),
109        }
110    }
111
112    /// Get the list of defined function identifiers in this global scope
113    pub fn known_functions_mut(&mut self) -> &mut IndexMap<SmolStr, FunctionPrototype> {
114        &mut self.known_functions
115    }
116
117    /// Get the list of defined templates in this global scope
118    pub fn declared_templates(&self) -> &IndexMap<SmolStr, Arc<TemplateDefinition>> {
119        &self.declared_templates
120    }
121
122    /// Get the list of defined pointer types in this global scope
123    pub fn declared_pointer_types(&self) -> &IndexMap<SmolStr, FunctionPrototype> {
124        &self.declared_pointer_types
125    }
126
127    /// Parse a top-level declaration from a GLSLT shader.
128    ///
129    /// If the declaration is a GLSLT definition, it will not be returned and stored as part of the
130    /// global scope for future template instantiations.
131    ///
132    /// # Parameters
133    ///
134    /// * `extdecl`: declaration to parse
135    ///
136    /// # Returns
137    ///
138    /// `Ok(None)` if the declaration was parsed as a template or GLSLT definition. `Ok(Some(...))`
139    /// if this declaration is not a template or needs to be instantiated in a global scope
140    pub fn parse_external_declaration(
141        &mut self,
142        extdecl: ExternalDeclaration,
143    ) -> Result<ParsedDeclaration> {
144        let span = extdecl.span;
145
146        match extdecl.content {
147            ExternalDeclarationData::Declaration(decl) => self.parse_declaration(decl),
148            ExternalDeclarationData::FunctionDefinition(def) => {
149                Ok(self.parse_function_definition(def)?)
150            }
151            // Just forward the others
152            other => Ok(ParsedDeclaration::Unparsed(Arc::new(
153                ExternalDeclaration::new(other, span),
154            ))),
155        }
156    }
157
158    /// Register a function name within the global scope.
159    ///
160    /// This is required until a proper symbol table is added in order to differentiate variables
161    /// from function names when instantiating templates.
162    ///
163    /// # Parameters
164    ///
165    /// * `def`: function definition to register
166    pub fn push_function_declaration(&mut self, def: &FunctionDefinition) {
167        // We discovered a new function
168        self.known_functions
169            .insert(def.prototype.name.0.clone(), def.prototype.clone());
170    }
171}
172
173impl Scope for GlobalScope {
174    fn config(&self) -> &TransformConfig {
175        &self.config
176    }
177
178    fn parent_scope(&self) -> Option<&dyn Scope> {
179        None
180    }
181
182    fn declared_pointer_types(&self) -> &IndexMap<SmolStr, FunctionPrototype> {
183        &self.declared_pointer_types
184    }
185
186    fn get_template(&self, template_name: &str) -> Option<Arc<TemplateDefinition>> {
187        self.declared_templates.get(template_name).cloned()
188    }
189
190    fn template_instance_declared(&self, template_name: &str) -> bool {
191        self.instantiated_templates.contains(template_name)
192    }
193
194    fn register_template_instance(&mut self, definitions: Vec<FunctionDefinition>) {
195        for template in definitions {
196            let template_name = template.prototype.name.0.as_str();
197
198            // Take note we instantiated the template
199            self.instantiated_templates.insert(template_name.into());
200
201            // Add them to the instanced templates
202            self.instanced_templates.push(template);
203        }
204    }
205
206    fn take_instanced_templates(&mut self) -> Vec<FunctionDefinition> {
207        std::mem::replace(&mut self.instanced_templates, Vec::with_capacity(2))
208    }
209
210    fn resolve_function_name(&self, name: &str) -> Option<ResolvedArgument> {
211        self.known_functions
212            .get(name)
213            .map(|proto| ResolvedArgument {
214                body: ResolvedArgumentExpr::FunctionName(proto.name.0.clone()),
215                pointer_type: proto,
216            })
217    }
218
219    fn transform_arg_call(
220        &mut self,
221        _expr: &mut Expr,
222        _instantiator: &mut super::instantiate::InstantiateTemplate,
223    ) -> Result<()> {
224        Err(Error::TransformAsTemplate)
225    }
226
227    fn captured_parameters(&self) -> &[super::instantiate::CapturedParameter] {
228        &[]
229    }
230}