1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::rc::Rc;
4
5use crate::error::{ErrorData, ErrorKind};
6use crate::ordered_map::OrderedMap;
7
8pub type Cell = Rc<RefCell<Value>>;
12
13#[derive(Debug, Clone)]
15pub enum Value {
16 Int(i64),
17 Float(f64),
18 Str(Rc<str>),
19 Bool(bool),
20 None,
21 List(Rc<RefCell<Vec<Value>>>),
22 Dict(Rc<RefCell<OrderedMap>>),
23 Object(Rc<RefCell<ObjectData>>),
24 Function(Rc<FunctionData>),
25 Class(Rc<FunctionData>),
31 BoundMethod(Rc<BoundMethodData>),
38 Error(Rc<ErrorData>),
39}
40
41#[derive(Debug)]
45pub struct BoundMethodData {
46 pub receiver: Value,
47 pub method: Rc<str>,
48}
49
50#[derive(Debug)]
55pub struct FunctionData {
56 pub fn_id: u32,
57 pub name: Rc<str>,
58 pub captures: Vec<Cell>,
59}
60
61#[derive(Debug)]
65pub struct ObjectData {
66 pub class_id: u32,
67 pub class_name: Rc<str>,
68 pub fields: HashMap<String, Value>,
69}
70
71impl Value {
72 pub fn str(s: impl AsRef<str>) -> Value {
74 Value::Str(Rc::from(s.as_ref()))
75 }
76
77 pub fn list(items: Vec<Value>) -> Value {
79 Value::List(Rc::new(RefCell::new(items)))
80 }
81
82 pub fn dict(entries: OrderedMap) -> Value {
84 Value::Dict(Rc::new(RefCell::new(entries)))
85 }
86
87 pub fn object(class_id: u32, class_name: &str) -> Value {
90 Value::Object(Rc::new(RefCell::new(ObjectData {
91 class_id,
92 class_name: Rc::from(class_name),
93 fields: HashMap::new(),
94 })))
95 }
96
97 pub fn error(kind: ErrorKind, message: &str, file: Rc<str>, line: u32) -> Value {
101 Value::Error(Rc::new(ErrorData {
102 kind,
103 message: Rc::from(message),
104 file,
105 line,
106 }))
107 }
108
109 pub fn function(fn_id: u32, name: &str, captures: Vec<Cell>) -> Value {
113 Value::Function(Rc::new(FunctionData {
114 fn_id,
115 name: Rc::from(name),
116 captures,
117 }))
118 }
119
120 pub fn bound_method(receiver: Value, method: &str) -> Value {
124 Value::BoundMethod(Rc::new(BoundMethodData {
125 receiver,
126 method: Rc::from(method),
127 }))
128 }
129
130 pub fn class(fn_id: u32, name: &str) -> Value {
134 Value::Class(Rc::new(FunctionData {
135 fn_id,
136 name: Rc::from(name),
137 captures: Vec::new(),
138 }))
139 }
140
141 pub fn dict_from_pairs(pairs: Vec<(Value, Value)>) -> crate::error::DogeResult {
145 let mut entries = OrderedMap::new();
146 for (key, value) in pairs {
147 match key {
148 Value::Str(k) => {
149 entries.insert(k.to_string(), value);
150 }
151 other => {
152 return Err(crate::error::DogeError::type_error(format!(
153 "dict keys must be a Str, got {}",
154 other.describe()
155 )))
156 }
157 }
158 }
159 Ok(Value::dict(entries))
160 }
161
162 pub fn truthy(&self) -> bool {
165 match self {
166 Value::Int(n) => *n != 0,
167 Value::Float(f) => *f != 0.0,
168 Value::Str(s) => !s.is_empty(),
169 Value::Bool(b) => *b,
170 Value::None => false,
171 Value::List(items) => !items.borrow().is_empty(),
172 Value::Dict(entries) => !entries.borrow().is_empty(),
173 Value::Object(_) => true,
174 Value::Function(_) => true,
175 Value::Class(_) => true,
176 Value::BoundMethod(_) => true,
177 Value::Error(_) => true,
178 }
179 }
180
181 pub fn type_name(&self) -> &'static str {
183 match self {
184 Value::Int(_) => "Int",
185 Value::Float(_) => "Float",
186 Value::Str(_) => "Str",
187 Value::Bool(_) => "Bool",
188 Value::None => "None",
189 Value::List(_) => "List",
190 Value::Dict(_) => "Dict",
191 Value::Object(_) => "Object",
192 Value::Function(_) => "Function",
193 Value::Class(_) => "Class",
194 Value::BoundMethod(_) => "Method",
195 Value::Error(_) => "Error",
196 }
197 }
198
199 pub fn describe(&self) -> String {
202 let name = self.type_name();
203 let article = match name.chars().next() {
204 Some('A' | 'E' | 'I' | 'O' | 'U') => "an",
205 _ => "a",
206 };
207 format!("{article} {name}")
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn truthiness_follows_python() {
217 assert!(!Value::Int(0).truthy());
218 assert!(Value::Int(1).truthy());
219 assert!(!Value::Float(0.0).truthy());
220 assert!(Value::Float(0.1).truthy());
221 assert!(!Value::str("").truthy());
222 assert!(Value::str("dog").truthy());
223 assert!(!Value::Bool(false).truthy());
224 assert!(Value::Bool(true).truthy());
225 assert!(!Value::None.truthy());
226 assert!(!Value::list(vec![]).truthy());
227 assert!(Value::list(vec![Value::Int(1)]).truthy());
228 assert!(!Value::dict(OrderedMap::new()).truthy());
229 assert!(Value::object(0, "Shibe").truthy());
231 assert!(Value::function(0, "greet", vec![]).truthy());
233 }
234
235 #[test]
236 fn type_names_match_design() {
237 assert_eq!(Value::Int(1).type_name(), "Int");
238 assert_eq!(Value::Float(1.0).type_name(), "Float");
239 assert_eq!(Value::str("x").type_name(), "Str");
240 assert_eq!(Value::Bool(true).type_name(), "Bool");
241 assert_eq!(Value::None.type_name(), "None");
242 assert_eq!(Value::list(vec![]).type_name(), "List");
243 assert_eq!(Value::dict(OrderedMap::new()).type_name(), "Dict");
244 assert_eq!(Value::object(0, "Shibe").type_name(), "Object");
245 assert_eq!(Value::function(0, "greet", vec![]).type_name(), "Function");
246 }
247
248 #[test]
249 fn describe_uses_the_right_article() {
250 assert_eq!(Value::Int(1).describe(), "an Int");
251 assert_eq!(Value::str("x").describe(), "a Str");
252 assert_eq!(Value::None.describe(), "a None");
253 }
254
255 #[test]
256 fn dict_from_pairs_last_duplicate_wins() {
257 let d = Value::dict_from_pairs(vec![
258 (Value::str("k"), Value::Int(1)),
259 (Value::str("k"), Value::Int(2)),
260 ])
261 .unwrap();
262 match d {
263 Value::Dict(entries) => {
264 let entries = entries.borrow();
265 assert_eq!(entries.len(), 1);
266 assert!(matches!(entries.get("k"), Some(Value::Int(2))));
267 }
268 _ => panic!("expected a dict"),
269 }
270 }
271
272 #[test]
273 fn dict_from_pairs_rejects_non_str_key() {
274 let err = Value::dict_from_pairs(vec![(Value::Int(1), Value::Int(2))]).unwrap_err();
275 assert_eq!(err.kind, crate::error::ErrorKind::TypeError);
276 }
277
278 #[test]
279 fn str_constructor_shares_via_rc() {
280 let a = Value::str("kabosu");
281 let b = a.clone();
282 match (&a, &b) {
284 (Value::Str(x), Value::Str(y)) => assert!(Rc::ptr_eq(x, y)),
285 _ => panic!("expected two Str values"),
286 }
287 }
288}