glsl_lang_pp/processor/expand/
if_stack.rs

1use thiserror::Error;
2
3use crate::processor::event::ProcessingErrorKind;
4
5#[derive(Clone, Copy, PartialEq, Eq)]
6enum IfState {
7    /// No #if group of this level was included
8    None,
9    /// The current #if group of this level is included
10    Active { else_seen: bool },
11    /// One past #if group of this level was included, but not the current one
12    One { else_seen: bool },
13}
14
15impl IfState {
16    pub fn else_seen(&self) -> bool {
17        match self {
18            Self::None => false,
19            Self::Active { else_seen } | Self::One { else_seen } => *else_seen,
20        }
21    }
22
23    pub fn none(&self) -> bool {
24        matches!(self, Self::None)
25    }
26
27    pub fn active(&self) -> bool {
28        matches!(self, Self::Active { .. })
29    }
30
31    pub fn activate(self) -> Self {
32        match self {
33            Self::None => Self::Active { else_seen: false },
34            Self::Active { else_seen } | Self::One { else_seen } => Self::Active { else_seen },
35        }
36    }
37
38    pub fn deactivate(self) -> Self {
39        match self {
40            Self::Active { else_seen } => Self::One { else_seen },
41            other => other,
42        }
43    }
44
45    pub fn with_else_seen(self, active: bool) -> Self {
46        if active {
47            match self {
48                Self::None => Self::Active { else_seen: true },
49                Self::Active { .. } | Self::One { .. } => Self::One { else_seen: true },
50            }
51        } else {
52            Self::One { else_seen: true }
53        }
54    }
55}
56
57#[derive(Debug, Error)]
58#[allow(clippy::enum_variant_names)]
59pub enum IfError {
60    #[error("unmatched #elif directive")]
61    ExtraElif,
62    #[error("unmatched #else directive")]
63    ExtraElse,
64    #[error("unmatched #endif directive")]
65    ExtraEndIf,
66}
67
68impl From<IfError> for ProcessingErrorKind {
69    fn from(value: IfError) -> Self {
70        match value {
71            IfError::ExtraElif => ProcessingErrorKind::ExtraElif,
72            IfError::ExtraElse => ProcessingErrorKind::ExtraElse,
73            IfError::ExtraEndIf => ProcessingErrorKind::ExtraEndIf,
74        }
75    }
76}
77
78pub struct IfStack {
79    stack: Vec<IfState>,
80}
81
82impl IfStack {
83    pub fn new() -> Self {
84        Self {
85            stack: Vec::with_capacity(4),
86        }
87    }
88
89    pub fn if_group_active(&self) -> bool {
90        let len = self.stack.len();
91        if len >= 2 {
92            unsafe { self.stack.get_unchecked(len - 2) }.active()
93        } else {
94            true
95        }
96    }
97
98    pub fn active(&self) -> bool {
99        self.stack.last().map(|top| top.active()).unwrap_or(true)
100    }
101
102    pub fn none(&self) -> bool {
103        self.stack.last().map(|top| top.none()).unwrap_or(false)
104    }
105
106    pub fn on_if_like(&mut self, expr: bool) {
107        if self.active() && expr {
108            self.stack.push(IfState::Active { else_seen: false });
109        } else {
110            self.stack.push(IfState::None);
111        }
112    }
113
114    pub fn on_elif(&mut self, expr: bool) -> Result<(), IfError> {
115        // Pop the current state
116        let top = if let Some(top) = self.stack.pop() {
117            top
118        } else {
119            return Err(IfError::ExtraElif);
120        };
121
122        // Check that we haven't already seen an else
123        if top.else_seen() {
124            // If that's the case, just ignore the next block
125            self.stack.push(IfState::One { else_seen: true });
126            return Err(IfError::ExtraElif);
127        }
128
129        // Is the next block active?
130        // Note that we use active, since we popped the current level off
131        if self.active() && expr {
132            self.stack.push(top.activate());
133        } else {
134            self.stack.push(top.deactivate());
135        }
136
137        Ok(())
138    }
139
140    pub fn on_else(&mut self) -> Result<(), IfError> {
141        // Pop the current state
142        let top = if let Some(top) = self.stack.pop() {
143            top
144        } else {
145            return Err(IfError::ExtraElse);
146        };
147
148        // Check that we haven't already seen an else
149        if top.else_seen() {
150            // If that's the case, just ignore the next block
151            self.stack.push(IfState::One { else_seen: true });
152            return Err(IfError::ExtraElif);
153        }
154
155        // The next block will be active if no other block before was active
156        self.stack.push(top.with_else_seen(self.active()));
157
158        Ok(())
159    }
160
161    pub fn on_endif(&mut self) -> Result<(), IfError> {
162        // Pop the current state
163        let _top = if let Some(top) = self.stack.pop() {
164            top
165        } else {
166            return Err(IfError::ExtraEndIf);
167        };
168
169        // Nothing more to do
170
171        Ok(())
172    }
173}