uni_core/
value.rs

1use crate::compat::{Rc, String, Vec, fmt};
2use crate::tokenizer::SourcePos;
3
4#[cfg(not(target_os = "none"))]
5use std::cell::RefCell;
6#[cfg(target_os = "none")]
7use core::cell::RefCell;
8
9use num_bigint::BigInt;
10#[cfg(feature = "complex_numbers")]
11use num_complex::Complex64;
12use num_rational::BigRational;
13use num_traits::{One, Zero};
14
15// RUST CONCEPT: Using the num ecosystem for arbitrary precision and special number types
16// BigInt: Arbitrary precision integers (unlimited size)
17// BigRational: Exact rational numbers (fractions)
18// GaussianInt: Gaussian integers (a + bi where a, b are integers)
19// Complex64: Complex numbers with f64 components
20
21#[derive(Debug, Clone)]
22pub enum Value {
23    Number(f64),                    // Floating point number (default)
24    Int32(i32),                     // 32-bit signed integer (embedded-friendly)
25    Integer(BigInt),                // Arbitrary precision integer
26    Rational(BigRational),          // Exact rational number (fraction)
27    #[cfg(feature = "complex_numbers")]
28    GaussianInt(BigInt, BigInt),    // Gaussian integer (real, imaginary) - both integers
29    #[cfg(feature = "complex_numbers")]
30    Complex(Complex64),             // Complex number (a + bi) - floating point components
31    Atom(Rc<str>),                  // Interned atoms for efficiency
32    QuotedAtom(Rc<str>),            // Quoted atoms - push without executing
33    String(Rc<str>),                // Literal strings - ref counted but not interned
34    Boolean(bool),                  // True/false boolean values
35    Null,                           // Null/undefined value (distinct from Nil empty list)
36    Pair(Rc<Value>, Rc<Value>),    // Cons cell for lists
37    Array(Rc<RefCell<Vec<Value>>>), // Mutable array/vector
38    Variable(Rc<RefCell<Value>>),   // Mutable variable (Forth-style)
39    Nil,                            // Empty list marker
40    Builtin(fn(&mut crate::interpreter::Interpreter) -> Result<(), RuntimeError>),
41    // RUST CONCEPT: Records (Scheme-style record types)
42    // Records are named product types with labeled fields
43    // type_name: The record type name (e.g., "person")
44    // fields: The field values stored in a mutable vector
45    // Uses Rc<RefCell<...>> for shared ownership with interior mutability
46    Record {
47        type_name: Rc<str>,
48        fields: Rc<RefCell<Vec<Value>>>,
49    },
50    // RUST CONCEPT: Record type descriptors
51    // Stores metadata about record types (field names, field count)
52    // Used to validate and access record instances
53    RecordType {
54        type_name: Rc<str>,
55        field_names: Rc<Vec<Rc<str>>>,
56    },
57    // RUST CONCEPT: I16 buffer for audio samples and DSP
58    // Stores 16-bit signed integers (standard for digital audio)
59    // Dynamic size Vec for flexibility - can grow/shrink as needed
60    // Use with record types to add audio metadata (sample rate, channels)
61    I16Buffer(Rc<RefCell<Vec<i16>>>),
62}
63
64impl Value {
65    // RUST CONCEPT: Get the type name of a value
66    // Returns a string describing the type for display and debugging
67    pub fn type_name(&self) -> &'static str {
68        match self {
69            Value::Number(_) => "number",
70            Value::Int32(_) => "int32",
71            Value::Integer(_) => "integer",
72            Value::Rational(_) => "rational",
73            #[cfg(feature = "complex_numbers")]
74            Value::GaussianInt(_, _) => "gaussian",
75            #[cfg(feature = "complex_numbers")]
76            Value::Complex(_) => "complex",
77            Value::Atom(_) => "atom",
78            Value::QuotedAtom(_) => "quoted-atom",
79            Value::String(_) => "string",
80            Value::Boolean(_) => "boolean",
81            Value::Null => "null",
82            Value::Pair(_, _) => "list",
83            Value::Array(_) => "vector",
84            Value::Variable(_) => "variable",
85            Value::Nil => "nil",
86            Value::Builtin(_) => "builtin",
87            Value::Record { .. } => "record",
88            Value::RecordType { .. } => "record-type",
89            Value::I16Buffer(_) => "i16-buffer",
90        }
91    }
92
93    // RUST CONCEPT: Automatic numeric type demotion for cleaner results
94    // This function attempts to demote numeric types to simpler representations:
95    // - Rational with denominator 1 → Integer or Int32
96    // - Rational with numerator 0 → Int32(0)
97    // - GaussianInt with imaginary 0 → Integer or Int32
98    // - Integer that fits in i32 → Int32
99    // This keeps values in their simplest form after arithmetic operations
100    pub fn demote(self) -> Self {
101        use num_traits::ToPrimitive;
102        match &self {
103            // Check Rational: demote if denominator is 1 or numerator is 0
104            Value::Rational(r) if r.numer().is_zero() => {
105                // 0/n → Int32(0)
106                Value::Int32(0)
107            }
108            Value::Rational(r) if r.denom().is_one() => {
109                // n/1 → Integer or Int32
110                // Extract the inner BigRational and clone its numerator
111                if let Value::Rational(r) = self {
112                    let big_int = r.numer().clone();
113                    // Try to fit in i32 for embedded systems
114                    if let Some(i32_val) = big_int.to_i32() {
115                        Value::Int32(i32_val)
116                    } else {
117                        Value::Integer(big_int)
118                    }
119                } else {
120                    unreachable!()
121                }
122            }
123            // Check GaussianInt: demote if imaginary part is 0
124            #[cfg(feature = "complex_numbers")]
125            Value::GaussianInt(_re, im) if im.is_zero() => {
126                // a+0i → Integer or Int32 (move real part out)
127                if let Value::GaussianInt(re, _im) = self {
128                    // Try to fit in i32
129                    if let Some(i32_val) = re.to_i32() {
130                        Value::Int32(i32_val)
131                    } else {
132                        Value::Integer(re)
133                    }
134                } else {
135                    unreachable!()
136                }
137            }
138            // Check Integer: demote to Int32 if it fits
139            Value::Integer(i) => {
140                if let Some(i32_val) = i.to_i32() {
141                    Value::Int32(i32_val)
142                } else {
143                    self
144                }
145            }
146            // All other cases: return unchanged (no deconstruct/reconstruct)
147            _ => self,
148        }
149    }
150}
151
152#[derive(Debug)]
153pub enum RuntimeError {
154    StackUnderflow,
155    StackUnderflowAt { pos: SourcePos, context: String },
156    TypeError(String),
157    UndefinedWord(String),
158    DivisionByZero,
159    ModuloByZero,
160    DomainError(String),
161    QuitRequested, // Special error to signal clean exit from REPL/script
162}
163
164// RUST CONCEPT: Implementing traits for custom error types
165// The Display trait allows us to convert errors to strings
166impl fmt::Display for RuntimeError {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        match self {
169            RuntimeError::StackUnderflow => write!(f, "Stack underflow"),
170            RuntimeError::StackUnderflowAt { pos, context } => {
171                write!(
172                    f,
173                    "Stack underflow at line {}, column {}: {}",
174                    pos.line, pos.column, context
175                )
176            }
177            RuntimeError::TypeError(msg) => write!(f, "Type error: {}", msg),
178            RuntimeError::UndefinedWord(word) => write!(f, "Undefined word: {}", word),
179            RuntimeError::DivisionByZero => write!(f, "Division by zero"),
180            RuntimeError::ModuloByZero => write!(f, "Modulo by zero"),
181            RuntimeError::DomainError(msg) => write!(f, "Domain error: {}", msg),
182            RuntimeError::QuitRequested => write!(f, "Quit requested"),
183        }
184    }
185}
186
187// RUST CONCEPT: Implementing Display for Value types
188// This is the "data display" mode - strings WITH quotes for data structures
189impl fmt::Display for Value {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        match self {
192            Value::Number(n) => write!(f, "{}", n),
193            Value::Int32(i) => write!(f, "{}", i),
194            Value::Integer(i) => write!(f, "{}", i),
195            // RUST CONCEPT: BigRational displays as "numerator/denominator"
196            Value::Rational(r) => write!(f, "{}", r), // Shows as fraction like "3/4"
197            // RUST CONCEPT: GaussianInt displays as "a+bi" with integer parts
198            #[cfg(feature = "complex_numbers")]
199            Value::GaussianInt(re, im) => {
200                use num_traits::Zero;
201
202                // Special case: 0+1i displays as just "i"
203                if re.is_zero() && im == &BigInt::from(1) {
204                    write!(f, "i")
205                }
206                // Special case: 0-1i displays as "-i"
207                else if re.is_zero() && im == &BigInt::from(-1) {
208                    write!(f, "-i")
209                }
210                // Special case: 0+ni displays as "ni" (pure imaginary)
211                else if re.is_zero() {
212                    write!(f, "{}i", im)
213                }
214                // Special case: a+0i displays as just "a" (pure real)
215                else if im.is_zero() {
216                    write!(f, "{}", re)
217                }
218                // General case: a+bi
219                else if im >= &BigInt::from(0) {
220                    write!(f, "{}+{}i", re, im)
221                } else {
222                    write!(f, "{}{}i", re, im)
223                }
224            }
225            // RUST CONCEPT: Complex64 displays as "a+bi" format with floating point
226            #[cfg(feature = "complex_numbers")]
227            Value::Complex(c) => {
228                // Custom formatting for complex numbers
229                if c.im >= 0.0 {
230                    write!(f, "{}+{}i", c.re, c.im)
231                } else {
232                    write!(f, "{}{}i", c.re, c.im)
233                }
234            }
235            Value::Atom(a) => write!(f, "{}", a),
236            Value::QuotedAtom(a) => write!(f, "'{}", a),
237            Value::String(s) => write!(f, "\"{}\"", s), // Strings WITH quotes
238            Value::Boolean(b) => write!(f, "{}", if *b { "true" } else { "false" }),
239            Value::Null => write!(f, "null"),
240            Value::Pair(head, tail) => {
241                write!(f, "[")?;
242                write!(f, "{}", head)?;
243                let mut current = tail;
244                loop {
245                    match current.as_ref() {
246                        Value::Nil => break,
247                        Value::Pair(h, t) => {
248                            write!(f, " {}", h)?;
249                            current = t;
250                        }
251                        other => {
252                            write!(f, " | {}", other)?;
253                            break;
254                        }
255                    }
256                }
257                write!(f, "]")
258            }
259            Value::Array(elements) => {
260                let elements_ref = elements.borrow();
261                write!(f, "#[")?;
262                let mut iter = elements_ref.iter();
263                if let Some(first) = iter.next() {
264                    write!(f, "{}", first)?;
265                    for elem in iter {
266                        write!(f, " {}", elem)?;
267                    }
268                }
269                write!(f, "]")
270            }
271            Value::Variable(cell) => {
272                write!(f, "<variable:{}>", cell.borrow())
273            }
274            Value::Nil => write!(f, "[]"),
275            Value::Builtin(_) => write!(f, "<builtin>"),
276            // RUST CONCEPT: Display for record instances
277            // Shows the type name and field values
278            Value::Record { type_name, fields } => {
279                let fields_ref = fields.borrow();
280                write!(f, "#<record:{}", type_name)?;
281                for field in fields_ref.iter() {
282                    write!(f, " {}", field)?;
283                }
284                write!(f, ">")
285            }
286            // RUST CONCEPT: Display for record type descriptors
287            // Shows the type name and field names
288            Value::RecordType {
289                type_name,
290                field_names,
291            } => {
292                write!(f, "#<record-type:{}", type_name)?;
293                for field_name in field_names.iter() {
294                    write!(f, " {}", field_name)?;
295                }
296                write!(f, ">")
297            }
298            // RUST CONCEPT: Display for i16 buffers
299            // Shows buffer length and first few samples for debugging
300            Value::I16Buffer(buffer) => {
301                let buffer_ref = buffer.borrow();
302                let len = buffer_ref.len();
303                write!(f, "#<i16-buffer:{}:[", len)?;
304
305                // Show first 8 samples (or fewer if buffer is smaller)
306                let preview_count = len.min(8);
307                for i in 0..preview_count {
308                    if i > 0 {
309                        write!(f, " ")?;
310                    }
311                    write!(f, "{}", buffer_ref[i])?;
312                }
313
314                if len > preview_count {
315                    write!(f, " ...")?;
316                }
317                write!(f, "]>")
318            }
319        }
320    }
321}