uni_core/primitives/
val.rs1use crate::compat::ToString;
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6
7pub fn val_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
14 let definition = interp.pop()?; let name_value = interp.pop()?; match name_value {
20 Value::Atom(atom_name) => {
21 use crate::interpreter::DictEntry;
23 let doc_clone = interp
24 .dictionary
25 .get(&atom_name)
26 .and_then(|entry| entry.doc.clone());
27 let entry = DictEntry {
28 value: definition,
29 is_executable: false, doc: doc_clone,
31 };
32 interp.dictionary.insert(atom_name.clone(), entry);
33 interp.set_pending_doc_target(atom_name);
34 Ok(())
35 }
36 _ => Err(RuntimeError::TypeError(
37 "val expects an atom as the constant name (use 'name value val)".to_string(),
38 )),
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::interpreter::DictEntry;
46 use crate::value::Value;
47
48 fn setup_interpreter() -> Interpreter {
49 Interpreter::new()
50 }
51
52 #[test]
53 fn test_val_builtin_constants() {
54 let mut interp = setup_interpreter();
55
56 let pi_atom = interp.intern_atom("pi");
59 interp.push(Value::Atom(pi_atom.clone()));
60 interp.push(Value::Number(3.14159));
61 val_builtin(&mut interp).unwrap();
62
63 match interp.dictionary.get(&pi_atom) {
65 Some(DictEntry {
66 value: Value::Number(n),
67 ..
68 }) => assert!((n - 3.14159).abs() < 1e-10),
69 _ => panic!("Expected pi constant"),
70 }
71
72 let greeting_atom = interp.intern_atom("greeting");
74 let hello_str: std::rc::Rc<str> = "Hello!".into();
75 interp.push(Value::Atom(greeting_atom.clone()));
76 interp.push(Value::String(hello_str));
77 val_builtin(&mut interp).unwrap();
78
79 match interp.dictionary.get(&greeting_atom) {
81 Some(DictEntry {
82 value: Value::String(s),
83 ..
84 }) => assert_eq!(s.as_ref(), "Hello!"),
85 _ => panic!("Expected greeting string constant"),
86 }
87
88 while interp.pop().is_ok() {}
90 }
91
92 #[test]
93 fn test_val_builtin_error_cases() {
94 let mut interp = setup_interpreter();
95
96 assert!(val_builtin(&mut interp).is_err()); interp.push(Value::Number(42.0));
101 assert!(val_builtin(&mut interp).is_err()); interp.push(Value::Number(42.0)); interp.push(Value::Number(123.0)); let result = val_builtin(&mut interp);
107 assert!(result.is_err());
108 assert!(
109 result
110 .unwrap_err()
111 .to_string()
112 .contains("val expects an atom")
113 );
114
115 while interp.pop().is_ok() {}
117 }
118
119 #[test]
120 fn test_val_builtin_constant_flag() {
121 let mut interp = setup_interpreter();
122
123 let test_atom = interp.intern_atom("test_constant");
125 interp.push(Value::Atom(test_atom.clone()));
126 interp.push(Value::Number(42.0));
127 val_builtin(&mut interp).unwrap();
128
129 match interp.dictionary.get(&test_atom) {
131 Some(DictEntry { is_executable, .. }) => assert!(!*is_executable),
132 _ => panic!("Expected test_constant to be defined"),
133 }
134
135 while interp.pop().is_ok() {}
137 }
138
139 #[test]
140 fn test_val_builtin_various_types() {
141 let mut interp = setup_interpreter();
142
143 let constants_to_test = vec![
145 ("bool_const", Value::Boolean(false)),
146 ("null_const", Value::Null),
147 ("string_const", Value::String("constant value".into())),
148 (
149 "atom_const",
150 Value::Atom(interp.intern_atom("constant_atom")),
151 ),
152 ];
153
154 for (name, value) in constants_to_test {
155 let name_atom = interp.intern_atom(name);
156 interp.push(Value::Atom(name_atom.clone()));
157 interp.push(value.clone());
158 val_builtin(&mut interp).unwrap();
159
160 match interp.dictionary.get(&name_atom) {
162 Some(DictEntry {
163 value: stored_value,
164 is_executable,
165 ..
166 }) => {
167 assert!(!*is_executable, "Constants should not be executable");
168
169 match (&value, stored_value) {
171 (Value::Boolean(b1), Value::Boolean(b2)) => assert_eq!(b1, b2),
172 (Value::Null, Value::Null) => (),
173 (Value::String(s1), Value::String(s2)) => assert_eq!(s1, s2),
174 (Value::Atom(a1), Value::Atom(a2)) => assert_eq!(a1, a2),
175 _ => panic!("Value type mismatch for {}", name),
176 }
177 }
178 _ => panic!("Expected {} to be defined", name),
179 }
180 }
181
182 while interp.pop().is_ok() {}
184 }
185
186 #[test]
187 fn test_val_builtin_redefinition() {
188 let mut interp = setup_interpreter();
189
190 let const_atom = interp.intern_atom("my_const");
192
193 interp.push(Value::Atom(const_atom.clone()));
195 interp.push(Value::Number(100.0));
196 val_builtin(&mut interp).unwrap();
197
198 match interp.dictionary.get(&const_atom) {
200 Some(DictEntry {
201 value: Value::Number(n),
202 ..
203 }) => assert_eq!(*n, 100.0),
204 _ => panic!("Expected first definition"),
205 }
206
207 interp.push(Value::Atom(const_atom.clone()));
209 interp.push(Value::Number(200.0));
210 val_builtin(&mut interp).unwrap();
211
212 match interp.dictionary.get(&const_atom) {
214 Some(DictEntry {
215 value: Value::Number(n),
216 ..
217 }) => assert_eq!(*n, 200.0),
218 _ => panic!("Expected redefinition"),
219 }
220
221 while interp.pop().is_ok() {}
223 }
224
225 #[test]
226 fn test_val_builtin_with_nil() {
227 let mut interp = setup_interpreter();
228
229 let nil_atom = interp.intern_atom("nil_const");
231 interp.push(Value::Atom(nil_atom.clone()));
232 interp.push(Value::Nil);
233 val_builtin(&mut interp).unwrap();
234
235 match interp.dictionary.get(&nil_atom) {
237 Some(DictEntry {
238 value: Value::Nil,
239 is_executable,
240 ..
241 }) => {
242 assert!(!*is_executable, "Nil constant should not be executable");
243 }
244 _ => panic!("Expected nil_const to be defined as Nil"),
245 }
246
247 while interp.pop().is_ok() {}
249 }
250
251 #[test]
252 fn test_val_builtin_with_list() {
253 let mut interp = setup_interpreter();
254
255 let list_atom = interp.intern_atom("list_const");
257 let constant_list = interp.make_list(vec![
258 Value::Number(1.0),
259 Value::Number(2.0),
260 Value::Number(3.0),
261 ]);
262
263 interp.push(Value::Atom(list_atom.clone()));
264 interp.push(constant_list);
265 val_builtin(&mut interp).unwrap();
266
267 match interp.dictionary.get(&list_atom) {
269 Some(DictEntry {
270 value: Value::Pair(_, _),
271 is_executable,
272 ..
273 }) => {
274 assert!(!*is_executable, "List constants should not be executable");
275 }
276 _ => panic!("Expected list_const to be defined as a list"),
277 }
278
279 while interp.pop().is_ok() {}
281 }
282}