hemtt_preprocessor/
context.rs

1use std::{
2    collections::HashMap,
3    sync::{atomic::AtomicUsize, Arc},
4};
5
6use hemtt_tokens::{Symbol, Token};
7
8use crate::{defines::Defines, ifstate::IfStates, Error};
9
10const BUILTIN: [&str; 37] = [
11    "__LINE__",
12    "__FILE__",
13    "__DATE_ARR__",
14    "__DATE_STR__",
15    "__DATE_STR_ISO8601__",
16    "__TIME__",
17    "__TIME_UTC__",
18    "__TIMESTAMP_UTC__",
19    "__COUNTER__",
20    "__COUNTER_RESET__",
21    "__RAND_INT8__",
22    "__RAND_INT16__",
23    "__RAND_INT32__",
24    "__RAND_INT64__",
25    "__RAND_UINT8__",
26    "__RAND_UINT16__",
27    "__RAND_UINT32__",
28    "__RAND_UINT64__",
29    "__ARMA__",
30    "__ARMA3__",
31    "__A3_DEBUG__",
32    "__HEMTT__",
33    "__HEMTT_DEBUG__",
34    "__HEMTT_VERSION__",
35    "__HEMTT_VERSION_MAJ__",
36    "__HEMTT_VERSION_MIN__",
37    "__HEMTT_VERSION_REV__",
38    "__HEMTT_VERSION_BUILD__",
39    "__HEMTT_PROJECT_NAME__",
40    "__HEMTT_PROJECT_VERSION__",
41    "__HEMTT_PROJECT_VERSION_MAJ__",
42    "__HEMTT_PROJECT_VERSION_MIN__",
43    "__HEMTT_PROJECT_VERSION_REV__",
44    "__HEMTT_PROJECT_VERSION_BUILD__",
45    "__HEMTT_PROJECT_MAINPREFIX__",
46    "__HEMTT_PROJECT_PREFIX__",
47    "__HEMTT_PROJECT_AUTHOR__",
48];
49
50#[derive(Clone, Debug)]
51/// Preprocessor context
52pub struct Context<'a> {
53    ifstates: IfStates,
54    definitions: Defines,
55    entry: String,
56    current_file: String,
57    counter: Arc<AtomicUsize>,
58    trace: Vec<Token>,
59    parent: Option<&'a Self>,
60}
61
62impl<'a> Context<'a> {
63    #[must_use]
64    /// Create a new `Context`
65    pub fn new(entry: String) -> Self {
66        Self {
67            ifstates: IfStates::new(),
68            definitions: HashMap::new(),
69            current_file: entry.clone(),
70            entry,
71            counter: Arc::new(AtomicUsize::new(0)),
72            trace: Vec::new(),
73            parent: None,
74        }
75    }
76
77    #[must_use]
78    /// Create a new `Context` from a parent
79    pub fn stack(&'a self, source: Token) -> Context<'a> {
80        Self {
81            ifstates: self.ifstates.clone(),
82            definitions: HashMap::new(),
83            current_file: self.current_file.clone(),
84            entry: self.entry.clone(),
85            counter: self.counter.clone(),
86            trace: {
87                let mut trace = self.trace.clone();
88                trace.push(source);
89                trace
90            },
91            parent: Some(self),
92        }
93    }
94
95    /// Push a [`Token`] to the trace
96    pub fn push(&mut self, source: Token) {
97        self.trace.push(source);
98    }
99
100    /// Pop a [`Token`] from the trace
101    pub fn pop(&mut self) -> Option<Token> {
102        self.trace.pop()
103    }
104
105    #[must_use]
106    /// Get the current trace
107    pub fn trace(&self) -> Vec<Token> {
108        self.trace.clone()
109    }
110
111    #[must_use]
112    /// Get the current [`IfState`](crate::ifstate::IfState)
113    pub const fn ifstates(&self) -> &IfStates {
114        &self.ifstates
115    }
116
117    /// Get the current [`IfState`](crate::ifstate::IfState) mutably
118    pub fn ifstates_mut(&mut self) -> &mut IfStates {
119        &mut self.ifstates
120    }
121
122    #[must_use]
123    /// Get the current [`Definition`]s
124    pub const fn definitions(&self) -> &Defines {
125        &self.definitions
126    }
127
128    /// Get the current [`Definition`]s mutably
129    pub fn definitions_mut(&mut self) -> &mut Defines {
130        &mut self.definitions
131    }
132
133    #[must_use]
134    /// Get the entry name
135    pub const fn entry(&self) -> &String {
136        &self.entry
137    }
138
139    #[must_use]
140    /// Get the current file
141    pub const fn current_file(&self) -> &String {
142        &self.current_file
143    }
144
145    /// Set the current file
146    pub fn set_current_file(&mut self, file: String) {
147        self.current_file = file;
148    }
149
150    /// Define a macro [`Definition`]
151    ///
152    /// # Errors
153    /// [`Error::ChangeBuiltin`] if the macro is a builtin macro
154    pub fn define(
155        &mut self,
156        ident: String,
157        source: Token,
158        definition: Definition,
159    ) -> Result<(), Error> {
160        if BUILTIN.contains(&ident.as_str()) {
161            return Err(Error::ChangeBuiltin {
162                token: Box::new(source),
163                trace: self.trace(),
164            });
165        }
166        self.definitions.insert(ident, (source, definition));
167        Ok(())
168    }
169
170    /// Undefine a macro [`Definition`]
171    ///
172    /// # Errors
173    /// [`Error::ChangeBuiltin`] if the macro is a builtin macro
174    pub fn undefine(
175        &mut self,
176        ident: &str,
177        source: &Token,
178    ) -> Result<Option<(Token, Definition)>, Error> {
179        if BUILTIN.contains(&ident) {
180            return Err(Error::ChangeBuiltin {
181                token: Box::new(source.clone()),
182                trace: self.trace(),
183            });
184        }
185        Ok(self.definitions.remove(ident))
186    }
187
188    #[must_use]
189    /// Check if a macro [`Definition`] exists
190    pub fn has(&self, ident: &str) -> bool {
191        self.definitions.contains_key(ident)
192    }
193
194    #[must_use]
195    /// Get a macro [`Definition`]
196    pub fn get(&self, ident: &str, token: &Token) -> Option<(Token, Definition)> {
197        match ident {
198            "__LINE__" => Some((
199                Token::builtin(Some(Box::new(token.clone()))),
200                Definition::Value(vec![Token::new(
201                    Symbol::Word(token.source().start().1 .0.to_string()),
202                    token.source().clone(),
203                    Some(Box::new(token.clone())),
204                )]),
205            )),
206            "__FILE__" => Some((
207                Token::builtin(Some(Box::new(token.clone()))),
208                Definition::Value(vec![Token::new(
209                    Symbol::Word(token.source().path().to_string().replace('\\', "/")),
210                    token.source().clone(),
211                    Some(Box::new(token.clone())),
212                )]),
213            )),
214            "__COUNTER__" => Some((
215                Token::builtin(Some(Box::new(token.clone()))),
216                Definition::Value(vec![Token::new(
217                    Symbol::Word(
218                        self.counter
219                            .fetch_add(1, std::sync::atomic::Ordering::SeqCst)
220                            .to_string(),
221                    ),
222                    token.source().clone(),
223                    Some(Box::new(token.clone())),
224                )]),
225            )),
226            "__COUNTER_RESET__" => {
227                self.counter.store(0, std::sync::atomic::Ordering::SeqCst);
228                Some((
229                    Token::builtin(Some(Box::new(token.clone()))),
230                    Definition::Value(vec![Token::new(
231                        Symbol::Void,
232                        token.source().clone(),
233                        Some(Box::new(token.clone())),
234                    )]),
235                ))
236            }
237            "__ARMA__" | "__ARMA3__" | "__HEMTT__" => Some((
238                Token::builtin(Some(Box::new(token.clone()))),
239                Definition::Value(vec![Token::new(
240                    Symbol::Digit(1),
241                    token.source().clone(),
242                    Some(Box::new(token.clone())),
243                )]),
244            )),
245            _ => {
246                // get locally or from parent
247                let mut context = self;
248                loop {
249                    if let Some((source, definition)) = context.definitions.get(ident) {
250                        return Some((source.clone(), definition.clone()));
251                    }
252                    if let Some(parent) = &context.parent {
253                        context = parent;
254                    } else {
255                        break;
256                    }
257                }
258                None
259            }
260        }
261    }
262}
263
264#[derive(Clone, Debug, PartialEq, Eq)]
265/// A macro definition
266pub enum Definition {
267    /// A [`FunctionDefinition`] that takes parameters
268    Function(FunctionDefinition),
269    /// A value that is a list of [`Token`]s to be added at the call site
270    Value(Vec<Token>),
271    /// A flag that can be checked with `#ifdef`
272    /// Tokens are only used for error reporting
273    Unit(Vec<Token>),
274}
275
276impl Definition {
277    #[must_use]
278    /// Check if the definition is a [`FunctionDefinition`]
279    pub const fn is_function(&self) -> bool {
280        matches!(self, Self::Function(_))
281    }
282
283    #[must_use]
284    /// Check if the definition is a value
285    pub const fn is_value(&self) -> bool {
286        matches!(self, Self::Value(_))
287    }
288
289    #[must_use]
290    /// Check if the definition is a flag
291    pub const fn is_unit(&self) -> bool {
292        matches!(self, Self::Unit(_))
293    }
294}
295
296#[derive(Clone, Debug, PartialEq, Eq)]
297/// A function definition
298///
299/// # Examples
300///
301/// ```cpp
302/// #define QUOTE(x) #x
303/// #define FOO(a, b) QUOTE(a + b)
304/// my_value = FOO(1, 2);
305/// ```
306pub struct FunctionDefinition {
307    parameters: Vec<Token>,
308    body: Vec<Token>,
309}
310
311impl FunctionDefinition {
312    #[must_use]
313    /// Create a new [`FunctionDefinition`]
314    pub fn new(parameters: Vec<Token>, body: Vec<Token>) -> Self {
315        Self { parameters, body }
316    }
317
318    #[must_use]
319    /// Get the parameter [`Token`]s
320    pub fn parameters(&self) -> &[Token] {
321        &self.parameters
322    }
323
324    #[must_use]
325    /// Get the body [`Token`]s
326    pub fn body(&self) -> &[Token] {
327        &self.body
328    }
329}