Skip to main content

logicaffeine_data/
types.rs

1//! Core runtime type definitions.
2//!
3//! This module defines the primitive types used by LOGOS programs at runtime.
4//! These are type aliases that map LOGOS types to their Rust equivalents.
5//!
6//! ## Type Mappings
7//!
8//! | LOGOS Type | Rust Type | Description |
9//! |------------|-----------|-------------|
10//! | `Nat` | `u64` | Natural numbers (non-negative) |
11//! | `Int` | `i64` | Signed integers |
12//! | `Real` | `f64` | Floating-point numbers |
13//! | `Text` | `String` | UTF-8 strings |
14//! | `Bool` | `bool` | Boolean values |
15//! | `Unit` | `()` | The unit type |
16//! | `Char` | `char` | Unicode scalar values |
17//! | `Byte` | `u8` | Raw bytes |
18//! | `Seq<T>` | `LogosSeq<T>` | Ordered sequences (reference semantics) |
19//! | `Set<T>` | `HashSet<T>` | Unordered unique elements |
20//! | `Map<K,V>` | `LogosMap<K,V>` | Key-value mappings (reference semantics) |
21
22use std::cell::RefCell;
23use std::hash::Hash;
24use std::rc::Rc;
25
26/// Non-negative integers. Maps to Peano `Nat` in the kernel.
27pub type Nat = u64;
28/// Signed integers.
29pub type Int = i64;
30/// IEEE 754 floating-point numbers.
31pub type Real = f64;
32/// UTF-8 encoded text strings.
33pub type Text = String;
34/// Boolean truth values.
35pub type Bool = bool;
36/// The unit type (single value).
37pub type Unit = ();
38/// Unicode scalar values.
39pub type Char = char;
40/// Raw bytes (0-255).
41pub type Byte = u8;
42
43/// Ordered sequence with reference semantics.
44///
45/// `LogosSeq<T>` wraps `Rc<RefCell<Vec<T>>>` to provide shared mutable access.
46/// Cloning a `LogosSeq` produces a shallow copy (shared reference), not a deep copy.
47/// Use `.deep_clone()` for an independent copy (LOGOS `copy of`).
48#[derive(Debug)]
49pub struct LogosSeq<T>(pub Rc<RefCell<Vec<T>>>);
50
51impl<T> LogosSeq<T> {
52    pub fn new() -> Self {
53        Self(Rc::new(RefCell::new(Vec::new())))
54    }
55
56    pub fn from_vec(v: Vec<T>) -> Self {
57        Self(Rc::new(RefCell::new(v)))
58    }
59
60    pub fn with_capacity(cap: usize) -> Self {
61        Self(Rc::new(RefCell::new(Vec::with_capacity(cap))))
62    }
63
64    pub fn push(&self, value: T) {
65        self.0.borrow_mut().push(value);
66    }
67
68    pub fn pop(&self) -> Option<T> {
69        self.0.borrow_mut().pop()
70    }
71
72    pub fn len(&self) -> usize {
73        self.0.borrow().len()
74    }
75
76    pub fn is_empty(&self) -> bool {
77        self.0.borrow().is_empty()
78    }
79
80    pub fn remove(&self, index: usize) -> T {
81        self.0.borrow_mut().remove(index)
82    }
83
84    pub fn borrow(&self) -> std::cell::Ref<'_, Vec<T>> {
85        self.0.borrow()
86    }
87
88    pub fn borrow_mut(&self) -> std::cell::RefMut<'_, Vec<T>> {
89        self.0.borrow_mut()
90    }
91}
92
93impl<T: Clone> LogosSeq<T> {
94    pub fn deep_clone(&self) -> Self {
95        Self(Rc::new(RefCell::new(self.0.borrow().clone())))
96    }
97
98    pub fn to_vec(&self) -> Vec<T> {
99        self.0.borrow().clone()
100    }
101
102    pub fn extend_from_slice(&self, other: &[T]) {
103        self.0.borrow_mut().extend_from_slice(other);
104    }
105
106    pub fn iter(&self) -> LogosSeqIter<T> {
107        LogosSeqIter {
108            data: self.to_vec(),
109            pos: 0,
110        }
111    }
112}
113
114pub struct LogosSeqIter<T> {
115    data: Vec<T>,
116    pos: usize,
117}
118
119impl<T: Clone> Iterator for LogosSeqIter<T> {
120    type Item = T;
121    fn next(&mut self) -> Option<T> {
122        if self.pos < self.data.len() {
123            let val = self.data[self.pos].clone();
124            self.pos += 1;
125            Some(val)
126        } else {
127            None
128        }
129    }
130}
131
132impl<T: Ord> LogosSeq<T> {
133    pub fn sort(&self) {
134        self.0.borrow_mut().sort();
135    }
136}
137
138impl<T> LogosSeq<T> {
139    pub fn reverse(&self) {
140        self.0.borrow_mut().reverse();
141    }
142}
143
144impl<T> Clone for LogosSeq<T> {
145    fn clone(&self) -> Self {
146        Self(Rc::clone(&self.0))
147    }
148}
149
150impl<T> Default for LogosSeq<T> {
151    fn default() -> Self {
152        Self::new()
153    }
154}
155
156impl<T: PartialEq> PartialEq for LogosSeq<T> {
157    fn eq(&self, other: &Self) -> bool {
158        *self.0.borrow() == *other.0.borrow()
159    }
160}
161
162impl<T: std::fmt::Display> std::fmt::Display for LogosSeq<T> {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        let inner = self.0.borrow();
165        write!(f, "[")?;
166        for (i, item) in inner.iter().enumerate() {
167            if i > 0 { write!(f, ", ")?; }
168            write!(f, "{}", item)?;
169        }
170        write!(f, "]")
171    }
172}
173
174impl<T> From<Vec<T>> for LogosSeq<T> {
175    fn from(v: Vec<T>) -> Self {
176        Self::from_vec(v)
177    }
178}
179
180impl<T: PartialEq> LogosContains<T> for LogosSeq<T> {
181    #[inline(always)]
182    fn logos_contains(&self, value: &T) -> bool {
183        self.0.borrow().contains(value)
184    }
185}
186
187impl<T: Clone> IntoIterator for LogosSeq<T> {
188    type Item = T;
189    type IntoIter = std::vec::IntoIter<T>;
190
191    fn into_iter(self) -> Self::IntoIter {
192        self.to_vec().into_iter()
193    }
194}
195
196/// Key-value mapping with reference semantics.
197///
198/// `LogosMap<K, V>` wraps `Rc<RefCell<FxHashMap<K, V>>>` to provide shared mutable access.
199/// Cloning a `LogosMap` produces a shallow copy (shared reference), not a deep copy.
200/// Use `.deep_clone()` for an independent copy (LOGOS `copy of`).
201#[derive(Debug)]
202pub struct LogosMap<K, V>(pub Rc<RefCell<rustc_hash::FxHashMap<K, V>>>);
203
204impl<K: Eq + Hash, V> LogosMap<K, V> {
205    pub fn new() -> Self {
206        Self(Rc::new(RefCell::new(rustc_hash::FxHashMap::default())))
207    }
208
209    pub fn with_capacity(cap: usize) -> Self {
210        Self(Rc::new(RefCell::new(
211            rustc_hash::FxHashMap::with_capacity_and_hasher(cap, Default::default()),
212        )))
213    }
214
215    pub fn from_map(m: rustc_hash::FxHashMap<K, V>) -> Self {
216        Self(Rc::new(RefCell::new(m)))
217    }
218
219    pub fn insert(&self, key: K, value: V) -> Option<V> {
220        self.0.borrow_mut().insert(key, value)
221    }
222
223    pub fn remove(&self, key: &K) -> Option<V> {
224        self.0.borrow_mut().remove(key)
225    }
226
227    pub fn len(&self) -> usize {
228        self.0.borrow().len()
229    }
230
231    pub fn is_empty(&self) -> bool {
232        self.0.borrow().is_empty()
233    }
234
235    pub fn contains_key(&self, key: &K) -> bool {
236        self.0.borrow().contains_key(key)
237    }
238
239    pub fn borrow(&self) -> std::cell::Ref<'_, rustc_hash::FxHashMap<K, V>> {
240        self.0.borrow()
241    }
242
243    pub fn borrow_mut(&self) -> std::cell::RefMut<'_, rustc_hash::FxHashMap<K, V>> {
244        self.0.borrow_mut()
245    }
246}
247
248impl<K: Eq + Hash + Clone, V: Clone> LogosMap<K, V> {
249    pub fn deep_clone(&self) -> Self {
250        Self(Rc::new(RefCell::new(self.0.borrow().clone())))
251    }
252
253    pub fn get(&self, key: &K) -> Option<V> {
254        self.0.borrow().get(key).cloned()
255    }
256
257    pub fn values(&self) -> Vec<V> {
258        self.0.borrow().values().cloned().collect()
259    }
260
261    pub fn keys(&self) -> Vec<K> {
262        self.0.borrow().keys().cloned().collect()
263    }
264}
265
266impl<K, V> Clone for LogosMap<K, V> {
267    fn clone(&self) -> Self {
268        Self(Rc::clone(&self.0))
269    }
270}
271
272impl<K: Eq + Hash, V> Default for LogosMap<K, V> {
273    fn default() -> Self {
274        Self::new()
275    }
276}
277
278impl<K: PartialEq + Eq + Hash, V: PartialEq> PartialEq for LogosMap<K, V> {
279    fn eq(&self, other: &Self) -> bool {
280        *self.0.borrow() == *other.0.borrow()
281    }
282}
283
284impl<K: std::fmt::Display + Eq + Hash, V: std::fmt::Display> std::fmt::Display for LogosMap<K, V> {
285    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286        let inner = self.0.borrow();
287        write!(f, "{{")?;
288        for (i, (k, v)) in inner.iter().enumerate() {
289            if i > 0 { write!(f, ", ")?; }
290            write!(f, "{}: {}", k, v)?;
291        }
292        write!(f, "}}")
293    }
294}
295
296impl<K: Eq + Hash, V> LogosContains<K> for LogosMap<K, V> {
297    #[inline(always)]
298    fn logos_contains(&self, key: &K) -> bool {
299        self.0.borrow().contains_key(key)
300    }
301}
302
303/// Ordered sequences with reference semantics.
304pub type Seq<T> = LogosSeq<T>;
305
306/// Key-value mappings with reference semantics.
307pub type Map<K, V> = LogosMap<K, V>;
308
309/// Unordered collections of unique elements with FxHash.
310pub type Set<T> = rustc_hash::FxHashSet<T>;
311
312/// Unified containment testing for all collection types.
313///
314/// This trait provides a consistent `logos_contains` method across Logos's
315/// collection types, abstracting over the different containment semantics
316/// of vectors (by value), sets (by membership), maps (by key), and
317/// strings (by substring or character).
318///
319/// # Implementations
320///
321/// - [`Vec<T>`]: Tests if the vector contains an element equal to the value
322/// - [`HashSet<T>`]: Tests if the element is a member of the set
323/// - [`HashMap<K, V>`]: Tests if a key exists in the map
324/// - [`String`]: Tests for substring (`&str`) or character (`char`) presence
325/// - [`ORSet<T, B>`]: Tests if the element is in the CRDT set
326///
327/// # Examples
328///
329/// ```
330/// use logicaffeine_data::LogosContains;
331///
332/// // Vector: contains by value equality
333/// let v = vec![1, 2, 3];
334/// assert!(v.logos_contains(&2));
335/// assert!(!v.logos_contains(&5));
336///
337/// // String: contains by substring
338/// let s = String::from("hello world");
339/// assert!(s.logos_contains(&"world"));
340///
341/// // String: contains by character
342/// assert!(s.logos_contains(&'o'));
343/// ```
344pub trait LogosContains<T> {
345    /// Check if this collection contains the given value.
346    fn logos_contains(&self, value: &T) -> bool;
347}
348
349impl<T: PartialEq> LogosContains<T> for Vec<T> {
350    #[inline(always)]
351    fn logos_contains(&self, value: &T) -> bool {
352        self.contains(value)
353    }
354}
355
356impl<T: PartialEq> LogosContains<T> for [T] {
357    #[inline(always)]
358    fn logos_contains(&self, value: &T) -> bool {
359        self.contains(value)
360    }
361}
362
363impl<T: Eq + Hash> LogosContains<T> for rustc_hash::FxHashSet<T> {
364    #[inline(always)]
365    fn logos_contains(&self, value: &T) -> bool {
366        self.contains(value)
367    }
368}
369
370impl<K: Eq + Hash, V> LogosContains<K> for rustc_hash::FxHashMap<K, V> {
371    #[inline(always)]
372    fn logos_contains(&self, key: &K) -> bool {
373        self.contains_key(key)
374    }
375}
376
377impl LogosContains<&str> for String {
378    #[inline(always)]
379    fn logos_contains(&self, value: &&str) -> bool {
380        self.contains(*value)
381    }
382}
383
384impl LogosContains<String> for String {
385    #[inline(always)]
386    fn logos_contains(&self, value: &String) -> bool {
387        self.contains(value.as_str())
388    }
389}
390
391impl LogosContains<char> for String {
392    #[inline(always)]
393    fn logos_contains(&self, value: &char) -> bool {
394        self.contains(*value)
395    }
396}
397
398impl<T: Eq + Hash + Clone, B: crate::crdt::SetBias> LogosContains<T>
399    for crate::crdt::ORSet<T, B>
400{
401    #[inline(always)]
402    fn logos_contains(&self, value: &T) -> bool {
403        self.contains(value)
404    }
405}
406
407/// Dynamic value type for heterogeneous collections.
408///
409/// `Value` enables tuples and other heterogeneous data structures in Logos.
410/// It supports basic arithmetic between compatible types and provides
411/// runtime type coercion where sensible.
412///
413/// # Variants
414///
415/// - `Int(i64)` - Integer values
416/// - `Float(f64)` - Floating-point values
417/// - `Bool(bool)` - Boolean values
418/// - `Text(String)` - String values
419/// - `Char(char)` - Single character values
420/// - `Nothing` - Unit/null value
421///
422/// # Arithmetic
423///
424/// Arithmetic operations are supported between numeric types:
425/// - `Int op Int` → `Int`
426/// - `Float op Float` → `Float`
427/// - `Int op Float` or `Float op Int` → `Float` (promotion)
428/// - `Text + Text` → `Text` (concatenation)
429///
430/// # Panics
431///
432/// Arithmetic on incompatible variants panics at runtime.
433///
434/// # Examples
435///
436/// ```
437/// use logicaffeine_data::Value;
438///
439/// let a = Value::Int(10);
440/// let b = Value::Int(3);
441/// assert_eq!(a + b, Value::Int(13));
442///
443/// let x = Value::Float(2.5);
444/// let y = Value::Int(2);
445/// assert_eq!(x * y, Value::Float(5.0));
446/// ```
447#[derive(Clone, Debug, PartialEq)]
448pub enum Value {
449    /// Integer values.
450    Int(i64),
451    /// Floating-point values.
452    Float(f64),
453    /// Boolean values.
454    Bool(bool),
455    /// String values.
456    Text(String),
457    /// Single character values.
458    Char(char),
459    /// Unit/null value.
460    Nothing,
461}
462
463impl std::fmt::Display for Value {
464    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465        match self {
466            Value::Int(n) => write!(f, "{}", n),
467            Value::Float(n) => write!(f, "{}", n),
468            Value::Bool(b) => write!(f, "{}", b),
469            Value::Text(s) => write!(f, "{}", s),
470            Value::Char(c) => write!(f, "{}", c),
471            Value::Nothing => write!(f, "nothing"),
472        }
473    }
474}
475
476// Conversion traits for Value
477impl From<i64> for Value {
478    fn from(n: i64) -> Self { Value::Int(n) }
479}
480
481impl From<f64> for Value {
482    fn from(n: f64) -> Self { Value::Float(n) }
483}
484
485impl From<bool> for Value {
486    fn from(b: bool) -> Self { Value::Bool(b) }
487}
488
489impl From<String> for Value {
490    fn from(s: String) -> Self { Value::Text(s) }
491}
492
493impl From<&str> for Value {
494    fn from(s: &str) -> Self { Value::Text(s.to_string()) }
495}
496
497impl From<char> for Value {
498    fn from(c: char) -> Self { Value::Char(c) }
499}
500
501/// Tuple type: Vec of heterogeneous Values (uses LogosIndex from indexing module)
502pub type Tuple = Vec<Value>;
503
504// NOTE: Showable impl for Value is in logicaffeine_system (io module)
505// This crate (logicaffeine_data) has NO IO dependencies.
506
507// Arithmetic operations for Value
508impl std::ops::Add for Value {
509    type Output = Value;
510
511    #[inline]
512    fn add(self, other: Value) -> Value {
513        match (self, other) {
514            (Value::Int(a), Value::Int(b)) => Value::Int(a + b),
515            (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
516            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 + b),
517            (Value::Float(a), Value::Int(b)) => Value::Float(a + b as f64),
518            (Value::Text(a), Value::Text(b)) => Value::Text(format!("{}{}", a, b)),
519            _ => panic!("Cannot add these value types"),
520        }
521    }
522}
523
524impl std::ops::Sub for Value {
525    type Output = Value;
526
527    #[inline]
528    fn sub(self, other: Value) -> Value {
529        match (self, other) {
530            (Value::Int(a), Value::Int(b)) => Value::Int(a - b),
531            (Value::Float(a), Value::Float(b)) => Value::Float(a - b),
532            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 - b),
533            (Value::Float(a), Value::Int(b)) => Value::Float(a - b as f64),
534            _ => panic!("Cannot subtract these value types"),
535        }
536    }
537}
538
539impl std::ops::Mul for Value {
540    type Output = Value;
541
542    #[inline]
543    fn mul(self, other: Value) -> Value {
544        match (self, other) {
545            (Value::Int(a), Value::Int(b)) => Value::Int(a * b),
546            (Value::Float(a), Value::Float(b)) => Value::Float(a * b),
547            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 * b),
548            (Value::Float(a), Value::Int(b)) => Value::Float(a * b as f64),
549            _ => panic!("Cannot multiply these value types"),
550        }
551    }
552}
553
554impl std::ops::Div for Value {
555    type Output = Value;
556
557    #[inline]
558    fn div(self, other: Value) -> Value {
559        match (self, other) {
560            (Value::Int(a), Value::Int(b)) => Value::Int(a / b),
561            (Value::Float(a), Value::Float(b)) => Value::Float(a / b),
562            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 / b),
563            (Value::Float(a), Value::Int(b)) => Value::Float(a / b as f64),
564            _ => panic!("Cannot divide these value types"),
565        }
566    }
567}
568
569#[cfg(test)]
570mod tests {
571    use super::*;
572
573    #[test]
574    fn value_int_arithmetic() {
575        assert_eq!(Value::Int(10) + Value::Int(3), Value::Int(13));
576        assert_eq!(Value::Int(10) - Value::Int(3), Value::Int(7));
577        assert_eq!(Value::Int(10) * Value::Int(3), Value::Int(30));
578        assert_eq!(Value::Int(10) / Value::Int(3), Value::Int(3));
579    }
580
581    #[test]
582    fn value_float_arithmetic() {
583        assert_eq!(Value::Float(2.5) + Value::Float(1.5), Value::Float(4.0));
584        assert_eq!(Value::Float(5.0) - Value::Float(1.5), Value::Float(3.5));
585        assert_eq!(Value::Float(2.0) * Value::Float(3.0), Value::Float(6.0));
586        assert_eq!(Value::Float(7.0) / Value::Float(2.0), Value::Float(3.5));
587    }
588
589    #[test]
590    fn value_cross_type_promotion() {
591        assert_eq!(Value::Int(2) + Value::Float(1.5), Value::Float(3.5));
592        assert_eq!(Value::Float(2.5) + Value::Int(2), Value::Float(4.5));
593        assert_eq!(Value::Int(3) * Value::Float(2.0), Value::Float(6.0));
594        assert_eq!(Value::Float(6.0) / Value::Int(2), Value::Float(3.0));
595    }
596
597    #[test]
598    fn value_text_concat() {
599        assert_eq!(
600            Value::Text("hello".to_string()) + Value::Text(" world".to_string()),
601            Value::Text("hello world".to_string())
602        );
603    }
604
605    #[test]
606    #[should_panic(expected = "divide by zero")]
607    fn value_div_by_zero_panics() {
608        let _ = Value::Int(1) / Value::Int(0);
609    }
610
611    #[test]
612    #[should_panic(expected = "Cannot add")]
613    fn value_incompatible_types_panic() {
614        let _ = Value::Bool(true) + Value::Int(1);
615    }
616
617    #[test]
618    fn value_display() {
619        assert_eq!(format!("{}", Value::Int(42)), "42");
620        assert_eq!(format!("{}", Value::Float(3.14)), "3.14");
621        assert_eq!(format!("{}", Value::Bool(true)), "true");
622        assert_eq!(format!("{}", Value::Text("hi".to_string())), "hi");
623        assert_eq!(format!("{}", Value::Char('a')), "a");
624        assert_eq!(format!("{}", Value::Nothing), "nothing");
625    }
626
627    #[test]
628    fn value_from_conversions() {
629        assert_eq!(Value::from(42i64), Value::Int(42));
630        assert_eq!(Value::from(3.14f64), Value::Float(3.14));
631        assert_eq!(Value::from(true), Value::Bool(true));
632        assert_eq!(Value::from("hello"), Value::Text("hello".to_string()));
633        assert_eq!(Value::from("hello".to_string()), Value::Text("hello".to_string()));
634        assert_eq!(Value::from('x'), Value::Char('x'));
635    }
636}