Skip to main content

Lisp

Struct Lisp 

Source
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>

Source

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.

Source

pub fn arena(&self) -> &Arena<Value, N>

Get reference to the underlying arena

Source

pub fn alloc(&self, value: Value) -> Result<ArenaIndex, ArenaError>

Allocate a value

Source

pub fn get(&self, index: ArenaIndex) -> Result<Value, ArenaError>

Get a value

Source

pub fn set(&self, index: ArenaIndex, value: Value) -> Result<(), ArenaError>

Set a value

Source

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).

Source

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.

Source

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.

Source

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.

Source

pub fn boolean(&self, b: bool) -> Result<ArenaIndex, ArenaError>

Allocate a boolean based on a Rust bool

Source

pub fn number(&self, n: isize) -> Result<ArenaIndex, ArenaError>

Allocate a number

Source

pub fn float(&self, f: f64) -> Result<ArenaIndex, ArenaError>

Allocate a floating-point number

Source

pub fn char(&self, c: char) -> Result<ArenaIndex, ArenaError>

Allocate a character

Source

pub fn cons( &self, car: ArenaIndex, cdr: ArenaIndex, ) -> Result<ArenaIndex, ArenaError>

Allocate a cons cell

Source

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.

Source

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.

Source

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

Source

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

Source

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.

Source

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.

Source

pub fn symbol_from_bytes(&self, bytes: &[u8]) -> Result<ArenaIndex, ArenaError>

Create or retrieve an interned symbol from bytes (for parsing)

Source

pub fn builtin(&self, b: Builtin) -> Result<ArenaIndex, ArenaError>

Allocate a builtin function

Source

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.

Source

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.

Source

pub fn set_stdlib_cache( &self, index: ArenaIndex, body: ArenaIndex, params: ArenaIndex, ) -> Result<(), ArenaError>

Set the cached body and params for a StdLib value.

Source

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.

Source

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))

Source

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)).

Source

pub fn list<I>(&self, items: I) -> Result<ArenaIndex, ArenaError>

Build a list from an iterator of indices

Source

pub fn list_len(&self, list: ArenaIndex) -> Result<usize, ArenaError>

Get the length of a list

Source

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.

Source

pub fn symbol_matches( &self, sym: ArenaIndex, name: &str, ) -> Result<bool, ArenaError>

Check if a symbol matches a string.

Source

pub fn symbol_to_bytes( &self, sym: ArenaIndex, buf: &mut [u8], ) -> Result<usize, ArenaError>

Extract symbol name to a fixed buffer.

Source

pub fn symbol_len(&self, sym: ArenaIndex) -> Result<usize, ArenaError>

Get the length of a symbol’s name.

Source

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

Source

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.

Source

pub fn alloc_or_gc( &self, value: Value, roots: &[ArenaIndex], ) -> Result<ArenaIndex, ArenaError>

Allocate with GC on failure

Source

pub fn stats(&self) -> ArenaStats

Get arena stats

Source

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');
Source

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.

Source

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.

Source

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
Source

pub fn string_eq_contiguous( &self, a: ArenaIndex, b: ArenaIndex, ) -> Result<bool, ArenaError>

Compare two strings for equality.

§Errors

Returns an error if either string index is invalid.

Source

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.

Source

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.

Source

pub fn string_free(&self, str_idx: ArenaIndex) -> Result<(), ArenaError>

Free a string and all its character slots.

§Errors

Returns an error if the string index is invalid.

Source

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);
Source

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.

Source

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
Source

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
Source

pub fn array_free(&self, arr_idx: ArenaIndex) -> Result<(), ArenaError>

Free an array and all its element slots.

§Errors

Returns an error if the array index is invalid.

Trait Implementations§

Source§

impl<const N: usize> Default for Lisp<N>

Source§

fn default() -> Lisp<N>

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<const N: usize> !Freeze for Lisp<N>

§

impl<const N: usize> !RefUnwindSafe for Lisp<N>

§

impl<const N: usize> Send for Lisp<N>

§

impl<const N: usize> !Sync for Lisp<N>

§

impl<const N: usize> Unpin for Lisp<N>

§

impl<const N: usize> UnwindSafe for Lisp<N>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.