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