Skip to main content

kaish_kernel/interpreter/
control_flow.rs

1//! Control flow signals for loops and functions.
2//!
3//! These types allow break, continue, return, and exit to propagate
4//! through the statement execution stack.
5
6use super::result::ExecResult;
7
8/// Control flow signal from statement execution.
9///
10/// Normal execution returns `Normal(result)`. Loop control uses `Break` and `Continue`.
11/// Function returns use `Return`, and script exits use `Exit`.
12#[derive(Debug, Clone)]
13pub enum ControlFlow {
14    /// Normal completion with a result.
15    Normal(ExecResult),
16    /// Break out of loop(s). `levels` indicates how many loops to break out of.
17    Break { levels: usize, result: ExecResult },
18    /// Continue to next iteration of loop(s). `levels` indicates how many loops to skip.
19    Continue { levels: usize, result: ExecResult },
20    /// Return from a function with a result.
21    Return { value: ExecResult },
22    /// Exit the entire script with an exit code.
23    Exit { code: i64 },
24}
25
26impl ControlFlow {
27    /// Create a normal control flow with a successful result.
28    pub fn ok(result: ExecResult) -> Self {
29        ControlFlow::Normal(result)
30    }
31
32    /// Create a break with 1 level.
33    pub fn break_one() -> Self {
34        ControlFlow::Break {
35            levels: 1,
36            result: ExecResult::success(""),
37        }
38    }
39
40    /// Create a break with n levels.
41    pub fn break_n(n: usize) -> Self {
42        ControlFlow::Break {
43            levels: n,
44            result: ExecResult::success(""),
45        }
46    }
47
48    /// Create a continue with 1 level.
49    pub fn continue_one() -> Self {
50        ControlFlow::Continue {
51            levels: 1,
52            result: ExecResult::success(""),
53        }
54    }
55
56    /// Create a continue with n levels.
57    pub fn continue_n(n: usize) -> Self {
58        ControlFlow::Continue {
59            levels: n,
60            result: ExecResult::success(""),
61        }
62    }
63
64    /// Create a return with a value.
65    pub fn return_value(value: ExecResult) -> Self {
66        ControlFlow::Return { value }
67    }
68
69    /// Create an exit with a code.
70    pub fn exit_code(code: i64) -> Self {
71        ControlFlow::Exit { code }
72    }
73
74    /// Check if this is normal flow.
75    pub fn is_normal(&self) -> bool {
76        matches!(self, ControlFlow::Normal(_))
77    }
78
79    /// Get the result if this is normal flow.
80    pub fn into_result(self) -> Option<ExecResult> {
81        match self {
82            ControlFlow::Normal(r) => Some(r),
83            _ => None,
84        }
85    }
86
87    /// Decrement break/continue levels by 1 and return whether we should stop here.
88    ///
89    /// Returns `true` if the break/continue should be handled at this level,
90    /// `false` if it should propagate further.
91    pub fn decrement_level(&mut self) -> bool {
92        match self {
93            ControlFlow::Break { levels, .. } | ControlFlow::Continue { levels, .. } => {
94                if *levels <= 1 {
95                    true
96                } else {
97                    *levels -= 1;
98                    false
99                }
100            }
101            _ => false,
102        }
103    }
104}
105
106impl Default for ControlFlow {
107    fn default() -> Self {
108        ControlFlow::Normal(ExecResult::success(""))
109    }
110}
111
112impl From<ExecResult> for ControlFlow {
113    fn from(result: ExecResult) -> Self {
114        ControlFlow::Normal(result)
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_normal_flow() {
124        let flow = ControlFlow::ok(ExecResult::success("test"));
125        assert!(flow.is_normal());
126    }
127
128    #[test]
129    fn test_break_decrement() {
130        let mut flow = ControlFlow::break_n(3);
131        assert!(!flow.decrement_level()); // 3 -> 2
132        assert!(!flow.decrement_level()); // 2 -> 1
133        assert!(flow.decrement_level()); // 1 -> should stop
134    }
135
136    #[test]
137    fn test_continue_decrement() {
138        let mut flow = ControlFlow::continue_n(2);
139        assert!(!flow.decrement_level()); // 2 -> 1
140        assert!(flow.decrement_level()); // 1 -> should stop
141    }
142
143    #[test]
144    fn test_from_exec_result() {
145        let result = ExecResult::success("hello");
146        let flow: ControlFlow = result.into();
147        assert!(flow.is_normal());
148    }
149}