Skip to main content

bock_interp/
error.rs

1//! Runtime error type for the Bock interpreter.
2
3use thiserror::Error;
4
5use crate::Value;
6
7/// An error that can occur during Bock program evaluation.
8///
9/// Three variants (`Return`, `Break`, `Continue`) are control-flow signals
10/// rather than true errors; they are propagated up through `eval_expr` and
11/// caught at the appropriate statement handler (function body, loop body).
12#[derive(Debug, Error, PartialEq)]
13pub enum RuntimeError {
14    // ── Type errors ───────────────────────────────────────────────────────
15    #[error("type error: {0}")]
16    TypeError(String),
17
18    // ── Variable / name errors ────────────────────────────────────────────
19    #[error("undefined variable: {name}")]
20    UndefinedVariable { name: String },
21
22    // ── Arithmetic errors ─────────────────────────────────────────────────
23    #[error("division by zero")]
24    DivisionByZero,
25
26    #[error("integer overflow")]
27    IntOverflow,
28
29    // ── Collection errors ─────────────────────────────────────────────────
30    #[error("index out of bounds: index {index} for length {len}")]
31    IndexOutOfBounds { index: i64, len: usize },
32
33    #[error("field not found: {field} on {type_name}")]
34    FieldNotFound { field: String, type_name: String },
35
36    // ── Call errors ───────────────────────────────────────────────────────
37    #[error("not callable: {value}")]
38    NotCallable { value: String },
39
40    #[error("arity mismatch: expected {expected} args, got {got}")]
41    ArityMismatch { expected: usize, got: usize },
42
43    // ── Error propagation (`?`) ───────────────────────────────────────────
44    /// Raised when `?` is applied to `None` or `Err(e)`. Caught by the
45    /// enclosing function body to propagate the error outward.
46    #[error("propagated error: {0}")]
47    Propagated(Box<Value>),
48
49    // ── Literal parse errors ──────────────────────────────────────────────
50    #[error("integer literal parse failed: {0}")]
51    IntParseFailed(String),
52
53    #[error("float literal parse failed: {0}")]
54    FloatParseFailed(String),
55
56    // ── Control-flow signals ──────────────────────────────────────────────
57    /// Carries the return value from a `return` expression.
58    #[error("return")]
59    Return(Box<Value>),
60
61    /// Carries an optional `break` value from a `break` expression.
62    #[error("break")]
63    Break(Option<Box<Value>>),
64
65    /// Signals `continue` inside a loop.
66    #[error("continue")]
67    Continue,
68
69    // ── Misc ──────────────────────────────────────────────────────────────
70    #[error("unreachable code reached")]
71    Unreachable,
72
73    #[error("match failed: no arm matched the scrutinee")]
74    MatchFailed,
75
76    #[error("no handler for effect `{effect}`: provide a handler via a `handling` block, module-level `handle`, or project config")]
77    NoEffectHandler { effect: String },
78
79    #[error("not implemented: {0}")]
80    NotImplemented(String),
81
82    // ── Test assertion errors ─────────────────────────────────────────────
83    /// Raised when a test assertion (e.g., `expect(x).to_equal(y)`) fails.
84    #[error("assertion failed: {0}")]
85    AssertionFailed(String),
86}