shader_sense/symbols/
prepocessor.rs

1use std::{
2    collections::HashSet,
3    path::{Path, PathBuf},
4};
5
6use crate::{
7    include::IncludeHandler,
8    position::{ShaderFileRange, ShaderRange},
9    shader::ShaderContextParams,
10    shader_error::ShaderDiagnostic,
11    symbols::{
12        shader_module::ShaderSymbols,
13        symbol_list::{ShaderSymbolList, ShaderSymbolListRef},
14        symbols::{
15            ShaderSymbol, ShaderSymbolData, ShaderSymbolMode, ShaderSymbolRuntime,
16            ShaderSymbolRuntimeContext,
17        },
18    },
19};
20
21#[derive(Debug, Default, Clone)]
22pub struct ShaderRegion {
23    pub range: ShaderRange,
24    // Could add some ShaderRegionType::Condition / ShaderRegionType::User...
25    pub is_active: bool, // Is this region passing preprocess
26}
27
28impl ShaderRegion {
29    pub fn new(range: ShaderRange, is_active: bool) -> Self {
30        Self { range, is_active }
31    }
32}
33
34#[derive(Debug, Default, Clone)]
35pub struct ShaderPreprocessorContext {
36    defines: Vec<ShaderSymbol>,
37    include_handler: IncludeHandler,
38    dirty_files: HashSet<PathBuf>, // Dirty files that need to be recomputed no matter what.
39    depth: usize,
40}
41
42impl ShaderPreprocessorContext {
43    pub fn main(file_path: &Path, shader_params: ShaderContextParams) -> Self {
44        Self {
45            defines: shader_params
46                .defines
47                .iter()
48                .map(|(key, value)| ShaderSymbol {
49                    label: key.clone(),
50                    requirement: None,
51                    data: ShaderSymbolData::Macro {
52                        value: value.clone(),
53                    },
54                    mode: ShaderSymbolMode::RuntimeContext(ShaderSymbolRuntimeContext::new()),
55                })
56                .collect(),
57            include_handler: IncludeHandler::main(
58                &file_path,
59                shader_params.includes,
60                shader_params.path_remapping,
61            ),
62            dirty_files: HashSet::new(),
63            depth: 0,
64        }
65    }
66    pub fn mark_dirty(&mut self, file_path: PathBuf) {
67        self.dirty_files.insert(file_path);
68    }
69    pub fn search_path_in_includes(&mut self, path: &Path) -> Option<PathBuf> {
70        self.include_handler.search_path_in_includes(path)
71    }
72    pub fn push_directory_stack(&mut self, canonical_path: &Path) {
73        self.include_handler.push_directory_stack(canonical_path);
74    }
75    pub fn push_define(&mut self, name: &str, value: &str) {
76        self.defines.push(ShaderSymbol {
77            label: name.into(),
78            requirement: None,
79            data: ShaderSymbolData::Macro {
80                value: value.into(),
81            },
82            mode: ShaderSymbolMode::RuntimeContext(ShaderSymbolRuntimeContext::new()),
83        });
84    }
85    pub fn append_defines(&mut self, defines: Vec<ShaderPreprocessorDefine>) {
86        self.defines
87            .extend(defines.iter().map(|define| define.symbol.clone()));
88    }
89    pub fn increase_depth(&mut self) -> bool {
90        if self.depth < IncludeHandler::DEPTH_LIMIT {
91            self.depth += 1;
92            true
93        } else {
94            false
95        }
96    }
97    pub fn decrease_depth(&mut self) {
98        assert!(self.depth > 0, "Decreasing depth but zero.");
99        self.depth -= 1;
100    }
101    pub fn get_visited_count(&mut self, path: &Path) -> usize {
102        self.include_handler.get_visited_count(path)
103    }
104    pub fn is_dirty(&self, file_path: &Path, context: &ShaderPreprocessorContext) -> bool {
105        // Compare defines to determine if context is different.
106        // Check if we need to force an update aswell.
107        fn are_defines_equal(lhs: &Vec<ShaderSymbol>, rhs: &Vec<ShaderSymbol>) -> bool {
108            if lhs.len() != rhs.len() {
109                return false;
110            }
111            for lhs_symbol in lhs.iter() {
112                if rhs
113                    .iter()
114                    .find(|rhs_symbol| {
115                        lhs_symbol.label == rhs_symbol.label
116                            && match (&lhs_symbol.data, &rhs_symbol.data) {
117                                (
118                                    ShaderSymbolData::Macro { value: l_value },
119                                    ShaderSymbolData::Macro { value: r_value },
120                                ) => l_value == r_value,
121                                _ => false,
122                            }
123                    })
124                    .is_none()
125                {
126                    return false;
127                }
128            }
129            true
130        }
131        fn are_includes_equal(lhs: &HashSet<PathBuf>, rhs: &HashSet<PathBuf>) -> bool {
132            if lhs.len() != rhs.len() {
133                return false;
134            }
135            for lhs_symbol in lhs.iter() {
136                if rhs
137                    .iter()
138                    .find(|rhs_symbol| lhs_symbol.as_os_str() == rhs_symbol.as_os_str())
139                    .is_none()
140                {
141                    return false;
142                }
143            }
144            true
145        }
146        !are_defines_equal(&context.defines, &self.defines)
147            || !are_includes_equal(
148                context.include_handler.get_includes(),
149                self.include_handler.get_includes(),
150            )
151            || context.dirty_files.contains(file_path)
152    }
153    pub fn get_define_value(&self, name: &str) -> Option<String> {
154        self.defines
155            .iter()
156            .find(|symbol| *symbol.label == *name)
157            .map(|symbol| match &symbol.data {
158                ShaderSymbolData::Macro { value } => value.clone(),
159                _ => panic!("Expected ShaderSymbolData::Macro"),
160            })
161    }
162    pub fn get_defines(&self) -> &Vec<ShaderSymbol> {
163        &self.defines
164    }
165}
166
167#[derive(Debug, Clone)]
168pub struct ShaderPreprocessorInclude {
169    // TODO: move cache to symbol data
170    pub cache: Option<ShaderSymbols>,
171    symbol: ShaderSymbol,
172}
173
174#[derive(Debug, Clone)]
175pub struct ShaderPreprocessorDefine {
176    symbol: ShaderSymbol,
177}
178
179#[derive(Debug, Default, Clone, PartialEq, Eq)]
180pub enum ShaderPreprocessorMode {
181    #[default]
182    Default,
183    Once,
184    OnceVisited,
185}
186
187#[derive(Debug, Default, Clone)]
188pub struct ShaderPreprocessor {
189    pub context: ShaderPreprocessorContext, // Defines from includer files when included, or config.
190
191    pub includes: Vec<ShaderPreprocessorInclude>,
192    pub defines: Vec<ShaderPreprocessorDefine>,
193    pub regions: Vec<ShaderRegion>,
194    pub diagnostics: Vec<ShaderDiagnostic>, // preprocessor errors
195    pub mode: ShaderPreprocessorMode,
196}
197impl ShaderPreprocessorDefine {
198    pub fn new(name: String, range: ShaderFileRange, value: Option<String>) -> Self {
199        Self {
200            symbol: ShaderSymbol {
201                label: name.clone(),
202                requirement: None,
203                data: ShaderSymbolData::Macro {
204                    value: match &value {
205                        Some(value) => value.clone(),
206                        None => "".into(),
207                    },
208                },
209                mode: ShaderSymbolMode::Runtime(ShaderSymbolRuntime::global(
210                    range.file_path,
211                    range.range,
212                )),
213            },
214        }
215    }
216    pub fn get_file_path(&self) -> &Path {
217        &self.symbol.mode.unwrap_runtime().file_path
218    }
219    pub fn get_range(&self) -> &ShaderRange {
220        &self.symbol.mode.unwrap_runtime().range
221    }
222    pub fn get_name(&self) -> &String {
223        &self.symbol.label
224    }
225    pub fn get_value(&self) -> Option<&String> {
226        match &self.symbol.data {
227            ShaderSymbolData::Macro { value } => Some(value),
228            _ => None,
229        }
230    }
231}
232impl ShaderPreprocessorInclude {
233    pub fn new(relative_path: String, absolute_path: PathBuf, range: ShaderFileRange) -> Self {
234        Self {
235            cache: None,
236            symbol: ShaderSymbol {
237                label: relative_path,
238                requirement: None,
239                data: ShaderSymbolData::Include {
240                    target: absolute_path,
241                },
242                mode: ShaderSymbolMode::Runtime(ShaderSymbolRuntime::global(
243                    range.file_path,
244                    range.range,
245                )),
246            },
247        }
248    }
249    pub fn get_range(&self) -> &ShaderRange {
250        &self.symbol.mode.unwrap_runtime().range
251    }
252    pub fn get_file_range(&self) -> ShaderFileRange {
253        let runtime = self.symbol.mode.unwrap_runtime();
254        runtime.range.clone_into_file(runtime.file_path.clone())
255    }
256    pub fn get_relative_path(&self) -> &String {
257        &self.symbol.label
258    }
259    pub fn get_absolute_path(&self) -> &Path {
260        match &self.symbol.data {
261            ShaderSymbolData::Include { target } => &target,
262            _ => panic!("Expected ShaderSymbolData::Link"),
263        }
264    }
265    pub fn get_cache(&self) -> &ShaderSymbols {
266        self.cache.as_ref().unwrap()
267    }
268    pub fn get_cache_mut(&mut self) -> &mut ShaderSymbols {
269        self.cache.as_mut().unwrap()
270    }
271}
272
273impl ShaderPreprocessor {
274    pub fn new(context: ShaderPreprocessorContext) -> Self {
275        Self {
276            context: context,
277            includes: Vec::new(),
278            defines: Vec::new(),
279            regions: Vec::new(),
280            diagnostics: Vec::new(),
281            mode: ShaderPreprocessorMode::default(),
282        }
283    }
284    pub fn preprocess_symbols<'a>(
285        &'a self,
286        shader_symbols: &'a ShaderSymbolList,
287    ) -> ShaderSymbolListRef<'a> {
288        // Filter inactive regions symbols
289        let inactive_regions: Vec<&ShaderRegion> =
290            self.regions.iter().filter(|r| !r.is_active).collect();
291        let mut preprocessed_symbols =
292            shader_symbols.filter(move |_symbol_type, symbol| match &symbol.mode {
293                ShaderSymbolMode::Runtime(runtime) => inactive_regions
294                    .iter()
295                    .find(|r| r.range.contain_bounds(&runtime.range))
296                    .is_none(),
297                ShaderSymbolMode::RuntimeContext(_) => true, // Global range
298                ShaderSymbolMode::Intrinsic(_) => true,      // Global range
299            });
300        // Add defines
301        let mut define_symbols: Vec<&ShaderSymbol> =
302            self.defines.iter().map(|define| &define.symbol).collect();
303        // Add includes as symbol
304        let mut include_symbols: Vec<&ShaderSymbol> = self
305            .includes
306            .iter()
307            .map(|include| &include.symbol)
308            .collect();
309        preprocessed_symbols.macros.append(&mut define_symbols);
310        preprocessed_symbols.includes.append(&mut include_symbols);
311        preprocessed_symbols
312    }
313}