pub struct Lisp<const N: usize> { /* private fields */ }Expand description
A Lisp execution context wrapping an arena
§Reserved Slots
The first 4 slots of the arena are reserved for singleton values:
- Slot 0:
Value::Nil- the empty list - Slot 1:
Value::True- boolean true (#t) - Slot 2:
Value::False- boolean false (#f) - Slot 3:
Value::Cons- intern table reference cell (car = intern table root)
These slots are pre-allocated during Lisp::new() and returned as
constants from nil(), true_val(), and false_val(). This optimization
avoids allocating new slots for these frequently-used values.
§Symbol Interning
All symbols are interned in an association list stored in the arena.
The intern_table_slot field points to a cons cell whose car is the alist
of (string_index . symbol_index) pairs. The cons cell is used as a
“reference cell” to allow updating the intern table without RefCell.
When creating a symbol, we first check if it already exists in the table.
This ensures that the same symbol name always returns the same index.
Implementations§
Source§impl<const N: usize> Lisp<N>
impl<const N: usize> Lisp<N>
Sourcepub fn new() -> Lisp<N>
pub fn new() -> Lisp<N>
Create a new Lisp context
Pre-allocates reserved slots for Nil, True, False, and intern table ref cell.
These slots (0, 1, 2, 3) are never freed and are returned as constants
from nil(), true_val(), and false_val().
The intern table is initialized to nil (empty alist).
§Panics
Panics if the arena capacity N < RESERVED_SLOTS, as we need at least 4 slots for the reserved singleton values and intern table reference cell.
Sourcepub fn alloc(&self, value: Value) -> Result<ArenaIndex, ArenaError>
pub fn alloc(&self, value: Value) -> Result<ArenaIndex, ArenaError>
Allocate a value
Sourcepub fn get(&self, index: ArenaIndex) -> Result<Value, ArenaError>
pub fn get(&self, index: ArenaIndex) -> Result<Value, ArenaError>
Get a value
Sourcepub fn set(&self, index: ArenaIndex, value: Value) -> Result<(), ArenaError>
pub fn set(&self, index: ArenaIndex, value: Value) -> Result<(), ArenaError>
Set a value
Sourcepub fn arena_index_at_offset(
&self,
base: ArenaIndex,
offset: usize,
) -> Result<ArenaIndex, ArenaError>
pub fn arena_index_at_offset( &self, base: ArenaIndex, offset: usize, ) -> Result<ArenaIndex, ArenaError>
Get an arena index at a given offset from a base index.
This is useful for accessing elements in contiguous storage (strings, arrays).
Sourcepub fn nil(&self) -> Result<ArenaIndex, ArenaError>
pub fn nil(&self) -> Result<ArenaIndex, ArenaError>
Get the pre-allocated Nil singleton (empty list)
This returns the reserved slot 0 which always contains Value::Nil.
No allocation is performed.
Sourcepub fn true_val(&self) -> Result<ArenaIndex, ArenaError>
pub fn true_val(&self) -> Result<ArenaIndex, ArenaError>
Get the pre-allocated True singleton (#t)
This returns the reserved slot 1 which always contains Value::True.
No allocation is performed.
Sourcepub fn false_val(&self) -> Result<ArenaIndex, ArenaError>
pub fn false_val(&self) -> Result<ArenaIndex, ArenaError>
Get the pre-allocated False singleton (#f)
This returns the reserved slot 2 which always contains Value::False.
No allocation is performed.
Sourcepub fn boolean(&self, b: bool) -> Result<ArenaIndex, ArenaError>
pub fn boolean(&self, b: bool) -> Result<ArenaIndex, ArenaError>
Allocate a boolean based on a Rust bool
Sourcepub fn number(&self, n: isize) -> Result<ArenaIndex, ArenaError>
pub fn number(&self, n: isize) -> Result<ArenaIndex, ArenaError>
Allocate a number
Sourcepub fn float(&self, f: f64) -> Result<ArenaIndex, ArenaError>
pub fn float(&self, f: f64) -> Result<ArenaIndex, ArenaError>
Allocate a floating-point number
Sourcepub fn char(&self, c: char) -> Result<ArenaIndex, ArenaError>
pub fn char(&self, c: char) -> Result<ArenaIndex, ArenaError>
Allocate a character
Sourcepub fn cons(
&self,
car: ArenaIndex,
cdr: ArenaIndex,
) -> Result<ArenaIndex, ArenaError>
pub fn cons( &self, car: ArenaIndex, cdr: ArenaIndex, ) -> Result<ArenaIndex, ArenaError>
Allocate a cons cell
Sourcepub fn car(&self, index: ArenaIndex) -> Result<ArenaIndex, ArenaError>
pub fn car(&self, index: ArenaIndex) -> Result<ArenaIndex, ArenaError>
Get car of a cons cell
In Scheme R7RS, car of an empty list is an error.
Sourcepub fn cdr(&self, index: ArenaIndex) -> Result<ArenaIndex, ArenaError>
pub fn cdr(&self, index: ArenaIndex) -> Result<ArenaIndex, ArenaError>
Get cdr of a cons cell
In Scheme R7RS, cdr of an empty list is an error.
Sourcepub fn set_car(
&self,
index: ArenaIndex,
new_car: ArenaIndex,
) -> Result<ArenaIndex, ArenaError>
pub fn set_car( &self, index: ArenaIndex, new_car: ArenaIndex, ) -> Result<ArenaIndex, ArenaError>
Set car of a cons cell (mutation operation) Returns the new value on success
Sourcepub fn set_cdr(
&self,
index: ArenaIndex,
new_cdr: ArenaIndex,
) -> Result<ArenaIndex, ArenaError>
pub fn set_cdr( &self, index: ArenaIndex, new_cdr: ArenaIndex, ) -> Result<ArenaIndex, ArenaError>
Set cdr of a cons cell (mutation operation) Returns the new value on success
Sourcepub fn intern_table(&self) -> ArenaIndex
pub fn intern_table(&self) -> ArenaIndex
Get the intern table root (for GC roots)
The intern table is stored in the car of the intern_table_slot cons cell.
Sourcepub fn symbol(&self, name: &str) -> Result<ArenaIndex, ArenaError>
pub fn symbol(&self, name: &str) -> Result<ArenaIndex, ArenaError>
Create or retrieve an interned symbol from a string slice
The symbol’s chars field points to a Value::String with the symbol name.
Symbol interning ensures the same symbol name always returns the same index.
This provides ~44% memory savings compared to linked list representation.
Sourcepub fn symbol_from_bytes(&self, bytes: &[u8]) -> Result<ArenaIndex, ArenaError>
pub fn symbol_from_bytes(&self, bytes: &[u8]) -> Result<ArenaIndex, ArenaError>
Create or retrieve an interned symbol from bytes (for parsing)
Sourcepub fn builtin(&self, b: Builtin) -> Result<ArenaIndex, ArenaError>
pub fn builtin(&self, b: Builtin) -> Result<ArenaIndex, ArenaError>
Allocate a builtin function
Sourcepub fn stdlib(&self, s: StdLib) -> Result<ArenaIndex, ArenaError>
pub fn stdlib(&self, s: StdLib) -> Result<ArenaIndex, ArenaError>
Allocate a stdlib function
StdLib functions are stored in static memory with on-demand parsing. The function body is parsed on first call and cached for reuse.
Sourcepub fn stdlib_cache(
&self,
index: ArenaIndex,
) -> Result<Option<(ArenaIndex, ArenaIndex)>, ArenaError>
pub fn stdlib_cache( &self, index: ArenaIndex, ) -> Result<Option<(ArenaIndex, ArenaIndex)>, ArenaError>
Get cached body and params from a StdLib value.
Returns Some((body, params)) if cached, None if not yet parsed.
Sourcepub fn set_stdlib_cache(
&self,
index: ArenaIndex,
body: ArenaIndex,
params: ArenaIndex,
) -> Result<(), ArenaError>
pub fn set_stdlib_cache( &self, index: ArenaIndex, body: ArenaIndex, params: ArenaIndex, ) -> Result<(), ArenaError>
Set the cached body and params for a StdLib value.
Sourcepub fn native(
&self,
id: usize,
name_hash: usize,
) -> Result<ArenaIndex, ArenaError>
pub fn native( &self, id: usize, name_hash: usize, ) -> Result<ArenaIndex, ArenaError>
Allocate a native function reference.
Native functions are Rust functions registered with the evaluator.
The id is the index in the NativeRegistry, and name_hash is
a simple hash for verification.
Sourcepub fn lambda(
&self,
params: ArenaIndex,
body: ArenaIndex,
env: ArenaIndex,
) -> Result<ArenaIndex, ArenaError>
pub fn lambda( &self, params: ArenaIndex, body: ArenaIndex, env: ArenaIndex, ) -> Result<ArenaIndex, ArenaError>
Allocate a lambda
Stores lambda data as a linked structure in the arena: (params . (body . env))
Sourcepub fn lambda_parts(
&self,
index: ArenaIndex,
) -> Result<(ArenaIndex, ArenaIndex, ArenaIndex), ArenaError>
pub fn lambda_parts( &self, index: ArenaIndex, ) -> Result<(ArenaIndex, ArenaIndex, ArenaIndex), ArenaError>
Extract parts from a lambda: (params, body, env)
Lambda data is stored as (params . (body . env)).
Sourcepub fn list<I>(&self, items: I) -> Result<ArenaIndex, ArenaError>
pub fn list<I>(&self, items: I) -> Result<ArenaIndex, ArenaError>
Build a list from an iterator of indices
Sourcepub fn list_len(&self, list: ArenaIndex) -> Result<usize, ArenaError>
pub fn list_len(&self, list: ArenaIndex) -> Result<usize, ArenaError>
Get the length of a list
Sourcepub fn symbol_eq(
&self,
a: ArenaIndex,
b: ArenaIndex,
) -> Result<bool, ArenaError>
pub fn symbol_eq( &self, a: ArenaIndex, b: ArenaIndex, ) -> Result<bool, ArenaError>
Check if two symbols are equal.
Symbols are compared by their underlying string content.
Sourcepub fn symbol_matches(
&self,
sym: ArenaIndex,
name: &str,
) -> Result<bool, ArenaError>
pub fn symbol_matches( &self, sym: ArenaIndex, name: &str, ) -> Result<bool, ArenaError>
Check if a symbol matches a string.
Sourcepub fn symbol_to_bytes(
&self,
sym: ArenaIndex,
buf: &mut [u8],
) -> Result<usize, ArenaError>
pub fn symbol_to_bytes( &self, sym: ArenaIndex, buf: &mut [u8], ) -> Result<usize, ArenaError>
Extract symbol name to a fixed buffer.
Sourcepub fn symbol_len(&self, sym: ArenaIndex) -> Result<usize, ArenaError>
pub fn symbol_len(&self, sym: ArenaIndex) -> Result<usize, ArenaError>
Get the length of a symbol’s name.
Sourcepub fn symbol_char_at(
&self,
sym: ArenaIndex,
index: usize,
) -> Result<Option<char>, ArenaError>
pub fn symbol_char_at( &self, sym: ArenaIndex, index: usize, ) -> Result<Option<char>, ArenaError>
Get a character at a specific index within a symbol’s name Returns None if the index is out of bounds or if the value is not a symbol
Sourcepub fn gc(&self, roots: &[ArenaIndex]) -> GcStats
pub fn gc(&self, roots: &[ArenaIndex]) -> GcStats
Run garbage collection with intern table as an additional root
The intern table reference cell (slot 3) is always included as a GC root to prevent interned symbols from being collected. The intern table is stored as a cons cell whose car points to the alist of interned symbols.
§Panics
Panics if the number of roots exceeds the internal limit (512 roots). This limit is chosen to balance stack usage in no_std environments with typical program needs. Most Lisp programs use far fewer roots.
Sourcepub fn alloc_or_gc(
&self,
value: Value,
roots: &[ArenaIndex],
) -> Result<ArenaIndex, ArenaError>
pub fn alloc_or_gc( &self, value: Value, roots: &[ArenaIndex], ) -> Result<ArenaIndex, ArenaError>
Allocate with GC on failure
Sourcepub fn stats(&self) -> ArenaStats
pub fn stats(&self) -> ArenaStats
Get arena stats
Sourcepub fn string(&self, s: &str) -> Result<ArenaIndex, ArenaError>
pub fn string(&self, s: &str) -> Result<ArenaIndex, ArenaError>
Allocate a string as contiguous Char values.
Returns a Value::String that stores the length, with data pointing to the first character slot. Empty strings have data == NULL and len == 0.
§Memory Usage
Allocates s.chars().count() slots for characters, plus 1 slot for
the String value itself.
§Errors
Returns ArenaError::OutOfMemory if:
- No contiguous block is available
§Example
use grift_parser::Lisp;
let lisp = Lisp::<1000>::new();
let hello = lisp.string("hello").unwrap();
assert_eq!(lisp.string_len(hello).unwrap(), 5);
assert_eq!(lisp.string_char_at(hello, 0).unwrap(), 'h');Sourcepub fn string_from_chars(
&self,
chars: &[char],
) -> Result<ArenaIndex, ArenaError>
pub fn string_from_chars( &self, chars: &[char], ) -> Result<ArenaIndex, ArenaError>
Allocate a string from a slice of chars.
Returns an ArenaIndex pointing to a Value::String.
Sourcepub fn string_len(&self, str_idx: ArenaIndex) -> Result<usize, ArenaError>
pub fn string_len(&self, str_idx: ArenaIndex) -> Result<usize, ArenaError>
Get the length of a string.
Returns O(1) since length is stored in the String value.
§Errors
Returns ArenaError::InvalidIndex if the index doesn’t point to
a valid string.
Sourcepub fn string_char_at(
&self,
str_idx: ArenaIndex,
char_index: usize,
) -> Result<char, ArenaError>
pub fn string_char_at( &self, str_idx: ArenaIndex, char_index: usize, ) -> Result<char, ArenaError>
Get a character at the given index within a string.
Character indices are 0-based. Returns O(1) access.
§Errors
Returns ArenaError::InvalidIndex if:
- The string index is invalid
- The character index is out of bounds
- The slot doesn’t contain a Char value
Sourcepub fn string_eq_contiguous(
&self,
a: ArenaIndex,
b: ArenaIndex,
) -> Result<bool, ArenaError>
pub fn string_eq_contiguous( &self, a: ArenaIndex, b: ArenaIndex, ) -> Result<bool, ArenaError>
Sourcepub fn string_matches(
&self,
str_idx: ArenaIndex,
s: &str,
) -> Result<bool, ArenaError>
pub fn string_matches( &self, str_idx: ArenaIndex, s: &str, ) -> Result<bool, ArenaError>
Check if a string matches a Rust string slice.
§Errors
Returns an error if the string index is invalid.
Sourcepub fn string_to_bytes(
&self,
str_idx: ArenaIndex,
buf: &mut [u8],
) -> Result<usize, ArenaError>
pub fn string_to_bytes( &self, str_idx: ArenaIndex, buf: &mut [u8], ) -> Result<usize, ArenaError>
Copy a string’s contents to a byte buffer.
Returns the number of bytes written. Only ASCII characters (0-127) are copied; non-ASCII characters are skipped.
§Warning
This method is designed for ASCII strings. For strings containing non-ASCII Unicode characters, some characters will be skipped and the byte count may not match the character count.
§Errors
Returns an error if the string index is invalid.
Sourcepub fn string_free(&self, str_idx: ArenaIndex) -> Result<(), ArenaError>
pub fn string_free(&self, str_idx: ArenaIndex) -> Result<(), ArenaError>
Sourcepub fn make_array(
&self,
len: usize,
default: ArenaIndex,
) -> Result<ArenaIndex, ArenaError>
pub fn make_array( &self, len: usize, default: ArenaIndex, ) -> Result<ArenaIndex, ArenaError>
Create an array with the given length, initialized with a default value.
The array stores len values contiguously in the arena.
§Memory Usage
Allocates len slots for element storage, plus 1 slot for the Array value itself.
§Example
use grift_parser::Lisp;
let lisp = Lisp::<1000>::new();
let arr = lisp.make_array(3, lisp.nil().unwrap()).unwrap();
assert_eq!(lisp.array_len(arr).unwrap(), 3);Sourcepub fn array_len(&self, arr_idx: ArenaIndex) -> Result<usize, ArenaError>
pub fn array_len(&self, arr_idx: ArenaIndex) -> Result<usize, ArenaError>
Get the length of an array.
Returns O(1) since length is stored in the Array value.
§Errors
Returns ArenaError::InvalidIndex if the index doesn’t point to an array.
Sourcepub fn array_get(
&self,
arr_idx: ArenaIndex,
index: usize,
) -> Result<ArenaIndex, ArenaError>
pub fn array_get( &self, arr_idx: ArenaIndex, index: usize, ) -> Result<ArenaIndex, ArenaError>
Get the element at the given index within an array.
Returns O(1) access via direct index calculation.
§Errors
Returns ArenaError::InvalidIndex if:
- The array index is invalid
- The element index is out of bounds
Sourcepub fn array_set(
&self,
arr_idx: ArenaIndex,
index: usize,
value: ArenaIndex,
) -> Result<(), ArenaError>
pub fn array_set( &self, arr_idx: ArenaIndex, index: usize, value: ArenaIndex, ) -> Result<(), ArenaError>
Set the element at the given index within an array.
Returns O(1) mutation via direct index calculation.
§Errors
Returns ArenaError::InvalidIndex if:
- The array index is invalid
- The element index is out of bounds