glsl_lang_lexer/full/
directives.rs

1use std::cmp;
2
3use glsl_lang_pp::processor::{
4    event::{DirectiveKind, EventDirective},
5    nodes::VersionProfile,
6};
7use glsl_lang_types::ast::{
8    self, PreprocessorExtensionBehaviorData, PreprocessorExtensionNameData,
9    PreprocessorVersionProfileData,
10};
11use lang_util::NodeContent;
12
13#[derive(Default, Debug, Clone)]
14pub struct Directives {
15    directives: Vec<EventDirective>,
16}
17
18impl Directives {
19    pub fn directives(&self) -> &[EventDirective] {
20        &self.directives
21    }
22
23    fn get_declaration(
24        directive: &EventDirective,
25        highest_version: &mut Option<(u16, Option<VersionProfile>)>,
26    ) -> Option<ast::ExternalDeclaration> {
27        let start = directive.text_range().start();
28        let end = directive.text_range().end();
29
30        match directive.kind() {
31            DirectiveKind::Version(version) => {
32                let version_number = version.number;
33                let profile = version.parsed_profile;
34
35                let had_highest_version;
36                let (highest_version_number, highest_profile) = match highest_version {
37                    Some(version_data) => {
38                        had_highest_version = true;
39                        version_data
40                    }
41                    None => {
42                        had_highest_version = false;
43                        highest_version.insert((version_number, profile))
44                    }
45                };
46
47                *highest_version_number = cmp::max(version_number, *highest_version_number);
48                *highest_profile = cmp::max(
49                    // Wrap in Some to avoid OpenGL ES being considered higher than no profile (i.e., core profile)
50                    Some(profile.unwrap_or(VersionProfile::None)),
51                    *highest_profile,
52                );
53
54                (!had_highest_version).then_some(
55                    ast::ExternalDeclarationData::Preprocessor(
56                        ast::PreprocessorData::Version(
57                            ast::PreprocessorVersionData {
58                                version: version_number,
59                                profile: match profile {
60                                    None | Some(VersionProfile::None) => None,
61                                    Some(VersionProfile::Core) => {
62                                        Some(PreprocessorVersionProfileData::Core.into())
63                                    }
64                                    Some(VersionProfile::Compatibility) => {
65                                        Some(PreprocessorVersionProfileData::Compatibility.into())
66                                    }
67                                    Some(VersionProfile::Es) => {
68                                        Some(PreprocessorVersionProfileData::Es.into())
69                                    }
70                                },
71                            }
72                            .into(),
73                        )
74                        .spanned(start, end),
75                    )
76                    .spanned(start, end),
77                )
78            }
79
80            DirectiveKind::Pragma(pragma) => Some(
81                ast::ExternalDeclarationData::Preprocessor(
82                    ast::PreprocessorData::Pragma(
83                        ast::PreprocessorPragmaData {
84                            command: pragma.raw().to_owned(),
85                        }
86                        .into(),
87                    )
88                    .spanned(start, end),
89                )
90                .spanned(start, end),
91            ),
92
93            DirectiveKind::Extension(extension) => Some(
94                ast::ExternalDeclarationData::Preprocessor(
95                    ast::PreprocessorData::Extension(
96                        ast::PreprocessorExtensionData {
97                            name: match extension.name {
98                                glsl_lang_pp::processor::nodes::ExtensionName::All => {
99                                    PreprocessorExtensionNameData::All
100                                }
101                                glsl_lang_pp::processor::nodes::ExtensionName::Specific(
102                                    ref name,
103                                ) => PreprocessorExtensionNameData::Specific(name.as_ref().into()),
104                            }
105                            .into(),
106                            behavior: Some(
107                                match extension.behavior {
108                                    glsl_lang_pp::processor::nodes::ExtensionBehavior::Require => {
109                                        PreprocessorExtensionBehaviorData::Require
110                                    }
111                                    glsl_lang_pp::processor::nodes::ExtensionBehavior::Enable => {
112                                        PreprocessorExtensionBehaviorData::Enable
113                                    }
114                                    glsl_lang_pp::processor::nodes::ExtensionBehavior::Warn => {
115                                        PreprocessorExtensionBehaviorData::Warn
116                                    }
117                                    glsl_lang_pp::processor::nodes::ExtensionBehavior::Disable => {
118                                        PreprocessorExtensionBehaviorData::Disable
119                                    }
120                                }
121                                .into(),
122                            ),
123                        }
124                        .into(),
125                    )
126                    .spanned(start, end),
127                )
128                .spanned(start, end),
129            ),
130
131            _ => None,
132        }
133    }
134
135    pub fn inject(mut self, root: &mut ast::TranslationUnit) -> Directives {
136        let mut directive_idx = 0;
137        let mut declaration_idx = 0;
138        let mut highest_version = None;
139        let mut version_directive_declaration_idx = None;
140
141        // Insert directives
142        let mut start = None;
143        while declaration_idx < root.0.len() && directive_idx < self.directives.len() {
144            // The current declaration. We want to insert directives before this declaration.
145            let current_declaration = &root.0[declaration_idx];
146
147            // Find where the range starts
148            let actual_start =
149                if let Some(current_start) = current_declaration.span.map(|span| span.start()) {
150                    current_start
151                } else if let Some(start) = start {
152                    // This is the end of the previous declaration
153                    start
154                } else {
155                    // No span information, keep looking
156                    declaration_idx += 1;
157                    continue;
158                };
159
160            // Find where the current declaration ends
161            let end = if let Some(current_end) = current_declaration.span.map(|span| span.end()) {
162                current_end
163            } else {
164                // The current node has no span information, so use the current start
165                actual_start
166            };
167
168            // Are there any directives to insert before the current declaration_idx?
169            while directive_idx < self.directives.len() {
170                // The current directive
171                let current_directive = &self.directives[directive_idx];
172                let span = current_directive.text_range();
173
174                // For directives in #include'd files, a previous #include directive event prevents
175                // inserting them too soon in the top-level file
176                if span.end().offset <= actual_start.offset
177                    || actual_start.source_id != span.source_id()
178                {
179                    if let Some(declaration) =
180                        Self::get_declaration(current_directive, &mut highest_version)
181                    {
182                        // Add to ast
183                        root.0.insert(declaration_idx, declaration);
184
185                        if matches!(current_directive.kind(), DirectiveKind::Version(_)) {
186                            version_directive_declaration_idx.get_or_insert(declaration_idx);
187                        }
188
189                        declaration_idx += 1;
190
191                        // Processed, remove from directive list
192                        self.directives.remove(directive_idx);
193                    } else {
194                        // Can't/shouldn't be processed, skip it
195                        directive_idx += 1;
196                    }
197                } else {
198                    // This directive comes later
199                    break;
200                }
201            }
202
203            // Advance the current declaration
204            declaration_idx += 1;
205
206            // Advance the directive range
207            start = Some(end);
208        }
209
210        // Append any remaining directives
211        while directive_idx < self.directives.len() {
212            let current_directive = &self.directives[directive_idx];
213
214            if let Some(declaration) =
215                Self::get_declaration(current_directive, &mut highest_version)
216            {
217                root.0.push(declaration);
218
219                if matches!(current_directive.kind(), DirectiveKind::Version(_)) {
220                    version_directive_declaration_idx.get_or_insert(root.0.len() - 1);
221                }
222
223                self.directives.remove(directive_idx);
224            } else {
225                directive_idx += 1;
226            }
227        }
228
229        // Set the version directive value to the highest (i.e., the one that requires the most
230        // OpenGL features) that was found
231        if let (
232            Some(version_directive_declaration_idx),
233            Some((highest_version_number, highest_profile)),
234        ) = (version_directive_declaration_idx, highest_version)
235        {
236            if let ast::ExternalDeclarationData::Preprocessor(preprocessor_data) =
237                &mut root.0[version_directive_declaration_idx].content
238            {
239                if let ast::PreprocessorData::Version(preprocessor_version) =
240                    &mut preprocessor_data.content
241                {
242                    preprocessor_version.version = highest_version_number;
243                    preprocessor_version.profile = match highest_profile {
244                        None | Some(VersionProfile::None) => None,
245                        Some(VersionProfile::Core) => {
246                            Some(PreprocessorVersionProfileData::Core.into())
247                        }
248                        Some(VersionProfile::Compatibility) => {
249                            Some(PreprocessorVersionProfileData::Compatibility.into())
250                        }
251                        Some(VersionProfile::Es) => Some(PreprocessorVersionProfileData::Es.into()),
252                    };
253                }
254            }
255        }
256
257        self
258    }
259}
260
261impl From<Vec<EventDirective>> for Directives {
262    fn from(directives: Vec<EventDirective>) -> Self {
263        Self { directives }
264    }
265}
266
267impl From<Directives> for Vec<EventDirective> {
268    fn from(directives: Directives) -> Self {
269        directives.directives
270    }
271}