uni_core/primitives/
to_string.rs

1// RUST CONCEPT: String conversion primitive
2// Converts any value to its string representation using Display trait
3use crate::compat::ToString;
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6
7// RUST CONCEPT: Universal string conversion
8// Stack-based conversion: ( any -- string )
9pub fn to_string_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
10    let value = interp.pop()?;
11
12    // RUST CONCEPT: Use Display trait for consistent string representation
13    let string_result = value.to_string();
14    interp.push(Value::String(string_result.into()));
15
16    Ok(())
17}
18
19#[cfg(test)]
20mod tests {
21    use super::*;
22    use crate::value::Value;
23
24    fn setup_interpreter() -> Interpreter {
25        Interpreter::new()
26    }
27
28    #[test]
29    fn test_to_string_builtin_number() {
30        let mut interp = setup_interpreter();
31
32        interp.push(Value::Number(42.0));
33        to_string_builtin(&mut interp).unwrap();
34
35        let result = interp.pop().unwrap();
36        assert!(matches!(result, Value::String(s) if s.as_ref() == "42"));
37    }
38
39    #[test]
40    fn test_to_string_builtin_negative_number() {
41        let mut interp = setup_interpreter();
42
43        interp.push(Value::Number(-3.14));
44        to_string_builtin(&mut interp).unwrap();
45
46        let result = interp.pop().unwrap();
47        assert!(matches!(result, Value::String(s) if s.as_ref() == "-3.14"));
48    }
49
50    #[test]
51    fn test_to_string_builtin_boolean() {
52        let mut interp = setup_interpreter();
53
54        interp.push(Value::Boolean(true));
55        to_string_builtin(&mut interp).unwrap();
56
57        let result = interp.pop().unwrap();
58        assert!(matches!(result, Value::String(s) if s.as_ref() == "true"));
59
60        interp.push(Value::Boolean(false));
61        to_string_builtin(&mut interp).unwrap();
62
63        let result = interp.pop().unwrap();
64        assert!(matches!(result, Value::String(s) if s.as_ref() == "false"));
65    }
66
67    #[test]
68    fn test_to_string_builtin_string() {
69        let mut interp = setup_interpreter();
70
71        // Converting string to string should add quotes (data representation)
72        interp.push(Value::String("hello".into()));
73        to_string_builtin(&mut interp).unwrap();
74
75        let result = interp.pop().unwrap();
76        assert!(matches!(result, Value::String(s) if s.as_ref() == "\"hello\""));
77    }
78
79    #[test]
80    fn test_to_string_builtin_atom() {
81        let mut interp = setup_interpreter();
82
83        let atom = interp.intern_atom("test");
84        interp.push(Value::Atom(atom));
85        to_string_builtin(&mut interp).unwrap();
86
87        let result = interp.pop().unwrap();
88        assert!(matches!(result, Value::String(s) if s.as_ref() == "test"));
89    }
90
91    #[test]
92    fn test_to_string_builtin_quoted_atom() {
93        let mut interp = setup_interpreter();
94
95        let quoted_atom = interp.intern_atom("quoted");
96        interp.push(Value::QuotedAtom(quoted_atom));
97        to_string_builtin(&mut interp).unwrap();
98
99        let result = interp.pop().unwrap();
100        assert!(matches!(result, Value::String(s) if s.as_ref() == "'quoted"));
101    }
102
103    #[test]
104    fn test_to_string_builtin_null() {
105        let mut interp = setup_interpreter();
106
107        interp.push(Value::Null);
108        to_string_builtin(&mut interp).unwrap();
109
110        let result = interp.pop().unwrap();
111        assert!(matches!(result, Value::String(s) if s.as_ref() == "null"));
112    }
113
114    #[test]
115    fn test_to_string_builtin_empty_list() {
116        let mut interp = setup_interpreter();
117
118        interp.push(Value::Nil);
119        to_string_builtin(&mut interp).unwrap();
120
121        let result = interp.pop().unwrap();
122        assert!(matches!(result, Value::String(s) if s.as_ref() == "[]"));
123    }
124
125    #[test]
126    fn test_to_string_builtin_list() {
127        let mut interp = setup_interpreter();
128
129        // Test converting a list to string
130        let list = interp.make_list(vec![
131            Value::Number(1.0),
132            Value::Number(2.0),
133            Value::Number(3.0),
134        ]);
135        interp.push(list);
136        to_string_builtin(&mut interp).unwrap();
137
138        let result = interp.pop().unwrap();
139        assert!(matches!(result, Value::String(s) if s.as_ref() == "[1 2 3]"));
140    }
141
142    #[test]
143    fn test_to_string_builtin_mixed_list() {
144        let mut interp = setup_interpreter();
145
146        // Test converting a mixed list to string
147        let mixed_list = interp.make_list(vec![
148            Value::String("hello".into()),
149            Value::Number(42.0),
150            Value::Boolean(true),
151        ]);
152        interp.push(mixed_list);
153        to_string_builtin(&mut interp).unwrap();
154
155        let result = interp.pop().unwrap();
156        assert!(matches!(result, Value::String(s) if s.as_ref() == "[\"hello\" 42 true]"));
157    }
158
159    #[test]
160    fn test_to_string_builtin_array() {
161        let mut interp = setup_interpreter();
162
163        let array = interp.make_array(vec![Value::Number(1.0), Value::Number(2.0)]);
164        interp.push(array);
165        to_string_builtin(&mut interp).unwrap();
166
167        let result = interp.pop().unwrap();
168        assert!(matches!(result, Value::String(s) if s.as_ref() == "#[1 2]"));
169    }
170
171    #[test]
172    fn test_to_string_builtin_stack_underflow() {
173        let mut interp = setup_interpreter();
174
175        // Test with empty stack
176        let result = to_string_builtin(&mut interp);
177        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
178    }
179}