as the focus ot this prototype is supposed to be building things in Lisp, I've started deliberately copying stuff that mostly works directly from the 0.0.6 branch into this branch. After all, if it's going to be replaced in Lisp, it doesn't have to be the most elegant C.
80 lines
2.3 KiB
C
80 lines
2.3 KiB
C
/**
|
|
* payloads/stack.c
|
|
*
|
|
* The execution stack.
|
|
*
|
|
* (c) 2026 Simon Brooke <simon@journeyman.cc>
|
|
* Licensed under GPL version 2.0, or, at your option, any later version.
|
|
*/
|
|
|
|
#include "memory/node.h"
|
|
#include "memory/pointer.h"
|
|
#include "memory/pso2.h"
|
|
#include "memory/pso4.h"
|
|
#include "memory/tags.h"
|
|
|
|
#include "payloads/cons.h"
|
|
#include "payloads/stack.h"
|
|
|
|
/**
|
|
* @brief The maximum depth of stack before we throw an exception.
|
|
*
|
|
* `0` is interpeted as `unlimited`.
|
|
*/
|
|
uint32_t stack_limit = 0;
|
|
|
|
/**
|
|
* Fetch a pointer to the value of the local variable at this index.
|
|
*
|
|
* TODO: I think the first argument would be better as a pso_pointer.
|
|
*/
|
|
struct pso_pointer fetch_arg( struct pso4 *frame, unsigned int index ) {
|
|
struct pso_pointer result = nil;
|
|
|
|
// TODO check that the frame is indeed a frame!
|
|
if ( index < frame->payload.stack_frame.args ) {
|
|
result = frame->payload.stack_frame.arg[index];
|
|
} else {
|
|
struct pso_pointer p = frame->payload.stack_frame.more;
|
|
|
|
for ( int i = args_in_frame; i < index; i++ ) {
|
|
p = pointer_to_object( p )->payload.cons.cdr;
|
|
}
|
|
|
|
result = pointer_to_object( p )->payload.cons.car;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the environment from the stack frame identified by this
|
|
* `frame_pointer`
|
|
*
|
|
* @param frame_pointer a pointer to a stack frame.
|
|
*/
|
|
struct pso_pointer fetch_env( struct pso_pointer frame_pointer ) {
|
|
return stackp( frame_pointer ) ?
|
|
pointer_to_pso4( frame_pointer )->payload.stack_frame.env : nil;
|
|
}
|
|
|
|
/**
|
|
* Push a binding (and therefore a reference) for this `local` onto the
|
|
* stack_frame indicated by this `frame_pointer`, thereby protecting the
|
|
* `local` from garbage collection until the frame itself is disposed of.
|
|
*
|
|
* This is a hack. For Lisp functions, where the stack frames are set up
|
|
* and torn down by eval/apply, it shouldn't be necessary.
|
|
*/
|
|
struct pso_pointer push_local( struct pso_pointer frame_pointer,
|
|
struct pso_pointer local ) {
|
|
if ( stackp( frame_pointer ) ) {
|
|
struct pso4 *frame = pointer_to_pso4( frame_pointer );
|
|
|
|
struct pso_pointer l = make_cons( frame_pointer, local,
|
|
frame->payload.stack_frame.locals );
|
|
frame->payload.stack_frame.locals = l;
|
|
}
|
|
|
|
return local;
|
|
}
|