Skip to main content

reqlang_expr/
value.rs

1//! The core value type used in the virtual machine.
2
3use std::fmt::Display;
4
5use crate::{
6    builtins::BuiltinFn,
7    errors::{ExprResult, RuntimeError},
8    types::Type,
9};
10
11#[derive(Debug, Clone, PartialEq)]
12pub enum Value {
13    String(String),
14    Number(f64),
15    Fn(Box<BuiltinFn<'static>>),
16    Bool(bool),
17    Type(Box<Type>),
18}
19
20impl Value {
21    pub fn get_type(&self) -> Type {
22        self.clone().into()
23    }
24
25    pub fn get_string(&self) -> ExprResult<&str> {
26        match self {
27            Value::String(s) => Ok(s.as_str()),
28            _ => Err(vec![(
29                RuntimeError::TypeMismatch {
30                    expected: Type::String,
31                    actual: self.get_type(),
32                }
33                .into(),
34                0..0,
35            )]),
36        }
37    }
38
39    pub fn get_func(&self) -> ExprResult<Box<BuiltinFn<'_>>> {
40        match self {
41            Value::Fn(f) => Ok(f.clone()),
42            _ => Err(vec![(
43                RuntimeError::TypeMismatch {
44                    expected: Type::Fn {
45                        args: vec![],
46                        variadic_arg: Some(Type::Value.into()),
47                        returns: Type::Value.into(),
48                    },
49                    actual: self.get_type(),
50                }
51                .into(),
52                0..0,
53            )]),
54        }
55    }
56
57    pub fn get_bool(&self) -> ExprResult<bool> {
58        match self {
59            Value::Bool(s) => Ok(*s),
60            _ => Err(vec![(
61                RuntimeError::TypeMismatch {
62                    expected: Type::Bool,
63                    actual: self.get_type(),
64                }
65                .into(),
66                0..0,
67            )]),
68        }
69    }
70}
71
72impl Display for Value {
73    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
74        match self {
75            Value::String(string) => write!(f, "`{string}`"),
76            Value::Number(value) => write!(f, "{value}"),
77            Value::Fn(builtin) => write!(f, "{builtin:?}"),
78            Value::Bool(value) => write!(f, "{value}"),
79            Value::Type(ty) => write!(f, "Type<{ty}>"),
80        }
81    }
82}
83
84impl From<bool> for Value {
85    fn from(value: bool) -> Self {
86        Self::Bool(value)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    use pretty_assertions::assert_eq;
95
96    fn example_builtin(_args: Vec<Value>) -> ExprResult<Value> {
97        Ok(Value::String("".to_string()))
98    }
99
100    #[test]
101    fn get_bool_on_string() {
102        assert_eq!(
103            Err(vec![(
104                RuntimeError::TypeMismatch {
105                    expected: Type::Bool,
106                    actual: Type::String
107                }
108                .into(),
109                0..0
110            )]),
111            Value::String("string".to_string()).get_bool()
112        );
113    }
114
115    #[test]
116    fn get_bool_on_bool_true() {
117        assert_eq!(Ok(true), Value::Bool(true).get_bool());
118    }
119
120    #[test]
121    fn get_string_on_bool() {
122        assert_eq!(
123            Err(vec![(
124                RuntimeError::TypeMismatch {
125                    expected: Type::String,
126                    actual: Type::Bool
127                }
128                .into(),
129                0..0
130            )]),
131            Value::Bool(true).get_string()
132        );
133    }
134
135    #[test]
136    fn get_string_on_string() {
137        let value = Value::String("test".to_string());
138        assert_eq!(Ok("test"), value.get_string());
139    }
140
141    #[test]
142    fn get_func_on_string() {
143        let value = Value::String("not a function".to_string());
144        assert_eq!(
145            Err(vec![(
146                RuntimeError::TypeMismatch {
147                    expected: Type::Fn {
148                        args: vec![],
149                        variadic_arg: Some(Type::Value.into()),
150                        returns: Type::Value.into()
151                    },
152                    actual: Type::String
153                }
154                .into(),
155                0..0
156            )]),
157            value.get_func()
158        );
159    }
160
161    #[test]
162    fn get_func_on_func() {
163        let expected_fn: Box<BuiltinFn> = BuiltinFn {
164            name: "name",
165            args: &[],
166            return_type: Type::Unknown,
167            func: example_builtin,
168        }
169        .into();
170
171        let value = Value::Fn(expected_fn.clone());
172
173        assert_eq!(Ok(expected_fn), value.get_func());
174    }
175
176    #[test]
177    fn get_func_on_bool() {
178        let value = Value::Bool(true);
179        assert_eq!(
180            Err(vec![(
181                RuntimeError::TypeMismatch {
182                    expected: Type::Fn {
183                        args: vec![],
184                        variadic_arg: Some(Type::Value.into()),
185                        returns: Type::Value.into()
186                    },
187                    actual: Type::Bool
188                }
189                .into(),
190                0..0
191            )]),
192            value.get_func()
193        );
194    }
195}