Skip to main content

nifty_interp/machine/
stmt.rs

1//! Statement execution.
2
3use nifty_check::hir::Stmt;
4
5use super::walk::rounds;
6use super::{Frame, Machine, Result, Slot, binary};
7use crate::value::Value;
8
9/// How a block finished: by running to its end, or by `return`.
10#[derive(Debug)]
11pub(crate) enum Flow {
12    Normal,
13    Return(Value),
14}
15
16impl Machine {
17    pub(crate) fn exec_block(&self, block: &[Stmt], frame: &mut Frame) -> Result<Flow> {
18        for stmt in block {
19            if let Flow::Return(value) = self.exec(stmt, frame)? {
20                return Ok(Flow::Return(value));
21            }
22        }
23        Ok(Flow::Normal)
24    }
25
26    fn exec(&self, stmt: &Stmt, frame: &mut Frame) -> Result<Flow> {
27        match stmt {
28            Stmt::Let(local, value) => {
29                frame[local.0] = Slot::Value(self.eval(value, frame)?);
30            }
31            Stmt::Assign { place, op, value } => {
32                let mut value = self.eval(value, frame)?;
33                let location = self.locate(place, frame)?;
34                if let Some(op) = op {
35                    let current = self.read(&location, frame, place.span)?;
36                    value = binary(*op, &current, &value, place.span)?;
37                }
38                self.write(&location, frame, value, place.span)?;
39            }
40            Stmt::If {
41                branches,
42                otherwise,
43            } => {
44                for (condition, body) in branches {
45                    if self.eval(condition, frame)?.as_bool() {
46                        return self.exec_block(body, frame);
47                    }
48                }
49                if let Some(otherwise) = otherwise {
50                    return self.exec_block(otherwise, frame);
51                }
52            }
53            Stmt::For { head, body } => {
54                let walk = self.walk(&head.iterable, frame)?;
55                for round in rounds() {
56                    if !self.enter_round(head, &walk, round, frame)? {
57                        break;
58                    }
59                    if let Flow::Return(value) = self.exec_block(body, frame)? {
60                        return Ok(Flow::Return(value));
61                    }
62                }
63            }
64            Stmt::Return(value) => {
65                let value = match value {
66                    Some(value) => self.eval(value, frame)?,
67                    None => Value::Void,
68                };
69                return Ok(Flow::Return(value));
70            }
71            Stmt::Expr(expr) => {
72                self.eval(expr, frame)?;
73            }
74        }
75        Ok(Flow::Normal)
76    }
77}