avmnif_rs/
term.rs

1extern crate alloc;
2
3use core::ffi::c_void;
4use alloc::{string::{String, ToString}, vec::Vec, boxed::Box};
5
6// Import types from atom module - centralized in atom.rs
7pub use crate::atom::{AtomIndex, AtomTableOps};
8
9// ── Core Type Definitions ───────────────────────────────────────────────────
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct ProcessId(pub u32);
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct PortId(pub u32);
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct RefId(pub u64);
19
20#[derive(Debug, Clone, PartialEq)]
21pub struct FunctionRef {
22    pub module: AtomIndex,
23    pub function: AtomIndex,
24    pub arity: u8,
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub struct ResourceRef {
29    pub type_name: String,
30    pub ptr: *mut c_void,
31}
32
33// ── Core ADT Definition ──────────────────────────────────────────────────────
34
35/// Clean, functional ADT for AtomVM terms
36/// 
37/// This is completely generic - it works with any atom table implementation
38/// through the AtomTableOps trait.
39#[derive(Debug, Clone, PartialEq)]
40pub enum TermValue {
41    // Immediate values
42    SmallInt(i32),
43    Atom(AtomIndex),
44    Nil,
45    
46    // Process identifiers  
47    Pid(ProcessId),
48    Port(PortId),
49    Reference(RefId),
50    
51    // Compound values
52    Tuple(Vec<TermValue>),
53    List(Box<TermValue>, Box<TermValue>), // Head, Tail (proper cons cell)
54    Map(Vec<(TermValue, TermValue)>),     // Key-Value pairs
55    Binary(Vec<u8>),
56    
57    // Special values
58    Function(FunctionRef),
59    Resource(ResourceRef),
60    Float(f64),
61    
62    // Error case
63    Invalid,
64}
65
66// ── Low-level Term (FFI boundary) ────────────────────────────────────────────
67
68/// Low-level term representation for FFI with AtomVM
69/// This handles the bit-level encoding/decoding
70#[derive(Copy, Clone, Debug, PartialEq, Eq)]
71#[repr(transparent)]
72pub struct Term(pub usize);
73
74/// AtomVM Context - opaque pointer to runtime context
75#[repr(C)]
76pub struct Context {
77    pub _private: [u8; 0],
78}
79
80/// AtomVM GlobalContext - runtime global state
81#[repr(C)]
82pub struct GlobalContext {
83    pub _private: [u8; 0],
84}
85
86/// AtomVM Heap for memory allocation
87#[repr(C)] 
88pub struct Heap {
89    pub _private: [u8; 0],
90}
91
92// ── AtomVM Constants ─────────────────────────────────────────────────────────
93
94#[derive(Debug, Copy, Clone, PartialEq, Eq)]
95enum TermType {
96    SmallInt,
97    Atom,
98    Nil,
99    Pid,
100    Port,
101    Reference,
102    Tuple,
103    List,
104    Map,
105    Binary,
106    Function,
107    Resource,
108    Float,
109    Invalid,
110}
111
112impl Term {
113    // AtomVM tag constants (from AtomVM source)
114    const TERM_PRIMARY_MASK: usize = 0x3;
115    const TERM_PRIMARY_IMMED: usize = 0x3;
116    const TERM_PRIMARY_LIST: usize = 0x1;
117    const TERM_PRIMARY_BOXED: usize = 0x2;
118    
119    const TERM_IMMED_TAG_MASK: usize = 0xF;
120    const TERM_INTEGER_TAG: usize = 0xF;
121    const TERM_ATOM_TAG: usize = 0xB;
122    const TERM_PID_TAG: usize = 0x3;
123    const TERM_PORT_TAG: usize = 0x7;
124    
125    const TERM_NIL: usize = 0x3B;
126    
127    const TERM_BOXED_TAG_MASK: usize = 0x3F;
128    const TERM_BOXED_TUPLE: usize = 0x00;
129    const TERM_BOXED_POSITIVE_INTEGER: usize = 0x08;
130    const TERM_BOXED_REF: usize = 0x10;
131    const TERM_BOXED_FUN: usize = 0x18;
132    const TERM_BOXED_FLOAT: usize = 0x20;
133    const TERM_BOXED_REFC_BINARY: usize = 0x28;
134    const TERM_BOXED_HEAP_BINARY: usize = 0x30;
135    const TERM_BOXED_SUB_BINARY: usize = 0x38;
136    const TERM_BOXED_MAP: usize = 0x40;
137    const TERM_BOXED_RESOURCE: usize = 0x48;
138
139    /// Get raw term value
140    pub fn raw(self) -> usize {
141        self.0
142    }
143    
144    /// Create term from raw value
145    pub fn from_raw(raw: usize) -> Self {
146        Term(raw)
147    }
148
149    /// Decode the low-level type of this term
150    fn decode_type(self) -> TermType {
151        if self.0 == Self::TERM_NIL {
152            return TermType::Nil;
153        }
154        
155        match self.0 & Self::TERM_PRIMARY_MASK {
156            Self::TERM_PRIMARY_IMMED => {
157                match self.0 & Self::TERM_IMMED_TAG_MASK {
158                    Self::TERM_INTEGER_TAG => TermType::SmallInt,
159                    Self::TERM_ATOM_TAG => TermType::Atom,
160                    Self::TERM_PID_TAG => TermType::Pid,
161                    Self::TERM_PORT_TAG => TermType::Port,
162                    _ => TermType::Invalid,
163                }
164            }
165            Self::TERM_PRIMARY_LIST => TermType::List,
166            Self::TERM_PRIMARY_BOXED => {
167                let boxed_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
168                if boxed_ptr.is_null() {
169                    return TermType::Invalid;
170                }
171                
172                let header = unsafe { *boxed_ptr };
173                match header & Self::TERM_BOXED_TAG_MASK {
174                    Self::TERM_BOXED_TUPLE => TermType::Tuple,
175                    Self::TERM_BOXED_POSITIVE_INTEGER => TermType::SmallInt,
176                    Self::TERM_BOXED_REF => TermType::Reference,
177                    Self::TERM_BOXED_FUN => TermType::Function,
178                    Self::TERM_BOXED_FLOAT => TermType::Float,
179                    Self::TERM_BOXED_REFC_BINARY |
180                    Self::TERM_BOXED_HEAP_BINARY |
181                    Self::TERM_BOXED_SUB_BINARY => TermType::Binary,
182                    Self::TERM_BOXED_MAP => TermType::Map,
183                    Self::TERM_BOXED_RESOURCE => TermType::Resource,
184                    _ => TermType::Invalid,
185                }
186            }
187            _ => TermType::Invalid,
188        }
189    }
190
191    // ── Low-level extraction methods ─────────────────────────────────────────
192
193    fn extract_small_int(self) -> NifResult<i32> {
194        match self.decode_type() {
195            TermType::SmallInt => {
196                let raw_value = (self.0 & !0xF) as i32 >> 4;
197                Ok(raw_value)
198            }
199            _ => Err(NifError::BadArg),
200        }
201    }
202
203    fn extract_atom_index(self) -> NifResult<AtomIndex> {
204        match self.decode_type() {
205            TermType::Atom => Ok(AtomIndex((self.0 >> 4) as u32)),
206            _ => Err(NifError::BadArg),
207        }
208    }
209
210    fn extract_tuple_arity(self) -> NifResult<usize> {
211        match self.decode_type() {
212            TermType::Tuple => {
213                let boxed_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
214                let header = unsafe { *boxed_ptr };
215                Ok((header >> 6) as usize)
216            }
217            _ => Err(NifError::BadArg),
218        }
219    }
220
221    fn extract_tuple_element(self, index: usize) -> NifResult<Term> {
222        let arity = self.extract_tuple_arity()?;
223        if index >= arity {
224            return Err(NifError::BadArg);
225        }
226        
227        let boxed_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
228        let element = unsafe { *boxed_ptr.add(1 + index) };
229        Ok(Term(element))
230    }
231
232    fn extract_list_head(self) -> NifResult<Term> {
233        match self.decode_type() {
234            TermType::List => {
235                let list_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
236                let head = unsafe { *list_ptr };
237                Ok(Term(head))
238            }
239            _ => Err(NifError::BadArg),
240        }
241    }
242
243    fn extract_list_tail(self) -> NifResult<Term> {
244        match self.decode_type() {
245            TermType::List => {
246                let list_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
247                let tail = unsafe { *list_ptr.add(1) };
248                Ok(Term(tail))
249            }
250            _ => Err(NifError::BadArg),
251        }
252    }
253
254    fn extract_binary_data(self) -> NifResult<&'static [u8]> {
255        match self.decode_type() {
256            TermType::Binary => {
257                let boxed_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
258                let size = unsafe { *boxed_ptr.add(1) };
259                let data_ptr = unsafe { boxed_ptr.add(2) as *const u8 };
260                Ok(unsafe { core::slice::from_raw_parts(data_ptr, size) })
261            }
262            _ => Err(NifError::BadArg),
263        }
264    }
265
266    fn extract_map_size(self) -> NifResult<usize> {
267        match self.decode_type() {
268            TermType::Map => {
269                let boxed_ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *const usize;
270                let size = unsafe { *boxed_ptr.add(1) };
271                Ok(size)
272            }
273            _ => Err(NifError::BadArg),
274        }
275    }
276
277    fn extract_map_key(self, _index: usize) -> NifResult<Term> {
278        // Placeholder - real implementation would traverse map structure
279        Err(NifError::Other("map traversal not implemented"))
280    }
281
282    fn extract_map_value(self, _index: usize) -> NifResult<Term> {
283        // Placeholder - real implementation would traverse map structure  
284        Err(NifError::Other("map traversal not implemented"))
285    }
286
287    fn extract_resource_ptr(self) -> NifResult<*mut c_void> {
288        match self.decode_type() {
289            TermType::Resource => {
290                let ptr = (self.0 & !Self::TERM_PRIMARY_MASK) as *mut c_void;
291                Ok(ptr)
292            }
293            _ => Err(NifError::BadArg),
294        }
295    }
296
297    // ── Low-level encoding methods ───────────────────────────────────────────
298
299    fn encode_small_int(value: i32) -> NifResult<Self> {
300        if value >= -(1 << 27) && value < (1 << 27) {
301            Ok(Term(((value as usize) << 4) | Self::TERM_INTEGER_TAG))
302        } else {
303            Err(NifError::Other("integer too large for small int"))
304        }
305    }
306
307    fn encode_atom(AtomIndex(index): AtomIndex) -> NifResult<Self> {
308        Ok(Term(((index as usize) << 4) | Self::TERM_ATOM_TAG))
309    }
310
311    fn encode_nil() -> Self {
312        Term(Self::TERM_NIL)
313    }
314
315    #[allow(dead_code)]
316    fn encode_tuple(_elements: Vec<Term>, _heap: &mut Heap) -> NifResult<Self> {
317        // Placeholder - would need actual heap allocation
318        Err(NifError::Other("tuple encoding not implemented"))
319    }
320
321    #[allow(dead_code)]
322    fn encode_list(_head: Term, _tail: Term, _heap: &mut Heap) -> NifResult<Self> {
323        // Placeholder - would need actual heap allocation
324        Err(NifError::Other("list encoding not implemented"))
325    }
326
327    #[allow(dead_code)]
328    fn encode_binary(_data: &[u8], _heap: &mut Heap) -> NifResult<Self> {
329        // Placeholder - would need actual heap allocation
330        Err(NifError::Other("binary encoding not implemented"))
331    }
332
333    #[allow(dead_code)]
334    fn encode_map(_pairs: Vec<(Term, Term)>, _heap: &mut Heap) -> NifResult<Self> {
335        // Placeholder - would need actual heap allocation
336        Err(NifError::Other("map encoding not implemented"))
337    }
338}
339
340// ── Conversion Between ADT and Low-level ─────────────────────────────────────
341
342impl Term {
343    /// Convert low-level term to high-level ADT
344    pub fn to_value(self) -> NifResult<TermValue> {
345        match self.decode_type() {
346            TermType::SmallInt => {
347                let val = self.extract_small_int()?;
348                Ok(TermValue::SmallInt(val))
349            }
350            TermType::Atom => {
351                let index = self.extract_atom_index()?;
352                Ok(TermValue::Atom(index))
353            }
354            TermType::Nil => Ok(TermValue::Nil),
355            TermType::Tuple => {
356                let arity = self.extract_tuple_arity()?;
357                let mut elements = Vec::with_capacity(arity);
358                for i in 0..arity {
359                    let elem_term = self.extract_tuple_element(i)?;
360                    elements.push(elem_term.to_value()?);
361                }
362                Ok(TermValue::Tuple(elements))
363            }
364            TermType::List => {
365                let head_term = self.extract_list_head()?;
366                let tail_term = self.extract_list_tail()?;
367                Ok(TermValue::List(
368                    Box::new(head_term.to_value()?),
369                    Box::new(tail_term.to_value()?)
370                ))
371            }
372            TermType::Binary => {
373                let data = self.extract_binary_data()?;
374                Ok(TermValue::Binary(data.to_vec()))
375            }
376            TermType::Map => {
377                let size = self.extract_map_size()?;
378                let mut pairs = Vec::with_capacity(size);
379                for i in 0..size {
380                    let key_term = self.extract_map_key(i)?;
381                    let val_term = self.extract_map_value(i)?;
382                    pairs.push((key_term.to_value()?, val_term.to_value()?));
383                }
384                Ok(TermValue::Map(pairs))
385            }
386            TermType::Resource => {
387                let ptr = self.extract_resource_ptr()?;
388                Ok(TermValue::Resource(ResourceRef {
389                    type_name: "unknown".into(),
390                    ptr,
391                }))
392            }
393            TermType::Pid => {
394                let id = (self.0 >> 4) as u32; // Simplified
395                Ok(TermValue::Pid(ProcessId(id)))
396            }
397            TermType::Port => {
398                let id = (self.0 >> 4) as u32; // Simplified
399                Ok(TermValue::Port(PortId(id)))
400            }
401            _ => Ok(TermValue::Invalid),
402        }
403    }
404    
405    /// Convert high-level ADT to low-level term
406    #[allow(dead_code)]
407    pub fn from_value(value: TermValue, heap: &mut Heap) -> NifResult<Self> {
408        match value {
409            TermValue::SmallInt(i) => Self::encode_small_int(i),
410            TermValue::Atom(idx) => Self::encode_atom(idx),
411            TermValue::Nil => Ok(Self::encode_nil()),
412            
413            TermValue::Tuple(elements) => {
414                let term_elements: Result<Vec<Term>, NifError> = elements
415                    .into_iter()
416                    .map(|elem| Self::from_value(elem, heap))
417                    .collect();
418                Self::encode_tuple(term_elements?, heap)
419            }
420            
421            TermValue::List(head, tail) => {
422                let head_term = Self::from_value(*head, heap)?;
423                let tail_term = Self::from_value(*tail, heap)?;
424                Self::encode_list(head_term, tail_term, heap)
425            }
426            
427            TermValue::Binary(data) => {
428                Self::encode_binary(&data, heap)
429            }
430            
431            TermValue::Map(pairs) => {
432                let term_pairs: Result<Vec<(Term, Term)>, NifError> = pairs
433                    .into_iter()
434                    .map(|(k, v)| Ok((Self::from_value(k, heap)?, Self::from_value(v, heap)?)))
435                    .collect();
436                Self::encode_map(term_pairs?, heap)
437            }
438            
439            _ => Err(NifError::Other("unsupported term type for encoding")),
440        }
441    }
442}
443
444// ── Functional Operations on TermValue (ADT Methods) ─────────────────────────
445
446impl TermValue {
447    /// Pattern match on integers
448    pub fn as_int(&self) -> Option<i32> {
449        match self {
450            TermValue::SmallInt(i) => Some(*i),
451            _ => None,
452        }
453    }
454    
455    /// Pattern match on atoms
456    pub fn as_atom(&self) -> Option<AtomIndex> {
457        match self {
458            TermValue::Atom(idx) => Some(*idx),
459            _ => None,
460        }
461    }
462    
463    /// Pattern match on tuples
464    pub fn as_tuple(&self) -> Option<&[TermValue]> {
465        match self {
466            TermValue::Tuple(elements) => Some(elements),
467            _ => None,
468        }
469    }
470    
471    /// Pattern match on lists (functional style)
472    pub fn as_list(&self) -> Option<(&TermValue, &TermValue)> {
473        match self {
474            TermValue::List(head, tail) => Some((head, tail)),
475            _ => None,
476        }
477    }
478
479    /// Check if this is nil
480    pub fn is_nil(&self) -> bool {
481        matches!(self, TermValue::Nil)
482    }
483
484    /// Check if this is an empty list
485    pub fn is_empty_list(&self) -> bool {
486        self.is_nil()
487    }
488    
489    /// Fold over list elements (functional programming!)
490    pub fn fold_list<T, F>(&self, init: T, f: F) -> T 
491    where 
492        F: Fn(T, &TermValue) -> T,
493    {
494        match self {
495            TermValue::Nil => init,
496            TermValue::List(head, tail) => {
497                let acc = f(init, head);
498                tail.fold_list(acc, f)
499            }
500            _ => init, // Not a list
501        }
502    }
503    
504    /// Map over list elements  
505    pub fn map_list<F>(&self, f: F) -> TermValue
506    where
507        F: Fn(&TermValue) -> TermValue + Clone,
508    {
509        match self {
510            TermValue::Nil => TermValue::Nil,
511            TermValue::List(head, tail) => {
512                TermValue::List(
513                    Box::new(f(head)),
514                    Box::new(tail.map_list(f))
515                )
516            }
517            _ => self.clone(), // Not a list
518        }
519    }
520
521    /// Filter list elements
522    pub fn filter_list<F>(&self, predicate: F) -> TermValue
523    where
524        F: Fn(&TermValue) -> bool + Clone,
525    {
526        match self {
527            TermValue::Nil => TermValue::Nil,
528            TermValue::List(head, tail) => {
529                let filtered_tail = tail.filter_list(predicate.clone());
530                if predicate(head) {
531                    TermValue::List(head.clone(), Box::new(filtered_tail))
532                } else {
533                    filtered_tail
534                }
535            }
536            _ => self.clone(),
537        }
538    }
539
540    /// Get list length
541    pub fn list_length(&self) -> usize {
542        self.fold_list(0, |acc, _| acc + 1)
543    }
544
545    /// Convert list to Vec
546    pub fn list_to_vec(&self) -> Vec<TermValue> {
547        let mut result = Vec::new();
548        let mut current = self;
549        
550        loop {
551            match current {
552                TermValue::Nil => break,
553                TermValue::List(head, tail) => {
554                    result.push((**head).clone());
555                    current = tail;
556                }
557                _ => break,
558            }
559        }
560        
561        result
562    }
563    
564    /// Get map value by key (functional lookup)
565    pub fn map_get(&self, key: &TermValue) -> Option<&TermValue> {
566        match self {
567            TermValue::Map(pairs) => {
568                pairs.iter()
569                    .find(|(k, _)| k == key)
570                    .map(|(_, v)| v)
571            }
572            _ => None,
573        }
574    }
575
576    /// Set map value (returns new map)
577    pub fn map_set(&self, key: TermValue, value: TermValue) -> TermValue {
578        match self {
579            TermValue::Map(pairs) => {
580                let mut new_pairs = pairs.clone();
581                
582                // Update existing key or add new one
583                if let Some(pos) = new_pairs.iter().position(|(k, _)| k == &key) {
584                    new_pairs[pos] = (key, value);
585                } else {
586                    new_pairs.push((key, value));
587                }
588                
589                TermValue::Map(new_pairs)
590            }
591            _ => self.clone(),
592        }
593    }
594    
595    /// Construct list from iterator (functional construction)
596    pub fn from_iter<I>(iter: I) -> TermValue 
597    where 
598        I: IntoIterator<Item = TermValue>,
599        I::IntoIter: DoubleEndedIterator,
600    {
601        iter.into_iter()
602            .rev()
603            .fold(TermValue::Nil, |acc, elem| {
604                TermValue::List(Box::new(elem), Box::new(acc))
605            })
606    }
607
608    /// Construct proper list from Vec
609    pub fn from_vec(elements: Vec<TermValue>) -> TermValue {
610        Self::from_iter(elements)
611    }
612}
613
614// ── Generic Smart Constructors ──────────────────────────────────────────────
615
616impl TermValue {
617    pub fn int(value: i32) -> Self {
618        TermValue::SmallInt(value)
619    }
620    
621    /// Create atom using any atom table (GENERIC!)
622    pub fn atom<T: AtomTableOps>(name: &str, table: &T) -> Self {
623        let index = table.ensure_atom_str(name).unwrap_or_else(|_| AtomIndex(0));
624        TermValue::Atom(index)
625    }
626    
627    pub fn tuple(elements: Vec<TermValue>) -> Self {
628        TermValue::Tuple(elements)
629    }
630    
631    pub fn list(elements: Vec<TermValue>) -> Self {
632        Self::from_vec(elements)
633    }
634    
635    pub fn binary(data: Vec<u8>) -> Self {
636        TermValue::Binary(data)
637    }
638    
639    pub fn map(pairs: Vec<(TermValue, TermValue)>) -> Self {
640        TermValue::Map(pairs)
641    }
642
643    pub fn pid(id: u32) -> Self {
644        TermValue::Pid(ProcessId(id))
645    }
646
647    pub fn port(id: u32) -> Self {
648        TermValue::Port(PortId(id))
649    }
650
651    pub fn reference(id: u64) -> Self {
652        TermValue::Reference(RefId(id))
653    }
654
655    pub fn float(value: f64) -> Self {
656        TermValue::Float(value)
657    }
658}
659
660// ── Generic Convenience Methods ─────────────────────────────────────────────
661
662impl TermValue {
663    /// Extract integer with default
664    pub fn to_int_or(&self, default: i32) -> i32 {
665        self.as_int().unwrap_or(default)
666    }
667
668    /// Extract tuple element by index
669    pub fn tuple_get(&self, index: usize) -> Option<&TermValue> {
670        self.as_tuple()?.get(index)
671    }
672
673    /// Extract tuple arity
674    pub fn tuple_arity(&self) -> usize {
675        self.as_tuple().map(|t| t.len()).unwrap_or(0)
676    }
677
678    /// Example: Sum all integers in a list
679    pub fn sum_list(&self) -> i32 {
680        self.fold_list(0, |acc, elem| {
681            acc + elem.as_int().unwrap_or(0)
682        })
683    }
684    
685    /// Example: Convert list of integers to list of their doubles
686    pub fn double_ints(&self) -> TermValue {
687        self.map_list(|elem| {
688            match elem.as_int() {
689                Some(i) => TermValue::int(i * 2),
690                None => elem.clone(),
691            }
692        })
693    }
694
695    /// Check if atom matches string using any atom table (GENERIC!)
696    pub fn is_atom_str<T: AtomTableOps>(&self, name: &str, table: &T) -> bool {
697        match self.as_atom() {
698            Some(idx) => table.atom_equals_str(idx, name),
699            None => false,
700        }
701    }
702
703    /// Get atom as string using any atom table (GENERIC!)
704    pub fn as_atom_str<T: AtomTableOps>(&self, table: &T) -> Option<String> {
705        match self.as_atom() {
706            Some(idx) => {
707                // Use the get_atom_string method from AtomTableOps
708                if let Ok(atom_ref) = table.get_atom_string(idx) {
709                    if let Ok(atom_str) = atom_ref.as_str() {
710                        return Some(atom_str.to_string());
711                    }
712                }
713                
714                // Fallback to checking common atoms (for compatibility)
715                let common_atoms = ["ok", "error", "true", "false", "undefined", "badarg", "nil"];
716                for atom_name in &common_atoms {
717                    if table.atom_equals_str(idx, atom_name) {
718                        return Some(atom_name.to_string());
719                    }
720                }
721                None
722            }
723            None => None,
724        }
725    }
726}
727
728// ── Error Types ──────────────────────────────────────────────────────────────
729
730#[derive(Debug, Clone, PartialEq, Eq)]
731pub enum TermError {
732    /// Wrong type for operation (covers most type mismatches)
733    WrongType,
734    /// Index/key out of bounds  
735    OutOfBounds,
736    /// Memory allocation failed
737    OutOfMemory,
738    /// Invalid UTF-8 in binary
739    InvalidUtf8,
740    /// Generic error with message
741    Other(String),
742}
743
744#[derive(Debug, Clone, PartialEq, Eq)]
745pub enum NifError {
746    BadArg,
747    BadArity, 
748    OutOfMemory,
749    SystemLimit,
750    InvalidTerm,
751    Other(&'static str),
752}
753
754impl From<&'static str> for NifError {
755    fn from(s: &'static str) -> Self {
756        NifError::Other(s)
757    }
758}
759
760pub type NifResult<T> = core::result::Result<T, NifError>;
761
762// ── Generic Constructor Macros ──────────────────────────────────────────────
763
764/// These macros now require an atom table parameter for full genericity
765
766#[macro_export]
767macro_rules! atom_with_table {
768    ($name:literal, $table:expr) => {
769        TermValue::atom($name, $table)
770    };
771}
772
773#[macro_export]
774macro_rules! tuple {
775    ($($elem:expr),* $(,)?) => {
776        TermValue::tuple(alloc::vec![$($elem),*])
777    };
778}
779
780#[macro_export]
781macro_rules! list {
782    ($($elem:expr),* $(,)?) => {
783        TermValue::list(alloc::vec![$($elem),*])
784    };
785}
786
787#[macro_export]
788macro_rules! map {
789    ($($key:expr => $val:expr),* $(,)?) => {
790        TermValue::map(alloc::vec![$(($key, $val)),*])
791    };
792}