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