1use std::collections::HashMap;
2
3use lang_util::{FileId, SmolStr};
4
5mod definition;
6use definition::Definition;
7
8pub mod event;
9
10pub mod expand;
11
12mod expr;
13
14pub mod fs;
15
16pub mod nodes;
17use nodes::{Define, DefineObject, Version};
18
19use crate::{
20 exts::Registry,
21 processor::nodes::{ExtensionBehavior, ExtensionName},
22};
23
24pub mod str;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum IncludeMode {
29 None,
31 ArbInclude { warn: bool },
33 GoogleInclude { warn: bool },
35}
36
37impl IncludeMode {
38 pub fn warn(self) -> bool {
39 match self {
40 IncludeMode::None => false,
41 IncludeMode::ArbInclude { warn } | IncludeMode::GoogleInclude { warn } => warn,
42 }
43 }
44}
45
46impl Default for IncludeMode {
47 fn default() -> Self {
48 Self::None
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct ProcessorState {
55 include_mode: IncludeMode,
56 definitions: HashMap<SmolStr, Definition>,
57 version: Version,
58 cpp_style_line: bool,
59}
60
61impl ProcessorState {
62 pub fn builder() -> ProcessorStateBuilder<'static> {
63 ProcessorStateBuilder::default()
64 }
65
66 fn get_definition(&self, name: &str) -> Option<&Definition> {
67 self.definitions.get(name)
68 }
69
70 pub fn definition(&mut self, definition: Define, file_id: FileId) -> bool {
72 let entry = self.definitions.entry(definition.name().into());
73
74 match entry {
75 std::collections::hash_map::Entry::Occupied(mut occupied) => {
76 if occupied.get().protected() {
77 false
78 } else {
79 occupied.insert(Definition::Regular(definition.into(), file_id));
80 true
81 }
82 }
83 std::collections::hash_map::Entry::Vacant(vacant) => {
84 vacant.insert(Definition::Regular(definition.into(), file_id));
85 true
86 }
87 }
88 }
89
90 fn add_extension(&mut self, name: &ExtensionName, behavior: ExtensionBehavior) {
91 let target_include_mode = if *name == ext_name!("GL_ARB_shading_language_include") {
93 Some(IncludeMode::ArbInclude {
94 warn: behavior == ExtensionBehavior::Warn,
95 })
96 } else if *name == ext_name!("GL_GOOGLE_include_directive") {
97 Some(IncludeMode::GoogleInclude {
98 warn: behavior == ExtensionBehavior::Warn,
99 })
100 } else {
101 None
102 };
103
104 if let Some(target) = target_include_mode {
105 if behavior.is_active() {
106 self.include_mode = target;
107
108 if let IncludeMode::GoogleInclude { .. } = target {
110 self.cpp_style_line = true;
111 }
112 } else {
113 self.include_mode = IncludeMode::None;
115 }
116 }
117
118 if *name == ext_name!("GL_GOOGLE_cpp_style_line_directive") {
120 if behavior.is_active() {
121 self.cpp_style_line = true;
122 } else {
123 if !matches!(self.include_mode, IncludeMode::GoogleInclude { .. }) {
125 self.cpp_style_line = false;
126 }
127 }
128 }
129 }
130
131 fn extension(&mut self, extension: &nodes::Extension) {
132 self.add_extension(&extension.name, extension.behavior);
133 }
134
135 fn cpp_style_line(&self) -> bool {
136 self.cpp_style_line
137 }
138}
139
140impl Default for ProcessorState {
141 fn default() -> Self {
142 ProcessorStateBuilder::default().finish()
143 }
144}
145
146#[derive(Clone)]
147pub struct ProcessorStateBuilder<'r> {
148 core_profile: bool,
149 compatibility_profile: bool,
150 es_profile: bool,
151 extensions: Vec<(ExtensionName, ExtensionBehavior)>,
152 definitions: Vec<Define>,
153 registry: &'r Registry,
154}
155
156impl<'r> ProcessorStateBuilder<'r> {
157 pub fn new(registry: &'r Registry) -> Self {
158 let default = ProcessorStateBuilder::default();
159 Self {
160 registry,
161 ..default
162 }
163 }
164
165 pub fn registry<'s>(self, registry: &'s Registry) -> ProcessorStateBuilder<'s> {
166 ProcessorStateBuilder::<'s> {
167 registry,
168 core_profile: self.core_profile,
169 compatibility_profile: self.compatibility_profile,
170 es_profile: self.es_profile,
171 extensions: self.extensions,
172 definitions: self.definitions,
173 }
174 }
175
176 pub fn core_profile(self, core_profile: bool) -> Self {
177 Self {
178 core_profile,
179 ..self
180 }
181 }
182
183 pub fn compatibility_profile(self, compatibility_profile: bool) -> Self {
184 Self {
185 compatibility_profile,
186 ..self
187 }
188 }
189
190 pub fn es_profile(self, es_profile: bool) -> Self {
191 Self { es_profile, ..self }
192 }
193
194 pub fn extension(
195 mut self,
196 name: impl Into<ExtensionName>,
197 behavior: impl Into<ExtensionBehavior>,
198 ) -> Self {
199 self.extensions.push((name.into(), behavior.into()));
200 self
201 }
202
203 pub fn definition(mut self, definition: impl Into<Define>) -> Self {
204 self.definitions.push(definition.into());
205 self
206 }
207
208 pub fn finish(self) -> ProcessorState {
209 let one = DefineObject::one();
210
211 let mut state =
212 ProcessorState {
213 include_mode: IncludeMode::None,
215 definitions: self
219 .core_profile
220 .then(|| Define::object("GL_core_profile".into(), one.clone(), true))
221 .into_iter()
222 .chain(self.compatibility_profile.then(|| {
223 Define::object("GL_compatibility_profile".into(), one.clone(), true)
224 }))
225 .chain(
226 self.es_profile
227 .then(|| Define::object("GL_es_profile".into(), one.clone(), true)),
228 )
229 .chain(self.definitions)
230 .map(|definition| Definition::Regular(definition.into(), FileId::default()))
231 .chain([Definition::Line, Definition::File, Definition::Version])
232 .chain(self.registry.all().map(|spec| {
233 Definition::Regular(
234 Define::object(spec.name().as_ref().into(), one.clone(), true).into(),
235 FileId::default(),
236 )
237 }))
238 .map(|definition| (definition.name().into(), definition))
239 .collect(),
240 version: Version::default(),
241 cpp_style_line: false,
242 };
243
244 for (name, behavior) in self.extensions {
245 state.add_extension(&name, behavior);
246 }
247
248 state
249 }
250}
251
252impl From<ProcessorStateBuilder<'_>> for ProcessorState {
253 fn from(builder: ProcessorStateBuilder<'_>) -> Self {
254 builder.finish()
255 }
256}
257
258impl Default for ProcessorStateBuilder<'static> {
259 fn default() -> Self {
260 Self {
261 core_profile: true,
262 compatibility_profile: false,
263 es_profile: false,
264 extensions: Default::default(),
265 definitions: Default::default(),
266 registry: &*crate::exts::DEFAULT_REGISTRY,
267 }
268 }
269}