summaryrefslogtreecommitdiff
path: root/src/eval/control.rs
blob: 166676d49e1841fa85777bf5ae9d308bd98ba512 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use super::{ops, EvalResult, Value};
use crate::diag::{At, TypError};
use crate::syntax::Span;

/// A control flow event that occurred during evaluation.
#[derive(Clone, Debug, PartialEq)]
pub enum Control {
    /// Stop iteration in a loop.
    Break(Value, Span),
    /// Skip the remainder of the current iteration in a loop.
    Continue(Value, Span),
    /// Stop execution of a function early, returning a value. The bool
    /// indicates whether this was an explicit return with value.
    Return(Value, bool, Span),
    /// Stop the execution because an error occurred.
    Err(TypError),
}

impl From<TypError> for Control {
    fn from(error: TypError) -> Self {
        Self::Err(error)
    }
}

impl From<Control> for TypError {
    fn from(control: Control) -> Self {
        match control {
            Control::Break(_, span) => {
                error!(span, "cannot break outside of loop")
            }
            Control::Continue(_, span) => {
                error!(span, "cannot continue outside of loop")
            }
            Control::Return(_, _, span) => {
                error!(span, "cannot return outside of function")
            }
            Control::Err(e) => e,
        }
    }
}

/// Join a value with an evaluated result.
pub(super) fn join_result(
    prev: Value,
    result: EvalResult<Value>,
    result_span: Span,
) -> EvalResult<Value> {
    match result {
        Ok(value) => Ok(ops::join(prev, value).at(result_span)?),
        Err(Control::Break(value, span)) => Err(Control::Break(
            ops::join(prev, value).at(result_span)?,
            span,
        )),
        Err(Control::Continue(value, span)) => Err(Control::Continue(
            ops::join(prev, value).at(result_span)?,
            span,
        )),
        Err(Control::Return(value, false, span)) => Err(Control::Return(
            ops::join(prev, value).at(result_span)?,
            false,
            span,
        )),
        other => other,
    }
}