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