uni_core/primitives/
def.rs1use crate::compat::ToString;
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6
7pub fn def_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
13 let definition = interp.pop()?; let name_value = interp.pop()?; match name_value {
24 Value::Atom(atom_name) => {
25 use crate::interpreter::DictEntry;
27 let doc_clone = interp
28 .dictionary
29 .get(&atom_name)
30 .and_then(|entry| entry.doc.clone());
31 let entry = DictEntry {
32 value: definition,
33 is_executable: true, doc: doc_clone,
35 };
36 interp.dictionary.insert(atom_name.clone(), entry);
37 interp.set_pending_doc_target(atom_name);
38 Ok(())
39 }
40
41 _ => Err(RuntimeError::TypeError(
44 "def expects an atom as the word name (use 'word-name definition def)".to_string(),
45 )),
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use crate::interpreter::DictEntry;
53 use crate::value::Value;
54
55 fn setup_interpreter() -> Interpreter {
56 Interpreter::new()
57 }
58
59 #[test]
60 fn test_def_builtin_constant() {
61 let mut interp = setup_interpreter();
62
63 let pi_atom = interp.intern_atom("pi");
66 interp.push(Value::Atom(pi_atom.clone())); interp.push(Value::Number(3.14159)); def_builtin(&mut interp).unwrap();
69
70 let pi_lookup = interp.intern_atom("pi");
72 assert!(interp.dictionary.contains_key(&pi_lookup));
73
74 match interp.dictionary.get(&pi_lookup) {
76 Some(DictEntry {
77 value: Value::Number(n),
78 ..
79 }) => assert!((n - 3.14159).abs() < 1e-10),
80 _ => panic!("Expected pi to be defined as a number"),
81 }
82
83 while interp.pop().is_ok() {}
85 }
86
87 #[test]
88 fn test_def_builtin_procedure() {
89 let mut interp = setup_interpreter();
90
91 let square_atom = interp.intern_atom("square");
94 let dup_atom = interp.intern_atom("dup");
95 let mul_atom = interp.intern_atom("*");
96
97 let square_proc = interp.make_list(vec![Value::Atom(dup_atom), Value::Atom(mul_atom)]);
99
100 interp.push(Value::Atom(square_atom.clone())); interp.push(square_proc); def_builtin(&mut interp).unwrap();
103
104 let square_lookup = interp.intern_atom("square");
106 assert!(interp.dictionary.contains_key(&square_lookup));
107
108 match interp.dictionary.get(&square_lookup) {
110 Some(DictEntry {
111 value: Value::Pair(_, _),
112 ..
113 }) => (), _ => panic!("Expected square to be defined as a list"),
115 }
116
117 while interp.pop().is_ok() {}
119 }
120
121 #[test]
122 fn test_def_builtin_string_definition() {
123 let mut interp = setup_interpreter();
124
125 let greeting_atom = interp.intern_atom("greeting");
128 let greeting_string: std::rc::Rc<str> = "Hello, Uni!".into();
129
130 interp.push(Value::Atom(greeting_atom.clone())); interp.push(Value::String(greeting_string)); def_builtin(&mut interp).unwrap();
133
134 let greeting_lookup = interp.intern_atom("greeting");
136 match interp.dictionary.get(&greeting_lookup) {
137 Some(DictEntry {
138 value: Value::String(s),
139 ..
140 }) => assert_eq!(s.as_ref(), "Hello, Uni!"),
141 _ => panic!("Expected greeting to be defined as a string"),
142 }
143
144 while interp.pop().is_ok() {}
146 }
147
148 #[test]
149 fn test_def_builtin_error_cases() {
150 let mut interp = setup_interpreter();
151
152 assert!(def_builtin(&mut interp).is_err()); interp.push(Value::Number(42.0));
157 assert!(def_builtin(&mut interp).is_err()); interp.push(Value::Number(42.0)); interp.push(Value::Number(123.0)); let result = def_builtin(&mut interp);
164 assert!(result.is_err());
165 assert!(
166 result
167 .unwrap_err()
168 .to_string()
169 .contains("def expects an atom")
170 );
171
172 while interp.pop().is_ok() {}
174 }
175
176 #[test]
177 fn test_def_builtin_redefinition() {
178 let mut interp = setup_interpreter();
179
180 let foo_atom = interp.intern_atom("foo");
183 interp.push(Value::Atom(foo_atom.clone()));
184 interp.push(Value::Number(123.0));
185 def_builtin(&mut interp).unwrap();
186
187 match interp.dictionary.get(&foo_atom) {
189 Some(DictEntry {
190 value: Value::Number(n),
191 ..
192 }) => assert_eq!(*n, 123.0),
193 _ => panic!("Expected foo to be 123"),
194 }
195
196 interp.push(Value::Atom(foo_atom.clone()));
198 interp.push(Value::Number(456.0));
199 def_builtin(&mut interp).unwrap();
200
201 match interp.dictionary.get(&foo_atom) {
203 Some(DictEntry {
204 value: Value::Number(n),
205 ..
206 }) => assert_eq!(*n, 456.0),
207 _ => panic!("Expected foo to be redefined as 456"),
208 }
209
210 while interp.pop().is_ok() {}
212 }
213
214 #[test]
215 fn test_def_builtin_with_nil() {
216 let mut interp = setup_interpreter();
217
218 let empty_atom = interp.intern_atom("empty");
220 interp.push(Value::Atom(empty_atom.clone()));
221 interp.push(Value::Nil);
222 def_builtin(&mut interp).unwrap();
223
224 match interp.dictionary.get(&empty_atom) {
226 Some(DictEntry {
227 value: Value::Nil, ..
228 }) => (),
229 _ => panic!("Expected empty to be defined as Nil"),
230 }
231
232 while interp.pop().is_ok() {}
234 }
235
236 #[test]
237 fn test_def_builtin_executable_flag() {
238 let mut interp = setup_interpreter();
239
240 let test_atom = interp.intern_atom("test");
242 interp.push(Value::Atom(test_atom.clone()));
243 interp.push(Value::Number(42.0));
244 def_builtin(&mut interp).unwrap();
245
246 match interp.dictionary.get(&test_atom) {
248 Some(DictEntry { is_executable, .. }) => assert!(*is_executable),
249 _ => panic!("Expected test to be defined"),
250 }
251
252 while interp.pop().is_ok() {}
254 }
255
256 #[test]
257 fn test_def_builtin_various_types() {
258 let mut interp = setup_interpreter();
259
260 let types_to_test = vec![
262 ("bool_val", Value::Boolean(true)),
263 ("null_val", Value::Null),
264 ("atom_val", Value::Atom(interp.intern_atom("test_atom"))),
265 ];
266
267 for (name, value) in types_to_test {
268 let name_atom = interp.intern_atom(name);
269 interp.push(Value::Atom(name_atom.clone()));
270 interp.push(value.clone());
271 def_builtin(&mut interp).unwrap();
272
273 assert!(interp.dictionary.contains_key(&name_atom));
275 match interp.dictionary.get(&name_atom) {
276 Some(DictEntry {
277 value: stored_value,
278 ..
279 }) => {
280 match (&value, stored_value) {
282 (Value::Boolean(b1), Value::Boolean(b2)) => assert_eq!(b1, b2),
283 (Value::Null, Value::Null) => (),
284 (Value::Atom(a1), Value::Atom(a2)) => assert_eq!(a1, a2),
285 _ => panic!("Value type mismatch for {}", name),
286 }
287 }
288 _ => panic!("Expected {} to be defined", name),
289 }
290 }
291
292 while interp.pop().is_ok() {}
294 }
295}