stuart_core/functions/parsers/
ifdefined.rs

1use crate::functions::{Function, FunctionParser};
2use crate::parse::{ParseError, RawFunction};
3use crate::process::stack::StackFrame;
4use crate::process::{ProcessError, Scope};
5use crate::{quiet_assert, TracebackError};
6
7use humphrey_json::Value;
8
9/// Parses the `ifdefined` function.
10pub struct IfDefinedParser;
11
12#[derive(Debug, Clone)]
13pub struct IfDefinedFunction {
14    variable_name: String,
15}
16
17impl FunctionParser for IfDefinedParser {
18    fn name(&self) -> &'static str {
19        "ifdefined"
20    }
21
22    fn parse(&self, raw: RawFunction) -> Result<Box<dyn Function>, ParseError> {
23        quiet_assert!(raw.positional_args.len() == 1)?;
24        quiet_assert!(raw.named_args.is_empty())?;
25
26        let variable_name = raw.positional_args[0]
27            .as_variable()
28            .ok_or(ParseError::InvalidArgument)?;
29
30        Ok(Box::new(IfDefinedFunction {
31            variable_name: variable_name.to_string(),
32        }))
33    }
34}
35
36impl Function for IfDefinedFunction {
37    fn name(&self) -> &'static str {
38        "ifdefined"
39    }
40
41    fn execute(&self, scope: &mut Scope) -> Result<(), TracebackError<ProcessError>> {
42        let self_token = scope.tokens.current().unwrap().clone();
43
44        let mut defined = scope
45            .get_variable(&self.variable_name)
46            .map(|v| !matches!(v, Value::Null))
47            .unwrap_or(false);
48
49        let frame = StackFrame::new(format!("ifdefined:{}", self.variable_name));
50
51        let stack_height = scope.stack.len();
52        scope.stack.push(frame);
53
54        while scope.stack.len() > stack_height {
55            let token = scope
56                .tokens
57                .next()
58                .ok_or_else(|| self_token.traceback(ProcessError::UnexpectedEndOfFile))?;
59
60            let function_name = token.as_function().map(|f| f.name().to_string());
61
62            if defined
63                || ((function_name == Some("end".to_string())
64                    || function_name == Some("else".to_string()))
65                    && scope.stack.len() == stack_height + 1)
66            {
67                token.process(scope)?;
68
69                if function_name == Some("else".to_string()) {
70                    defined = !defined;
71                }
72            }
73        }
74
75        Ok(())
76    }
77}