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: PartialEq> LogosContains<T> for LogosSeq<T> {
175    #[inline(always)]
176    fn logos_contains(&self, value: &T) -> bool {
177        self.0.borrow().contains(value)
178    }
179}
180
181impl<T: Clone> IntoIterator for LogosSeq<T> {
182    type Item = T;
183    type IntoIter = std::vec::IntoIter<T>;
184
185    fn into_iter(self) -> Self::IntoIter {
186        self.to_vec().into_iter()
187    }
188}
189
190/// Key-value mapping with reference semantics.
191///
192/// `LogosMap<K, V>` wraps `Rc<RefCell<FxHashMap<K, V>>>` to provide shared mutable access.
193/// Cloning a `LogosMap` produces a shallow copy (shared reference), not a deep copy.
194/// Use `.deep_clone()` for an independent copy (LOGOS `copy of`).
195#[derive(Debug)]
196pub struct LogosMap<K, V>(pub Rc<RefCell<rustc_hash::FxHashMap<K, V>>>);
197
198impl<K: Eq + Hash, V> LogosMap<K, V> {
199    pub fn new() -> Self {
200        Self(Rc::new(RefCell::new(rustc_hash::FxHashMap::default())))
201    }
202
203    pub fn with_capacity(cap: usize) -> Self {
204        Self(Rc::new(RefCell::new(
205            rustc_hash::FxHashMap::with_capacity_and_hasher(cap, Default::default()),
206        )))
207    }
208
209    pub fn from_map(m: rustc_hash::FxHashMap<K, V>) -> Self {
210        Self(Rc::new(RefCell::new(m)))
211    }
212
213    pub fn insert(&self, key: K, value: V) -> Option<V> {
214        self.0.borrow_mut().insert(key, value)
215    }
216
217    pub fn remove(&self, key: &K) -> Option<V> {
218        self.0.borrow_mut().remove(key)
219    }
220
221    pub fn len(&self) -> usize {
222        self.0.borrow().len()
223    }
224
225    pub fn is_empty(&self) -> bool {
226        self.0.borrow().is_empty()
227    }
228
229    pub fn contains_key(&self, key: &K) -> bool {
230        self.0.borrow().contains_key(key)
231    }
232
233    pub fn borrow(&self) -> std::cell::Ref<'_, rustc_hash::FxHashMap<K, V>> {
234        self.0.borrow()
235    }
236
237    pub fn borrow_mut(&self) -> std::cell::RefMut<'_, rustc_hash::FxHashMap<K, V>> {
238        self.0.borrow_mut()
239    }
240}
241
242impl<K: Eq + Hash + Clone, V: Clone> LogosMap<K, V> {
243    pub fn deep_clone(&self) -> Self {
244        Self(Rc::new(RefCell::new(self.0.borrow().clone())))
245    }
246
247    pub fn get(&self, key: &K) -> Option<V> {
248        self.0.borrow().get(key).cloned()
249    }
250}
251
252impl<K, V> Clone for LogosMap<K, V> {
253    fn clone(&self) -> Self {
254        Self(Rc::clone(&self.0))
255    }
256}
257
258impl<K: Eq + Hash, V> Default for LogosMap<K, V> {
259    fn default() -> Self {
260        Self::new()
261    }
262}
263
264impl<K: PartialEq + Eq + Hash, V: PartialEq> PartialEq for LogosMap<K, V> {
265    fn eq(&self, other: &Self) -> bool {
266        *self.0.borrow() == *other.0.borrow()
267    }
268}
269
270impl<K: std::fmt::Display + Eq + Hash, V: std::fmt::Display> std::fmt::Display for LogosMap<K, V> {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        let inner = self.0.borrow();
273        write!(f, "{{")?;
274        for (i, (k, v)) in inner.iter().enumerate() {
275            if i > 0 { write!(f, ", ")?; }
276            write!(f, "{}: {}", k, v)?;
277        }
278        write!(f, "}}")
279    }
280}
281
282impl<K: Eq + Hash, V> LogosContains<K> for LogosMap<K, V> {
283    #[inline(always)]
284    fn logos_contains(&self, key: &K) -> bool {
285        self.0.borrow().contains_key(key)
286    }
287}
288
289/// Ordered sequences with reference semantics.
290pub type Seq<T> = LogosSeq<T>;
291
292/// Key-value mappings with reference semantics.
293pub type Map<K, V> = LogosMap<K, V>;
294
295/// Unordered collections of unique elements with FxHash.
296pub type Set<T> = rustc_hash::FxHashSet<T>;
297
298/// Unified containment testing for all collection types.
299///
300/// This trait provides a consistent `logos_contains` method across Logos's
301/// collection types, abstracting over the different containment semantics
302/// of vectors (by value), sets (by membership), maps (by key), and
303/// strings (by substring or character).
304///
305/// # Implementations
306///
307/// - [`Vec<T>`]: Tests if the vector contains an element equal to the value
308/// - [`HashSet<T>`]: Tests if the element is a member of the set
309/// - [`HashMap<K, V>`]: Tests if a key exists in the map
310/// - [`String`]: Tests for substring (`&str`) or character (`char`) presence
311/// - [`ORSet<T, B>`]: Tests if the element is in the CRDT set
312///
313/// # Examples
314///
315/// ```
316/// use logicaffeine_data::LogosContains;
317///
318/// // Vector: contains by value equality
319/// let v = vec![1, 2, 3];
320/// assert!(v.logos_contains(&2));
321/// assert!(!v.logos_contains(&5));
322///
323/// // String: contains by substring
324/// let s = String::from("hello world");
325/// assert!(s.logos_contains(&"world"));
326///
327/// // String: contains by character
328/// assert!(s.logos_contains(&'o'));
329/// ```
330pub trait LogosContains<T> {
331    /// Check if this collection contains the given value.
332    fn logos_contains(&self, value: &T) -> bool;
333}
334
335impl<T: PartialEq> LogosContains<T> for Vec<T> {
336    #[inline(always)]
337    fn logos_contains(&self, value: &T) -> bool {
338        self.contains(value)
339    }
340}
341
342impl<T: PartialEq> LogosContains<T> for [T] {
343    #[inline(always)]
344    fn logos_contains(&self, value: &T) -> bool {
345        self.contains(value)
346    }
347}
348
349impl<T: Eq + Hash> LogosContains<T> for rustc_hash::FxHashSet<T> {
350    #[inline(always)]
351    fn logos_contains(&self, value: &T) -> bool {
352        self.contains(value)
353    }
354}
355
356impl<K: Eq + Hash, V> LogosContains<K> for rustc_hash::FxHashMap<K, V> {
357    #[inline(always)]
358    fn logos_contains(&self, key: &K) -> bool {
359        self.contains_key(key)
360    }
361}
362
363impl LogosContains<&str> for String {
364    #[inline(always)]
365    fn logos_contains(&self, value: &&str) -> bool {
366        self.contains(*value)
367    }
368}
369
370impl LogosContains<String> for String {
371    #[inline(always)]
372    fn logos_contains(&self, value: &String) -> bool {
373        self.contains(value.as_str())
374    }
375}
376
377impl LogosContains<char> for String {
378    #[inline(always)]
379    fn logos_contains(&self, value: &char) -> bool {
380        self.contains(*value)
381    }
382}
383
384impl<T: Eq + Hash + Clone, B: crate::crdt::SetBias> LogosContains<T>
385    for crate::crdt::ORSet<T, B>
386{
387    #[inline(always)]
388    fn logos_contains(&self, value: &T) -> bool {
389        self.contains(value)
390    }
391}
392
393/// Dynamic value type for heterogeneous collections.
394///
395/// `Value` enables tuples and other heterogeneous data structures in Logos.
396/// It supports basic arithmetic between compatible types and provides
397/// runtime type coercion where sensible.
398///
399/// # Variants
400///
401/// - `Int(i64)` - Integer values
402/// - `Float(f64)` - Floating-point values
403/// - `Bool(bool)` - Boolean values
404/// - `Text(String)` - String values
405/// - `Char(char)` - Single character values
406/// - `Nothing` - Unit/null value
407///
408/// # Arithmetic
409///
410/// Arithmetic operations are supported between numeric types:
411/// - `Int op Int` → `Int`
412/// - `Float op Float` → `Float`
413/// - `Int op Float` or `Float op Int` → `Float` (promotion)
414/// - `Text + Text` → `Text` (concatenation)
415///
416/// # Panics
417///
418/// Arithmetic on incompatible variants panics at runtime.
419///
420/// # Examples
421///
422/// ```
423/// use logicaffeine_data::Value;
424///
425/// let a = Value::Int(10);
426/// let b = Value::Int(3);
427/// assert_eq!(a + b, Value::Int(13));
428///
429/// let x = Value::Float(2.5);
430/// let y = Value::Int(2);
431/// assert_eq!(x * y, Value::Float(5.0));
432/// ```
433#[derive(Clone, Debug, PartialEq)]
434pub enum Value {
435    /// Integer values.
436    Int(i64),
437    /// Floating-point values.
438    Float(f64),
439    /// Boolean values.
440    Bool(bool),
441    /// String values.
442    Text(String),
443    /// Single character values.
444    Char(char),
445    /// Unit/null value.
446    Nothing,
447}
448
449impl std::fmt::Display for Value {
450    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451        match self {
452            Value::Int(n) => write!(f, "{}", n),
453            Value::Float(n) => write!(f, "{}", n),
454            Value::Bool(b) => write!(f, "{}", b),
455            Value::Text(s) => write!(f, "{}", s),
456            Value::Char(c) => write!(f, "{}", c),
457            Value::Nothing => write!(f, "nothing"),
458        }
459    }
460}
461
462// Conversion traits for Value
463impl From<i64> for Value {
464    fn from(n: i64) -> Self { Value::Int(n) }
465}
466
467impl From<f64> for Value {
468    fn from(n: f64) -> Self { Value::Float(n) }
469}
470
471impl From<bool> for Value {
472    fn from(b: bool) -> Self { Value::Bool(b) }
473}
474
475impl From<String> for Value {
476    fn from(s: String) -> Self { Value::Text(s) }
477}
478
479impl From<&str> for Value {
480    fn from(s: &str) -> Self { Value::Text(s.to_string()) }
481}
482
483impl From<char> for Value {
484    fn from(c: char) -> Self { Value::Char(c) }
485}
486
487/// Tuple type: Vec of heterogeneous Values (uses LogosIndex from indexing module)
488pub type Tuple = Vec<Value>;
489
490// NOTE: Showable impl for Value is in logicaffeine_system (io module)
491// This crate (logicaffeine_data) has NO IO dependencies.
492
493// Arithmetic operations for Value
494impl std::ops::Add for Value {
495    type Output = Value;
496
497    #[inline]
498    fn add(self, other: Value) -> Value {
499        match (self, other) {
500            (Value::Int(a), Value::Int(b)) => Value::Int(a + b),
501            (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
502            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 + b),
503            (Value::Float(a), Value::Int(b)) => Value::Float(a + b as f64),
504            (Value::Text(a), Value::Text(b)) => Value::Text(format!("{}{}", a, b)),
505            _ => panic!("Cannot add these value types"),
506        }
507    }
508}
509
510impl std::ops::Sub for Value {
511    type Output = Value;
512
513    #[inline]
514    fn sub(self, other: Value) -> Value {
515        match (self, other) {
516            (Value::Int(a), Value::Int(b)) => Value::Int(a - b),
517            (Value::Float(a), Value::Float(b)) => Value::Float(a - b),
518            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 - b),
519            (Value::Float(a), Value::Int(b)) => Value::Float(a - b as f64),
520            _ => panic!("Cannot subtract these value types"),
521        }
522    }
523}
524
525impl std::ops::Mul for Value {
526    type Output = Value;
527
528    #[inline]
529    fn mul(self, other: Value) -> Value {
530        match (self, other) {
531            (Value::Int(a), Value::Int(b)) => Value::Int(a * b),
532            (Value::Float(a), Value::Float(b)) => Value::Float(a * b),
533            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 * b),
534            (Value::Float(a), Value::Int(b)) => Value::Float(a * b as f64),
535            _ => panic!("Cannot multiply these value types"),
536        }
537    }
538}
539
540impl std::ops::Div for Value {
541    type Output = Value;
542
543    #[inline]
544    fn div(self, other: Value) -> Value {
545        match (self, other) {
546            (Value::Int(a), Value::Int(b)) => Value::Int(a / b),
547            (Value::Float(a), Value::Float(b)) => Value::Float(a / b),
548            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 / b),
549            (Value::Float(a), Value::Int(b)) => Value::Float(a / b as f64),
550            _ => panic!("Cannot divide these value types"),
551        }
552    }
553}
554
555#[cfg(test)]
556mod tests {
557    use super::*;
558
559    #[test]
560    fn value_int_arithmetic() {
561        assert_eq!(Value::Int(10) + Value::Int(3), Value::Int(13));
562        assert_eq!(Value::Int(10) - Value::Int(3), Value::Int(7));
563        assert_eq!(Value::Int(10) * Value::Int(3), Value::Int(30));
564        assert_eq!(Value::Int(10) / Value::Int(3), Value::Int(3));
565    }
566
567    #[test]
568    fn value_float_arithmetic() {
569        assert_eq!(Value::Float(2.5) + Value::Float(1.5), Value::Float(4.0));
570        assert_eq!(Value::Float(5.0) - Value::Float(1.5), Value::Float(3.5));
571        assert_eq!(Value::Float(2.0) * Value::Float(3.0), Value::Float(6.0));
572        assert_eq!(Value::Float(7.0) / Value::Float(2.0), Value::Float(3.5));
573    }
574
575    #[test]
576    fn value_cross_type_promotion() {
577        assert_eq!(Value::Int(2) + Value::Float(1.5), Value::Float(3.5));
578        assert_eq!(Value::Float(2.5) + Value::Int(2), Value::Float(4.5));
579        assert_eq!(Value::Int(3) * Value::Float(2.0), Value::Float(6.0));
580        assert_eq!(Value::Float(6.0) / Value::Int(2), Value::Float(3.0));
581    }
582
583    #[test]
584    fn value_text_concat() {
585        assert_eq!(
586            Value::Text("hello".to_string()) + Value::Text(" world".to_string()),
587            Value::Text("hello world".to_string())
588        );
589    }
590
591    #[test]
592    #[should_panic(expected = "divide by zero")]
593    fn value_div_by_zero_panics() {
594        let _ = Value::Int(1) / Value::Int(0);
595    }
596
597    #[test]
598    #[should_panic(expected = "Cannot add")]
599    fn value_incompatible_types_panic() {
600        let _ = Value::Bool(true) + Value::Int(1);
601    }
602
603    #[test]
604    fn value_display() {
605        assert_eq!(format!("{}", Value::Int(42)), "42");
606        assert_eq!(format!("{}", Value::Float(3.14)), "3.14");
607        assert_eq!(format!("{}", Value::Bool(true)), "true");
608        assert_eq!(format!("{}", Value::Text("hi".to_string())), "hi");
609        assert_eq!(format!("{}", Value::Char('a')), "a");
610        assert_eq!(format!("{}", Value::Nothing), "nothing");
611    }
612
613    #[test]
614    fn value_from_conversions() {
615        assert_eq!(Value::from(42i64), Value::Int(42));
616        assert_eq!(Value::from(3.14f64), Value::Float(3.14));
617        assert_eq!(Value::from(true), Value::Bool(true));
618        assert_eq!(Value::from("hello"), Value::Text("hello".to_string()));
619        assert_eq!(Value::from("hello".to_string()), Value::Text("hello".to_string()));
620        assert_eq!(Value::from('x'), Value::Char('x'));
621    }
622}