doge_runtime/
functions.rs1use std::rc::Rc;
2
3use crate::error::{DogeError, DogeResult};
4use crate::value::{Cell, FunctionData, Value};
5
6pub fn cell_get(cell: &Cell) -> Value {
8 cell.borrow().clone()
9}
10
11pub fn cell_set(cell: &Cell, value: Value) {
14 *cell.borrow_mut() = value;
15}
16
17pub fn callee_function(value: &Value) -> DogeResult<Rc<FunctionData>> {
22 match value {
23 Value::Function(f) | Value::Class(f) => Ok(Rc::clone(f)),
24 other => Err(DogeError::type_error(format!(
25 "cannot call {} — it is not a function",
26 other.describe()
27 ))),
28 }
29}
30
31pub(crate) fn arity_phrase(subject: &str, min: usize, max: Option<usize>, got: usize) -> String {
35 let noun = |count: usize| if count == 1 { "argument" } else { "arguments" };
36 match max {
37 Some(max) if max == min => {
38 format!("{subject} takes {min} {}, got {got}", noun(min))
39 }
40 Some(max) => format!("{subject} takes {min} to {max} arguments, got {got}"),
41 None => format!("{subject} takes at least {min} {}, got {got}", noun(min)),
42 }
43}
44
45pub fn function_arity_error(name: &str, min: usize, max: Option<usize>, got: usize) -> DogeError {
49 DogeError::type_error(arity_phrase(name, min, max, got))
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use crate::error::ErrorKind;
56
57 #[test]
58 fn cell_round_trips_and_shares() {
59 let cell: Cell = Rc::new(std::cell::RefCell::new(Value::int(1)));
60 assert!(crate::values_equal(&cell_get(&cell), &Value::int(1)));
61 let shared = Rc::clone(&cell);
62 cell_set(&cell, Value::int(2));
63 assert!(crate::values_equal(&cell_get(&shared), &Value::int(2)));
65 }
66
67 #[test]
68 fn callee_function_unwraps_a_function() {
69 let f = Value::function(3, "greet", vec![]);
70 let got = callee_function(&f).expect("a function");
71 assert_eq!(got.fn_id, 3);
72 }
73
74 #[test]
75 fn calling_a_non_function_is_a_catchable_type_error() {
76 let err = callee_function(&Value::int(1)).unwrap_err();
77 assert_eq!(err.kind, ErrorKind::TypeError);
78 assert_eq!(err.message, "cannot call an Int — it is not a function");
79 }
80
81 #[test]
82 fn function_arity_error_matches_the_user_wording() {
83 assert_eq!(
84 function_arity_error("greet", 2, Some(2), 1).message,
85 "greet takes 2 arguments, got 1"
86 );
87 assert_eq!(
88 function_arity_error("f", 1, Some(1), 0).message,
89 "f takes 1 argument, got 0"
90 );
91 }
92
93 #[test]
94 fn function_arity_error_reports_ranges_and_variadics() {
95 assert_eq!(
96 function_arity_error("greet", 1, Some(3), 4).message,
97 "greet takes 1 to 3 arguments, got 4"
98 );
99 assert_eq!(
100 function_arity_error("party", 1, None, 0).message,
101 "party takes at least 1 argument, got 0"
102 );
103 }
104}