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    ///
24    /// `result` carries the output produced before the exit. An immediate exit
25    /// stops the script; it does not discard the iterations that already ran.
26    /// `code` stays authoritative for the script's status — `result.code` is
27    /// never read.
28    Exit { code: i64, result: ExecResult },
29}
30
31impl ControlFlow {
32    /// Create a normal control flow with a successful result.
33    pub fn ok(result: ExecResult) -> Self {
34        ControlFlow::Normal(result)
35    }
36
37    /// Create a break with 1 level.
38    pub fn break_one() -> Self {
39        ControlFlow::Break {
40            levels: 1,
41            result: ExecResult::success(""),
42        }
43    }
44
45    /// Create a break with n levels.
46    pub fn break_n(n: usize) -> Self {
47        ControlFlow::Break {
48            levels: n,
49            result: ExecResult::success(""),
50        }
51    }
52
53    /// Create a continue with 1 level.
54    pub fn continue_one() -> Self {
55        ControlFlow::Continue {
56            levels: 1,
57            result: ExecResult::success(""),
58        }
59    }
60
61    /// Create a continue with n levels.
62    pub fn continue_n(n: usize) -> Self {
63        ControlFlow::Continue {
64            levels: n,
65            result: ExecResult::success(""),
66        }
67    }
68
69    /// Create a return with a value.
70    pub fn return_value(value: ExecResult) -> Self {
71        ControlFlow::Return { value }
72    }
73
74    /// Create an exit with a code, carrying no output yet.
75    ///
76    /// Each loop the exit passes through folds its own accumulated output in,
77    /// so the output arrives with the signal rather than being left behind.
78    pub fn exit_code(code: i64) -> Self {
79        ControlFlow::Exit {
80            code,
81            result: ExecResult::success(""),
82        }
83    }
84
85    /// Check if this is normal flow.
86    pub fn is_normal(&self) -> bool {
87        matches!(self, ControlFlow::Normal(_))
88    }
89
90    /// Get the result if this is normal flow.
91    pub fn into_result(self) -> Option<ExecResult> {
92        match self {
93            ControlFlow::Normal(r) => Some(r),
94            _ => None,
95        }
96    }
97
98    /// Decrement break/continue levels by 1 and return whether we should stop here.
99    ///
100    /// Returns `true` if the break/continue should be handled at this level,
101    /// `false` if it should propagate further.
102    pub fn decrement_level(&mut self) -> bool {
103        match self {
104            ControlFlow::Break { levels, .. } | ControlFlow::Continue { levels, .. } => {
105                if *levels <= 1 {
106                    true
107                } else {
108                    *levels -= 1;
109                    false
110                }
111            }
112            _ => false,
113        }
114    }
115}
116
117impl Default for ControlFlow {
118    fn default() -> Self {
119        ControlFlow::Normal(ExecResult::success(""))
120    }
121}
122
123impl From<ExecResult> for ControlFlow {
124    fn from(result: ExecResult) -> Self {
125        ControlFlow::Normal(result)
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_normal_flow() {
135        let flow = ControlFlow::ok(ExecResult::success("test"));
136        assert!(flow.is_normal());
137    }
138
139    #[test]
140    fn test_break_decrement() {
141        let mut flow = ControlFlow::break_n(3);
142        assert!(!flow.decrement_level()); // 3 -> 2
143        assert!(!flow.decrement_level()); // 2 -> 1
144        assert!(flow.decrement_level()); // 1 -> should stop
145    }
146
147    #[test]
148    fn test_continue_decrement() {
149        let mut flow = ControlFlow::continue_n(2);
150        assert!(!flow.decrement_level()); // 2 -> 1
151        assert!(flow.decrement_level()); // 1 -> should stop
152    }
153
154    #[test]
155    fn test_from_exec_result() {
156        let result = ExecResult::success("hello");
157        let flow: ControlFlow = result.into();
158        assert!(flow.is_normal());
159    }
160}