uni_core/primitives/
var.rs

1// RUST CONCEPT: Forth-style variable creation
2// Creates a mutable variable and binds it to a name in the dictionary
3use crate::compat::{format, Rc};
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6
7#[cfg(not(target_os = "none"))]
8use std::cell::RefCell;
9#[cfg(target_os = "none")]
10use core::cell::RefCell;
11
12// RUST CONCEPT: Variable creation primitive
13// Stack-based: ( initial-value 'name -- )
14// Creates a Variable containing initial-value and defines it as 'name'
15pub fn var_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
16    let name_val = interp.pop()?;
17    let initial_value = interp.pop()?;
18
19    // Extract the atom name (like def, expects Atom not QuotedAtom)
20    let name = match name_val {
21        Value::Atom(ref atom) => atom.clone(),
22        _ => {
23            return Err(RuntimeError::TypeError(format!(
24                "var expects atom name, got {:?}",
25                name_val
26            )))
27        }
28    };
29
30    // Create the variable
31    let var = Value::Variable(Rc::new(RefCell::new(initial_value)));
32
33    // Store in dictionary as an executable word
34    let dict_entry = crate::interpreter::DictEntry {
35        value: var,
36        is_executable: true,
37        doc: None,
38    };
39
40    interp.dictionary.insert(name, dict_entry);
41
42    Ok(())
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::value::Value;
49
50    fn setup_interpreter() -> Interpreter {
51        Interpreter::new()
52    }
53
54    #[test]
55    fn test_var_basic() {
56        let mut interp = setup_interpreter();
57
58        // Create a variable: 42 'x var
59        interp.push(Value::Int32(42));
60        let name_atom = interp.intern_atom("x");
61        interp.push(Value::Atom(name_atom.clone()));
62        var_builtin(&mut interp).unwrap();
63
64        // Check that 'x' is in the dictionary
65        assert!(interp.dictionary.contains_key(&name_atom));
66
67        // Get the dictionary entry
68        let entry = interp.dictionary.get(&name_atom).unwrap();
69
70        // Verify it's a Variable
71        assert!(matches!(entry.value, Value::Variable(_)));
72
73        // Verify it's executable
74        assert!(entry.is_executable);
75    }
76
77    #[test]
78    fn test_var_wrong_name_type() {
79        let mut interp = setup_interpreter();
80
81        // Try to create variable with non-quoted atom
82        interp.push(Value::Int32(42));
83        interp.push(Value::Int32(5)); // Wrong type for name
84        let result = var_builtin(&mut interp);
85
86        assert!(matches!(result, Err(RuntimeError::TypeError(_))));
87    }
88
89    #[test]
90    fn test_var_stack_underflow() {
91        let mut interp = setup_interpreter();
92
93        // Not enough values on stack
94        let result = var_builtin(&mut interp);
95        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
96    }
97}